source: trunk/src/algorithm/CentroidArea.cpp

Last change on this file was 3773, checked in by strk, 11 years ago

Fix centroid computation for collections with empty components

Fixes bug #582

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/plain
File size: 4.2 KB
Line 
1/**********************************************************************
2 *
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
5 *
6 * Copyright (C) 2001-2002 Vivid Solutions Inc.
7 * Copyright (C) 2006 Refractions Research Inc.
8 *
9 * This is free software; you can redistribute and/or modify it under
10 * the terms of the GNU Lesser General Public Licence as published
11 * by the Free Software Foundation.
12 * See the COPYING file for more information.
13 *
14 **********************************************************************
15 *
16 * Last port: algorithm/CentroidArea.java r612
17 *
18 **********************************************************************/
19
20#include <geos/algorithm/CentroidArea.h>
21#include <geos/algorithm/CGAlgorithms.h>
22#include <geos/geom/Coordinate.h>
23#include <geos/geom/CoordinateSequence.h>
24#include <geos/geom/Geometry.h>
25#include <geos/geom/GeometryCollection.h>
26#include <geos/geom/LineString.h>
27#include <geos/geom/Polygon.h>
28
29#include <typeinfo>
30
31using namespace geos::geom;
32
33namespace geos {
34namespace algorithm { // geos.algorithm
35
36/*public*/
37void
38CentroidArea::add(const Geometry *geom)
39{
40 if(geom->isEmpty()) return;
41 else if(const Polygon *poly=dynamic_cast<const Polygon*>(geom)) {
42 setBasePoint(poly->getExteriorRing()->getCoordinateN(0));
43 add(poly);
44 }
45 else if(const GeometryCollection *gc=dynamic_cast<const GeometryCollection*>(geom))
46 {
47 for(std::size_t i=0, n=gc->getNumGeometries(); i<n; ++i)
48 {
49 add(gc->getGeometryN(i));
50 }
51 }
52}
53
54/*public*/
55void
56CentroidArea::add(const CoordinateSequence *ring)
57{
58 setBasePoint(ring->getAt(0));
59 addShell(ring);
60}
61
62/* TODO: deprecate this */
63Coordinate*
64CentroidArea::getCentroid() const
65{
66 Coordinate *cent = new Coordinate();
67 getCentroid(*cent); // or return NULL on failure !
68 return cent;
69}
70
71bool
72CentroidArea::getCentroid(Coordinate& ret) const
73{
74 if ( areasum2 ) {
75 ret = Coordinate(cg3.x/3.0/areasum2, cg3.y/3.0/areasum2);
76 } else if ( totalLength ) {
77 ret = Coordinate(centSum.x/totalLength, centSum.y/totalLength);
78 } else {
79 return false;
80 }
81 return true;
82}
83
84void
85CentroidArea::setBasePoint(const Coordinate &newbasePt)
86{
87 basePt=newbasePt;
88}
89
90void
91CentroidArea::add(const Polygon *poly)
92{
93 addShell(poly->getExteriorRing()->getCoordinatesRO());
94 for(size_t i=0, n=poly->getNumInteriorRing(); i<n; ++i)
95 {
96 addHole(poly->getInteriorRingN(i)->getCoordinatesRO());
97 }
98}
99
100void
101CentroidArea::addShell(const CoordinateSequence *pts)
102{
103 bool isPositiveArea=!CGAlgorithms::isCCW(pts);
104 std::size_t const n=pts->getSize()-1;
105 for(std::size_t i=0; i<n; ++i)
106 {
107 addTriangle(basePt, pts->getAt(i), pts->getAt(i+1), isPositiveArea);
108 }
109 addLinearSegments(*pts);
110}
111
112void
113CentroidArea::addHole(const CoordinateSequence *pts)
114{
115 bool isPositiveArea=CGAlgorithms::isCCW(pts);
116 std::size_t const n=pts->getSize()-1;
117 for(std::size_t i=0; i<n; ++i)
118 {
119 addTriangle(basePt, pts->getAt(i), pts->getAt(i+1), isPositiveArea);
120 }
121 addLinearSegments(*pts);
122}
123
124void
125CentroidArea::addTriangle(const Coordinate &p0, const Coordinate &p1,
126 const Coordinate &p2, bool isPositiveArea)
127{
128 double sign=(isPositiveArea)?1.0:-1.0;
129 centroid3(p0,p1,p2,triangleCent3);
130 double area2res=area2(p0,p1,p2);
131 cg3.x+=sign*area2res*triangleCent3.x;
132 cg3.y+=sign*area2res*triangleCent3.y;
133 areasum2+=sign*area2res;
134}
135
136/**
137 * Returns three times the centroid of the triangle p1-p2-p3.
138 * The factor of 3 is
139 * left in to permit division to be avoided until later.
140 */
141void
142CentroidArea::centroid3(const Coordinate &p1, const Coordinate &p2,
143 const Coordinate &p3, Coordinate &c)
144{
145 c.x=p1.x+p2.x+p3.x;
146 c.y=p1.y+p2.y+p3.y;
147}
148
149/**
150 * Returns twice the signed area of the triangle p1-p2-p3,
151 * positive if a,b,c are oriented ccw, and negative if cw.
152 */
153double
154CentroidArea::area2(const Coordinate &p1, const Coordinate &p2, const Coordinate &p3)
155{
156 return (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y);
157}
158
159void
160CentroidArea::addLinearSegments(const geom::CoordinateSequence& pts)
161{
162 std::size_t const n = pts.size()-1;
163 for (std::size_t i = 0; i < n; ++i) {
164 double segmentLen = pts[i].distance(pts[i + 1]);
165 totalLength += segmentLen;
166
167 double midx = (pts[i].x + pts[i + 1].x) / 2;
168 centSum.x += segmentLen * midx;
169 double midy = (pts[i].y + pts[i + 1].y) / 2;
170 centSum.y += segmentLen * midy;
171 }
172}
173
174} // namespace geos.algorithm
175} //namespace geos
Note: See TracBrowser for help on using the repository browser.