Coverage Report

Created: 2026-08-13 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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