EVOLUTION-MANAGER
Edit File: ConnectedInteriorTester.cpp
/********************************************************************** * * GEOS - Geometry Engine Open Source * http://geos.osgeo.org * * Copyright (C) 2007-2010 Safe Software Inc. * Copyright (C) 2005-2006 Refractions Research Inc. * Copyright (C) 2001-2002 Vivid Solutions Inc. * * This is free software; you can redistribute and/or modify it under * the terms of the GNU Lesser General Public Licence as published * by the Free Software Foundation. * See the COPYING file for more information. * ********************************************************************** * * Last port: operation/valid/ConnectedInteriorTester.java rev. 1.15 (JTS-1.10) * ********************************************************************** * * TODO: * * - Remove heap allocation of GeometryFactory (might use a singleton) * - Track MaximalEdgeRing references: we might be deleting them * leaving dangling refs around. * **********************************************************************/ #include <geos/operation/valid/ConnectedInteriorTester.h> #include <geos/operation/overlay/MaximalEdgeRing.h> #include <geos/operation/overlay/MinimalEdgeRing.h> #include <geos/operation/overlay/OverlayNodeFactory.h> #include <geos/geom/GeometryFactory.h> #include <geos/geom/CoordinateSequence.h> #include <geos/geom/Coordinate.h> #include <geos/geom/Location.h> #include <geos/geom/Polygon.h> #include <geos/geom/MultiPolygon.h> #include <geos/geom/LineString.h> #include <geos/geomgraph/GeometryGraph.h> #include <geos/geomgraph/PlanarGraph.h> #include <geos/geomgraph/EdgeRing.h> #include <geos/geomgraph/DirectedEdge.h> #include <geos/geomgraph/Position.h> #include <geos/geomgraph/Label.h> #include <vector> #include <cassert> #include <typeinfo> #ifndef GEOS_DEBUG #define GEOS_DEBUG 0 #endif //#define GEOS_CAST_PARANOIA 1 #if GEOS_DEBUG #include <iostream> #endif using namespace std; using namespace geos::geom; using namespace geos::geomgraph; using namespace geos::operation::overlay; namespace geos { namespace operation { // geos.operation namespace valid { // geos.operation.valid ConnectedInteriorTester::ConnectedInteriorTester(GeometryGraph& newGeomGraph): geometryFactory(GeometryFactory::create()), geomGraph(newGeomGraph), disconnectedRingcoord() { } Coordinate& ConnectedInteriorTester::getCoordinate() { return disconnectedRingcoord; } const Coordinate& ConnectedInteriorTester::findDifferentPoint(const CoordinateSequence* coord, const Coordinate& pt) { assert(coord); size_t npts = coord->getSize(); for(size_t i = 0; i < npts; ++i) { if(!(coord->getAt(i) == pt)) { return coord->getAt(i); } } return Coordinate::getNull(); } /*public*/ bool ConnectedInteriorTester::isInteriorsConnected() { // node the edges, in case holes touch the shell std::vector<Edge*> splitEdges; geomGraph.computeSplitEdges(&splitEdges); // form the edges into rings PlanarGraph graph(operation::overlay::OverlayNodeFactory::instance()); graph.addEdges(splitEdges); setInteriorEdgesInResult(graph); graph.linkResultDirectedEdges(); std::vector<EdgeRing*> edgeRings; buildEdgeRings(graph.getEdgeEnds(), edgeRings); #if GEOS_DEBUG cerr << "buildEdgeRings constructed " << edgeRings.size() << " edgeRings." << endl; #endif /* * Mark all the edges for the edgeRings corresponding to the shells * of the input polygons. * * Only ONE ring gets marked for each shell - if there are others * which remain unmarked this indicates a disconnected interior. */ visitShellInteriors(geomGraph.getGeometry(), graph); #if GEOS_DEBUG cerr << "after visitShellInteriors edgeRings are " << edgeRings.size() << " edgeRings." << endl; #endif /* * If there are any unvisited shell edges * (i.e. a ring which is not a hole and which has the interior * of the parent area on the RHS) * this means that one or more holes must have split the interior of the * polygon into at least two pieces. The polygon is thus invalid. */ bool res = !hasUnvisitedShellEdge(&edgeRings); #if GEOS_DEBUG cerr << "releasing " << edgeRings.size() << " edgeRings." << endl; #endif // Release memory allocated by buildEdgeRings for(size_t i = 0, n = edgeRings.size(); i < n; ++i) { EdgeRing* er = edgeRings[i]; #if GEOS_DEBUG cerr << *er << endl; #endif assert(er); delete er; #if GEOS_DEBUG cerr << "releasing edgeRing at " << er << endl; #endif } edgeRings.clear(); // Release memory allocated by MaximalEdgeRings // There should be no more references to this object // how to check this ? boost::shared_ptr<> comes to mind. // for(size_t i = 0, n = maximalEdgeRings.size(); i < n; i++) { delete maximalEdgeRings[i]; } maximalEdgeRings.clear(); return res; } void ConnectedInteriorTester::setInteriorEdgesInResult(PlanarGraph& graph) { std::vector<EdgeEnd*>* ee = graph.getEdgeEnds(); for(size_t i = 0, n = ee->size(); i < n; ++i) { // Unexpected non DirectedEdge in graphEdgeEnds assert(dynamic_cast<DirectedEdge*>((*ee)[i])); DirectedEdge* de = static_cast<DirectedEdge*>((*ee)[i]); if(de->getLabel().getLocation(0, Position::RIGHT) == Location::INTERIOR) { de->setInResult(true); } } } /*private*/ void ConnectedInteriorTester::buildEdgeRings(std::vector<EdgeEnd*>* dirEdges, std::vector<EdgeRing*>& minEdgeRings) { #if GEOS_DEBUG cerr << __FUNCTION__ << " got " << dirEdges->size() << " EdgeEnd vector" << endl; #endif typedef std::vector<EdgeEnd*> EdgeEnds; //std::vector<MinimalEdgeRing*> minEdgeRings; for(EdgeEnds::size_type i = 0, n = dirEdges->size(); i < n; ++i) { #ifdef GEOS_CAST_PARANOIA assert(dynamic_cast<DirectedEdge*>((*dirEdges)[i])); #endif DirectedEdge* de = static_cast<DirectedEdge*>((*dirEdges)[i]); #if GEOS_DEBUG cerr << "DirectedEdge " << i << ": " << de->print() << endl; #endif // if this edge has not yet been processed if(de->isInResult() && de->getEdgeRing() == nullptr) { MaximalEdgeRing* er = new MaximalEdgeRing(de, geometryFactory.get()); // We track MaximalEdgeRings allocations // using the private maximalEdgeRings vector maximalEdgeRings.push_back(er); er->linkDirectedEdgesForMinimalEdgeRings(); er->buildMinimalRings(minEdgeRings); } } /* std::vector<EdgeRing*> *edgeRings=new std::vector<EdgeRing*>(); edgeRings->assign(minEdgeRings.begin(), minEdgeRings.end()); return edgeRings; */ } /** * Mark all the edges for the edgeRings corresponding to the shells * of the input polygons. Note only ONE ring gets marked for each shell. */ void ConnectedInteriorTester::visitShellInteriors(const Geometry* g, PlanarGraph& graph) { if(const Polygon* p = dynamic_cast<const Polygon*>(g)) { visitInteriorRing(p->getExteriorRing(), graph); } if(const MultiPolygon* mp = dynamic_cast<const MultiPolygon*>(g)) { for(size_t i = 0, n = mp->getNumGeometries(); i < n; i++) { const Polygon* p = dynamic_cast<const Polygon*>(mp->getGeometryN(i)); visitInteriorRing(p->getExteriorRing(), graph); } } } void ConnectedInteriorTester::visitInteriorRing(const LineString* ring, PlanarGraph& graph) { // can't visit an empty ring if(ring->isEmpty()) { return; } const CoordinateSequence* pts = ring->getCoordinatesRO(); const Coordinate& pt0 = pts->getAt(0); /** * Find first point in coord list different to initial point. * Need special check since the first point may be repeated. */ const Coordinate& pt1 = findDifferentPoint(pts, pt0); Edge* e = graph.findEdgeInSameDirection(pt0, pt1); DirectedEdge* de = static_cast<DirectedEdge*>(graph.findEdgeEnd(e)); DirectedEdge* intDe = nullptr; if(de->getLabel().getLocation(0, Position::RIGHT) == Location::INTERIOR) { intDe = de; } else if(de->getSym()->getLabel().getLocation(0, Position::RIGHT) == Location::INTERIOR) { intDe = de->getSym(); } assert(intDe != nullptr); // unable to find dirEdge with Interior on RHS visitLinkedDirectedEdges(intDe); } void ConnectedInteriorTester::visitLinkedDirectedEdges(DirectedEdge* start) { DirectedEdge* startDe = start; DirectedEdge* de = start; //Debug.println(de); do { // found null Directed Edge assert(de != nullptr); de->setVisited(true); de = de->getNext(); //Debug.println(de); } while(de != startDe); } /*private*/ bool ConnectedInteriorTester::hasUnvisitedShellEdge(std::vector<EdgeRing*>* edgeRings) { #if GEOS_DEBUG cerr << "hasUnvisitedShellEdge called with " << edgeRings->size() << " edgeRings." << endl; #endif for(std::vector<EdgeRing*>::iterator it = edgeRings->begin(), itEnd = edgeRings->end(); it != itEnd; ++it) { EdgeRing* er = *it; assert(er); // don't check hole rings if(er->isHole()) { continue; } std::vector<DirectedEdge*>& edges = er->getEdges(); DirectedEdge* de = edges[0]; assert(de); // don't check CW rings which are holes // (MD - this check may now be irrelevant - 2006-03-09) if(de->getLabel().getLocation(0, Position::RIGHT) != Location::INTERIOR) { continue; } /* * the edgeRing is CW ring which surrounds the INT * of the area, so check all edges have been visited. * If any are unvisited, this is a disconnected part * of the interior */ for(std::vector<DirectedEdge*>::iterator jt = edges.begin(), jtEnd = edges.end(); jt != jtEnd; ++jt) { de = *jt; assert(de); //Debug.print("visted? "); Debug.println(de); if(!de->isVisited()) { //Debug.print("not visited "); Debug.println(de); disconnectedRingcoord = de->getCoordinate(); return true; } } } return false; } } // namespace geos.operation.valid } // namespace geos.operation } // namespace geos