Coverage Report

Created: 2026-06-13 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/operation/polygonize/PolygonizeGraph.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2005-2006 Refractions Research Inc.
7
 * Copyright (C) 2001-2002 Vivid Solutions 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: operation/polygonize/PolygonizeGraph.java rev. 6/138 (JTS-1.10)
17
 *
18
 **********************************************************************/
19
20
#include <geos/operation/polygonize/PolygonizeGraph.h>
21
#include <geos/operation/polygonize/PolygonizeDirectedEdge.h>
22
#include <geos/operation/polygonize/PolygonizeEdge.h>
23
#include <geos/operation/polygonize/EdgeRing.h>
24
#include <geos/operation/valid/RepeatedPointRemover.h>
25
#include <geos/planargraph/Node.h>
26
#include <geos/planargraph/DirectedEdgeStar.h>
27
#include <geos/planargraph/DirectedEdge.h>
28
#include <geos/geom/CircularString.h>
29
#include <geos/geom/CoordinateSequence.h>
30
#include <geos/geom/LineString.h>
31
#include <geos/util.h>
32
33
#include <cassert>
34
#include <vector>
35
#include <set>
36
37
//
38
using namespace geos::planargraph;
39
using namespace geos::geom;
40
41
// Define the following to add assertions on downcasts
42
//#define GEOS_CAST_PARANOIA 1
43
44
namespace geos {
45
namespace operation { // geos.operation
46
namespace polygonize { // geos.operation.polygonize
47
48
int
49
PolygonizeGraph::getDegreeNonDeleted(Node* node)
50
0
{
51
0
    auto edges = node->getOutEdges()->getEdges();
52
0
    int degree = 0;
53
0
    for(const auto& de : edges) {
54
0
        if(!de->isMarked()) {
55
0
            ++degree;
56
0
        }
57
0
    }
58
0
    return degree;
59
0
}
60
61
int
62
PolygonizeGraph::getDegree(Node* node, long label)
63
0
{
64
0
    auto edges = node->getOutEdges()->getEdges();
65
0
    int degree = 0;
66
0
    for(const auto& de : edges) {
67
0
        auto pde = detail::down_cast<PolygonizeDirectedEdge*>(de);
68
0
        if(pde->getLabel() == label) {
69
0
            ++degree;
70
0
        }
71
0
    }
72
0
    return degree;
73
0
}
74
75
/**
76
 * Deletes all edges at a node
77
 */
78
void
79
PolygonizeGraph::deleteAllEdges(Node* node)
80
0
{
81
0
    auto edges = node->getOutEdges()->getEdges();
82
0
    for(const auto& de : edges) {
83
0
        de->setMarked(true);
84
0
        auto sym = de->getSym();
85
0
        if(sym != nullptr) {
86
0
            sym->setMarked(true);
87
0
        }
88
0
    }
89
0
}
90
91
/*
92
 * Create a new polygonization graph.
93
 */
94
PolygonizeGraph::PolygonizeGraph(const GeometryFactory* newFactory):
95
0
    factory(newFactory)
96
0
{
97
0
}
98
99
/*
100
 * Destroy a PolygonizeGraph
101
 */
102
PolygonizeGraph::~PolygonizeGraph()
103
0
{
104
0
    unsigned int i;
105
0
    for(i = 0; i < newEdges.size(); i++) {
106
0
        delete newEdges[i];
107
0
    }
108
0
    for(i = 0; i < newDirEdges.size(); i++) {
109
0
        delete newDirEdges[i];
110
0
    }
111
0
    for(i = 0; i < newNodes.size(); i++) {
112
0
        delete newNodes[i];
113
0
    }
114
0
    for(i = 0; i < newEdgeRings.size(); i++) {
115
0
        delete newEdgeRings[i];
116
0
    }
117
0
    for(i = 0; i < newCoords.size(); i++) {
118
0
        delete newCoords[i];
119
0
    }
120
0
}
121
122
/*
123
 * Add a LineString forming an edge of the polygon graph.
124
 * @param line the line to add
125
 */
126
void
127
PolygonizeGraph::addEdge(const LineString* line)
128
0
{
129
0
    if(line->isEmpty()) {
130
0
        return;
131
0
    }
132
133
0
    auto linePts = valid::RepeatedPointRemover::removeRepeatedPoints(line->getCoordinatesRO());
134
135
    /*
136
     * This would catch invalid linestrings
137
     * (containing duplicated points only)
138
     */
139
0
    if(linePts->getSize() < 2) {
140
0
        return;
141
0
    }
142
143
0
    const CoordinateXY& startPt = linePts->getAt<CoordinateXY>(0);
144
0
    const CoordinateXY& endPt = linePts->getAt<CoordinateXY>(linePts->getSize() - 1);
145
0
    Node* nStart = getNode(startPt);
146
0
    Node* nEnd = getNode(endPt);
147
0
    DirectedEdge* de0 = new PolygonizeDirectedEdge(nStart, nEnd, linePts->getAt(1), true);
148
0
    newDirEdges.push_back(de0);
149
0
    DirectedEdge* de1 = new PolygonizeDirectedEdge(nEnd, nStart,
150
0
            linePts->getAt<CoordinateXY>(linePts->getSize() - 2), false);
151
0
    newDirEdges.push_back(de1);
152
0
    Edge* edge = new PolygonizeEdge(line);
153
0
    newEdges.push_back(edge);
154
0
    edge->setDirectedEdges(de0, de1);
155
0
    add(edge);
156
157
0
    newCoords.push_back(linePts.release());
158
0
}
159
160
void
161
PolygonizeGraph::addEdge(const CircularString* geom)
162
0
{
163
0
    if(geom->isEmpty()) {
164
0
        return;
165
0
    }
166
167
0
    const CoordinateSequence* linePts = geom->getCoordinatesRO();
168
169
0
    const CoordinateXY& startPt = linePts->getAt<CoordinateXY>(0);
170
0
    const CoordinateXY& endPt = linePts->getAt<CoordinateXY>(linePts->getSize() - 1);
171
0
    Node* nStart = getNode(startPt);
172
0
    Node* nEnd = getNode(endPt);
173
174
0
    DirectedEdge* de0;
175
0
    DirectedEdge* de1;
176
177
    // Forward edge
178
0
    {
179
0
        CircularArc startArc(*linePts, 0);
180
0
        CoordinateXY dirPt = startArc.getDirectionPoint();
181
0
        de0 = new PolygonizeDirectedEdge(nStart, nEnd, dirPt, true);
182
0
    }
183
184
    // Reverse edge
185
0
    {
186
0
        CircularArc endArc = CircularArc(*linePts, linePts->getSize() - 3).reverse();
187
0
        CoordinateXY dirPt = endArc.getDirectionPoint();
188
0
        de1 = new PolygonizeDirectedEdge(nEnd, nStart, dirPt, false);
189
0
    }
190
191
0
    newDirEdges.push_back(de0);
192
0
    newDirEdges.push_back(de1);
193
194
0
    Edge* edge = new PolygonizeEdge(geom);
195
0
    newEdges.push_back(edge);
196
0
    edge->setDirectedEdges(de0, de1);
197
0
    add(edge);
198
0
}
199
200
Node*
201
PolygonizeGraph::getNode(const CoordinateXY& pt)
202
0
{
203
0
    Node* node = findNode(pt);
204
0
    if(node == nullptr) {
205
0
        node = new Node(pt);
206
0
        newNodes.push_back(node);
207
        // ensure node is only added once to graph
208
0
        add(node);
209
0
    }
210
0
    return node;
211
0
}
212
213
void
214
PolygonizeGraph::computeNextCWEdges()
215
0
{
216
0
    typedef std::vector<Node*> Nodes;
217
0
    Nodes pns;
218
0
    getNodes(pns);
219
    // set the next pointers for the edges around each node
220
0
    for(Node* node : pns) {
221
0
        computeNextCWEdges(node);
222
0
    }
223
0
}
224
225
/* private */
226
void
227
PolygonizeGraph::convertMaximalToMinimalEdgeRings(
228
    std::vector<PolygonizeDirectedEdge*>& ringEdges)
229
0
{
230
0
    std::vector<Node*> intNodes;
231
0
    for(PolygonizeDirectedEdge* de : ringEdges) {
232
0
        long p_label = de->getLabel();
233
0
        findIntersectionNodes(de, p_label, intNodes);
234
235
        // set the next pointers for the edges around each node
236
0
        for(Node* node : intNodes) {
237
0
            computeNextCCWEdges(node, p_label);
238
0
        }
239
240
0
        intNodes.clear();
241
0
    }
242
0
}
243
244
/* private static */
245
void
246
PolygonizeGraph::findIntersectionNodes(PolygonizeDirectedEdge* startDE,
247
                                       long label, std::vector<Node*>& intNodes)
248
0
{
249
0
    PolygonizeDirectedEdge* de = startDE;
250
0
    do {
251
0
        Node* node = de->getFromNode();
252
0
        if(getDegree(node, label) > 1) {
253
0
            intNodes.push_back(node);
254
0
        }
255
0
        de = de->getNext();
256
0
        assert(de != nullptr); // found NULL DE in ring
257
0
        assert(de == startDE || !de->isInRing()); // found DE already in ring
258
0
    }
259
0
    while(de != startDE);
260
0
}
261
262
/* public */
263
void
264
PolygonizeGraph::getEdgeRings(std::vector<EdgeRing*>& edgeRingList)
265
0
{
266
    // maybe could optimize this, since most of these pointers should
267
    // be set correctly already
268
    // by deleteCutEdges()
269
0
    computeNextCWEdges();
270
271
    // clear labels of all edges in graph
272
0
    label(dirEdges, -1);
273
0
    std::vector<PolygonizeDirectedEdge*> maximalRings;
274
0
    findLabeledEdgeRings(dirEdges, maximalRings);
275
0
    convertMaximalToMinimalEdgeRings(maximalRings);
276
0
    maximalRings.clear(); // not needed anymore
277
278
    // find all edgerings
279
0
    for(DirectedEdge* de : dirEdges) {
280
0
        auto pde = detail::down_cast<PolygonizeDirectedEdge*>(de);
281
0
        if(pde->isMarked()) {
282
0
            continue;
283
0
        }
284
0
        if(pde->isInRing()) {
285
0
            continue;
286
0
        }
287
0
        EdgeRing* er = findEdgeRing(pde);
288
0
        edgeRingList.push_back(er);
289
0
    }
290
0
}
291
292
/* static private */
293
void
294
PolygonizeGraph::findLabeledEdgeRings(std::vector<DirectedEdge*>& dirEdges,
295
                                      std::vector<PolygonizeDirectedEdge*>& edgeRingStarts)
296
0
{
297
    // label the edge rings formed
298
0
    long currLabel = 1;
299
0
    for(DirectedEdge* de : dirEdges) {
300
0
        auto pde = detail::down_cast<PolygonizeDirectedEdge*>(de);
301
302
0
        if(pde->isMarked()) {
303
0
            continue;
304
0
        }
305
0
        if(pde->getLabel() >= 0) {
306
0
            continue;
307
0
        }
308
0
        edgeRingStarts.push_back(pde);
309
310
0
        auto edges = EdgeRing::findDirEdgesInRing(pde);
311
0
        label(edges, currLabel);
312
0
        edges.clear();
313
314
0
        ++currLabel;
315
0
    }
316
0
}
317
318
/* public */
319
void
320
PolygonizeGraph::deleteCutEdges(std::vector<const SimpleCurve*>& cutLines)
321
0
{
322
0
    computeNextCWEdges();
323
324
0
    typedef std::vector<PolygonizeDirectedEdge*> DirEdges;
325
326
    // label the current set of edgerings
327
0
    DirEdges junk;
328
0
    findLabeledEdgeRings(dirEdges, junk);
329
0
    junk.clear(); // not needed anymore
330
331
    /*
332
     * Cut Edges are edges where both dirEdges have the same label.
333
     * Delete them, and record them
334
     */
335
0
    for(DirectedEdge* de : dirEdges) {
336
0
        auto pde = detail::down_cast<PolygonizeDirectedEdge*>(de);
337
338
0
        if(de->isMarked()) {
339
0
            continue;
340
0
        }
341
342
0
        auto sym = detail::down_cast<PolygonizeDirectedEdge*>(de->getSym());
343
344
0
        if(pde->getLabel() == sym->getLabel()) {
345
0
            de->setMarked(true);
346
0
            sym->setMarked(true);
347
348
            // save the line as a cut edge
349
0
            auto e = detail::down_cast<PolygonizeEdge*>(de->getEdge());
350
351
0
            cutLines.push_back(e->getLine());
352
0
        }
353
0
    }
354
0
}
355
356
void
357
PolygonizeGraph::label(std::vector<PolygonizeDirectedEdge*>& dirEdges, long label)
358
0
{
359
0
    for (auto & pde: dirEdges) {
360
0
        pde->setLabel(label);
361
0
    }
362
0
}
363
364
void
365
PolygonizeGraph::label(std::vector<DirectedEdge*>& dirEdges, long label)
366
0
{
367
0
    for(auto& de : dirEdges) {
368
0
        auto pde = detail::down_cast<PolygonizeDirectedEdge*>(de);
369
0
        pde->setLabel(label);
370
0
    }
371
0
}
372
373
void
374
PolygonizeGraph::computeNextCWEdges(Node* node)
375
0
{
376
0
    DirectedEdgeStar* deStar = node->getOutEdges();
377
0
    PolygonizeDirectedEdge* startDE = nullptr;
378
0
    PolygonizeDirectedEdge* prevDE = nullptr;
379
380
    // the edges are stored in CCW order around the star
381
0
    std::vector<DirectedEdge*>& pde = deStar->getEdges();
382
0
    for(DirectedEdge* de : pde) {
383
0
        auto outDE = detail::down_cast<PolygonizeDirectedEdge*>(de);
384
0
        if(outDE->isMarked()) {
385
0
            continue;
386
0
        }
387
0
        if(startDE == nullptr) {
388
0
            startDE = outDE;
389
0
        }
390
0
        if(prevDE != nullptr) {
391
0
            auto sym = detail::down_cast<PolygonizeDirectedEdge*>(prevDE->getSym());
392
0
            sym->setNext(outDE);
393
0
        }
394
0
        prevDE = outDE;
395
0
    }
396
0
    if(prevDE != nullptr) {
397
0
        auto sym = detail::down_cast<PolygonizeDirectedEdge*>(prevDE->getSym());
398
0
        sym->setNext(startDE);
399
0
    }
400
0
}
401
402
/**
403
 * Computes the next edge pointers going CCW around the given node, for the
404
 * given edgering label.
405
 * This algorithm has the effect of converting maximal edgerings into
406
 * minimal edgerings
407
 */
408
void
409
PolygonizeGraph::computeNextCCWEdges(Node* node, long label)
410
0
{
411
0
    DirectedEdgeStar* deStar = node->getOutEdges();
412
0
    PolygonizeDirectedEdge* firstOutDE = nullptr;
413
0
    PolygonizeDirectedEdge* prevInDE = nullptr;
414
415
    // the edges are stored in CCW order around the star
416
0
    std::vector<DirectedEdge*>& edges = deStar->getEdges();
417
418
0
    for(auto i = edges.size(); i > 0; --i) {
419
0
        PolygonizeDirectedEdge* de = detail::down_cast<PolygonizeDirectedEdge*>(edges[i - 1]);
420
0
        PolygonizeDirectedEdge* sym = detail::down_cast<PolygonizeDirectedEdge*>(de->getSym());
421
0
        PolygonizeDirectedEdge* outDE = nullptr;
422
0
        if(de->getLabel() == label) {
423
0
            outDE = de;
424
0
        }
425
0
        PolygonizeDirectedEdge* inDE = nullptr;
426
0
        if(sym->getLabel() == label) {
427
0
            inDE = sym;
428
0
        }
429
0
        if(outDE == nullptr && inDE == nullptr) {
430
0
            continue;    // this edge is not in edgering
431
0
        }
432
0
        if(inDE != nullptr) {
433
0
            prevInDE = inDE;
434
0
        }
435
0
        if(outDE != nullptr) {
436
0
            if(prevInDE != nullptr) {
437
0
                prevInDE->setNext(outDE);
438
0
                prevInDE = nullptr;
439
0
            }
440
0
            if(firstOutDE == nullptr) {
441
0
                firstOutDE = outDE;
442
0
            }
443
0
        }
444
0
    }
445
0
    if(prevInDE != nullptr) {
446
0
        assert(firstOutDE != nullptr);
447
0
        prevInDE->setNext(firstOutDE);
448
0
    }
449
0
}
450
451
EdgeRing*
452
PolygonizeGraph::findEdgeRing(PolygonizeDirectedEdge* startDE)
453
0
{
454
0
    PolygonizeDirectedEdge* de = startDE;
455
0
    EdgeRing* er = new EdgeRing(factory);
456
    // Now, when will we delete those EdgeRings ?
457
0
    newEdgeRings.push_back(er);
458
0
    do {
459
0
        er->add(de);
460
0
        de->setRing(er);
461
0
        de = de->getNext();
462
0
        assert(de != nullptr); // found NULL DE in ring
463
0
        assert(de == startDE || ! de->isInRing()); // found DE already in ring
464
0
    }
465
0
    while(de != startDE);
466
0
    return er;
467
0
}
468
469
/* public */
470
void
471
PolygonizeGraph::deleteDangles(std::vector<const SimpleCurve*>& dangleLines)
472
0
{
473
0
    std::vector<Node*> nodeStack;
474
0
    findNodesOfDegree(1, nodeStack);
475
476
0
    std::set<const SimpleCurve*> uniqueDangles;
477
478
0
    while(!nodeStack.empty()) {
479
0
        Node* node = nodeStack.back();
480
0
        nodeStack.pop_back();
481
0
        deleteAllEdges(node);
482
0
        auto nodeOutEdges = node->getOutEdges()->getEdges();
483
0
        for(DirectedEdge* de : nodeOutEdges) {
484
            // delete this edge and its sym
485
0
            de->setMarked(true);
486
0
            auto sym = dynamic_cast<PolygonizeDirectedEdge*>(de->getSym());
487
0
            if(sym != nullptr) {
488
0
                sym->setMarked(true);
489
0
            }
490
            // save the line as a dangle
491
0
            auto e = detail::down_cast<PolygonizeEdge*>(de->getEdge());
492
0
            const SimpleCurve* ls = e->getLine();
493
0
            if(uniqueDangles.insert(ls).second) {
494
0
                dangleLines.push_back(ls);
495
0
            }
496
0
            Node* toNode = de->getToNode();
497
            // add the toNode to the list to be processed,
498
            // if it is now a dangle
499
0
            if(getDegreeNonDeleted(toNode) == 1) {
500
0
                nodeStack.push_back(toNode);
501
0
            }
502
0
        }
503
0
    }
504
505
0
}
506
507
} // namespace geos.operation.polygonize
508
} // namespace geos.operation
509
} // namespace geos
510