Coverage Report

Created: 2026-10-28 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/operation/polygonize/Polygonizer.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2010 Sandro Santilli <strk@kbt.io>
7
 * Copyright (C) 2005-2006 Refractions Research Inc.
8
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
9
 *
10
 * This is free software; you can redistribute and/or modify it under
11
 * the terms of the GNU Lesser General Public Licence as published
12
 * by the Free Software Foundation.
13
 * See the COPYING file for more information.
14
 *
15
 **********************************************************************
16
 *
17
 * Last port: operation/polygonize/Polygonizer.java 0b3c7e3eb0d3e
18
 *
19
 **********************************************************************/
20
21
#include <geos/operation/polygonize/Polygonizer.h>
22
#include <geos/operation/polygonize/PolygonizeGraph.h>
23
#include <geos/operation/polygonize/EdgeRing.h>
24
#include <geos/operation/polygonize/HoleAssigner.h>
25
#include <geos/geom/CircularString.h>
26
#include <geos/geom/CompoundCurve.h>
27
#include <geos/geom/LineString.h>
28
#include <geos/geom/Geometry.h>
29
#include <geos/geom/GeometryFactory.h>
30
#include <geos/geom/Polygon.h>
31
#include <geos/util/Interrupt.h>
32
// std
33
#include <vector>
34
35
#ifdef _MSC_VER
36
#pragma warning(disable:4355)
37
#endif
38
39
#ifndef GEOS_DEBUG
40
#define GEOS_DEBUG 0
41
#endif
42
43
44
using namespace geos::geom;
45
46
namespace geos {
47
namespace operation { // geos.operation
48
namespace polygonize { // geos.operation.polygonize
49
50
Polygonizer::SimpleCurveAdder::SimpleCurveAdder(Polygonizer* p):
51
0
    pol(p)
52
0
{
53
0
}
54
55
void
56
Polygonizer::SimpleCurveAdder::filter_ro(const Geometry* g)
57
0
{
58
0
    if (g->getGeometryTypeId() == GEOS_COMPOUNDCURVE) {
59
0
        const auto& cc = static_cast<const CompoundCurve&>(*g);
60
0
        for (std::size_t i = 0; i < cc.getNumCurves(); i++) {
61
0
            filter_ro(cc.getCurveN(i));
62
0
        }
63
64
0
        return;
65
0
    }
66
67
0
    if(const auto sc = dynamic_cast<const SimpleCurve*>(g)) {
68
0
        pol->add(sc);
69
0
    }
70
0
}
71
72
Polygonizer::Polygonizer(bool onlyPolygonal):
73
0
    lineStringAdder(this),
74
0
    extractOnlyPolygonal(onlyPolygonal),
75
0
    computed(false),
76
0
    graph(nullptr),
77
0
    dangles(),
78
0
    cutEdges(),
79
0
    invalidRingLines(),
80
0
    holeList(),
81
0
    shellList()
82
0
{
83
0
}
84
85
void
86
Polygonizer::add(std::vector<Geometry*>* geomList)
87
0
{
88
0
    for(auto& g : (*geomList)) {
89
0
        add(g);
90
0
    }
91
0
}
92
93
void
94
Polygonizer::add(std::vector<const Geometry*>* geomList)
95
0
{
96
0
    for(auto& g : (*geomList)) {
97
0
        add(g);
98
0
    }
99
0
}
100
101
void
102
Polygonizer::add(const Geometry* g)
103
0
{
104
0
    g->apply_ro(&lineStringAdder);
105
0
}
106
107
void
108
Polygonizer::add(const SimpleCurve* line)
109
0
{
110
    // create a new graph using the factory from the input Geometry
111
0
    if(graph == nullptr) {
112
0
        graph.reset(new PolygonizeGraph(line->getFactory()));
113
0
    }
114
115
0
    if (line->getGeometryTypeId() == GEOS_CIRCULARSTRING) {
116
0
        graph->addEdge(detail::down_cast<const CircularString*>(line));
117
0
    } else {
118
0
        graph->addEdge(detail::down_cast<const LineString*>(line));
119
0
    }
120
0
}
121
122
std::vector<std::unique_ptr<Polygon>>
123
Polygonizer::getPolygons()
124
0
{
125
0
    polygonize();
126
127
0
    std::vector<std::unique_ptr<Polygon>> ret;
128
0
    ret.reserve(polyList.size());
129
0
    for (auto& surf : polyList) {
130
0
        if (surf->getGeometryTypeId() == GEOS_POLYGON) {
131
0
            ret.emplace_back(detail::down_cast<Polygon*>(surf.release()));
132
0
        }
133
0
    }
134
0
    polyList.clear();
135
136
0
    return ret;
137
0
}
138
139
std::vector<std::unique_ptr<Surface>>
140
Polygonizer::getSurfaces()
141
0
{
142
0
    polygonize();
143
144
0
    return std::move(polyList);
145
0
}
146
147
148
/* public */
149
const std::vector<const SimpleCurve*>&
150
Polygonizer::getDangles()
151
0
{
152
0
    polygonize();
153
0
    return dangles;
154
0
}
155
156
bool
157
0
Polygonizer::hasDangles() {
158
0
    polygonize();
159
0
    return !dangles.empty();
160
0
}
161
162
/* public */
163
const std::vector<const SimpleCurve*>&
164
Polygonizer::getCutEdges()
165
0
{
166
0
    polygonize();
167
0
    return cutEdges;
168
0
}
169
170
bool
171
Polygonizer::hasCutEdges()
172
0
{
173
0
    polygonize();
174
0
    return !cutEdges.empty();
175
0
}
176
177
/* public */
178
const std::vector<std::unique_ptr<Curve>>&
179
Polygonizer::getInvalidRingLines()
180
0
{
181
0
    polygonize();
182
0
    return invalidRingLines;
183
0
}
184
185
bool
186
Polygonizer::hasInvalidRingLines()
187
0
{
188
0
    polygonize();
189
0
    return !invalidRingLines.empty();
190
0
}
191
192
bool
193
Polygonizer::allInputsFormPolygons()
194
0
{
195
0
    polygonize();
196
0
    return !hasCutEdges() && !hasDangles() &&!hasInvalidRingLines();
197
0
}
198
199
/* public */
200
void
201
Polygonizer::polygonize()
202
0
{
203
    // check if already computed
204
0
    if(computed) {
205
0
        return;
206
0
    }
207
208
    // if no geometries were supplied it's possible graph could be null
209
0
    if(graph == nullptr) {
210
0
        polyList.clear();
211
0
        return;
212
0
    }
213
214
0
    graph->deleteDangles(dangles);
215
216
0
    graph->deleteCutEdges(cutEdges);
217
218
0
    std::vector<EdgeRing*> edgeRingList;
219
0
    graph->getEdgeRings(edgeRingList);
220
#if GEOS_DEBUG
221
    std::cerr << "Polygonizer::polygonize(): " << edgeRingList.size() << " edgeRings in graph" << std::endl;
222
#endif
223
0
    std::vector<EdgeRing*> validEdgeRingList;
224
0
    std::vector<EdgeRing*> invalidRings;
225
0
    invalidRingLines.clear(); /* what if it was populated already ? we should clean ! */
226
0
    findValidRings(edgeRingList, validEdgeRingList, invalidRings);
227
0
    invalidRingLines = extractInvalidLines(invalidRings);
228
#if GEOS_DEBUG
229
    std::cerr << "                           " << validEdgeRingList.size() << " valid" << std::endl;
230
    std::cerr << "                           " << invalidRingLines.size() << " invalid" << std::endl;
231
#endif
232
233
0
    findShellsAndHoles(validEdgeRingList);
234
#if GEOS_DEBUG
235
    std::cerr << "                           " << holeList.size() << " holes" << std::endl;
236
    std::cerr << "                           " << shellList.size() << " shells" << std::endl;
237
#endif
238
239
0
    HoleAssigner::assignHolesToShells(holeList, shellList);
240
241
0
    bool includeAll = true;
242
0
    if (extractOnlyPolygonal) {
243
0
        findDisjointShells();
244
0
        includeAll = false;
245
0
    }
246
0
    polyList = extractPolygons(shellList, includeAll);
247
248
0
    computed = true;
249
0
}
250
251
/* private */
252
void
253
Polygonizer::findValidRings(const std::vector<EdgeRing*>& edgeRingList,
254
                            std::vector<EdgeRing*>& validEdgeRingList,
255
                            std::vector<EdgeRing*>& invalidRingList)
256
0
{
257
0
    for(const auto& er : edgeRingList) {
258
0
        er->computeValid();
259
0
        if(er->isValid()) {
260
0
            validEdgeRingList.push_back(er);
261
0
        }
262
0
        else {
263
0
            invalidRingList.push_back(er);
264
0
        }
265
0
        GEOS_CHECK_FOR_INTERRUPTS();
266
0
    }
267
0
}
268
269
std::vector<std::unique_ptr<geom::Curve>>
270
Polygonizer::extractInvalidLines(std::vector<EdgeRing*>& invalidRings)
271
0
{
272
    /**
273
     * Sort rings by increasing envelope area.
274
     * This causes inner rings to be processed before the outer rings
275
     * containing them, which allows outer invalid rings to be discarded
276
     * since their linework is already reported in the inner rings.
277
     */
278
0
    std::sort(invalidRings.begin(),
279
0
              invalidRings.end(),
280
0
              [](EdgeRing* a, EdgeRing* b) {
281
0
                return a->getRingInternal()->getEnvelope()->getArea() <
282
0
                       b->getRingInternal()->getEnvelope()->getArea();
283
0
    });
284
285
    /**
286
     * Scan through rings.  Keep only rings which have an adjacent EdgeRing
287
     * which is either valid or marked as not processed.
288
     * This avoids including outer rings which have linework which is duplicated.
289
     */
290
0
    std::vector<std::unique_ptr<Curve>> invalidLines;
291
0
    for (EdgeRing* er : invalidRings) {
292
0
        if (isIncludedInvalid(er)) {
293
0
            auto ringGeom = er->getRingOwnership();
294
0
            if (ringGeom->getGeometryTypeId() == GEOS_LINEARRING) {
295
0
                ringGeom = ringGeom->getFactory()->createLineString(ringGeom->getCoordinates());
296
0
            }
297
298
0
            invalidLines.push_back(std::move(ringGeom));
299
0
        }
300
0
        er->setProcessed(true);
301
0
    }
302
303
0
    return invalidLines;
304
0
}
305
306
bool
307
Polygonizer::isIncludedInvalid(const EdgeRing* invalidRing)
308
0
{
309
0
    for (const PolygonizeDirectedEdge* de: invalidRing->getEdges()) {
310
0
        const PolygonizeDirectedEdge* deAdj = static_cast<PolygonizeDirectedEdge*>(de->getSym());
311
0
        const EdgeRing* erAdj = deAdj->getRing();
312
313
0
        bool isEdgeIncluded = erAdj->isValid() || erAdj->isProcessed();
314
0
        if (!isEdgeIncluded) {
315
0
            return true;
316
0
        }
317
0
    }
318
319
0
    return false;
320
0
}
321
322
/* private */
323
void
324
Polygonizer::findShellsAndHoles(const std::vector<EdgeRing*>& edgeRingList)
325
0
{
326
0
    holeList.clear();
327
0
    shellList.clear();
328
0
    for(auto& er : edgeRingList) {
329
0
        er->computeHole();
330
0
        if(er->isHole()) {
331
0
            holeList.push_back(er);
332
0
        }
333
0
        else {
334
0
            shellList.push_back(er);
335
0
        }
336
337
0
        GEOS_CHECK_FOR_INTERRUPTS();
338
0
    }
339
0
}
340
341
342
void
343
0
Polygonizer::findDisjointShells() {
344
0
    findOuterShells(shellList);
345
346
0
    for (EdgeRing *er : shellList) {
347
0
        if (!er->isIncludedSet()) {
348
0
            er->updateIncludedRecursive();
349
0
        }
350
0
    }
351
352
0
    return;
353
0
}
354
355
void
356
Polygonizer::findOuterShells(std::vector<EdgeRing*> & shells)
357
0
{
358
0
    for (EdgeRing* er : shells) {
359
0
        auto outerHoleER = er->getOuterHole();
360
0
        if (outerHoleER != nullptr && !outerHoleER->isProcessed()) {
361
0
            er->setIncluded(true);
362
0
            outerHoleER->setProcessed(true);
363
0
        }
364
0
    }
365
0
}
366
367
std::vector<std::unique_ptr<Surface>>
368
Polygonizer::extractPolygons(std::vector<EdgeRing*> & shells, bool includeAll)
369
0
{
370
0
    std::vector<std::unique_ptr<Surface>> polys;
371
0
    for (EdgeRing* er : shells) {
372
0
        if (includeAll || er->isIncluded()) {
373
0
            polys.emplace_back(er->getPolygon());
374
0
        }
375
0
    }
376
377
0
    return polys;
378
0
}
379
380
} // namespace geos.operation.polygonize
381
} // namespace geos.operation
382
} // namespace geos
383