4

Given a point and a line, I am trying to determine the point on the line that is closest to the given point. The given point is always at least fairly close to the line (3-4 meters max)

Originally I was using the nearestPointOnLine function from the turf JS library. However, that gives inaccurate results, especially for long lines: https://github.com/Turfjs/turf/issues/1440

Another approach I tried was using ST_ClosestPoint via postgresql:

WITH geoms AS (
  SELECT
    ST_MakeLine(ST_MakePoint (- 86.26725, 34.32582) , ST_MakePoint (- 86.14040, 34.39281)) AS line
    , ST_MakePoint (- 86.19219 , 34.36546) AS point
)
select
    st_distance(point::geography, line::geography) AS i
    , st_distance(st_closestpoint(line, point)::geography, line::geography) AS closest_point_distance
    , st_dwithin(st_closestpoint(line, point)::geography, line::geography, 0.1) AS closest_point_is_close_to_line
from
    geoms
original_distance closest_point_distance closest_point_is_close_to_line
2.64173838 2.68938954 false

This runs into the rounding issue described here: point returned from ST_LineLocatePoint returns false in ST_Contains

At this point I am considering getting a small line segment close to the point from PostGIS, then getting the closest point on that line with turf. But that seems very involved, and I am wondering if I am missing an easy way (via PostGIS or JavaScript) to get the nearest point (to a close by point) on a long line.

1
  • 5cm is a lot when considering rounding issue... one would expect it more like 10^-6 or smaller – JGH Apr 22 at 14:51
5

The >0 distance returned by the query is actually correct.

Let's remember that when using the type geometry, distances are computed using a straight line while with the type geography, the great circle line is used.

So, your data is in geometry and you compute the closest point using a straight line (between the start and end point of the line). The closest point is on the line.

Then, you cast to geography the computed point and the line. The point doesn't move, but the line is now a great circle and the point is not on the line anymore.

Try running the query using geometries only. The closest point is indeed on the line.

WITH geoms AS (
  SELECT
    ST_MakeLine(ST_MakePoint (- 86.26725, 34.32582) , ST_MakePoint (- 86.14040, 34.39281)) AS line
    , ST_MakePoint (- 86.19219 , 34.36546) AS point
)
select
    st_distance(point, line) AS i
    , st_distance(st_closestpoint(line, point), line) AS closest_point_distance
from
    geoms;
           i            | closest_point_distance
------------------------+------------------------
 4.5032368018051133e-07 |                      0
(1 row)
0
2

If you segmentize (or densify) your line then it will work better.

SELECT
    ST_Segmentize(ST_MakeLine(ST_MakePoint (- 86.26725, 34.32582) , ST_MakePoint (- 86.14040, 34.39281)),.01) AS line
    , ST_MakePoint (- 86.19219 , 34.36546) AS point
)
select
    st_distance(point::geography, line::geography) AS i
    , st_distance(st_closestpoint(line, point)::geography, line::geography) AS closest_point_distance
    , st_dwithin(st_closestpoint(line, point)::geography, line::geography, 0.1) AS closest_point_is_close_to_line
from
    geoms;

Gives me:

     i     | closest_point_distance | closest_point_is_close_to_line 
-----------+------------------------+--------------------------------
 0.0341319 |             0.01351816 | t

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.