You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Title: Feature request: neighbor-aware label placement for consumers that bake labels into individual tiles (tile-provider use case)
Type: Feature request (current behavior is expected, not a bug — but a couple of concrete API tweaks below would make this use case first-class and much cheaper).
I use mapsforge_flutter purely as an offline .map tile renderer, feeding the rendered ui.Image tiles into a different Flutter map widget (flutter_map) through a custom TileProvider. I don't use mapsforge's own MapsforgeView / label layer — the host widget owns the map surface, gestures, overlays, etc. So each output tile must be self-contained, with labels baked in:
final result =await renderer.executeJob(JobRequest(Tile(x, y, z, 0)));
// renderer = DatastoreRenderer(datastore, theme, useSeparateLabelLayer: false)final img = result.picture!.convertPictureToImage();
This is the classic "render tiles for a third-party map widget / tile server" scenario.
Problem
With useSeparateLabelLayer: false, each 256px tile's labels are collision-resolved in isolation (per-tile collisionFreeOrdered()). Because a single tile has little room, label/POI density comes out noticeably lower than what MapsforgeView's separate label layer produces (which resolves collisions across the whole viewport), and labels near tile edges are lost. _processLabels + TileDependencies handle a label that visually spans a boundary, but they don't change which labels win the per-tile collision, so the density gap remains. Screenshots comparing our offline render vs. the same style rendered with a global label layer show the difference clearly (far fewer place/POI/trail names).
Workaround I built (and why it's costly)
To recover density I did consumer-side "meta-tiling": for each output tile, render the base label-free, then pull labels for a 3×3 neighborhood (which does collide across the range) and composite:
// base: useSeparateLabelLayer: true -> picture without labelsfinalbase=await renderer.executeJob(JobRequest(centerTile));
// labels with cross-tile collision context:final lbl =await renderer.retrieveLabels(JobRequest(topLeftTile, bottomRightTile)); // 3x3// composite base + lbl.renderInfo onto one UiCanvas, clip to the center tile
It works (density is close to the label-layer output), but it's expensive per output tile. Measured on a real Taiwan topo map @ z17, read path only:
meta-tiling costs ~2.3–2.75× the read work of the plain single-tile path (even after I added a per-tile label cache so adjacent output tiles share neighbor reads).
Two structural costs that are hard to avoid from outside the library:
The center tile is read twice.executeJob(useSeparateLabelLayer: true) reads the center tile, but then labels.clear() (datastore_renderer.dart:214) and returns the emptied collection (JobResult.normal(picture, labels) at :230). So to get the center tile's labels I have to retrieveLabels it again.
Every neighborhood fetch is a separate retrieveLabels → isolate round-trip serializing a LayerContainerCollection back.
What would help (any one of these)
Ideal — a first-class meta-tile / neighbor-aware tile API for the "bake tiles for a third-party widget" use case: render a tile (or an N×N block of tiles) whose labels are placed with a configurable neighborhood collision context, reading the region once and emitting self-contained tiles. This is what map tile servers do (render a big meta-tile, place labels globally, slice). It would let consumers match MapsforgeView-level density without re-implementing it expensively per tile.
Minimal — don't clear the labels in the useSeparateLabelLayer: true path; return them in JobResult instead of the emptied collection. Then a consumer can get (label-free base picture + this tile's labels) from one executeJob and only needs to fetch the 8 border tiles' labels, halving the center-tile work and avoiding the double read.
A short doc note on the recommended pattern for rendering self-contained tiles (labels baked) with good density, for consumers not using MapsforgeView.
Happy to open a PR toward (2) if that direction sounds right, and to share the meta-tiling + caching consumer code / benchmarks if useful for (1).
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Title: Feature request: neighbor-aware label placement for consumers that bake labels into individual tiles (tile-provider use case)
Type: Feature request (current behavior is expected, not a bug — but a couple of concrete API tweaks below would make this use case first-class and much cheaper).
master @ af87d70
Use case
I use
mapsforge_flutterpurely as an offline.maptile renderer, feeding the renderedui.Imagetiles into a different Flutter map widget (flutter_map) through a customTileProvider. I don't use mapsforge's ownMapsforgeView/ label layer — the host widget owns the map surface, gestures, overlays, etc. So each output tile must be self-contained, with labels baked in:This is the classic "render tiles for a third-party map widget / tile server" scenario.
Problem
With
useSeparateLabelLayer: false, each 256px tile's labels are collision-resolved in isolation (per-tilecollisionFreeOrdered()). Because a single tile has little room, label/POI density comes out noticeably lower than whatMapsforgeView's separate label layer produces (which resolves collisions across the whole viewport), and labels near tile edges are lost._processLabels+TileDependencieshandle a label that visually spans a boundary, but they don't change which labels win the per-tile collision, so the density gap remains. Screenshots comparing our offline render vs. the same style rendered with a global label layer show the difference clearly (far fewer place/POI/trail names).Workaround I built (and why it's costly)
To recover density I did consumer-side "meta-tiling": for each output tile, render the base label-free, then pull labels for a 3×3 neighborhood (which does collide across the range) and composite:
It works (density is close to the label-layer output), but it's expensive per output tile. Measured on a real Taiwan topo map @ z17, read path only:
executeJob(useSeparateLabelLayer: true)reads the center tile, but thenlabels.clear()(datastore_renderer.dart:214) and returns the emptied collection (JobResult.normal(picture, labels)at :230). So to get the center tile's labels I have toretrieveLabelsit again.retrieveLabels→ isolate round-trip serializing aLayerContainerCollectionback.What would help (any one of these)
Ideal — a first-class meta-tile / neighbor-aware tile API for the "bake tiles for a third-party widget" use case: render a tile (or an N×N block of tiles) whose labels are placed with a configurable neighborhood collision context, reading the region once and emitting self-contained tiles. This is what map tile servers do (render a big meta-tile, place labels globally, slice). It would let consumers match
MapsforgeView-level density without re-implementing it expensively per tile.Minimal — don't clear the labels in the
useSeparateLabelLayer: truepath; return them inJobResultinstead of the emptied collection. Then a consumer can get (label-free base picture + this tile's labels) from oneexecuteJoband only needs to fetch the 8 border tiles' labels, halving the center-tile work and avoiding the double read.A short doc note on the recommended pattern for rendering self-contained tiles (labels baked) with good density, for consumers not using
MapsforgeView.Happy to open a PR toward (2) if that direction sounds right, and to share the meta-tiling + caching consumer code / benchmarks if useful for (1).
All reactions