From 76de567c83486b7a022fa469c0c49c1650597d68 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 15 May 2024 21:46:44 -0700 Subject: [PATCH] Optimize geometry zero-len line computation --- .../operation/relateng/RelateGeometry.java | 78 ++++++++++++------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/RelateGeometry.java b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/RelateGeometry.java index 8c7335ed24..7fc6dbb8b3 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/relateng/RelateGeometry.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/relateng/RelateGeometry.java @@ -48,7 +48,7 @@ public static String name(boolean isA) { private Geometry geom; private boolean isPrepared = false; - private int dim = Dimension.FALSE; + private int geomDim = Dimension.FALSE; private List pts; private Set uniquePoints; private BoundaryNodeRule boundaryNodeRule; @@ -57,6 +57,8 @@ public static String name(boolean isA) { private boolean hasPoints; private boolean hasLines; private boolean hasAreas; + private boolean isLineZeroLen; + private boolean isGeomEmpty; public RelateGeometry(Geometry input) { this(input, false, BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE); @@ -70,27 +72,30 @@ public RelateGeometry(Geometry input, boolean isPrepared, BoundaryNodeRule bnRul this.geom = input; this.isPrepared = isPrepared; this.boundaryNodeRule = bnRule; - dim = input.getDimension(); + //-- cache geometry metadata + isGeomEmpty = geom.isEmpty(); + isLineZeroLen = isZeroLength(geom); + geomDim = input.getDimension(); analyzeDimensions(); } private void analyzeDimensions() { - if (geom.isEmpty()) { + if (isGeomEmpty) { return; } if (geom instanceof Point || geom instanceof MultiPoint) { hasPoints = true; - dim = Dimension.P; + geomDim = Dimension.P; return; } if (geom instanceof LineString || geom instanceof MultiLineString) { hasLines = true; - dim = Dimension.L; + geomDim = Dimension.L; return; } if (geom instanceof Polygon || geom instanceof MultiPolygon) { hasAreas = true; - dim = Dimension.A; + geomDim = Dimension.A; return; } //-- analyze a (possibly mixed type) collection @@ -101,19 +106,48 @@ private void analyzeDimensions() { continue; if (elem instanceof Point) { hasPoints = true; - if (dim < Dimension.P) dim = Dimension.P; + if (geomDim < Dimension.P) geomDim = Dimension.P; } if (elem instanceof LineString) { hasLines = true; - if (dim < Dimension.L) dim = Dimension.L; + if (geomDim < Dimension.L) geomDim = Dimension.L; } if (elem instanceof Polygon) { hasAreas = true; - if (dim < Dimension.A) dim = Dimension.A; - } + if (geomDim < Dimension.A) geomDim = Dimension.A; + } } } + /** + * Tests if geometry linear elements are zero-length. + * The test is optimized to + * avoid computing length in the common case, since that is expensive. + * + * @param geom + * @return + */ + private boolean isZeroLength(Geometry geom) { + Iterator geomi = new GeometryCollectionIterator(geom); + while (geomi.hasNext()) { + Geometry elem = (Geometry) geomi.next(); + if (elem instanceof LineString) { + LineString line = (LineString) elem; + if (line.getNumPoints() >= 2) { + Coordinate p0 = line.getCoordinateN(0); + Coordinate p1 = line.getCoordinateN(1); + //-- the usual non-zero-length case will have first two points non-equal + if (! p0.equals2D(p1) + || line.getLength() > 0) { + return false; + } + } + } + } + return true; + } + + public Geometry getGeometry() { return geom; } @@ -127,7 +161,7 @@ public Envelope getEnvelope() { } public int getDimension() { - return dim; + return geomDim; } public boolean hasDimension(int dim) { @@ -140,8 +174,8 @@ public boolean hasDimension(int dim) { } public int getDimensionEffective() { - if (geom.isEmpty()) return Dimension.FALSE; - if (getDimension() == 1 && geom.getLength() == 0) + if (isGeomEmpty) return Dimension.FALSE; + if (getDimension() == 1 && isLineZeroLen) return Dimension.P; if (hasAreas) return Dimension.A; if (hasLines) return Dimension.L; @@ -152,11 +186,6 @@ public boolean hasEdges() { return hasLines || hasAreas; } - public boolean isZeroLength() { - //TODO: evaluate component-wise and short-circuit - return geom.getLength() <= 0; - } - private RelatePointLocator getLocator() { if (locator == null) locator = new RelatePointLocator(geom, isPrepared, boundaryNodeRule); @@ -219,7 +248,7 @@ public boolean isPolygonal() { } public boolean isEmpty() { - return geom.isEmpty(); + return isGeomEmpty; } public boolean hasBoundary() { @@ -235,19 +264,12 @@ public Set getUniquePoints() { } private Set createUniquePoints() { - //TODO: make more efficient (ie by scanning geometry?) - List pts = getCoordinates(); + //-- only called on P geometries + List pts = ComponentCoordinateExtracter.getCoordinates(geom); Set set = new HashSet(); set.addAll(pts); return set; } - - public List getCoordinates() { - if (pts == null) { - pts = ComponentCoordinateExtracter.getCoordinates(geom); - } - return pts; - } public List getEffectivePoints() { List ptListAll = PointExtracter.getPoints(geom);