diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsSimpleOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsSimpleOp.java
index cefdb32746..df548320ab 100644
--- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsSimpleOp.java
+++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsSimpleOp.java
@@ -20,6 +20,7 @@
import org.locationtech.jts.algorithm.LineIntersector;
import org.locationtech.jts.algorithm.RobustLineIntersector;
import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.CoordinateArrays;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.LineString;
@@ -43,6 +44,8 @@
*
MultiLineString geometries are simple if
* their elements are simple and they intersect only at points
* which are boundary points of both elements.
@@ -63,7 +66,8 @@
*
* Note that under the Mod-2 rule, closed LineStrings (rings)
* have no boundary.
- * This means that an intersection at their endpoints makes the geometry non-simple.
+ * This means that an intersection at the endpoints of
+ * two closed LineStrings makes the geometry non-simple.
* If it is required to test whether a set of LineString
s touch
* only at their endpoints, use {@link BoundaryNodeRule#ENDPOINT_BOUNDARY_RULE}.
* For example, this can be used to validate that a collection of lines
@@ -281,12 +285,44 @@ private static List extractSegmentStrings(Geometry geom) {
List segStrings = new ArrayList();
for (int i = 0; i < geom.getNumGeometries(); i++) {
LineString line = (LineString) geom.getGeometryN(i);
- SegmentString ss = new BasicSegmentString(line.getCoordinates(), null);
- segStrings.add(ss);
+ Coordinate[] trimPts = trimRepeatedPoints(line.getCoordinates());
+ if (trimPts != null) {
+ SegmentString ss = new BasicSegmentString(trimPts, null);
+ segStrings.add(ss);
+ }
}
return segStrings;
}
+ private static Coordinate[] trimRepeatedPoints(Coordinate[] pts) {
+ if (pts.length <= 2)
+ return pts;
+
+ int len = pts.length;
+ boolean hasRepeatedStart = pts[0].equals2D(pts[1]);
+ boolean hasRepeatedEnd = pts[len - 1].equals2D(pts[len - 2]);
+ if (! hasRepeatedStart && ! hasRepeatedEnd)
+ return pts;
+
+ //-- trim ends
+ int startIndex = 0;
+ Coordinate startPt = pts[0];
+ while (startIndex < len - 1 && startPt.equals2D(pts[startIndex+1])) {
+ startIndex++;
+ }
+ int endIndex = len-1;
+ Coordinate endPt = pts[endIndex];
+ while (endIndex > 0 && endPt.equals2D(pts[endIndex - 1])) {
+ endIndex--;
+ }
+ //-- are all points identical?
+ if (endIndex - startIndex < 1) {
+ return null;
+ }
+ Coordinate[] trimPts = CoordinateArrays.extract(pts, startIndex, endIndex);
+ return trimPts;
+ }
+
private static class NonSimpleIntersectionFinder
implements SegmentIntersector
{
diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsSimpleTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsSimpleTest.java
index 0d4c402809..0da9868c84 100644
--- a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsSimpleTest.java
+++ b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsSimpleTest.java
@@ -98,6 +98,38 @@ public void testRing()
checkIsSimple(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, true );
}
+ public void testLineRepeatedStart() {
+ String a = "LINESTRING (100 100, 100 100, 20 20, 200 20, 100 100)";
+
+ // rings are simple under all rules
+ checkIsSimple(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, true );
+ checkIsSimple(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, true );
+ }
+
+ public void testLineRepeatedEnd() {
+ String a = "LINESTRING (100 100, 20 20, 200 20, 100 100, 100 100)";
+
+ // rings are simple under all rules
+ checkIsSimple(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, true );
+ checkIsSimple(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, true );
+ }
+
+ public void testLineRepeatedBothEnds() {
+ String a = "LINESTRING (100 100, 100 100, 100 100, 20 20, 200 20, 100 100, 100 100)";
+
+ // rings are simple under all rules
+ checkIsSimple(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, true );
+ checkIsSimple(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, true );
+ }
+
+ public void testLineRepeatedAll() {
+ String a = "LINESTRING (100 100, 100 100, 100 100)";
+
+ // rings are simple under all rules
+ checkIsSimple(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, true );
+ checkIsSimple(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, true );
+ }
+
public void testLinesAll() {
checkIsSimpleAll("MULTILINESTRING ((10 20, 90 20), (10 30, 90 30), (50 40, 50 10))",
BoundaryNodeRule.MOD2_BOUNDARY_RULE,
diff --git a/modules/tests/src/test/resources/testxml/general/TestSimple.xml b/modules/tests/src/test/resources/testxml/general/TestSimple.xml
index b5e8333124..6dcc56241e 100644
--- a/modules/tests/src/test/resources/testxml/general/TestSimple.xml
+++ b/modules/tests/src/test/resources/testxml/general/TestSimple.xml
@@ -61,6 +61,54 @@
+
+ L - simple line - repeated start point
+
+ LINESTRING(10 10, 10 10, 20 20)
+
+
+
+ true
+
+
+
+
+
+ L - simple line - repeated end point
+
+ LINESTRING(10 10, 20 20, 20 20)
+
+
+
+ true
+
+
+
+
+
+ L - simple line - repeated points at both ends
+
+ LINESTRING(10 10, 10 10, 20 20, 20 20)
+
+
+
+ true
+
+
+
+
+
+ L - simple line - zerolength
+
+ LINESTRING(10 10, 10 10, 10 10)
+
+
+
+ true
+
+
+
+
L - non-simple, proper interior intersection