Repository navigation
[GH-3519] Interpolate ST_InterpolatePoint measures within the closest segment - #3520
Conversation
ST_InterpolatePoint took the closest point's fraction of the total line length and interpolated between the line's first and last M values. That is only right when the measures grow in proportion to length. For a track with time as M, or a route with uneven mileposts, the result was wrong: LINESTRING M (0 0 0, 2 0 2, 4 0 20) and POINT (1 1) gave 5.0 instead of 1.0. Locate the closest point with LocationIndexedLine and interpolate between the M values of the two vertices of the segment that contains it.
| // Find the closest point on the line to the input point | ||
| Coordinate closestPoint = DistanceOp.nearestPoints(line, point)[0]; | ||
| // Locate the closest point on the line (the first one along the line if there is a tie) | ||
| LinearLocation location = new LocationIndexedLine(line).project(point.getCoordinate()); |
There was a problem hiding this comment.
For LINESTRING M (0 0 0, 0 0 10, 10 0 20) and POINT (0 1), this now returns 10 instead of 0. I checked the parent commit and PostGIS 3.3.2; both return 0. POINT (-1 0) also regresses. JTS advances to the next vertex on the zero-length segment, so a stationary track gets the later timestamp. Please preserve the first closest vertex in this case and add regression tests for these two points.
There was a problem hiding this comment.
Thanks for catching this. I no longer use LocationIndexedLine: the fix now walks the segments and keeps the first closest one on a tie, so the zero-length segment keeps the first vertex's measure. LINESTRING M (0 0 0, 0 0 10, 10 0 20) returns 0 again for both POINT (0 1) and POINT (-1 0), matching the parent commit and PostGIS, and both are now regression cases in interpolatePointMeasuresNotProportionalToLength. With the previous revision that test fails with expected:<0.0> but was:<10.0>. I also updated the PR description, which wrongly said the old lookup and LocationIndexedLine agree on ties.
Did you read the Contributor Guide?
Is this PR related to a ticket?
[GH-XXX] my subject. Closes ST_InterpolatePoint returns the wrong measure when M is not proportional to length #3519What changes were proposed in this PR?
ST_InterpolatePointtook the closest point's fraction of the total line length and interpolated between the line's first and last M values. The measures of the interior vertices were never read, so the result was only right when M grows in proportion to length. Tracks with time as M and routes with uneven mileposts got wrong values:LINESTRING M (0 0 0, 2 0 2, 4 0 4)(docs example)POINT (1 1)LINESTRING M (0 0 0, 2 0 2, 4 0 20)POINT (1 1)LINESTRING M (0 0 0, 10 0 10, 20 0 1000)POINT (5 1)LINESTRING ZM (0 0 5 100, 10 0 5 90, 10 10 5 0)POINT (10 5)With the old value,
ST_LocateAlong(line, ST_InterpolatePoint(line, 'POINT (5 1)'))on the third line returnedMULTIPOINT M((12.424242424242424 0 250)). With the fix it returnsMULTIPOINT M((5 0 5)), the closest point.The fix walks the segments of the line and takes the one closest to the point, keeping the first one along the line on a tie, as the previous
DistanceOplookup and PostGIS do. A zero-length segment (a stationary track) therefore keeps the measure of its first vertex. The fix then interpolates between the M values of the two vertices of that segment. For lines whose M is proportional to length the results are the same as before, so the existing test values are unchanged.ST_LocateAlongandST_AddMeasurealready work per segment, so they need no change. Spark, Flink and the Python API all callFunctions.interpolatePoint, so this one change covers them.How was this patch tested?
FunctionsTest#interpolatePointMeasuresNotProportionalToLength. It covers a point inside a segment, the closest point at an interior vertex and at each endpoint, a track with uneven timing, an XYZM line with decreasing measures, and a stationary track (a zero-length segment) wherePOINT (0 1)andPOINT (-1 0)must keep the first vertex's measure. Without the change toFunctions.javait fails withexpected:<1.0> but was:<5.0>. With the change it passes.mvn -pl common test -Dtest='FunctionsTest#interpolatePoint*': 2 tests, 0 failures. With the previous revision of this PR, the stationary-track case fails withexpected:<0.0> but was:<10.0>. The rest ofFunctionsTestis left to CI.GeometrySerializerand from WKB, as well as from WKT.mvn -pl common spotless:checkpasses.ST_InterpolatePointtests locally. Their cases use M equal to cumulative length, which this change leaves at the same values. CI covers them.Did this PR include necessary documentation updates?