diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/LineSegment.java b/modules/core/src/main/java/org/locationtech/jts/geom/LineSegment.java index 4a608f1bb1..15e38b5302 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/LineSegment.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/LineSegment.java @@ -505,9 +505,23 @@ public Coordinate reflect(Coordinate p) { */ public Coordinate closestPoint(Coordinate p) { - double factor = projectionFactor(p); - if (factor > 0 && factor < 1) { - return project(p, factor); + return closestPoint(p, false); + } + + /** + * Computes the closest point on this line segment to another point. + * @param p the point to find the closest point to + * @param skip0 If true, do not check p0. Client will have determined this is redundant. + * @return a Coordinate which is the closest point on the line segment to the point p + */ + public Coordinate closestPoint(Coordinate p, boolean skip0) + { + Coordinate orthog = orthogonalPoint(p); + if (orthog != null) { + return orthog; + } + if (skip0) { + return p1; } double dist0 = p0.distance(p); double dist1 = p1.distance(p); @@ -515,6 +529,25 @@ public Coordinate closestPoint(Coordinate p) return p0; return p1; } + + /** + * Computes the orthogonal point on this line segment to another point. + * @param p the point to find the closest point to + * @return a Coordinate which is the orthogonal point on the line segment to the point p. Null if it does not exist. + */ + public Coordinate orthogonalPoint(Coordinate p) + { + double factor = projectionFactor(p); + if (factor == 0.0) { + return p0; + } else if (factor == 1.0) { + return p1; + } else if (factor > 0 && factor < 1) { + return project(p, factor); + } else { + return null; + } + } /** * Computes the closest points on two line segments. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/distance/DistanceOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/distance/DistanceOp.java index 34542df972..e396ee8856 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/distance/DistanceOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/distance/DistanceOp.java @@ -428,7 +428,7 @@ private void computeMinDistance(LineString line, Point pt, if (dist < minDistance) { minDistance = dist; LineSegment seg = new LineSegment(coord0[i], coord0[i + 1]); - Coordinate segClosestPoint = seg.closestPoint(coord); + Coordinate segClosestPoint = seg.closestPoint(coord, i > 0); locGeom[0] = new GeometryLocation(line, i, segClosestPoint); locGeom[1] = new GeometryLocation(pt, 0, coord); } diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/LineSegmentTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/LineSegmentTest.java index ce6c42393a..0baf6e5e73 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/LineSegmentTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/LineSegmentTest.java @@ -53,6 +53,37 @@ public void testProjectionFactor() assertTrue(seg2.projectionFactor(new Coordinate(11, 0)) == 0.1); } + public void testOrthogonalPoint() + { + Coordinate l0 = new Coordinate(0, 0); + Coordinate l1 = new Coordinate(10, 10); + LineSegment seg = new LineSegment(l0, l1); + + Coordinate orth = seg.orthogonalPoint(new Coordinate(0, 0)); + assertTrue(orth.equals(l0)); + + orth = seg.orthogonalPoint(new Coordinate(10, 10)); + assertTrue(orth.equals(l1)); + + orth = seg.orthogonalPoint(new Coordinate(10, 0)); + assertTrue(orth.equals(new Coordinate(5, 5))); + + orth = seg.orthogonalPoint(new Coordinate(0, 10)); + assertTrue(orth.equals(new Coordinate(5, 5))); + + orth = seg.orthogonalPoint(new Coordinate(10, -10)); + assertTrue(orth.equals(l0)); + + orth = seg.orthogonalPoint(new Coordinate(-10, 10)); + assertTrue(orth.equals(l0)); + + orth = seg.orthogonalPoint(new Coordinate(11, 10)); + assertNull(orth); + + orth = seg.orthogonalPoint(new Coordinate(-1, 0)); + assertNull(orth); + } + public void testLineIntersection() { // simple case checkLineIntersection(