/// <summary> /// Adds an offset curve for a polygon 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 AddRingSide(Coordinate[] coord, double offsetDistance, Position side, Location cwLeftLoc, Location cwRightLoc) { // don't bother adding ring if it is "flat" and will disappear in the output if (offsetDistance == 0.0 && coord.Length < LinearRing.MinimumValidSize) { return; } var leftLoc = cwLeftLoc; var rightLoc = cwRightLoc; /* * Use area-based orientation test, * to ensure that for invalid rings the largest enclosed area * is used to determine orientation. * This produces a more sensible result especially when * used for validifying polygonal geometry via buffer-by-zero. * For buffering use, the lower robustness of ccw-by-area * doesn't matter, since very narrow or flat rings * produce an acceptable offset curve for either orientation. */ if (coord.Length >= LinearRing.MinimumValidSize && Orientation.IsCCWArea(coord)) { leftLoc = cwRightLoc; rightLoc = cwLeftLoc; side = side.Opposite; } var curve = _curveBuilder.GetRingCurve(coord, side, offsetDistance); AddCurve(curve, leftLoc, rightLoc); }
private void InitSideSegments(Coordinate s1, Coordinate s2, Position side) { _s1 = s1; _s2 = s2; _side = side; _seg1.SetCoordinates(s1, s2); ComputeOffsetSegment(_seg1, side, _distance, _offset1); }
/// <summary> /// Compute an offset segment for an input segment on a given side and at a given distance. /// The offset points are computed in full double precision, for accuracy. /// </summary> /// <param name="seg">The segment to offset</param> /// <param name="side">The side of the segment (<see cref="Positions"/>) the offset lies on</param> /// <param name="distance">The offset distance</param> /// <param name="offset">The points computed for the offset segment</param> private static void ComputeOffsetSegment(LineSegment seg, Position side, double distance, LineSegment offset) { int sideSign = side == Position.Left ? 1 : -1; double dx = seg.P1.X - seg.P0.X; double dy = seg.P1.Y - seg.P0.Y; double len = Math.Sqrt(dx * dx + dy * dy); // u is the vector that is the length of the offset, in the direction of the segment double ux = sideSign * distance * dx / len; double uy = sideSign * distance * dy / len; offset.P0.X = seg.P0.X - uy; offset.P0.Y = seg.P0.Y + ux; offset.P1.X = seg.P1.X - uy; offset.P1.Y = seg.P1.Y + ux; }
/// <summary> /// This method handles the degenerate cases of single points and lines, /// as well as rings. /// </summary> /// <returns>A Coordinate array representing the curve<br/> /// or <c>null</c> if the curve is empty</returns> public Coordinate[] GetRingCurve(Coordinate[] inputPts, Position side, double distance) { _distance = distance; if (inputPts.Length <= 2) { return(GetLineCurve(inputPts, distance)); } // optimize creating ring for for zero distance if (distance == 0.0) { return(CopyCoordinates(inputPts)); } var segGen = GetSegmentGenerator(distance); ComputeRingBufferCurve(inputPts, side, segGen); return(segGen.GetCoordinates()); }
/// <summary> /// To compute the summary label for a side, the algorithm is: /// FOR all edges /// IF any edge's location is Interior for the side, side location = Interior /// ELSE IF there is at least one Exterior attribute, side location = Exterior /// ELSE side location = Null /// Note that it is possible for two sides to have apparently contradictory information /// i.e. one edge side may indicate that it is in the interior of a point, while /// another edge side may indicate the exterior of the same point. This is /// not an incompatibility - GeometryCollections may contain two Polygons that touch /// along an edge. This is the reason for Interior-primacy rule above - it /// results in the summary label having the Geometry interior on both sides. /// </summary> /// <param name="geomIndex"></param> /// <param name="side"></param> private void ComputeLabelSide(int geomIndex, Position side) { foreach (var e in _edgeEnds) { if (e.Label.IsArea()) { var loc = e.Label.GetLocation(geomIndex, side); if (loc == Location.Interior) { Label.SetLocation(geomIndex, side, Location.Interior); return; } if (loc == Location.Exterior) { Label.SetLocation(geomIndex, side, Location.Exterior); } } } }
/// <summary> /// This method handles the degenerate cases of single points and lines, as well as rings. /// </summary> /// <returns>a List of Coordinate[]</returns> public IList <Coordinate[]> GetRingCurve(Coordinate[] inputPts, Position side, double distance) { var lineList = new List <Coordinate[]>(); Init(distance); if (inputPts.Length <= 2) { return(GetLineCurve(inputPts, distance)); } // optimize creating ring for for zero distance if (distance == 0.0) { lineList.Add(CopyCoordinates(inputPts)); return(lineList); } ComputeRingBufferCurve(inputPts, side); lineList.Add(_vertexList.Coordinates); return(lineList); }
private void ComputeRingBufferCurve(Coordinate[] inputPts, Position side) { // simplify input line to improve performance double distTol = SimplifyTolerance(_distance); // ensure that correct side is simplified if (side == Position.Right) { distTol = -distTol; } var simp = BufferInputLineSimplifier.Simplify(inputPts, distTol); // Coordinate[] simp = inputPts; int n = simp.Length - 1; InitSideSegments(simp[n - 1], simp[0], side); for (int i = 1; i <= n; i++) { bool addStartPoint = i != 1; AddNextSegment(simp[i], addStartPoint); } _vertexList.CloseRing(); }
private void ComputeRingBufferCurve(Coordinate[] inputPts, Position side, OffsetSegmentGenerator segGen) { // simplify input line to improve performance double distTol = SimplifyTolerance(_distance); // ensure that correct side is simplified if (side == Position.Right) { distTol = -distTol; } var simp = BufferInputLineSimplifier.Simplify(inputPts, distTol); // MD - used for testing only (to eliminate simplification) // Coordinate[] simp = inputPts; int n = simp.Length - 1; segGen.InitSideSegments(simp[n - 1], simp[0], side); for (int i = 1; i <= n; i++) { bool addStartPoint = i != 1; segGen.AddNextSegment(simp[i], addStartPoint); } segGen.CloseRing(); }