basemaps icon indicating copy to clipboard operation
basemaps copied to clipboard

sort_rank and highroad-style layering

Open bdon opened this issue 2 years ago • 14 comments

In order to make correct casing ordering on complex overpasses, we may need to change Tilezen - Tangram relied on sort_rank with casings to overlay an arbitrary number of layers. For MapLibre GL we have sort_key in the style but that only works per-layer: casings needs to be drawn as separate layers.

In my experience the only solution is to have a fixed set of layers each with their own filter e.g. layers -2, -1, 0, 1, 2. @nvkelso

bdon avatar Jul 21 '23 09:07 bdon

My work around recommendation is to have -1, 0, 1, and a single new MapLibre style layer for "even higher bridges" (level = 2, 3, 4, 5, 6).

That way you avoid parsing the MVT in MapLibre "again, again, again" looking for features at those levels per style layer – when mostly no MVT data layer features are found to be eligible for the GL style layers.

Ideally the "inline" and "outline" are in the same GL style layer and could use the existing sort_key from Tilezen (which would need a port over to this repo). But the explosion of MapLibre style layers for each kind and the levels is already burdensome...

nvkelso avatar Jul 27 '23 22:07 nvkelso

@nvkelso I don't think there is still any solve for what you described like tilezen sort_key because of intersections? https://github.com/maplibre/maplibre/discussions/164#discussioncomment-4925345

So we need have 4 levels, say 5 road classes, and casings/inner strokes, 4x5x2 = 40 layers...

Unless you think we can combine all road classes into a single layer with expressions

bdon avatar Aug 07 '23 07:08 bdon

Not so flexible alternative is this: https://github.com/wipfli/single-highway-layer

wipfli avatar Aug 07 '23 15:08 wipfli

@wipfli what if styles weren't embedded in the properties, but you had a single layer for all classes, and the paint properties were a (very big) case statement? So a complete stack would be like

tunnels_casing
tunnels
ground_casing
ground
bridges_casing
bridges
bridges_2_casing
bridges_2

Instead of having motorway, primary, etc.

bdon avatar Aug 08 '23 09:08 bdon

Yes the case approach works. A limitation is that MapLibre GL JS currently does not support data-driven line-dash arrays and some other properties like line-caps. Maybe worth talking to @zelonewolf and others from the American map style. They run into the same problem.

wipfli avatar Aug 09 '23 14:08 wipfli

Agreed, it's capability gap in the current maplibre-gl-js and we've resorted to very inefficient techniques to resolve it in Americana.

ZeLonewolf avatar Aug 09 '23 16:08 ZeLonewolf

So if we designed around the constraint that there are no variation in linecaps or dashes, we can get away with the total set of ~8 road layers I listed above - it would be cleaner in the number of layers, and have beastly match/case expressions - is this really better for performance than having 40 layers, or about the same?

bdon avatar Aug 09 '23 16:08 bdon

regarding performance cc @msbarry

wipfli avatar Aug 09 '23 19:08 wipfli

Currently most of the processing time for each tile is evaluating each filter expression on each feature. I want to make a change that will make that more of a constant time for each feature regardless of how many layers there are. I need to set up new benchmarks in maplibre first though https://github.com/maplibre/maplibre-gl-js/issues/982

msbarry avatar Aug 09 '23 23:08 msbarry

Looking at tag info for stats: https://taginfo.openstreetmap.org/keys/layer#values

96.99% of layer tags are -1, 0 or 1.

We can try a hybrid approach of @wipfli 's for the other 3.01% where a casing feature is duplicated in the tileset - this should have minimal bloat

So the complete stack of "normal" road symbology for all kinds is

roads_tunnel_2_minus
roads_tunnel_1_casing
roads_tunnel_1
roads_0_casing
roads_0
roads_bridges_1_casing
roads_bridges_1
roads_bridges_2_plus

For a total of 8 road MapLibre GL layers supporting an arbitrary # of flyover levels and tunnels.

bdon avatar Aug 14 '23 03:08 bdon

Screenshot 2023-08-14 at 12 15 56

A style can already support an arbitrary # of layers by using data-driven line-color to visually distinguish each layer. This looks a bit weird aesthetically, and using casings is better in the majority of cases...

"line-color":["rgb",["-",255,["*",20,["to-number",["get","layer"]]]],255,["-",255,["*",20,["to-number",["get","layer"]]]]],

bdon avatar Aug 14 '23 04:08 bdon

proof of concept working:

Screenshot 2023-08-14 at 14 23 51

The main problem now is the lack of data-driven line endings, because using butt will show seams, and using Round will end up like this:

Screenshot 2023-08-14 at 14 25 02

bdon avatar Aug 14 '23 06:08 bdon

Planet build proof of concept live: 5 road classes, unlimited stacking, 8 MapLibre layers:

  • DEMO: https://bdon.github.io/HighRoadPlus/
  • repo: https://github.com/bdon/HighRoadPlus
  • JS code to generate style: https://github.com/bdon/HighRoadPlus/blob/main/highroadplus.js
Screenshot 2023-08-14 at 20 57 52

bdon avatar Aug 14 '23 12:08 bdon

Relevant MapLibre issue: https://github.com/maplibre/maplibre-gl-js/issues/2108

bdon avatar Aug 14 '23 14:08 bdon