Coverage Report

Created: 2026-10-28 06:48

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
0
{
42
0
    Centroid cent(geom);
43
0
    return cent.getCentroid(pt);
44
0
}
45
46
/* public */
47
bool
48
Centroid::getCentroid(CoordinateXY& cent) const
49
0
{
50
0
    if(std::abs(areasum2) > 0.0) {
51
0
        cent.x = cg3.x / 3 / areasum2;
52
0
        cent.y = cg3.y / 3 / areasum2;
53
0
    }
54
0
    else if(totalLength > 0.0) {
55
        // if polygon was degenerate, compute linear centroid instead
56
0
        cent.x = lineCentSum.x / totalLength;
57
0
        cent.y = lineCentSum.y / totalLength;
58
0
    }
59
0
    else if(ptCount > 0) {
60
0
        cent.x = ptCentSum.x / ptCount;
61
0
        cent.y = ptCentSum.y / ptCount;
62
0
    }
63
0
    else {
64
0
        return false;
65
0
    }
66
0
    return true;
67
0
}
68
69
/* private */
70
void
71
Centroid::add(const Geometry& geom)
72
0
{
73
0
    util::ensureNoCurvedComponents(geom);
74
75
0
    if(geom.isEmpty()) {
76
0
        return;
77
0
    }
78
79
0
    if(const Point* pt = dynamic_cast<const Point*>(&geom)) {
80
0
        addPoint(*pt->getCoordinate());
81
0
    }
82
0
    else if(const LineString* ls = dynamic_cast<const LineString*>(&geom)) {
83
0
        addLineSegments(*ls->getCoordinatesRO());
84
0
    }
85
0
    else if(const Polygon* p = dynamic_cast<const Polygon*>(&geom)) {
86
0
        add(*p);
87
0
    }
88
0
    else if(const GeometryCollection* g = dynamic_cast<const GeometryCollection*>(&geom)) {
89
0
        for(std::size_t i = 0; i < g->getNumGeometries(); i++) {
90
0
            add(*g->getGeometryN(i));
91
0
        }
92
0
    }
93
0
}
94
95
/* private */
96
void
97
Centroid::setAreaBasePoint(const CoordinateXY& basePt)
98
0
{
99
0
    areaBasePt.reset(new CoordinateXY(basePt));
100
0
}
101
102
/* private */
103
void
104
Centroid::add(const Polygon& poly)
105
0
{
106
0
    addShell(*poly.getExteriorRing()->getCoordinatesRO());
107
0
    for(std::size_t i = 0; i < poly.getNumInteriorRing(); i++) {
108
0
        addHole(*poly.getInteriorRingN(i)->getCoordinatesRO());
109
0
    }
110
0
}
111
112
/* private */
113
void
114
Centroid::addShell(const CoordinateSequence& pts)
115
0
{
116
0
    std::size_t len = pts.size();
117
0
    if(len > 0) {
118
0
        setAreaBasePoint(pts.getAt<CoordinateXY>(0));
119
0
    }
120
0
    bool isPositiveArea = ! Orientation::isCCW(&pts);
121
0
    for(std::size_t i = 0; i < len - 1; ++i) {
122
0
        addTriangle(*areaBasePt, pts.getAt<CoordinateXY>(i), pts.getAt<CoordinateXY>(i + 1), isPositiveArea);
123
0
    }
124
0
    addLineSegments(pts);
125
0
}
126
127
/* private */
128
void
129
Centroid::addHole(const CoordinateSequence& pts)
130
0
{
131
0
    if (pts.isEmpty()) {
132
0
        return;
133
0
    }
134
135
0
    bool isPositiveArea = Orientation::isCCW(&pts);
136
0
    for(std::size_t i = 0, e = pts.size() - 1; i < e; ++i) {
137
0
        addTriangle(*areaBasePt, pts.getAt<CoordinateXY>(i), pts.getAt<CoordinateXY>(i + 1), isPositiveArea);
138
0
    }
139
0
    addLineSegments(pts);
140
0
}
141
142
/* private */
143
void
144
Centroid::addTriangle(const CoordinateXY& p0, const CoordinateXY& p1, const CoordinateXY& p2, bool isPositiveArea)
145
0
{
146
0
    double sign = (isPositiveArea) ? 1.0 : -1.0;
147
0
    centroid3(p0, p1, p2, triangleCent3);
148
0
    double a2 = area2(p0, p1, p2);
149
0
    cg3.x += sign * a2 * triangleCent3.x;
150
0
    cg3.y += sign * a2 * triangleCent3.y;
151
0
    areasum2 += sign * a2;
152
0
}
153
154
/* static private */
155
void
156
Centroid::centroid3(const CoordinateXY& p1, const CoordinateXY& p2, const CoordinateXY& p3, CoordinateXY& c)
157
0
{
158
0
    c.x = p1.x + p2.x + p3.x;
159
0
    c.y = p1.y + p2.y + p3.y;
160
0
    return;
161
0
}
162
163
/* static private */
164
double
165
Centroid::area2(const CoordinateXY& p1, const CoordinateXY& p2, const CoordinateXY& p3)
166
0
{
167
0
    return
168
0
        (p2.x - p1.x) * (p3.y - p1.y) -
169
0
        (p3.x - p1.x) * (p2.y - p1.y);
170
0
}
171
172
/* private */
173
void
174
Centroid::addLineSegments(const CoordinateSequence& pts)
175
0
{
176
0
    std::size_t npts = pts.size();
177
0
    double lineLen = 0.0;
178
0
    for(std::size_t i = 0; i < npts - 1; i++) {
179
0
        double segmentLen = pts.getAt<CoordinateXY>(i).distance(pts.getAt<CoordinateXY>(i + 1));
180
0
        if(segmentLen == 0.0) {
181
0
            continue;
182
0
        }
183
184
0
        lineLen += segmentLen;
185
186
0
        double midx = (pts.getAt<CoordinateXY>(i).x + pts.getAt<CoordinateXY>(i + 1).x) / 2;
187
0
        lineCentSum.x += segmentLen * midx;
188
0
        double midy = (pts.getAt<CoordinateXY>(i).y + pts.getAt<CoordinateXY>(i + 1).y) / 2;
189
0
        lineCentSum.y += segmentLen * midy;
190
0
    }
191
0
    totalLength += lineLen;
192
0
    if(lineLen == 0.0 && npts > 0) {
193
0
        addPoint(pts[0]);
194
0
    }
195
0
}
196
197
/* private */
198
void
199
Centroid::addPoint(const CoordinateXY& pt)
200
0
{
201
0
    ptCount += 1;
202
0
    ptCentSum.x += pt.x;
203
0
    ptCentSum.y += pt.y;
204
0
}
205
206
} // namespace geos.algorithm
207
} // namespace geos