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.
2323 */
2424package com .esri .core .geometry ;
2525
26- import static java .lang .Math .sqrt ;
27-
2826public 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