Repository navigation
fix(spatial): cover the antimeridian and the poles in Near queries (#166) - #174
Conversation
mrdevrobot
left a comment
There was a problem hiding this comment.
Reviewed only 3450e15. The math checks out: asin(sin r / cos φ) is the spherical-cap longitude half-width and the non-pole branch guarantees |φ| + r < 90°; the pole branch covers r ≥ 90°, lat = ±90, r = 0 and r ≥ πR; the split boxes can't overlap since dLon ≤ 90°, so no point is returned twice; GeoBox argument order matches. 523/523 targeted tests pass, full suite green apart from the known .wal-shm failures, no conflicts with main (5.2.3). Approving once #173 lands; two robustness notes.
SpatialMath.cs:49: the pole guard is done in degrees but the asin argument issin(r)/cos(lat); when|lat| + ris within a few ulp of 90° the ratio can round to1 + ε,Asinreturns NaN, both longitude bounds become NaN andGeoBox.Intersectsthen matches every entry (all comparisons false). The distance check keeps the result correct, but it becomes a full index walk. Clamp withMath.Min(1.0, ...)or do the pole test on the same ratio (sin r >= cos lat).- An unnormalised centre longitude (
lon = 190) produces one inverted (empty) box and one over-wide box; still correct after the distance check. Normalisingloninto[-180, 180)at the top keeps the boxes tight.
Nits: RTreeIndex.cs:66 "the boxes never overlap" deserves the one-line reason (dLon ≤ 90°); the whole-globe case could short-circuit before computing anything when angularRadius >= Math.PI.
|
Thanks. Both notes are addressed in the latest commit, on top of the updated #173 (merged in; the 1. NaN near the pole. The pole test is now done on the same ratio that feeds 2. Unnormalised centre. The centre longitude is normalised into [-180, 180) first. New test Nits:
Full suite on macOS: 2414 passed, 0 failed, 4 skipped. 🤖 Generated with Claude Code |
mrdevrobot
left a comment
There was a problem hiding this comment.
All points are fixed. I scanned about 1.06M (lat, radius) pairs within ±40 ulp of the pole distance: the old code produced 109 NaN boxes, the new one none. A 2M-sample random scan with lon in [-360, 360] and r up to 20,000 km found no NaN, inverted or out-of-range boxes. Normalisation maps ±180 and ±540 to -180 with the right split. Full suite on this branch 2414 / 0 / 4 skipped.
Approving, to land after #173.
One test request: NearQueryBoxes_AtThePoleBoundary_NeverContainNaN passes with the fix reverted, because the 0.37° sweep never hits the rounding case. Please add an explicit input that did produce NaN, for example lat = -88.3413, radius = 184.4390248253298 km. NearQueryBoxes_LargerThanHalfTheEarth_CoverTheWholeGlobe also passes when reverted, since the degree check already returns the whole globe. That one is harmless.
|
Thanks. Added in 256e8e5: I didn't use lat = -88.3413, radius = 184.4390248253298. With the pre-fix code on this branch, Agreed on |
mrdevrobot
left a comment
There was a problem hiding this comment.
Verified 256e8e5, and you're right about the input I suggested: with the pre-fix code, lat ± dLat lands exactly on ±90° there, so the old degree check already returned the whole globe and asin was never reached. Your ±2.1702 / 9,766.228 km case does produce NaN boxes before the fix and passes after it. On main with #169/#170 merged the full suite passed (2434 / 0 / 4 skipped).
#173 is now merged as a squash (efacaf4), and this branch still carries its earlier commits (224b332, 97bfb7d), so it conflicts with main. Please rebase onto main keeping only 3450e15, 8ba4628 and 256e8e5, and mark it ready for review. I'll approve and merge once CI is green.
…ntglDb#166) The R-Tree query box for Near was plain lat ± dLat, lon ± dLon. A box around longitude -179.9 never reached +179.9, and a circle that contains a pole reaches every longitude while its box only spanned lon ± dLon. The longitude half-width radius / cos(lat) was also too narrow at high latitudes: the circle is widest poleward of its center. SpatialMath.NearQueryBoxes replaces BoundingBox: - a circle that contains a pole gets one box over all longitudes, from its far edge up to the pole; - otherwise the half-width is asin(sin r / cos lat), and a box that crosses ±180° is split into one box on each side. RTreeIndex.SearchNear searches each box and keeps the exact great-circle distance check, so the extra area is trimmed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Db#166) - The pole case is decided on the same ratio that feeds asin (sin r >= cos lat), so rounding can no longer produce an asin argument above 1 and NaN bounds that match every index entry. - The centre longitude is normalised into [-180, 180), so an unnormalised centre (e.g. 190) yields the same tight boxes as its normalised form. - A radius of at least half the Earth's circumference short-circuits to a whole-globe box. - The non-overlap comment in SearchNear states why (dLon < 90°). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lDb#166) The 0.37 degree sweep in NearQueryBoxes_AtThePoleBoundary_NeverContainNaN never hits the rounding case, so it passed with the fix reverted. The new cases (lat +/-2.1702, radius 9766.228168206266 km, the distance to the pole) give NaN boxes without the sin(r) >= cos(lat) check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
256e8e5 to
c82db82
Compare
|
Rebased onto |
Fixes #166.
Problem
The R-Tree query box for
Nearwaslat ± dLat,lon ± dLon, compared against raw coordinates:lon ± dLon.radius / cos(lat)is measured at the center latitude, but the circle is widest further toward the pole. Around (80°N, 0°) with a 556 km radius the box spans ±28.8° while the circle reaches ±30.1°, so points near its widest part were missed even with no pole or meridian involved.Fix
SpatialMath.NearQueryBoxesreplacesBoundingBox. The old method's only caller wasRTreeIndex.SearchNear; the two unused internal overloads are removed.lat ± dLatreaches ±90°) gets one box over all longitudes, from its far edge up to the pole. One that contains both poles covers the whole globe.asin(sin r / cos lat)withrthe angular radius. The argument stays below 1 whenever no pole is inside.RTreeIndex.SearchNearsearches each box and keeps the exact great-circle check from #173, which trims the extra area. Both LINQNearand typed and dynamicNearAsyncgo through it.Tests
NearAntimeridianPoleTestsadds 5 tests. All fail without this commit. Each checksNearAsyncand LINQNearand requires them to agree:DynamicCollection.NearAsyncacross the antimeridian.Full suite on macOS: 2395 passed, 3 failed, 4 skipped. The 3 failures are the known
MultiProcessWalSharedMemoryTests.wal-shmlock timeouts.Not in scope
The issue's open question about
Withinwithmin.Longitude > max.Longitudeis unchanged. It still matches nothing, in LINQ (index and in-memory) and in$geoWithinalike. Whether it should mean a box wrapping across ±180° or be rejected as invalid is your call. Either is a small follow-up on top ofNearQueryBoxes.🤖 Generated with Claude Code