-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Description
This is an umbrella ticket for various reports of occasional artifacts when rendering polygons with GeoJSON source, with a summary of the issue and our road to a solution.
Previous tickets: #12356, #10768, #10592, #10299, #10106, #9981, #9913, #9761, #9441, #9072, #7857, #7663, #7433, #7228, #6383, #6313, #6069, #3545, #3080. And related ones: #13147 #12903 #7748, #7233, #5265, #4962, #3032, #2975, #2696
To be able to render polygons in WebGL, we have to turn them into a set of triangles — this is done by the earcut library. It is very fast, but has one serious limitation — it can't handle non-simple polygons, such as those with self-intersections, intersecting rings, or holes outside of the outer ring. This is itself a constant point of confusion (as suggested by related tickets above), especially given that Canvas and SVG don't have such problems (browser rendering engines handle degeneracies internally). But at least in the invalid polygon case, we can offer a workaround — fixing the input polygons before feeding them to GL JS.
What makes matters worse is that polygon geometries have to be processed before being rendered — specifically, they have to be sliced into tiles, with shapes converted into an integer tile coordinate system and simplified for every zoom level — performed by the geojson-vt. This process alters original geometry in subtle ways that can lead fully valid simple polygons to become invalid on certain zoom levels — in particular, introduce self-intersections. This in turn, although rarely, triggers rendering artifacts in GL JS for which we don't have any workarounds to offer.
This is a fundamental problem that I've been meaning to address for a long time, but it is notoriously difficult to solve algorithmically. The only attainable solution I see is fixing polygons at runtime after processing on the client. This is what we already do in GL Native with wagyu. So we have two options:
- Port Wagyu to JavaScript. This C++ library is 5000 lines of very complex code, so the port would be very difficult, potentially add a significant overhead to the GL JS bundle size, without any guarantee that the JS port will be performant enough to handle the issue.
- Compile Wagyu with Emscripten into a browser version (either JS or potentially WebAssembly in future) and use it is a kind of a drop-in plugin for situations when the problem arises, similar to how we solved RTL text rendering. This is our last resort solution, because the library still won't work by default, but it may work well enough and at last provide a practical workaround for some relief.
- Come up with a new, lightweight, JS-centric approach. This is what I have attempted multiple times throughout the years in the polysnap project, and the approach is very promising. I feel like I came very close to a working solution recently (although the WIP code is not yet pushed to the repo), but still need more time to tackle this — hopefully in the nearest months.
Also note that solving the issue for the valid polygons use case (where they turn invalid later in the GL JS pipeline) will automatically solve it for other use cases such as invalid input polygons, make our API easier to use.
I'll provide updates to this ticket when the matters progress — stay tuned. Meanwhile, I'm closing the open tickets among the linked ones to centralize the discussion.