Skip to content

repair corrupt (multi)polygons as far as possible. Fixes many clipping#908

Open
geoneutrino wants to merge 1 commit into
systemed:masterfrom
geoneutrino:features/geometry-corrections
Open

repair corrupt (multi)polygons as far as possible. Fixes many clipping#908
geoneutrino wants to merge 1 commit into
systemed:masterfrom
geoneutrino:features/geometry-corrections

Conversation

@geoneutrino
Copy link
Copy Markdown

This code tries to fix as many geometry errors, clipping and collapsing problems (geometry / parts missing in single tiles) as possible before writing to mbtiles
Area guard (50%) to prevent complete collapses
Buffer(0) for additional corrections (solves some intersections and topological errors)

Fixes:

  • No more crashes due to invalid geometries in maplibre-native (had this in Bangladesh and Finland)

Fixes nearly all clipping errors in complex geometries i knew about, e.g.

  • Greece Island
  • Venice
  • Malta
  • Bangladesh river deltas and Kaptai Lake
  • Amazon river delta
  • Belize Central Lagoon
  • and many others

Can introduce an effect with spikes in non-correctable geometries but i have only seen this in Kaptai Lake area (which is with the fix much better than before)

Performance
depending on data used performance in my tests was in the region ~10% slower. To be expected when working heavily with geometries
But as correctness improves significant i think this is acceptable
(maybe i can have a deeper look at performance when i have time in the future)

Not solved / still open
With ocean shapes i still have few issues (saw this on an Philippines island and Denmark). Not repairable right now due todesign decisions in tilemaker - will open a discussion on this later this week

This code was written with help of AI. I'm mainly a C# developer so if a C++/boost guru sees potential feel free

@systemed
Copy link
Copy Markdown
Owner

systemed commented Jun 1, 2026

Thanks! Really smart ideas in here - both the area guard and the zero-width buffer.

I'm slightly nervous about imposing a 10% slowdown on everyone just because Maplibre Native can be a bit crashy on occasion (which I've also noticed) - Maplibre JS is more tolerant. So although I would very much like to merge something like this, I might play around with it a little more to see if we can ease the performance impact. My experience is that these errors mostly happen when geometries are simplified for lower zoom levels, which might help in targeting a little. On the examples you've cited, are these with tilemaker's out-of-the-box OMT config?

@geoneutrino
Copy link
Copy Markdown
Author

Sorry, my text was maybe a bit inprecise. The hard raster/maplibre crash was only at the 2 locations mentioned
The other locations are maplibre js.

Yes, its also in OMT config
I took Bangladesh for development because it seems to be one of the areas with lots of complex polygons on the planet
E.g. the Kaptai Lake at roughly 22.924/92.1274 has the same problems with being there z6, losing parts in z7, ok in z8 but lots of empty tiles in z9-11

@geoneutrino
Copy link
Copy Markdown
Author

maybe its also good to know this finding / discussion
#909
which has impacts on geometry validation

@geoneutrino
Copy link
Copy Markdown
Author

And a thought on performance: it is not perfect to control program flow via parameters but it might be an option to introduce a new command line switch "repair extended (slower)" doing workload stuff but the default would stay on the current program flow ?

@Symmetricity
Copy link
Copy Markdown
Contributor

I tested a narrower version of this idea to see whether the main lower-zoom correctness win could be kept without applying the full cost to every output multipolygon.

The lower-overhead shape I tested was:

  • keep the area-guarded dissolve + zero-width-buffer repair helper;
  • use it after Visvalingam multipolygon simplification;
  • in writeMultiPolygon(), only run the extra validity/repair path when
    simplifyLevel > 0;
  • avoid the broader tile_data.cpp clip-cache repair path for now.

That lines up with Richard's comment that these failures mostly seem to happen after simplification at lower zooms.

Results from local testing:

Variant Wall time vs upstream User CPU vs upstream Peak RSS vs upstream
Full PR #908 +10.86% +14.57% +0.35%
Targeted simplified-output repair +3.66% +2.68% -0.06%

Test shape: warmed Austria extract, OpenMapTiles profile, PMTiles output, no --store, --threads 8.

I also tested the reported Kaptai Lake case using a Bangladesh extract and focused on water at z6-z11. The targeted version repaired the same collapsed water geometries as this PR in the changed z8-z11 tiles. The current output was
not missing the feature records entirely; several water features had collapsed into tiny slivers. With the targeted repair, those features were restored to large lake polygons.

Example decoded vector-tile geometry areas:

Tile Before tile area After tile area
z9/387/289 1,924.5 1,185,870.5
z10/774/578 4,426.5 3,803,304.5
z11/1548/1158 2,219.0 3,701,046.5

So I think there may be a useful compromise here: keep the area-guarded repair, but apply it by default only to simplified output. That appears to preserve the Kaptai/low-zoom water fix while cutting most of the measured overhead.

Caveat: this does not try to solve the separate #909-style issue where Boost considers a clipped geometry valid but a renderer still rejects it. That probably needs a separate targeted fix.

@systemed
Copy link
Copy Markdown
Owner

systemed commented Jun 4, 2026

That sounds promising - thank you for having a go. I'll work up something based on that and perhaps include a switch (or a flag in the JSON layer definition) so you can still take a more cautious "fix everything" approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants