-
Notifications
You must be signed in to change notification settings - Fork 837
Description
Hello.
At https://github.com/gravitystorm/openstreetmap-carto we are trying to render unpaved roads different from paved ones.
Normally, I would avoid to ask this type of questions here on the issue tracker, but I started to write the code for openstreetmap-carto in May 2017, since than the solution is in continuous development, we have tested a lot of possible solutions, but I still struggle to have a well-performing solution. That’s why now, after one year, finally I ask here.
What is the desired rendering?
Paved roads
We want to render paved roads still like they do currently in openstreetmap-carto: with a plain colour.
Unpaved roads
Unpaved roads should however be rendered from now on with a fine, irregular-looking pattern. At this image you can see the rendering we want to get. The irregular pattern fills exactly the space that would have been occupied for a paved road. Neither at crossings between two unpaved roads nor at crossings between a paved and an unpaved road there is any visual artefact.
Further requirements
We are using round line caps for roads to avoid visual artefacts at places where OSM ways are split.
line-pattern?
The natural choise would be Mapnik’s line-pattern feature. Mapnik renders this pattern following the geometry of the OSM way, blending it at angles within the OSM way. See here for a minimalistic example. But I do not get the desired rendering because line-pattern seems to have no support for round line caps, so this leads to ugly rendering artefacts on every single angle in the line, and especially where OSM ways end. (Furthermore, we need many pattern files: one for each combination of road colour and width. But the main problem with this solution are the rendering artefacts.)
dash-array?
We could use various dash-arrays to try to get a similar visual effect. But also here, we have rendering artefacts.
polygon-pattern on line geometries?
A polygon-pattern would be the perfect solution. It is not blended at angles. It can even be aligned globally, so that on crossings of two unpaved roads, the pattern makes a smooth connection. It would also play well together with highway area rendering, which are polygons. But unfortunately it is not possible to use polygon-pattern with line geometries in Mapnik. (This would be a great feature! Something like applying an SVG pattern to a line geometry just like you would apply a colour – the pattern would only be visible where the line is drawn, and the pattern itself could ideally be aligned globally.)
Our workaround…
What is the principle of the workaround?
As there is no build-in support for polygon-pattern on line geometries, we have developed a workaround. In short:
- We apply
line-comp-op: dst-out;to the line geometries of the unpaved roads to cut holes in the rendering canvas. - In the SQL query, our data source, we create a fake geometry which is a global bounding box for the whole world. This fake geometry, we render once for each different road colour as polygon-pattern using
dst-overbehind the existing canvas.
There is a short, but complete documentation available.
What’s the problem with the workaround?
We have about 10 different road colours. Reading the Mapnik documentation about comp-op, we would expect that feature-level comp-op would work on a per-feature base. Quote from the documentation:
Feature-level compositing, during rendering, means that for every geometry processed the […] operation will be used to blend the rendered pixels of the polygon against the destination pixels (all data previously rendered on the canvas whether from previous layers or just another polygon from the same style).
But apparently this does not happen. What we observe is:
- We have yet various things like landcovers rendered. Let’s call this canvas A.
- Render white unpaved roads: Cut a hole in the existing image by using comp-op. Now we have canvas A with some holes.
- Render the white pattern behind: It will be only visible where we have the holes in canvas A that have been done in the previous step. Now we have still canvas A with some holes. And we have canvas B with the white pattern on the global bounding box. Canvas B is behind canvas A.
- Render white paved roads. They will be rendered in canvas A.
- Render yellow (higher road class!) unpaved roads: Cut a hole in the existing image by using comp-op. This cuts more holes in canvas A (only!), but does not touch canvas B.
- Render the yellow pattern behind: This will create canvas C, which will be behind canvas B. It will therefore never be visible.
That’s bad for our use case. So I have worked around this problem: In our MSS code (we are using CartoCSS) I’ve created attachments. This way, the rendering rules for each road colour end up in an own attachment (all of them within the same layer). Also the fake-global-bounding-box gets its own attachments: one for each road colour, in the MSS code always directly after the rendering rules for this road colour. This leads to a perfect rendering without any artefacts. So the pull request implementing this for openstreetmap-carto had been merged.
Unfortunately, it turns out that just using many attachments makes the rendering process slower – by factor 2 or 3 measured for the whole style! Given that the whole style has also many other layers than the roads, the attachments itself will make a much greater slow-down for its own layer only, maybe factor 50? (That’s even true when these attachments do not actually get rendered because there is no data available for them in the data source.) The pull request has therefore been reverted.
My questions
- Are we doing something wrong? Maybe overlooking at all a better approach to get the same rendering?
- Are we doing something wrong with the comp-op? Can we get it working really on a per-feature base as described in the Mapnik documentation, and so avoid using attachments?
- If there is no other approach, would it be possible to tune the current approach somehow, to avoid an extreme slow-down because of the attachments?
If we could some help or some ideas here, that would be great – I would highly appreciate!