/// <summary> /// Constructs an Operation object. /// </summary> /// <param name="g0"></param> public GeometryGraphOperation(Geometry g0) { SetComputationPrecision( g0.PrecisionModel ); _arg = new GeometryGraph[1]; _arg[0] = new GeometryGraph(0, g0);; }
/// <summary> /// Constructs an Operation object. /// </summary> /// <param name="g0"></param> /// <param name="g1"></param> public GeometryGraphOperation(Geometry g0, Geometry g1) { SetComputationPrecision( g0.PrecisionModel ); _arg = new GeometryGraph[2]; _arg[0] = new GeometryGraph(0, g0); _arg[1] = new GeometryGraph(1, g1); }
/// <summary> /// /// </summary> /// <param name="geomGraph"></param> public void Build(GeometryGraph geomGraph) { // compute nodes for intersections between previously noded edges ComputeIntersectionNodes( geomGraph, 0 ); // Copy the labelling for the nodes in the parent Geometry. These override // any labels determined by intersections. CopyNodesAndLabels( geomGraph, 0 ); // Build EdgeEnds for all intersections. EdgeEndBuilder eeBuilder = new EdgeEndBuilder(); ArrayList eeList = eeBuilder.ComputeEdgeEnds( geomGraph.Edges ); InsertEdgeEnds( eeList ); //Trace.WriteLine("==== NodeList ==="); //Trace.WriteLine( _nodes.ToString() ); } // public void Build(GeometryGraph geomGraph)
/// <summary> /// Find a point from the list of testCoords /// that is NOT a node in the edge for the list of searchCoords /// </summary> /// <param name="testCoords"></param> /// <param name="searchRing"></param> /// <param name="graph"></param> /// <returns>return the point found, or null if none found</returns> public static Coordinate FindPtNotNode( Coordinates testCoords, LinearRing searchRing, GeometryGraph graph) { // find edge corresponding to searchRing. Edge searchEdge = graph.FindEdge(searchRing ); // find a point in the testCoords which is not a node of the searchRing EdgeIntersectionList eiList = searchEdge.EdgeIntersectionList; // somewhat inefficient - is there a better way? (Use a node map, for instance?) for (int i = 0 ; i < testCoords.Count; i++) { Coordinate pt = testCoords[i]; if (! eiList.IsIntersection(pt)) { return pt; } } return null; }
} // private bool IsSimpleLinearGeometry( Geometry geom ) /// <summary> /// For all edges, check if there are any intersections which are NOT at an endpoint. /// The Geometry is not simple if there are intersections not at endpoints. /// </summary> /// <param name="graph"></param> /// <returns></returns> private bool HasNonEndpointIntersection( GeometryGraph graph ) { foreach ( object obj in graph.Edges ) { Edge e = (Edge) obj; int maxSegmentIndex = e.MaximumSegmentIndex; foreach ( object eiObj in e.EdgeIntersectionList ) { EdgeIntersection ei = (EdgeIntersection) eiObj; if ( !ei.IsEndPoint( maxSegmentIndex ) ) { return true; } } } // foreach ( object obj in graph.Edges ) return false; } // private bool HasNonEndpointIntersection(GeometryGraph graph)
private void CheckConsistentArea(GeometryGraph graph) { ConsistentAreaTester cat = new ConsistentAreaTester(graph); bool isValidArea = cat.IsNodeConsistentArea(); if (! isValidArea) { _validErr = new TopologyValidationError( TopologyValidationError.SelfIntersection, cat.GetInvalidPoint()); return; } if (cat.HasDuplicateRings()) { _validErr = new TopologyValidationError( TopologyValidationError.DuplicateRings, cat.GetInvalidPoint()); } }
private void CheckTooFewPoints(GeometryGraph graph) { if (graph.HasTooFewPoints()) { _validErr = new TopologyValidationError( TopologyValidationError.TooFewPoints, graph.GetInvalidPoint()); return; } }
/** * Use a GeometryGraph to node the created edges, * and create split edges between the nodes */ private ArrayList NodeEdges(ArrayList edges) { // intersect edges again to ensure they are noded correctly GeometryGraph graph = new GeometryGraph(0, _geomFact.PrecisionModel, 0); //for (Iterator i = edges.iterator(); i.hasNext(); ) foreach(object obj in edges) { Edge e = (Edge) obj; graph.AddEdge(e); } SegmentIntersector si = graph.ComputeSelfNodes(_li); /* if (si.hasProperIntersection()) Debug.println("proper intersection found"); else Debug.println("no proper intersection found"); */ ArrayList newEdges = new ArrayList(); graph.ComputeSplitEdges(newEdges); return newEdges; }
/// <summary> /// Test that each hole is inside the polygon shell. /// This routine assumes that the holes have previously been tested /// to ensure that all vertices lie on the shell or inside it. /// A simple test of a single point in the hole can be used, /// provide the point is chosen such that it does not lie on the /// boundary of the shell. /// </summary> /// <param name="p">The polygon to be tested for hole inclusion.</param> /// <param name="graph">Graph a GeometryGraph incorporating the polygon.</param> private void CheckHolesInShell(Polygon p, GeometryGraph graph) { LinearRing shell = (LinearRing) p.GetExteriorRing(); Coordinates shellPts = shell.GetCoordinates(); //PointInRing pir = new SimplePointInRing(shell); //PointInRing pir = new SIRtreePointInRing(shell); IPointInRing pir = new MCPointInRing(shell); for (int i = 0; i < p.GetNumInteriorRing(); i++) { LinearRing hole = (LinearRing) p.GetInteriorRingN(i); Coordinate holePt = FindPtNotNode(hole.GetCoordinates(), shell, graph); if (holePt == null) { throw new InvalidOperationException("Unable to find a hole point not a vertex of the shell."); } bool outside = ! pir.IsInside(holePt); if ( outside ) { _validErr = new TopologyValidationError( TopologyValidationError.HoleOutsideShell, holePt); return; } } }
} // public void ComputeIntersectionNodes( GeometryGraph geomGraph, int argIndex ) /// <summary> /// Copy all nodes from an arg geometry into this graph. /// The node label in the arg geometry overrides any previously computed /// label for that argIndex. /// (E.g. a node may be an intersection node with /// a computed label of BOUNDARY, /// but in the original arg Geometry it is actually /// in the interior due to the Boundary Determination Rule) /// </summary> /// <param name="geomGraph"></param> /// <param name="argIndex"></param> public void CopyNodesAndLabels( GeometryGraph geomGraph, int argIndex ) { foreach ( DictionaryEntry obj in geomGraph.Nodes ) { Node graphNode = (Node) obj.Value; Node newNode = _nodes.AddNode( graphNode.GetCoordinate() ); newNode.SetLabel( argIndex, graphNode.Label.GetLocation( argIndex ) ); //Trace.WriteLine( _node.ToString() ); } // foreach ( object obj in geomGraph.Nodes ) } // public void CopyNodesAndLabels( GeometryGraph geomGraph, int argIndex )
/// <summary> /// Constructor. /// </summary> /// <param name="arg">Array of GeometryGraph objects.</param> public RelateComputer( GeometryGraph[] arg ) { _arg = arg; } // public RelateComputer(GeometryGraph[] arg)
private void CheckConnectedInteriors(GeometryGraph graph) { ConnectedInteriorTester cit = new ConnectedInteriorTester(graph); if (! cit.IsInteriorsConnected()) { _validErr = new TopologyValidationError( TopologyValidationError.DisconnectedInterior, cit.GetCoordinate()); } }
/// <summary> /// This routine Checks to see if a shell is properly contained in a hole. /// </summary> /// <param name="shell"></param> /// <param name="hole"></param> /// <param name="graph"></param> private void CheckShellInsideHole(LinearRing shell, LinearRing hole, GeometryGraph graph) { Coordinates shellPts = shell.GetCoordinates(); Coordinates holePts = hole.GetCoordinates(); // TODO: improve performance of this - by sorting pointlists for instance? Coordinate shellPt = FindPtNotNode(shellPts, hole, graph); // if point is on shell but not hole, Check that the shell is inside the hole if (shellPt != null) { bool insideHole = _cga.IsPointInRing(shellPt, holePts); if (! insideHole) _validErr = new TopologyValidationError( TopologyValidationError.NestedShells, shellPt); return; } Coordinate holePt = FindPtNotNode(holePts, shell, graph); // if point is on hole but not shell, Check that the hole is outside the shell if (holePt != null) { bool insideShell = _cga.IsPointInRing(holePt, shellPts); if (insideShell) { _validErr = new TopologyValidationError( TopologyValidationError.NestedShells, holePt); } return; } throw new InvalidOperationException("Points in shell and hole appear to be equal."); }
/// <summary> /// Check if a shell is incorrectly nested within a polygon. This is the case /// if the shell is inside the polygon shell, but not inside a polygon hole. /// If the shell is inside a polygon hole, the nesting is valid.) /// /// The algorithm used relies on the fact that the rings must be properly contained. /// E.g. they cannot partially overlap (this has been previously Checked by /// CheckRelateConsistency /// </summary> /// <param name="shell"></param> /// <param name="p"></param> /// <param name="graph"></param> private void CheckShellNotNested(LinearRing shell, Polygon p, GeometryGraph graph) { Coordinates shellPts = shell.GetCoordinates(); // test if shell is inside polygon shell LinearRing polyShell = (LinearRing) p.GetExteriorRing(); Coordinates polyPts = polyShell.GetCoordinates(); Coordinate shellPt = FindPtNotNode(shellPts, polyShell, graph); // if no point could be found, we can assume that the shell is outside the polygon if (shellPt == null) return; bool insidePolyShell = _cga.IsPointInRing(shellPt, polyPts); if (! insidePolyShell) return; // if no holes, this is an error! if (p.GetNumInteriorRing() <= 0) { _validErr = new TopologyValidationError( TopologyValidationError.NestedShells, shellPt); return; } for (int i = 0; i < p.GetNumInteriorRing(); i++) { LinearRing hole = p.GetInteriorRingN( i ); CheckShellInsideHole(shell, hole, graph); if (_validErr != null) return; } }
/* private void SLOWCheckHolesNotNested(Polygon p) { for (int i = 0; i < p.GetNumInteriorRing(); i++) { LinearRing innerHole = p.GetInteriorRingN( i ); Coordinates innerHolePts = innerHole.GetCoordinates(); for (int j = 0; j < p.GetNumInteriorRing(); j++) { // don't test hole against itself! if (i == j) continue; LinearRing searchHole = p.GetInteriorRingN( j ); // if envelopes don't overlap, holes are not nested if (! innerHole.GetEnvelopeInternal().Overlaps(searchHole.GetEnvelopeInternal())) continue; Coordinates searchHolePts = searchHole.GetCoordinates(); Coordinate innerholePt = FindPtNotNode(innerHolePts, searchHole, _arg[0]); //Assert.isTrue(innerholePt != null, "Unable to find a hole point not a node of the search hole"); bool inside = _cga.IsPointInPolygon(innerholePt, searchHolePts); if ( inside ) { _validErr = new TopologyValidationError( TopologyValidationError.NestedHoles, innerholePt); return; } } } } */ /// <summary> /// Test that no element polygon is wholly in the interior of another element polygon. /// </summary> /// <remarks> /// TODO: It handles the case that one polygon is nested inside a hole of another. /// /// Preconditions: /// <ul> /// <li>shells do not partially overlap</li> /// <li>shells do not touch along an edge</li> /// <li>no duplicate rings exist</li> /// </ul> /// This routine relies on the fact that while polygon shells may touch at one or /// more vertices, they cannot touch at ALL vertices. /// </remarks> /// <param name="mp"></param> /// <param name="graph"></param> private void CheckShellsNotNested(MultiPolygon mp, GeometryGraph graph) { for (int i = 0; i < mp.GetNumGeometries(); i++) { Polygon p = (Polygon) mp.GetGeometryN(i); LinearRing shell = (LinearRing) p.GetExteriorRing(); for (int j = 0; j < mp.GetNumGeometries(); j++) { if (i == j) continue; Polygon p2 = (Polygon) mp.GetGeometryN(j); CheckShellNotNested(shell, p2, graph); if (_validErr != null) return; } } }
/// <summary> /// Tests that no hole is nested inside another hole. /// </summary> /// <remarks> /// This routine assumes that the holes are disjoint. /// To ensure this, holes have previously been tested /// to ensure that: /// <ul> /// <li>they do not partially overlap /// (Checked by CheckRelateConsistency)</li> /// <li>they are not identical /// (Checked by CheckRelateConsistency)</li> /// <li>they do not touch at a vertex /// (Checked by ????)</li> /// </ul> /// </remarks> /// <param name="p"></param> /// <param name="graph"></param> private void CheckHolesNotNested(Polygon p, GeometryGraph graph) { QuadtreeNestedRingTester nestedTester = new QuadtreeNestedRingTester(graph); //SimpleNestedRingTester nestedTester = new SimpleNestedRingTester(_arg[0]); //SweeplineNestedRingTester nestedTester = new SweeplineNestedRingTester(_arg[0]); for (int i = 0; i < p.GetNumInteriorRing(); i++) { LinearRing innerHole = p.GetInteriorRingN( i ); nestedTester.Add(innerHole); } bool isNonNested = nestedTester.IsNonNested(); if ( ! isNonNested ) { _validErr = new TopologyValidationError( TopologyValidationError.NestedHoles, nestedTester.GetNestedPoint()); } }
} // class EndpointInfo /// <summary> /// Test that no edge intersection is the endpoint of a closed line. To check this we /// compute the degree of each endpoint. The degree of endpoints of closed lines must be /// exactly 2. /// </summary> /// <param name="graph">The GeometryGraph to test.</param> /// <returns></returns> private bool HasClosedEndpointIntersection( GeometryGraph graph ) { SortedList endPoints = new SortedList(); foreach ( object obj in graph.Edges ) { Edge e = (Edge) obj; int maxSegmentIndex = e.MaximumSegmentIndex; bool isClosed = e.IsClosed; Coordinate p0 = e.GetCoordinate( 0 ); AddEndpoint( ref endPoints, p0, isClosed ); Coordinate p1 = e.GetCoordinate( e.NumPoints - 1 ); AddEndpoint( ref endPoints, p1, isClosed ); } foreach( DictionaryEntry entry in endPoints ) { EndpointInfo eiInfo = (EndpointInfo) entry.Value; if ( eiInfo._isClosed && eiInfo._degree != 2) { return true; } } return false; } // private bool HasClosedEndpointIntersection( GeometryGraph graph )
/// <summary> /// Compute the labelling for all dirEdges in this star, as well /// as the overall labelling /// </summary> /// <param name="geom"></param> public override void ComputeLabelling(GeometryGraph[] geom) { //Trace.WriteLine( ToString() ); base.ComputeLabelling( geom ); // determine the overall labelling for this DirectedEdgeStar // (i.e. for the node it is based at) _label = new Label( Location.Null ); foreach ( object obj in Edges() ) { EdgeEnd ee = (EdgeEnd)obj; Edge e = ee.Edge; Label eLabel = e.Label; for (int i = 0; i < 2; i++) { int eLoc = eLabel.GetLocation(i); if ( eLoc == Location.Interior || eLoc == Location.Boundary ) { _label.SetLocation( i, Location.Interior ); } } } // foreach ( object obj in edges ) //Trace.WriteLine( ToString() ); }
} // public bool IsSimple( MultiPoint mp ) /// <summary> /// Tests to see if geometry is simple. /// </summary> /// <param name="geom">Geometry to test.</param> /// <returns>Returns true if geometry is simple, false otherwise.</returns> private bool IsSimpleLinearGeometry( Geometry geom ) { if( geom.IsEmpty() ) { return true; } GeometryGraph graph = new GeometryGraph( 0, geom ); LineIntersector li = new RobustLineIntersector(); SegmentIntersector si = graph.ComputeSelfNodes( li ); // if no self-intersection, must be simple if( !si.HasIntersection ) { return true; } if( si.HasProperIntersection ) { return false; } if( HasNonEndpointIntersection( graph ) ) { return false; } if( HasClosedEndpointIntersection( graph ) ) { return false; } return true; } // private bool IsSimpleLinearGeometry( Geometry geom )
} // public void Build(GeometryGraph geomGraph) /// <summary> /// Insert nodes for all intersections on the edges of a Geometry. /// Label the created nodes the same as the edge label if they do not already have a label. /// This allows nodes created by either self-intersections or /// mutual intersections to be labelled. /// Endpoint nodes will already be labelled from when they were inserted. /// Precondition: edge intersections have been computed. /// </summary> /// <param name="geomGraph"></param> /// <param name="argIndex"></param> public void ComputeIntersectionNodes( GeometryGraph geomGraph, int argIndex ) { foreach ( object obj in geomGraph.Edges ) { Edge e = (Edge) obj; int eLoc = e.Label.GetLocation( argIndex ); foreach ( object objEdgeIntersection in e.EdgeIntersectionList ) { EdgeIntersection ei = (EdgeIntersection) objEdgeIntersection; RelateNode n = (RelateNode) _nodes.AddNode( ei.Coordinate ); if ( eLoc == Location.Boundary ) { n.SetLabelBoundary( argIndex ); } else { if ( n.Label.IsNull( argIndex ) ) { n.SetLabel( argIndex, Location.Interior ); } // if ( n.Label.IsNull( argIndex ) ) } //Trace.WriteLine( n.ToString() ); } } // foreach ( object obj in geomGraph.Edges ) } // public void ComputeIntersectionNodes( GeometryGraph geomGraph, int argIndex )
/// <summary> /// /// </summary> /// <param name="g"></param> /// <param name="li"></param> /// <param name="includeProper"></param> /// <returns></returns> public SegmentIntersector ComputeEdgeIntersections( GeometryGraph g, LineIntersector li, bool includeProper) { SegmentIntersector si = new SegmentIntersector( li, includeProper, true); si.SetBoundaryNodes( GetBoundaryNodes(), g.GetBoundaryNodes() ); EdgeSetIntersector esi = new SimpleMCSweepLineIntersector(); esi.ComputeIntersections( _edges, g.Edges, si ); /* foreach ( object obj in g ) { Edge e = (Edge) obj; Trace.WriteLine( e.EdgeIntersectionList.ToString() ); } */ return si; }
} // public EdgeEnd GetNextCW(EdgeEnd ee) /// <summary> /// /// </summary> /// <param name="geom"></param> public virtual void ComputeLabelling( GeometryGraph[] geom ) { ComputeEdgeEndLabels(); // Propagate side labels around the edges in the star // for each parent Geometry Trace.WriteLine( ToString() ); PropagateSideLabels(0); Trace.WriteLine( ToString() ); PropagateSideLabels(1); Trace.WriteLine( ToString() ); /** * If there are edges that still have null labels for a geometry * this must be because there are no area edges for that geometry incident on this node. * In this case, to label the edge for that geometry we must test whether the * edge is in the interior of the geometry. * To do this it suffices to determine whether the node for the edge is in the interior of an area. * If so, the edge has location INTERIOR for the geometry. * In all other cases (e.g. the node is on a line, on a point, or not on the geometry at all) the edge * has the location EXTERIOR for the geometry. * * Note that the edge cannot be on the BOUNDARY of the geometry, since then * there would have been a parallel edge from the Geometry at this node also labelled BOUNDARY * and this edge would have been labelled in the previous step. * * This code causes a problem when dimensional collapses are present, since it may try and * determine the location of a node where a dimensional collapse has occurred. * The point should be considered to be on the EXTERIOR * of the polygon, but locate() will return INTERIOR, since it is passed * the original Geometry, not the collapsed version. * * If there are incident edges which are Line edges labelled BOUNDARY, * then they must be edges resulting from dimensional collapses. * In this case the other edges can be labelled EXTERIOR for this Geometry. * * MD 8/11/01 - NOT TRUE! The collapsed edges may in fact be in the interior of the Geometry, * which means the other edges should be labelled INTERIOR for this Geometry. * Not sure how solve this... Possibly labelling needs to be split into several phases: * area label propagation, symLabel merging, then finally null label resolution. */ bool[] hasDimensionalCollapseEdge = new bool[]{ false, false }; foreach ( EdgeEnd e in Edges() ) { Label label = e.Label; for (int geomi = 0; geomi < 2; geomi++) { if ( label.IsLine( geomi ) && label.GetLocation( geomi ) == Location.Boundary ) { hasDimensionalCollapseEdge[geomi] = true; } } // for (int geomi = 0; geomi < 2; geomi++) } // foreach ( EdgeEnd e in edges ) //Trace.WriteLine( ToString() ); foreach ( EdgeEnd e in Edges() ) { Label label = e.Label; //Trace.WriteLine( label.ToString() ); for (int geomi = 0; geomi < 2; geomi++) { if ( label.IsAnyNull( geomi ) ) { int loc = Location.Null; if ( hasDimensionalCollapseEdge[geomi] ) { loc = Location.Exterior; } else { Coordinate p = e.Coordinate; loc = GetLocation( geomi, p, geom ); } label.SetAllLocationsIfNull( geomi, loc ); } } //Tract.WriteLin( e.ToString() ); } //Trace.WriteLine( ToString() ); } // public void ComputeLabelling( GeometryGraph[] geom )
/// <summary> /// Initializes a new instance of the SimpleNestedRingTester class. /// </summary> public SimpleNestedRingTester(GeometryGraph graph) { this._graph = graph; }
private void CheckNoSelfIntersectingRings(GeometryGraph graph) { foreach (object obj in graph.Edges ) { Edge e = (Edge) obj; CheckSelfIntersectingRing(e.EdgeIntersectionList); if (_validErr != null) { return; } } }
///<summary> /// Returns the boundary, or the empty geometry if this Geometry is empty. ///</summary> ///<remarks>For a discussion /// of this function, see the OpenGIS Simple Features Specification. As stated in SFS /// Section 2.1.13.1, "the boundary of a Geometry is a set of Geometries of the next lower dimension."</remarks> ///<returns>Returns the closure of the combinatorial boundary of this Geometry.</returns> public override Geometry GetBoundary() { if ( IsEmpty() ) { return _geometryFactory.CreateGeometryCollection( null ); } GeometryGraph g = new GeometryGraph(0, this); Coordinates pts = g.GetBoundaryPoints(); return _geometryFactory.CreateMultiPoint(pts); }
public ConnectedInteriorTester(GeometryGraph geomGraph) { this._geomGraph = geomGraph; }
int GetLocation(int geomIndex, Coordinate p, GeometryGraph[] geom) { // compute location only on demand if ( _ptInAreaLocation[ geomIndex ] == Location.Null ) { _ptInAreaLocation[ geomIndex ] = SimplePointInAreaLocator.Locate( p, geom[geomIndex].Geometry ); } return _ptInAreaLocation[geomIndex]; }
/// <summary> /// Initializes a new instance of the QuadtreeNestedRingTester class. /// </summary> public QuadtreeNestedRingTester(GeometryGraph graph) { this._graph = graph; }
/// <summary> /// Initializes a new instance of the SweeplineNestedRingTester class. /// </summary> public SweeplineNestedRingTester(GeometryGraph graph) { this._graph = graph; }
private void CheckValid(MultiPolygon g) { GeometryGraph graph = new GeometryGraph(0, g); CheckConsistentArea(graph); if (_validErr != null) return; CheckNoSelfIntersectingRings(graph); if (_validErr != null) return; for (int i = 0; i < g.GetNumGeometries(); i++) { Polygon p = (Polygon) g.GetGeometryN(i); CheckHolesInShell(p, graph); if (_validErr != null) return; } for (int i = 0; i < g.GetNumGeometries(); i++) { Polygon p = (Polygon) g.GetGeometryN(i); CheckHolesNotNested(p, graph); if (_validErr != null) return; } CheckShellsNotNested(g, graph); if (_validErr != null) return; CheckConnectedInteriors(graph); }