Skip to content

Commit 8e390b9

Browse files
authored
centroid fixes (#227)
1 parent 8070e1e commit 8e390b9

6 files changed

Lines changed: 238 additions & 174 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,5 @@ Thumbs.db
3838
target/*
3939
/bin/
4040
/target/
41+
42+
.metadata/

.travis.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
language: java
22
jdk:
3-
- openjdk7
4-
- openjdk8
5-
- oraclejdk8
6-
- oraclejdk9
3+
- openjdk9
4+
- openjdk10
5+
- openjdk14
6+
# - oraclejdk9
7+
- oraclejdk11
78
notifications:
89
email: false

pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@
9494
<properties>
9595
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
9696

97-
<java.source.version>1.6</java.source.version>
98-
<java.target.version>1.6</java.target.version>
97+
<java.source.version>1.7</java.source.version>
98+
<java.target.version>1.7</java.target.version>
9999

100100
<!-- dependency versions -->
101101
<jackson.version>2.9.6</jackson.version>
@@ -194,7 +194,7 @@
194194
<plugin>
195195
<groupId>org.sonatype.plugins</groupId>
196196
<artifactId>nexus-staging-maven-plugin</artifactId>
197-
<version>1.6.2</version>
197+
<version>1.6.8</version>
198198
<extensions>true</extensions>
199199
<configuration>
200200
<serverId>ossrh</serverId>
Lines changed: 64 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 1995-2017 Esri
2+
Copyright 1995-2019 Esri
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -23,8 +23,6 @@
2323
*/
2424
package com.esri.core.geometry;
2525

26-
import static java.lang.Math.sqrt;
27-
2826
public class OperatorCentroid2DLocal extends OperatorCentroid2D {
2927
@Override
3028
public Point2D execute(Geometry geometry, ProgressTracker progressTracker) {
@@ -56,7 +54,7 @@ private static Point2D computeLineCentroid(Line line) {
5654
}
5755

5856
// Points centroid is arithmetic mean of the input points
59-
private static Point2D computePointsCentroid(MultiPoint multiPoint) {
57+
private static Point2D computePointsCentroid(MultiVertexGeometry multiPoint) {
6058
double xSum = 0;
6159
double ySum = 0;
6260
int pointCount = multiPoint.getPointCount();
@@ -71,89 +69,75 @@ private static Point2D computePointsCentroid(MultiPoint multiPoint) {
7169

7270
// Lines centroid is weighted mean of each line segment, weight in terms of line
7371
// length
74-
private static Point2D computePolylineCentroid(Polyline polyline) {
75-
double xSum = 0;
76-
double ySum = 0;
77-
double weightSum = 0;
78-
79-
Point2D startPoint = new Point2D();
80-
Point2D endPoint = new Point2D();
81-
for (int i = 0; i < polyline.getPathCount(); i++) {
82-
polyline.getXY(polyline.getPathStart(i), startPoint);
83-
polyline.getXY(polyline.getPathEnd(i) - 1, endPoint);
84-
double dx = endPoint.x - startPoint.x;
85-
double dy = endPoint.y - startPoint.y;
86-
double length = sqrt(dx * dx + dy * dy);
87-
weightSum += length;
88-
xSum += (startPoint.x + endPoint.x) * length / 2;
89-
ySum += (startPoint.y + endPoint.y) * length / 2;
90-
}
91-
return new Point2D(xSum / weightSum, ySum / weightSum);
92-
}
93-
94-
// Polygon centroid: area weighted average of centroids in case of holes
95-
private static Point2D computePolygonCentroid(Polygon polygon) {
96-
int pathCount = polygon.getPathCount();
97-
98-
if (pathCount == 1) {
99-
return getPolygonSansHolesCentroid(polygon);
72+
private static Point2D computePolylineCentroid(MultiPath polyline) {
73+
double totalLength = polyline.calculateLength2D();
74+
if (totalLength == 0) {
75+
return computePointsCentroid(polyline);
10076
}
101-
102-
double xSum = 0;
103-
double ySum = 0;
104-
double areaSum = 0;
105-
106-
for (int i = 0; i < pathCount; i++) {
107-
int startIndex = polygon.getPathStart(i);
108-
int endIndex = polygon.getPathEnd(i);
109-
110-
Polygon sansHoles = getSubPolygon(polygon, startIndex, endIndex);
111-
112-
Point2D centroid = getPolygonSansHolesCentroid(sansHoles);
113-
double area = sansHoles.calculateArea2D();
114-
115-
xSum += centroid.x * area;
116-
ySum += centroid.y * area;
117-
areaSum += area;
77+
78+
MathUtils.KahanSummator xSum = new MathUtils.KahanSummator(0);
79+
MathUtils.KahanSummator ySum = new MathUtils.KahanSummator(0);
80+
Point2D point = new Point2D();
81+
SegmentIterator iter = polyline.querySegmentIterator();
82+
while (iter.nextPath()) {
83+
while (iter.hasNextSegment()) {
84+
Segment seg = iter.nextSegment();
85+
seg.getCoord2D(0.5, point);
86+
double length = seg.calculateLength2D();
87+
point.scale(length);
88+
xSum.add(point.x);
89+
ySum.add(point.y);
90+
}
11891
}
119-
120-
return new Point2D(xSum / areaSum, ySum / areaSum);
92+
93+
return new Point2D(xSum.getResult() / totalLength, ySum.getResult() / totalLength);
12194
}
12295

123-
private static Polygon getSubPolygon(Polygon polygon, int startIndex, int endIndex) {
124-
Polyline boundary = new Polyline();
125-
boundary.startPath(polygon.getPoint(startIndex));
126-
for (int i = startIndex + 1; i < endIndex; i++) {
127-
Point current = polygon.getPoint(i);
128-
boundary.lineTo(current);
96+
// Polygon centroid: area weighted average of centroids
97+
private static Point2D computePolygonCentroid(Polygon polygon) {
98+
double totalArea = polygon.calculateArea2D();
99+
if (totalArea == 0)
100+
{
101+
return computePolylineCentroid(polygon);
129102
}
130-
131-
final Polygon newPolygon = new Polygon();
132-
newPolygon.add(boundary, false);
133-
return newPolygon;
134-
}
135-
136-
// Polygon sans holes centroid:
137-
// c[x] = (Sigma(x[i] + x[i + 1]) * (x[i] * y[i + 1] - x[i + 1] * y[i]), for i =
138-
// 0 to N - 1) / (6 * signedArea)
139-
// c[y] = (Sigma(y[i] + y[i + 1]) * (x[i] * y[i + 1] - x[i + 1] * y[i]), for i =
140-
// 0 to N - 1) / (6 * signedArea)
141-
private static Point2D getPolygonSansHolesCentroid(Polygon polygon) {
142-
int pointCount = polygon.getPointCount();
143-
double xSum = 0;
144-
double ySum = 0;
145-
double signedArea = 0;
146-
103+
104+
MathUtils.KahanSummator xSum = new MathUtils.KahanSummator(0);
105+
MathUtils.KahanSummator ySum = new MathUtils.KahanSummator(0);
106+
Point2D startPoint = new Point2D();
147107
Point2D current = new Point2D();
148108
Point2D next = new Point2D();
149-
for (int i = 0; i < pointCount; i++) {
150-
polygon.getXY(i, current);
151-
polygon.getXY((i + 1) % pointCount, next);
152-
double ladder = current.x * next.y - next.x * current.y;
153-
xSum += (current.x + next.x) * ladder;
154-
ySum += (current.y + next.y) * ladder;
155-
signedArea += ladder / 2;
109+
Point2D origin = polygon.getXY(0);
110+
111+
for (int ipath = 0, npaths = polygon.getPathCount(); ipath < npaths; ipath++) {
112+
int startIndex = polygon.getPathStart(ipath);
113+
int endIndex = polygon.getPathEnd(ipath);
114+
int pointCount = endIndex - startIndex;
115+
if (pointCount < 3) {
116+
continue;
117+
}
118+
119+
polygon.getXY(startIndex, startPoint);
120+
polygon.getXY(startIndex + 1, current);
121+
current.sub(startPoint);
122+
for (int i = startIndex + 2, n = endIndex; i < n; i++) {
123+
polygon.getXY(i, next);
124+
next.sub(startPoint);
125+
double twiceTriangleArea = next.x * current.y - current.x * next.y;
126+
xSum.add((current.x + next.x) * twiceTriangleArea);
127+
ySum.add((current.y + next.y) * twiceTriangleArea);
128+
current.setCoords(next);
129+
}
130+
131+
startPoint.sub(origin);
132+
startPoint.scale(6.0 * polygon.calculateRingArea2D(ipath));
133+
//add weighted startPoint
134+
xSum.add(startPoint.x);
135+
ySum.add(startPoint.y);
156136
}
157-
return new Point2D(xSum / (signedArea * 6), ySum / (signedArea * 6));
137+
138+
totalArea *= 6.0;
139+
Point2D res = new Point2D(xSum.getResult() / totalArea, ySum.getResult() / totalArea);
140+
res.add(origin);
141+
return res;
158142
}
159143
}

0 commit comments

Comments
 (0)