Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GeoJSON polygon rendering is unreliable (master ticket) #7023

Open
mourner opened this issue Jul 25, 2018 · 37 comments
Open

GeoJSON polygon rendering is unreliable (master ticket) #7023

mourner opened this issue Jul 25, 2018 · 37 comments

Comments

@mourner
Copy link
Member

mourner commented Jul 25, 2018

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: #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.

@hctomkins
Copy link

hctomkins commented Jul 25, 2018

A few other possiblities - unsure how each of them would work:

if geojson-vt is causing segmentation issues and if the errors introduced by geojson-vt are predictable enough, could we not fix that issue at source in geojson-vt?

A further suggestion as a hotfix for the current scenario - could we set a flag to disable this tiling for simple shapes that are causing defects, and just display the shape at full detail over all zoom levels? I.E. bypass geojson-vt altogether.

@mourner
Copy link
Member Author

mourner commented Jul 25, 2018

if geojson-vt is causing segmentation issues and if the errors introduced by geojson-vt are predictable enough, could we not fix that issue at source in geojson-vt?

They aren't predictable — basically, any rounding of a coordinate to integer grid can introduce a self-intersection, even if you set simplification tolerance to 0. Also, there are no known fast simplification algorithms that preserve topology and are guaranteed to avoid self-intersections.

A further suggestion as a hotfix for the current scenario - could we set a flag to disable this tiling for simple shapes that are causing defects, and just display the shape at full detail over all zoom levels? I.E. bypass geojson-vt altogether.

No, because the whole architecture of Mapbox GL relies on the data being tiled and converted to integer tile coordinates.

@samfader
Copy link
Contributor

@mourner et al - running into this issue in GL JS 0.46.0 and higher.

https://bl.ocks.org/samfader/raw/0c68b85662ecb933bc5cbcfb7a673cfc/ - uses GL JS 0.46.0

https://bl.ocks.org/samfader/e6806f9961daf7aeb3a5123610f207f2 - uses GL JS 0.45.0

The polygons look fine in geojson.io and in GL JS 0.45.0 and lower, and also when created as tilesets via Studio, but if added in GL JS in a version equal to or higher than 0.46.0, the polygons start to get randomly simplified/cut off. Do you think this is related to this ticket, or worth a separate one?

@anisart
Copy link

anisart commented Oct 16, 2018

I have another example of this issue on mapbox-gl-js 0.46.0+
https://codepen.io/anisart/pen/wYpwpb - geometry breaks on 5+ zoom level.

But I don't use non-simple polygons. I need draw squares only, when every square is equal to tile of some given zoom level.
Is there any solution for this case? Or should I use the version 0.45.0 for now?

@mourner
Copy link
Member Author

mourner commented Oct 17, 2018

@anisart no, that looks like a different issue — might be a regression. Let me take a look.

@ifzm
Copy link

ifzm commented Nov 3, 2018

Looking forward to getting good results,follow 👍

@reyemtm
Copy link

reyemtm commented Jan 9, 2019

#7748 is also likely related to this issue. Setting maxzoom: 18 in my geojson source options eliminates the 'overzooming' artifacts in my example on that issue.

@simonrp84
Copy link

Hi,
I encountered this problem but have managed to resolve most of the artefacts by using fill-antialias: false.
Here's an example of a polygon layer with geoJSON:

const layer_low ={"id": 'poly_lo', "type": "fill", "source": 'data_positions', 'paint':{'fill-outline-color': '#ff0', 'fill-color': '#ff0', "fill-opacity": 0.3}, filter: ['==', 'conf', 'Low']}

I have two other layers with similar definitions. This results in the following display:
Bad

But, if I change my definition to this:

const layer_low ={"id": 'poly_lo', "type": "fill", "source": 'data_positions', 'paint':{'fill-outline-color': '#ff0', 'fill-color': '#ff0', "fill-opacity": 0.3, 'fill-antialias': false}, filter: ['==', 'conf', 'Low']}

Then the map output is as follows:
Fix

Much better! Thought I'd mention it here in case it helps anyone else.

@mccainz
Copy link

mccainz commented Jan 13, 2021

I have an example using a polygon with holes where artifacts appear based on the zoom level.

screencast.2021-01-12.19-01-45.mp4

@hborrelli1
Copy link

