예제 #1
0
        /// <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);
        }
예제 #2
0
        /// <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();
        }
예제 #3
0
        /// <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();
            }
        }
예제 #4
0
        /// <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);
        }
예제 #5
0
        /// <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);
        }
예제 #6
0
        /// <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;
                }
            }
        }
예제 #7
0
        /// <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;
            }
        }
예제 #8
0
        /// <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;
                    }
                }
            }
        }
예제 #9
0
        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);
        }
예제 #10
0
        /// <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));
        }
예제 #11
0
 /// <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));
 }
예제 #12
0
        /// <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);
            }
        }
예제 #13
0
 /// <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);
 }
예제 #14
0
        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);
        }
예제 #15
0
        /// <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);
        }
예제 #16
0
        /// <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);
        }
예제 #17
0
        /// <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);
        }
예제 #18
0
        /// <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);
        }
예제 #19
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));
 }
예제 #20
0
        /// <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);
        }
예제 #21
0
        /// <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);
        }
예제 #22
0
        /// <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);
        }
예제 #23
0
        /// <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);
        }
예제 #24
0
        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);
            }
        }
예제 #25
0
 /// <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));
 }
예제 #26
0
 /// <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)));
 }
예제 #27
0
        /// <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();
        }
예제 #28
0
        /// <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));
        }
예제 #29
0
        /// <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);
                    }
                }
            }
        }
예제 #30
0
        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()));
            }
        }