1 /**********************************************************************
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
6 * Copyright (C) 2007-2010 Safe Software Inc.
7 * Copyright (C) 2005-2006 Refractions Research Inc.
8 * Copyright (C) 2001-2002 Vivid Solutions Inc.
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.
15 **********************************************************************
17 * Last port: operation/valid/ConnectedInteriorTester.java rev. 1.15 (JTS-1.10)
19 **********************************************************************
23 * - Remove heap allocation of GeometryFactory (might use a singleton)
24 * - Track MaximalEdgeRing references: we might be deleting them
25 * leaving dangling refs around.
27 **********************************************************************/
29 #include <geos/operation/valid/ConnectedInteriorTester.h>
30 #include <geos/operation/overlay/MaximalEdgeRing.h>
31 #include <geos/operation/overlay/MinimalEdgeRing.h>
32 #include <geos/operation/overlay/OverlayNodeFactory.h>
33 #include <geos/geom/GeometryFactory.h>
34 #include <geos/geom/CoordinateSequence.h>
35 #include <geos/geom/Coordinate.h>
36 #include <geos/geom/Location.h>
37 #include <geos/geom/Polygon.h>
38 #include <geos/geom/MultiPolygon.h>
39 #include <geos/geom/MultiPolygon.h>
40 #include <geos/geom/LineString.h>
41 #include <geos/geomgraph/GeometryGraph.h>
42 #include <geos/geomgraph/PlanarGraph.h>
43 #include <geos/geomgraph/EdgeRing.h>
44 #include <geos/geomgraph/DirectedEdge.h>
45 #include <geos/geomgraph/Position.h>
46 #include <geos/geomgraph/Label.h>
56 //#define GEOS_CAST_PARANOIA 1
63 using namespace geos::geom
;
64 using namespace geos::geomgraph
;
65 using namespace geos::operation::overlay
;
68 namespace operation
{ // geos.operation
69 namespace valid
{ // geos.operation.valid
71 ConnectedInteriorTester::ConnectedInteriorTester(GeometryGraph
&newGeomGraph
):
72 geometryFactory(GeometryFactory::create()),
73 geomGraph(newGeomGraph
),
74 disconnectedRingcoord()
78 ConnectedInteriorTester::~ConnectedInteriorTester()
83 ConnectedInteriorTester::getCoordinate()
85 return disconnectedRingcoord
;
89 ConnectedInteriorTester::findDifferentPoint(const CoordinateSequence
*coord
,
93 size_t npts
=coord
->getSize();
94 for(size_t i
=0; i
<npts
; ++i
)
96 if(!(coord
->getAt(i
)==pt
))
97 return coord
->getAt(i
);
99 return Coordinate::getNull();
104 ConnectedInteriorTester::isInteriorsConnected()
107 // node the edges, in case holes touch the shell
108 std::vector
<Edge
*> splitEdges
;
109 geomGraph
.computeSplitEdges(&splitEdges
);
111 // form the edges into rings
112 PlanarGraph
graph(operation::overlay::OverlayNodeFactory::instance());
114 graph
.addEdges(splitEdges
);
115 setInteriorEdgesInResult(graph
);
116 graph
.linkResultDirectedEdges();
118 std::vector
<EdgeRing
*> edgeRings
;
119 buildEdgeRings(graph
.getEdgeEnds(), edgeRings
);
122 cerr
<< "buildEdgeRings constructed " << edgeRings
.size() << " edgeRings." << endl
;
126 * Mark all the edges for the edgeRings corresponding to the shells
127 * of the input polygons.
129 * Only ONE ring gets marked for each shell - if there are others
130 * which remain unmarked this indicates a disconnected interior.
132 visitShellInteriors(geomGraph
.getGeometry(), graph
);
135 cerr
<< "after visitShellInteriors edgeRings are " << edgeRings
.size() << " edgeRings." << endl
;
139 * If there are any unvisited shell edges
140 * (i.e. a ring which is not a hole and which has the interior
141 * of the parent area on the RHS)
142 * this means that one or more holes must have split the interior of the
143 * polygon into at least two pieces. The polygon is thus invalid.
145 bool res
=!hasUnvisitedShellEdge(&edgeRings
);
148 cerr
<< "releasing " << edgeRings
.size() << " edgeRings." << endl
;
150 // Release memory allocated by buildEdgeRings
151 for(size_t i
=0, n
=edgeRings
.size(); i
<n
; ++i
)
153 EdgeRing
* er
= edgeRings
[i
];
160 cerr
<< "releasing edgeRing at " << er
<< endl
;
165 // Release memory allocated by MaximalEdgeRings
166 // There should be no more references to this object
167 // how to check this ? boost::shared_ptr<> comes to mind.
169 for (size_t i
=0, n
=maximalEdgeRings
.size(); i
<n
; i
++)
171 delete maximalEdgeRings
[i
];
173 maximalEdgeRings
.clear();
179 ConnectedInteriorTester::setInteriorEdgesInResult(PlanarGraph
&graph
)
181 std::vector
<EdgeEnd
*> *ee
=graph
.getEdgeEnds();
182 for(size_t i
=0, n
=ee
->size(); i
<n
; ++i
)
184 // Unexpected non DirectedEdge in graphEdgeEnds
185 assert(dynamic_cast<DirectedEdge
*>((*ee
)[i
]));
186 DirectedEdge
*de
=static_cast<DirectedEdge
*>((*ee
)[i
]);
187 if ( de
->getLabel().getLocation(0, Position::RIGHT
) == Location::INTERIOR
)
189 de
->setInResult(true);
196 ConnectedInteriorTester::buildEdgeRings(std::vector
<EdgeEnd
*> *dirEdges
,
197 std::vector
<EdgeRing
*>& minEdgeRings
)
200 cerr
<< __FUNCTION__
<< " got " << dirEdges
->size() << " EdgeEnd vector" << endl
;
203 typedef std::vector
<EdgeEnd
*> EdgeEnds
;
205 //std::vector<MinimalEdgeRing*> minEdgeRings;
206 for(EdgeEnds::size_type i
=0, n
=dirEdges
->size(); i
<n
; ++i
)
208 #ifdef GEOS_CAST_PARANOIA
209 assert(dynamic_cast<DirectedEdge
*>((*dirEdges
)[i
]));
211 DirectedEdge
*de
=static_cast<DirectedEdge
*>((*dirEdges
)[i
]);
214 cerr
<< "DirectedEdge " << i
<< ": " << de
->print() << endl
;
217 // if this edge has not yet been processed
218 if(de
->isInResult() && de
->getEdgeRing()==NULL
)
220 MaximalEdgeRing
* er
= new MaximalEdgeRing(de
,
221 geometryFactory
.get());
222 // We track MaximalEdgeRings allocations
223 // using the private maximalEdgeRings vector
224 maximalEdgeRings
.push_back(er
);
226 er
->linkDirectedEdgesForMinimalEdgeRings();
227 er
->buildMinimalRings(minEdgeRings
);
231 std::vector<EdgeRing*> *edgeRings=new std::vector<EdgeRing*>();
232 edgeRings->assign(minEdgeRings.begin(), minEdgeRings.end());
238 * Mark all the edges for the edgeRings corresponding to the shells
239 * of the input polygons. Note only ONE ring gets marked for each shell.
242 ConnectedInteriorTester::visitShellInteriors(const Geometry
*g
, PlanarGraph
&graph
)
244 if (const Polygon
* p
=dynamic_cast<const Polygon
*>(g
))
246 visitInteriorRing(p
->getExteriorRing(), graph
);
249 if (const MultiPolygon
* mp
=dynamic_cast<const MultiPolygon
*>(g
))
251 for (size_t i
=0, n
=mp
->getNumGeometries(); i
<n
; i
++) {
252 const Polygon
*p
=dynamic_cast<const Polygon
*>(mp
->getGeometryN(i
));
253 visitInteriorRing(p
->getExteriorRing(), graph
);
259 ConnectedInteriorTester::visitInteriorRing(const LineString
*ring
, PlanarGraph
&graph
)
261 // can't visit an empty ring
262 if(ring
->isEmpty()) return;
264 const CoordinateSequence
*pts
=ring
->getCoordinatesRO();
265 const Coordinate
& pt0
=pts
->getAt(0);
268 * Find first point in coord list different to initial point.
269 * Need special check since the first point may be repeated.
271 const Coordinate
& pt1
=findDifferentPoint(pts
, pt0
);
272 Edge
*e
=graph
.findEdgeInSameDirection(pt0
, pt1
);
273 DirectedEdge
*de
=static_cast<DirectedEdge
*>(graph
.findEdgeEnd(e
));
274 DirectedEdge
*intDe
=NULL
;
275 if (de
->getLabel().getLocation(0,Position::RIGHT
)==Location::INTERIOR
) {
277 } else if (de
->getSym()->getLabel().getLocation(0,Position::RIGHT
)==Location::INTERIOR
) {
280 assert(intDe
!=NULL
); // unable to find dirEdge with Interior on RHS
281 visitLinkedDirectedEdges(intDe
);
286 ConnectedInteriorTester::visitLinkedDirectedEdges(DirectedEdge
*start
)
288 DirectedEdge
*startDe
=start
;
289 DirectedEdge
*de
=start
;
292 // found null Directed Edge
295 de
->setVisited(true);
298 } while (de
!=startDe
);
303 ConnectedInteriorTester::hasUnvisitedShellEdge(std::vector
<EdgeRing
*> *edgeRings
)
307 cerr
<< "hasUnvisitedShellEdge called with " << edgeRings
->size() << " edgeRings." << endl
;
310 for(std::vector
<EdgeRing
*>::iterator
311 it
=edgeRings
->begin(), itEnd
=edgeRings
->end();
318 // don't check hole rings
319 if (er
->isHole()) continue;
321 std::vector
<DirectedEdge
*>& edges
=er
->getEdges();
322 DirectedEdge
*de
=edges
[0];
325 // don't check CW rings which are holes
326 // (MD - this check may now be irrelevant - 2006-03-09)
327 if (de
->getLabel().getLocation(0, Position::RIGHT
) != Location::INTERIOR
) continue;
330 * the edgeRing is CW ring which surrounds the INT
331 * of the area, so check all edges have been visited.
332 * If any are unvisited, this is a disconnected part
335 for(std::vector
<DirectedEdge
*>::iterator
336 jt
=edges
.begin(), jtEnd
=edges
.end();
342 //Debug.print("visted? "); Debug.println(de);
343 if (!de
->isVisited()) {
344 //Debug.print("not visited "); Debug.println(de);
345 disconnectedRingcoord
=de
->getCoordinate();
353 } // namespace geos.operation.valid
354 } // namespace geos.operation