/src/geos/src/operation/polygonize/Polygonizer.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (C) 2010 Sandro Santilli <strk@kbt.io> |
7 | | * Copyright (C) 2005-2006 Refractions Research Inc. |
8 | | * Copyright (C) 2001-2002 Vivid Solutions Inc. |
9 | | * |
10 | | * This is free software; you can redistribute and/or modify it under |
11 | | * the terms of the GNU Lesser General Public Licence as published |
12 | | * by the Free Software Foundation. |
13 | | * See the COPYING file for more information. |
14 | | * |
15 | | ********************************************************************** |
16 | | * |
17 | | * Last port: operation/polygonize/Polygonizer.java 0b3c7e3eb0d3e |
18 | | * |
19 | | **********************************************************************/ |
20 | | |
21 | | #include <geos/operation/polygonize/Polygonizer.h> |
22 | | #include <geos/operation/polygonize/PolygonizeGraph.h> |
23 | | #include <geos/operation/polygonize/EdgeRing.h> |
24 | | #include <geos/operation/polygonize/HoleAssigner.h> |
25 | | #include <geos/geom/CircularString.h> |
26 | | #include <geos/geom/CompoundCurve.h> |
27 | | #include <geos/geom/LineString.h> |
28 | | #include <geos/geom/Geometry.h> |
29 | | #include <geos/geom/GeometryFactory.h> |
30 | | #include <geos/geom/Polygon.h> |
31 | | #include <geos/util/Interrupt.h> |
32 | | // std |
33 | | #include <vector> |
34 | | |
35 | | #ifdef _MSC_VER |
36 | | #pragma warning(disable:4355) |
37 | | #endif |
38 | | |
39 | | #ifndef GEOS_DEBUG |
40 | | #define GEOS_DEBUG 0 |
41 | | #endif |
42 | | |
43 | | |
44 | | using namespace geos::geom; |
45 | | |
46 | | namespace geos { |
47 | | namespace operation { // geos.operation |
48 | | namespace polygonize { // geos.operation.polygonize |
49 | | |
50 | | Polygonizer::SimpleCurveAdder::SimpleCurveAdder(Polygonizer* p): |
51 | 0 | pol(p) |
52 | 0 | { |
53 | 0 | } |
54 | | |
55 | | void |
56 | | Polygonizer::SimpleCurveAdder::filter_ro(const Geometry* g) |
57 | 0 | { |
58 | 0 | if (g->getGeometryTypeId() == GEOS_COMPOUNDCURVE) { |
59 | 0 | const auto& cc = static_cast<const CompoundCurve&>(*g); |
60 | 0 | for (std::size_t i = 0; i < cc.getNumCurves(); i++) { |
61 | 0 | filter_ro(cc.getCurveN(i)); |
62 | 0 | } |
63 | |
|
64 | 0 | return; |
65 | 0 | } |
66 | | |
67 | 0 | if(const auto sc = dynamic_cast<const SimpleCurve*>(g)) { |
68 | 0 | pol->add(sc); |
69 | 0 | } |
70 | 0 | } |
71 | | |
72 | | Polygonizer::Polygonizer(bool onlyPolygonal): |
73 | 0 | lineStringAdder(this), |
74 | 0 | extractOnlyPolygonal(onlyPolygonal), |
75 | 0 | computed(false), |
76 | 0 | graph(nullptr), |
77 | 0 | dangles(), |
78 | 0 | cutEdges(), |
79 | 0 | invalidRingLines(), |
80 | 0 | holeList(), |
81 | 0 | shellList() |
82 | 0 | { |
83 | 0 | } |
84 | | |
85 | | void |
86 | | Polygonizer::add(std::vector<Geometry*>* geomList) |
87 | 0 | { |
88 | 0 | for(auto& g : (*geomList)) { |
89 | 0 | add(g); |
90 | 0 | } |
91 | 0 | } |
92 | | |
93 | | void |
94 | | Polygonizer::add(std::vector<const Geometry*>* geomList) |
95 | 0 | { |
96 | 0 | for(auto& g : (*geomList)) { |
97 | 0 | add(g); |
98 | 0 | } |
99 | 0 | } |
100 | | |
101 | | void |
102 | | Polygonizer::add(const Geometry* g) |
103 | 0 | { |
104 | 0 | g->apply_ro(&lineStringAdder); |
105 | 0 | } |
106 | | |
107 | | void |
108 | | Polygonizer::add(const SimpleCurve* line) |
109 | 0 | { |
110 | | // create a new graph using the factory from the input Geometry |
111 | 0 | if(graph == nullptr) { |
112 | 0 | graph.reset(new PolygonizeGraph(line->getFactory())); |
113 | 0 | } |
114 | |
|
115 | 0 | if (line->getGeometryTypeId() == GEOS_CIRCULARSTRING) { |
116 | 0 | graph->addEdge(detail::down_cast<const CircularString*>(line)); |
117 | 0 | } else { |
118 | 0 | graph->addEdge(detail::down_cast<const LineString*>(line)); |
119 | 0 | } |
120 | 0 | } |
121 | | |
122 | | std::vector<std::unique_ptr<Polygon>> |
123 | | Polygonizer::getPolygons() |
124 | 0 | { |
125 | 0 | polygonize(); |
126 | |
|
127 | 0 | std::vector<std::unique_ptr<Polygon>> ret; |
128 | 0 | ret.reserve(polyList.size()); |
129 | 0 | for (auto& surf : polyList) { |
130 | 0 | if (surf->getGeometryTypeId() == GEOS_POLYGON) { |
131 | 0 | ret.emplace_back(detail::down_cast<Polygon*>(surf.release())); |
132 | 0 | } |
133 | 0 | } |
134 | 0 | polyList.clear(); |
135 | |
|
136 | 0 | return ret; |
137 | 0 | } |
138 | | |
139 | | std::vector<std::unique_ptr<Surface>> |
140 | | Polygonizer::getSurfaces() |
141 | 0 | { |
142 | 0 | polygonize(); |
143 | |
|
144 | 0 | return std::move(polyList); |
145 | 0 | } |
146 | | |
147 | | |
148 | | /* public */ |
149 | | const std::vector<const SimpleCurve*>& |
150 | | Polygonizer::getDangles() |
151 | 0 | { |
152 | 0 | polygonize(); |
153 | 0 | return dangles; |
154 | 0 | } |
155 | | |
156 | | bool |
157 | 0 | Polygonizer::hasDangles() { |
158 | 0 | polygonize(); |
159 | 0 | return !dangles.empty(); |
160 | 0 | } |
161 | | |
162 | | /* public */ |
163 | | const std::vector<const SimpleCurve*>& |
164 | | Polygonizer::getCutEdges() |
165 | 0 | { |
166 | 0 | polygonize(); |
167 | 0 | return cutEdges; |
168 | 0 | } |
169 | | |
170 | | bool |
171 | | Polygonizer::hasCutEdges() |
172 | 0 | { |
173 | 0 | polygonize(); |
174 | 0 | return !cutEdges.empty(); |
175 | 0 | } |
176 | | |
177 | | /* public */ |
178 | | const std::vector<std::unique_ptr<Curve>>& |
179 | | Polygonizer::getInvalidRingLines() |
180 | 0 | { |
181 | 0 | polygonize(); |
182 | 0 | return invalidRingLines; |
183 | 0 | } |
184 | | |
185 | | bool |
186 | | Polygonizer::hasInvalidRingLines() |
187 | 0 | { |
188 | 0 | polygonize(); |
189 | 0 | return !invalidRingLines.empty(); |
190 | 0 | } |
191 | | |
192 | | bool |
193 | | Polygonizer::allInputsFormPolygons() |
194 | 0 | { |
195 | 0 | polygonize(); |
196 | 0 | return !hasCutEdges() && !hasDangles() &&!hasInvalidRingLines(); |
197 | 0 | } |
198 | | |
199 | | /* public */ |
200 | | void |
201 | | Polygonizer::polygonize() |
202 | 0 | { |
203 | | // check if already computed |
204 | 0 | if(computed) { |
205 | 0 | return; |
206 | 0 | } |
207 | | |
208 | | // if no geometries were supplied it's possible graph could be null |
209 | 0 | if(graph == nullptr) { |
210 | 0 | polyList.clear(); |
211 | 0 | return; |
212 | 0 | } |
213 | | |
214 | 0 | graph->deleteDangles(dangles); |
215 | |
|
216 | 0 | graph->deleteCutEdges(cutEdges); |
217 | |
|
218 | 0 | std::vector<EdgeRing*> edgeRingList; |
219 | 0 | graph->getEdgeRings(edgeRingList); |
220 | | #if GEOS_DEBUG |
221 | | std::cerr << "Polygonizer::polygonize(): " << edgeRingList.size() << " edgeRings in graph" << std::endl; |
222 | | #endif |
223 | 0 | std::vector<EdgeRing*> validEdgeRingList; |
224 | 0 | std::vector<EdgeRing*> invalidRings; |
225 | 0 | invalidRingLines.clear(); /* what if it was populated already ? we should clean ! */ |
226 | 0 | findValidRings(edgeRingList, validEdgeRingList, invalidRings); |
227 | 0 | invalidRingLines = extractInvalidLines(invalidRings); |
228 | | #if GEOS_DEBUG |
229 | | std::cerr << " " << validEdgeRingList.size() << " valid" << std::endl; |
230 | | std::cerr << " " << invalidRingLines.size() << " invalid" << std::endl; |
231 | | #endif |
232 | |
|
233 | 0 | findShellsAndHoles(validEdgeRingList); |
234 | | #if GEOS_DEBUG |
235 | | std::cerr << " " << holeList.size() << " holes" << std::endl; |
236 | | std::cerr << " " << shellList.size() << " shells" << std::endl; |
237 | | #endif |
238 | |
|
239 | 0 | HoleAssigner::assignHolesToShells(holeList, shellList); |
240 | |
|
241 | 0 | bool includeAll = true; |
242 | 0 | if (extractOnlyPolygonal) { |
243 | 0 | findDisjointShells(); |
244 | 0 | includeAll = false; |
245 | 0 | } |
246 | 0 | polyList = extractPolygons(shellList, includeAll); |
247 | |
|
248 | 0 | computed = true; |
249 | 0 | } |
250 | | |
251 | | /* private */ |
252 | | void |
253 | | Polygonizer::findValidRings(const std::vector<EdgeRing*>& edgeRingList, |
254 | | std::vector<EdgeRing*>& validEdgeRingList, |
255 | | std::vector<EdgeRing*>& invalidRingList) |
256 | 0 | { |
257 | 0 | for(const auto& er : edgeRingList) { |
258 | 0 | er->computeValid(); |
259 | 0 | if(er->isValid()) { |
260 | 0 | validEdgeRingList.push_back(er); |
261 | 0 | } |
262 | 0 | else { |
263 | 0 | invalidRingList.push_back(er); |
264 | 0 | } |
265 | 0 | GEOS_CHECK_FOR_INTERRUPTS(); |
266 | 0 | } |
267 | 0 | } |
268 | | |
269 | | std::vector<std::unique_ptr<geom::Curve>> |
270 | | Polygonizer::extractInvalidLines(std::vector<EdgeRing*>& invalidRings) |
271 | 0 | { |
272 | | /** |
273 | | * Sort rings by increasing envelope area. |
274 | | * This causes inner rings to be processed before the outer rings |
275 | | * containing them, which allows outer invalid rings to be discarded |
276 | | * since their linework is already reported in the inner rings. |
277 | | */ |
278 | 0 | std::sort(invalidRings.begin(), |
279 | 0 | invalidRings.end(), |
280 | 0 | [](EdgeRing* a, EdgeRing* b) { |
281 | 0 | return a->getRingInternal()->getEnvelope()->getArea() < |
282 | 0 | b->getRingInternal()->getEnvelope()->getArea(); |
283 | 0 | }); |
284 | | |
285 | | /** |
286 | | * Scan through rings. Keep only rings which have an adjacent EdgeRing |
287 | | * which is either valid or marked as not processed. |
288 | | * This avoids including outer rings which have linework which is duplicated. |
289 | | */ |
290 | 0 | std::vector<std::unique_ptr<Curve>> invalidLines; |
291 | 0 | for (EdgeRing* er : invalidRings) { |
292 | 0 | if (isIncludedInvalid(er)) { |
293 | 0 | auto ringGeom = er->getRingOwnership(); |
294 | 0 | if (ringGeom->getGeometryTypeId() == GEOS_LINEARRING) { |
295 | 0 | ringGeom = ringGeom->getFactory()->createLineString(ringGeom->getCoordinates()); |
296 | 0 | } |
297 | |
|
298 | 0 | invalidLines.push_back(std::move(ringGeom)); |
299 | 0 | } |
300 | 0 | er->setProcessed(true); |
301 | 0 | } |
302 | |
|
303 | 0 | return invalidLines; |
304 | 0 | } |
305 | | |
306 | | bool |
307 | | Polygonizer::isIncludedInvalid(const EdgeRing* invalidRing) |
308 | 0 | { |
309 | 0 | for (const PolygonizeDirectedEdge* de: invalidRing->getEdges()) { |
310 | 0 | const PolygonizeDirectedEdge* deAdj = static_cast<PolygonizeDirectedEdge*>(de->getSym()); |
311 | 0 | const EdgeRing* erAdj = deAdj->getRing(); |
312 | |
|
313 | 0 | bool isEdgeIncluded = erAdj->isValid() || erAdj->isProcessed(); |
314 | 0 | if (!isEdgeIncluded) { |
315 | 0 | return true; |
316 | 0 | } |
317 | 0 | } |
318 | | |
319 | 0 | return false; |
320 | 0 | } |
321 | | |
322 | | /* private */ |
323 | | void |
324 | | Polygonizer::findShellsAndHoles(const std::vector<EdgeRing*>& edgeRingList) |
325 | 0 | { |
326 | 0 | holeList.clear(); |
327 | 0 | shellList.clear(); |
328 | 0 | for(auto& er : edgeRingList) { |
329 | 0 | er->computeHole(); |
330 | 0 | if(er->isHole()) { |
331 | 0 | holeList.push_back(er); |
332 | 0 | } |
333 | 0 | else { |
334 | 0 | shellList.push_back(er); |
335 | 0 | } |
336 | |
|
337 | 0 | GEOS_CHECK_FOR_INTERRUPTS(); |
338 | 0 | } |
339 | 0 | } |
340 | | |
341 | | |
342 | | void |
343 | 0 | Polygonizer::findDisjointShells() { |
344 | 0 | findOuterShells(shellList); |
345 | |
|
346 | 0 | for (EdgeRing *er : shellList) { |
347 | 0 | if (!er->isIncludedSet()) { |
348 | 0 | er->updateIncludedRecursive(); |
349 | 0 | } |
350 | 0 | } |
351 | |
|
352 | 0 | return; |
353 | 0 | } |
354 | | |
355 | | void |
356 | | Polygonizer::findOuterShells(std::vector<EdgeRing*> & shells) |
357 | 0 | { |
358 | 0 | for (EdgeRing* er : shells) { |
359 | 0 | auto outerHoleER = er->getOuterHole(); |
360 | 0 | if (outerHoleER != nullptr && !outerHoleER->isProcessed()) { |
361 | 0 | er->setIncluded(true); |
362 | 0 | outerHoleER->setProcessed(true); |
363 | 0 | } |
364 | 0 | } |
365 | 0 | } |
366 | | |
367 | | std::vector<std::unique_ptr<Surface>> |
368 | | Polygonizer::extractPolygons(std::vector<EdgeRing*> & shells, bool includeAll) |
369 | 0 | { |
370 | 0 | std::vector<std::unique_ptr<Surface>> polys; |
371 | 0 | for (EdgeRing* er : shells) { |
372 | 0 | if (includeAll || er->isIncluded()) { |
373 | 0 | polys.emplace_back(er->getPolygon()); |
374 | 0 | } |
375 | 0 | } |
376 | |
|
377 | 0 | return polys; |
378 | 0 | } |
379 | | |
380 | | } // namespace geos.operation.polygonize |
381 | | } // namespace geos.operation |
382 | | } // namespace geos |
383 | | |