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
3028public 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 ) {
6058double xSum =0 ;
6159double ySum =0 ;
6260int 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+ // Polygoncentroid: 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 ();
147107Point2D current =new Point2D ();
148108Point2D 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}