/src/geos/src/geomgraph/EdgeRing.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (C) 2011 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: geomgraph/EdgeRing.java r428 (JTS-1.12+) |
18 | | * |
19 | | **********************************************************************/ |
20 | | |
21 | | #include <geos/util/Assert.h> |
22 | | #include <geos/util/TopologyException.h> |
23 | | #include <geos/algorithm/PointLocation.h> |
24 | | #include <geos/algorithm/Orientation.h> |
25 | | #include <geos/geomgraph/EdgeRing.h> |
26 | | #include <geos/geomgraph/DirectedEdge.h> |
27 | | #include <geos/geomgraph/DirectedEdgeStar.h> |
28 | | #include <geos/geomgraph/Edge.h> |
29 | | #include <geos/geomgraph/Node.h> |
30 | | #include <geos/geomgraph/Label.h> |
31 | | #include <geos/geom/Position.h> |
32 | | #include <geos/geom/CoordinateSequence.h> |
33 | | #include <geos/geom/GeometryFactory.h> |
34 | | #include <geos/geom/LinearRing.h> |
35 | | #include <geos/geom/Location.h> |
36 | | #include <geos/geom/Envelope.h> |
37 | | #include <geos/util.h> |
38 | | |
39 | | #include <vector> |
40 | | #include <cassert> |
41 | | #include <iostream> // for operator<< |
42 | | |
43 | | #ifndef GEOS_DEBUG |
44 | | #define GEOS_DEBUG 0 |
45 | | #endif |
46 | | |
47 | | using namespace geos::algorithm; |
48 | | using namespace geos::geom; |
49 | | |
50 | | namespace geos { |
51 | | namespace geomgraph { // geos.geomgraph |
52 | | |
53 | | EdgeRing::EdgeRing(DirectedEdge* newStart, |
54 | | const GeometryFactory* newGeometryFactory) |
55 | | : |
56 | 0 | startDe(newStart), |
57 | 0 | geometryFactory(newGeometryFactory), |
58 | 0 | holes(), |
59 | 0 | maxNodeDegree(-1), |
60 | 0 | edges(), |
61 | 0 | label(Location::NONE), // new Label(Location::NONE)), |
62 | 0 | ring(nullptr), |
63 | 0 | isHoleVar(false), |
64 | 0 | shell(nullptr) |
65 | 0 | { |
66 | | /* |
67 | | * Commented out to fix different polymorphism in C++ (from Java) |
68 | | * Make sure these calls are made by derived classes ! |
69 | | */ |
70 | | //computePoints(start); |
71 | | //computeRing(); |
72 | | #if GEOS_DEBUG |
73 | | std::cerr << "EdgeRing[" << this << "] ctor" << std::endl; |
74 | | #endif |
75 | 0 | testInvariant(); |
76 | 0 | } |
77 | | |
78 | | bool |
79 | | EdgeRing::isIsolated() |
80 | 0 | { |
81 | 0 | testInvariant(); |
82 | 0 | return (label.getGeometryCount() == 1); |
83 | 0 | } |
84 | | |
85 | | bool |
86 | | EdgeRing::isHole() |
87 | 0 | { |
88 | 0 | testInvariant(); |
89 | | |
90 | | // We can't tell if this is an hole |
91 | | // unless we computed the ring |
92 | | // see computeRing() |
93 | 0 | assert(ring); |
94 | |
|
95 | 0 | return isHoleVar; |
96 | 0 | } |
97 | | |
98 | | |
99 | | LinearRing* |
100 | | EdgeRing::getLinearRing() |
101 | 0 | { |
102 | 0 | testInvariant(); |
103 | 0 | return ring.get(); |
104 | 0 | } |
105 | | |
106 | | Label& |
107 | | EdgeRing::getLabel() |
108 | 0 | { |
109 | 0 | testInvariant(); |
110 | 0 | return label; |
111 | 0 | } |
112 | | |
113 | | bool |
114 | | EdgeRing::isShell() |
115 | 0 | { |
116 | 0 | testInvariant(); |
117 | 0 | return (shell == nullptr); |
118 | 0 | } |
119 | | |
120 | | EdgeRing* |
121 | | EdgeRing::getShell() |
122 | 0 | { |
123 | 0 | testInvariant(); |
124 | 0 | return shell; |
125 | 0 | } |
126 | | |
127 | | void |
128 | | EdgeRing::setShell(EdgeRing* newShell) |
129 | 0 | { |
130 | 0 | shell = newShell; |
131 | 0 | if(shell != nullptr) { |
132 | 0 | shell->addHole(this); |
133 | 0 | } |
134 | 0 | testInvariant(); |
135 | 0 | } |
136 | | |
137 | | void |
138 | | EdgeRing::addHole(EdgeRing* edgeRing) |
139 | 0 | { |
140 | 0 | holes.emplace_back(edgeRing); |
141 | 0 | testInvariant(); |
142 | 0 | } |
143 | | |
144 | | /*public*/ |
145 | | std::unique_ptr<Polygon> |
146 | | EdgeRing::toPolygon(const GeometryFactory* p_geometryFactory) |
147 | 0 | { |
148 | 0 | testInvariant(); |
149 | | |
150 | | // We don't use "clone" here because |
151 | | // GeometryFactory::createPolygon really |
152 | | // wants a LinearRing |
153 | 0 | auto shellLR = detail::make_unique<LinearRing>(*(getLinearRing())); |
154 | 0 | if (holes.empty()) { |
155 | 0 | return p_geometryFactory->createPolygon(std::move(shellLR)); |
156 | 0 | } else { |
157 | 0 | std::size_t nholes = holes.size(); |
158 | 0 | std::vector<std::unique_ptr<LinearRing>> holeLR(nholes); |
159 | 0 | for(std::size_t i = 0; i < nholes; ++i) { |
160 | 0 | holeLR[i] = detail::make_unique<LinearRing>(*(holes[i]->getLinearRing())); |
161 | 0 | } |
162 | |
|
163 | 0 | return p_geometryFactory->createPolygon(std::move(shellLR), std::move(holeLR)); |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | | /*public*/ |
168 | | void |
169 | | EdgeRing::computeRing() |
170 | 0 | { |
171 | 0 | testInvariant(); |
172 | |
|
173 | 0 | if(ring != nullptr) { |
174 | 0 | return; // don't compute more than once |
175 | 0 | } |
176 | 0 | auto coordSeq = detail::make_unique<CoordinateSequence>(std::move(pts)); |
177 | 0 | ring = geometryFactory->createLinearRing(std::move(coordSeq)); |
178 | 0 | isHoleVar = Orientation::isCCW(ring->getCoordinatesRO()); |
179 | |
|
180 | 0 | testInvariant(); |
181 | 0 | } |
182 | | |
183 | | /*public*/ |
184 | | std::vector<DirectedEdge*>& |
185 | | EdgeRing::getEdges() |
186 | 0 | { |
187 | 0 | testInvariant(); |
188 | |
|
189 | 0 | return edges; |
190 | 0 | } |
191 | | |
192 | | /*protected*/ |
193 | | void |
194 | | EdgeRing::computePoints(DirectedEdge* newStart) |
195 | | // throw(const TopologyException &) |
196 | 0 | { |
197 | 0 | startDe = newStart; |
198 | 0 | DirectedEdge* de = newStart; |
199 | 0 | bool isFirstEdge = true; |
200 | 0 | do { |
201 | | //util::Assert::isTrue(de!=NULL,"EdgeRing::computePoints: found null Directed Edge"); |
202 | | //assert(de!=NULL); // EdgeRing::computePoints: found null Directed Edge |
203 | 0 | if(de == nullptr) |
204 | 0 | throw util::TopologyException( |
205 | 0 | "EdgeRing::computePoints: found null Directed Edge"); |
206 | | |
207 | 0 | if(de->getEdgeRing() == this) |
208 | 0 | throw util::TopologyException( |
209 | 0 | "Directed Edge visited twice during ring-building", |
210 | 0 | de->getCoordinate()); |
211 | | |
212 | 0 | edges.push_back(de); |
213 | 0 | const Label& deLabel = de->getLabel(); |
214 | 0 | assert(deLabel.isArea()); |
215 | 0 | mergeLabel(deLabel); |
216 | 0 | addPoints(de->getEdge(), de->isForward(), isFirstEdge); |
217 | 0 | isFirstEdge = false; |
218 | 0 | setEdgeRing(de, this); |
219 | 0 | de = getNext(de); |
220 | 0 | } |
221 | 0 | while(de != startDe); |
222 | | |
223 | 0 | testInvariant(); |
224 | |
|
225 | 0 | } |
226 | | |
227 | | /*public*/ |
228 | | int |
229 | | EdgeRing::getMaxNodeDegree() |
230 | 0 | { |
231 | |
|
232 | 0 | testInvariant(); |
233 | |
|
234 | 0 | if(maxNodeDegree < 0) { |
235 | 0 | computeMaxNodeDegree(); |
236 | 0 | } |
237 | 0 | return maxNodeDegree; |
238 | 0 | } |
239 | | |
240 | | /*private*/ |
241 | | void |
242 | | EdgeRing::computeMaxNodeDegree() |
243 | 0 | { |
244 | 0 | maxNodeDegree = 0; |
245 | 0 | DirectedEdge* de = startDe; |
246 | 0 | do { |
247 | 0 | Node* node = de->getNode(); |
248 | 0 | EdgeEndStar* ees = node->getEdges(); |
249 | 0 | DirectedEdgeStar* des = detail::down_cast<DirectedEdgeStar*>(ees); |
250 | 0 | int degree = des->getOutgoingDegree(this); |
251 | 0 | if(degree > maxNodeDegree) { |
252 | 0 | maxNodeDegree = degree; |
253 | 0 | } |
254 | 0 | de = getNext(de); |
255 | 0 | } |
256 | 0 | while(de != startDe); |
257 | 0 | maxNodeDegree *= 2; |
258 | |
|
259 | 0 | testInvariant(); |
260 | |
|
261 | 0 | } |
262 | | |
263 | | /*public*/ |
264 | | void |
265 | | EdgeRing::setInResult() |
266 | 0 | { |
267 | 0 | DirectedEdge* de = startDe; |
268 | 0 | do { |
269 | 0 | de->getEdge()->setInResult(true); |
270 | 0 | de = de->getNext(); |
271 | 0 | } |
272 | 0 | while(de != startDe); |
273 | |
|
274 | 0 | testInvariant(); |
275 | |
|
276 | 0 | } |
277 | | |
278 | | /*protected*/ |
279 | | void |
280 | | EdgeRing::mergeLabel(const Label& deLabel) |
281 | 0 | { |
282 | 0 | mergeLabel(deLabel, 0); |
283 | 0 | mergeLabel(deLabel, 1); |
284 | |
|
285 | 0 | testInvariant(); |
286 | |
|
287 | 0 | } |
288 | | |
289 | | /*protected*/ |
290 | | void |
291 | | EdgeRing::mergeLabel(const Label& deLabel, uint8_t geomIndex) |
292 | 0 | { |
293 | |
|
294 | 0 | testInvariant(); |
295 | |
|
296 | 0 | Location loc = deLabel.getLocation(geomIndex, Position::RIGHT); |
297 | | // no information to be had from this label |
298 | 0 | if(loc == Location::NONE) { |
299 | 0 | return; |
300 | 0 | } |
301 | | |
302 | | // if there is no current RHS value, set it |
303 | 0 | if(label.getLocation(geomIndex) == Location::NONE) { |
304 | 0 | label.setLocation(geomIndex, loc); |
305 | 0 | return; |
306 | 0 | } |
307 | 0 | } |
308 | | |
309 | | /*protected*/ |
310 | | void |
311 | | EdgeRing::addPoints(Edge* edge, bool isForward, bool isFirstEdge) |
312 | 0 | { |
313 | | // EdgeRing::addPoints: can't add points after LinearRing construction |
314 | 0 | assert(ring == nullptr); |
315 | |
|
316 | 0 | assert(edge); |
317 | 0 | const CoordinateSequence* edgePts = edge->getCoordinates(); |
318 | |
|
319 | 0 | assert(edgePts); |
320 | 0 | std::size_t numEdgePts = edgePts->getSize(); |
321 | |
|
322 | 0 | if(isForward) { |
323 | 0 | if(isFirstEdge) { |
324 | 0 | pts = *edgePts; |
325 | 0 | return; |
326 | 0 | } else { |
327 | 0 | for(std::size_t i = 1; i < numEdgePts; ++i) { |
328 | 0 | pts.add(edgePts->getAt(i)); |
329 | 0 | } |
330 | 0 | } |
331 | 0 | } |
332 | | |
333 | 0 | else { // is backward |
334 | 0 | std::size_t startIndex = numEdgePts - 1; |
335 | 0 | if(isFirstEdge) { |
336 | 0 | startIndex = numEdgePts; |
337 | 0 | } |
338 | 0 | for(std::size_t i = startIndex; i > 0; --i) { |
339 | 0 | pts.add(edgePts->getAt(i - 1)); |
340 | 0 | } |
341 | 0 | } |
342 | | |
343 | 0 | testInvariant(); |
344 | 0 | } |
345 | | |
346 | | /*public*/ |
347 | | bool |
348 | | EdgeRing::containsPoint(const Coordinate& p) |
349 | 0 | { |
350 | 0 | testInvariant(); |
351 | |
|
352 | 0 | assert(ring); |
353 | |
|
354 | 0 | const Envelope* env = ring->getEnvelopeInternal(); |
355 | 0 | assert(env); |
356 | 0 | if(! env->contains(p)) { |
357 | 0 | return false; |
358 | 0 | } |
359 | | |
360 | 0 | if(! PointLocation::isInRing(p, ring->getCoordinatesRO())) { |
361 | 0 | return false; |
362 | 0 | } |
363 | | |
364 | 0 | for(const auto& hole : holes) { |
365 | 0 | assert(hole); |
366 | 0 | if(hole->containsPoint(p)) { |
367 | 0 | return false; |
368 | 0 | } |
369 | 0 | } |
370 | 0 | return true; |
371 | 0 | } |
372 | | |
373 | | std::ostream& |
374 | | operator<< (std::ostream& os, const EdgeRing& er) |
375 | 0 | { |
376 | 0 | os << "EdgeRing[" << &er << "]: " |
377 | 0 | << std::endl; |
378 | 0 | return os; |
379 | 0 | } |
380 | | |
381 | | } // namespace geos.geomgraph |
382 | | } // namespace geos |
383 | | |