/// <summary> /// Collect edges from Area inputs which should be in the result but /// which have not been included in a result area. /// This happens ONLY: /// during an intersection when the boundaries of two /// areas touch in a line segment /// OR as a result of a dimensional collapse. /// </summary> /// <param name="de"></param> /// <param name="opCode"></param> /// <param name="edges"></param> public void CollectBoundaryTouchEdge(DirectedEdge de, SpatialFunctions opCode, IList edges) { Label label = de.Label; if (de.IsLineEdge) { return; // only interested in area edges } if (de.IsVisited) { return; // already processed } if (de.IsInteriorAreaEdge) { return; // added to handle dimensional collapses } if (de.Edge.IsInResult) { return; // if the edge linework is already included, don't include it again } // sanity check for labelling of result edgerings Assert.IsTrue(!(de.IsInResult || de.Sym.IsInResult) || !de.Edge.IsInResult); // include the linework if it's in the result of the operation if (OverlayOp.IsResultOfOp(label, opCode) && opCode == SpatialFunctions.Intersection) { edges.Add(de.Edge); de.VisitedEdge = true; } }
/// <summary> /// /// </summary> /// <param name="opCode"></param> /// <returns> /// A list of the LineStrings in the result of the specified overlay operation. /// </returns> public IList Build(SpatialFunctions opCode) { FindCoveredLineEdges(); CollectLines(opCode); BuildLines(opCode); return resultLineList; }
/// <summary> /// This method will handle arguments of Location.NULL correctly. /// </summary> /// <returns><c>true</c> if the locations correspond to the opCode.</returns> public static bool IsResultOfOp(Locations loc0, Locations loc1, SpatialFunctions opCode) { if (loc0 == Locations.Boundary) { loc0 = Locations.Interior; } if (loc1 == Locations.Boundary) { loc1 = Locations.Interior; } switch (opCode) { case SpatialFunctions.Intersection: return(loc0 == Locations.Interior && loc1 == Locations.Interior); case SpatialFunctions.Union: return(loc0 == Locations.Interior || loc1 == Locations.Interior); case SpatialFunctions.Difference: return(loc0 == Locations.Interior && loc1 != Locations.Interior); case SpatialFunctions.SymDifference: return((loc0 == Locations.Interior && loc1 != Locations.Interior) || (loc0 != Locations.Interior && loc1 == Locations.Interior)); default: return(false); } }
/// <summary> /// /// </summary> /// <param name="opCode"></param> /// <returns> /// A list of the Points in the result of the specified overlay operation. /// </returns> public virtual IList Build(SpatialFunctions opCode) { IList nodeList = CollectNodes(opCode); IList resultPointList = SimplifyPoints(nodeList); return(resultPointList); }
/// <summary> /// /// </summary> /// <param name="geom0"></param> /// <param name="geom1"></param> /// <param name="opCode"></param> /// <returns></returns> public static IGeometry Overlay(IGeometry geom0, IGeometry geom1, SpatialFunctions opCode) { OverlayOp gov = new OverlayOp(geom0, geom1); IGeometry geomOv = gov.GetResultGeometry(opCode); return(geomOv); }
/// <summary> /// /// </summary> /// <param name="label"></param> /// <param name="opCode"></param> /// <returns></returns> public static bool IsResultOfOp(Label label, SpatialFunctions opCode) { Locations loc0 = label.GetLocation(0); Locations loc1 = label.GetLocation(1); return(IsResultOfOp(loc0, loc1, opCode)); }
/// <summary> /// /// </summary> /// <param name="opCode"></param> /// <returns> /// A list of the LineStrings in the result of the specified overlay operation. /// </returns> public IList Build(SpatialFunctions opCode) { FindCoveredLineEdges(); CollectLines(opCode); BuildLines(opCode); return(resultLineList); }
/// <summary> /// /// </summary> /// <param name="opCode"></param> private void BuildLines(SpatialFunctions opCode) { for (IEnumerator it = lineEdgesList.GetEnumerator(); it.MoveNext();) { Edge e = (Edge)it.Current; ILineString line = geometryFactory.CreateLineString(e.Coordinates); resultLineList.Add(line); e.InResult = true; } }
/// <summary> /// /// </summary> /// <param name="opCode"></param> private void CollectLines(SpatialFunctions opCode) { IEnumerator it = op.Graph.EdgeEnds.GetEnumerator(); while (it.MoveNext()) { DirectedEdge de = (DirectedEdge)it.Current; CollectLineEdge(de, opCode, lineEdgesList); CollectBoundaryTouchEdge(de, opCode, lineEdgesList); } }
/// <summary> /// /// </summary> /// <param name="opCode"></param> private void ComputeOverlay(SpatialFunctions opCode) { // copy points from input Geometries. // This ensures that any Point geometries // in the input are considered for inclusion in the result set CopyPoints(0); CopyPoints(1); // node the input Geometries arg[0].ComputeSelfNodes(lineIntersector, false); arg[1].ComputeSelfNodes(lineIntersector, false); // compute intersections between edges of the two input geometries arg[0].ComputeEdgeIntersections(arg[1], lineIntersector, true); IList baseSplitEdges = new ArrayList(); arg[0].ComputeSplitEdges(baseSplitEdges); arg[1].ComputeSplitEdges(baseSplitEdges); // add the noded edges to this result graph InsertUniqueEdges(baseSplitEdges); ComputeLabelsFromDepths(); ReplaceCollapsedEdges(); graph.AddEdges(edgeList.Edges); ComputeLabelling(); LabelIncompleteNodes(); /* * The ordering of building the result Geometries is important. * Areas must be built before lines, which must be built before points. * This is so that lines which are covered by areas are not included * explicitly, and similarly for points. */ FindResultAreaEdges(opCode); CancelDuplicateResultEdges(); PolygonBuilder polyBuilder = new PolygonBuilder(geomFact); polyBuilder.Add(graph); resultPolyList = polyBuilder.Polygons; LineBuilder lineBuilder = new LineBuilder(this, geomFact, ptLocator); resultLineList = lineBuilder.Build(opCode); PointBuilder pointBuilder = new PointBuilder(this, geomFact, ptLocator); resultPointList = pointBuilder.Build(opCode); // gather the results from all calculations into a single Geometry for the result set resultGeom = ComputeGeometry(resultPointList, resultLineList, resultPolyList); }
/// <summary> /// /// </summary> /// <param name="de"></param> /// <param name="opCode"></param> /// <param name="edges"></param> public void CollectLineEdge(DirectedEdge de, SpatialFunctions opCode, IList edges) { Label label = de.Label; Edge e = de.Edge; // include Curve edges which are in the result if (de.IsLineEdge) { if (!de.IsVisited && OverlayOp.IsResultOfOp(label, opCode) && !e.IsCovered) { edges.Add(e); de.VisitedEdge = true; } } }
/// <summary> /// Find all edges whose label indicates that they are in the result area(s), /// according to the operation being performed. Since we want polygon shells to be /// oriented CW, choose dirEdges with the interior of the result on the RHS. /// Mark them as being in the result. /// Interior Area edges are the result of dimensional collapses. /// They do not form part of the result area boundary. /// </summary> private void FindResultAreaEdges(SpatialFunctions opCode) { IEnumerator it = graph.EdgeEnds.GetEnumerator(); while (it.MoveNext()) { DirectedEdge de = (DirectedEdge)it.Current; // mark all dirEdges with the appropriate label Label label = de.Label; if (label.IsArea() && !de.IsInteriorAreaEdge && IsResultOfOp(label.GetLocation(0, Positions.Right), label.GetLocation(1, Positions.Right), opCode)) { de.InResult = true; } } }
/// <summary> /// /// </summary> /// <param name="opCode"></param> /// <returns></returns> private IList CollectNodes(SpatialFunctions opCode) { IList resultNodeList = new ArrayList(); // add nodes from edge intersections which have not already been included in the result IEnumerator nodeit = op.Graph.Nodes.GetEnumerator(); while (nodeit.MoveNext()) { Node n = (Node) nodeit.Current; if (!n.IsInResult) { Label label = n.Label; if (OverlayOp.IsResultOfOp(label, opCode)) resultNodeList.Add(n); } } return resultNodeList; }
/// <summary> /// /// </summary> /// <param name="opCode"></param> /// <returns></returns> private IList CollectNodes(SpatialFunctions opCode) { IList resultNodeList = new ArrayList(); // add nodes from edge intersections which have not already been included in the result IEnumerator nodeit = op.Graph.Nodes.GetEnumerator(); while (nodeit.MoveNext()) { Node n = (Node)nodeit.Current; if (!n.IsInResult) { Label label = n.Label; if (OverlayOp.IsResultOfOp(label, opCode)) { resultNodeList.Add(n); } } } return(resultNodeList); }
/// <summary> /// Collect edges from Area inputs which should be in the result but /// which have not been included in a result area. /// This happens ONLY: /// during an intersection when the boundaries of two /// areas touch in a line segment /// OR as a result of a dimensional collapse. /// </summary> /// <param name="de"></param> /// <param name="opCode"></param> /// <param name="edges"></param> public void CollectBoundaryTouchEdge(DirectedEdge de, SpatialFunctions opCode, IList edges) { Label label = de.Label; if (de.IsLineEdge) return; // only interested in area edges if (de.IsVisited) return; // already processed if (de.IsInteriorAreaEdge) return; // added to handle dimensional collapses if (de.Edge.IsInResult) return; // if the edge linework is already included, don't include it again // sanity check for labelling of result edgerings Assert.IsTrue(!(de.IsInResult || de.Sym.IsInResult) || !de.Edge.IsInResult); // include the linework if it's in the result of the operation if (OverlayOp.IsResultOfOp(label, opCode) && opCode == SpatialFunctions.Intersection) { edges.Add(de.Edge); de.VisitedEdge = true; } }
/// <summary> /// This method will handle arguments of Location.NULL correctly. /// </summary> /// <returns><c>true</c> if the locations correspond to the opCode.</returns> public static bool IsResultOfOp(Locations loc0, Locations loc1, SpatialFunctions opCode) { if (loc0 == Locations.Boundary) loc0 = Locations.Interior; if (loc1 == Locations.Boundary) loc1 = Locations.Interior; switch (opCode) { case SpatialFunctions.Intersection: return loc0 == Locations.Interior && loc1 == Locations.Interior; case SpatialFunctions.Union: return loc0 == Locations.Interior || loc1 == Locations.Interior; case SpatialFunctions.Difference: return loc0 == Locations.Interior && loc1 != Locations.Interior; case SpatialFunctions.SymDifference: return (loc0 == Locations.Interior && loc1 != Locations.Interior) || (loc0 != Locations.Interior && loc1 == Locations.Interior); default: return false; } }
/// <summary> /// /// </summary> /// <param name="funcCode"></param> /// <returns></returns> public IGeometry GetResultGeometry(SpatialFunctions funcCode) { ComputeOverlay(funcCode); return(resultGeom); }
/// <summary> /// /// </summary> /// <param name="opCode"></param> /// <returns> /// A list of the Points in the result of the specified overlay operation. /// </returns> public IList Build(SpatialFunctions opCode) { IList nodeList = CollectNodes(opCode); IList resultPointList = SimplifyPoints(nodeList); return resultPointList; }
/// <summary> /// /// </summary> /// <param name="label"></param> /// <param name="opCode"></param> /// <returns></returns> public static bool IsResultOfOp(Label label, SpatialFunctions opCode) { Locations loc0 = label.GetLocation(0); Locations loc1 = label.GetLocation(1); return IsResultOfOp(loc0, loc1, opCode); }
/// <summary> /// /// </summary> /// <param name="geom0"></param> /// <param name="geom1"></param> /// <param name="opCode"></param> /// <returns></returns> public static IGeometry Overlay(IGeometry geom0, IGeometry geom1, SpatialFunctions opCode) { OverlayOp gov = new OverlayOp(geom0, geom1); IGeometry geomOv = gov.GetResultGeometry(opCode); return geomOv; }
/// <summary> /// Find all edges whose label indicates that they are in the result area(s), /// according to the operation being performed. Since we want polygon shells to be /// oriented CW, choose dirEdges with the interior of the result on the RHS. /// Mark them as being in the result. /// Interior Area edges are the result of dimensional collapses. /// They do not form part of the result area boundary. /// </summary> private void FindResultAreaEdges(SpatialFunctions opCode) { IEnumerator it = graph.EdgeEnds.GetEnumerator(); while (it.MoveNext()) { DirectedEdge de = (DirectedEdge) it.Current; // mark all dirEdges with the appropriate label Label label = de.Label; if (label.IsArea() && !de.IsInteriorAreaEdge && IsResultOfOp(label.GetLocation(0, Positions.Right), label.GetLocation(1, Positions.Right), opCode)) de.IsInResult = true; } }
/// <summary> /// /// </summary> /// <param name="opCode"></param> private void BuildLines(SpatialFunctions opCode) { for (IEnumerator it = lineEdgesList.GetEnumerator(); it.MoveNext(); ) { Edge e = (Edge) it.Current; ILineString line = geometryFactory.CreateLineString(e.Coordinates); resultLineList.Add(line); e.InResult = true; } }
/// <summary> /// /// </summary> /// <param name="funcCode"></param> /// <returns></returns> public IGeometry GetResultGeometry(SpatialFunctions funcCode) { ComputeOverlay(funcCode); return resultGeom; }
/// <summary> /// /// </summary> /// <param name="opCode"></param> private void CollectLines(SpatialFunctions opCode) { IEnumerator it = op.Graph.EdgeEnds.GetEnumerator(); while (it.MoveNext()) { DirectedEdge de = (DirectedEdge) it.Current; CollectLineEdge(de, opCode, lineEdgesList); CollectBoundaryTouchEdge(de, opCode, lineEdgesList); } }
/// <summary> /// /// </summary> /// <param name="opCode"></param> private void ComputeOverlay(SpatialFunctions opCode) { // copy points from input Geometries. // This ensures that any Point geometries // in the input are considered for inclusion in the result set CopyPoints(0); CopyPoints(1); // node the input Geometries arg[0].ComputeSelfNodes(lineIntersector, false); arg[1].ComputeSelfNodes(lineIntersector, false); // --- Needs to convert args to monotonic edges or something. // compute intersections between edges of the two input geometries arg[0].ComputeEdgeIntersections(arg[1], lineIntersector, true); IList baseSplitEdges = new ArrayList(); arg[0].ComputeSplitEdges(baseSplitEdges); arg[1].ComputeSplitEdges(baseSplitEdges); // add the noded edges to this result graph InsertUniqueEdges(baseSplitEdges); ComputeLabelsFromDepths(); ReplaceCollapsedEdges(); graph.AddEdges(edgeList.Edges); ComputeLabelling(); LabelIncompleteNodes(); /* * The ordering of building the result Geometries is important. * Areas must be built before lines, which must be built before points. * This is so that lines which are covered by areas are not included * explicitly, and similarly for points. */ FindResultAreaEdges(opCode); CancelDuplicateResultEdges(); PolygonBuilder polyBuilder = new PolygonBuilder(geomFact); polyBuilder.Add(graph); resultPolyList = polyBuilder.Polygons; LineBuilder lineBuilder = new LineBuilder(this, geomFact, ptLocator); resultLineList = lineBuilder.Build(opCode); PointBuilder pointBuilder = new PointBuilder(this, geomFact, ptLocator); resultPointList = pointBuilder.Build(opCode); // gather the results from all calculations into a single Geometry for the result set resultGeom = ComputeGeometry(resultPointList, resultLineList, resultPolyList); }