diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/Centroid.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/Centroid.java
index d8aa8558d4..50712ec4a0 100644
--- a/modules/core/src/main/java/org/locationtech/jts/algorithm/Centroid.java
+++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/Centroid.java
@@ -15,8 +15,6 @@
import org.locationtech.jts.geom.Coordinate;
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;
/**
@@ -92,22 +90,22 @@ private void add(Geometry geom)
{
if (geom.isEmpty())
return;
- if (geom instanceof Point) {
+ if (geom instanceof GeometryCollection) {
+ GeometryCollection gc = (GeometryCollection) geom;
+ for (int i = 0; i < gc.getNumGeometries(); i++) {
+ add(gc.getGeometryN(i));
+ }
+ }
+ else if (geom.getDimension() == 0) {
addPoint(geom.getCoordinate());
}
- else if (geom instanceof LineString) {
+ else if (geom.getDimension() == 1) {
addLineSegments(geom.getCoordinates());
}
- else if (geom instanceof Polygon) {
+ else if (geom.getDimension() == 2) {
Polygon poly = (Polygon) geom;
add(poly);
}
- else if (geom instanceof GeometryCollection) {
- GeometryCollection gc = (GeometryCollection) geom;
- for (int i = 0; i < gc.getNumGeometries(); i++) {
- add(gc.getGeometryN(i));
- }
- }
}
/**
diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointLine.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointLine.java
index 1971d5da6f..c550758083 100644
--- a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointLine.java
+++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointLine.java
@@ -57,7 +57,7 @@ public Coordinate getInteriorPoint()
*/
private void addInterior(Geometry geom)
{
- if (geom instanceof LineString) {
+ if (geom.getDimension() == 1) {
addInterior(geom.getCoordinates());
}
else if (geom instanceof GeometryCollection) {
diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointPoint.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointPoint.java
index 71e14ce811..30afc1719a 100644
--- a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointPoint.java
+++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointPoint.java
@@ -15,7 +15,6 @@
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
-import org.locationtech.jts.geom.Point;
/**
* Computes a point in the interior of an point geometry.
@@ -44,15 +43,15 @@ public InteriorPointPoint(Geometry g)
*/
private void add(Geometry geom)
{
- if (geom instanceof Point) {
- add(geom.getCoordinate());
- }
- else if (geom instanceof GeometryCollection) {
+ if (geom instanceof GeometryCollection) {
GeometryCollection gc = (GeometryCollection) geom;
for (int i = 0; i < gc.getNumGeometries(); i++) {
add(gc.getGeometryN(i));
}
}
+ else if (geom.getDimension() == 0) {
+ add(geom.getCoordinate());
+ }
}
private void add(Coordinate point)
{
diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/LineString.java b/modules/core/src/main/java/org/locationtech/jts/geom/LineString.java
index 13624b3859..84768c38de 100644
--- a/modules/core/src/main/java/org/locationtech/jts/geom/LineString.java
+++ b/modules/core/src/main/java/org/locationtech/jts/geom/LineString.java
@@ -102,11 +102,16 @@ public Coordinate getCoordinate()
}
public int getDimension() {
- return 1;
+ // Test if geometry is non empty and has several distinct coordinates
+ if (new CoordinateList(points.toCoordinateArray(),false).size()==1)
+ return 0;
+ else
+ // valid or empty LineString return 1
+ return 1;
}
public int getBoundaryDimension() {
- if (isClosed()) {
+ if (isClosed() || getDimension() == 0) {
return Dimension.FALSE;
}
return 0;
diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/MultiLineString.java b/modules/core/src/main/java/org/locationtech/jts/geom/MultiLineString.java
index 15ce502a0b..f7ec4dc50f 100644
--- a/modules/core/src/main/java/org/locationtech/jts/geom/MultiLineString.java
+++ b/modules/core/src/main/java/org/locationtech/jts/geom/MultiLineString.java
@@ -55,10 +55,16 @@ public MultiLineString(LineString[] lineStrings, GeometryFactory factory) {
super(lineStrings, factory);
}
+
public int getDimension() {
- return 1;
+ if (isEmpty())
+ return 1;
+ else
+ // super will get the heighest dimension of components
+ return super.getDimension();
}
+
public int getBoundaryDimension() {
if (isClosed()) {
return Dimension.FALSE;
diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/MultiPolygon.java b/modules/core/src/main/java/org/locationtech/jts/geom/MultiPolygon.java
index 67df633248..41a4a09404 100644
--- a/modules/core/src/main/java/org/locationtech/jts/geom/MultiPolygon.java
+++ b/modules/core/src/main/java/org/locationtech/jts/geom/MultiPolygon.java
@@ -67,7 +67,11 @@ public MultiPolygon(Polygon[] polygons, GeometryFactory factory) {
}
public int getDimension() {
- return 2;
+ if (isEmpty())
+ return 2;
+ else
+ // super will get the heighest dimension of components
+ return super.getDimension();
}
public int getBoundaryDimension() {
diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/Polygon.java b/modules/core/src/main/java/org/locationtech/jts/geom/Polygon.java
index bf389c6460..3ef47963c4 100644
--- a/modules/core/src/main/java/org/locationtech/jts/geom/Polygon.java
+++ b/modules/core/src/main/java/org/locationtech/jts/geom/Polygon.java
@@ -164,11 +164,20 @@ public int getNumPoints() {
}
public int getDimension() {
- return 2;
+ if (getExteriorRing().getDimension() == 0)
+ return 0;
+ else
+ // Does not handle the case of 1-dimension collapsed polygon
+ // if exteriorRing is empty, returns 2 (not sure why)
+ return 2;
}
public int getBoundaryDimension() {
- return 1;
+ if (isEmpty())
+ // Return the same dimension as an empty LinearRing
+ return 1;
+ else
+ return getExteriorRing().getDimension();
}
public boolean isEmpty() {
diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/GeometryGraph.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/GeometryGraph.java
index caa8e83175..c583ffd840 100644
--- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/GeometryGraph.java
+++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/GeometryGraph.java
@@ -292,6 +292,10 @@ private void addLineString(LineString line)
if (coord.length < 2) {
hasTooFewPoints = true;
invalidPoint = coord[0];
+ // If line is made of a single point, geometry is invalid (hasTooFewPoint),
+ // but the point must still be added to the graph in order to be able to be
+ // compute correct predicate (e.g. intersects) with another GeometryGraphe
+ addPoint(coord[0]);
return;
}
diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/InteriorPointTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/InteriorPointTest.java
index 1419157229..f4ff867675 100644
--- a/modules/core/src/test/java/org/locationtech/jts/algorithm/InteriorPointTest.java
+++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/InteriorPointTest.java
@@ -89,4 +89,34 @@ private void checkInteriorPoint(Geometry g)
assertTrue(g.contains(ip));
}
+ /**
+ public void testPointInteriorPoint() throws ParseException {
+ Geometry point = rdr.read("Point(10 10)");
+ assertTrue(point.getInteriorPoint().equals(rdr.read("POINT(10 10)")));
+ }
+
+ public void testMultiPointInteriorPoint() throws ParseException {
+ Geometry point = rdr.read("MULTIPOINT ((60 300), (200 200), (240 240), (200 300), (40 140), (80 240), (140 240), (100 160), (140 200), (60 200))");
+ assertTrue(point.getInteriorPoint().equals(rdr.read("POINT (140 240)")));
+ }
+
+ public void testRelate() throws ParseException {
+ Geometry point = rdr.read("POINT (10 10)");
+ Geometry line = rdr.read("LINESTRING (10 10, 10 10)");
+ assertTrue(point.equalsTopo(line));
+ }
+
+ public void testGeometryCollection() throws ParseException {
+ Geometry gc = rdr.read("GEOMETRYCOLLECTION (POLYGON ((10 10, 10 10, 10 10, 10 10)), \n" +
+ " LINESTRING (20 20, 30 30))");
+ assertTrue(gc.getInteriorPoint().equals(rdr.read("POINT(20 20)")));
+ }
+
+
+ public void testZeroLengthLineStringInteriorPoint() throws ParseException {
+ Geometry line = rdr.read("LineString(10 10, 10 10)");
+ assertTrue(line.getInteriorPoint().equals(rdr.read("POINT(10 10)")));
+ }
+ **/
+
}
diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/CollapsedGeometryDimensionTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/CollapsedGeometryDimensionTest.java
new file mode 100644
index 0000000000..5c84d91499
--- /dev/null
+++ b/modules/core/src/test/java/org/locationtech/jts/geom/CollapsedGeometryDimensionTest.java
@@ -0,0 +1,118 @@
+package org.locationtech.jts.geom;
+
+import junit.framework.TestCase;
+import junit.textui.TestRunner;
+import org.locationtech.jts.io.ParseException;
+import org.locationtech.jts.io.WKTReader;
+
+
+public class CollapsedGeometryDimensionTest extends TestCase {
+
+ public static void main(String args[]) {
+ TestRunner.run(CollapsedGeometryDimensionTest.class);
+ }
+
+ private GeometryFactory factory = new GeometryFactory();
+ private WKTReader reader = new WKTReader(factory);
+
+ public CollapsedGeometryDimensionTest(String name) { super(name); }
+
+ public void testMock()
+ {
+ assertTrue(true);
+ }
+
+ public void testPoint() throws ParseException
+ {
+ // empty
+ assertTrue(reader.read("Point Empty").getDimension() == 0);
+ // normal
+ assertTrue(reader.read("Point(0 0)").getDimension() == 0);
+ // collapsed
+ assertTrue(reader.read("Point Empty").getDimension() == 0);
+ }
+
+ public void testLineString() throws ParseException
+ {
+ // empty
+ assertTrue(reader.read("LineString Empty").getDimension() == 1);
+ // normal (open/closed)
+ assertTrue(reader.read("LineString(0 0, 1 0)").getDimension() == 1);
+ assertTrue(reader.read("LineString(0 0, 1 0, 0.5 0.5, 0 0)").getDimension() == 1);
+ // collapsed
+ assertTrue(reader.read("LineString(0 0, 0 0)").getDimension() == 0);
+ }
+
+ public void testPolygon() throws ParseException
+ {
+ // empty
+ assertTrue(reader.read("Polygon Empty").getDimension() == 2);
+ // normal
+ assertTrue(reader.read("Polygon((0 0, 1 0, 0.5 0.5, 0 0))").getDimension() == 2);
+ // collapsed in dimension 1 (not yet detected -> dimension 2)
+ assertTrue(reader.read("Polygon((0 0, 1 0, 2 0, 0 0))").getDimension() == 2);
+ // collapsed
+ assertTrue(reader.read("Polygon((0 0, 0 0, 0 0, 0 0))").getDimension() == 0);
+ // cannot be initialized (linear ring not closed)
+ //assertTrue(reader.read("Polygon((0 0, 1 0, 1 0, 1 0))").getDimension() == 1);
+ }
+
+ public void testLinearRing() throws ParseException
+ {
+ // empty
+ assertTrue(reader.read("LinearRing Empty").getDimension() == 1);
+ // normal
+ assertTrue(reader.read("LinearRing(0 0, 1 0, 0.5 0.5, 0 0)").getDimension() == 1);
+ assertTrue(reader.read("LinearRing(0 0, 1 0, 2 0, 0 0)").getDimension() == 1);
+ // collapsed
+ assertTrue(reader.read("LinearRing(0 0, 0 0, 0 0, 0 0)").getDimension() == 0);
+ }
+
+ public void testMultiPoint() throws ParseException
+ {
+ // empty
+ assertTrue(reader.read("MultiPoint Empty").getDimension() == 0);
+ // normal
+ assertTrue(reader.read("MultiPoint((0 0), (1 1))").getDimension() == 0);
+ // normal
+ assertTrue(reader.read("MultiPoint((0 0), (0 0))").getDimension() == 0);
+ }
+
+ public void testMultiLineString() throws ParseException
+ {
+ // empty
+ assertTrue(reader.read("MultiLineString Empty").getDimension() == 1);
+ // normal (open/closed)
+ assertTrue(reader.read("MultiLineString((0 0, 1 0), (2 0, 3 0))").getDimension() == 1);
+ assertTrue(reader.read("MultiLineString((0 0, 1 0, 0.5 0.5, 0 0))").getDimension() == 1);
+ // partially collapsed
+ assertTrue(reader.read("MultiLineString((0 0, 0 0), (1 0, 2 0))").getDimension() == 1);
+ // collapsed
+ assertTrue(reader.read("MultiLineString((0 0, 0 0), (1 0, 1 0))").getDimension() == 0);
+ }
+
+ public void testMultiPolygon() throws ParseException
+ {
+ // empty
+ assertTrue(reader.read("MultiPolygon Empty").getDimension() == 2);
+ // normal
+ assertTrue(reader.read("MultiPolygon(((0 0, 1 0, 0.5 0.5, 0 0)),((10 0, 11 0, 10.5 0.5, 10 0)))").getDimension() == 2);
+ // partially collapsed
+ assertTrue(reader.read("MultiPolygon(((0 0, 1 0, 0.5 0.5, 0 0)),((0 0, 0 0, 0 0, 0 0)))").getDimension() == 2);
+ // collapsed
+ assertTrue(reader.read("MultiPolygon(((0 0, 0 0, 0 0, 0 0)),((1 1, 1 1, 1 1, 1 1)))").getDimension() == 0);
+ }
+
+ public void tesGeometryCollection() throws ParseException
+ {
+ // empty
+ assertTrue(reader.read("GeometryCollection Empty").getDimension() == 0);
+ // normal
+ assertTrue(reader.read("GeometryCollection (Point(0 0),LineString(0 0, 1 0)").getDimension() == 1);
+ assertTrue(reader.read("GeometryCollection (Point(0 0),LineString(0 0, 1 0), Polygon((0 0, 1 0, 0.5 0.5, 0 0))").getDimension() == 2);
+ // collapsed
+ assertTrue(reader.read("GeometryCollection (Point(0 0),LineString(0 0, 0 0)").getDimension() == 0);
+ assertTrue(reader.read("GeometryCollection (Point(0 0),Polygon((0 0, 0 0, 0 0, 0 0))").getDimension() == 0);
+ }
+
+}
diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/relate/ZeroLengthLineStringTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/relate/ZeroLengthLineStringTest.java
new file mode 100644
index 0000000000..cf8b3c6110
--- /dev/null
+++ b/modules/core/src/test/java/org/locationtech/jts/operation/relate/ZeroLengthLineStringTest.java
@@ -0,0 +1,135 @@
+package org.locationtech.jts.operation.relate;
+
+import junit.framework.TestCase;
+import junit.textui.TestRunner;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.io.WKTReader;
+
+public class ZeroLengthLineStringTest extends TestCase {
+
+ public static void main(String args[]) {
+ TestRunner.run(ZeroLengthLineStringTest.class);
+ }
+
+ private GeometryFactory factory = new GeometryFactory();
+ private WKTReader reader = new WKTReader(factory);
+
+ public ZeroLengthLineStringTest(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * From JTS #345
+ *
+ * 0-length LineString is invalid (not clear from the spec, refers
+ * to the ticket about this question)
+ *
+ * @throws Exception
+ */
+ public void testZeroLengthLineStringInvalid()
+ throws Exception
+ {
+ String a = "LINESTRING (0 0, 0 0)";
+ Geometry geom1 = reader.read(a);
+ assertTrue(!geom1.isValid());
+ }
+
+
+ /**
+ * From JTS #345
+ *
+ * Intersects a geom with itself should return true, even if geom
+ * is a 0-length (degenerated) LineString
+ *
+ * @throws Exception
+ */
+ public void testIntersectsZeroLengthLineStringWithItself()
+ throws Exception
+ {
+ String a = "LINESTRING (0 0, 0 0)";
+ Geometry geom = reader.read(a);
+ assertTrue(geom.intersects(geom));
+ }
+
+ /**
+ * From JTS #345
+ *
+ * Intersects geom with a buffer around it should return true,
+ * even if geom is a 0-length (degenerated) LineString
+ *
+ * @throws Exception
+ */
+ public void testIntersectsZeroLengthLineStringWithBuffer()
+ throws Exception
+ {
+ String a = "LINESTRING (0 0, 0 0)";
+ Geometry geom = reader.read(a);
+ assertTrue(geom.intersects(geom.buffer(1.0)));
+ }
+
+ /**
+ * From JTS #345
+ *
+ * Boundary of a zero-length LineString is empty
+ *
+ * @throws Exception
+ */
+ public void testZeroLengthLineStringBoundary()
+ throws Exception
+ {
+ String a = "LINESTRING (0 0, 0 0)";
+ Geometry geom = reader.read(a);
+ assertTrue(geom.getBoundary().isEmpty());
+ }
+
+ /**
+ * From JTS #345
+ *
+ * Intersects a valid LineString with a 0-dimensional LineString
+ * located on one of its boundary should return true
+ *
+ * @throws Exception
+ */
+ public void testIntersectsBetweenLineStringAndItsBoundary()
+ throws Exception
+ {
+ String a = "LINESTRING (0 0, 1 0)";
+ String b = "LINESTRING (0 0, 0 0)";
+ Geometry geom1 = reader.read(a);
+ Geometry geom2 = reader.read(b);
+ assertTrue(geom1.intersects(geom2));
+ }
+
+ /**
+ * From JTS #345
+ *
+ * WARNING touches between a LineString and a 0-length LineString lying
+ * on its boundary returns false but it should return true as for the
+ * punctal case.
+ * The test with 0-length linestring is deactivated.
+ * One way to return true as for the Point is to say that 0-length LineString
+ * has dimension 1. IMHO, it LineString#getDimension() should return one, but
+ * it breaks other tests which check that empty LineString#getDimension return
+ * 1 (which can probably be discussed).
+ *
+ * @throws Exception
+ */
+
+ public void testTouchesBetweenLineStringAndItsBoundary()
+ throws Exception
+ {
+ String a = "LINESTRING (0 0, 1 0)";
+ String b = "LINESTRING (0 0, 0 0)";
+ String c = "POINT (0 0)";
+ Geometry geom1 = reader.read(a);
+ Geometry geom2 = reader.read(b);
+ Geometry geom3 = reader.read(c);
+ assertTrue(geom1.touches(geom2));
+ assertTrue(geom2.touches(geom1));
+ assertTrue(geom1.touches(geom3));
+ assertTrue(geom3.touches(geom1));
+ }
+
+}
diff --git a/modules/tests/src/test/resources/testxml/general/TestInteriorPoint.xml b/modules/tests/src/test/resources/testxml/general/TestInteriorPoint.xml
index 0d765cacab..9368171187 100644
--- a/modules/tests/src/test/resources/testxml/general/TestInteriorPoint.xml
+++ b/modules/tests/src/test/resources/testxml/general/TestInteriorPoint.xml
@@ -103,7 +103,9 @@
GEOMETRYCOLLECTION (POLYGON ((10 10, 10 10, 10 10, 10 10)),
LINESTRING (20 20, 30 30))
- POINT (10 10)
+
+ POINT (20 20)
diff --git a/modules/tests/src/test/resources/testxml/validate/TestRelatePL.xml b/modules/tests/src/test/resources/testxml/validate/TestRelatePL.xml
index 2e11fb7b9e..d24c7f2f43 100644
--- a/modules/tests/src/test/resources/testxml/validate/TestRelatePL.xml
+++ b/modules/tests/src/test/resources/testxml/validate/TestRelatePL.xml
@@ -40,7 +40,9 @@
true
false
false
- false
+
+ true
true
false
false