diff --git a/planetiler-core/src/main/java/com/onthegomap/planetiler/FeatureMerge.java b/planetiler-core/src/main/java/com/onthegomap/planetiler/FeatureMerge.java index 2901907d19..6e0a54824f 100644 --- a/planetiler-core/src/main/java/com/onthegomap/planetiler/FeatureMerge.java +++ b/planetiler-core/src/main/java/com/onthegomap/planetiler/FeatureMerge.java @@ -29,6 +29,7 @@ import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.Polygonal; import org.locationtech.jts.geom.TopologyException; +import org.locationtech.jts.geom.util.GeometryFixer; import org.locationtech.jts.index.strtree.STRtree; import org.locationtech.jts.operation.buffer.BufferOp; import org.locationtech.jts.operation.buffer.BufferParameters; @@ -323,7 +324,7 @@ public static List mergeNearbyPolygons(List> groupByAttrs( * Merges nearby polygons by expanding each individual polygon by {@code buffer}, unioning them, and contracting the * result. */ - private static Geometry bufferUnionUnbuffer(double buffer, List polygonGroup) throws GeometryException { + static Geometry bufferUnionUnbuffer(double buffer, List polygonGroup, Stats stats) { /* * A simpler alternative that might initially appear faster would be: * @@ -433,10 +434,11 @@ private static Geometry bufferUnionUnbuffer(double buffer, List polygo try { merged = union(merged); } catch (TopologyException e) { - throw new GeometryException("buffer_union_failure", "Error unioning buffered polygons", e) - .addGeometryDetails("original", GeoUtils.createGeometryCollection(polygonGroup)) - .addDetails(() -> "buffer: " + buffer) - .addGeometryDetails("buffered", GeoUtils.createGeometryCollection(buffered)); + // buffer result is sometimes invalid, which makes union throw so fix + // it and try again (see #700) + stats.dataError("buffer_union_unbuffer_union_failed"); + merged = GeometryFixer.fix(merged); + merged = union(merged); } merged = unbuffer(buffer, merged); return merged; diff --git a/planetiler-core/src/test/java/com/onthegomap/planetiler/FeatureMergeTest.java b/planetiler-core/src/test/java/com/onthegomap/planetiler/FeatureMergeTest.java index 32b6527e0e..0fd1628ca3 100644 --- a/planetiler-core/src/test/java/com/onthegomap/planetiler/FeatureMergeTest.java +++ b/planetiler-core/src/test/java/com/onthegomap/planetiler/FeatureMergeTest.java @@ -10,7 +10,9 @@ import com.onthegomap.planetiler.geo.GeometryException; import com.onthegomap.planetiler.geo.GeometryType; import com.onthegomap.planetiler.mbtiles.Mbtiles; +import com.onthegomap.planetiler.stats.Stats; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -20,11 +22,14 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryCollection; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKBReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -853,4 +858,27 @@ void removePointsOutsideBufferMultiPoints() throws GeometryException { ) ); } + + @ParameterizedTest + @ValueSource(strings = { + "/issue_700/exception_1.wkb", + "/issue_700/exception_2.wkb", + "/issue_700/exception_3.wkb", + "/issue_700/exception_4.wkb", + "/issue_700/exception_5.wkb", + "/issue_700/exception_6.wkb", + "/issue_700/exception_7.wkb", + "/issue_700/exception_8.wkb", + "/issue_700/exception_9.wkb", + }) + void testIssue700BufferUnionUnbufferFailure(String path) throws IOException, ParseException { + try (var is = getClass().getResource(path).openStream()) { + GeometryCollection collection = (GeometryCollection) new WKBReader().read(is.readAllBytes()); + List geometries = new ArrayList<>(); + for (int i = 0; i < collection.getNumGeometries(); i++) { + geometries.add(collection.getGeometryN(i)); + } + FeatureMerge.bufferUnionUnbuffer(0.5, geometries, Stats.inMemory()); + } + } } diff --git a/planetiler-core/src/test/resources/issue_700/exception_1.wkb b/planetiler-core/src/test/resources/issue_700/exception_1.wkb new file mode 100644 index 0000000000..4b2108d4f0 Binary files /dev/null and b/planetiler-core/src/test/resources/issue_700/exception_1.wkb differ diff --git a/planetiler-core/src/test/resources/issue_700/exception_2.wkb b/planetiler-core/src/test/resources/issue_700/exception_2.wkb new file mode 100644 index 0000000000..7f4c774fb3 Binary files /dev/null and b/planetiler-core/src/test/resources/issue_700/exception_2.wkb differ diff --git a/planetiler-core/src/test/resources/issue_700/exception_3.wkb b/planetiler-core/src/test/resources/issue_700/exception_3.wkb new file mode 100644 index 0000000000..e75c968489 Binary files /dev/null and b/planetiler-core/src/test/resources/issue_700/exception_3.wkb differ diff --git a/planetiler-core/src/test/resources/issue_700/exception_4.wkb b/planetiler-core/src/test/resources/issue_700/exception_4.wkb new file mode 100644 index 0000000000..f16cc01532 Binary files /dev/null and b/planetiler-core/src/test/resources/issue_700/exception_4.wkb differ diff --git a/planetiler-core/src/test/resources/issue_700/exception_5.wkb b/planetiler-core/src/test/resources/issue_700/exception_5.wkb new file mode 100644 index 0000000000..e259e5f956 Binary files /dev/null and b/planetiler-core/src/test/resources/issue_700/exception_5.wkb differ diff --git a/planetiler-core/src/test/resources/issue_700/exception_6.wkb b/planetiler-core/src/test/resources/issue_700/exception_6.wkb new file mode 100644 index 0000000000..9e14a47124 Binary files /dev/null and b/planetiler-core/src/test/resources/issue_700/exception_6.wkb differ diff --git a/planetiler-core/src/test/resources/issue_700/exception_7.wkb b/planetiler-core/src/test/resources/issue_700/exception_7.wkb new file mode 100644 index 0000000000..ecbbe0b960 Binary files /dev/null and b/planetiler-core/src/test/resources/issue_700/exception_7.wkb differ diff --git a/planetiler-core/src/test/resources/issue_700/exception_8.wkb b/planetiler-core/src/test/resources/issue_700/exception_8.wkb new file mode 100644 index 0000000000..fcf0537bad Binary files /dev/null and b/planetiler-core/src/test/resources/issue_700/exception_8.wkb differ diff --git a/planetiler-core/src/test/resources/issue_700/exception_9.wkb b/planetiler-core/src/test/resources/issue_700/exception_9.wkb new file mode 100644 index 0000000000..b66fca1920 Binary files /dev/null and b/planetiler-core/src/test/resources/issue_700/exception_9.wkb differ