I had written about non-locality in tiled rule based map rendering in general some time ago and that post gathered quite a bit of interest – including renewed interest in OSM-Carto development for problems where non-locality is a long known issue. Here i want to continue more in depth on a specific aspect of this.
The difficulty of aggregation
The specific issue this post is about is the aggregation or merging of geometries in map rendering. As i mentioned in the previous post in the section on explicit non-localities in map design:
Since geometries in OpenStreetMap are frequently split in a relatively fine grained fashion to differentiate attributes locally, it is often a problem to render the individual components separately, because it disrupts the rendering. For example, when a road is rendered based on relatively short segments in mapping, dashing patterns and labeling are going to be affected by this. So it might seem a good idea to simply merge all road elements within a tile that are going to be rendered in a common styling. This will, however, lead to different geometries in different tiles and therefore dashing and labeling will be discontinuous across tile edges if you do that – unless you take further measures to avoid that.
To make the problem more clear here the situation in a simple form:

The problem of aggregating geometries across tile boundaries in the most simple form
You have a line split into three ways here extending across several tiles. To achieve the desired rendering results you want these splits to be dissolved before rendering and you want to show these three ways as a continuous line. The way you’d do that in tiled rendering is to gather all the ways intersecting the bounds of the tile and aggregate them with ST_LineMerge(). The problem is that if you’d do so on the tile highlighted in gray you would merge way 1 and way 2. When rendering the other tile to the left, however, you would merge way 2 and way 3. Bottom line – rendering the two tiles would be based on different geometries and therefore in many cases (like when you use line patterns or when you place labels on the lines) the results would be inconsistent across the tile boundary. And the rendering buffer routinely used in tiled rendering does not really help with that in general.
There are a number of obvious approaches you could think of to avoid this kind of problem while still merging geometries:
- exclude any ways from merging that are not fully within the tile boundaries. That would often exclude quite a lot of ways so the aim of dissolving splits would be only partially accomplished that way. And any non-localities in rendering of these elements that require a rendering buffer to be handled correctly would lead to problems.
- when the resulting geometries are exclusively for labeling: Avoid any labels crossing the tile boundaries. This avoids any hard inconsistencies in the form of cut off labels, but it massively interfers with label placement in general and will still indirectly affect other blocking elements.
- clip the merged geometries at the tile boundaries. That would formally avoid inconsistencies at the tile boundaries, but it would introduce new artifical splits that are not in the source data. This is often highly undesirable.
- recursively include additional geometries outside the tile connected to those you want to merge. This is practically expensive to do in all cases and often is not feasible at all when it might lead to merging of thousands of ways across hundreds of tiles until the connectivity ends.
As you can see none of the obvious approaches that come to mind how to handle the issue are really an overall solution that will work in most cases. So we have to look for a different approach.
Aggregating in a tile independent fashion
As already indicated the ultimate source of the inconsistency between rendering results in different neighboring tiles is that the rendering is based on different merged geometries. You merge ways within the area of the tile and that set of ways merged is not the same for different tiles and hence the merged geometries are different as well. So what you need to do is to aggregate in sets that are identical between neighboring tiles. You could, for example, aggregate based on administrative units of a certain level. Practically such an approach of aggregation using spatial units derived from geographic data is usually not going to be viable, especially not on a global map. A more suitable approach is to use abstract geometric units, specifically of the size and shape of the metatiles being rendered.
Here is how this practically looks like:

The concept of aggregating based on a global grid of aggregation sectors
In the center in gray color is the tile we are going to render and in dark pink around the rendering buffer for that tile. In blue you can see the grid of aggregation sectors, one of them further highlighted with a light blue fill. The key here is that the aggregation sectors form a space filling pattern that is the same no matter from which tile you look at it. What you now need to do is assign each and every one of the ways you would like to merge non-ambiguously to one of the sectors. Easiest way to do that is to determine the sector in which the center of the bounding box of the way is located in and to assign the way to that sector. This is shown in pink. Now all you need to do is to aggregate the ways per sector. The resulting geometries will then be identical between neighboring tiles since the aggregation sectors are identical.
The reason why the sector grid is offset by half a metatile size relative to the rendering grid is to minimize the number of sectors that need to be processed per tile to four. If the sector grid was identical to the tile grid you would have to process nine sectors to accurately cover the rendering extent with the buffer.
There is one important caveat here: We also need to exclude any very large geometries from aggregation. Any geometry that would be assigned to a sector other than the four ones meeting in the tile center and that extends into the rendering area of the tile would need to stay out of the aggregation in all tiles. The correct size limit would be the grid sector size (which here is the same as the metatile size) minus the buffer size (or whatever inherent non-locality that specific feature type is subject to – if that is less).
The main limitation that comes with this approach is that there are obviously residual unmerged splits. You can reduce those by enlarging the aggregation sector size. If you do that the aggregation grid no more looks identical from every metatile rendered, therefore some metatiles need to be handled differently than others, which complicates things. And the computational overhead increases.
Can this also be done in preprocessing
The next question obviously is if this procedure could also be done in pre-processing rather than at render time. Since we aggregate in sectors that are shared between four metatiles each and the aggregation is repeated for each of those, there is quite some inefficiency in doing this on the fly.
The answer is yes, you can do this. But especially if you want to do this with regular (like minutely) updates it gets quite a bit more complicated. And the criteria for a preprocessed solution are quite a bit different so the sketched approach is not necessarily the best for that. I will not discuss preprocessing strategies in more detail here, i just wanted to mention the principal possibility.
Practical application
I don’t want to demonstrate a labeling application here since that inevitably involves other non-locality problems due to blocking. Instead i want to follow-up on a design project i presented some time ago where i used geometry aggregation but did not really discuss the non-locality issues resulting from that. This example is the rendering of pipelines.
This project was demonstrating context adjusted dashing patterns and how they can be used to improve the rendering of pipelines with a line signature showing flanges. As i implemented it back then this showed rendering inconsistencies at tile edges due to the basic use of ST_LineMerge() on a tile basis.
The graphical samples shown here use single tile rendering with a buffer size of 64 pixel. This highlights problems at tile edges. In practical rendering most commonly 8×8 metatiles with at least 128 pixel buffer are used so tile edge issues less common in production maps.
Since this specific project involved both merging ans splitting the pipeline ways an alternative solution here would be to just universally split all pipelines at the metatile edges in addition to the splits performed for design reasons. How this looks like you can see here
The sector based solution i discussed above can likewise be used to address this problem though:
This second solution is the more sophisticated one and can be considered better in the results as well since it does not introduce any arbitrary splits not justified by the data.
The implementation relies on a modification of Mapnik that makes the unbuffered bounding box available on the query level.
Conclusion
I tried to demonstrate here in more detail a specific practical non-locality problem in map design that designers frequently are confronted with and which i only mentioned in passing in my more general discussion of the subject in January. I am not aware of the solution i present here being discussed in the context of map rendering elsewhere before, but it is not unlikely that others have used the same approach before independent of me. I hope the discussion here helps others better understand the problem of geometry aggregation in tiled map rendering and how this can be dealt with and maybe even find more efficient solutions for variations of this problem. Pointers to such work are very welcome.




































































