/// <summary> /// /// </summary> /// <param name="p"></param> /// <param name="seg"></param> private void TestLineSegment(ICoordinate p, LineSegment seg) { double xInt; // x intersection of segment with ray double x1; // translated coordinates double y1; double x2; double y2; /* * Test if segment crosses ray from test point in positive x direction. */ ICoordinate p1 = seg.P0; ICoordinate p2 = seg.P1; x1 = p1.X - p.X; y1 = p1.Y - p.Y; x2 = p2.X - p.X; y2 = p2.Y - p.Y; if (((y1 > 0) && (y2 <= 0)) || ((y2 > 0) && (y1 <= 0))) { /* * segment straddles x axis, so compute intersection. */ xInt = RobustDeterminant.SignOfDet2x2(x1, y1, x2, y2) / (y2 - y1); /* * crosses ray if strictly positive intersection. */ if (0.0 < xInt) crossings++; } }
/// <summary> /// /// </summary> /// <param name="querySeg"></param> /// <returns></returns> public IList Query(LineSegment querySeg) { Envelope env = new Envelope(querySeg.P0, querySeg.P1); LineSegmentVisitor visitor = new LineSegmentVisitor(querySeg); index.Query(env, visitor); IList itemsFound = visitor.Items; return itemsFound; }
/// <summary> /// /// </summary> private void BuildIndex() { sirTree = new SIRtree(); ICoordinate[] pts = ring.Coordinates; for (int i = 1; i < pts.Length; i++) { if (pts[i - 1].Equals(pts[i])) continue; LineSegment seg = new LineSegment(pts[i - 1], pts[i]); sirTree.Insert(seg.P0.Y, seg.P1.Y, seg); } }
/// <summary> /// /// </summary> /// <param name="querySeg"></param> public LineSegmentVisitor(LineSegment querySeg) { this.querySeg = querySeg; }
/// <summary> /// /// </summary> /// <param name="seg"></param> public void Remove(LineSegment seg) { index.Remove(new Envelope(seg.P0, seg.P1), seg); }
/// <summary> /// /// </summary> /// <param name="ls"></param> public override void Select(LineSegment ls) { container.TestLineSegment(p, ls); }
/// <summary> /// /// </summary> /// <param name="line"></param> /// <param name="pt"></param> /// <param name="locGeom"></param> private void ComputeMinDistance(ILineString line, IPoint pt, GeometryLocation[] locGeom) { if (line.EnvelopeInternal.Distance(pt.EnvelopeInternal) > minDistance) return; ICoordinate[] coord0 = line.Coordinates; ICoordinate coord = pt.Coordinate; // brute force approach! for (int i = 0; i < coord0.Length - 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]); ICoordinate segClosestPoint = seg.ClosestPoint(coord); locGeom[0] = new GeometryLocation(line, i, segClosestPoint); locGeom[1] = new GeometryLocation(pt, 0, coord); } if (minDistance <= terminateDistance) return; } }
/// <summary> /// /// </summary> /// <param name="index"></param> /// <param name="ls"></param> public void GetLineSegment(int index, ref LineSegment ls) { ls.P0 = pts[index]; ls.P1 = pts[index + 1]; }
/// <summary> /// Add an end cap around point p1, terminating a line segment coming from p0. /// </summary> private void AddLineEndCap(ICoordinate p0, ICoordinate p1) { var seg = new LineSegment(p0, p1); var offsetL = new LineSegment(); ComputeOffsetSegment(seg, Positions.Left, distance, offsetL); var offsetR = new LineSegment(); ComputeOffsetSegment(seg, Positions.Right, distance, offsetR); var dx = p1.X - p0.X; var dy = p1.Y - p0.Y; var angle = Math.Atan2(dy, dx); switch (endCapStyle) { case BufferStyle.CapRound: // add offset seg points with a fillet between them AddPt(offsetL.P1); AddFillet(p1, angle + Math.PI / 2, angle - Math.PI / 2, CGAlgorithms.Clockwise, distance); AddPt(offsetR.P1); break; case BufferStyle.CapButt: // only offset segment points are added AddPt(offsetL.P1); AddPt(offsetR.P1); break; case BufferStyle.CapSquare: // add a square defined by extensions of the offset segment endpoints ICoordinate squareCapSideOffset = new Coordinate(); squareCapSideOffset.X = Math.Abs(distance) * Math.Cos(angle); squareCapSideOffset.Y = Math.Abs(distance) * Math.Sin(angle); ICoordinate squareCapLOffset = new Coordinate( offsetL.P1.X + squareCapSideOffset.X, offsetL.P1.Y + squareCapSideOffset.Y); ICoordinate squareCapROffset = new Coordinate( offsetR.P1.X + squareCapSideOffset.X, offsetR.P1.Y + squareCapSideOffset.Y); AddPt(squareCapLOffset); AddPt(squareCapROffset); break; default: break; } }
/// <summary> /// /// </summary> /// <param name="seg0"></param> /// <param name="seg1"></param> /// <returns></returns> private bool HasInteriorIntersection(LineSegment seg0, LineSegment seg1) { li.ComputeIntersection(seg0.P0, seg0.P1, seg1.P0, seg1.P1); return li.IsInteriorIntersection(); }
/// <summary> /// /// </summary> /// <param name="ls"></param> public void SetCoordinates(LineSegment ls) { SetCoordinates(ls.P0, ls.P1); }
/// <summary> /// /// </summary> /// <param name="ls"></param> public LineSegment(LineSegment ls) : this(ls.p0, ls.p1) { }
/// <summary> /// Returns <c>true</c> if <c>other</c> is /// topologically equal to this LineSegment (e.g. irrespective /// of orientation). /// </summary> /// <param name="other"> /// A <c>LineSegment</c> with which to do the comparison. /// </param> /// <returns> /// <c>true</c> if <c>other</c> is a <c>LineSegment</c> /// with the same values for the x and y ordinates. /// </returns> public bool EqualsTopologically(LineSegment other) { return P0.Equals(other.P0) && P1.Equals(other.P1) || P0.Equals(other.P1) && P1.Equals(other.P0); }
/// <summary> /// /// </summary> /// <param name="ls"></param> public void SetCoordinates(LineSegment ls) { SetCoordinates(ls.P0, ls.P1); }
/// <summary> /// /// </summary> /// <param name="ls"></param> public LineSegment(LineSegment ls) : this(ls.p0, ls.p1) { }
/// <summary> /// This is a convenience function which can be overridden to obtain the actual /// line segment which is selected. /// </summary> /// <param name="seg"></param> public virtual void Select(LineSegment seg) { }
/// <summary> /// Returns <c>true</c> if <c>other</c> is /// topologically equal to this LineSegment (e.g. irrespective /// of orientation). /// </summary> /// <param name="other"> /// A <c>LineSegment</c> with which to do the comparison. /// </param> /// <returns> /// <c>true</c> if <c>other</c> is a <c>LineSegment</c> /// with the same values for the x and y ordinates. /// </returns> public bool EqualsTopologically(LineSegment other) { return(P0.Equals(other.P0) && P1.Equals(other.P1) || P0.Equals(other.P1) && P1.Equals(other.P0)); }
/// <summary> /// /// </summary> /// <param name="geom"></param> private void ComputeWidthConvex(IGeometry geom) { ICoordinate[] pts = null; if (geom is IPolygon) pts = ((IPolygon) geom).ExteriorRing.Coordinates; else pts = geom.Coordinates; // special cases for lines or points or degenerate rings if (pts.Length == 0) { minWidth = 0.0; minWidthPt = null; minBaseSeg = null; } else if (pts.Length == 1) { minWidth = 0.0; minWidthPt = pts[0]; minBaseSeg.P0 = pts[0]; minBaseSeg.P1 = pts[0]; } else if (pts.Length == 2 || pts.Length == 3) { minWidth = 0.0; minWidthPt = pts[0]; minBaseSeg.P0 = pts[0]; minBaseSeg.P1 = pts[1]; } else ComputeConvexRingMinDiameter(pts); }
/// <summary> /// /// </summary> /// <param name="i"></param> /// <param name="j"></param> /// <param name="depth"></param> private void SimplifySection(int i, int j, int depth) { depth += 1; int[] sectionIndex = new int[2]; if((i+1) == j) { LineSegment newSeg = line.GetSegment(i); line.AddToResult(newSeg); // leave this segment in the input index, for efficiency return; } double[] distance = new double[1]; int furthestPtIndex = FindFurthestPoint(linePts, i, j, distance); bool isValidToFlatten = true; // must have enough points in the output line if (line.ResultSize < line.MinimumSize && depth < 2) isValidToFlatten = false; // flattening must be less than distanceTolerance if (distance[0] > DistanceTolerance) isValidToFlatten = false; // test if flattened section would cause intersection LineSegment candidateSeg = new LineSegment(); candidateSeg.P0 = linePts[i]; candidateSeg.P1 = linePts[j]; sectionIndex[0] = i; sectionIndex[1] = j; if (HasBadIntersection(line, sectionIndex, candidateSeg)) isValidToFlatten = false; if (isValidToFlatten) { LineSegment newSeg = Flatten(i, j); line.AddToResult(newSeg); return; } SimplifySection(i, furthestPtIndex, depth); SimplifySection(furthestPtIndex, j, depth); }
/// <summary> /// Compute the width information for a ring of <c>Coordinate</c>s. /// Leaves the width information in the instance variables. /// </summary> /// <param name="pts"></param> private void ComputeConvexRingMinDiameter(ICoordinate[] pts) { // for each segment in the ring minWidth = Double.MaxValue; int currMaxIndex = 1; LineSegment seg = new LineSegment(); // compute the max distance for all segments in the ring, and pick the minimum for (int i = 0; i < pts.Length - 1; i++) { seg.P0 = pts[i]; seg.P1 = pts[i + 1]; currMaxIndex = FindMaxPerpDistance(pts, seg, currMaxIndex); } }
/// <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 the offset lies on.</param> /// <param name="distance">The offset distance.</param> /// <param name="offset">The points computed for the offset segment.</param> private void ComputeOffsetSegment(LineSegment seg, Positions side, double distance, LineSegment offset) { var sideSign = side == Positions.Left ? 1 : -1; var dx = seg.P1.X - seg.P0.X; var dy = seg.P1.Y - seg.P0.Y; var len = Math.Sqrt(dx * dx + dy * dy); // u is the vector that is the length of the offset, in the direction of the segment var ux = sideSign * distance * dx / len; var 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> /// /// </summary> /// <param name="pts"></param> /// <param name="seg"></param> /// <param name="startIndex"></param> /// <returns></returns> private int FindMaxPerpDistance(ICoordinate[] pts, LineSegment seg, int startIndex) { double maxPerpDistance = seg.DistancePerpendicular(pts[startIndex]); double nextPerpDistance = maxPerpDistance; int maxIndex = startIndex; int nextIndex = maxIndex; while (nextPerpDistance >= maxPerpDistance) { maxPerpDistance = nextPerpDistance; maxIndex = nextIndex; nextIndex = NextIndex(pts, maxIndex); nextPerpDistance = seg.DistancePerpendicular(pts[nextIndex]); } // found maximum width for this segment - update global min dist if appropriate if (maxPerpDistance < minWidth) { minPtIndex = maxIndex; minWidth = maxPerpDistance; minWidthPt = pts[minPtIndex]; minBaseSeg = new LineSegment(seg); } return maxIndex; }
/// <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; ICoordinate[] coord0 = line0.Coordinates; ICoordinate[] coord1 = line1.Coordinates; // brute force approach! for (int i = 0; i < coord0.Length - 1; i++) { for (int j = 0; j < coord1.Length - 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]); ICoordinate[] closestPt = seg0.ClosestPoints(seg1); locGeom[0] = new GeometryLocation(line0, i, closestPt[0]); locGeom[1] = new GeometryLocation(line1, j, closestPt[1]); } if (minDistance <= terminateDistance) return; } } }
/** * @param co * input coordinate in the neighbourhood of the MLineString * @param tolerance * max. distance that co may be from this MLineString * @return an MCoordinate on this MLineString with appropriate M-value */ public MCoordinate GetClosestPoint(ICoordinate co, double tolerance) { if (!this.IsMonotone(false)) { throw new ApplicationException("MGeometryException.OPERATION_REQUIRES_MONOTONE"); } if (!this.IsEmpty) { LineSegment seg = new LineSegment(); ICoordinate[] coAr = this.Coordinates; seg.P0 = coAr[0]; double d = 0.0; double projfact = 0.0; double minDist = Double.PositiveInfinity; MCoordinate mincp = null; for (int i = 1; i < coAr.Length; i++) { seg.P1 = coAr[i]; ICoordinate cp = seg.ClosestPoint(co); d = cp.Distance(co); if (d <= tolerance && d <= minDist) { MCoordinate testcp = new MCoordinate(cp); projfact = seg.ProjectionFactor(cp); testcp.M = ((MCoordinate)coAr[i - 1]).M + projfact * (((MCoordinate)coAr[i]).M - ((MCoordinate)coAr[i - 1]).M); if (d < minDist || testcp.M < mincp.M) { mincp = testcp; minDist = d; } } seg.P0 = seg.P1; } if (minDist > tolerance) { return null; } else { return mincp; } } else { return null; } }
/// <summary> /// This is a convenience function which can be overridden to obtain the actual /// line segments which overlap. /// </summary> /// <param name="seg1"></param> /// <param name="seg2"></param> public virtual void Overlap(LineSegment seg1, LineSegment seg2) { }
/// <summary> /// /// </summary> /// <param name="pts"></param> /// <param name="i"></param> /// <param name="j"></param> /// <param name="maxDistance"></param> /// <returns></returns> private int FindFurthestPoint(ICoordinate[] pts, int i, int j, double[] maxDistance) { LineSegment seg = new LineSegment(); seg.P0 = pts[i]; seg.P1 = pts[j]; double maxDist = -1.0; int maxIndex = i; for (int k = i + 1; k < j; k++) { ICoordinate midPt = pts[k]; double distance = seg.Distance(midPt); if (distance > maxDist) { maxDist = distance; maxIndex = k; } } maxDistance[0] = maxDist; return maxIndex; }
/// <summary> /// /// </summary> /// <param name="seg"></param> public void Add(LineSegment seg) { index.Insert(new Envelope(seg.P0, seg.P1), seg); }
/// <summary> /// /// </summary> /// <param name="start"></param> /// <param name="end"></param> /// <returns></returns> private LineSegment Flatten(int start, int end) { // make a new segment for the simplified point ICoordinate p0 = linePts[start]; ICoordinate p1 = linePts[end]; LineSegment newSeg = new LineSegment(p0, p1); // update the indexes Remove(line, start, end); outputIndex.Add(newSeg); return newSeg; }
/// <summary> /// /// </summary> /// <param name="parentLine"></param> /// <param name="sectionIndex"></param> /// <param name="candidateSeg"></param> /// <returns></returns> private bool HasBadIntersection(TaggedLineString parentLine, int[] sectionIndex, LineSegment candidateSeg) { if (HasBadOutputIntersection(candidateSeg)) return true; if (HasBadInputIntersection(parentLine, sectionIndex, candidateSeg)) return true; return false; }
/// <summary> /// /// </summary> /// <param name="candidateSeg"></param> /// <returns></returns> private bool HasBadOutputIntersection(LineSegment candidateSeg) { IList querySegs = outputIndex.Query(candidateSeg); for (IEnumerator i = querySegs.GetEnumerator(); i.MoveNext(); ) { LineSegment querySeg = (LineSegment) i.Current; if (HasInteriorIntersection(querySeg, candidateSeg)) return true; } return false; }
/// <summary> /// Create a rectangle containing the study area and dividible by the desired grid size /// </summary> /// <param name="pol"></param> /// <param name="widthDivBy"></param> /// <param name="heightDivBy"></param> private void roundedRectangle(IPolygon pol, int widthDivBy, int heightDivBy) { try { double width = new LineSegment(pol.Coordinates[0],pol.Coordinates[1]).Length; double height = new LineSegment(pol.Coordinates[1],pol.Coordinates[2]).Length; var roundedWidth = (int) Math.Sign(width) * Math.Ceiling(Math.Abs(width)/widthDivBy) * widthDivBy; var roundedHeight= (int) Math.Sign(height) * Math.Ceiling(Math.Abs(height)/heightDivBy) * heightDivBy; //get the total num of cols and rows _gridCols=(int)roundedWidth/widthDivBy; _gridRows=(int)roundedHeight/heightDivBy; ICoordinate origin =(ICoordinate)pol.Coordinates[0]; ICoordinate[] coords=new ICoordinate[5]; coords[0]= origin; coords[1]= new Coordinate(origin.X+roundedWidth, origin.Y); coords[2]= new Coordinate(origin.X+roundedWidth,origin.Y+roundedHeight); coords[3]= new Coordinate(origin.X,origin.Y+roundedHeight); coords[4]= origin; ILinearRing lr=new LinearRing(coords); _roundedRectangle=new Polygon(lr); } catch (Exception ex) { throw ex; } }
/// <summary> /// /// </summary> /// <param name="parentLine"></param> /// <param name="sectionIndex"></param> /// <param name="candidateSeg"></param> /// <returns></returns> private bool HasBadInputIntersection(TaggedLineString parentLine, int[] sectionIndex, LineSegment candidateSeg) { IList querySegs = inputIndex.Query(candidateSeg); for (IEnumerator i = querySegs.GetEnumerator(); i.MoveNext(); ) { TaggedLineSegment querySeg = (TaggedLineSegment) i.Current; if (HasInteriorIntersection(querySeg, candidateSeg)) { if (IsInLineSection(parentLine, sectionIndex, querySeg)) continue; return true; } } return false; }
/// <summary> /// /// </summary> /// <param name="seg"></param> public void AddToResult(LineSegment seg) { resultSegs.Add(seg); }
/// <summary> /// Computes the distance between this line segment and another one. /// </summary> /// <param name="ls"></param> /// <returns></returns> public double Distance(LineSegment ls) { return(CGAlgorithms.DistanceLineLine(P0, P1, ls.P0, ls.P1)); }