/// <summary> /// Tests 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">A GeometryGraph incorporating the polygon.</param> private void CheckHolesInShell(IPolygon p, GeometryGraph graph) { ILinearRing shell = p.Shell; IPointInRing pir = new MCPointInRing(shell); for (int i = 0; i < p.NumInteriorRings; i++) { ILinearRing hole = p.Holes[i]; ICoordinate holePt = FindPointNotNode(hole.Coordinates, shell, graph); /* * If no non-node hole vertex can be found, the hole must * split the polygon into disconnected interiors. * This will be caught by a subsequent check. */ if (holePt == null) { return; } bool outside = !pir.IsInside((Coordinate)holePt); if (outside) { validErr = new TopologyValidationError(TopologyValidationErrors.HoleOutsideShell, holePt); return; } } }
/// <summary> /// /// </summary> /// <param name="ring"></param> private void CheckClosedRing(ILinearRing ring) { if (!ring.IsClosed) { validErr = new TopologyValidationError(TopologyValidationErrors.RingNotClosed, ring.GetCoordinateN(0)); } }
/// <summary> /// /// </summary> /// <param name="graph"></param> private void CheckTooFewPoints(GeometryGraph graph) { if (graph.HasTooFewPoints) { validErr = new TopologyValidationError(TopologyValidationErrors.TooFewPoints, graph.InvalidPoint); return; } }
/// <summary> /// /// </summary> /// <param name="graph"></param> private void CheckConnectedInteriors(GeometryGraph graph) { ConnectedInteriorTester cit = new ConnectedInteriorTester(graph); if (!cit.IsInteriorsConnected()) { validErr = new TopologyValidationError(TopologyValidationErrors.DisconnectedInteriors, cit.Coordinate); } }
/// <summary> /// /// </summary> /// <param name="coords"></param> private void CheckInvalidCoordinates(ICoordinate[] coords) { foreach (ICoordinate c in coords) { if (!IsValidCoordinate(c)) { validErr = new TopologyValidationError(TopologyValidationErrors.InvalidCoordinate, c); return; } } }
/// <summary> /// /// </summary> /// <param name="graph"></param> private void CheckConsistentArea(GeometryGraph graph) { ConsistentAreaTester cat = new ConsistentAreaTester(graph); bool isValidArea = cat.IsNodeConsistentArea; if (!isValidArea) { validErr = new TopologyValidationError(TopologyValidationErrors.SelfIntersection, cat.InvalidPoint); return; } if (cat.HasDuplicateRings) { validErr = new TopologyValidationError(TopologyValidationErrors.DuplicateRings, cat.InvalidPoint); return; } }
/// <summary> /// Tests that no hole is nested inside another hole. /// This routine assumes that the holes are disjoint. /// To ensure this, holes have previously been tested /// to ensure that: /// They do not partially overlap /// (checked by <c>checkRelateConsistency</c>). /// They are not identical /// (checked by <c>checkRelateConsistency</c>). /// </summary> private void CheckHolesNotNested(IPolygon p, GeometryGraph graph) { QuadtreeNestedRingTester nestedTester = new QuadtreeNestedRingTester(graph); foreach (ILinearRing innerHole in p.Holes) { nestedTester.Add(innerHole); } bool isNonNested = nestedTester.IsNonNested(); if (!isNonNested) { validErr = new TopologyValidationError(TopologyValidationErrors.NestedHoles, nestedTester.NestedPoint); } }
/// <summary> /// /// </summary> /// <param name="g"></param> private void CheckValid(IGeometry g) { if (isChecked) { return; } validErr = null; if (g.IsEmpty) { return; } if (g is IPoint) { CheckValid((IPoint)g); } else if (g is IMultiPoint) { CheckValid((IMultiPoint)g); } else if (g is ILinearRing) // LineString also handles LinearRings { CheckValid((ILinearRing)g); } else if (g is ILineString) { CheckValid((ILineString)g); } else if (g is IPolygon) { CheckValid((IPolygon)g); } else if (g is IMultiPolygon) { CheckValid((IMultiPolygon)g); } else if (g is IGeometryCollection) { CheckValid((IGeometryCollection)g); } else { throw new NotSupportedException(g.GetType().FullName); } }
/// <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) { ICoordinate[] shellPts = shell.Coordinates; // test if shell is inside polygon shell ILinearRing polyShell = p.Shell; ICoordinate[] polyPts = polyShell.Coordinates; ICoordinate 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 = CGAlgorithms.IsPointInRing(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. */ ICoordinate 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> /// Check that a ring does not self-intersect, except at its endpoints. /// Algorithm is to count the number of times each node along edge occurs. /// If any occur more than once, that must be a self-intersection. /// </summary> private void CheckNoSelfIntersectingRing(EdgeIntersectionList eiList) { ISet nodeSet = new ListSet(); bool isFirst = true; foreach (EdgeIntersection ei in eiList) { if (isFirst) { isFirst = false; continue; } if (nodeSet.Contains(ei.Coordinate)) { validErr = new TopologyValidationError(TopologyValidationErrors.RingSelfIntersection, ei.Coordinate); return; } else { nodeSet.Add(ei.Coordinate); } } }
/// <summary> /// /// </summary> /// <param name="ring"></param> private void CheckClosedRing(ILinearRing ring) { if (!ring.IsClosed) validErr = new TopologyValidationError(TopologyValidationErrors.RingNotClosed, ring.GetCoordinateN(0)); }
/// <summary> /// Tests 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">A GeometryGraph incorporating the polygon.</param> private void CheckHolesInShell(IPolygon p, GeometryGraph graph) { ILinearRing shell = p.Shell; IPointInRing pir = new MCPointInRing(shell); for (int i = 0; i < p.NumInteriorRings; i++) { ILinearRing hole = p.Holes[i]; ICoordinate holePt = FindPointNotNode(hole.Coordinates, shell, graph); /* * If no non-node hole vertex can be found, the hole must * split the polygon into disconnected interiors. * This will be caught by a subsequent check. */ if (holePt == null) return; bool outside = !pir.IsInside((Coordinate) holePt); if(outside) { validErr = new TopologyValidationError(TopologyValidationErrors.HoleOutsideShell, holePt); return; } } }
/// <summary> /// Tests that no hole is nested inside another hole. /// This routine assumes that the holes are disjoint. /// To ensure this, holes have previously been tested /// to ensure that: /// They do not partially overlap /// (checked by <c>checkRelateConsistency</c>). /// They are not identical /// (checked by <c>checkRelateConsistency</c>). /// </summary> private void CheckHolesNotNested(IPolygon p, GeometryGraph graph) { QuadtreeNestedRingTester nestedTester = new QuadtreeNestedRingTester(graph); foreach (ILinearRing innerHole in p.Holes) nestedTester.Add(innerHole); bool isNonNested = nestedTester.IsNonNested(); if (!isNonNested) validErr = new TopologyValidationError(TopologyValidationErrors.NestedHoles, nestedTester.NestedPoint); }
/// <summary> /// Check that a ring does not self-intersect, except at its endpoints. /// Algorithm is to count the number of times each node along edge occurs. /// If any occur more than once, that must be a self-intersection. /// </summary> private void CheckNoSelfIntersectingRing(EdgeIntersectionList eiList) { ISet nodeSet = new ListSet(); bool isFirst = true; foreach(EdgeIntersection ei in eiList) { if (isFirst) { isFirst = false; continue; } if (nodeSet.Contains(ei.Coordinate)) { validErr = new TopologyValidationError(TopologyValidationErrors.RingSelfIntersection, ei.Coordinate); return; } else nodeSet.Add(ei.Coordinate); } }
/// <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) { ICoordinate[] shellPts = shell.Coordinates; // test if shell is inside polygon shell ILinearRing polyShell = p.Shell; ICoordinate[] polyPts = polyShell.Coordinates; ICoordinate 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 = CGAlgorithms.IsPointInRing(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. */ ICoordinate 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> /// /// </summary> /// <param name="graph"></param> private void CheckConnectedInteriors(GeometryGraph graph) { ConnectedInteriorTester cit = new ConnectedInteriorTester(graph); if (!cit.IsInteriorsConnected()) validErr = new TopologyValidationError(TopologyValidationErrors.DisconnectedInteriors, cit.Coordinate); }
/// <summary> /// /// </summary> /// <param name="g"></param> private void CheckValid(IGeometry g) { if(isChecked) return; validErr = null; if (g.IsEmpty) return; if (g is IPoint) CheckValid((IPoint) g); else if (g is IMultiPoint) CheckValid((IMultiPoint) g); else if (g is ILinearRing) // LineString also handles LinearRings CheckValid((ILinearRing) g); else if (g is ILineString) CheckValid((ILineString) g); else if (g is IPolygon) CheckValid((IPolygon) g); else if (g is IMultiPolygon) CheckValid((IMultiPolygon) g); else if (g is IGeometryCollection) CheckValid((IGeometryCollection) g); else throw new NotSupportedException(g.GetType().FullName); }