I am experiencing a very similar issue. In our case we are taking a geoJson object, using turf.difference to create a mask shape. When we add the layer to the map it looks correct at a zoom level of 10. However, when we zoom in further around zoom level 11 and 12, we get strange shapes appearing. This typically only happens with more complicated shapes with holes. To avoid complications with Turf while dealing with complicated shapes, we are running calculations to separate interior shapes and add them as separate layers - this is working fine. However, the issue is still present when only creating a mask with the main shape and exterior shapes.

Correct Geometry View:
correct-geometry-view

Incorrect View at zoom level 11:
screenshot-of-issue

Is there any way to fix this issue currently?

@reyemtm
Copy link

reyemtm commented Sep 10, 2021 via email

@mourner
Copy link
Member Author

mourner commented Sep 10, 2021

@hborrelli1 currently the only 100% reliable workaround is to upload and use it as a vector tile dataset instead of GeoJSON — Mapbox vector tile server has advanced processing that cleans up all topologic issues with the polygons in each tile, so they're pretty much guaranteed to render correctly. The difficulty is in bringing this processing to the client, which prompted the ticket.

@reyemtm
Copy link

reyemtm commented Sep 11, 2021 via email

@dmitrykinakh
Copy link

@mourner - the problem with using vector tiles for the case that @hborrelli1 has described in the fact that we are not able to achieve the same result and I'll explain why.
We have a Postgres table with different types(city, county, municipality, zip code, etc) of places in the USA.

For a line layer (red line on @hborrelli1's screenshot) it's quite simple (let's ignore now there are some artifacts on the map):
image

But how to gray out the rest territory?
The following filter will not give us a needed result as there are other places located in the same place where feature ca-state-place-placercountyunincorporated is located. It will be annoying to exclude all of them by id's and not necessary they fit into ca-state-place-placercountyunincorporated shape boundary.

  "filter": [
    "all",
    [
      "!=",
      "id",
      "ca-state-place-placercountyunincorporated"
    ]
  ],

Do you know any examples of how to grey our territory around a feature according to my example?

@mclaeysb
Copy link

For what it's worth, I wrote a little JS module to deal with non-simple polygons (back in the days when I was trying to write a buffer algorithm using JS for the Turf project). Just in case it could be of help with any clean-up process. Good luck!

@finesome
Copy link

I'm not sure if my idea is valid (please correct me) but I've tried generating GeoJSON feature for polygon by first creating a polygon in Leaflet and converting it to GeoJSON using toGeoJSON() method. Then I used that generated polygon feature in Mapbox application.

@carlosdag28
Copy link

Any update on this?. I have tried with the leaflet library and geojson.io and they show me well but in mapbox v2.11.0 it is not rendering well

@WebHero0544
Copy link

I still have this problem with the latest version (v3.0.0-Bet.4), I wonder when it will be fixed?

@david-morris
Copy link

david-morris commented Oct 20, 2023

I have a problem that might be related which I thought had to do with self-overlapping MultiPolygons:

MultiPolygon.flicker.edit.mp4

On closer inspection, this happens because of the size and positioning of our layers (we cover the entire world except a region of interest such as a country or province by our own coordinates).

Are there any heuristics to make this happen less often? This is a massive visual glitch for us, we use this layer to show customers where they can use our product.

@WebHero0544
Copy link

@mourner Hello, when will this problem be solved, rendering under albers projection is all wrong

@david-morris
Copy link

@WebHero0544 this ticket seems to be related to some deep problems with assumptions made in the render pipeline. I'm on the outside of this project, but I suspect that this problem will be solved when other projects start coming close to mapbox's featureset and it becomes a worthwhile investment.

Have you tried using simplified geometries at farther (lower) zoom levels? That reduces the appearance of this issue for us.

@WebHero0544
Copy link

@mourner Thank you for your advice. I will try to extract the geojson data for a try. Could you please give me feedback on the problem? When can it be solved? mapbox is used in my company project, and I have paid for it

@WebHero0544
Copy link

@mourner It's even worse under the albers projection

@WebHero0544
Copy link

@mourner I wanted to use CustomLayerInterface to render geojson myself, but albers projection is not supported

@WebHero0544
Copy link

@david-morris I have just extracted the geojson data, but there is still a problem

@david-morris
Copy link

I've used turf simplify in cases where our backend doesn't have simpler geometry available. It's a big compromise, but it tends to reduce this problem.

@WebHero0544 What do you mean "extract geojson data"? If you're using the layer editor, why not convert to raster? I don't think this issue affects raster layers.

Also I'm not totally sure this is the same issue, since it's an unusual projection. What does it look like and what kinds of data does it happen on? We have more of these artifacts on larger, more complicated shapes.

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

No branches or pull requests