From 0cb551f3147f91af8a658508574d992f2e37eae1 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 5 Mar 2021 11:01:55 -0800 Subject: [PATCH] Fix InteriorPoint to handle partially empty collections Signed-off-by: Martin Davis --- .../jts/algorithm/InteriorPoint.java | 34 ++++++++++++++- .../org/locationtech/jts/geom/Geometry.java | 7 +++- .../testxml/general/TestCentroid.xml | 41 +++++++++++++++---- .../testxml/general/TestInteriorPoint.xml | 38 ++++++++++++++++- 4 files changed, 107 insertions(+), 13 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPoint.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPoint.java index b57b957aae..215ccfa5a8 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPoint.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPoint.java @@ -15,6 +15,9 @@ import org.locationtech.jts.algorithm.construct.MaximumInscribedCircle; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryCollection; +import org.locationtech.jts.geom.GeometryComponentFilter; +import org.locationtech.jts.geom.GeometryFilter; /** * Computes an interior point of a {@link Geometry}. @@ -57,8 +60,12 @@ public static Coordinate getInteriorPoint(Geometry geom) { return null; Coordinate interiorPt = null; - //TODO: determine highest non-empty dimension - int dim = geom.getDimension(); + //int dim = geom.getDimension(); + int dim = effectiveDimension(geom); + // this should not happen, but just in case... + if (dim < 0) { + return null; + } if (dim == 0) { interiorPt = InteriorPointPoint.getInteriorPoint(geom); } @@ -71,4 +78,27 @@ else if (dim == 1) { return interiorPt; } + private static int effectiveDimension(Geometry geom) { + EffectiveDimensionFilter dimFilter = new EffectiveDimensionFilter(); + geom.apply(dimFilter); + return dimFilter.getDimension(); + } + + private static class EffectiveDimensionFilter implements GeometryFilter + { + private int dim = -1; + + public int getDimension() { + return dim; + } + + public void filter(Geometry elem) { + if (elem instanceof GeometryCollection) + return; + if (! elem.isEmpty()) { + int elemDim = elem.getDimension(); + if (elemDim > dim) dim = elemDim; + } + } + } } diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java b/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java index b01c0b85a6..9ba1c8e16d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java @@ -780,10 +780,10 @@ public boolean intersects(Geometry g) { *
  • [0********] (for L/L situations) * * - * For any other combination of dimensions this predicate returns false. + * For the A/A and P/P situations this predicate returns false. *

    * The SFS defined this predicate only for P/L, P/A, L/L, and L/A situations. - * In order to make the relation symmetric, + * To make the relation symmetric * JTS extends the definition to apply to L/P, A/P and A/L situations as well. * *@param g the Geometry with which to compare this Geometry @@ -1857,6 +1857,9 @@ protected boolean equal(Coordinate a, Coordinate b, double tolerance) { private Point createPointFromInternalCoord(Coordinate coord, Geometry exemplar) { + // create empty point for null input + if (coord == null) + return exemplar.getFactory().createPoint(); exemplar.getPrecisionModel().makePrecise(coord); return exemplar.getFactory().createPoint(coord); } diff --git a/modules/tests/src/test/resources/testxml/general/TestCentroid.xml b/modules/tests/src/test/resources/testxml/general/TestCentroid.xml index 8d37d79f1c..c078e8afde 100644 --- a/modules/tests/src/test/resources/testxml/general/TestCentroid.xml +++ b/modules/tests/src/test/resources/testxml/general/TestCentroid.xml @@ -91,6 +91,13 @@ POINT (0 0) + + A - empty + POLYGON EMPTY + + POINT EMPTY + + A - box POLYGON ((40 160, 160 160, 160 40, 40 40, 40 160)) @@ -218,6 +225,33 @@ POINT (10 10) + + GC - collection with empty polygon, line, and point + GEOMETRYCOLLECTION (POLYGON EMPTY, + LINESTRING (20 20, 30 30, 40 40), + MULTIPOINT ((20 10), (10 20)) ) + + POINT (30 30) + + + + GC - collection with empty polygon, empty line, and point + GEOMETRYCOLLECTION (POLYGON EMPTY, + LINESTRING EMPTY, + POINT (10 10) ) + + POINT (10 10) + + + + GC - collection with empty polygon, empty line, and empty point + GEOMETRYCOLLECTION (POLYGON EMPTY, + LINESTRING EMPTY, + POINT EMPTY ) + + POINT EMPTY + + GC - overlapping polygons GEOMETRYCOLLECTION (POLYGON ((20 100, 20 -20, 60 -20, 60 100, 20 100)), @@ -238,13 +272,6 @@ POINT (55 55) - - A - empty - POLYGON EMPTY - - POINT EMPTY - - A - almost degenerate triangle POLYGON(( diff --git a/modules/tests/src/test/resources/testxml/general/TestInteriorPoint.xml b/modules/tests/src/test/resources/testxml/general/TestInteriorPoint.xml index 0d765cacab..ed601d481a 100644 --- a/modules/tests/src/test/resources/testxml/general/TestInteriorPoint.xml +++ b/modules/tests/src/test/resources/testxml/general/TestInteriorPoint.xml @@ -20,10 +20,17 @@ POINT (140 240) + + L - empty + LINESTRING EMPTY + + POINT EMPTY + + L - linestring with single segment LINESTRING (0 0, 7 14) - + POINT (7 14) @@ -119,10 +126,37 @@ GEOMETRYCOLLECTION (POLYGON ((10 10, 10 10, 10 10, 10 10)), LINESTRING (20 20, 20 20), MULTIPOINT ((20 10), (10 20)) ) - + + POINT (10 10) + + + + GC - collection with empty polygon, line, and point + GEOMETRYCOLLECTION (POLYGON EMPTY, + LINESTRING (20 20, 30 30, 40 40), + MULTIPOINT ((20 10), (10 20)) ) + + POINT (30 30) + + + + GC - collection with empty polygon, empty line, and point + GEOMETRYCOLLECTION (POLYGON EMPTY, + LINESTRING EMPTY, + POINT (10 10) ) + POINT (10 10) + + GC - collection with empty polygon, empty line, and empty point + GEOMETRYCOLLECTION (POLYGON EMPTY, + LINESTRING EMPTY, + POINT EMPTY ) + + POINT EMPTY + +