/src/geos/src/operation/buffer/PolygonBuilder.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) 2001-2002 Vivid Solutions Inc. |
7 | | * Copyright (C) 2005 Refractions Research 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/overlay/PolygonBuilder.java rev. 1.20 (JTS-1.10) |
17 | | * |
18 | | **********************************************************************/ |
19 | | |
20 | | #include <geos/operation/buffer/PolygonBuilder.h> |
21 | | #include <geos/operation/buffer/MaximalEdgeRing.h> |
22 | | #include <geos/operation/buffer/MinimalEdgeRing.h> |
23 | | #include <geos/operation/polygonize/EdgeRing.h> |
24 | | #include <geos/geomgraph/Node.h> |
25 | | #include <geos/geomgraph/NodeMap.h> |
26 | | #include <geos/geomgraph/DirectedEdgeStar.h> |
27 | | #include <geos/geomgraph/PlanarGraph.h> |
28 | | #include <geos/geom/GeometryFactory.h> |
29 | | #include <geos/geom/LinearRing.h> |
30 | | #include <geos/geom/Polygon.h> |
31 | | #include <geos/algorithm/PointLocation.h> |
32 | | #include <geos/util/TopologyException.h> |
33 | | #include <geos/util/GEOSException.h> |
34 | | #include <geos/util.h> |
35 | | |
36 | | |
37 | | #include <vector> |
38 | | #include <cassert> |
39 | | |
40 | | #ifndef GEOS_DEBUG |
41 | | #define GEOS_DEBUG 0 |
42 | | #endif |
43 | | |
44 | | #if GEOS_DEBUG |
45 | | #include <iostream> |
46 | | #endif |
47 | | |
48 | | using namespace geos::geomgraph; |
49 | | using namespace geos::algorithm; |
50 | | using namespace geos::geom; |
51 | | |
52 | | namespace geos { |
53 | | namespace operation { // geos.operation |
54 | | namespace buffer { // geos.operation.buffer |
55 | | |
56 | | PolygonBuilder::PolygonBuilder(const GeometryFactory* newGeometryFactory) |
57 | | : |
58 | 0 | geometryFactory(newGeometryFactory) |
59 | 0 | { |
60 | 0 | } |
61 | | |
62 | | PolygonBuilder::~PolygonBuilder() |
63 | 0 | { |
64 | 0 | for(std::size_t i = 0, n = shellList.size(); i < n; ++i) { |
65 | 0 | delete shellList[i]; |
66 | 0 | } |
67 | 0 | } |
68 | | |
69 | | /*public*/ |
70 | | void |
71 | | PolygonBuilder::add(PlanarGraph* graph) |
72 | | //throw(TopologyException *) |
73 | 0 | { |
74 | 0 | const std::vector<EdgeEnd*>* eeptr = graph->getEdgeEnds(); |
75 | 0 | assert(eeptr); |
76 | 0 | const std::vector<EdgeEnd*>& ee = *eeptr; |
77 | |
|
78 | 0 | std::size_t eeSize = ee.size(); |
79 | |
|
80 | | #if GEOS_DEBUG |
81 | | std::cerr << __FUNCTION__ << ": PlanarGraph has " << eeSize << " EdgeEnds" << std::endl; |
82 | | #endif |
83 | |
|
84 | 0 | std::vector<DirectedEdge*> dirEdges(eeSize); |
85 | 0 | for(std::size_t i = 0; i < eeSize; ++i) { |
86 | 0 | DirectedEdge* de = detail::down_cast<DirectedEdge*>(ee[i]); |
87 | 0 | dirEdges[i] = de; |
88 | 0 | } |
89 | |
|
90 | 0 | const auto& nodeMap = graph->getNodeMap()->nodeMap; |
91 | 0 | std::vector<Node*> nodes; |
92 | 0 | nodes.reserve(nodeMap.size()); |
93 | 0 | for(const auto& nodeIt: nodeMap) { |
94 | 0 | Node* node = nodeIt.second.get(); |
95 | 0 | nodes.push_back(node); |
96 | 0 | } |
97 | |
|
98 | 0 | add(&dirEdges, &nodes); // might throw a TopologyException * |
99 | 0 | } |
100 | | |
101 | | /*public*/ |
102 | | void |
103 | | PolygonBuilder::add(const std::vector<DirectedEdge*>* dirEdges, |
104 | | const std::vector<Node*>* nodes) |
105 | | //throw(TopologyException *) |
106 | 0 | { |
107 | 0 | PlanarGraph::linkResultDirectedEdges(nodes->begin(), nodes->end()); |
108 | |
|
109 | 0 | std::vector<MaximalEdgeRing*> maxEdgeRings; |
110 | 0 | buildMaximalEdgeRings(dirEdges, maxEdgeRings); |
111 | |
|
112 | 0 | std::vector<EdgeRing*> freeHoleList; |
113 | 0 | std::vector<MaximalEdgeRing*> edgeRings; |
114 | 0 | buildMinimalEdgeRings(maxEdgeRings, shellList, freeHoleList, edgeRings); |
115 | |
|
116 | 0 | sortShellsAndHoles(edgeRings, shellList, freeHoleList); |
117 | |
|
118 | 0 | std::vector<FastPIPRing> indexedshellist; |
119 | 0 | for(auto const& shell : shellList) { |
120 | 0 | FastPIPRing pipRing { shell, new geos::algorithm::locate::IndexedPointInAreaLocator(*shell->getLinearRing()) }; |
121 | 0 | indexedshellist.push_back(pipRing); |
122 | 0 | } |
123 | 0 | placeFreeHoles(indexedshellist, freeHoleList); |
124 | | //Assert: every hole on freeHoleList has a shell assigned to it |
125 | |
|
126 | 0 | for(auto const& shell : indexedshellist) { |
127 | 0 | delete shell.pipLocator; |
128 | 0 | } |
129 | 0 | } |
130 | | |
131 | | /*public*/ |
132 | | std::vector<std::unique_ptr<Geometry>> |
133 | | PolygonBuilder::getPolygons() |
134 | 0 | { |
135 | 0 | std::vector<std::unique_ptr<Geometry>> resultPolyList = computePolygons(shellList); |
136 | 0 | return resultPolyList; |
137 | 0 | } |
138 | | |
139 | | |
140 | | /*private*/ |
141 | | void |
142 | | PolygonBuilder::buildMaximalEdgeRings(const std::vector<DirectedEdge*>* dirEdges, |
143 | | std::vector<MaximalEdgeRing*>& maxEdgeRings) |
144 | | // throw(const TopologyException &) |
145 | 0 | { |
146 | | #if GEOS_DEBUG |
147 | | std::cerr << "PolygonBuilder::buildMaximalEdgeRings got " << dirEdges->size() << " dirEdges" << std::endl; |
148 | | #endif |
149 | |
|
150 | 0 | std::vector<MaximalEdgeRing*>::size_type oldSize = maxEdgeRings.size(); |
151 | |
|
152 | 0 | for(std::size_t i = 0, n = dirEdges->size(); i < n; i++) { |
153 | 0 | DirectedEdge* de = (*dirEdges)[i]; |
154 | | #if GEOS_DEBUG |
155 | | std::cerr << " dirEdge " << i << std::endl |
156 | | << de->printEdge() << std::endl |
157 | | << " inResult:" << de->isInResult() << std::endl |
158 | | << " isArea:" << de->getLabel().isArea() << std::endl; |
159 | | #endif |
160 | 0 | if(de->isInResult() && de->getLabel().isArea()) { |
161 | | // if this edge has not yet been processed |
162 | 0 | if(de->getEdgeRing() == nullptr) { |
163 | 0 | MaximalEdgeRing* er; |
164 | 0 | try { |
165 | | // MaximalEdgeRing constructor may throw |
166 | 0 | er = new MaximalEdgeRing(de, geometryFactory); |
167 | 0 | } |
168 | 0 | catch(util::GEOSException&) { |
169 | | // cleanup if that happens (see stmlf-cases-20061020.xml) |
170 | 0 | for(std::size_t p_i = oldSize, p_n = maxEdgeRings.size(); p_i < p_n; p_i++) { |
171 | 0 | delete maxEdgeRings[p_i]; |
172 | 0 | } |
173 | | //cerr << "Exception! " << e.what() << std::endl; |
174 | 0 | throw; |
175 | 0 | } |
176 | 0 | maxEdgeRings.push_back(er); |
177 | 0 | er->setInResult(); |
178 | | //System.out.println("max node degree=" + er.getMaxDegree()); |
179 | 0 | } |
180 | 0 | } |
181 | 0 | } |
182 | | #if GEOS_DEBUG |
183 | | std::cerr << " pushed " << maxEdgeRings.size() - oldSize << " maxEdgeRings" << std::endl; |
184 | | #endif |
185 | 0 | } |
186 | | |
187 | | /*private*/ |
188 | | void |
189 | | PolygonBuilder::buildMinimalEdgeRings( |
190 | | std::vector<MaximalEdgeRing*>& maxEdgeRings, |
191 | | std::vector<EdgeRing*>& newShellList, std::vector<EdgeRing*>& freeHoleList, |
192 | | std::vector<MaximalEdgeRing*>& edgeRings) |
193 | 0 | { |
194 | 0 | for(std::size_t i = 0, n = maxEdgeRings.size(); i < n; ++i) { |
195 | 0 | MaximalEdgeRing* er = maxEdgeRings[i]; |
196 | | #if GEOS_DEBUG |
197 | | std::cerr << "buildMinimalEdgeRings: maxEdgeRing " << i << " has " << er->getMaxNodeDegree() << " maxNodeDegree" << std::endl; |
198 | | #endif |
199 | 0 | if(er->getMaxNodeDegree() > 2) { |
200 | 0 | er->linkDirectedEdgesForMinimalEdgeRings(); |
201 | 0 | std::vector<MinimalEdgeRing*> minEdgeRings; |
202 | 0 | er->buildMinimalRings(minEdgeRings); |
203 | | // at this point we can go ahead and attempt to place |
204 | | // holes, if this EdgeRing is a polygon |
205 | 0 | EdgeRing* shell = findShell(&minEdgeRings); |
206 | 0 | if(shell != nullptr) { |
207 | 0 | placePolygonHoles(shell, &minEdgeRings); |
208 | 0 | newShellList.push_back(shell); |
209 | 0 | } |
210 | 0 | else { |
211 | 0 | freeHoleList.insert(freeHoleList.end(), |
212 | 0 | minEdgeRings.begin(), |
213 | 0 | minEdgeRings.end()); |
214 | 0 | } |
215 | 0 | delete er; |
216 | 0 | } |
217 | 0 | else { |
218 | 0 | edgeRings.push_back(er); |
219 | 0 | } |
220 | 0 | } |
221 | 0 | } |
222 | | |
223 | | /*private*/ |
224 | | EdgeRing* |
225 | | PolygonBuilder::findShell(std::vector<MinimalEdgeRing*>* minEdgeRings) |
226 | 0 | { |
227 | 0 | int shellCount = 0; |
228 | 0 | EdgeRing* shell = nullptr; |
229 | |
|
230 | | #if GEOS_DEBUG |
231 | | std::cerr << "PolygonBuilder::findShell got " << minEdgeRings->size() << " minEdgeRings" << std::endl; |
232 | | #endif |
233 | |
|
234 | 0 | for(std::size_t i = 0, n = minEdgeRings->size(); i < n; ++i) { |
235 | 0 | EdgeRing* er = (*minEdgeRings)[i]; |
236 | 0 | if(! er->isHole()) { |
237 | 0 | shell = er; |
238 | 0 | ++shellCount; |
239 | 0 | } |
240 | 0 | } |
241 | |
|
242 | 0 | if(shellCount > 1) { |
243 | 0 | throw util::TopologyException("found two shells in MinimalEdgeRing list"); |
244 | 0 | } |
245 | | |
246 | 0 | return shell; |
247 | 0 | } |
248 | | |
249 | | /*private*/ |
250 | | void |
251 | | PolygonBuilder::placePolygonHoles(EdgeRing* shell, |
252 | | std::vector<MinimalEdgeRing*>* minEdgeRings) |
253 | 0 | { |
254 | 0 | for(std::size_t i = 0, n = minEdgeRings->size(); i < n; ++i) { |
255 | 0 | MinimalEdgeRing* er = (*minEdgeRings)[i]; |
256 | 0 | if(er->isHole()) { |
257 | 0 | er->setShell(shell); |
258 | 0 | } |
259 | 0 | } |
260 | 0 | } |
261 | | |
262 | | /*private*/ |
263 | | void |
264 | | PolygonBuilder::sortShellsAndHoles(std::vector<MaximalEdgeRing*>& edgeRings, |
265 | | std::vector<EdgeRing*>& newShellList, std::vector<EdgeRing*>& freeHoleList) |
266 | 0 | { |
267 | 0 | for(std::size_t i = 0, n = edgeRings.size(); i < n; i++) { |
268 | 0 | EdgeRing* er = edgeRings[i]; |
269 | | //er->setInResult(); |
270 | 0 | if(er->isHole()) { |
271 | 0 | freeHoleList.push_back(er); |
272 | 0 | } |
273 | 0 | else { |
274 | 0 | newShellList.push_back(er); |
275 | 0 | } |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | /*private*/ |
280 | | void |
281 | | PolygonBuilder::placeFreeHoles(std::vector<FastPIPRing>& newShellList, |
282 | | std::vector<EdgeRing*>& freeHoleList) |
283 | 0 | { |
284 | 0 | for(std::vector<EdgeRing*>::iterator |
285 | 0 | it = freeHoleList.begin(), itEnd = freeHoleList.end(); |
286 | 0 | it != itEnd; |
287 | 0 | ++it) { |
288 | 0 | EdgeRing* hole = *it; |
289 | | // only place this hole if it doesn't yet have a shell |
290 | 0 | if(hole->getShell() == nullptr) { |
291 | 0 | EdgeRing* shell = findEdgeRingContaining(hole, newShellList); |
292 | | /** |
293 | | * If hole lies outside shell, discard it. |
294 | | */ |
295 | 0 | if(shell != nullptr) { |
296 | 0 | hole->setShell(shell); |
297 | 0 | } |
298 | 0 | else { |
299 | 0 | delete hole; |
300 | 0 | } |
301 | |
|
302 | 0 | } |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | | /*private*/ |
307 | | EdgeRing* |
308 | | PolygonBuilder::findEdgeRingContaining(EdgeRing* testEr, |
309 | | std::vector<FastPIPRing>& newShellList) |
310 | 0 | { |
311 | 0 | LinearRing* testRing = testEr->getLinearRing(); |
312 | 0 | const Envelope* testEnv = testRing->getEnvelopeInternal(); |
313 | 0 | EdgeRing* minShell = nullptr; |
314 | 0 | const Envelope* minShellEnv = nullptr; |
315 | |
|
316 | 0 | for(auto const& tryShell : newShellList) { |
317 | 0 | LinearRing* tryShellRing = tryShell.edgeRing->getLinearRing(); |
318 | 0 | const Envelope* tryShellEnv = tryShellRing->getEnvelopeInternal(); |
319 | | // the hole envelope cannot equal the shell envelope |
320 | | // (also guards against testing rings against themselves) |
321 | 0 | if(tryShellEnv->equals(testEnv)) { |
322 | 0 | continue; |
323 | 0 | } |
324 | | // hole must be contained in shell |
325 | 0 | if(!tryShellEnv->contains(testEnv)) { |
326 | 0 | continue; |
327 | 0 | } |
328 | | |
329 | 0 | const CoordinateSequence* tsrcs = tryShellRing->getCoordinatesRO(); |
330 | 0 | const Coordinate& testPt = operation::polygonize::EdgeRing::ptNotInList(testRing->getCoordinatesRO(), tsrcs); |
331 | |
|
332 | 0 | bool isContained = false; |
333 | 0 | if(tryShell.pipLocator->locate(&testPt) != Location::EXTERIOR) { |
334 | 0 | isContained = true; |
335 | 0 | } |
336 | | |
337 | | // check if this new containing ring is smaller than |
338 | | // the current minimum ring |
339 | 0 | if(isContained) { |
340 | 0 | if(minShell == nullptr |
341 | 0 | || minShellEnv->contains(tryShellEnv)) { |
342 | 0 | minShell = tryShell.edgeRing; |
343 | 0 | minShellEnv = minShell->getLinearRing()->getEnvelopeInternal(); |
344 | 0 | } |
345 | 0 | } |
346 | 0 | } |
347 | 0 | return minShell; |
348 | 0 | } |
349 | | |
350 | | /*private*/ |
351 | | std::vector<std::unique_ptr<Geometry>> |
352 | | PolygonBuilder::computePolygons(std::vector<EdgeRing*>& newShellList) |
353 | 0 | { |
354 | | #if GEOS_DEBUG |
355 | | std::cerr << "PolygonBuilder::computePolygons: got " << newShellList.size() << " shells" << std::endl; |
356 | | #endif |
357 | 0 | std::vector<std::unique_ptr<Geometry>> resultPolyList; |
358 | | |
359 | | // add Polygons for all shells |
360 | 0 | for(std::size_t i = 0, n = newShellList.size(); i < n; i++) { |
361 | 0 | EdgeRing* er = newShellList[i]; |
362 | 0 | auto poly = er->toPolygon(geometryFactory); |
363 | 0 | resultPolyList.push_back(std::move(poly)); |
364 | 0 | } |
365 | 0 | return resultPolyList; |
366 | 0 | } |
367 | | |
368 | | |
369 | | } // namespace geos.operation.buffer |
370 | | } // namespace geos.operation |
371 | | } // namespace geos |
372 | | |