/// <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="seg"></param> /// <param name="inputPt"></param> /// <returns></returns> public static double SegmentFraction(LineSegment seg, ICoordinate inputPt) { double segFrac = seg.ProjectionFactor(inputPt); if (segFrac < 0.0) segFrac = 0.0; else if (segFrac > 1.0) segFrac = 1.0; return segFrac; }
/// <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> /// Compare two collinear segments for left-most ordering. /// If segs are vertical, use vertical ordering for comparison. /// If segs are equal, return 0. /// Segments are assumed to be directed so that the second coordinate is >= to the first /// (e.g. up and to the right). /// </summary> /// <param name="seg0">A segment to compare.</param> /// <param name="seg1">A segment to compare.</param> /// <returns></returns> private int CompareX(LineSegment seg0, LineSegment seg1) { int compare0 = seg0.P0.CompareTo(seg1.P0); if (compare0 != 0) return compare0; return seg0.P1.CompareTo(seg1.P1); }
/// <summary> /// /// </summary> /// <param name="seg"></param> /// <param name="depth"></param> public DepthSegment(LineSegment seg, int depth) { // input seg is assumed to be normalized upwardSeg = new LineSegment(seg); this.leftDepth = depth; }
/// <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); }
/// <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="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> /// 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> /// 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 int OrientationIndex(LineSegment 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> /// /// </summary> /// <param name="ls"></param> public override void Select(LineSegment ls) { container.TestLineSegment(p, ls); }
/// <summary> /// /// </summary> /// <param name="inputPt"></param> /// <param name="minIndex"></param> /// <returns></returns> private double IndexOfFromStart(ICoordinate inputPt, double minIndex) { double minDistance = Double.MaxValue; double ptMeasure = minIndex; double segmentStartMeasure = 0.0; LineSegment seg = new LineSegment(); foreach(LinearIterator.LinearElement element in new LinearIterator(linearGeom)) { if (!element.IsEndOfLine) { seg.P0 = element.SegmentStart; seg.P1 = element.SegmentEnd; double segDistance = seg.Distance(inputPt); double segMeasureToPt = SegmentNearestMeasure(seg, inputPt, segmentStartMeasure); if (segDistance < minDistance && segMeasureToPt > minIndex) { ptMeasure = segMeasureToPt; minDistance = segDistance; } segmentStartMeasure += seg.Length; } } return ptMeasure; }
/// <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> /// /// </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> /// Project a line segment onto this line segment and return the resulting /// line segment. The returned line segment will be a subset of /// the target line line segment. This subset may be null, if /// the segments are oriented in such a way that there is no projection. /// Note that the returned line may have zero length (i.e. the same endpoints). /// This can happen for instance if the lines are perpendicular to one another. /// </summary> /// <param name="seg">The line segment to project.</param> /// <returns>The projected line segment, or <c>null</c> if there is no overlap.</returns> public LineSegment Project(LineSegment seg) { double pf0 = ProjectionFactor(seg.P0); double pf1 = ProjectionFactor(seg.P1); // check if segment projects at all if (pf0 >= 1.0 && pf1 >= 1.0) return null; if (pf0 <= 0.0 && pf1 <= 0.0) return null; ICoordinate newp0 = Project(seg.P0); if (pf0 < 0.0) newp0 = P0; if (pf0 > 1.0) newp0 = P1; ICoordinate newp1 = Project(seg.P1); if (pf1 < 0.0) newp1 = P0; if (pf1 > 1.0) newp1 = P1; return new LineSegment(newp0, newp1); }
/// <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="seg"></param> /// <param name="inputPt"></param> /// <param name="segmentStartMeasure"></param> /// <returns></returns> private double SegmentNearestMeasure(LineSegment seg, ICoordinate inputPt, double segmentStartMeasure) { // found new minimum, so compute location distance of point double projFactor = seg.ProjectionFactor(inputPt); if (projFactor <= 0.0) return segmentStartMeasure; if (projFactor <= 1.0) return segmentStartMeasure + projFactor * seg.Length; // ASSERT: projFactor > 1.0 return segmentStartMeasure + seg.Length; }
/// <summary> /// Computes an intersection point between two segments, if there is one. /// There may be 0, 1 or many intersection points between two segments. /// If there are 0, null is returned. If there is 1 or more, a single one /// is returned (chosen at the discretion of the algorithm). If /// more information is required about the details of the intersection, /// the {RobustLineIntersector} class should be used. /// </summary> /// <param name="line"></param> /// <returns> An intersection point, or <c>null</c> if there is none.</returns> public ICoordinate Intersection(LineSegment line) { LineIntersector li = new RobustLineIntersector(); li.ComputeIntersection(P0, P1, line.P0, line.P1); if (li.HasIntersection) return li.GetIntersection(0); return null; }
/// <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> /// /// </summary> /// <param name="seg"></param> public void AddToResult(LineSegment seg) { resultSegs.Add(seg); }
/// <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="querySeg"></param> public LineSegmentVisitor(LineSegment querySeg) { this.querySeg = querySeg; }
/// <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; } } }
/// <summary> /// /// </summary> /// <param name="seg"></param> public void Add(LineSegment seg) { index.Insert(new Envelope(seg.P0, seg.P1), seg); }
/// <summary> /// /// </summary> /// <param name="inputPt"></param> /// <param name="minIndex"></param> /// <returns></returns> private LinearLocation IndexOfFromStart(ICoordinate inputPt, LinearLocation minIndex) { double minDistance = Double.MaxValue; int minComponentIndex = 0; int minSegmentIndex = 0; double minFrac = -1.0; LineSegment seg = new LineSegment(); foreach (LinearIterator.LinearElement element in new LinearIterator(linearGeom)) { if (!element.IsEndOfLine) { seg.P0 = element.SegmentStart; seg.P1 = element.SegmentEnd; double segDistance = seg.Distance(inputPt); double segFrac = SegmentFraction(seg, inputPt); int candidateComponentIndex = element.ComponentIndex; int candidateSegmentIndex = element.VertexIndex; if (segDistance < minDistance) { // ensure after minLocation, if any if (minIndex == null || minIndex.CompareLocationValues(candidateComponentIndex, candidateSegmentIndex, segFrac) < 0) { // otherwise, save this as new minimum minComponentIndex = candidateComponentIndex; minSegmentIndex = candidateSegmentIndex; minFrac = segFrac; minDistance = segDistance; } } } } LinearLocation loc = new LinearLocation(minComponentIndex, minSegmentIndex, minFrac); return loc; }
/// <summary> /// /// </summary> /// <param name="seg"></param> public void Remove(LineSegment seg) { index.Remove(new Envelope(seg.P0, seg.P1), seg); }
/// <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> /// Computes the closest points on a line segment. /// </summary> /// <param name="line"></param> /// <returns> /// A pair of Coordinates which are the closest points on the line segments. /// </returns> public ICoordinate[] ClosestPoints(LineSegment line) { // test for intersection ICoordinate intPt = Intersection(line); if (intPt != null) return new ICoordinate[] { intPt, intPt }; /* * if no intersection closest pair contains at least one endpoint. * Test each endpoint in turn. */ ICoordinate[] closestPt = new ICoordinate[2]; double minDistance = Double.MaxValue; double dist; ICoordinate close00 = ClosestPoint(line.P0); minDistance = close00.Distance(line.P0); closestPt[0] = close00; closestPt[1] = line.P0; ICoordinate close01 = ClosestPoint(line.P1); dist = close01.Distance(line.P1); if (dist < minDistance) { minDistance = dist; closestPt[0] = close01; closestPt[1] = line.P1; } ICoordinate close10 = line.ClosestPoint(P0); dist = close10.Distance(P0); if (dist < minDistance) { minDistance = dist; closestPt[0] = P0; closestPt[1] = close10; } ICoordinate close11 = line.ClosestPoint(P1); dist = close11.Distance(P1); if (dist < minDistance) { minDistance = dist; closestPt[0] = P1; closestPt[1] = close11; } return closestPt; }