/// <summary> /// The left and right topological location arguments assume that the ring is oriented CW. /// If the ring is in the opposite orientation, /// the left and right locations must be interchanged. /// </summary> /// <param name="lr"></param> /// <param name="cwLeft"></param> /// <param name="cwRight"></param> private void AddPolygonRing(IBasicGeometry lr, LocationType cwLeft, LocationType cwRight) { IList <Coordinate> coord = CoordinateArrays.RemoveRepeatedPoints(lr.Coordinates); if (coord.Count < 4) { _hasTooFewPoints = true; _invalidPoint = coord[0]; return; } LocationType left = cwLeft; LocationType right = cwRight; if (CgAlgorithms.IsCounterClockwise(coord)) { left = cwRight; right = cwLeft; } Edge e = new Edge(coord, new Label(_argIndex, LocationType.Boundary, left, right)); _lineEdgeMap.Add(lr, e); InsertEdge(e); // insert the endpoint as a node, to mark that it is on the boundary InsertPoint(_argIndex, coord[0], LocationType.Boundary); }
/// <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 static Coordinate CheckShellInsideHole(LinearRing shell, LinearRing hole, GeometryGraph graph) { IList <Coordinate> shellPts = shell.Coordinates; IList <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 = CgAlgorithms.IsPointInRing(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 = CgAlgorithms.IsPointInRing(holePt, shellPts); return(insideShell ? holePt : null); } throw new ShellHoleIdentityException(); }
/// <summary> /// /// </summary> /// <param name="ring"></param> /// <param name="clockwise"></param> private static void Normalize(ILinearRing ring, bool clockwise) { if (ring.IsEmpty) { return; } Coordinate[] uniqueCoordinates = new Coordinate[ring.Coordinates.Count - 1]; for (int i = 0; i < uniqueCoordinates.Length; i++) { uniqueCoordinates[i] = ring.Coordinates[i]; } Coordinate minCoordinate = CoordinateArrays.MinCoordinate(uniqueCoordinates); CoordinateArrays.Scroll(uniqueCoordinates, minCoordinate); List <Coordinate> result = new List <Coordinate>(); for (int i = 0; i < uniqueCoordinates.Length; i++) { result.Add(ring.Coordinates[i]); } result.Add(uniqueCoordinates[0].Copy()); ring.Coordinates = result; if (CgAlgorithms.IsCounterClockwise(ring.Coordinates) == clockwise) { ring.Coordinates.Reverse(); } }
/// <summary> /// /// </summary> /// <returns></returns> public virtual bool IsNonNested() { for (int i = 0; i < _rings.Count; i++) { LinearRing innerRing = (LinearRing)_rings[i]; IList <Coordinate> innerRingPts = innerRing.Coordinates; for (int j = 0; j < _rings.Count; j++) { LinearRing searchRing = (LinearRing)_rings[j]; IList <Coordinate> searchRingPts = searchRing.Coordinates; if (innerRing == searchRing) { continue; } if (!innerRing.EnvelopeInternal.Intersects(searchRing.EnvelopeInternal)) { continue; } Coordinate 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 = CgAlgorithms.IsPointInRing(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, OR /// null if no containing EdgeRing is found.</returns> private static EdgeRing FindEdgeRingContaining(EdgeRing testEr, IEnumerable shellList) { ILinearRing teString = testEr.LinearRing; IEnvelope testEnv = teString.EnvelopeInternal; Coordinate testPt = teString.Coordinates[0]; EdgeRing minShell = null; IEnvelope minEnv = null; for (IEnumerator it = shellList.GetEnumerator(); it.MoveNext();) { EdgeRing tryShell = (EdgeRing)it.Current; ILinearRing tryRing = tryShell.LinearRing; IEnvelope tryEnv = tryRing.EnvelopeInternal; if (minShell != null) { minEnv = minShell.LinearRing.EnvelopeInternal; } bool isContained = false; if (tryEnv.Contains(testEnv) && CgAlgorithms.IsPointInRing(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> /// /// </summary> /// <param name="line"></param> /// <param name="pt"></param> /// <param name="locGeom"></param> private void ComputeMinDistance(ILineString line, Point pt, GeometryLocation[] locGeom) { if (line.EnvelopeInternal.Distance(pt.EnvelopeInternal) > _minDistance) { return; } IList <Coordinate> coord0 = line.Coordinates; Coordinate coord = pt.Coordinate; // brute force approach! for (int i = 0; i < coord0.Count - 1; i++) { double dist = CgAlgorithms.DistancePointLine(coord, coord0[i], coord0[i + 1]); if (dist < _minDistance) { _minDistance = dist; LineSegment seg = new LineSegment(coord0[i], coord0[i + 1]); Coordinate segClosestPoint = new Coordinate(seg.ClosestPoint(coord)); locGeom[0] = new GeometryLocation(line, i, segClosestPoint); locGeom[1] = new GeometryLocation(pt, 0, coord); } if (_minDistance <= _terminateDistance) { return; } } }
/// <summary> /// /// </summary> private void FindRightmostEdgeAtVertex() { /* * The rightmost point is an interior vertex, so it has a segment on either side of it. * If these segments are both above or below the rightmost point, we need to * determine their relative orientation to decide which is rightmost. */ IList <Coordinate> pts = _minDe.Edge.Coordinates; Assert.IsTrue(_minIndex > 0 && _minIndex < pts.Count, "rightmost point expected to be interior vertex of edge"); Coordinate pPrev = pts[_minIndex - 1]; Coordinate pNext = pts[_minIndex + 1]; int orientation = CgAlgorithms.ComputeOrientation(_minCoord, pNext, pPrev); bool usePrev = false; // both segments are below min point if (pPrev.Y < _minCoord.Y && pNext.Y < _minCoord.Y && orientation == CgAlgorithms.COUNTER_CLOCKWISE) { usePrev = true; } else if (pPrev.Y > _minCoord.Y && pNext.Y > _minCoord.Y && orientation == CgAlgorithms.CLOCKWISE) { usePrev = true; } // if both segments are on the same side, do nothing - either is safe // to select as a rightmost segment if (usePrev) { _minIndex = _minIndex - 1; } }
/// <summary> /// /// </summary> /// <param name="line0"></param> /// <param name="line1"></param> /// <param name="locGeom"></param> private void ComputeMinDistance(ILineString line0, ILineString line1, GeometryLocation[] locGeom) { if (line0.EnvelopeInternal.Distance(line1.EnvelopeInternal) > _minDistance) { return; } IList <Coordinate> coord0 = line0.Coordinates; IList <Coordinate> coord1 = line1.Coordinates; // brute force approach! for (int i = 0; i < coord0.Count - 1; i++) { for (int j = 0; j < coord1.Count - 1; j++) { double dist = CgAlgorithms.DistanceLineLine( coord0[i], coord0[i + 1], coord1[j], coord1[j + 1]); if (dist < _minDistance) { _minDistance = dist; LineSegment seg0 = new LineSegment(coord0[i], coord0[i + 1]); LineSegment seg1 = new LineSegment(coord1[j], coord1[j + 1]); Coordinate[] closestPt = seg0.ClosestPoints(seg1); locGeom[0] = new GeometryLocation(line0, i, new Coordinate(closestPt[0])); locGeom[1] = new GeometryLocation(line1, j, new Coordinate(closestPt[1])); } if (_minDistance <= _terminateDistance) { return; } } } }
private double GetArea(List <Coordinate> tempPolygon) { double area = Math.Abs(CgAlgorithms.SignedArea(tempPolygon)); if (_previousParts == null || _previousParts.Count == 0) { _firstPartIsCounterClockwise = CgAlgorithms.IsCounterClockwise(tempPolygon); } else { if (CgAlgorithms.IsCounterClockwise(tempPolygon) != _firstPartIsCounterClockwise) { area = -area; } } if (Map.Projection != null) { if (Map.Projection.IsLatLon) { // this code really assumes the location is near the equator const double RadiusOfEarth = 111319.5; area *= RadiusOfEarth * RadiusOfEarth; } else { area *= Map.Projection.Unit.Meters * Map.Projection.Unit.Meters; } } return(area); }
/// <summary> /// Tests whether a triangular ring would be eroded completely by the given /// buffer distance. /// This is a precise test. It uses the fact that the inner buffer of a /// triangle converges on the inCentre of the triangle (the point /// equidistant from all sides). If the buffer distance is greater than the /// distance of the inCentre from a side, the triangle will be eroded completely. /// This test is important, since it removes a problematic case where /// the buffer distance is slightly larger than the inCentre distance. /// In this case the triangle buffer curve "inverts" with incorrect topology, /// producing an incorrect hole in the buffer. /// </summary> /// <param name="triangleCoord"></param> /// <param name="bufferDistance"></param> /// <returns></returns> private bool IsTriangleErodedCompletely(IList <Coordinate> triangleCoord, double bufferDistance) { Triangle tri = new Triangle(triangleCoord[0], triangleCoord[1], triangleCoord[2]); Coordinate inCentre = tri.InCentre; double distToCentre = CgAlgorithms.DistancePointLine(inCentre, tri.P0, tri.P1); return(distToCentre < Math.Abs(bufferDistance)); }
/// <summary> /// The coordinate pairs match if they define line segments lying in the same direction. /// E.g. the segments are parallel and in the same quadrant /// (as opposed to parallel and opposite!). /// </summary> /// <param name="p0"></param> /// <param name="p1"></param> /// <param name="ep0"></param> /// <param name="ep1"></param> private static bool MatchInSameDirection(Coordinate p0, Coordinate p1, Coordinate ep0, Coordinate ep1) { if (!p0.Equals(ep0)) { return(false); } return(CgAlgorithms.ComputeOrientation(p0, p1, ep1) == CgAlgorithms.COLLINEAR && QuadrantOp.Quadrant(p0, p1) == QuadrantOp.Quadrant(ep0, ep1)); }
/// <summary> /// Finds all non-horizontal segments intersecting the stabbing line /// in the input dirEdge. /// The stabbing line is the ray to the right of stabbingRayLeftPt. /// </summary> /// <param name="stabbingRayLeftPt">The left-hand origin of the stabbing line.</param> /// <param name="dirEdge"></param> /// <param name="stabbedSegments">The current list of DepthSegments intersecting the stabbing line.</param> private void FindStabbedSegments(Coordinate stabbingRayLeftPt, DirectedEdge dirEdge, IList stabbedSegments) { IList <Coordinate> pts = dirEdge.Edge.Coordinates; for (int i = 0; i < pts.Count - 1; i++) { _seg.P0 = pts[i]; _seg.P1 = pts[i + 1]; // ensure segment always points upwards if (_seg.P0.Y > _seg.P1.Y) { _seg.Reverse(); } // skip segment if it is left of the stabbing line double maxx = Math.Max(_seg.P0.X, _seg.P1.X); if (maxx < stabbingRayLeftPt.X) { continue; } // skip horizontal segments (there will be a non-horizontal one carrying the same depth info if (_seg.IsHorizontal) { continue; } // skip if segment is above or below stabbing line if (stabbingRayLeftPt.Y < _seg.P0.Y || stabbingRayLeftPt.Y > _seg.P1.Y) { continue; } // skip if stabbing ray is right of the segment if (CgAlgorithms.ComputeOrientation(_seg.P0, _seg.P1, stabbingRayLeftPt) == CgAlgorithms.RIGHT) { continue; } // stabbing line cuts this segment, so record it int depth = dirEdge.GetDepth(PositionType.Left); // if segment direction was flipped, use RHS depth instead if (!_seg.P0.Equals(pts[i])) { depth = dirEdge.GetDepth(PositionType.Right); } DepthSegment ds = new DepthSegment(_seg, depth); stabbedSegments.Add(ds); } }
/// <summary> /// Compute a LinearRing from the point list previously collected. /// Test if the ring is a hole (i.e. if it is CCW) and set the hole flag /// accordingly. /// </summary> public void ComputeRing() { if (_ring != null) { return; // don't compute more than once } Coordinate[] coord = new Coordinate[_pts.Count]; for (int i = 0; i < _pts.Count; i++) { coord[i] = (Coordinate)_pts[i]; } _ring = InnerGeometryFactory.CreateLinearRing(coord); _isHole = CgAlgorithms.IsCounterClockwise(_ring.Coordinates); }
private static bool IsAllCW(IPolygon poly) { IList <Coordinate> shell = poly.Shell.Coordinates; for (int i = 0, c = shell.Count - 3; i < c; i++) { if (CgAlgorithms.ComputeOrientation(shell[i], shell[i + 1], shell[i + 2]) == 1) { return(false); } } return(true); }
/// <summary> /// Add an offset curve for a ring. /// The side and left and right topological location arguments /// assume that the ring is oriented CW. /// If the ring is in the opposite orientation, /// the left and right locations must be interchanged and the side flipped. /// </summary> /// <param name="coord">The coordinates of the ring (must not contain repeated points).</param> /// <param name="offsetDistance">The distance at which to create the buffer.</param> /// <param name="side">The side of the ring on which to construct the buffer line.</param> /// <param name="cwLeftLoc">The location on the L side of the ring (if it is CW).</param> /// <param name="cwRightLoc">The location on the R side of the ring (if it is CW).</param> private void AddPolygonRing(IList <Coordinate> coord, double offsetDistance, PositionType side, LocationType cwLeftLoc, LocationType cwRightLoc) { LocationType leftLoc = cwLeftLoc; LocationType rightLoc = cwRightLoc; if (CgAlgorithms.IsCounterClockwise(coord)) { leftLoc = cwRightLoc; rightLoc = cwLeftLoc; side = Position.Opposite(side); } IList lineList = _curveBuilder.GetRingCurve(coord, side, offsetDistance); AddCurves(lineList, leftLoc, rightLoc); }
/// <summary> /// Returns 1 if this DirectedEdge has a greater angle with the /// positive x-axis than b", 0 if the DirectedEdges are collinear, and -1 otherwise. /// Using the obvious algorithm of simply computing the angle is not robust, /// since the angle calculation is susceptible to roundoff. A robust algorithm /// is: /// first compare the quadrants. If the quadrants are different, it it /// trivial to determine which vector is "greater". /// if the vectors lie in the same quadrant, the robust /// <c>RobustCgAlgorithms.ComputeOrientation(Coordinate, Coordinate, Coordinate)</c> /// function can be used to decide the relative orientation of the vectors. /// </summary> /// <param name="e"></param> /// <returns></returns> public virtual int CompareDirection(DirectedEdge e) { // if the rays are in different quadrants, determining the ordering is trivial if (_quadrant > e.Quadrant) { return(1); } if (_quadrant < e.Quadrant) { return(-1); } // vectors are in the same quadrant - check relative orientation of direction vectors // this is > e if it is CCW of e int i = CgAlgorithms.ComputeOrientation(e.StartPoint, e.EndPoint, _p1); return(i); }
/// <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(LinearRing shell, Polygon p, GeometryGraph graph) { IList <Coordinate> shellPts = shell.Coordinates; // test if shell is inside polygon shell LinearRing polyShell = (LinearRing)p.ExteriorRing; IList <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 = CgAlgorithms.IsPointInRing(shellPt, polyPts); if (!insidePolyShell) { return; } // if no holes, this is an error! if (p.NumHoles <= 0) { _validErr = new TopologyValidationError(TopologyValidationErrorType.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.NumHoles; i++) { LinearRing hole = (LinearRing)p.GetInteriorRingN(i); badNestedPt = CheckShellInsideHole(shell, hole, graph); if (badNestedPt == null) { return; } } _validErr = new TopologyValidationError(TopologyValidationErrorType.NestedShells, badNestedPt); }
/// <summary> /// Determines the orientation of a LineSegment relative to this segment. /// The concept of orientation is specified as follows: /// Given two line segments A and L, /// A is to the left of a segment L if A lies wholly in the /// closed half-plane lying to the left of L /// A is to the right of a segment L if A lies wholly in the /// closed half-plane lying to the right of L /// otherwise, A has indeterminate orientation relative to L. This /// happens if A is collinear with L or if A crosses the line determined by L. /// </summary> /// <param name="seg">The <c>LineSegment</c> to compare.</param> /// <returns> /// 1 if <c>seg</c> is to the left of this segment, /// -1 if <c>seg</c> is to the right of this segment, /// 0 if <c>seg</c> has indeterminate orientation relative to this segment. /// </returns> public virtual int OrientationIndex(ILineSegmentBase seg) { int orient0 = CgAlgorithms.OrientationIndex(P0, P1, seg.P0); int orient1 = CgAlgorithms.OrientationIndex(P0, P1, seg.P1); // this handles the case where the points are Curve or collinear if (orient0 >= 0 && orient1 >= 0) { return(Math.Max(orient0, orient1)); } // this handles the case where the points are R or collinear if (orient0 <= 0 && orient1 <= 0) { return(Math.Max(orient0, orient1)); } // points lie on opposite sides ==> indeterminate orientation return(0); }
/// <summary> /// Implements the total order relation: /// a has a greater angle with the positive x-axis than b. /// Using the obvious algorithm of simply computing the angle is not robust, /// since the angle calculation is obviously susceptible to roundoff. /// A robust algorithm is: /// - first compare the quadrant. If the quadrants /// are different, it it trivial to determine which vector is "greater". /// - if the vectors lie in the same quadrant, the computeOrientation function /// can be used to decide the relative orientation of the vectors. /// </summary> /// <param name="e"></param> public virtual int CompareDirection(EdgeEnd e) { if (_dx == e._dx && _dy == e._dy) { return(0); } // if the rays are in different quadrants, determining the ordering is trivial if (_quadrant > e._quadrant) { return(1); } if (_quadrant < e._quadrant) { return(-1); } // vectors are in the same quadrant - check relative orientation of direction vectors // this is > e if it is CCW of e return(CgAlgorithms.ComputeOrientation(e._p0, e._p1, _p1)); }
/// <summary> /// /// </summary> /// <param name="innerRing"></param> /// <param name="searchRing"></param> /// <returns></returns> private bool IsInside(ILinearRing innerRing, ILinearRing searchRing) { IList <Coordinate> innerRingPts = innerRing.Coordinates; IList <Coordinate> searchRingPts = searchRing.Coordinates; if (!innerRing.EnvelopeInternal.Intersects(searchRing.EnvelopeInternal)) { return(false); } Coordinate 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 = CgAlgorithms.IsPointInRing(innerRingPt, searchRingPts); if (isInside) { _nestedPt = new Coordinate(innerRingPt); return(true); } return(false); }
/// <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">The EdgeRing to test.</param> /// <param name="shellList">The list of shells to test.</param> /// <returns>Containing EdgeRing, if there is one, OR /// null if no containing EdgeRing is found.</returns> public static EdgeRing FindEdgeRingContaining(EdgeRing testEr, IList shellList) { ILinearRing teString = testEr.Ring; IEnvelope testEnv = teString.EnvelopeInternal; EdgeRing minShell = null; IEnvelope minEnv = null; for (IEnumerator it = shellList.GetEnumerator(); it.MoveNext();) { EdgeRing tryShell = (EdgeRing)it.Current; ILinearRing tryRing = tryShell.Ring; IEnvelope tryEnv = tryRing.EnvelopeInternal; if (minShell != null) { minEnv = minShell.Ring.EnvelopeInternal; } bool isContained = false; // the hole envelope cannot equal the shell envelope if (tryEnv.Equals(testEnv)) { continue; } Coordinate testPt = PtNotInList(teString.Coordinates, tryRing.Coordinates); if (tryEnv.Contains(testEnv) && CgAlgorithms.IsPointInRing(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 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 virtual bool ContainsPoint(Coordinate p) { ILinearRing shell = LinearRing; IEnvelope env = shell.EnvelopeInternal; if (!env.Contains(p)) { return(false); } if (!CgAlgorithms.IsPointInRing(p, shell.Coordinates)) { return(false); } for (IEnumerator i = _holes.GetEnumerator(); i.MoveNext();) { EdgeRing hole = (EdgeRing)i.Current; if (hole.ContainsPoint(p)) { return(false); } } return(true); }
/// <inheritdoc/> protected override void AppendBasicGeometry(ShapefileHeader header, IBasicGeometry feature, int numFeatures) { FileInfo fi = new FileInfo(Filename); int offset = Convert.ToInt32(fi.Length / 2); FileStream shpStream = new FileStream(Filename, FileMode.Append, FileAccess.Write, FileShare.None, 10000); FileStream shxStream = new FileStream(header.ShxFilename, FileMode.Append, FileAccess.Write, FileShare.None, 100); List <int> parts = new List <int>(); List <Coordinate> points = new List <Coordinate>(); int contentLength = 22; for (int iPart = 0; iPart < feature.NumGeometries; iPart++) { parts.Add(points.Count); IBasicPolygon pg = feature.GetBasicGeometryN(iPart) as IBasicPolygon; if (pg == null) { continue; } var bl = pg.Shell; var coords = bl.Coordinates; if (CgAlgorithms.IsCounterClockwise(coords)) { // Exterior rings need to be clockwise coords.Reverse(); } points.AddRange(coords); foreach (IBasicLineString hole in pg.Holes) { parts.Add(points.Count); var holeCoords = hole.Coordinates; if (CgAlgorithms.IsCounterClockwise(holeCoords) == false) { // Interior rings need to be counter-clockwise holeCoords.Reverse(); } points.AddRange(holeCoords); } } contentLength += 2 * parts.Count; if (header.ShapeType == ShapeType.Polygon) { contentLength += points.Count * 8; } if (header.ShapeType == ShapeType.PolygonM) { contentLength += 8; // mmin mmax contentLength += points.Count * 12; // x, y, m } if (header.ShapeType == ShapeType.PolygonZ) { contentLength += 16; // mmin, mmax, zmin, zmax contentLength += points.Count * 16; // x, y, m, z } // Index File // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shxStream.WriteBe(offset); // Byte 0 Offset Integer 1 Big shxStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shxStream.Flush(); shxStream.Close(); // X Y Poly Lines // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shpStream.WriteBe(numFeatures); // Byte 0 Record Number Integer 1 Big shpStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shpStream.WriteLe((int)header.ShapeType); // Byte 8 Shape Type 3 Integer 1 Little if (header.ShapeType == ShapeType.NullShape) { return; } shpStream.WriteLe(feature.Envelope.Minimum.X); // Byte 12 Xmin Double 1 Little shpStream.WriteLe(feature.Envelope.Minimum.Y); // Byte 20 Ymin Double 1 Little shpStream.WriteLe(feature.Envelope.Maximum.X); // Byte 28 Xmax Double 1 Little shpStream.WriteLe(feature.Envelope.Maximum.Y); // Byte 36 Ymax Double 1 Little shpStream.WriteLe(parts.Count); // Byte 44 NumParts Integer 1 Little shpStream.WriteLe(points.Count); // Byte 48 NumPoints Integer 1 Little // Byte 52 Parts Integer NumParts Little foreach (int iPart in parts) { shpStream.WriteLe(iPart); } double[] xyVals = new double[points.Count * 2]; int i = 0; foreach (Coordinate coord in points) { xyVals[i * 2] = coord.X; xyVals[i * 2 + 1] = coord.Y; i++; } shpStream.WriteLe(xyVals, 0, 2 * points.Count); if (header.ShapeType == ShapeType.PolygonZ) { shpStream.WriteLe(feature.Envelope.Minimum.Z); shpStream.WriteLe(feature.Envelope.Maximum.Z); double[] zVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; ipoint++) { zVals[ipoint] = points[ipoint].Z; } shpStream.WriteLe(zVals, 0, points.Count); } if (header.ShapeType == ShapeType.PolygonM || header.ShapeType == ShapeType.PolygonZ) { if (feature.Envelope == null) { shpStream.WriteLe(0.0); shpStream.WriteLe(0.0); } else { shpStream.WriteLe(feature.Envelope.Minimum.M); shpStream.WriteLe(feature.Envelope.Maximum.M); } double[] mVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; i++) { mVals[ipoint] = points[ipoint].M; ipoint++; } shpStream.WriteLe(mVals, 0, points.Count); } shpStream.Flush(); shpStream.Close(); offset += contentLength; Shapefile.WriteFileLength(Filename, offset); Shapefile.WriteFileLength(header.ShxFilename, 50 + numFeatures * 4); }
private void ReadPolygonShape(Shape shape) { _envelope = shape.Range.Extent.ToEnvelope(); List <ILinearRing> shells = new List <ILinearRing>(); List <ILinearRing> holes = new List <ILinearRing>(); foreach (PartRange part in shape.Range.Parts) { List <Coordinate> coords = new List <Coordinate>(); int i = part.StartIndex; foreach (Vertex d in part) { Coordinate c = new Coordinate(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 LinearRing(coords); if (shape.Range.Parts.Count == 1) { shells.Add(ring); } else { if (CgAlgorithms.IsCounterClockwise(ring.Coordinates)) { holes.Add(ring); } else { shells.Add(ring); } //if (part.IsHole()) //{ // holes.Add(ring); //} //else //{ // shells.Add(ring); //} } } if (shells.Count == 0 && holes.Count > 0) { shells = holes; holes = new List <ILinearRing>(); } //// Now we have a list of all shells and all holes List <ILinearRing>[] holesForShells = new List <ILinearRing> [shells.Count]; for (int i = 0; i < shells.Count; i++) { holesForShells[i] = new List <ILinearRing>(); } // Find holes for (int i = 0; i < holes.Count; i++) { ILinearRing testRing = holes[i]; ILinearRing minShell = null; IEnvelope minEnv = null; IEnvelope testEnv = testRing.EnvelopeInternal; Coordinate testPt = testRing.Coordinates[0]; ILinearRing tryRing; for (int j = 0; j < shells.Count; j++) { tryRing = shells[j]; IEnvelope tryEnv = tryRing.EnvelopeInternal; if (minShell != null) { minEnv = minShell.EnvelopeInternal; } bool isContained = false; if (tryEnv.Contains(testEnv) && (CgAlgorithms.IsPointInRing(testPt, tryRing.Coordinates) || (PointInList(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 = tryRing; } holesForShells[j].Add(holes[i]); } } } 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) { _basicGeometry = polygons[0]; } else { // It's a multi part _basicGeometry = new MultiPolygon(polygons); } }
/// <summary> /// Computes the perpendicular distance between the (infinite) line defined /// by this line segment and a point. /// </summary> /// <param name="p"></param> /// <returns></returns> public virtual double DistancePerpendicular(Coordinate p) { return(CgAlgorithms.DistancePointLinePerpendicular(new Coordinate(p), P0, P1)); }
/// <summary> /// Computes the distance between this line segment and another one. /// </summary> /// <param name="ls"></param> /// <returns></returns> public virtual double Distance(ILineSegmentBase ls) { return(CgAlgorithms.DistanceLineLine(P0, P1, new Coordinate(ls.P0), new Coordinate(ls.P1))); }
/// <summary> /// Saves the file to a new location /// </summary> /// <param name="fileName">The fileName to save</param> /// <param name="overwrite">Boolean that specifies whether or not to overwrite the existing file</param> public override void SaveAs(string fileName, bool overwrite) { if (IndexMode) { SaveAsIndexed(fileName, overwrite); return; } Filename = fileName; string dir = Path.GetDirectoryName(Path.GetFullPath(fileName)); if (dir != null && !Directory.Exists(dir)) { Directory.CreateDirectory(dir); } if (File.Exists(fileName)) { if (fileName != Filename && overwrite == false) { throw new IOException("File exists."); } File.Delete(fileName); string shx = Path.ChangeExtension(fileName, ".shx"); if (File.Exists(shx)) { File.Delete(shx); } } InvalidateEnvelope(); if (CoordinateType == CoordinateType.Regular) { Header.ShapeType = ShapeType.Polygon; } if (CoordinateType == CoordinateType.M) { Header.ShapeType = ShapeType.PolygonM; } if (CoordinateType == CoordinateType.Z) { Header.ShapeType = ShapeType.PolygonZ; } // Set ShapeType before setting extent. Header.SetExtent(Extent); Header.ShxLength = Features.Count * 4 + 50; Header.SaveAs(fileName); BufferedBinaryWriter bbWriter = new BufferedBinaryWriter(fileName); BufferedBinaryWriter indexWriter = new BufferedBinaryWriter(Header.ShxFilename); int fid = 0; int offset = 50; // the shapefile header starts at 100 bytes, so the initial offset is 50 words int contentLength = 0; foreach (IFeature f in Features) { List <int> parts = new List <int>(); offset += contentLength; // adding the previous content length from each loop calculates the word offset List <Coordinate> points = new List <Coordinate>(); contentLength = 22; for (int iPart = 0; iPart < f.NumGeometries; iPart++) { parts.Add(points.Count); IBasicPolygon pg = f.GetBasicGeometryN(iPart) as IBasicPolygon; if (pg == null) { continue; } IBasicLineString bl = pg.Shell; IList <Coordinate> coords = bl.Coordinates; if (CgAlgorithms.IsCounterClockwise(coords)) { // Exterior rings need to be clockwise coords.Reverse(); } foreach (Coordinate coord in coords) { points.Add(coord); } foreach (IBasicLineString hole in pg.Holes) { parts.Add(points.Count); IList <Coordinate> holeCoords = hole.Coordinates; if (CgAlgorithms.IsCounterClockwise(holeCoords) == false) { // Interior rings need to be counter-clockwise holeCoords.Reverse(); } foreach (Coordinate coord in holeCoords) { points.Add(coord); } } } contentLength += 2 * parts.Count; if (Header.ShapeType == ShapeType.Polygon) { contentLength += points.Count * 8; } if (Header.ShapeType == ShapeType.PolygonM) { contentLength += 8; // mmin mmax contentLength += points.Count * 12; // x, y, m } if (Header.ShapeType == ShapeType.PolygonZ) { contentLength += 16; // mmin, mmax, zmin, zmax contentLength += points.Count * 16; // x, y, m, z } // Index File // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- indexWriter.Write(offset, false); // Byte 0 Offset Integer 1 Big indexWriter.Write(contentLength, false); // Byte 4 Content Length Integer 1 Big // X Y Poly Lines // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- bbWriter.Write(fid + 1, false); // Byte 0 Record Number Integer 1 Big bbWriter.Write(contentLength, false); // Byte 4 Content Length Integer 1 Big bbWriter.Write((int)Header.ShapeType); // Byte 8 Shape Type 3 Integer 1 Little if (Header.ShapeType == ShapeType.NullShape) { continue; } bbWriter.Write(f.Envelope.Minimum.X); // Byte 12 Xmin Double 1 Little bbWriter.Write(f.Envelope.Minimum.Y); // Byte 20 Ymin Double 1 Little bbWriter.Write(f.Envelope.Maximum.X); // Byte 28 Xmax Double 1 Little bbWriter.Write(f.Envelope.Maximum.Y); // Byte 36 Ymax Double 1 Little bbWriter.Write(parts.Count); // Byte 44 NumParts Integer 1 Little bbWriter.Write(points.Count); // Byte 48 NumPoints Integer 1 Little // Byte 52 Parts Integer NumParts Little foreach (int iPart in parts) { bbWriter.Write(iPart); } double[] xyVals = new double[points.Count * 2]; int i = 0; // Byte X Points Point NumPoints Little foreach (Coordinate coord in points) { xyVals[i * 2] = coord.X; xyVals[i * 2 + 1] = coord.Y; i++; } bbWriter.Write(xyVals); if (Header.ShapeType == ShapeType.PolygonZ) { bbWriter.Write(f.Envelope.Minimum.Z); bbWriter.Write(f.Envelope.Maximum.Z); double[] zVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; i++) { zVals[ipoint] = points[ipoint].Z; ipoint++; } bbWriter.Write(zVals); } if (Header.ShapeType == ShapeType.PolygonM || Header.ShapeType == ShapeType.PolygonZ) { if (f.Envelope == null) { bbWriter.Write(0.0); bbWriter.Write(0.0); } else { bbWriter.Write(f.Envelope.Minimum.M); bbWriter.Write(f.Envelope.Maximum.M); } double[] mVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; i++) { mVals[ipoint] = points[ipoint].M; ipoint++; } bbWriter.Write(mVals); } fid++; offset += 4; // header bytes } bbWriter.Close(); indexWriter.Close(); offset += contentLength; //offset += 4; WriteFileLength(fileName, offset); WriteFileLength(Header.ShxFilename, 50 + fid * 4); UpdateAttributes(); SaveProjection(); }
/// <summary> /// Creates a Polygon or MultiPolygon from this Polygon shape. /// </summary> /// <param name="factory">The IGeometryFactory to use to create the new IGeometry.</param> /// <returns>The IPolygon or IMultiPolygon created from this shape.</returns> protected IGeometry FromPolygon(IGeometryFactory factory) { if (factory == null) { factory = Geometry.DefaultFactory; } List <ILinearRing> shells = new List <ILinearRing>(); List <ILinearRing> holes = new List <ILinearRing>(); foreach (PartRange part in _shapeRange.Parts) { List <Coordinate> coords = new List <Coordinate>(); int i = part.StartIndex; foreach (Vertex d in part) { Coordinate c = new Coordinate(d.X, d.Y); if (M != null && M.Length > 0) { c.M = M[i]; } if (Z != null && Z.Length > 0) { c.Z = Z[i]; } i++; coords.Add(c); } ILinearRing ring = factory.CreateLinearRing(coords); if (_shapeRange.Parts.Count == 1) { shells.Add(ring); } else { if (CgAlgorithms.IsCounterClockwise(ring.Coordinates)) { holes.Add(ring); } else { shells.Add(ring); } } } //// Now we have a list of all shells and all holes List <ILinearRing>[] holesForShells = new List <ILinearRing> [shells.Count]; for (int i = 0; i < shells.Count; i++) { holesForShells[i] = new List <ILinearRing>(); } // Find holes foreach (ILinearRing t in holes) { ILinearRing testRing = t; ILinearRing minShell = null; IEnvelope minEnv = null; IEnvelope testEnv = testRing.EnvelopeInternal; Coordinate testPt = testRing.Coordinates[0]; ILinearRing tryRing; for (int j = 0; j < shells.Count; j++) { tryRing = shells[j]; IEnvelope tryEnv = tryRing.EnvelopeInternal; if (minShell != null) { minEnv = minShell.EnvelopeInternal; } bool isContained = false; if (tryEnv.Contains(testEnv) && (CgAlgorithms.IsPointInRing(testPt, tryRing.Coordinates) || (PointInList(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 = tryRing; } holesForShells[j].Add(t); } } } IPolygon[] 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)); }
/// <summary> /// /// </summary> /// <param name="p"></param> /// <param name="addStartPoint"></param> private void AddNextSegment(Coordinate p, bool addStartPoint) { // s0-s1-s2 are the coordinates of the previous segment and the current one _s0 = _s1; _s1 = _s2; _s2 = p; _seg0.SetCoordinates(_s0, _s1); ComputeOffsetSegment(_seg0, _side, _distance, _offset0); _seg1.SetCoordinates(_s1, _s2); ComputeOffsetSegment(_seg1, _side, _distance, _offset1); // do nothing if points are equal if (_s1.Equals(_s2)) { return; } int orientation = CgAlgorithms.ComputeOrientation(_s0, _s1, _s2); bool outsideTurn = (orientation == CgAlgorithms.CLOCKWISE && _side == PositionType.Left) || (orientation == CgAlgorithms.COUNTER_CLOCKWISE && _side == PositionType.Right); if (orientation == 0) { // lines are collinear _li.ComputeIntersection(_s0, _s1, _s1, _s2); int numInt = _li.IntersectionNum; /* * if numInt is < 2, the lines are parallel and in the same direction. * In this case the point can be ignored, since the offset lines will also be * parallel. */ if (numInt >= 2) { /* * segments are collinear but reversing. Have to add an "end-cap" fillet * all the way around to other direction * This case should ONLY happen for LineStrings, so the orientation is always CW. * (Polygons can never have two consecutive segments which are parallel but reversed, * because that would be a self intersection. */ AddFillet(_s1, _offset0.P1, _offset1.P0, CgAlgorithms.CLOCKWISE, _distance); } } else if (outsideTurn) { // add a fillet to connect the endpoints of the offset segments if (addStartPoint) { AddPt(_offset0.P1); } AddFillet(_s1, _offset0.P1, _offset1.P0, orientation, _distance); AddPt(_offset1.P0); } else { // inside turn /* * add intersection point of offset segments (if any) */ _li.ComputeIntersection(_offset0.P0, _offset0.P1, _offset1.P0, _offset1.P1); if (_li.HasIntersection) { AddPt(_li.GetIntersection(0)); } else { /* * If no intersection, it means the angle is so small and/or the offset so large * that the offsets segments don't intersect. * In this case we must add a offset joining curve to make sure the buffer line * is continuous and tracks the buffer correctly around the corner. * Notice that the joining curve won't appear in the final buffer. * * The intersection test above is vulnerable to robustness errors; * i.e. it may be that the offsets should intersect very close to their * endpoints, but don't due to rounding. To handle this situation * appropriately, we use the following test: * If the offset points are very close, don't add a joining curve * but simply use one of the offset points */ if (new Coordinate(_offset0.P1).Distance(_offset1.P0) < _distance / 1000.0) { AddPt(_offset0.P1); } else { // add endpoint of this segment offset AddPt(_offset0.P1); // <FIX> MD - add in centre point of corner, to make sure offset closer lines have correct topology AddPt(_s1); AddPt(_offset1.P0); } } } }
private IGeometry ReadPolygon() { ShapefileGeometryType type = (ShapefileGeometryType)_reader.ReadInt32(); if (type == ShapefileGeometryType.NullShape) { return(_gf.CreatePolygon(null, null)); } if (type != ShapefileGeometryType.Polygon && type != ShapefileGeometryType.PolygonZ) { throw new Exception("Attempting to load a non-polygon as polygon."); } // Read the box double xMin = _reader.ReadDouble(), yMin = _reader.ReadDouble(); ShapeEnvelope.Init(xMin, _reader.ReadDouble(), yMin, _reader.ReadDouble()); // Read poly header int numParts = _reader.ReadInt32(); int numPoints = _reader.ReadInt32(); // Read parts array int[] partOffsets = new int[numParts]; for (int i = 0; i < numParts; i++) { partOffsets[i] = _reader.ReadInt32(); } // Read the parts and their points List <ILinearRing> shells = new List <ILinearRing>(); List <ILinearRing> holes = new List <ILinearRing>(); Coordinate[] allCoords = new Coordinate[numPoints]; for (int part = 0, last = numParts - 1, x = 0; part < numParts; part++) { int start = partOffsets[part], stop = (part == last ? numPoints : partOffsets[part + 1]); Coordinate[] coords = new Coordinate[stop - start]; for (int i = 0; i < coords.Length; i++) { coords[i] = allCoords[x++] = new Coordinate( _reader.ReadDouble(), _reader.ReadDouble()); } ILinearRing ring = _gf.CreateLinearRing(coords); // Check for hole. if (CgAlgorithms.IsCounterClockwise(coords)) { holes.Add(ring); } else { shells.Add(ring); } } if (type == ShapefileGeometryType.PolygonZ) { // z min/max _reader.ReadDouble(); _reader.ReadDouble(); for (int i = 0; i < allCoords.Length; i++) { allCoords[i].Z = _reader.ReadDouble(); } // m min/max _reader.ReadDouble(); _reader.ReadDouble(); for (int i = 0; i < allCoords.Length; i++) { allCoords[i].M = _reader.ReadDouble(); } } // Create the polygon if (shells.Count == 1) { return(_gf.CreatePolygon(shells[0], holes.ToArray())); } else // Create a multipolygon { List <IPolygon> polys = new List <IPolygon>(shells.Count); foreach (ILinearRing shell in shells) { IEnvelope shellEnv = shell.EnvelopeInternal; List <ILinearRing> shellHoles = new List <ILinearRing>(); for (int i = holes.Count - 1; i >= 0; i--) { ILinearRing hole = holes[i]; if (shellEnv.Contains(hole.EnvelopeInternal)) { shellHoles.Add(hole); holes.RemoveAt(i); } } polys.Add(_gf.CreatePolygon(shell, shellHoles.ToArray())); } // Add the holes that weren't contains as shells foreach (ILinearRing hole in holes) { LinearRing ring = new LinearRing(hole.Reverse()); polys.Add(_gf.CreatePolygon(ring, null)); } return(_gf.CreateMultiPolygon(polys.ToArray())); } }