I could not be at the Karlsruhe Hack Weekend this time, but i did some hacking work during that and the following week i wanted to write about here. Back in May i discussed in depth the problem of visualizing points of interest in automated rule based maps with symbols and labels in combination. I want to revisit that problem now.
In a nutshell – what i discussed back then was, that you can approach the problem primarily in two ways:
- separately rendering the symbols and labels, typically with the symbols having higher priority than the labels.
- connecting the label and the symbol for every feature and render them with the same priority (typically via shield symbolizer in Mapnik)
Both approaches have advantages and disadvantages – as discussed and demonstrated in that post. The bottom line is – quoting what i wrote back then:
What we would really like instead is rendering the symbols as in the first approach but then render the labels only for those features for which a symbol has been successfully rendered. This, however, is not possible with the current tools.
I have tried to work on this dilemma now by extending Mapnik with the possibility to connect two different independently rendered elements in the map – making the rendering of an element (like a label or a marker) conditional in some form on if other elements have been (or have not been) successfully placed before.
The reason why this kind of functionality is not available in Mapnik so far is probably because Mapnik mostly aims to provide higher level features to connect different elements in the form of what is called group and shield symbolizers (to tie several elements to be either rendered together or not at all) and placements (to try several alternative labeling/symbol placement options). But those are practically severely limited in the level of control they provide to the map designer and they do not allow connecting elements independent of their priorities.
Mapnik’s internal design is fairly complicated and very sparsely documented so what i implemented is surely not very elegant and in many ways not how it should be done. Be that as it may – i will try to explain the feature and how it works in its CartoCSS syntax. For setting up Carto with the new feature you also need an updated mapnik-reference.
The feature is called an anchor – which is a string you can assign to any blocking element you render (point, marker and text symbolizers) – either statically or based on data properties via expressions. With an osm2pgsql PostGIS database the most straight away attribute to use is the
So with the separately rendered POI symbols and labels the symbols get something like
which means: When this marker is successfully placed (i.e nothing previously rendered is blocking it) record its
osm_id attribute in the list of elements successfully placed.
osm_id of course has to be added to the SQL query for the layer.
When you later, after having placed all the symbols, want to place the matching offset labels as far as there is space left and want to avoid placing labels for features for which no symbol has been successfully placed you use
which means: Only place this label if its
osm_id attribute is in the list of anchors of elements successfully placed before. If you want to – in case no symbol could be placed – try to render a centered non-offset label for the feature in question you use
which means: Only place this label if its
osm_id attribute is not in the list of anchors successfully placed before.
As you can see this is pretty simple to use – and it solved our symbol and label rendering dilemma quite nicely. For reference here the two variants offered by the AC-style so far – first the symbol and label separate version (similar to OSM-Carto, but with consistent priorities):
and in the combined version:
which has the main disadvantage that the labels of high priority features block the symbols for lower priority features – hence no shelters on the right side in the second sample image.
With the help of the new Mapnik feature the separate version combines the benefits of both:
On the very right you can see that the higher priority wilderness hut does not prevent the shelter symbol from showing because its label is not rendered together with the symbol but placed later with lower priority. At the same time it avoids the problem of stray offset labels without a corresponding symbol as it happened previously when rendering symbols and labels separately.
That’s it essentially – a relatively simple extension of Mapnik solves the dilemma of combined point symbols and labels that bugged OSM-Carto for almost exactly ten years now.
In terms of the list of desirable features of map design tools i published recently this implements parts of point 5 by the way.
But of course the concept of anchors in Mapnik and making the rendering of elements conditional on one another in a freely definable way can be used for other things as well. More on that in the next post.