package com.revolsys.geometry.model.coordinates; import java.util.Set; import java.util.TreeSet; import com.revolsys.geometry.algorithm.LineIntersector; import com.revolsys.geometry.algorithm.RobustDeterminant; import com.revolsys.geometry.algorithm.RobustLineIntersector; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.geometry.model.LineString; import com.revolsys.geometry.model.Point; import com.revolsys.geometry.model.coordinates.comparator.CoordinatesDistanceComparator; import com.revolsys.geometry.model.coordinates.list.CoordinatesListUtil; import com.revolsys.geometry.model.impl.LineStringDouble; import com.revolsys.geometry.model.impl.PointDouble; import com.revolsys.geometry.model.impl.PointDoubleXY; import com.revolsys.geometry.model.impl.PointDoubleXYZ; import com.revolsys.geometry.util.BoundingBoxUtil; import com.revolsys.math.Angle; import com.revolsys.util.MathUtil; import com.revolsys.util.number.Doubles; public class LineSegmentUtil { public static Point closestPoint(final double x1, final double y1, final double x2, final double y2, final double x, final double y) { final double factor = projectionFactor(x1, y1, x2, y2, x, y); if (factor > 0 && factor < 1) { return project(x1, y1, x2, y2, factor); } final double dist0 = MathUtil.distance(x1, y1, x, y); final double dist1 = MathUtil.distance(x2, y2, x, y); if (dist0 < dist1) { return new PointDoubleXY(x1, y1); } return new PointDoubleXY(x2, y2); } public static Point closestPoint(final Point lineStart, final Point lineEnd, final Point point) { final double factor = projectionFactor(lineStart, lineEnd, point); if (factor > 0 && factor < 1) { return project(null, lineStart, lineEnd, point); } final double dist0 = lineStart.distancePoint(point); final double dist1 = lineEnd.distancePoint(point); if (dist0 < dist1) { return lineStart; } return lineEnd; } public static double det(final double a, final double b, final double c, final double d) { return a * d - b * c; } /** * Computes the distance from a line segment AB to a line segment CD * * Note: NON-ROBUST! * * @param A * a point of one line * @param B * the second point of (must be different to A) * @param C * one point of the line * @param D * another point of the line (must be different to A) */ public static double distanceLineLine(final double line1x1, final double line1y1, final double line1x2, final double line1y2, final double line2x1, final double line2y1, final double line2x2, final double line2y2) { // check for zero-length segments if (line1x1 == line1x2 && line1y1 == line1y2) { return distanceLinePoint(line2x1, line2y1, line2x2, line2y2, line1x1, line1y1); } else if (line2x1 == line2x2 && line2y1 == line2y2) { return distanceLinePoint(line1x1, line1y1, line1x2, line1y2, line2x1, line2y1); } else { // 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 * P be the position vector of the intersection point, then P=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. */ boolean noIntersection = false; if (!BoundingBoxUtil.intersectsMinMax(line1x1, line1y1, line1x2, line1y2, line2x1, line2y1, line2x2, line2y2)) { noIntersection = true; } else { final double denom = (line1x2 - line1x1) * (line2y2 - line2y1) - (line1y2 - line1y1) * (line2x2 - line2x1); if (denom == 0) { noIntersection = true; } else { final double r_num = (line1y1 - line2y1) * (line2x2 - line2x1) - (line1x1 - line2x1) * (line2y2 - line2y1); final double s_num = (line1y1 - line2y1) * (line1x2 - line1x1) - (line1x1 - line2x1) * (line1y2 - line1y1); final double s = s_num / denom; final double r = r_num / denom; if (r < 0 || r > 1 || s < 0 || s > 1) { noIntersection = true; } } } if (noIntersection) { final double distance1 = distanceLinePoint(line2x1, line2y1, line2x2, line2y2, line1x1, line1y1); final double distance2 = distanceLinePoint(line2x1, line2y1, line2x2, line2y2, line1x2, line1y2); final double distance3 = distanceLinePoint(line1x1, line1y1, line1x2, line1y2, line2x1, line2y1); final double distance4 = distanceLinePoint(line1x1, line1y1, line1x2, line1y2, line2x2, line2y2); return MathUtil.min(distance1, distance2, distance3, distance4); } else { // segments intersect return 0.0; } } } public static double distanceLineLine(final Point line1From, final Point line1To, final Point line2From, final Point line2To) { final double line1x1 = line1From.getX(); final double line1y1 = line1From.getY(); final double line1x2 = line1To.getX(); final double line1y2 = line1To.getY(); final double line2x1 = line2From.getX(); final double line2y1 = line2From.getY(); final double line2x2 = line2To.getX(); final double line2y2 = line2To.getY(); return distanceLineLine(line1x1, line1y1, line1x2, line1y2, line2x1, line2y1, line2x2, line2y2); } /** * Calculate the distance between the line from x1,y1 to x2,y2 and the point * x,y. * * @param x1 The x coordinate at the start of the line. * @param y1 The y coordinate at the start of the line. * @param x2 The x coordinate at the end of the line. * @param y2 The y coordinate at the end of the line. * @param x The x coordinate of the point. * @param y The y coordinate of the point. * @return The distance. */ public static double distanceLinePoint(final double x1, final double y1, final double x2, final double y2, final double x, final double y) { if (x1 == x2 && y1 == y2) { return MathUtil.distance(x, y, x1, y1); } else { final double dxx1 = x - x1; final double dx2x1 = x2 - x1; final double dyy1 = y - y1; final double dy2y1 = y2 - y1; final double d2x1sq = dx2x1 * dx2x1; final double dy2y1sq = dy2y1 * dy2y1; final double r = (dxx1 * dx2x1 + dyy1 * dy2y1) / (d2x1sq + dy2y1sq); if (r <= 0.0) { return MathUtil.distance(x, y, x1, y1); } else if (r >= 1.0) { return MathUtil.distance(x, y, x2, y2); } else { final double dy1y = y1 - y; final double dx1x = x1 - x; final double s = (dy1y * dx2x1 - dx1x * dy2y1) / (d2x1sq + dy2y1sq); return Math.abs(s) * Math.sqrt(d2x1sq + dy2y1sq); } } } public static double distanceLinePoint(final int x1, final int y1, final int x2, final int y2, final int x, final int y) { if (x1 == x2 && y1 == y2) { return MathUtil.distanceInt(x, y, x1, y1); } else { final long dxx1 = x - x1; final long dx2x1 = x2 - x1; final long dyy1 = y - y1; final long dy2y1 = y2 - y1; final long d2x1sq = dx2x1 * dx2x1; final long dy2y1sq = dy2y1 * dy2y1; final double ratio = (dxx1 * dx2x1 + dyy1 * dy2y1) / (d2x1sq + dy2y1sq); if (ratio <= 0.0) { return MathUtil.distanceInt(x, y, x1, y1); } else if (ratio >= 1.0) { return MathUtil.distanceInt(x, y, x2, y2); } else { final long dy1y = y1 - y; final int dx1x = x1 - x; final double s = (dy1y * dx2x1 - dx1x * dy2y1) / (d2x1sq + dy2y1sq); return Math.abs(s) * Math.sqrt(d2x1sq + dy2y1sq); } } } /** * Calculate the distance between the line from lineStart to lineEnd and the * point. * * @param lineStart The point at the start of the line. * @param lineEnd The point at the end of the line. * @param point The point. * @param point The coordinates of the point location. * @return The distance. */ public static double distanceLinePoint(final Point lineStart, final Point lineEnd, final Point point) { final double x1 = lineStart.getX(); final double y1 = lineStart.getY(); final double x2 = lineEnd.getX(); final double y2 = lineEnd.getY(); final double x = point.getX(); final double y = point.getY(); return distanceLinePoint(x1, y1, x2, y2, x, y); } /** * Computes the perpendicular distance from a point p to the (infinite) line * containing the points AB * * @param x The x coordinate of the point p to compute the distance for * @param Y The y coordinate of the point p to compute the distance for * @param x1 The x coordinate of the one point A of the line * @param y1 The y coordinate of the one point A of the line * @param x2 The x coordinate of the another point B of the line (must be different to A) * @param 72 The y coordinate of the another point B of the line (must be different to A) * @return the distance from p to line AB */ public static double distancePointLinePerpendicular(final double x, final double y, final double x1, final double y1, final double x2, final double y2) { // use comp.graphics.algorithms Frequently Asked Questions method /* * (2) s = (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) ----------------------------- L^2 * Then the distance from C to P = |s|*L. */ final double len2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); final double s = ((y1 - y) * (x2 - x1) - (x1 - x) * (y2 - y1)) / len2; return Math.abs(s) * Math.sqrt(len2); } /** * Computes the perpendicular distance from a point p to the (infinite) line * containing the points AB * * @param p * the point to compute the distance for * @param A * one point of the line * @param B * another point of the line (must be different to A) * @return the distance from p to line AB */ public static double distancePointLinePerpendicular(final Point p, final Point A, final Point B) { final double x = p.getX(); final double y = p.getY(); final double x1 = A.getX(); final double y1 = A.getY(); final double x2 = B.getX(); final double y2 = B.getY(); return distancePointLinePerpendicular(x, y, x1, y1, x2, y2); } /** * Check to see if the point (x,y) intersects the envelope of the line from * (x1,y1) to (x2,y2). * * @param x1 The x coordinate at the start of the line. * @param y1 The y coordinate at the start of the line. * @param x2 The x coordinate at the end of the line. * @param y2 The y coordinate at the end of the line. * @param x The x coordinate of the point. * @param y The y coordinate of the point. * @return True if the point intersects the line's envelope. */ public static boolean envelopeIntersects(final double x1, final double y1, final double x2, final double y2, final double x, final double y) { final double minX = Math.min(x1, x2); if (x >= minX) { final double maxX = Math.max(x1, x2); if (x <= maxX) { final double minY = Math.min(y1, y2); if (y >= minY) { final double maxY = Math.max(y1, y2); if (y <= maxY) { return true; } } } } return false; } /** * Check to see if the point intersects the envelope of the line from * lineStart to lineEnd. * * @param lineStart The point at the start of the line. * @param lineEnd The point at the end of the line. * @param point The point. * @return True if the point intersects the line's envelope. */ public static boolean envelopeIntersects(final Point lineStart, final Point lineEnd, final Point point) { final double x1 = lineStart.getX(); final double y1 = lineStart.getY(); final double x2 = lineEnd.getX(); final double y2 = lineEnd.getY(); final double x = point.getX(); final double y = point.getY(); return envelopeIntersects(x1, y1, x2, y2, x, y); } /** * Check to see if the envelope one the line from line1Start to line1End * intersects the envelope of the line from line2Start to line2End. * * @param line1Start The point at the start of the first line. * @param line1End The point at the end of the first line. * @param line2Start The point at the start of the second line. * @param line2End The point at the end of the second line. * @return True if the envelope of line intersects the envelope of line 2. */ public static boolean envelopeIntersects(final Point line1Start, final Point line1End, final Point line2Start, final Point line2End) { final double line1x1 = line1Start.getX(); final double line1x2 = line1End.getX(); final double line2x1 = line2Start.getX(); final double line2x2 = line2End.getX(); final double max1X = Math.max(line1x1, line1x2); final double min2X = Math.min(line2x1, line2x2); if (min2X <= max1X) { final double min1X = Math.min(line1x1, line1x2); final double max2X = Math.max(line2x1, line2x2); if (min1X <= max2X) { final double line1y1 = line1Start.getY(); final double line1y2 = line1End.getY(); final double line2y1 = line2Start.getY(); final double line2y2 = line2End.getY(); final double max1Y = Math.max(line1y1, line1y2); final double min2Y = Math.min(line2y1, line2y2); if (min2Y <= max1Y) { final double min1Y = Math.min(line1y1, line1y2); final double max2Y = Math.max(line2y1, line2y2); if (min1Y <= max2Y) { return true; } } } } return false; } public static Point getElevation(final GeometryFactory geometryFactory, final Point lineStart, final Point lineEnd, final Point point) { final int axisCount = geometryFactory.getAxisCount(); final double[] coordinates = point.getCoordinates(); if (axisCount > 2) { final double fraction = point.distancePoint(lineStart) / lineStart.distancePoint(lineEnd); double z1 = lineStart.getZ(); if (Double.isNaN(z1)) { z1 = 0; } double z2 = lineEnd.getZ(); if (Double.isNaN(z2)) { z2 = 0; } final double z = z1 + (z2 - z1) * fraction; if (coordinates.length < 3) { return geometryFactory.point(coordinates[0], coordinates[1], z); } else { coordinates[2] = z; return geometryFactory.point(coordinates); } } else { return point; } } public static double getElevation(final Point lineStart, final Point lineEnd, final Point point) { final double fraction = point.distancePoint(lineStart) / lineStart.distancePoint(lineEnd); final double z = lineStart.getZ() + (lineEnd.getZ() - lineStart.getZ()) * fraction; return z; } /** * Get the intersection between line (segment) 1 and line (segment) 2. The * result will be either and empty collection, a single coordinates value for * a crosses intersection or a pair of coordinates for a linear intersection. * The results will be rounded according to the precision model. Any z-value * interpolation will be calculated using the z-values from line (segment) 1. * For linear intersections the order of the points will be the same as the * orientation of line1. * * @param geometryFactory * @param line1Start * @param line1End * @param line2Start * @param line2End * @return */ public static LineString getIntersection(final GeometryFactory geometryFactory, Point line1Start, Point line1End, Point line2Start, Point line2End) { line1Start = line1Start.convertGeometry(geometryFactory); line1End = line1End.convertGeometry(geometryFactory); line2Start = line2Start.convertGeometry(geometryFactory); line2End = line2End.convertGeometry(geometryFactory); if (BoundingBoxUtil.intersects(line1Start, line1End, line2Start, line2End)) { final Set<Point> intersections = new TreeSet<>( new CoordinatesDistanceComparator(line1Start.getX(), line1Start.getY())); if (LineSegmentUtil.isPointOnLine(geometryFactory, line2Start, line2End, line1Start)) { intersections.add(line1Start); } if (LineSegmentUtil.isPointOnLine(geometryFactory, line2Start, line2End, line1End)) { intersections.add(line1End); } if (LineSegmentUtil.isPointOnLine(geometryFactory, line1Start, line1End, line2Start)) { final Point intersection = getElevation(geometryFactory, line1Start, line1End, line2Start); intersections.add(intersection); } if (LineSegmentUtil.isPointOnLine(geometryFactory, line1Start, line1End, line2End)) { final Point intersection = getElevation(geometryFactory, line1Start, line1End, line2End); intersections.add(intersection); } if (intersections.isEmpty()) { final double line1x1 = line1Start.getX(); final double line1y1 = line1Start.getY(); final double line1x2 = line1End.getX(); final double line1y2 = line1End.getY(); final double line2x1 = line2Start.getX(); final double line2y1 = line2Start.getY(); final double line2x2 = line2End.getX(); final double line2y2 = line2End.getY(); final int Pq1 = CoordinatesListUtil.orientationIndex(line1x1, line1y1, line1x2, line1y2, line2x1, line2y1); final int Pq2 = CoordinatesListUtil.orientationIndex(line1x1, line1y1, line1x2, line1y2, line2x2, line2y2); if (!(Pq1 > 0 && Pq2 > 0 || Pq1 < 0 && Pq2 < 0)) { final int Qp1 = CoordinatesListUtil.orientationIndex(line2x1, line2y1, line2x2, line2y2, line1x1, line1y1); final int Qp2 = CoordinatesListUtil.orientationIndex(line2x1, line2y1, line2x2, line2y2, line1x2, line1y2); if (!(Qp1 > 0 && Qp2 > 0 || Qp1 < 0 && Qp2 < 0)) { final double detLine1StartLine1End = LineSegmentUtil.det(line1x1, line1y1, line1x2, line1y2); final double detLine2StartLine2End = LineSegmentUtil.det(line2x1, line2y1, line2x2, line2y2); final double x = LineSegmentUtil.det(detLine1StartLine1End, line1x1 - line1x2, detLine2StartLine2End, line2x1 - line2x2) / LineSegmentUtil.det(line1x1 - line1x2, line1y1 - line1y2, line2x1 - line2x2, line2y1 - line2y2); final double y = LineSegmentUtil.det(detLine1StartLine1End, line1y1 - line1y2, detLine2StartLine2End, line2y1 - line2y2) / LineSegmentUtil.det(line1x1 - line1x2, line1y1 - line1y2, line2x1 - line2x2, line2y1 - line2y2); Point intersection = geometryFactory.point(x, y); intersection = getElevation(geometryFactory, line1Start, line1End, intersection); final LineStringDouble points = new LineStringDouble(geometryFactory.getAxisCount(), intersection); return points; } } } else { return geometryFactory.lineString(intersections); } } return geometryFactory.lineString(); } public static boolean intersects(final Point line1p1, final Point line1p2, final Point line2p1, final Point line2p2) { final LineIntersector li = new RobustLineIntersector(); li.computeIntersectionPoints(line1p1, line1p2, line2p1, line2p2); return li.hasIntersection(); } public static boolean isPointOnLine(final double x1, final double y1, final double x2, final double y2, final double x, final double y, final double maxDistance) { if (Doubles.equal(x, x1) && Doubles.equal(y, y1)) { return true; } else if (Doubles.equal(x, x2) && Doubles.equal(y, y2)) { return true; } else { final double distance = distanceLinePoint(x1, y1, x2, y2, x, y); if (distance < maxDistance) { final double projectionFactor = projectionFactor(x1, y1, x2, y2, y, y); if (projectionFactor >= 0.0 && projectionFactor <= 1.0) { return true; } } return false; } } public static boolean isPointOnLine(final GeometryFactory precisionModel, final double x1, final double y1, final double x2, final double y2, final double x, final double y) { if (Doubles.equal(x, x1) && Doubles.equal(y, y1)) { return true; } else if (Doubles.equal(x, x2) && Doubles.equal(y, y2)) { return true; } else { final double projectionFactor = projectionFactor(x1, y1, x2, y2, y, y); if (projectionFactor >= 0.0 && projectionFactor <= 1.0) { double newX = x1 + projectionFactor * (x2 - x1); double newY = y1 + projectionFactor * (y2 - y1); if (precisionModel != null) { newX = precisionModel.makeXyPrecise(newX); newY = precisionModel.makeXyPrecise(newY); } if (x == newX && y == newY) { return true; } } return false; } } /** * Check to see if the point is on the line between lineStart and lineEnd * using the precision model to see if a line split at the projection of the * point on the line would be the same point. * * @param precisionModel The precision model. * @param lineStart The point at the start of the line. * @param lineEnd The point at the end of the line. * @param point The point. * @return True if the point is on the line. */ public static boolean isPointOnLine(final GeometryFactory precisionModel, final Point lineStart, final Point lineEnd, final Point point) { if (lineStart.equals(2, point)) { return true; } else if (lineEnd.equals(2, point)) { return true; } else { final double projectionFactor = projectionFactor(lineStart, lineEnd, point); if (projectionFactor >= 0.0 && projectionFactor <= 1.0) { Point projectedPoint = project(2, lineStart, lineEnd, projectionFactor); if (precisionModel != null) { projectedPoint = precisionModel.getPreciseCoordinates(projectedPoint); } if (projectedPoint.equals(2, point)) { return true; } } return false; } } public static boolean isPointOnLine(final Point lineStart, final Point lineEnd, final Point point, final double maxDistance) { if (lineStart.equals(2, point)) { return true; } else if (lineEnd.equals(2, point)) { return true; } else { final double distance = distanceLinePoint(lineStart, lineEnd, point); if (distance < maxDistance) { final double projectionFactor = projectionFactor(lineStart, lineEnd, point); if (projectionFactor >= 0.0 && projectionFactor <= 1.0) { return true; } } return false; } } public static boolean isPointOnLineMiddle(final GeometryFactory precisionModel, final Point lineStart, final Point lineEnd, final Point point) { if (point.equals(2, lineStart)) { return false; } else if (point.equals(2, lineEnd)) { return false; } else { final double projectionFactor = projectionFactor(lineStart, lineEnd, point); if (projectionFactor >= 0.0 && projectionFactor <= 1.0) { Point projectedPoint = project(2, lineStart, lineEnd, projectionFactor); projectedPoint = precisionModel.getPreciseCoordinates(projectedPoint); if (projectedPoint.equals(2, point)) { return true; } } return false; } } public static Point midPoint(final GeometryFactory precisionModel, final Point lineStart, final Point lineEnd) { return project(precisionModel, lineStart, lineEnd, 0.5); } public static Point midPoint(final Point lineStart, final Point lineEnd) { return midPoint(null, lineStart, lineEnd); } public static int orientationIndex(final double x1, final double y1, final double x2, final double y2, final double x, final double y) { final double lineDx = x2 - x1; final double lineDy = y2 - y1; final double dx2 = x - x2; final double dy2 = y - y2; return RobustDeterminant.signOfDet2x2(lineDx, lineDy, dx2, dy2); } public static int orientationIndex(final Point lineStart, final Point lineEnd, final Point point) { final double x1 = lineStart.getX(); final double y1 = lineStart.getY(); final double x2 = lineEnd.getX(); final double y2 = lineEnd.getY(); final double x = point.getX(); final double y = point.getY(); return orientationIndex(x1, y1, x2, y2, x, y); } /** * Calculate the counter clockwise angle in radians of the difference between * the two vectors from the start point and line1End and line2End. The angle * is relative to the vector from start to line1End. The angle will be in the * range 0 -> 2 * PI. * * @return The angle in radians. */ public static double orientedAngleBetween2d(final Point start, final Point line1End, final Point line2End) { final double angle1 = start.angle2d(line1End); final double angle2 = start.angle2d(line2End); return Angle.angleBetweenOriented(angle1, angle2); } public static Point pointAlong(final GeometryFactory precisionModel, final Point lineStart, final Point lineEnd, final Point point) { final double projectionFactor = projectionFactor(lineStart, lineEnd, point); if (projectionFactor < 0.0) { return lineStart; } else if (projectionFactor > 1.0) { return lineEnd; } else { return project(precisionModel, lineStart, lineEnd, projectionFactor); } } public static Point pointAlong(final Point p0, final Point p1, final double segmentLengthFraction) { final double x1 = p0.getX(); final double x2 = p1.getX(); final double y1 = p0.getY(); final double y2 = p1.getY(); final double x = x1 + segmentLengthFraction * (x2 - x1); final double y = y1 + segmentLengthFraction * (y2 - y1); return new PointDoubleXY(x, y); } public static Point pointAlongSegmentByFraction(final double x1, final double y1, final double x2, final double y2, final double fraction) { if (fraction <= 0.0) { return new PointDoubleXY(x1, y1); } else if (fraction >= 1.0) { return new PointDoubleXY(x2, y2); } else { final double x = (x2 - x1) * fraction + x1; final double y = (y2 - y1) * fraction + y1; return new PointDoubleXY(x, y); } } public static Point project(final double x1, final double y1, final double x2, final double y2, final double r) { final double x = x1 + r * (x2 - x1); final double y = y1 + r * (y2 - y1); return new PointDoubleXY(x, y); } public static Point project(final GeometryFactory geometryFactory, final Point lineStart, final Point lineEnd, final double r) { final int axisCount = CoordinatesUtil.getAxisCount(lineStart, lineEnd); final Point point = project(axisCount, lineStart, lineEnd, r); if (geometryFactory != null) { return geometryFactory.getPreciseCoordinates(point); } return point; } public static Point project(final GeometryFactory geometryFactory, final Point lineStart, final Point lineEnd, final Point point) { if (point.equals(2, lineStart) || point.equals(2, lineEnd)) { return point; } else { final double r = projectionFactor(lineStart, lineEnd, point); final int axisCount = CoordinatesUtil.getAxisCount(point, lineStart, lineEnd); Point projectedPoint = project(axisCount, lineStart, lineEnd, r); if (geometryFactory != null) { projectedPoint = geometryFactory.getPreciseCoordinates(projectedPoint); } if (projectedPoint.equals(2, lineStart)) { return lineStart; } else if (projectedPoint.equals(2, lineEnd)) { return lineEnd; } else { if (axisCount > 2) { final double z = projectedPoint.getZ(); if (!Double.isFinite(z) || z == 0) { final double[] coordinates = projectedPoint.getCoordinates(axisCount); for (int axisIndex = 2; axisIndex < axisCount; axisIndex++) { coordinates[axisIndex] = point.getCoordinate(axisIndex); } if (geometryFactory == null) { return new PointDouble(coordinates); } else { return geometryFactory.point(coordinates); } } } return projectedPoint; } } } public static Point project(final int axisCount, final Point lineStart, final Point lineEnd, final double r) { final double x1 = lineStart.getX(); final double y1 = lineStart.getY(); final double z1 = lineStart.getZ(); final double x2 = lineEnd.getX(); final double y2 = lineEnd.getY(); final double z2 = lineEnd.getZ(); final double x = x1 + r * (x2 - x1); final double y = y1 + r * (y2 - y1); if (axisCount == 2) { return new PointDoubleXY(x, y); } else { double z; if (Double.isFinite(z1) && Double.isFinite(z2)) { z = z1 + r * (z2 - z1); return new PointDoubleXYZ(x, y, z); } else { return new PointDoubleXY(x, y); } } } /** * Calculate the projection factor of the distance of the point (x,y) * coordinates along the line (x1,y1 -> x2,y2). If the point is within the * line the range will be between 0.0 -> 1.0. * * @param x1 The x coordinate for the start of the line. * @param y1 The y coordinate for the start of the line. * @param x2 The x coordinate for the end of the line. * @param y2 The y coordinate for the end of the line. * @param x The x coordinate for the point. * @param y The y coordinate for the point. * @return The projection factor from (-inf -> +inf). */ public static double projectionFactor(final double x1, final double y1, final double x2, final double y2, final double x, final double y) { final double dx = x2 - x1; final double dy = y2 - y1; final double length = dx * dx + dy * dy; final double r = ((x - x1) * dx + (y - y1) * dy) / length; return r; } /** * Calculate the projection factor of the distance of the point coordinates * along the line. If the point is within the line the range will be between * 0.0 -> 1.0. * * @param lineStart The start coordinates of the line. * @param lineEnd The end coordinates of the line. * @param point The point coordinates. * @return The projection factor from (-inf -> +inf). */ public static double projectionFactor(final Point lineStart, final Point lineEnd, final Point point) { final double x1 = lineStart.getX(); final double y1 = lineStart.getY(); final double x2 = lineEnd.getX(); final double y2 = lineEnd.getY(); final double x = point.getX(); final double y = point.getY(); return projectionFactor(x1, y1, x2, y2, x, y); } public static double segmentFraction(final Point lineStart, final Point lineEnd, final Point point) { final double segFrac = projectionFactor(lineStart, lineEnd, point); if (segFrac < 0.0) { return 0.0; } else if (segFrac > 1.0) { return 1.0; } else { return segFrac; } } public static double segmentFractionOnLine(final double x1, final double y1, final double x2, final double y2, final double x, final double y) { double segmentFraction = LineSegmentUtil.projectionFactor(x1, y1, x2, y2, x, y); if (segmentFraction < 0.0) { segmentFraction = 0.0; } else if (segmentFraction > 1.0) { segmentFraction = 1.0; } return segmentFraction; } }