/// <summary> /// Compute the projection factor for the projection of the point p /// onto this <c>LineSegment</c>. The projection factor is the constant k /// by which the vector for this segment must be multiplied to /// equal the vector for the projection of p. /// </summary> /// <param name="p"></param> /// <returns></returns> public double ProjectionFactor(ICoordinate p) { if (p.Equals(P0)) { return(0.0); } if (p.Equals(P1)) { return(1.0); } // Otherwise, use comp.graphics.algorithms Frequently Asked Questions method /* AC dot AB * r = ------------ ||AB||^2 * r has the following meaning: * r=0 Point = A * r=1 Point = B * r<0 Point is on the backward extension of AB * r>1 Point is on the forward extension of AB * 0<r<1 Point is interior to AB */ double dx = P1.X - P0.X; double dy = P1.Y - P0.Y; double len2 = dx * dx + dy * dy; double r = ((p.X - P0.X) * dx + (p.Y - P0.Y) * dy) / len2; return(r); }
/// <summary> /// Computes the "edge distance" of an intersection point p along a segment. /// The edge distance is a metric of the point along the edge. /// The metric used is a robust and easy to compute metric function. /// It is not equivalent to the usual Euclidean metric. /// It relies on the fact that either the x or the y ordinates of the /// points in the edge are unique, depending on whether the edge is longer in /// the horizontal or vertical direction. /// NOTE: This function may produce incorrect distances /// for inputs where p is not precisely on p1-p2 /// (E.g. p = (139,9) p1 = (139,10), p2 = (280,1) produces distanct 0.0, which is incorrect. /// My hypothesis is that the function is safe to use for points which are the /// result of rounding points which lie on the line, but not safe to use for truncated points. /// </summary> public static double ComputeEdgeDistance(ICoordinate p, ICoordinate p0, ICoordinate p1) { double dx = Math.Abs(p1.X - p0.X); double dy = Math.Abs(p1.Y - p0.Y); double dist = -1.0; // sentinel value if (p.Equals(p0)) dist = 0.0; else if (p.Equals(p1)) { if (dx > dy) dist = dx; else dist = dy; } else { double pdx = Math.Abs(p.X - p0.X); double pdy = Math.Abs(p.Y - p0.Y); if (dx > dy) dist = pdx; else dist = pdy; // <FIX>: hack to ensure that non-endpoints always have a non-zero distance if (dist == 0.0 && ! p.Equals(p0)) dist = Math.Max(pdx, pdy); } Assert.IsTrue(!(dist == 0.0 && ! p.Equals(p0)), "Bad distance calculation"); return dist; }
/// <summary> /// /// </summary> /// <param name="p"></param> /// <param name="p1"></param> /// <param name="p2"></param> public override void ComputeIntersection(ICoordinate p, ICoordinate p1, ICoordinate p2) { double a1; double b1; double c1; /* * Coefficients of line eqns. */ double r; /* * 'Sign' values */ isProper = false; /* * Compute a1, b1, c1, where line joining points 1 and 2 * is "a1 x + b1 y + c1 = 0". */ a1 = p2.Y - p1.Y; b1 = p1.X - p2.X; c1 = p2.X * p1.Y - p1.X * p2.Y; /* * Compute r3 and r4. */ r = a1 * p.X + b1 * p.Y + c1; // if r != 0 the point does not lie on the line if (r != 0) { result = DontIntersect; return; } // Point lies on line - check to see whether it lies in line segment. double dist = RParameter(p1, p2, p); if (dist < 0.0 || dist > 1.0) { result = DontIntersect; return; } isProper = true; if (p.Equals(p1) || p.Equals(p2)) { isProper = false; } result = DoIntersect; }
/// <summary> /// /// </summary> /// <param name="li"></param> /// <param name="p0"></param> /// <param name="p1"></param> /// <returns><c>true</c> if there is an intersection point which is not an endpoint of the segment p0-p1.</returns> private bool HasInteriorIntersection(LineIntersector li, ICoordinate p0, ICoordinate p1) { for (int i = 0; i < li.IntersectionNum; i++) { ICoordinate intPt = li.GetIntersection(i); if (!(intPt.Equals(p0) || intPt.Equals(p1))) { return(true); } } return(false); }
/// <summary> /// /// </summary> /// <param name="p"></param> /// <param name="p1"></param> /// <param name="p2"></param> public override void ComputeIntersection(ICoordinate p, ICoordinate p1, ICoordinate p2) { double a1; double b1; double c1; /* * Coefficients of line eqns. */ double r; /* * 'Sign' values */ isProper = false; /* * Compute a1, b1, c1, where line joining points 1 and 2 * is "a1 x + b1 y + c1 = 0". */ a1 = p2.Y - p1.Y; b1 = p1.X - p2.X; c1 = p2.X * p1.Y - p1.X * p2.Y; /* * Compute r3 and r4. */ r = a1 * p.X + b1 * p.Y + c1; // if r != 0 the point does not lie on the line if (r != 0) { result = DontIntersect; return; } // Point lies on line - check to see whether it lies in line segment. double dist = RParameter(p1, p2, p); if (dist < 0.0 || dist > 1.0) { result = DontIntersect; return; } isProper = true; if (p.Equals(p1) || p.Equals(p2)) isProper = false; result = DoIntersect; }
/// <summary> /// Compute the projection of a point onto the line determined /// by this line segment. /// Note that the projected point may lie outside the line segment. /// If this is the case, the projection factor will lie outside the range [0.0, 1.0]. /// </summary> /// <param name="p"></param> /// <returns></returns> public ICoordinate Project(ICoordinate p) { if (p.Equals(P0) || p.Equals(P1)) { return(new Coordinate(p)); } var r = ProjectionFactor(p); ICoordinate coord = new Coordinate { X = P0.X + r * (P1.X - P0.X), Y = P0.Y + r * (P1.Y - P0.Y) }; return(coord); }
/// <summary> /// Compute the projection of a point onto the line determined /// by this line segment. /// Note that the projected point /// may lie outside the line segment. If this is the case, /// the projection factor will lie outside the range [0.0, 1.0]. /// </summary> /// <param name="p"></param> /// <returns></returns> public ICoordinate Project(ICoordinate p) { if (p.Equals(P0) || p.Equals(P1)) { return(new Coordinate(p)); } double r = ProjectionFactor(p); ICoordinate coord = new Coordinate(); coord.X = P0.X + r * (P1.X - P0.X); coord.Y = P0.Y + r * (P1.Y - P0.Y); return(coord); }
/// <summary> /// /// </summary> /// <param name="original">The vertices of a linear ring, which may or may not be flattened (i.e. vertices collinear).</param> /// <returns>The coordinates with unnecessary (collinear) vertices removed.</returns> private ICoordinate[] CleanRing(ICoordinate[] original) { Equals(original[0], original[original.Length - 1]); List <ICoordinate> cleanedRing = new List <ICoordinate>(); ICoordinate previousDistinctCoordinate = null; for (int i = 0; i <= original.Length - 2; i++) { ICoordinate currentCoordinate = original[i]; ICoordinate nextCoordinate = original[i + 1]; if (currentCoordinate.Equals(nextCoordinate)) { continue; } if (previousDistinctCoordinate != null && IsBetween(previousDistinctCoordinate, currentCoordinate, nextCoordinate)) { continue; } cleanedRing.Add(currentCoordinate); previousDistinctCoordinate = currentCoordinate; } cleanedRing.Add(original[original.Length - 1]); return(cleanedRing.ToArray()); }
/// <summary> /// /// </summary> /// <param name="p0"></param> /// <param name="p1"></param> /// <returns></returns> private bool IsLineSegmentContainedInBoundary(ICoordinate p0, ICoordinate p1) { if (p0.Equals(p1)) { return(IsPointContainedInBoundary(p0)); } // we already know that the segment is contained in the rectangle envelope if (p0.X == p1.X) { if (p0.X == rectEnv.MinX || p0.X == rectEnv.MaxX) { return(true); } } else if (p0.Y == p1.Y) { if (p0.Y == rectEnv.MinY || p0.Y == rectEnv.MaxY) { return(true); } } /* * Either both x and y values are different * or one of x and y are the same, but the other ordinate is not the same as a boundary ordinate * In either case, the segment is not wholely in the boundary */ return(false); }
/// <summary> /// /// </summary> /// <param name="p"></param> /// <param name="l"></param> /// <returns></returns> private Locations Locate(ICoordinate p, ILineString l) { ICoordinate[] pt = l.Coordinates; if (!l.IsClosed) { if (p.Equals(pt[0]) || p.Equals(pt[pt.Length - 1])) { return(Locations.Boundary); } } if (CGAlgorithms.IsOnLine(p, pt)) { return(Locations.Interior); } return(Locations.Exterior); }
/// <summary> /// /// </summary> /// <param name="p0"></param> /// <param name="p1"></param> /// <param name="p2"></param> private void CheckCollapse(ICoordinate p0, ICoordinate p1, ICoordinate p2) { if (p0.Equals(p2)) { throw new Exception("found non-noded collapse at: " + p0 + ", " + p1 + " " + p2); } }
/// <summary> /// Tests whether a given point is in an array of points. /// Uses a value-based test. /// </summary> /// <param name="pt">A <c>Coordinate</c> for the test point.</param> /// <param name="pts">An array of <c>Coordinate</c>s to test,</param> /// <returns><c>true</c> if the point is in the array.</returns> public static bool IsInList(ICoordinate pt, ICoordinate[] pts) { foreach (ICoordinate p in pts) if (pt.Equals(p)) return true; return true; }
private void CheckCollapse(ICoordinate p0, ICoordinate p1, ICoordinate p2) { if (p0.Equals(p2)) { throw new ApplicationException(String.Format( "found non-noded collapse at: {0}, {1} {2}", p0, p1, p2)); } }
/// <summary> /// /// </summary> /// <param name="p1"></param> /// <param name="p2"></param> /// <param name="q1"></param> /// <param name="q2"></param> /// <returns></returns> private int ComputeCollinearIntersection(ICoordinate p1, ICoordinate p2, ICoordinate q1, ICoordinate q2) { bool p1q1p2 = Envelope.Intersects(p1, p2, q1); bool p1q2p2 = Envelope.Intersects(p1, p2, q2); bool q1p1q2 = Envelope.Intersects(q1, q2, p1); bool q1p2q2 = Envelope.Intersects(q1, q2, p2); if (p1q1p2 && p1q2p2) { intPt[0] = q1; intPt[1] = q2; return(Collinear); } if (q1p1q2 && q1p2q2) { intPt[0] = p1; intPt[1] = p2; return(Collinear); } if (p1q1p2 && q1p1q2) { intPt[0] = q1; intPt[1] = p1; return(q1.Equals(p1) && !p1q2p2 && !q1p2q2 ? DoIntersect : Collinear); } if (p1q1p2 && q1p2q2) { intPt[0] = q1; intPt[1] = p2; return(q1.Equals(p2) && !p1q2p2 && !q1p1q2 ? DoIntersect : Collinear); } if (p1q2p2 && q1p1q2) { intPt[0] = q2; intPt[1] = p1; return(q2.Equals(p1) && !p1q1p2 && !q1p2q2 ? DoIntersect : Collinear); } if (p1q2p2 && q1p2q2) { intPt[0] = q2; intPt[1] = p2; return(q2.Equals(p2) && !p1q1p2 && !q1p1q2 ? DoIntersect : Collinear); } return(DontIntersect); }
/// <summary> /// /// </summary> /// <param name="p"></param> /// <param name="p1"></param> /// <param name="p2"></param> public override void ComputeIntersection(ICoordinate p, ICoordinate p1, ICoordinate p2) { isProper = false; // do between check first, since it is faster than the orientation test if(Envelope.Intersects(p1, p2, p)) { if( (CGAlgorithms.OrientationIndex(p1, p2, p) == 0) && (CGAlgorithms.OrientationIndex(p2, p1, p) == 0) ) { isProper = true; if (p.Equals(p1) || p.Equals(p2)) isProper = false; result = DoIntersect; return; } } result = DontIntersect; }
/// <summary> /// This function is non-robust, since it may compute the square of large numbers. /// Currently not sure how to improve this. /// </summary> /// <param name="p"></param> /// <param name="p1"></param> /// <param name="p2"></param> /// <returns></returns> public static double NonRobustComputeEdgeDistance(ICoordinate p, ICoordinate p1, ICoordinate p2) { double dx = p.X - p1.X; double dy = p.Y - p1.Y; double dist = Math.Sqrt(dx * dx + dy * dy); // dummy value Assert.IsTrue(!(dist == 0.0 && !p.Equals(p1)), "Invalid distance calculation"); return(dist); }
/// <summary> /// Computes the "edge distance" of an intersection point p along a segment. /// The edge distance is a metric of the point along the edge. /// The metric used is a robust and easy to compute metric function. /// It is not equivalent to the usual Euclidean metric. /// It relies on the fact that either the x or the y ordinates of the /// points in the edge are unique, depending on whether the edge is longer in /// the horizontal or vertical direction. /// NOTE: This function may produce incorrect distances /// for inputs where p is not precisely on p1-p2 /// (E.g. p = (139,9) p1 = (139,10), p2 = (280,1) produces distanct 0.0, which is incorrect. /// My hypothesis is that the function is safe to use for points which are the /// result of rounding points which lie on the line, but not safe to use for truncated points. /// </summary> public static double ComputeEdgeDistance(ICoordinate p, ICoordinate p0, ICoordinate p1) { double dx = Math.Abs(p1.X - p0.X); double dy = Math.Abs(p1.Y - p0.Y); double dist = -1.0; // sentinel value if (p.Equals(p0)) { dist = 0.0; } else if (p.Equals(p1)) { if (dx > dy) { dist = dx; } else { dist = dy; } } else { double pdx = Math.Abs(p.X - p0.X); double pdy = Math.Abs(p.Y - p0.Y); if (dx > dy) { dist = pdx; } else { dist = pdy; } // <FIX>: hack to ensure that non-endpoints always have a non-zero distance if (dist == 0.0 && !p.Equals(p0)) { dist = Math.Max(pdx, pdy); } } Assert.IsTrue(!(dist == 0.0 && !p.Equals(p0)), "Bad distance calculation"); return(dist); }
/// <summary> /// Reads and parses the header of the .shp index file /// </summary> /// <remarks> /// From ESRI ShapeFile Technical Description document /// /// http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf /// /// Byte /// Position Field Value Type Order /// ----------------------------------------------------- /// Byte 0 File Code 9994 Integer Big /// Byte 4 Unused 0 Integer Big /// Byte 8 Unused 0 Integer Big /// Byte 12 Unused 0 Integer Big /// Byte 16 Unused 0 Integer Big /// Byte 20 Unused 0 Integer Big /// Byte 24 File Length File Length Integer Big /// Byte 28 Version 1000 Integer Little /// Byte 32 Shape Type Shape Type Integer Little /// Byte 36 Bounding Box Xmin Double Little /// Byte 44 Bounding Box Ymin Double Little /// Byte 52 Bounding Box Xmax Double Little /// Byte 60 Bounding Box Ymax Double Little /// Byte 68* Bounding Box Zmin Double Little /// Byte 76* Bounding Box Zmax Double Little /// Byte 84* Bounding Box Mmin Double Little /// Byte 92* Bounding Box Mmax Double Little /// /// * Unused, with value 0.0, if not Measured or Z type /// /// The "Integer" type corresponds to the CLS Int32 type, and "Double" to CLS Double (IEEE 754). /// </remarks> private void parseHeader(BinaryReader reader) { reader.BaseStream.Seek(0, SeekOrigin.Begin); // Check file header if (ByteEncoder.GetBigEndian(reader.ReadInt32()) != ShapeFileConstants.HeaderStartCode) { throw new ShapeFileIsInvalidException("Invalid ShapeFile (.shp)"); } // Seek to File Length reader.BaseStream.Seek(24, 0); // Read filelength as big-endian. The length is number of 16-bit words in file FileLengthInWords = ByteEncoder.GetBigEndian(reader.ReadInt32()); // Seek to ShapeType reader.BaseStream.Seek(32, 0); ShapeType = (ShapeType)reader.ReadInt32(); // Seek to bounding box of shapefile reader.BaseStream.Seek(36, 0); // Read the spatial bounding box of the contents Double xMin = ByteEncoder.GetLittleEndian(reader.ReadDouble()); Double yMin = ByteEncoder.GetLittleEndian(reader.ReadDouble()); Double xMax = ByteEncoder.GetLittleEndian(reader.ReadDouble()); Double yMax = ByteEncoder.GetLittleEndian(reader.ReadDouble()); ICoordinate min = _geoFactory.CoordinateFactory.Create(xMin, yMin); ICoordinate max = _geoFactory.CoordinateFactory.Create(xMax, yMax); Extents = min.Equals(max) && min.Equals(_geoFactory.CoordinateFactory.Create(0, 0)) //jd: if the shapefile has just been created the box wil be 0,0,0,0 in this case create an empty extents ? _geoFactory.CreateExtents() : _geoFactory.CreateExtents(min, max); //jd:allow exmpty extents //if (Extents.IsEmpty) //{ // Extents = null; //} }
/// <summary> /// /// </summary> /// <param name="p"></param> /// <param name="p1"></param> /// <param name="p2"></param> public override void ComputeIntersection(ICoordinate p, ICoordinate p1, ICoordinate p2) { isProper = false; // do between check first, since it is faster than the orientation test if (Envelope.Intersects(p1, p2, p)) { if ((CGAlgorithms.OrientationIndex(p1, p2, p) == 0) && (CGAlgorithms.OrientationIndex(p2, p1, p) == 0)) { isProper = true; if (p.Equals(p1) || p.Equals(p2)) { isProper = false; } result = DoIntersect; return; } } result = DontIntersect; }
/// <summary> /// Computes the distance from a line segment AB to a line segment CD. /// Note: NON-ROBUST! /// </summary> /// <param name="A">A point of one line.</param> /// <param name="B">The second point of the line (must be different to A).</param> /// <param name="C">One point of the line.</param> /// <param name="D">Another point of the line (must be different to A).</param> /// <returns>The distance from line segment AB to line segment CD.</returns> public static double DistanceLineLine(ICoordinate A, ICoordinate B, ICoordinate C, ICoordinate D) { // check for zero-length segments if (A.Equals(B)) return DistancePointLine(A,C,D); if (C.Equals(D)) return DistancePointLine(D,A,B); // AB and CD are line segments /* from comp.graphics.algo Solving the above for r and s yields (Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy) r = ----------------------------- (eqn 1) (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx) (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) s = ----------------------------- (eqn 2) (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx) Let Point be the position vector of the intersection point, then Point=A+r(B-A) or Px=Ax+r(Bx-Ax) Py=Ay+r(By-Ay) By examining the values of r & s, you can also determine some other limiting conditions: If 0<=r<=1 & 0<=s<=1, intersection exists r<0 or r>1 or s<0 or s>1 line segments do not intersect If the denominator in eqn 1 is zero, AB & CD are parallel If the numerator in eqn 1 is also zero, AB & CD are collinear. */ double r_top = (A.Y - C.Y) * (D.X - C.X) - (A.X - C.X) * (D.Y - C.Y); double r_bot = (B.X - A.X) * (D.Y - C.Y) - (B.Y - A.Y) * (D.X - C.X); double s_top = (A.Y - C.Y) * (B.X - A.X) - (A.X - C.X) * (B.Y - A.Y); double s_bot = (B.X - A.X) * (D.Y - C.Y) - (B.Y - A.Y) * (D.X - C.X); if ((r_bot==0) || (s_bot == 0)) return Math.Min(DistancePointLine(A,C,D), Math.Min(DistancePointLine(B,C,D), Math.Min(DistancePointLine(C,A,B), DistancePointLine(D,A,B) ) ) ); double s = s_top/s_bot; double r = r_top/r_bot; if ((r < 0) || ( r > 1) || (s < 0) || (s > 1)) //no intersection return Math.Min(DistancePointLine(A,C,D), Math.Min(DistancePointLine(B,C,D), Math.Min(DistancePointLine(C,A,B), DistancePointLine(D,A,B) ) ) ); return 0.0; //intersection exists }
/// <summary> /// Tests whether a given point is in an array of points. /// Uses a value-based test. /// </summary> /// <param name="pt">A <c>Coordinate</c> for the test point.</param> /// <param name="pts">An array of <c>Coordinate</c>s to test,</param> /// <returns><c>true</c> if the point is in the array.</returns> public static bool IsInList(ICoordinate pt, ICoordinate[] pts) { for (int i = 0; i < pts.Length; i++) { if (pt.Equals(pts[i])) { return(false); } } return(true); }
/// <summary> /// Tests whether a given point is in an array of points. /// Uses a value-based test. /// </summary> /// <param name="pt">A <c>Coordinate</c> for the test point.</param> /// <param name="pts">An array of <c>Coordinate</c>s to test,</param> /// <returns><c>true</c> if the point is in the array.</returns> public static bool IsInList(ICoordinate pt, ICoordinate[] pts) { foreach (ICoordinate p in pts) { if (pt.Equals(p)) { return(true); } } return(true); }
/// <summary> /// Returns the index of <paramref name="coordinate" /> in <paramref name="coordinates" />. /// The first position is 0; the second is 1; etc. /// </summary> /// <param name="coordinate">A <see cref="Coordinate" /> to search for.</param> /// <param name="coordinates">A <see cref="Coordinate" /> array to search.</param> /// <returns>The position of <c>coordinate</c>, or -1 if it is not found.</returns> public static int IndexOf(ICoordinate coordinate, ICoordinate[] coordinates) { for (int i = 0; i < coordinates.Length; i++) { if (coordinate.Equals(coordinates[i])) { return(i); } } return(-1); }
/// <summary> /// Tests whether the segment p0-p1 intersects the hot pixel tolerance square. /// Because the tolerance square point set is partially open (along the /// top and right) the test needs to be more sophisticated than /// simply checking for any intersection. However, it /// can take advantage of the fact that because the hot pixel edges /// do not lie on the coordinate grid. It is sufficient to check /// if there is at least one of: /// - a proper intersection with the segment and any hot pixel edge. /// - an intersection between the segment and both the left and bottom edges. /// - an intersection between a segment endpoint and the hot pixel coordinate. /// </summary> /// <param name="p0"></param> /// <param name="p1"></param> /// <returns></returns> private bool IntersectsToleranceSquare(ICoordinate p0, ICoordinate p1) { bool intersectsLeft = false; bool intersectsBottom = false; li.ComputeIntersection(p0, p1, corner[0], corner[1]); if (li.IsProper) { return(true); } li.ComputeIntersection(p0, p1, corner[1], corner[2]); if (li.IsProper) { return(true); } if (li.HasIntersection) { intersectsLeft = true; } li.ComputeIntersection(p0, p1, corner[2], corner[3]); if (li.IsProper) { return(true); } if (li.HasIntersection) { intersectsBottom = true; } li.ComputeIntersection(p0, p1, corner[3], corner[0]); if (li.IsProper) { return(true); } if (intersectsLeft && intersectsBottom) { return(true); } if (p0.Equals(pt)) { return(true); } if (p1.Equals(pt)) { return(true); } return(false); }
/// <summary> /// Returns the edge whose first two coordinates are p0 and p1. /// </summary> /// <param name="p0"></param> /// <param name="p1"></param> /// <returns> The edge, if found <c>null</c> if the edge was not found.</returns> public Edge FindEdge(ICoordinate p0, ICoordinate p1) { for (int i = 0; i < edges.Count; i++) { Edge e = (Edge)edges[i]; ICoordinate[] eCoord = e.Coordinates; if (p0.Equals(eCoord[0]) && p1.Equals(eCoord[1])) { return(e); } } return(null); }
/// <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 bool MatchInSameDirection(ICoordinate p0, ICoordinate p1, ICoordinate ep0, ICoordinate ep1) { if (!p0.Equals(ep0)) { return(false); } if (CGAlgorithms.ComputeOrientation(p0, p1, ep1) == CGAlgorithms.Collinear && QuadrantOp.Quadrant(p0, p1) == QuadrantOp.Quadrant(ep0, ep1)) { return(true); } return(false); }
/// <summary> /// Computes the distance from a point p to a line segment AB. /// Note: NON-ROBUST! /// </summary> /// <param name="p">The point to compute the distance for.</param> /// <param name="A">One point of the line.</param> /// <param name="B">Another point of the line (must be different to A).</param> /// <returns> The distance from p to line segment AB.</returns> public static double DistancePointLine(ICoordinate p, ICoordinate A, ICoordinate B) { // if start == end, then use pt distance if (A.Equals(B)) { return(p.Distance(A)); } // otherwise use comp.graphics.algorithms Frequently Asked Questions method /*(1) AC dot AB * r = --------- ||AB||^2 * * r has the following meaning: * r=0 Point = A * r=1 Point = B * r<0 Point is on the backward extension of AB * r>1 Point is on the forward extension of AB * 0<r<1 Point is interior to AB */ double r = ((p.X - A.X) * (B.X - A.X) + (p.Y - A.Y) * (B.Y - A.Y)) / ((B.X - A.X) * (B.X - A.X) + (B.Y - A.Y) * (B.Y - A.Y)); if (r <= 0.0) { return(p.Distance(A)); } if (r >= 1.0) { return(p.Distance(B)); } /*(2) * (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) * s = ----------------------------- * Curve^2 * * Then the distance from C to Point = |s|*Curve. */ double s = ((A.Y - p.Y) * (B.X - A.X) - (A.X - p.X) * (B.Y - A.Y)) / ((B.X - A.X) * (B.X - A.X) + (B.Y - A.Y) * (B.Y - A.Y)); return(Math.Abs(s) * Math.Sqrt(((B.X - A.X) * (B.X - A.X) + (B.Y - A.Y) * (B.Y - A.Y)))); }
/// <summary> /// Returns <c>true</c> if <c>o</c> has the same values for its points. /// </summary> /// <param name="o">A <c>LineSegment</c> with which to do the comparison.</param> /// <returns> /// <c>true</c> if <c>o</c> is a <c>LineSegment</c> /// with the same values for the x and y ordinates. /// </returns> public override bool Equals(object o) { if (o == null) { return(false); } if (!(o is LineSegment)) { return(false); } LineSegment other = (LineSegment)o; return(p0.Equals(other.p0) && p1.Equals(other.p1)); }
/// <summary> /// /// </summary> /// <param name="dirEdgeList"></param> public void FindEdge(IList dirEdgeList) { /* * Check all forward DirectedEdges only. This is still general, * because each edge has a forward DirectedEdge. */ for (IEnumerator i = dirEdgeList.GetEnumerator(); i.MoveNext();) { DirectedEdge de = (DirectedEdge)i.Current; if (!de.IsForward) { continue; } CheckForRightmostCoordinate(de); } /* * If the rightmost point is a node, we need to identify which of * the incident edges is rightmost. */ Assert.IsTrue(minIndex != 0 || minCoord.Equals(minDe.Coordinate), "inconsistency in rightmost processing"); if (minIndex == 0) { FindRightmostEdgeAtNode(); } else { FindRightmostEdgeAtVertex(); } /* * now check that the extreme side is the R side. * If not, use the sym instead. */ orientedDe = minDe; Positions rightmostSide = GetRightmostSide(minDe, minIndex); if (rightmostSide == Positions.Left) { orientedDe = minDe.Sym; } }
/// <summary> /// Calculates the dyad product. /// </summary> /// <typeparam name="T">The type of coordinate values.</typeparam> /// <param name="point1">The point1.</param> /// <param name="point2">The point2.</param> /// <returns>The result of dyad product operation.</returns> public static BaseDyadCoordinate <T> DyadProduct <T>(this ICoordinate <T> point1, ICoordinate <T> point2) where T : struct { if (point1.Equals(point2)) { return(new SymmetricDyadCoordinate <T>( (dynamic)point1.X * point2.X, (dynamic)point1.X * point2.Y, (dynamic)point1.X * point2.Z, (dynamic)point1.Y * point2.Y, (dynamic)point1.Y * point2.Z, (dynamic)point1.Z * point2.Z)); } return(new DyadCoordinate <T>( (dynamic)point1.X * point2.X, (dynamic)point1.X * point2.Y, (dynamic)point1.X * point2.Z, (dynamic)point1.Y * point2.X, (dynamic)point1.Y * point2.Y, (dynamic)point1.Y * point2.Z, (dynamic)point1.Z * point2.X, (dynamic)point1.Z * point2.Y, (dynamic)point1.Z * point2.Z)); }
/// <summary> /// /// </summary> /// <param name="p1"></param> /// <param name="p2"></param> /// <param name="q1"></param> /// <param name="q2"></param> /// <returns></returns> private int ComputeCollinearIntersection(ICoordinate p1, ICoordinate p2, ICoordinate q1, ICoordinate q2) { bool p1q1p2 = Envelope.Intersects(p1, p2, q1); bool p1q2p2 = Envelope.Intersects(p1, p2, q2); bool q1p1q2 = Envelope.Intersects(q1, q2, p1); bool q1p2q2 = Envelope.Intersects(q1, q2, p2); if (p1q1p2 && p1q2p2) { intPt[0] = q1; intPt[1] = q2; return Collinear; } if (q1p1q2 && q1p2q2) { intPt[0] = p1; intPt[1] = p2; return Collinear; } if (p1q1p2 && q1p1q2) { intPt[0] = q1; intPt[1] = p1; return q1.Equals(p1) && !p1q2p2 && !q1p2q2 ? DoIntersect : Collinear; } if (p1q1p2 && q1p2q2) { intPt[0] = q1; intPt[1] = p2; return q1.Equals(p2) && !p1q2p2 && !q1p1q2 ? DoIntersect : Collinear; } if (p1q2p2 && q1p1q2) { intPt[0] = q2; intPt[1] = p1; return q2.Equals(p1) && !p1q1p2 && !q1p2q2 ? DoIntersect : Collinear; } if (p1q2p2 && q1p2q2) { intPt[0] = q2; intPt[1] = p2; return q2.Equals(p2) && !p1q1p2 && !q1p1q2 ? DoIntersect : Collinear; } return DontIntersect; }
/// <summary> /// Computes the distance from a line segment AB to a line segment CD. /// Note: NON-ROBUST! /// </summary> /// <param name="A">A point of one line.</param> /// <param name="B">The second point of the line (must be different to A).</param> /// <param name="C">One point of the line.</param> /// <param name="D">Another point of the line (must be different to A).</param> /// <returns>The distance from line segment AB to line segment CD.</returns> public static double DistanceLineLine(ICoordinate A, ICoordinate B, ICoordinate C, ICoordinate D) { // check for zero-length segments if (A.Equals(B)) { return(DistancePointLine(A, C, D)); } if (C.Equals(D)) { return(DistancePointLine(D, A, B)); } // AB and CD are line segments /* from comp.graphics.algo * * Solving the above for r and s yields * (Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy) * r = ----------------------------- (eqn 1) * (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx) * * (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) * s = ----------------------------- (eqn 2) * (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx) * Let Point be the position vector of the intersection point, then * Point=A+r(B-A) or * Px=Ax+r(Bx-Ax) * Py=Ay+r(By-Ay) * By examining the values of r & s, you can also determine some other * limiting conditions: * If 0<=r<=1 & 0<=s<=1, intersection exists * r<0 or r>1 or s<0 or s>1 line segments do not intersect * If the denominator in eqn 1 is zero, AB & CD are parallel * If the numerator in eqn 1 is also zero, AB & CD are collinear. * */ double r_top = (A.Y - C.Y) * (D.X - C.X) - (A.X - C.X) * (D.Y - C.Y); double r_bot = (B.X - A.X) * (D.Y - C.Y) - (B.Y - A.Y) * (D.X - C.X); double s_top = (A.Y - C.Y) * (B.X - A.X) - (A.X - C.X) * (B.Y - A.Y); double s_bot = (B.X - A.X) * (D.Y - C.Y) - (B.Y - A.Y) * (D.X - C.X); if ((r_bot == 0) || (s_bot == 0)) { return(Math.Min(DistancePointLine(A, C, D), Math.Min(DistancePointLine(B, C, D), Math.Min(DistancePointLine(C, A, B), DistancePointLine(D, A, B))))); } double s = s_top / s_bot; double r = r_top / r_bot; if ((r < 0) || (r > 1) || (s < 0) || (s > 1)) { //no intersection return(Math.Min(DistancePointLine(A, C, D), Math.Min(DistancePointLine(B, C, D), Math.Min(DistancePointLine(C, A, B), DistancePointLine(D, A, B))))); } return(0.0); //intersection exists }
/// <summary> /// /// </summary> /// <param name="p0"></param> /// <param name="p1"></param> /// <param name="p2"></param> private void CheckCollapse(ICoordinate p0, ICoordinate p1, ICoordinate p2) { if (p0.Equals(p2)) throw new Exception("found non-noded collapse at: " + p0 + ", " + p1 + " " + p2); }
private void CheckCollapse(ICoordinate p0, ICoordinate p1, ICoordinate p2) { if (p0.Equals(p2)) throw new ApplicationException(String.Format( "found non-noded collapse at: {0}, {1} {2}", p0, p1, p2)); }
/// <summary> /// Computes whether a ring defined by an array of <c>Coordinate</c> is /// oriented counter-clockwise. /// This will handle coordinate lists which contain repeated points. /// </summary> /// <param name="ring">an array of coordinates forming a ring.</param> /// <returns> /// <c>true</c> if the ring is oriented counter-clockwise. /// throws <c>ArgumentException</c> if the ring is degenerate (does not contain 3 different points) /// </returns> public static bool IsCCW(ICoordinate[] ring) { // # of points without closing endpoint int nPts = ring.Length - 1; // check that this is a valid ring - if not, simply return a dummy value if (nPts < 4) { return(false); } // algorithm to check if a Ring is stored in CCW order // find highest point ICoordinate hip = ring[0]; int hii = 0; for (int i = 1; i <= nPts; i++) { ICoordinate p = ring[i]; if (p.Y > hip.Y) { hip = p; hii = i; } } // find different point before highest point int iPrev = hii; do { iPrev = (iPrev - 1) % nPts; }while(ring[iPrev].Equals(hip) && iPrev != hii); // find different point after highest point int iNext = hii; do { iNext = (iNext + 1) % nPts; }while (ring[iNext].Equals(hip) && iNext != hii); ICoordinate prev = ring[iPrev]; ICoordinate next = ring[iNext]; if (prev.Equals(hip) || next.Equals(hip) || prev.Equals(next)) { throw new ArgumentException("degenerate ring (does not contain 3 different points)"); } // translate so that hip is at the origin. // This will not affect the area calculation, and will avoid // finite-accuracy errors (i.e very small vectors with very large coordinates) // This also simplifies the discriminant calculation. double prev2x = prev.X - hip.X; double prev2y = prev.Y - hip.Y; double next2x = next.X - hip.X; double next2y = next.Y - hip.Y; // compute cross-product of vectors hip->next and hip->prev // (e.g. area of parallelogram they enclose) double disc = next2x * prev2y - next2y * prev2x; /* If disc is exactly 0, lines are collinear. There are two possible cases: * (1) the lines lie along the x axis in opposite directions * (2) the line lie on top of one another * * (2) should never happen, so we're going to ignore it! * (Might want to assert this) * * (1) is handled by checking if next is left of prev ==> CCW */ if (disc == 0.0) { return(prev.X > next.X); // poly is CCW if prev x is right of next x } else { return(disc > 0.0); // if area is positive, points are ordered CCW } }
/// <summary> /// Compute the projection of a point onto the line determined /// by this line segment. /// Note that the projected point /// may lie outside the line segment. If this is the case, /// the projection factor will lie outside the range [0.0, 1.0]. /// </summary> /// <param name="p"></param> /// <returns></returns> public ICoordinate Project(ICoordinate p) { if (p.Equals(P0) || p.Equals(P1)) return new Coordinate(p); double r = ProjectionFactor(p); ICoordinate coord = new Coordinate(); coord.X = P0.X + r * (P1.X - P0.X); coord.Y = P0.Y + r * (P1.Y - P0.Y); return coord; }
/// <summary> /// This function is non-robust, since it may compute the square of large numbers. /// Currently not sure how to improve this. /// </summary> /// <param name="p"></param> /// <param name="p1"></param> /// <param name="p2"></param> /// <returns></returns> public static double NonRobustComputeEdgeDistance(ICoordinate p, ICoordinate p1, ICoordinate p2) { double dx = p.X - p1.X; double dy = p.Y - p1.Y; double dist = Math.Sqrt(dx * dx + dy * dy); // dummy value Assert.IsTrue(! (dist == 0.0 && ! p.Equals(p1)), "Invalid distance calculation"); return dist; }
/// <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 bool MatchInSameDirection(ICoordinate p0, ICoordinate p1, ICoordinate ep0, ICoordinate ep1) { if (! p0.Equals(ep0)) return false; if (CGAlgorithms.ComputeOrientation(p0, p1, ep1) == CGAlgorithms.Collinear && QuadrantOp.Quadrant(p0, p1) == QuadrantOp.Quadrant(ep0, ep1) ) return true; return false; }
/// <summary> /// Tests whether the segment p0-p1 intersects the hot pixel tolerance square. /// Because the tolerance square point set is partially open (along the /// top and right) the test needs to be more sophisticated than /// simply checking for any intersection. However, it /// can take advantage of the fact that because the hot pixel edges /// do not lie on the coordinate grid. It is sufficient to check /// if there is at least one of: /// - a proper intersection with the segment and any hot pixel edge. /// - an intersection between the segment and both the left and bottom edges. /// - an intersection between a segment endpoint and the hot pixel coordinate. /// </summary> /// <param name="p0"></param> /// <param name="p1"></param> /// <returns></returns> private bool IntersectsToleranceSquare(ICoordinate p0, ICoordinate p1) { bool intersectsLeft = false; bool intersectsBottom = false; li.ComputeIntersection(p0, p1, corner[0], corner[1]); if(li.IsProper) return true; li.ComputeIntersection(p0, p1, corner[1], corner[2]); if(li.IsProper) return true; if(li.HasIntersection) intersectsLeft = true; li.ComputeIntersection(p0, p1, corner[2], corner[3]); if(li.IsProper) return true; if(li.HasIntersection) intersectsBottom = true; li.ComputeIntersection(p0, p1, corner[3], corner[0]); if(li.IsProper) return true; if(intersectsLeft && intersectsBottom) return true; if(p0.Equals(pt)) return true; if(p1.Equals(pt)) return true; return false; }
/// <summary> /// /// </summary> /// <param name="p"></param> /// <param name="l"></param> /// <returns></returns> private Locations Locate(ICoordinate p, ILineString l) { ICoordinate[] pt = l.Coordinates; if(!l.IsClosed) if(p.Equals(pt[0]) || p.Equals(pt[pt.Length - 1])) return Locations.Boundary; if (CGAlgorithms.IsOnLine(p, pt)) return Locations.Interior; return Locations.Exterior; }
/// <summary> /// /// </summary> /// <param name="p0"></param> /// <param name="p1"></param> /// <returns></returns> private bool IsLineSegmentContainedInBoundary(ICoordinate p0, ICoordinate p1) { if (p0.Equals(p1)) return IsPointContainedInBoundary(p0); // we already know that the segment is contained in the rectangle envelope if (p0.X == p1.X) { if (p0.X == rectEnv.MinX || p0.X == rectEnv.MaxX) return true; } else if (p0.Y == p1.Y) { if (p0.Y == rectEnv.MinY || p0.Y == rectEnv.MaxY) return true; } /* * Either both x and y values are different * or one of x and y are the same, but the other ordinate is not the same as a boundary ordinate * In either case, the segment is not wholely in the boundary */ return false; }
/// <summary> /// /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="tolerance"></param> /// <returns></returns> protected bool Equal(ICoordinate a, ICoordinate b, double tolerance) { if (tolerance == 0) return a.Equals(b); return a.Distance(b) <= tolerance; }
/// <summary> /// Tests whether a given point is in an array of points. /// Uses a value-based test. /// </summary> /// <param name="pt">A <c>Coordinate</c> for the test point.</param> /// <param name="pts">An array of <c>Coordinate</c>s to test,</param> /// <returns><c>true</c> if the point is in the array.</returns> public static bool IsInList(ICoordinate pt, ICoordinate[] pts) { for (int i = 0; i < pts.Length; i++) if (pt.Equals(pts[i])) return false; return true; }
/// <summary> /// Compute the projection of a point onto the line determined /// by this line segment. /// Note that the projected point may lie outside the line segment. /// If this is the case, the projection factor will lie outside the range [0.0, 1.0]. /// </summary> /// <param name="p"></param> /// <returns></returns> public ICoordinate Project(ICoordinate p) { if (p.Equals(P0) || p.Equals(P1)) return new Coordinate(p); var r = ProjectionFactor(p); ICoordinate coord = new Coordinate {X = P0.X + r*(P1.X - P0.X), Y = P0.Y + r*(P1.Y - P0.Y)}; return coord; }
/// <summary> /// Computes the distance from a point p to a line segment AB. /// Note: NON-ROBUST! /// </summary> /// <param name="p">The point to compute the distance for.</param> /// <param name="A">One point of the line.</param> /// <param name="B">Another point of the line (must be different to A).</param> /// <returns> The distance from p to line segment AB.</returns> public static double DistancePointLine(ICoordinate p, ICoordinate A, ICoordinate B) { // if start == end, then use pt distance if (A.Equals(B)) return p.Distance(A); // otherwise use comp.graphics.algorithms Frequently Asked Questions method /*(1) AC dot AB r = --------- ||AB||^2 r has the following meaning: r=0 Point = A r=1 Point = B r<0 Point is on the backward extension of AB r>1 Point is on the forward extension of AB 0<r<1 Point is interior to AB */ double r = ( (p.X - A.X) * (B.X - A.X) + (p.Y - A.Y) * (B.Y - A.Y) ) / ( (B.X - A.X) * (B.X - A.X) + (B.Y - A.Y) * (B.Y - A.Y) ); if (r <= 0.0) return p.Distance(A); if (r >= 1.0) return p.Distance(B); /*(2) (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) s = ----------------------------- Curve^2 Then the distance from C to Point = |s|*Curve. */ double s = ( (A.Y - p.Y) * (B.X - A.X) - (A.X - p.X) * (B.Y - A.Y) ) / ( (B.X - A.X) * (B.X - A.X) + (B.Y - A.Y) * (B.Y - A.Y) ); return Math.Abs(s) * Math.Sqrt(((B.X - A.X) * (B.X - A.X) + (B.Y - A.Y) * (B.Y - A.Y))); }
/// <summary> /// Returns the edge whose first two coordinates are p0 and p1. /// </summary> /// <param name="p0"></param> /// <param name="p1"></param> /// <returns> The edge, if found <c>null</c> if the edge was not found.</returns> public Edge FindEdge(ICoordinate p0, ICoordinate p1) { for (int i = 0; i < edges.Count; i++) { Edge e = (Edge) edges[i]; ICoordinate[] eCoord = e.Coordinates; if (p0.Equals(eCoord[0]) && p1.Equals(eCoord[1])) return e; } return null; }
/// <summary> /// Compute the projection factor for the projection of the point p /// onto this <c>LineSegment</c>. The projection factor is the constant k /// by which the vector for this segment must be multiplied to /// equal the vector for the projection of p. /// </summary> /// <param name="p"></param> /// <returns></returns> public double ProjectionFactor(ICoordinate p) { if (p.Equals(P0)) return 0.0; if (p.Equals(P1)) return 1.0; // Otherwise, use comp.graphics.algorithms Frequently Asked Questions method /* AC dot AB r = ------------ ||AB||^2 r has the following meaning: r=0 Point = A r=1 Point = B r<0 Point is on the backward extension of AB r>1 Point is on the forward extension of AB 0<r<1 Point is interior to AB */ double dx = P1.X - P0.X; double dy = P1.Y - P0.Y; double len2 = dx * dx + dy * dy; double r = ((p.X - P0.X) * dx + (p.Y - P0.Y) * dy) / len2; return r; }