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