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 e82125c123..85e364c403 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 @@ -151,9 +151,8 @@ else if (ptCount > 0){ return cent; } - private void setBasePoint(Coordinate basePt) + private void setAreaBasePoint(Coordinate basePt) { - if (this.areaBasePt == null) this.areaBasePt = basePt; } @@ -168,7 +167,7 @@ private void add(Polygon poly) private void addShell(Coordinate[] pts) { if (pts.length > 0) - setBasePoint(pts[0]); + setAreaBasePoint(pts[0]); boolean isPositiveArea = ! CGAlgorithms.isCCW(pts); for (int i = 0; i < pts.length - 1; i++) { addTriangle(areaBasePt, pts[i], pts[i+1], isPositiveArea); diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/CentroidTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/CentroidTest.java new file mode 100644 index 0000000000..86b48a1454 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/CentroidTest.java @@ -0,0 +1,50 @@ +package org.locationtech.jts.algorithm; + +import junit.framework.TestCase; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.io.WKTReader; + +public class CentroidTest extends TestCase { + + private static final double TOLERANCE = 1e-10; + + public CentroidTest(String name) { + super(name); + } + + /** Compute the centroid of a geometry as an area-weighted average of the centroids + * of its components. + * + * @param g a polygonal geometry + * @return Coordinate of the geometry's centroid + */ + private static Coordinate areaWeightedCentroid(Geometry g) { + double totalArea = g.getArea(); + double cx = 0; + double cy = 0; + + for(int i = 0; i < g.getNumGeometries(); i++) { + Geometry component = g.getGeometryN(i); + double areaFraction = component.getArea() / totalArea; + + Coordinate componentCentroid = component.getCentroid().getCoordinate(); + + cx += areaFraction * componentCentroid.x; + cy += areaFraction * componentCentroid.y; + } + + return new Coordinate(cx, cy); + } + + public void testCentroidMultiPolygon() throws Exception { + // Verify that the computed centroid of a MultiPolygon is equivalent to the + // area-weighted average of its components. + Geometry g = new WKTReader().read( + "MULTIPOLYGON ((( -92.661322 36.58994900000003, -92.66132199999993 36.58994900000005, -92.66132199999993 36.589949000000004, -92.661322 36.589949, -92.661322 36.58994900000003)), (( -92.65560500000008 36.58708800000005, -92.65560499999992 36.58708800000005, -92.65560499998745 36.587087999992576, -92.655605 36.587088, -92.65560500000008 36.58708800000005 )), (( -92.65512450000065 36.586800000000466, -92.65512449999994 36.58680000000004, -92.65512449998666 36.5867999999905, -92.65512450000065 36.586800000000466 )))" + ); + + assertTrue(areaWeightedCentroid(g).equals2D(g.getCentroid().getCoordinate(), TOLERANCE)); + } + +} \ No newline at end of file diff --git a/modules/tests/src/test/resources/testxml/general/TestCentroid.xml b/modules/tests/src/test/resources/testxml/general/TestCentroid.xml index 7ddc4e93a3..068b6177f3 100644 --- a/modules/tests/src/test/resources/testxml/general/TestCentroid.xml +++ b/modules/tests/src/test/resources/testxml/general/TestCentroid.xml @@ -242,6 +242,30 @@ POINT (56.52883333335 25.21033333335) - + + A - almost degenerate MultiPolygon + + MULTIPOLYGON ((( + -92.661322 36.58994900000003, + -92.66132199999993 36.58994900000005, + -92.66132199999993 36.589949000000004, + -92.661322 36.589949, + -92.661322 36.58994900000003)), + (( + -92.65560500000008 36.58708800000005, + -92.65560499999992 36.58708800000005, + -92.65560499998745 36.587087999992576, + -92.655605 36.587088, + -92.65560500000008 36.58708800000005 + )), + (( + -92.65512450000065 36.586800000000466, + -92.65512449999994 36.58680000000004, + -92.65512449998666 36.5867999999905, + -92.65512450000065 36.586800000000466 + ))) + + POINT (-92.6553838608954 36.58695407733924) +