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(new GeometryFactory()),
73 geomGraph(newGeomGraph
),
74 disconnectedRingcoord()
78 ConnectedInteriorTester::~ConnectedInteriorTester()
80 delete geometryFactory
;
84 ConnectedInteriorTester::getCoordinate()
86 return disconnectedRingcoord
;
90 ConnectedInteriorTester::findDifferentPoint(const CoordinateSequence
*coord
,
94 size_t npts
=coord
->getSize();
95 for(size_t i
=0; i
<npts
; ++i
)
97 if(!(coord
->getAt(i
)==pt
))
98 return coord
->getAt(i
);
100 return Coordinate::getNull();
105 ConnectedInteriorTester::isInteriorsConnected()
108 // node the edges, in case holes touch the shell
109 std::vector
<Edge
*> splitEdges
;
110 geomGraph
.computeSplitEdges(&splitEdges
);
112 // form the edges into rings
113 PlanarGraph
graph(operation::overlay::OverlayNodeFactory::instance());
115 graph
.addEdges(splitEdges
);
116 setInteriorEdgesInResult(graph
);
117 graph
.linkResultDirectedEdges();
119 std::vector
<EdgeRing
*> edgeRings
;
120 buildEdgeRings(graph
.getEdgeEnds(), edgeRings
);
123 cerr
<< "buildEdgeRings constructed " << edgeRings
.size() << " edgeRings." << endl
;
127 * Mark all the edges for the edgeRings corresponding to the shells
128 * of the input polygons.
130 * Only ONE ring gets marked for each shell - if there are others
131 * which remain unmarked this indicates a disconnected interior.
133 visitShellInteriors(geomGraph
.getGeometry(), graph
);
136 cerr
<< "after visitShellInteriors edgeRings are " << edgeRings
.size() << " edgeRings." << endl
;
140 * If there are any unvisited shell edges
141 * (i.e. a ring which is not a hole and which has the interior
142 * of the parent area on the RHS)
143 * this means that one or more holes must have split the interior of the
144 * polygon into at least two pieces. The polygon is thus invalid.
146 bool res
=!hasUnvisitedShellEdge(&edgeRings
);
149 cerr
<< "releasing " << edgeRings
.size() << " edgeRings." << endl
;
151 // Release memory allocated by buildEdgeRings
152 for(size_t i
=0, n
=edgeRings
.size(); i
<n
; ++i
)
154 EdgeRing
* er
= edgeRings
[i
];
161 cerr
<< "releasing edgeRing at " << er
<< endl
;
166 // Release memory allocated by MaximalEdgeRings
167 // There should be no more references to this object
168 // how to check this ? boost::shared_ptr<> comes to mind.
170 for (size_t i
=0, n
=maximalEdgeRings
.size(); i
<n
; i
++)
172 delete maximalEdgeRings
[i
];
174 maximalEdgeRings
.clear();
180 ConnectedInteriorTester::setInteriorEdgesInResult(PlanarGraph
&graph
)
182 std::vector
<EdgeEnd
*> *ee
=graph
.getEdgeEnds();
183 for(size_t i
=0, n
=ee
->size(); i
<n
; ++i
)
185 // Unexpected non DirectedEdge in graphEdgeEnds
186 assert(dynamic_cast<DirectedEdge
*>((*ee
)[i
]));
187 DirectedEdge
*de
=static_cast<DirectedEdge
*>((*ee
)[i
]);
188 if ( de
->getLabel().getLocation(0, Position::RIGHT
) == Location::INTERIOR
)
190 de
->setInResult(true);
197 ConnectedInteriorTester::buildEdgeRings(std::vector
<EdgeEnd
*> *dirEdges
,
198 std::vector
<EdgeRing
*>& minEdgeRings
)
201 cerr
<< __FUNCTION__
<< " got " << dirEdges
->size() << " EdgeEnd vector" << endl
;
204 typedef std::vector
<EdgeEnd
*> EdgeEnds
;
206 //std::vector<MinimalEdgeRing*> minEdgeRings;
207 for(EdgeEnds::size_type i
=0, n
=dirEdges
->size(); i
<n
; ++i
)
209 #ifdef GEOS_CAST_PARANOIA
210 assert(dynamic_cast<DirectedEdge
*>((*dirEdges
)[i
]));
212 DirectedEdge
*de
=static_cast<DirectedEdge
*>((*dirEdges
)[i
]);
215 cerr
<< "DirectedEdge " << i
<< ": " << de
->print() << endl
;
218 // if this edge has not yet been processed
219 if(de
->isInResult() && de
->getEdgeRing()==NULL
)
221 MaximalEdgeRing
* er
= new MaximalEdgeRing(de
,
223 // We track MaximalEdgeRings allocations
224 // using the private maximalEdgeRings vector
225 maximalEdgeRings
.push_back(er
);
227 er
->linkDirectedEdgesForMinimalEdgeRings();
228 er
->buildMinimalRings(minEdgeRings
);
232 std::vector<EdgeRing*> *edgeRings=new std::vector<EdgeRing*>();
233 edgeRings->assign(minEdgeRings.begin(), minEdgeRings.end());
239 * Mark all the edges for the edgeRings corresponding to the shells
240 * of the input polygons. Note only ONE ring gets marked for each shell.
243 ConnectedInteriorTester::visitShellInteriors(const Geometry
*g
, PlanarGraph
&graph
)
245 if (const Polygon
* p
=dynamic_cast<const Polygon
*>(g
))
247 visitInteriorRing(p
->getExteriorRing(), graph
);
250 if (const MultiPolygon
* mp
=dynamic_cast<const MultiPolygon
*>(g
))
252 for (size_t i
=0, n
=mp
->getNumGeometries(); i
<n
; i
++) {
253 const Polygon
*p
=dynamic_cast<const Polygon
*>(mp
->getGeometryN(i
));
254 visitInteriorRing(p
->getExteriorRing(), graph
);
260 ConnectedInteriorTester::visitInteriorRing(const LineString
*ring
, PlanarGraph
&graph
)
262 // can't visit an empty ring
263 if(ring
->isEmpty()) return;
265 const CoordinateSequence
*pts
=ring
->getCoordinatesRO();
266 const Coordinate
& pt0
=pts
->getAt(0);
269 * Find first point in coord list different to initial point.
270 * Need special check since the first point may be repeated.
272 const Coordinate
& pt1
=findDifferentPoint(pts
, pt0
);
273 Edge
*e
=graph
.findEdgeInSameDirection(pt0
, pt1
);
274 DirectedEdge
*de
=static_cast<DirectedEdge
*>(graph
.findEdgeEnd(e
));
275 DirectedEdge
*intDe
=NULL
;
276 if (de
->getLabel().getLocation(0,Position::RIGHT
)==Location::INTERIOR
) {
278 } else if (de
->getSym()->getLabel().getLocation(0,Position::RIGHT
)==Location::INTERIOR
) {
281 assert(intDe
!=NULL
); // unable to find dirEdge with Interior on RHS
282 visitLinkedDirectedEdges(intDe
);
287 ConnectedInteriorTester::visitLinkedDirectedEdges(DirectedEdge
*start
)
289 DirectedEdge
*startDe
=start
;
290 DirectedEdge
*de
=start
;
293 // found null Directed Edge
296 de
->setVisited(true);
299 } while (de
!=startDe
);
304 ConnectedInteriorTester::hasUnvisitedShellEdge(std::vector
<EdgeRing
*> *edgeRings
)
308 cerr
<< "hasUnvisitedShellEdge called with " << edgeRings
->size() << " edgeRings." << endl
;
311 for(std::vector
<EdgeRing
*>::iterator
312 it
=edgeRings
->begin(), itEnd
=edgeRings
->end();
319 // don't check hole rings
320 if (er
->isHole()) continue;
322 std::vector
<DirectedEdge
*>& edges
=er
->getEdges();
323 DirectedEdge
*de
=edges
[0];
326 // don't check CW rings which are holes
327 // (MD - this check may now be irrelevant - 2006-03-09)
328 if (de
->getLabel().getLocation(0, Position::RIGHT
) != Location::INTERIOR
) continue;
331 * the edgeRing is CW ring which surrounds the INT
332 * of the area, so check all edges have been visited.
333 * If any are unvisited, this is a disconnected part
336 for(std::vector
<DirectedEdge
*>::iterator
337 jt
=edges
.begin(), jtEnd
=edges
.end();
343 //Debug.print("visted? "); Debug.println(de);
344 if (!de
->isVisited()) {
345 //Debug.print("not visited "); Debug.println(de);
346 disconnectedRingcoord
=de
->getCoordinate();
354 } // namespace geos.operation.valid
355 } // namespace geos.operation
358 /**********************************************************************
360 * Revision 1.29 2006/06/12 11:29:24 strk
361 * unsigned int => size_t
363 * Revision 1.28 2006/04/06 12:45:28 strk
364 * Delayed deletion of newly allocated MaximalEdgeRings.
365 * Existing 'valid' operation tests don't should instability with
368 * Revision 1.27 2006/03/29 13:53:59 strk
369 * EdgeRing equipped with Invariant testing function and lots of exceptional assertions. Removed useless heap allocations, and pointers usages.
371 * Revision 1.26 2006/03/27 16:02:34 strk
372 * Added INL file for MinimalEdgeRing, added many debugging blocks,
373 * fixed memory leak in ConnectedInteriorTester (bug #59)
375 * Revision 1.25 2006/03/27 14:20:46 strk
376 * Added paranoid assertion checking and a note in header about responsibility of return from buildMaximalEdgeRings()
378 * Revision 1.24 2006/03/20 16:57:44 strk
379 * spatialindex.h and opValid.h headers split
381 * Revision 1.23 2006/03/17 16:48:55 strk
382 * LineIntersector and PointLocator made complete components of RelateComputer
383 * (were statics const pointers before). Reduced inclusions from opRelate.h
384 * and opValid.h, updated .cpp files to allow build.
386 **********************************************************************/