/src/geos/src/operation/polygonize/EdgeRing.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (C) 2005-2006 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/polygonize/EdgeRing.java 0b3c7e3eb0d3e |
17 | | * |
18 | | **********************************************************************/ |
19 | | |
20 | | #include <geos/operation/polygonize/EdgeRing.h> |
21 | | #include <geos/operation/polygonize/PolygonizeEdge.h> |
22 | | #include <geos/planargraph/DirectedEdge.h> |
23 | | #include <geos/geom/CoordinateSequence.h> |
24 | | #include <geos/geom/LinearRing.h> |
25 | | #include <geos/geom/Coordinate.h> |
26 | | #include <geos/geom/Envelope.h> |
27 | | #include <geos/geom/GeometryFactory.h> |
28 | | #include <geos/algorithm/PointLocation.h> |
29 | | #include <geos/algorithm/Orientation.h> |
30 | | #include <geos/util/IllegalArgumentException.h> |
31 | | #include <geos/util.h> // TODO: drop this, includes too much |
32 | | #include <geos/algorithm/locate/IndexedPointInAreaLocator.h> |
33 | | #include <geos/geom/Location.h> |
34 | | |
35 | | #include <vector> |
36 | | #include <cassert> |
37 | | |
38 | | //#define DEBUG_ALLOC 1 |
39 | | //#define GEOS_PARANOIA_LEVEL 2 |
40 | | |
41 | | |
42 | | using namespace geos::planargraph; |
43 | | using namespace geos::algorithm; |
44 | | using namespace geos::geom; |
45 | | |
46 | | namespace geos { |
47 | | namespace operation { // geos.operation |
48 | | namespace polygonize { // geos.operation.polygonize |
49 | | |
50 | | /*public*/ |
51 | | EdgeRing* |
52 | | EdgeRing::findEdgeRingContaining(const std::vector<EdgeRing*> & erList) |
53 | 0 | { |
54 | 0 | const LinearRing* testRing = getRingInternal(); |
55 | 0 | if(! testRing) { |
56 | 0 | return nullptr; |
57 | 0 | } |
58 | 0 | const Envelope* testEnv = testRing->getEnvelopeInternal(); |
59 | 0 | EdgeRing* minRing = nullptr; |
60 | 0 | const Envelope* minRingEnv = nullptr; |
61 | |
|
62 | 0 | for(auto& tryEdgeRing : erList) { |
63 | 0 | auto tryRing = tryEdgeRing->getRingInternal(); |
64 | 0 | auto tryShellEnv = tryRing->getEnvelopeInternal(); |
65 | | // the hole envelope cannot equal the shell envelope |
66 | | // (also guards against testing rings against themselves) |
67 | 0 | if (tryShellEnv->equals(testEnv)) { |
68 | 0 | continue; |
69 | 0 | } |
70 | | // hole must be contained in shell |
71 | 0 | if (!tryShellEnv->contains(testEnv)) { |
72 | 0 | continue; |
73 | 0 | } |
74 | | |
75 | 0 | auto tryCoords = tryRing->getCoordinatesRO(); |
76 | 0 | const Coordinate& testPt = ptNotInList(testRing->getCoordinatesRO(), tryCoords); |
77 | | |
78 | | // check if this new containing ring is smaller than the current minimum ring |
79 | 0 | if(tryEdgeRing->isInRing(testPt)) { |
80 | 0 | if(minRing == nullptr || minRingEnv->contains(tryShellEnv)) { |
81 | 0 | minRing = tryEdgeRing; |
82 | 0 | minRingEnv = minRing->getRingInternal()->getEnvelopeInternal(); |
83 | 0 | } |
84 | 0 | } |
85 | 0 | } |
86 | 0 | return minRing; |
87 | 0 | } |
88 | | |
89 | | std::vector<PolygonizeDirectedEdge*> |
90 | 0 | EdgeRing::findDirEdgesInRing(PolygonizeDirectedEdge* startDE) { |
91 | 0 | auto de = startDE; |
92 | 0 | std::vector<decltype(de)> edges; |
93 | |
|
94 | 0 | do { |
95 | 0 | edges.push_back(de); |
96 | 0 | de = de->getNext(); |
97 | 0 | } while (de != startDE); |
98 | |
|
99 | 0 | return edges; |
100 | 0 | } |
101 | | |
102 | | /*public static*/ |
103 | | const Coordinate& |
104 | | EdgeRing::ptNotInList(const CoordinateSequence* testPts, |
105 | | const CoordinateSequence* pts) |
106 | 11.3k | { |
107 | 11.3k | const std::size_t npts = testPts->getSize(); |
108 | 12.7k | for(std::size_t i = 0; i < npts; ++i) { |
109 | 12.6k | const Coordinate& testPt = testPts->getAt(i); |
110 | 12.6k | if(!isInList(testPt, pts)) { |
111 | 11.2k | return testPt; |
112 | 11.2k | } |
113 | 12.6k | } |
114 | 197 | return Coordinate::getNull(); |
115 | 11.3k | } |
116 | | |
117 | | /*public static*/ |
118 | | bool |
119 | | EdgeRing::isInList(const Coordinate& pt, |
120 | | const CoordinateSequence* pts) |
121 | 12.6k | { |
122 | 12.6k | const std::size_t npts = pts->getSize(); |
123 | 222k | for(std::size_t i = 0; i < npts; ++i) { |
124 | 211k | if(pt == pts->getAt(i)) { |
125 | 1.40k | return true; |
126 | 1.40k | } |
127 | 211k | } |
128 | 11.2k | return false; |
129 | 12.6k | } |
130 | | |
131 | | /*public*/ |
132 | | EdgeRing::EdgeRing(const GeometryFactory* newFactory) |
133 | | : |
134 | 0 | factory(newFactory), |
135 | 0 | ring(nullptr), |
136 | 0 | ringPts(nullptr), |
137 | 0 | holes(nullptr), |
138 | 0 | is_hole(false) |
139 | 0 | { |
140 | | #ifdef DEBUG_ALLOC |
141 | | std::cerr << "[" << this << "] EdgeRing(factory)" << std::endl; |
142 | | #endif // DEBUG_ALLOC |
143 | 0 | } |
144 | | |
145 | | void |
146 | 0 | EdgeRing::build(PolygonizeDirectedEdge* startDE) { |
147 | 0 | auto de = startDE; |
148 | 0 | do { |
149 | 0 | add(de); |
150 | 0 | de->setRing(this); |
151 | 0 | de = de->getNext(); |
152 | 0 | } while (de != startDE); |
153 | 0 | } |
154 | | |
155 | | /*public*/ |
156 | | void |
157 | | EdgeRing::add(const PolygonizeDirectedEdge* de) |
158 | 0 | { |
159 | 0 | deList.push_back(de); |
160 | 0 | } |
161 | | |
162 | | /*public*/ |
163 | | void |
164 | | EdgeRing::computeHole() |
165 | 0 | { |
166 | 0 | getRingInternal(); |
167 | 0 | is_hole = Orientation::isCCW(ring->getCoordinatesRO()); |
168 | 0 | } |
169 | | |
170 | | /*public*/ |
171 | | void |
172 | | EdgeRing::addHole(LinearRing* hole) |
173 | 0 | { |
174 | 0 | if(holes == nullptr) { |
175 | 0 | holes.reset(new std::vector<std::unique_ptr<LinearRing>>()); |
176 | 0 | } |
177 | 0 | holes->emplace_back(hole); |
178 | 0 | } |
179 | | |
180 | | void |
181 | 0 | EdgeRing::addHole(EdgeRing* holeER) { |
182 | 0 | holeER->setShell(this); |
183 | 0 | auto hole = holeER->getRingOwnership(); // TODO is this right method? |
184 | 0 | addHole(hole.release()); |
185 | 0 | } |
186 | | |
187 | | /*public*/ |
188 | | std::unique_ptr<Polygon> |
189 | | EdgeRing::getPolygon() |
190 | 0 | { |
191 | 0 | if (holes) { |
192 | 0 | return factory->createPolygon(std::move(ring), std::move(*holes)); |
193 | 0 | } else { |
194 | 0 | return factory->createPolygon(std::move(ring)); |
195 | 0 | } |
196 | 0 | } |
197 | | |
198 | | /*public*/ |
199 | | bool |
200 | | EdgeRing::isValid() const |
201 | 0 | { |
202 | 0 | return is_valid; |
203 | 0 | } |
204 | | |
205 | | void |
206 | | EdgeRing::computeValid() |
207 | 0 | { |
208 | 0 | getCoordinates(); |
209 | 0 | if (ringPts->size() <= 3) { |
210 | 0 | is_valid = false; |
211 | 0 | return; |
212 | 0 | } |
213 | 0 | getRingInternal(); |
214 | 0 | is_valid = ring->isValid(); |
215 | 0 | } |
216 | | |
217 | | /*private*/ |
218 | | const CoordinateSequence* |
219 | | EdgeRing::getCoordinates() |
220 | 0 | { |
221 | 0 | if(ringPts == nullptr) { |
222 | 0 | ringPts = detail::make_unique<CoordinateSequence>(0u, 0u); |
223 | 0 | for(const auto& de : deList) { |
224 | 0 | auto edge = dynamic_cast<PolygonizeEdge*>(de->getEdge()); |
225 | 0 | addEdge(edge->getLine()->getCoordinatesRO(), |
226 | 0 | de->getEdgeDirection(), ringPts.get()); |
227 | 0 | } |
228 | 0 | } |
229 | 0 | return ringPts.get(); |
230 | 0 | } |
231 | | |
232 | | /*public*/ |
233 | | std::unique_ptr<LineString> |
234 | | EdgeRing::getLineString() |
235 | 0 | { |
236 | 0 | getCoordinates(); |
237 | 0 | return std::unique_ptr<LineString>(factory->createLineString(*ringPts)); |
238 | 0 | } |
239 | | |
240 | | /*public*/ |
241 | | LinearRing* |
242 | | EdgeRing::getRingInternal() |
243 | 0 | { |
244 | 0 | if(ring != nullptr) { |
245 | 0 | return ring.get(); |
246 | 0 | } |
247 | | |
248 | 0 | getCoordinates(); |
249 | 0 | try { |
250 | 0 | ring = factory->createLinearRing(*ringPts); |
251 | 0 | } |
252 | 0 | catch(const geos::util::IllegalArgumentException& e) { |
253 | | #if GEOS_DEBUG |
254 | | // FIXME: print also ringPts |
255 | | std::cerr << "EdgeRing::getRingInternal: " |
256 | | << e.what() |
257 | | << std::endl; |
258 | | #endif |
259 | 0 | ::geos::ignore_unused_variable_warning(e); |
260 | 0 | } |
261 | 0 | return ring.get(); |
262 | 0 | } |
263 | | |
264 | | /*public*/ |
265 | | std::unique_ptr<LinearRing> |
266 | | EdgeRing::getRingOwnership() |
267 | 0 | { |
268 | 0 | getRingInternal(); // active lazy generation |
269 | 0 | return std::move(ring); |
270 | 0 | } |
271 | | |
272 | | /*private*/ |
273 | | void |
274 | | EdgeRing::addEdge(const CoordinateSequence* coords, bool isForward, |
275 | | CoordinateSequence* coordList) |
276 | 0 | { |
277 | 0 | const std::size_t npts = coords->getSize(); |
278 | 0 | if(isForward) { |
279 | 0 | for(std::size_t i = 0; i < npts; ++i) { |
280 | 0 | coordList->add(coords->getAt(i), false); |
281 | 0 | } |
282 | 0 | } |
283 | 0 | else { |
284 | 0 | for(std::size_t i = npts; i > 0; --i) { |
285 | 0 | coordList->add(coords->getAt(i - 1), false); |
286 | 0 | } |
287 | 0 | } |
288 | 0 | } |
289 | | |
290 | | EdgeRing* |
291 | 0 | EdgeRing::getOuterHole() const { |
292 | | // Only shells can have outer holes |
293 | 0 | if (isHole()) { |
294 | 0 | return nullptr; |
295 | 0 | } |
296 | | |
297 | | // A shell is an outer shell if any edge is also in an outer hole. |
298 | | // A hole is an outer shell if it is not contained by a shell. |
299 | 0 | for (auto& de : deList) { |
300 | 0 | auto adjRing = (dynamic_cast<PolygonizeDirectedEdge*>(de->getSym()))->getRing(); |
301 | 0 | if (adjRing->isOuterHole()) { |
302 | 0 | return adjRing; |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | 0 | return nullptr; |
307 | 0 | } |
308 | | |
309 | | void |
310 | 0 | EdgeRing::updateIncludedRecursive() { |
311 | 0 | visitedByUpdateIncludedRecursive = true; |
312 | |
|
313 | 0 | if (isHole()) { |
314 | 0 | return; |
315 | 0 | } |
316 | | |
317 | 0 | for (const auto& de : deList) { |
318 | 0 | auto adjShell = (dynamic_cast<const PolygonizeDirectedEdge*>(de->getSym()))->getRing()->getShell(); |
319 | |
|
320 | 0 | if (adjShell != nullptr) { |
321 | 0 | if (!adjShell->isIncludedSet() && !adjShell->visitedByUpdateIncludedRecursive) { |
322 | 0 | adjShell->updateIncludedRecursive(); |
323 | 0 | } |
324 | 0 | } |
325 | 0 | } |
326 | |
|
327 | 0 | for (const auto& de : deList) { |
328 | 0 | auto adjShell = (dynamic_cast<const PolygonizeDirectedEdge*>(de->getSym()))->getRing()->getShell(); |
329 | |
|
330 | 0 | if (adjShell != nullptr) { |
331 | 0 | if (adjShell->isIncludedSet()) { |
332 | 0 | setIncluded(!adjShell->isIncluded()); |
333 | 0 | return; |
334 | 0 | } |
335 | 0 | } |
336 | 0 | } |
337 | |
|
338 | 0 | } |
339 | | |
340 | | } // namespace geos.operation.polygonize |
341 | | } // namespace geos.operation |
342 | | } // namespace geos |