/// <summary> /// /// </summary> /// <returns></returns> public bool IsNonNested() { foreach (var innerRing in rings) { var innerRingPts = innerRing.Coordinates; foreach (var searchRing in rings) { var searchRingPts = searchRing.Coordinates; if (innerRing == searchRing) { continue; } if (!innerRing.EnvelopeInternal.Intersects(searchRing.EnvelopeInternal)) { continue; } var innerRingPt = IsValidOp.FindPointNotNode(innerRingPts, searchRing, graph); Assert.IsTrue(innerRingPt != null, "Unable to find a ring point not a node of the search ring"); bool isInside = PointLocation.IsInRing(innerRingPt, searchRingPts); if (isInside) { nestedPt = innerRingPt; return(false); } } } return(true); }
/// <summary> /// Find the innermost enclosing shell EdgeRing containing the argument EdgeRing, if any. /// The innermost enclosing ring is the <i>smallest</i> enclosing ring. /// The algorithm used depends on the fact that: /// ring A contains ring B iff envelope(ring A) contains envelope(ring B). /// This routine is only safe to use if the chosen point of the hole /// is known to be properly contained in a shell /// (which is guaranteed to be the case if the hole does not touch its shell). /// </summary> /// <param name="testEr"></param> /// <param name="shellList"></param> /// <returns>Containing EdgeRing, if there is one <br/> or /// <value>null</value> if no containing EdgeRing is found.</returns> private static EdgeRing FindEdgeRingContaining(EdgeRing testEr, IEnumerable <EdgeRing> shellList) { var teString = testEr.LinearRing; var testEnv = teString.EnvelopeInternal; var testPt = teString.GetCoordinateN(0); EdgeRing minShell = null; Envelope minEnv = null; foreach (var tryShell in shellList) { var tryRing = tryShell.LinearRing; var tryEnv = tryRing.EnvelopeInternal; if (minShell != null) { minEnv = minShell.LinearRing.EnvelopeInternal; } bool isContained = false; if (tryEnv.Contains(testEnv) && PointLocation.IsInRing(testPt, tryRing.Coordinates)) { isContained = true; } // check if this new containing ring is smaller than the current minimum ring if (isContained) { if (minShell == null || minEnv.Contains(tryEnv)) { minShell = tryShell; } } } return(minShell); }
/// <summary> /// This routine checks to see if a shell is properly contained in a hole. /// It assumes that the edges of the shell and hole do not /// properly intersect. /// </summary> /// <param name="shell"></param> /// <param name="hole"></param> /// <param name="graph"></param> /// <returns> /// <c>null</c> if the shell is properly contained, or /// a Coordinate which is not inside the hole if it is not. /// </returns> private Coordinate CheckShellInsideHole(ILinearRing shell, ILinearRing hole, GeometryGraph graph) { Coordinate[] shellPts = shell.Coordinates; Coordinate[] holePts = hole.Coordinates; // TODO: improve performance of this - by sorting pointlists? Coordinate shellPt = FindPointNotNode(shellPts, hole, graph); // if point is on shell but not hole, check that the shell is inside the hole if (shellPt != null) { bool insideHole = PointLocation.IsInRing(shellPt, holePts); if (!insideHole) { return(shellPt); } } Coordinate holePt = FindPointNotNode(holePts, shell, graph); // if point is on hole but not shell, check that the hole is outside the shell if (holePt != null) { bool insideShell = PointLocation.IsInRing(holePt, shellPts); if (insideShell) { return(holePt); } return(null); } Assert.ShouldNeverReachHere("points in shell and hole appear to be equal"); return(null); }
public bool IsNonNested() { BuildIndex(); for (int i = 0; i < _rings.Count; i++) { var innerRing = (ILinearRing)_rings[i]; Coordinate[] innerRingPts = innerRing.Coordinates; var results = _index.Query(innerRing.EnvelopeInternal); for (int j = 0; j < results.Count; j++) { var searchRing = (ILinearRing)results[j]; var searchRingPts = searchRing.Coordinates; if (innerRing == searchRing) { continue; } if (!innerRing.EnvelopeInternal.Intersects(searchRing.EnvelopeInternal)) { continue; } Coordinate innerRingPt = IsValidOp.FindPointNotNode(innerRingPts, searchRing, _graph); // Diego Guidi: removed => see Issue 121 //Assert.IsTrue(innerRingPt != null, "Unable to find a ring point not a node of the search ring"); /** * If no non-node pts can be found, this means * that the searchRing touches ALL of the innerRing vertices. * This indicates an invalid polygon, since either * the two holes create a disconnected interior, * or they touch in an infinite number of points * (i.e. along a line segment). * Both of these cases are caught by other tests, * so it is safe to simply skip this situation here. */ if (innerRingPt == null) { continue; } Boolean isInside = PointLocation.IsInRing(innerRingPt, searchRingPts); if (isInside) { _nestedPt = innerRingPt; return(false); } } } return(true); }
/// <summary> /// Tests whether the point pt is contained in the triangle defined by 3 <see cref="QuadEdge"/>es. /// </summary> /// <param name="tri">an array containing at least 3 QuadEdges</param> /// <param name="pt">the point to test</param> /// <returns>true if the point is contained in the triangle</returns> public static bool Contains(QuadEdge[] tri, Coordinate pt) { var ring = new[] { tri[0].Orig.Coordinate, tri[1].Orig.Coordinate, tri[2].Orig.Coordinate, tri[0].Orig.Coordinate }; return(PointLocation.IsInRing(pt, ring)); }
/// <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 /// <c>CheckRelateConsistency</c>). /// </summary> private void CheckShellNotNested(ILinearRing shell, IPolygon p, GeometryGraph graph) { Coordinate[] shellPts = shell.Coordinates; // test if shell is inside polygon shell ILinearRing polyShell = p.Shell; Coordinate[] polyPts = polyShell.Coordinates; Coordinate shellPt = FindPointNotNode(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 = PointLocation.IsInRing(shellPt, polyPts); if (!insidePolyShell) { return; } // if no holes, this is an error! if (p.NumInteriorRings <= 0) { _validErr = new TopologyValidationError(TopologyValidationErrors.NestedShells, shellPt); return; } /* * Check if the shell is inside one of the holes. * This is the case if one of the calls to checkShellInsideHole * returns a null coordinate. * Otherwise, the shell is not properly contained in a hole, which is an error. */ Coordinate badNestedPt = null; for (int i = 0; i < p.NumInteriorRings; i++) { ILinearRing hole = p.Holes[i]; badNestedPt = CheckShellInsideHole(shell, hole, graph); if (badNestedPt == null) { return; } } _validErr = new TopologyValidationError(TopologyValidationErrors.NestedShells, badNestedPt); }
/// <summary> /// Find the innermost enclosing shell EdgeRing containing the argument EdgeRing, if any. /// The innermost enclosing ring is the <i>smallest</i> enclosing ring. /// The algorithm used depends on the fact that: /// ring A contains ring B iff envelope(ring A) contains envelope(ring B). /// This routine is only safe to use if the chosen point of the hole /// is known to be properly contained in a shell /// (which is guaranteed to be the case if the hole does not touch its shell). /// </summary> /// <param name="shellList"></param> /// <param name="testEr"></param> /// <returns>Containing EdgeRing, if there is one <br/> /// or <value>null</value> if no containing EdgeRing is found.</returns> public static EdgeRing FindEdgeRingContaining(EdgeRing testEr, IList <EdgeRing> shellList) { var testRing = testEr.Ring; var testEnv = testRing.EnvelopeInternal; //var testPt = testRing.GetCoordinateN(0); EdgeRing minShell = null; Envelope minShellEnv = null; foreach (var tryShell in shellList) { var tryShellRing = tryShell.Ring; var tryShellEnv = tryShellRing.EnvelopeInternal; if (minShell != null) { minShellEnv = minShell.Ring.EnvelopeInternal; } // the hole envelope cannot equal the shell envelope // (also guards against testing rings against themselves) if (tryShellEnv.Equals(testEnv)) { continue; } // hole must be contained in shell if (!tryShellEnv.Contains(testEnv)) { continue; } var testPt = CoordinateArrays.PointNotInList(testRing.Coordinates, tryShellRing.Coordinates); var isContained = PointLocation.IsInRing(testPt, tryShellRing.Coordinates); // check if this new containing ring is smaller than the current minimum ring if (isContained) { if (minShell == null || minShellEnv.Contains(tryShellEnv)) { minShell = tryShell; minShellEnv = minShell.Ring.EnvelopeInternal; } } } return(minShell); }
/// <summary> /// Find the innermost enclosing shell EdgeRing containing the argument EdgeRing, if any. /// The innermost enclosing ring is the <i>smallest</i> enclosing ring. /// The algorithm used depends on the fact that: /// ring A contains ring B iff envelope(ring A) contains envelope(ring B). /// This routine is only safe to use if the chosen point of the hole /// is known to be properly contained in a shell /// (which is guaranteed to be the case if the hole does not touch its shell). /// </summary> /// <param name="testEr"></param> /// <param name="shellList"></param> /// <returns>Containing EdgeRing, if there is one <br/> or /// <c>null</c> if no containing EdgeRing is found.</returns> private static EdgeRing FindEdgeRingContaining(EdgeRing testEr, IEnumerable <EdgeRing> shellList) { var teString = testEr.LinearRing; var testEnv = teString.EnvelopeInternal; var testPt = teString.GetCoordinateN(0); EdgeRing minShell = null; Envelope minShellEnv = null; foreach (var tryShell in shellList) { var tryShellRing = tryShell.LinearRing; var tryShellEnv = tryShellRing.EnvelopeInternal; // the hole envelope cannot equal the shell envelope // (also guards against testing rings against themselves) if (tryShellEnv.Equals(testEnv)) { continue; } // hole must be contained in shell if (!tryShellEnv.Contains(testEnv)) { continue; } bool isContained = false; if (PointLocation.IsInRing(testPt, tryShellRing.Coordinates)) { isContained = true; } // check if this new containing ring is smaller than the current minimum ring if (isContained) { if (minShell == null || minShellEnv.Contains(tryShellEnv)) { minShell = tryShell; minShellEnv = tryShellEnv; } } } return(minShell); }
/// <summary> /// /// </summary> /// <param name="innerRing"></param> /// <param name="searchRing"></param> /// <returns></returns> private bool IsInside(LinearRing innerRing, LinearRing searchRing) { var innerRingPts = innerRing.Coordinates; var searchRingPts = searchRing.Coordinates; if (!innerRing.EnvelopeInternal.Intersects(searchRing.EnvelopeInternal)) { return(false); } var innerRingPt = IsValidOp.FindPointNotNode(innerRingPts, searchRing, graph); Assert.IsTrue(innerRingPt != null, "Unable to find a ring point not a node of the search ring"); bool isInside = PointLocation.IsInRing(innerRingPt, searchRingPts); if (isInside) { nestedPt = innerRingPt; return(true); } return(false); }
protected override void RunPtInRing(Location expectedLoc, Coordinate pt, string wkt) { // isPointInRing is not defined for pts on boundary if (expectedLoc == Location.Boundary) { return; } var geom = reader.Read(wkt); bool expected = expectedLoc == Location.Interior; Assert.AreEqual(expected, PointLocation.IsInRing(pt, geom.Coordinates)); var poly = geom as IPolygon; if (poly == null) { return; } Assert.AreEqual(expected, PointLocation.IsInRing(pt, poly.ExteriorRing.CoordinateSequence)); }
/// <summary> /// This method will cause the ring to be computed. /// It will also check any holes, if they have been assigned. /// </summary> /// <param name="p"></param> public bool ContainsPoint(Coordinate p) { ILinearRing shell = LinearRing; Envelope env = shell.EnvelopeInternal; if (!env.Contains(p)) { return(false); } if (!PointLocation.IsInRing(p, shell.Coordinates)) { return(false); } foreach (EdgeRing hole in _holes) { if (hole.ContainsPoint(p)) { return(false); } } return(true); }
/// <summary> /// /// </summary> /// <returns></returns> public bool IsNonNested() { BuildQuadtree(); for (int i = 0; i < _rings.Count; i++) { var innerRing = _rings[i]; var innerRingPts = innerRing.Coordinates; var results = _quadtree.Query(innerRing.EnvelopeInternal); for (int j = 0; j < results.Count; j++) { var searchRing = results[j]; var searchRingPts = searchRing.Coordinates; if (innerRing == searchRing) { continue; } if (!innerRing.EnvelopeInternal.Intersects(searchRing.EnvelopeInternal)) { continue; } var innerRingPt = IsValidOp.FindPointNotNode(innerRingPts, searchRing, _graph); Assert.IsTrue(innerRingPt != null, "Unable to find a ring point not a node of the search ring"); bool isInside = PointLocation.IsInRing(innerRingPt, searchRingPts); if (isInside) { _nestedPt = innerRingPt; return(false); } } } return(true); }
public bool Contains(Coordinate pt) { var ring = GetCoordinates(); return(PointLocation.IsInRing(pt, ring)); }
private void ReadPolygonShape(Shape shape) { List <LinearRing> shells = new(); List <LinearRing> holes = new(); foreach (PartRange part in shape.Range.Parts) { List <Coordinate> coords = new(); int i = part.StartIndex; foreach (Vertex d in part) { Coordinate c = new(d.X, d.Y); if (shape.M != null && shape.M.Length > 0) { c.M = shape.M[i]; } if (shape.Z != null && shape.Z.Length > 0) { c.Z = shape.Z[i]; } i++; coords.Add(c); } LinearRing ring = new(coords.ToArray()); if (shape.Range.Parts.Count == 1) { shells.Add(ring); } else { if (ring.IsCCW) { holes.Add(ring); } else { shells.Add(ring); } } } if (shells.Count == 0 && holes.Count > 0) { shells = holes; holes = new List <LinearRing>(); } //// Now we have a list of all shells and all holes List <LinearRing>[] holesForShells = new List <LinearRing> [shells.Count]; for (int i = 0; i < shells.Count; i++) { holesForShells[i] = new List <LinearRing>(); } // Find holes foreach (LinearRing hole in holes) { LinearRing minShell = null; Envelope minEnv = null; Envelope testEnv = hole.EnvelopeInternal; Coordinate testPt = hole.Coordinates[0]; for (int j = 0; j < shells.Count; j++) { LinearRing tryRing = shells[j]; Envelope tryEnv = tryRing.EnvelopeInternal; if (minShell != null) { minEnv = minShell.EnvelopeInternal; } // Check if this new containing ring is smaller than the current minimum ring if (tryEnv.Contains(testEnv) && (PointLocation.IsInRing(testPt, tryRing.Coordinates) || PointInList(testPt, tryRing.Coordinates))) { if (minShell == null || minEnv.Contains(tryEnv)) { minShell = tryRing; } holesForShells[j].Add(hole); } } } var polygons = new Polygon[shells.Count]; for (int i = 0; i < shells.Count; i++) { polygons[i] = new Polygon(shells[i], holesForShells[i].ToArray()); } if (polygons.Length == 1) { _geometry = polygons[0]; } else { // It's a multi part _geometry = new MultiPolygon(polygons); } _featureType = FeatureType.Polygon; }
/// <summary> /// Reads a stream and converts the shapefile record to an equilivent geometry object. /// </summary> /// <param name="file">The stream to read.</param> /// <param name="totalRecordLength">Total length of the record we are about to read</param> /// <param name="factory">The geometry factory to use when making the object.</param> /// <returns>The Geometry object that represents the shape file record.</returns> public override IGeometry Read(BigEndianBinaryReader file, int totalRecordLength, IGeometryFactory factory) { int totalRead = 0; var type = (ShapeGeometryType)ReadInt32(file, totalRecordLength, ref totalRead); if (type == ShapeGeometryType.NullShape) { return(factory.CreatePolygon(null, null)); } if (type != ShapeType) { throw new ShapefileException(string.Format("Encountered a '{0}' instead of a '{1}'", type, ShapeType)); } // Read and for now ignore bounds. var bblength = GetBoundingBoxLength(); boundingBox = new double[bblength]; for (; boundingBoxIndex < 4; boundingBoxIndex++) { boundingBox[boundingBoxIndex] = ReadDouble(file, totalRecordLength, ref totalRead); } var numParts = ReadInt32(file, totalRecordLength, ref totalRead); var numPoints = ReadInt32(file, totalRecordLength, ref totalRead); var partOffsets = new int[numParts]; for (var i = 0; i < numParts; i++) { partOffsets[i] = ReadInt32(file, totalRecordLength, ref totalRead); } var skippedList = new HashSet <int>(); //var allPoints = new List<Coordinate>(); var buffer = new CoordinateBuffer(numPoints, NoDataBorderValue, true); var pm = factory.PrecisionModel; for (var part = 0; part < numParts; part++) { var start = partOffsets[part]; var finish = (part == numParts - 1) ? numPoints : partOffsets[part + 1]; var length = finish - start; for (var i = 0; i < length; i++) { var x = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); var y = pm.MakePrecise(ReadDouble(file, totalRecordLength, ref totalRead)); // Thanks to Abhay Menon! if (!(Coordinate.NullOrdinate.Equals(x) || Coordinate.NullOrdinate.Equals(y))) { buffer.AddCoordinate(x, y); } else { skippedList.Add(start + i); } } //Add a marker that we have finished one part of the geometry buffer.AddMarker(); } // Trond Benum: We have now read all the parts, let's read optional Z and M values // and populate Z in the coordinate before we start manipulating the segments // We have to track corresponding optional M values and set them up in the // Geometries via ICoordinateSequence further down. GetZMValues(file, totalRecordLength, ref totalRead, buffer, skippedList); // Get the resulting sequences var sequences = buffer.ToSequences(factory.CoordinateSequenceFactory); var shells = new List <ILinearRing>(); var holes = new List <ILinearRing>(); for (var i = 0; i < sequences.Length; i++) { //Skip garbage input data with 0 points if (sequences[i].Count < 1) { continue; } var tmp = EnsureClosedSequence(sequences[i], factory.CoordinateSequenceFactory); var ring = factory.CreateLinearRing(tmp); if (ring.IsCCW) { holes.Add(ring); } else { shells.Add(ring); } } // Ensure the ring is encoded right if (shells.Count == 0 && holes.Count == 1) { shells.Add(factory.CreateLinearRing(holes[0].CoordinateSequence.Reversed())); holes.Clear(); } // Now we have lists of all shells and all holes var holesForShells = new List <List <ILinearRing> >(shells.Count); for (var i = 0; i < shells.Count; i++) { holesForShells.Add(new List <ILinearRing>()); } //Thanks to Bruno.Labrecque //Sort shells by area, rings should only be added to the smallest shell, that contains the ring shells.Sort(ProbeLinearRing); // Find holes foreach (var testHole in holes) { var testEnv = testHole.EnvelopeInternal; var testPt = testHole.GetCoordinateN(0); //We have the shells sorted for (var j = 0; j < shells.Count; j++) { var tryShell = shells[j]; var tryEnv = tryShell.EnvelopeInternal; var isContained = tryEnv.Contains(testEnv) && PointLocation.IsInRing(testPt, tryShell.Coordinates); // Check if this new containing ring is smaller than the current minimum ring if (isContained) { // Suggested by Brian Macomber and added 3/28/2006: // holes were being found but never added to the holesForShells array // so when converted to geometry by the factory, the inner rings were never created. var holesForThisShell = holesForShells[j]; holesForThisShell.Add(testHole); //Suggested by Bruno.Labrecque //A LinearRing should only be added to one outer shell break; } } } var polygons = new IPolygon[shells.Count]; for (var i = 0; i < shells.Count; i++) { polygons[i] = (factory.CreatePolygon(shells[i], holesForShells[i].ToArray())); } if (polygons.Length == 1) { geom = polygons[0]; } else { geom = factory.CreateMultiPolygon(polygons); } return(geom); }
/// <summary> /// Creates a Polygon or MultiPolygon from this Polygon shape. /// </summary> /// <param name="factory">The GeometryFactory to use to create the new Geometry.</param> /// <returns>The Polygon or IMultiPolygon created from this shape.</returns> protected Geometry FromPolygon(GeometryFactory factory) { if (factory == null) { factory = Geometry.DefaultFactory; } List <LinearRing> shells = new(); List <LinearRing> holes = new(); foreach (var part in Range.Parts) { var coords = GetCoordinates(part); var ring = factory.CreateLinearRing(coords.ToArray()); if (Range.Parts.Count == 1) { shells.Add(ring); } else { if (ring.IsCCW) { holes.Add(ring); } else { shells.Add(ring); } } } // Now we have a list of all shells and all holes List <LinearRing>[] holesForShells = new List <LinearRing> [shells.Count]; for (int i = 0; i < shells.Count; i++) { holesForShells[i] = new List <LinearRing>(); } // Find holes foreach (LinearRing t in holes) { LinearRing testRing = t; LinearRing minShell = null; Envelope minEnv = null; Envelope testEnv = testRing.EnvelopeInternal; Coordinate testPt = testRing.Coordinates[0]; for (int j = 0; j < shells.Count; j++) { LinearRing tryRing = shells[j]; Envelope tryEnv = tryRing.EnvelopeInternal; if (minShell != null) { minEnv = minShell.EnvelopeInternal; } var isContained = tryEnv.Contains(testEnv) && (PointLocation.IsInRing(testPt, tryRing.Coordinates) || PointInList(testPt, tryRing.Coordinates)); // Check if this new containing ring is smaller than the current minimum ring if (isContained) { if (minShell == null || minEnv.Contains(tryEnv)) { minShell = tryRing; } holesForShells[j].Add(t); } } } var polygons = new Polygon[shells.Count]; for (int i = 0; i < shells.Count; i++) { polygons[i] = factory.CreatePolygon(shells[i], holesForShells[i].ToArray()); } if (polygons.Length == 1) { return(polygons[0]); } // It's a multi part return(factory.CreateMultiPolygon(polygons)); }