/src/geos/src/algorithm/Centroid.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (C) 2013 Sandro Santilli <strk@kbt.io> |
7 | | * |
8 | | * This is free software; you can redistribute and/or modify it under |
9 | | * the terms of the GNU Lesser General Public Licence as published |
10 | | * by the Free Software Foundation. |
11 | | * See the COPYING file for more information. |
12 | | * |
13 | | ********************************************************************** |
14 | | * |
15 | | * Last port: algorithm/Centroid.java r728 (JTS-0.13+) |
16 | | * |
17 | | **********************************************************************/ |
18 | | |
19 | | #include <geos/algorithm/Centroid.h> |
20 | | #include <geos/algorithm/Orientation.h> |
21 | | #include <geos/geom/CoordinateSequence.h> |
22 | | #include <geos/geom/Geometry.h> |
23 | | #include <geos/geom/Point.h> |
24 | | #include <geos/geom/Polygon.h> |
25 | | #include <geos/geom/GeometryCollection.h> |
26 | | #include <geos/geom/LineString.h> |
27 | | |
28 | | |
29 | | #include <cmath> // for std::abs |
30 | | |
31 | | #include "geos/util.h" |
32 | | |
33 | | using namespace geos::geom; |
34 | | |
35 | | namespace geos { |
36 | | namespace algorithm { // geos.algorithm |
37 | | |
38 | | /* static public */ |
39 | | bool |
40 | | Centroid::getCentroid(const Geometry& geom, CoordinateXY& pt) |
41 | 24.1k | { |
42 | 24.1k | Centroid cent(geom); |
43 | 24.1k | return cent.getCentroid(pt); |
44 | 24.1k | } |
45 | | |
46 | | /* public */ |
47 | | bool |
48 | | Centroid::getCentroid(CoordinateXY& cent) const |
49 | 22.2k | { |
50 | 22.2k | if(std::abs(areasum2) > 0.0) { |
51 | 275 | cent.x = cg3.x / 3 / areasum2; |
52 | 275 | cent.y = cg3.y / 3 / areasum2; |
53 | 275 | } |
54 | 21.9k | else if(totalLength > 0.0) { |
55 | | // if polygon was degenerate, compute linear centroid instead |
56 | 10.3k | cent.x = lineCentSum.x / totalLength; |
57 | 10.3k | cent.y = lineCentSum.y / totalLength; |
58 | 10.3k | } |
59 | 11.5k | else if(ptCount > 0) { |
60 | 3.93k | cent.x = ptCentSum.x / ptCount; |
61 | 3.93k | cent.y = ptCentSum.y / ptCount; |
62 | 3.93k | } |
63 | 7.62k | else { |
64 | 7.62k | return false; |
65 | 7.62k | } |
66 | 14.5k | return true; |
67 | 22.2k | } |
68 | | |
69 | | /* private */ |
70 | | void |
71 | | Centroid::add(const Geometry& geom) |
72 | 449k | { |
73 | 449k | util::ensureNoCurvedComponents(geom); |
74 | | |
75 | 449k | if(geom.isEmpty()) { |
76 | 52 | return; |
77 | 52 | } |
78 | | |
79 | 449k | if(const Point* pt = dynamic_cast<const Point*>(&geom)) { |
80 | 425k | addPoint(*pt->getCoordinate()); |
81 | 425k | } |
82 | 23.9k | else if(const LineString* ls = dynamic_cast<const LineString*>(&geom)) { |
83 | 18.1k | addLineSegments(*ls->getCoordinatesRO()); |
84 | 18.1k | } |
85 | 5.77k | else if(const Polygon* p = dynamic_cast<const Polygon*>(&geom)) { |
86 | 798 | add(*p); |
87 | 798 | } |
88 | 4.98k | else if(const GeometryCollection* g = dynamic_cast<const GeometryCollection*>(&geom)) { |
89 | 428k | for(std::size_t i = 0; i < g->getNumGeometries(); i++) { |
90 | 425k | add(*g->getGeometryN(i)); |
91 | 425k | } |
92 | 3.07k | } |
93 | 449k | } |
94 | | |
95 | | /* private */ |
96 | | void |
97 | | Centroid::setAreaBasePoint(const CoordinateXY& basePt) |
98 | 798 | { |
99 | 798 | areaBasePt.reset(new CoordinateXY(basePt)); |
100 | 798 | } |
101 | | |
102 | | /* private */ |
103 | | void |
104 | | Centroid::add(const Polygon& poly) |
105 | 798 | { |
106 | 798 | addShell(*poly.getExteriorRing()->getCoordinatesRO()); |
107 | 58.0k | for(std::size_t i = 0; i < poly.getNumInteriorRing(); i++) { |
108 | 57.2k | addHole(*poly.getInteriorRingN(i)->getCoordinatesRO()); |
109 | 57.2k | } |
110 | 798 | } |
111 | | |
112 | | /* private */ |
113 | | void |
114 | | Centroid::addShell(const CoordinateSequence& pts) |
115 | 798 | { |
116 | 798 | std::size_t len = pts.size(); |
117 | 798 | if(len > 0) { |
118 | 798 | setAreaBasePoint(pts.getAt<CoordinateXY>(0)); |
119 | 798 | } |
120 | 798 | bool isPositiveArea = ! Orientation::isCCW(&pts); |
121 | 3.96k | for(std::size_t i = 0; i < len - 1; ++i) { |
122 | 3.16k | addTriangle(*areaBasePt, pts.getAt<CoordinateXY>(i), pts.getAt<CoordinateXY>(i + 1), isPositiveArea); |
123 | 3.16k | } |
124 | 798 | addLineSegments(pts); |
125 | 798 | } |
126 | | |
127 | | /* private */ |
128 | | void |
129 | | Centroid::addHole(const CoordinateSequence& pts) |
130 | 57.2k | { |
131 | 57.2k | if (pts.isEmpty()) { |
132 | 55.8k | return; |
133 | 55.8k | } |
134 | | |
135 | 1.42k | bool isPositiveArea = Orientation::isCCW(&pts); |
136 | 115k | for(std::size_t i = 0, e = pts.size() - 1; i < e; ++i) { |
137 | 113k | addTriangle(*areaBasePt, pts.getAt<CoordinateXY>(i), pts.getAt<CoordinateXY>(i + 1), isPositiveArea); |
138 | 113k | } |
139 | 1.42k | addLineSegments(pts); |
140 | 1.42k | } |
141 | | |
142 | | /* private */ |
143 | | void |
144 | | Centroid::addTriangle(const CoordinateXY& p0, const CoordinateXY& p1, const CoordinateXY& p2, bool isPositiveArea) |
145 | 116k | { |
146 | 116k | double sign = (isPositiveArea) ? 1.0 : -1.0; |
147 | 116k | centroid3(p0, p1, p2, triangleCent3); |
148 | 116k | double a2 = area2(p0, p1, p2); |
149 | 116k | cg3.x += sign * a2 * triangleCent3.x; |
150 | 116k | cg3.y += sign * a2 * triangleCent3.y; |
151 | 116k | areasum2 += sign * a2; |
152 | 116k | } |
153 | | |
154 | | /* static private */ |
155 | | void |
156 | | Centroid::centroid3(const CoordinateXY& p1, const CoordinateXY& p2, const CoordinateXY& p3, CoordinateXY& c) |
157 | 116k | { |
158 | 116k | c.x = p1.x + p2.x + p3.x; |
159 | 116k | c.y = p1.y + p2.y + p3.y; |
160 | 116k | return; |
161 | 116k | } |
162 | | |
163 | | /* static private */ |
164 | | double |
165 | | Centroid::area2(const CoordinateXY& p1, const CoordinateXY& p2, const CoordinateXY& p3) |
166 | 116k | { |
167 | 116k | return |
168 | 116k | (p2.x - p1.x) * (p3.y - p1.y) - |
169 | 116k | (p3.x - p1.x) * (p2.y - p1.y); |
170 | 116k | } |
171 | | |
172 | | /* private */ |
173 | | void |
174 | | Centroid::addLineSegments(const CoordinateSequence& pts) |
175 | 20.3k | { |
176 | 20.3k | std::size_t npts = pts.size(); |
177 | 20.3k | double lineLen = 0.0; |
178 | 1.19M | for(std::size_t i = 0; i < npts - 1; i++) { |
179 | 1.17M | double segmentLen = pts.getAt<CoordinateXY>(i).distance(pts.getAt<CoordinateXY>(i + 1)); |
180 | 1.17M | if(segmentLen == 0.0) { |
181 | 497k | continue; |
182 | 497k | } |
183 | | |
184 | 674k | lineLen += segmentLen; |
185 | | |
186 | 674k | double midx = (pts.getAt<CoordinateXY>(i).x + pts.getAt<CoordinateXY>(i + 1).x) / 2; |
187 | 674k | lineCentSum.x += segmentLen * midx; |
188 | 674k | double midy = (pts.getAt<CoordinateXY>(i).y + pts.getAt<CoordinateXY>(i + 1).y) / 2; |
189 | 674k | lineCentSum.y += segmentLen * midy; |
190 | 674k | } |
191 | 20.3k | totalLength += lineLen; |
192 | 20.3k | if(lineLen == 0.0 && npts > 0) { |
193 | 1.46k | addPoint(pts[0]); |
194 | 1.46k | } |
195 | 20.3k | } |
196 | | |
197 | | /* private */ |
198 | | void |
199 | | Centroid::addPoint(const CoordinateXY& pt) |
200 | 426k | { |
201 | 426k | ptCount += 1; |
202 | 426k | ptCentSum.x += pt.x; |
203 | 426k | ptCentSum.y += pt.y; |
204 | 426k | } |
205 | | |
206 | | } // namespace geos.algorithm |
207 | | } // namespace geos |