1 /**********************************************************************
2 *
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
5 *
6 * Copyright (C) 2001-2002 Vivid Solutions Inc.
7 * Copyright (C) 2005 Refractions Research 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/overlay/PolygonBuilder.java rev. 1.20 (JTS-1.10)
17 *
18 **********************************************************************/
19
20 #include <geos/operation/overlay/PolygonBuilder.h>
21 #include <geos/operation/overlay/OverlayOp.h>
22 #include <geos/operation/overlay/MaximalEdgeRing.h>
23 #include <geos/operation/overlay/MinimalEdgeRing.h>
24 #include <geos/operation/polygonize/EdgeRing.h>
25 #include <geos/geomgraph/Node.h>
26 #include <geos/geomgraph/NodeMap.h>
27 #include <geos/geomgraph/DirectedEdgeStar.h>
28 #include <geos/geom/GeometryFactory.h>
29 #include <geos/geom/LinearRing.h>
30 #include <geos/geom/Polygon.h>
31 #include <geos/geom/CoordinateArraySequence.h>
32 #include <geos/algorithm/PointLocation.h>
33 #include <geos/util/TopologyException.h>
34 #include <geos/util/GEOSException.h>
35 #include <geos/util.h>
36
37
38 #include <vector>
39 #include <cassert>
40
41 #ifndef GEOS_DEBUG
42 #define GEOS_DEBUG 0
43 #endif
44
45 using namespace std;
46 using namespace geos::geomgraph;
47 using namespace geos::algorithm;
48 using namespace geos::geom;
49
50 namespace geos {
51 namespace operation { // geos.operation
52 namespace overlay { // geos.operation.overlay
53
PolygonBuilder(const GeometryFactory * newGeometryFactory)54 PolygonBuilder::PolygonBuilder(const GeometryFactory* newGeometryFactory)
55 :
56 geometryFactory(newGeometryFactory)
57 {
58 }
59
~PolygonBuilder()60 PolygonBuilder::~PolygonBuilder()
61 {
62 for(size_t i = 0, n = shellList.size(); i < n; ++i) {
63 delete shellList[i];
64 }
65 }
66
67 /*public*/
68 void
add(PlanarGraph * graph)69 PolygonBuilder::add(PlanarGraph* graph)
70 //throw(TopologyException *)
71 {
72 const vector<EdgeEnd*>* eeptr = graph->getEdgeEnds();
73 assert(eeptr);
74 const vector<EdgeEnd*>& ee = *eeptr;
75
76 size_t eeSize = ee.size();
77
78 #if GEOS_DEBUG
79 cerr << __FUNCTION__ << ": PlanarGraph has " << eeSize << " EdgeEnds" << endl;
80 #endif
81
82 vector<DirectedEdge*> dirEdges(eeSize);
83 for(size_t i = 0; i < eeSize; ++i) {
84 DirectedEdge* de = detail::down_cast<DirectedEdge*>(ee[i]);
85 dirEdges[i] = de;
86 }
87
88 NodeMap::container& nodeMap = graph->getNodeMap()->nodeMap;
89 vector<Node*> nodes;
90 nodes.reserve(nodeMap.size());
91 for(NodeMap::iterator it = nodeMap.begin(), itEnd = nodeMap.end();
92 it != itEnd; ++it) {
93 Node* node = it->second;
94 nodes.push_back(node);
95 }
96
97 add(&dirEdges, &nodes); // might throw a TopologyException *
98 }
99
100 /*public*/
101 void
add(const vector<DirectedEdge * > * dirEdges,const vector<Node * > * nodes)102 PolygonBuilder::add(const vector<DirectedEdge*>* dirEdges,
103 const vector<Node*>* nodes)
104 //throw(TopologyException *)
105 {
106 PlanarGraph::linkResultDirectedEdges(nodes->begin(), nodes->end());
107
108 vector<MaximalEdgeRing*> maxEdgeRings;
109 buildMaximalEdgeRings(dirEdges, maxEdgeRings);
110
111 vector<EdgeRing*> freeHoleList;
112 vector<MaximalEdgeRing*> edgeRings;
113 buildMinimalEdgeRings(maxEdgeRings, shellList, freeHoleList, edgeRings);
114
115 sortShellsAndHoles(edgeRings, shellList, freeHoleList);
116
117 vector<FastPIPRing> indexedshellist;
118 for(auto const& shell : shellList) {
119 FastPIPRing pipRing { shell, new geos::algorithm::locate::IndexedPointInAreaLocator(*shell->getLinearRing()) };
120 indexedshellist.push_back(pipRing);
121 }
122 placeFreeHoles(indexedshellist, freeHoleList);
123 //Assert: every hole on freeHoleList has a shell assigned to it
124
125 for(auto const& shell : indexedshellist) {
126 delete shell.pipLocator;
127 }
128 }
129
130 /*public*/
131 vector<Geometry*>*
getPolygons()132 PolygonBuilder::getPolygons()
133 {
134 vector<Geometry*>* resultPolyList = computePolygons(shellList);
135 return resultPolyList;
136 }
137
138
139 /*private*/
140 void
buildMaximalEdgeRings(const vector<DirectedEdge * > * dirEdges,vector<MaximalEdgeRing * > & maxEdgeRings)141 PolygonBuilder::buildMaximalEdgeRings(const vector<DirectedEdge*>* dirEdges,
142 vector<MaximalEdgeRing*>& maxEdgeRings)
143 // throw(const TopologyException &)
144 {
145 #if GEOS_DEBUG
146 cerr << "PolygonBuilder::buildMaximalEdgeRings got " << dirEdges->size() << " dirEdges" << endl;
147 #endif
148
149 vector<MaximalEdgeRing*>::size_type oldSize = maxEdgeRings.size();
150
151 for(size_t i = 0, n = dirEdges->size(); i < n; i++) {
152 DirectedEdge* de = (*dirEdges)[i];
153 #if GEOS_DEBUG
154 cerr << " dirEdge " << i << endl
155 << de->printEdge() << endl
156 << " inResult:" << de->isInResult() << endl
157 << " isArea:" << de->getLabel().isArea() << endl;
158 #endif
159 if(de->isInResult() && de->getLabel().isArea()) {
160 // if this edge has not yet been processed
161 if(de->getEdgeRing() == nullptr) {
162 MaximalEdgeRing* er;
163 try {
164 // MaximalEdgeRing constructor may throw
165 er = new MaximalEdgeRing(de, geometryFactory);
166 }
167 catch(util::GEOSException&) {
168 // cleanup if that happens (see stmlf-cases-20061020.xml)
169 for(size_t p_i = oldSize, p_n = maxEdgeRings.size(); p_i < p_n; p_i++) {
170 delete maxEdgeRings[p_i];
171 }
172 //cerr << "Exception! " << e.what() << endl;
173 throw;
174 }
175 maxEdgeRings.push_back(er);
176 er->setInResult();
177 //System.out.println("max node degree=" + er.getMaxDegree());
178 }
179 }
180 }
181 #if GEOS_DEBUG
182 cerr << " pushed " << maxEdgeRings.size() - oldSize << " maxEdgeRings" << endl;
183 #endif
184 }
185
186 /*private*/
187 void
buildMinimalEdgeRings(vector<MaximalEdgeRing * > & maxEdgeRings,vector<EdgeRing * > & newShellList,vector<EdgeRing * > & freeHoleList,vector<MaximalEdgeRing * > & edgeRings)188 PolygonBuilder::buildMinimalEdgeRings(
189 vector<MaximalEdgeRing*>& maxEdgeRings,
190 vector<EdgeRing*>& newShellList, vector<EdgeRing*>& freeHoleList,
191 vector<MaximalEdgeRing*>& edgeRings)
192 {
193 for(size_t i = 0, n = maxEdgeRings.size(); i < n; ++i) {
194 MaximalEdgeRing* er = maxEdgeRings[i];
195 #if GEOS_DEBUG
196 cerr << "buildMinimalEdgeRings: maxEdgeRing " << i << " has " << er->getMaxNodeDegree() << " maxNodeDegree" << endl;
197 #endif
198 if(er->getMaxNodeDegree() > 2) {
199 er->linkDirectedEdgesForMinimalEdgeRings();
200 vector<MinimalEdgeRing*> minEdgeRings;
201 er->buildMinimalRings(minEdgeRings);
202 // at this point we can go ahead and attempt to place
203 // holes, if this EdgeRing is a polygon
204 EdgeRing* shell = findShell(&minEdgeRings);
205 if(shell != nullptr) {
206 placePolygonHoles(shell, &minEdgeRings);
207 newShellList.push_back(shell);
208 }
209 else {
210 freeHoleList.insert(freeHoleList.end(),
211 minEdgeRings.begin(),
212 minEdgeRings.end());
213 }
214 delete er;
215 }
216 else {
217 edgeRings.push_back(er);
218 }
219 }
220 }
221
222 /*private*/
223 EdgeRing*
findShell(vector<MinimalEdgeRing * > * minEdgeRings)224 PolygonBuilder::findShell(vector<MinimalEdgeRing*>* minEdgeRings)
225 {
226 int shellCount = 0;
227 EdgeRing* shell = nullptr;
228
229 #if GEOS_DEBUG
230 cerr << "PolygonBuilder::findShell got " << minEdgeRings->size() << " minEdgeRings" << endl;
231 #endif
232
233 for(size_t i = 0, n = minEdgeRings->size(); i < n; ++i) {
234 EdgeRing* er = (*minEdgeRings)[i];
235 if(! er->isHole()) {
236 shell = er;
237 ++shellCount;
238 }
239 }
240
241 if(shellCount > 1) {
242 throw util::TopologyException("found two shells in MinimalEdgeRing list");
243 }
244
245 return shell;
246 }
247
248 /*private*/
249 void
placePolygonHoles(EdgeRing * shell,vector<MinimalEdgeRing * > * minEdgeRings)250 PolygonBuilder::placePolygonHoles(EdgeRing* shell,
251 vector<MinimalEdgeRing*>* minEdgeRings)
252 {
253 for(size_t i = 0, n = minEdgeRings->size(); i < n; ++i) {
254 MinimalEdgeRing* er = (*minEdgeRings)[i];
255 if(er->isHole()) {
256 er->setShell(shell);
257 }
258 }
259 }
260
261 /*private*/
262 void
sortShellsAndHoles(vector<MaximalEdgeRing * > & edgeRings,vector<EdgeRing * > & newShellList,vector<EdgeRing * > & freeHoleList)263 PolygonBuilder::sortShellsAndHoles(vector<MaximalEdgeRing*>& edgeRings,
264 vector<EdgeRing*>& newShellList, vector<EdgeRing*>& freeHoleList)
265 {
266 for(size_t i = 0, n = edgeRings.size(); i < n; i++) {
267 EdgeRing* er = edgeRings[i];
268 //er->setInResult();
269 if(er->isHole()) {
270 freeHoleList.push_back(er);
271 }
272 else {
273 newShellList.push_back(er);
274 }
275 }
276 }
277
278 /*private*/
279 void
placeFreeHoles(vector<FastPIPRing> & newShellList,std::vector<EdgeRing * > & freeHoleList)280 PolygonBuilder::placeFreeHoles(vector<FastPIPRing>& newShellList,
281 std::vector<EdgeRing*>& freeHoleList)
282 {
283 for(std::vector<EdgeRing*>::iterator
284 it = freeHoleList.begin(), itEnd = freeHoleList.end();
285 it != itEnd;
286 ++it) {
287 EdgeRing* hole = *it;
288 // only place this hole if it doesn't yet have a shell
289 if(hole->getShell() == nullptr) {
290 EdgeRing* shell = findEdgeRingContaining(hole, newShellList);
291 if(shell == nullptr) {
292 #if GEOS_DEBUG
293 Geometry* geom;
294 std::cerr << "CREATE TABLE shells (g geometry);" << std::endl;
295 std::cerr << "CREATE TABLE hole (g geometry);" << std::endl;
296 for(std::vector<EdgeRing*>::iterator rIt = newShellList.begin(),
297 rEnd = newShellList.end(); rIt != rEnd; rIt++) {
298 geom = (*rIt)->toPolygon(geometryFactory);
299 std::cerr << "INSERT INTO shells VALUES ('"
300 << *geom
301 << "');" << std::endl;
302 delete geom;
303 }
304 geom = hole->toPolygon(geometryFactory);
305 std::cerr << "INSERT INTO hole VALUES ('"
306 << *geom
307 << "');" << std::endl;
308 delete geom;
309 #endif
310 //assert(shell!=NULL); // unable to assign hole to a shell
311 throw util::TopologyException("unable to assign hole to a shell");
312 }
313
314 hole->setShell(shell);
315 }
316 }
317 }
318
319 /*private*/
320 EdgeRing*
findEdgeRingContaining(EdgeRing * testEr,vector<FastPIPRing> & newShellList)321 PolygonBuilder::findEdgeRingContaining(EdgeRing* testEr,
322 vector<FastPIPRing>& newShellList)
323 {
324 LinearRing* testRing = testEr->getLinearRing();
325 const Envelope* testEnv = testRing->getEnvelopeInternal();
326 EdgeRing* minShell = nullptr;
327 const Envelope* minShellEnv = nullptr;
328
329 for(auto const& tryShell : newShellList) {
330 LinearRing* tryShellRing = tryShell.edgeRing->getLinearRing();
331 const Envelope* tryShellEnv = tryShellRing->getEnvelopeInternal();
332 // the hole envelope cannot equal the shell envelope
333 // (also guards against testing rings against themselves)
334 if(tryShellEnv->equals(testEnv)) {
335 continue;
336 }
337 // hole must be contained in shell
338 if(!tryShellEnv->contains(testEnv)) {
339 continue;
340 }
341
342 const CoordinateSequence* tsrcs = tryShellRing->getCoordinatesRO();
343 const Coordinate& testPt = operation::polygonize::EdgeRing::ptNotInList(testRing->getCoordinatesRO(), tsrcs);
344
345 bool isContained = false;
346 if(tryShell.pipLocator->locate(&testPt) != Location::EXTERIOR) {
347 isContained = true;
348 }
349
350 // check if this new containing ring is smaller than
351 // the current minimum ring
352 if(isContained) {
353 if(minShell == nullptr
354 || minShellEnv->contains(tryShellEnv)) {
355 minShell = tryShell.edgeRing;
356 minShellEnv = minShell->getLinearRing()->getEnvelopeInternal();
357 }
358 }
359 }
360 return minShell;
361 }
362
363 /*private*/
364 vector<Geometry*>*
computePolygons(vector<EdgeRing * > & newShellList)365 PolygonBuilder::computePolygons(vector<EdgeRing*>& newShellList)
366 {
367 #if GEOS_DEBUG
368 cerr << "PolygonBuilder::computePolygons: got " << newShellList.size() << " shells" << endl;
369 #endif
370 vector<Geometry*>* resultPolyList = new vector<Geometry*>();
371
372 // add Polygons for all shells
373 for(size_t i = 0, n = newShellList.size(); i < n; i++) {
374 EdgeRing* er = newShellList[i];
375 Polygon* poly = er->toPolygon(geometryFactory).release();
376 resultPolyList->push_back(poly);
377 }
378 return resultPolyList;
379 }
380
381
382 } // namespace geos.operation.overlay
383 } // namespace geos.operation
384 } // namespace geos
385
386