Coverage Report

Created: 2025-08-25 06:57

/src/geos/src/operation/buffer/BufferSubgraph.cpp
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2005 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/buffer/BufferSubgraph.java r378 (JTS-1.12)
17
 *
18
 **********************************************************************/
19
20
#include <geos/operation/buffer/BufferSubgraph.h>
21
#include <geos/util/TopologyException.h>
22
#include <geos/geom/Envelope.h>
23
#include <geos/geom/CoordinateSequence.h>
24
#include <geos/geomgraph/Node.h>
25
#include <geos/geomgraph/Edge.h>
26
#include <geos/geomgraph/DirectedEdge.h>
27
#include <geos/geomgraph/DirectedEdgeStar.h>
28
#include <geos/geomgraph/EdgeEndStar.h>
29
#include <geos/geom/Position.h>
30
#include <geos/util.h>
31
32
#include <cassert>
33
#include <vector>
34
#include <iostream>
35
#include <list>
36
37
#ifndef GEOS_DEBUG
38
#define GEOS_DEBUG 0
39
#endif
40
41
42
using namespace geos::geomgraph;
43
using namespace geos::algorithm;
44
using namespace geos::operation;
45
using namespace geos::geom;
46
47
namespace geos {
48
namespace operation { // geos.operation
49
namespace buffer { // geos.operation.buffer
50
51
// Argument is unused
52
BufferSubgraph::BufferSubgraph()
53
    :
54
0
    finder(),
55
0
    dirEdgeList(),
56
0
    nodes(),
57
0
    rightMostCoord(nullptr),
58
0
    env(nullptr)
59
0
{
60
0
}
61
62
BufferSubgraph::~BufferSubgraph()
63
0
{
64
0
    delete env;
65
0
}
66
67
/*public*/
68
void
69
BufferSubgraph::create(Node* node)
70
0
{
71
0
    addReachable(node);
72
73
    // We are assuming that dirEdgeList
74
    // contains *at least* ONE forward DirectedEdge
75
0
    finder.findEdge(&dirEdgeList);
76
77
0
    rightMostCoord = &(finder.getCoordinate());
78
79
    // this is what happen if no forward DirectedEdge
80
    // is passed to the RightmostEdgeFinder
81
0
    assert(rightMostCoord);
82
0
}
83
84
/*private*/
85
void
86
BufferSubgraph::addReachable(Node* startNode)
87
0
{
88
0
    std::vector<Node*> nodeStack;
89
0
    nodeStack.push_back(startNode);
90
0
    while(!nodeStack.empty()) {
91
0
        Node* node = nodeStack.back();
92
0
        nodeStack.pop_back();
93
0
        add(node, &nodeStack);
94
0
    }
95
0
}
96
97
/*private*/
98
void
99
BufferSubgraph::add(Node* node, std::vector<Node*>* nodeStack)
100
0
{
101
0
    node->setVisited(true);
102
0
    nodes.push_back(node);
103
0
    EdgeEndStar* ees = node->getEdges();
104
0
    EdgeEndStar::iterator it = ees->begin();
105
0
    EdgeEndStar::iterator endIt = ees->end();
106
0
    for(; it != endIt; ++it) {
107
0
        DirectedEdge* de = detail::down_cast<DirectedEdge*>(*it);
108
0
        dirEdgeList.push_back(de);
109
0
        DirectedEdge* sym = de->getSym();
110
0
        Node* symNode = sym->getNode();
111
        /*
112
         * NOTE: this is a depth-first traversal of the graph.
113
         * This will cause a large depth of recursion.
114
         * It might be better to do a breadth-first traversal.
115
         */
116
0
        if(! symNode->isVisited()) {
117
0
            nodeStack->push_back(symNode);
118
0
        }
119
0
    }
120
0
}
121
122
/*private*/
123
void
124
BufferSubgraph::clearVisitedEdges()
125
0
{
126
0
    for(std::size_t i = 0, n = dirEdgeList.size(); i < n; ++i) {
127
0
        DirectedEdge* de = dirEdgeList[i];
128
0
        de->setVisited(false);
129
0
    }
130
0
}
131
132
/*public*/
133
void
134
BufferSubgraph::computeDepth(int outsideDepth)
135
0
{
136
0
    clearVisitedEdges();
137
    // find an outside edge to assign depth to
138
0
    DirectedEdge* de = finder.getEdge();
139
#if GEOS_DEBUG
140
    std::cerr << "outside depth: " << outsideDepth << std::endl;
141
#endif
142
    //Node *n=de->getNode();
143
    //Label *label=de->getLabel();
144
145
    // right side of line returned by finder is on the outside
146
0
    de->setEdgeDepths(Position::RIGHT, outsideDepth);
147
0
    copySymDepths(de);
148
149
    //computeNodeDepth(n, de);
150
0
    computeDepths(de);
151
0
}
152
153
void
154
BufferSubgraph::computeNodeDepth(Node* n)
155
// throw(TopologyException *)
156
0
{
157
    // find a visited dirEdge to start at
158
0
    DirectedEdge* startEdge = nullptr;
159
160
0
    DirectedEdgeStar* ees = detail::down_cast<DirectedEdgeStar*>(n->getEdges());
161
162
0
    EdgeEndStar::iterator endIt = ees->end();
163
164
0
    EdgeEndStar::iterator it = ees->begin();
165
0
    for(; it != endIt; ++it) {
166
0
        DirectedEdge* de = detail::down_cast<DirectedEdge*>(*it);
167
0
        if(de->isVisited() || de->getSym()->isVisited()) {
168
0
            startEdge = de;
169
0
            break;
170
0
        }
171
0
    }
172
    // MD - testing  Result: breaks algorithm
173
    //if (startEdge==null) return;
174
175
    // only compute string append if assertion would fail
176
0
    if(startEdge == nullptr) {
177
0
        throw util::TopologyException(
178
0
            "unable to find edge to compute depths at",
179
0
            n->getCoordinate());
180
0
    }
181
182
0
    ees->computeDepths(startEdge);
183
184
    // copy depths to sym edges
185
0
    for(it = ees->begin(); it != endIt; ++it) {
186
0
        DirectedEdge* de = detail::down_cast<DirectedEdge*>(*it);
187
0
        de->setVisited(true);
188
0
        copySymDepths(de);
189
0
    }
190
0
}
191
192
/*private*/
193
void
194
BufferSubgraph::copySymDepths(DirectedEdge* de)
195
0
{
196
#if GEOS_DEBUG
197
    std::cerr << "copySymDepths: " << de->getDepth(Position::LEFT)
198
         << ", " << de->getDepth(Position::RIGHT)
199
         << std::endl;
200
#endif
201
0
    DirectedEdge* sym = de->getSym();
202
0
    sym->setDepth(Position::LEFT, de->getDepth(Position::RIGHT));
203
0
    sym->setDepth(Position::RIGHT, de->getDepth(Position::LEFT));
204
0
}
205
206
/*public*/
207
void
208
BufferSubgraph::findResultEdges()
209
0
{
210
#if GEOS_DEBUG
211
    std::cerr << "BufferSubgraph::findResultEdges got " << dirEdgeList.size() << " edges" << std::endl;
212
#endif
213
0
    for(std::size_t i = 0, n = dirEdgeList.size(); i < n; ++i) {
214
0
        DirectedEdge* de = dirEdgeList[i];
215
216
        /*
217
         * Select edges which have an interior depth on the RHS
218
         * and an exterior depth on the LHS.
219
         * Note that because of weird rounding effects there may be
220
         * edges which have negative depths!  Negative depths
221
         * count as "outside".
222
         */
223
        // <FIX> - handle negative depths
224
#if GEOS_DEBUG
225
        std::cerr << " dirEdge " << i << ": " << de->printEdge() << std::endl
226
             << "         depth right: " << de->getDepth(Position::RIGHT) << std::endl
227
             << "          depth left: " << de->getDepth(Position::LEFT) << std::endl
228
             << "    interiorAreaEdge: " << de->isInteriorAreaEdge() << std::endl;
229
#endif
230
0
        if(de->getDepth(Position::RIGHT) >= 1
231
0
                &&  de->getDepth(Position::LEFT) <= 0
232
0
                && !de->isInteriorAreaEdge()) {
233
0
            de->setInResult(true);
234
#if GEOS_DEBUG
235
            std::cerr << "   IN RESULT" << std::endl;
236
#endif
237
0
        }
238
0
    }
239
0
}
240
241
/*public*/
242
int
243
BufferSubgraph::compareTo(BufferSubgraph* graph)
244
0
{
245
0
    assert(rightMostCoord);
246
0
    if(rightMostCoord->x < graph->rightMostCoord->x) {
247
0
        return -1;
248
0
    }
249
0
    if(rightMostCoord->x > graph->rightMostCoord->x) {
250
0
        return 1;
251
0
    }
252
0
    return 0;
253
0
}
254
255
/*private*/
256
void
257
BufferSubgraph::computeDepths(DirectedEdge* startEdge)
258
0
{
259
0
    std::set<Node*> nodesVisited;
260
0
    std::list<Node*> nodeQueue; // Used to be a vector
261
0
    Node* startNode = startEdge->getNode();
262
0
    nodeQueue.push_back(startNode);
263
    //nodesVisited.push_back(startNode);
264
0
    nodesVisited.insert(startNode);
265
0
    startEdge->setVisited(true);
266
267
0
    while(! nodeQueue.empty()) {
268
        //System.out.println(nodes.size() + " queue: " + nodeQueue.size());
269
0
        Node* n = nodeQueue.front(); // [0];
270
        //nodeQueue.erase(nodeQueue.begin());
271
0
        nodeQueue.pop_front();
272
273
0
        nodesVisited.insert(n);
274
275
        // compute depths around node, starting at this edge since it has depths assigned
276
0
        computeNodeDepth(n);
277
278
        // add all adjacent nodes to process queue,
279
        // unless the node has been visited already
280
0
        EdgeEndStar* ees = n->getEdges();
281
0
        EdgeEndStar::iterator endIt = ees->end();
282
0
        EdgeEndStar::iterator it = ees->begin();
283
0
        for(; it != endIt; ++it) {
284
0
            DirectedEdge* de = detail::down_cast<DirectedEdge*>(*it);
285
0
            DirectedEdge* sym = de->getSym();
286
0
            if(sym->isVisited()) {
287
0
                continue;
288
0
            }
289
0
            Node* adjNode = sym->getNode();
290
291
            //if (! contains(nodesVisited,adjNode))
292
0
            if(nodesVisited.insert(adjNode).second) {
293
0
                nodeQueue.push_back(adjNode);
294
                //nodesVisited.insert(adjNode);
295
0
            }
296
0
        }
297
0
    }
298
0
}
299
300
/*private*/
301
bool
302
BufferSubgraph::contains(std::set<Node*>& nodeSet, Node* node)
303
0
{
304
    //bool result=false;
305
0
    if(nodeSet.find(node) != nodeSet.end()) {
306
0
        return true;
307
0
    }
308
0
    return false;
309
0
}
310
311
/*public*/
312
Envelope*
313
BufferSubgraph::getEnvelope()
314
0
{
315
0
    if(env == nullptr) {
316
0
        env = new Envelope();
317
0
        std::size_t const size = dirEdgeList.size();
318
0
        for(std::size_t i = 0; i < size; ++i) {
319
0
            DirectedEdge* dirEdge = dirEdgeList[i];
320
0
            const CoordinateSequence* pts = dirEdge->getEdge()->getCoordinates();
321
0
            std::size_t const n = pts->getSize() - 1;
322
0
            for(std::size_t j = 0; j < n; ++j) {
323
0
                env->expandToInclude(pts->getAt(j));
324
0
            }
325
0
        }
326
0
    }
327
0
    return env;
328
0
}
329
330
std::ostream&
331
operator<< (std::ostream& os, const BufferSubgraph& bs)
332
0
{
333
0
    os << "BufferSubgraph[" << &bs << "] "
334
0
       << bs.nodes.size() << " nodes, "
335
0
       << bs.dirEdgeList.size() << " directed edges" << std::endl;
336
337
0
    for(std::size_t i = 0, n = bs.nodes.size(); i < n; i++) {
338
0
        os << "  Node " << i << ": " << *(bs.nodes[i]) << std::endl;
339
0
    }
340
341
0
    for(std::size_t i = 0, n = bs.dirEdgeList.size(); i < n; i++) {
342
0
        os << "  DirEdge " << i << ": " << std::endl
343
0
           << bs.dirEdgeList[i]->printEdge() << std::endl;
344
0
    }
345
346
0
    return os;
347
0
}
348
349
} // namespace geos.operation.buffer
350
} // namespace geos.operation
351
} // namespace geos