/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 |