Coverage Report

Created: 2025-10-28 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/operation/overlayng/MaximalEdgeRing.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2020 Paul Ramsey <pramsey@cleverelephant.ca>
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
#include <geos/operation/overlayng/MaximalEdgeRing.h>
16
#include <geos/operation/overlayng/OverlayEdge.h>
17
#include <geos/geom/Location.h>
18
#include <geos/geom/Coordinate.h>
19
#include <geos/geom/CoordinateSequence.h>
20
#include <geos/geom/GeometryFactory.h>
21
#include <geos/util/TopologyException.h>
22
#include <geos/algorithm/locate/IndexedPointInAreaLocator.h>
23
#include <geos/io/WKTWriter.h>
24
25
#include <cassert>
26
27
namespace geos {      // geos
28
namespace operation { // geos.operation
29
namespace overlayng { // geos.operation.overlayng
30
31
using namespace geos::geom;
32
33
34
/*public static*/
35
void
36
MaximalEdgeRing::linkResultAreaMaxRingAtNode(OverlayEdge* nodeEdge)
37
643k
{
38
    // assertion is only valid if building a polygonal geometry (ie not a coverage)
39
643k
    assert(nodeEdge->isInResultArea());
40
41
    /**
42
     * Since the node edge is an out-edge,
43
     * make it the last edge to be linked
44
     * by starting at the next edge.
45
     * The node edge cannot be an in-edge as well,
46
     * but the next one may be the first in-edge.
47
     */
48
643k
    OverlayEdge* endOut = nodeEdge->oNextOE();
49
643k
    OverlayEdge* currOut = endOut;
50
51
643k
    int state = STATE_FIND_INCOMING;
52
643k
    OverlayEdge* currResultIn = nullptr;
53
1.76M
    do {
54
        /**
55
        * If an edge is linked this node has already been processed
56
        * so can skip further processing
57
        */
58
1.76M
        if (currResultIn != nullptr && currResultIn->isResultMaxLinked())
59
86.5k
            return;
60
61
1.67M
        switch (state) {
62
710k
            case STATE_FIND_INCOMING: {
63
710k
                OverlayEdge* currIn = currOut->symOE();
64
710k
                if (! currIn->isInResultArea())
65
74.7k
                    break;
66
635k
                currResultIn = currIn;
67
635k
                state = STATE_LINK_OUTGOING;
68
635k
                break;
69
710k
            }
70
963k
            case STATE_LINK_OUTGOING: {
71
963k
                if (! currOut->isInResultArea())
72
327k
                    break;
73
                // link the in edge to the out edge
74
635k
                currResultIn->setNextResultMax(currOut);
75
635k
                state = STATE_FIND_INCOMING;
76
635k
                break;
77
963k
            }
78
1.67M
        }
79
1.67M
        currOut = currOut->oNextOE();
80
1.67M
    }
81
1.67M
    while (currOut != endOut);
82
83
556k
    if (state == STATE_LINK_OUTGOING) {
84
0
        throw util::TopologyException("no outgoing edge found", nodeEdge->getCoordinate());
85
0
    }
86
556k
}
87
88
89
/*private*/
90
void
91
MaximalEdgeRing::attachEdges(OverlayEdge* p_startEdge)
92
177k
{
93
177k
    OverlayEdge* edge = p_startEdge;
94
630k
    do {
95
630k
        if (edge == nullptr)
96
0
            throw util::TopologyException("Ring edge is null");
97
630k
        if (edge->getEdgeRingMax() == this)
98
0
            throw util::TopologyException("Ring edge visited twice", edge->getCoordinate());
99
630k
        if (edge->nextResultMax() == nullptr) {
100
5.07k
            throw util::TopologyException("Ring edge missing", edge->dest());
101
5.07k
        }
102
625k
        edge->setEdgeRingMax(this);
103
625k
        edge = edge->nextResultMax();
104
625k
    }
105
625k
    while (edge != p_startEdge);
106
177k
}
107
108
/*public*/
109
std::vector<std::unique_ptr<OverlayEdgeRing>>
110
MaximalEdgeRing::buildMinimalRings(const GeometryFactory* geometryFactory)
111
171k
{
112
171k
    linkMinimalRings();
113
171k
    std::vector<std::unique_ptr<OverlayEdgeRing>> outOERs;
114
171k
    OverlayEdge* e = startEdge;
115
617k
    do {
116
617k
        if (e->getEdgeRing() == nullptr) {
117
191k
            outOERs.emplace_back(new OverlayEdgeRing(e, geometryFactory));
118
191k
        }
119
617k
        e = e->nextResultMax();
120
617k
    }
121
617k
    while (e != startEdge);
122
171k
    return outOERs;
123
171k
}
124
125
/*private*/
126
void
127
MaximalEdgeRing::linkMinimalRings()
128
171k
{
129
171k
    OverlayEdge* e = startEdge;
130
617k
    do {
131
617k
        linkMinRingEdgesAtNode(e, this);
132
617k
        e = e->nextResultMax();
133
617k
    }
134
617k
    while (e != startEdge);
135
171k
}
136
137
/*private static*/
138
void
139
MaximalEdgeRing::linkMinRingEdgesAtNode(OverlayEdge* nodeEdge, MaximalEdgeRing* maxRing)
140
617k
{
141
142
    /**
143
     * The node edge is an out-edge,
144
     * so it is the first edge linked
145
     * with the next CCW in-edge
146
     */
147
617k
    OverlayEdge* endOut = nodeEdge;
148
617k
    OverlayEdge* currMaxRingOut = endOut;
149
617k
    OverlayEdge* currOut = endOut->oNextOE();
150
151
1.13M
    do {
152
1.13M
        if (isAlreadyLinked(currOut->symOE(), maxRing))
153
20.0k
            return;
154
155
1.11M
        if (currMaxRingOut == nullptr) {
156
331k
            currMaxRingOut = selectMaxOutEdge(currOut, maxRing);
157
331k
        }
158
787k
        else {
159
787k
            currMaxRingOut = linkMaxInEdge(currOut, currMaxRingOut, maxRing);
160
787k
        }
161
1.11M
        currOut = currOut->oNextOE();
162
1.11M
    }
163
1.11M
    while (currOut != endOut);
164
165
597k
    if (currMaxRingOut != nullptr) {
166
0
        throw util::TopologyException("Unmatched edge found during min-ring linking", nodeEdge->getCoordinate());
167
0
    }
168
597k
}
169
170
/*private static*/
171
bool
172
MaximalEdgeRing::isAlreadyLinked(OverlayEdge* edge, MaximalEdgeRing* maxRing)
173
1.13M
{
174
1.13M
    bool isLinked = (edge->getEdgeRingMax() == maxRing) &&
175
637k
                    (edge->isResultLinked());
176
1.13M
    return isLinked;
177
1.13M
}
178
179
/*private static*/
180
OverlayEdge*
181
MaximalEdgeRing::selectMaxOutEdge(OverlayEdge* currOut, MaximalEdgeRing* maxEdgeRing)
182
331k
{
183
    // select if currOut edge is part of this max ring
184
331k
    if (currOut->getEdgeRingMax() ==  maxEdgeRing)
185
20.0k
        return currOut;
186
    // otherwise skip this edge
187
311k
    return nullptr;
188
331k
}
189
190
/*private static*/
191
OverlayEdge*
192
MaximalEdgeRing::linkMaxInEdge(OverlayEdge* currOut, OverlayEdge* currMaxRingOut, MaximalEdgeRing* maxEdgeRing)
193
787k
{
194
787k
    OverlayEdge* currIn = currOut->symOE();
195
787k
    if (currIn->getEdgeRingMax() !=  maxEdgeRing)
196
169k
        return currMaxRingOut;
197
198
617k
    currIn->setNextResult(currMaxRingOut);
199
617k
    return nullptr;
200
787k
}
201
202
/*public*/
203
std::ostream&
204
operator<<(std::ostream& os, const MaximalEdgeRing& mer)
205
0
{
206
0
    CoordinateSequence coords;
207
0
    OverlayEdge* edge = mer.startEdge;
208
0
    do {
209
0
        coords.add(edge->orig());
210
0
        if (edge->nextResultMax() == nullptr)
211
0
            break;
212
0
        edge = edge->nextResultMax();
213
0
    }
214
0
    while (edge != mer.startEdge);
215
0
    coords.add(edge->dest());
216
0
    std::string wkt = io::WKTWriter::toLineString(coords);
217
0
    os << wkt;
218
0
    return os;
219
220
0
}
221
222
223
224
} // namespace geos.operation.overlayng
225
} // namespace geos.operation
226
} // namespace geos