From d6d1791fb99421f1cf45b91e496275efe1649ecc Mon Sep 17 00:00:00 2001 From: Hannah Bast Date: Fri, 14 Aug 2026 15:41:17 +0200 Subject: [PATCH 1/2] Fix `within-distance` check between a point and a linestring So far, the `within-distance` check between a point and a linestring initialized its running minimum with the distance from the point to the two endpoints of the line, clamped to the given maximum distance. When no segment of the line was within the maximum distance, this clamped value was also returned. The caller in `spatialjoin` compares the result inclusively against the threshold, so every candidate pair from the sweep was reported as a match, with a reported distance exactly equal to the threshold. As a consequence, a `within-distance` join between points and a linestring returned every point in the padded bounding box of the linestring. For example, a query for all OSM points within 100 m of the Route d'Arlon (N 6) in Luxembourg returned 124,532 points, of which only 18,389 are actually within 100 m. The endpoints are now probed with an unbounded distance function, exactly as the corresponding check between a point and a polygon ring already does. The returned value is then an honest distance. The clamp to the maximum distance is kept in the two arguments to the padding function, so the search behavior is unchanged. Only the point-linestring case is affected. The ring, simple-segment, line-line and collection variants already behave correctly when nothing is within range. --- geo/Geo.tpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/geo/Geo.tpp b/geo/Geo.tpp index 529e59c..e03a496 100644 --- a/geo/Geo.tpp +++ b/geo/Geo.tpp @@ -1362,11 +1362,12 @@ double withinDist(const Point& p, const XSortedLine& line, double maxDist, double euclideanDistUpperBound = std::min(maxEuclideanDist, std::min(dist(p, line.rawLine().back().other), dist(p, line.rawLine().front().p))); - double minDist = std::min( - std::min(maxDist, distFunc(p, line.rawLine().back().other, maxDist)), - distFunc(p, line.rawLine().front().p, maxDist)); + double minDist = std::min(distFunc(p, line.rawLine().back().other, + std::numeric_limits::max()), + distFunc(p, line.rawLine().front().p, + std::numeric_limits::max())); - auto padding = paddingFunc(euclideanDistUpperBound, minDist, + auto padding = paddingFunc(euclideanDistUpperBound, std::min(maxDist, minDist), getBoundingBox(p), line.boundingBox()); // skip irrelevant parts @@ -1400,8 +1401,8 @@ double withinDist(const Point& p, const XSortedLine& line, double maxDist, if (euclideanDist < euclideanDistUpperBound) { euclideanDistUpperBound = euclideanDist; - padding = paddingFunc(euclideanDistUpperBound, minDist, getBoundingBox(p), - line.boundingBox()); + padding = paddingFunc(euclideanDistUpperBound, std::min(maxDist, minDist), + getBoundingBox(p), line.boundingBox()); } } From c67d9324d232d79dc75f01914f063e731ea252d4 Mon Sep 17 00:00:00 2001 From: Patrick Brosi Date: Fri, 14 Aug 2026 22:54:19 +0200 Subject: [PATCH 2/2] add some regression tests --- tests/GeoTestDist.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/GeoTestDist.cpp b/tests/GeoTestDist.cpp index f14b021..03b19d3 100644 --- a/tests/GeoTestDist.cpp +++ b/tests/GeoTestDist.cpp @@ -150,9 +150,17 @@ static void testDistWithinRealisticMaxDist() { approx(5.5)); TEST(util::geo::withinDist(XSortedLine(line), point, 10.0), ==, approx(5.5)); + TEST(util::geo::withinDist(XSortedLine(line), point, 5.4), >, + 5.4); + TEST(util::geo::withinDist(XSortedLine(line), point, 5.0), >, + 5.0); + TEST(util::geo::withinDist(XSortedLine(line), point, 1.0), >, + 1.0); TEST(util::geo::withinDist(XSortedPolygon(poly), point2, 10.0), ==, approx(sqrt(2))); + TEST(util::geo::withinDist(XSortedPolygon(poly), point2, 1.0), >, + 1); TEST(util::geo::withinDist(point2, XSortedPolygon(poly), 10.0), ==, approx(sqrt(2))); @@ -172,6 +180,12 @@ static void testDistWithinRealisticMaxDist() { TEST(util::geo::withinDist(XSortedPolygon(polyWithInner), point, 10.0), ==, approx(0.5)); + TEST(util::geo::withinDist(XSortedPolygon(polyWithInner), + XSortedLine(line2), .2), + >, 0.2); + TEST(util::geo::withinDist(XSortedPolygon(polyWithInner), + point, .4), + >, 0.4); TEST(util::geo::withinDist(XSortedPolygon(poly2), XSortedPolygon(polyWithInner), 10.0), @@ -179,6 +193,9 @@ static void testDistWithinRealisticMaxDist() { TEST(util::geo::withinDist(XSortedPolygon(polyWithInner), XSortedPolygon(poly2), 10.0), ==, approx(0.25)); + TEST(util::geo::withinDist(XSortedPolygon(polyWithInner), + XSortedPolygon(poly2), .2), + >, 0.2); } // _____________________________________________________________________________