locationtech / jts

The JTS Topology Suite is a Java library for creating and manipulating vector geometry.
Other
1.9k stars 437 forks source link

buffer(0) returns wrong geom #629

Closed i23098 closed 3 years ago

i23098 commented 3 years ago

I have an isochrone returned by Valhalla:

valhalla-isochrone-json.txt

Since sometimes the geometries are a bit invalid, I use JTS .buffer(0) to clean it.

Initially, it's a big poligon: POLYGON ((-91.483063 45.088623, -91.482628 45.085438, -91.479324 45.085125, -91.478821 45.082005, -91.47226 45.076057, -91.470383 45.072361, -91.468643 45.071594, -91.465019 45.072636, ...

but the clean version is not ok, nor in shape, nor in location itself

POLYGON ((-91.48652270044593 45.088637411415355, -91.486664 45.088638, -91.486732 45.089169, -91.48652270044593 45.088637411415355))

I'm using jts-core-1.17.1

dr-jts commented 3 years ago

This is expected behaviour, unfortunately. The buffer(0) trick is just a heuristic that mostly works - but not always. In particular, if the invalidity is caused by a "figure-8"/self-intersection at the highest point of the polygon, then the buffer result keeps the portion of the polygon at the high point - even if that is a very small part of the area. That's whats happening here.

Here's an image of the small area at the top of the polygon which is the buffer(0) result:

image

dr-jts commented 3 years ago

Thinking about this has given me an idea for a further heuristic to counter this situation. If the polygon is flipped along the X axis, then buffer(0) will return the larger part, which can then be flipped back. To detect that the "wrong" part of the polygon is returned by the first buffer, a couple of heuristics come to mind, such as:

Of course, if the polygon also happens to contain a self-intersection at the bottom, then this won't work. But there are two other transformations that can be tried: flipping along the XY axis, and along the -XY axis. One of these should work except for highly pathological polygons.

Here's an example. A polygon with a top self-intersection produces a non-useful result from buffer(0):

image

Flipping the polygon in X produces the desired result:

image

I will code this up in a TestBuilder function and post a link to the code.

i23098 commented 3 years ago

Meanwhile, I found similar issue in stackoverflow: https://stackoverflow.com/questions/31473553/is-there-a-way-to-convert-a-self-intersecting-polygon-to-a-multipolygon-in-jts

dr-jts commented 3 years ago

Meanwhile, I found similar issue in stackoverflow: https://stackoverflow.com/questions/31473553/is-there-a-way-to-convert-a-self-intersecting-polygon-to-a-multipolygon-in-jts

Yes, this is a very common problem. Really JTS needs a MakeValid function similar to the one in GEOS. (Although that also has issues).

The code given in the SO question is fine as far as it goes, but as noted there in a comment it doesn't handle polygons with holes well.

dr-jts commented 3 years ago

Fixed by #655