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