Skip to content

reproject: fix half-pixel offset in geoid and datum-shift grid lookups (#2508)#2516

Merged
brendancol merged 2 commits into
xarray-contrib:mainfrom
brendancol:deep-sweep-accuracy-reproject-2026-05-27
May 28, 2026
Merged

reproject: fix half-pixel offset in geoid and datum-shift grid lookups (#2508)#2516
brendancol merged 2 commits into
xarray-contrib:mainfrom
brendancol:deep-sweep-accuracy-reproject-2026-05-27

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Summary

_interp_geoid_point and _grid_interp_point indexed pixel-edge coordinates into pixel-center anchored grids. The bilinear stencil landed half a pixel off, producing up to 2.1 m error on the EGM96 geoid (mean 24 cm, p95 69 cm at pixel centers) and an analogous offset on NADCON / OSTN / NTv2 horizontal datum shift grids. Cross-checked against pyproj: ~9 cm New York geoid error before the fix, ~1e-10 m after.

Closes #2508.

What changed

  • _interp_geoid_point (in xrspatial/reproject/_vertical.py): subtract 0.5 from the row/col index so data[r, c] is queried at its pixel-center coordinate. Edge handling extended so queries inside the outer pixel center's half-pixel border return the edge pixel value rather than NaN, matching PROJ's vgridshift. Negative col_f is handled via math.floor so the longitude wrap still picks the antipodal column correctly.
  • _grid_interp_point (in xrspatial/reproject/_datum_grids.py): same -0.5 correction. Out-of-grid queries still return (0, 0) matching PROJ's hgridshift.
  • TestGeoidPixelCenterIndexing in xrspatial/tests/test_reproject.py: three regressions
    • pixel-center identity on the real EGM96 grid (catches the offset directly)
    • sub-cm pyproj cross-check at 5 reference locations including the antimeridian pole where the bias was largest
    • synthetic-grid test for _grid_interp_point so it doesn't depend on a downloaded NADCON file

Why existing tests missed it

TestGeoidHeightBehaviour checked reference values with a 3 m tolerance, which is wider than the 2.1 m worst-case offset error.

Test plan

  • pytest xrspatial/tests/test_reproject.py -k 'geoid or vertical or datum' -- 47 passed (44 prior + 3 new)
  • pytest xrspatial/tests/test_reproject.py -- 348 passed (was 345)

Found by /sweep-accuracy against reproject on 2026-05-27.

xarray-contrib#2508)

`_interp_geoid_point` and `_grid_interp_point` indexed pixel-edge
coordinates into pixel-center anchored grids. The bilinear stencil
landed half a pixel off, producing up to 2.1 m error in the EGM96
geoid undulation (mean 24 cm, p95 69 cm) and an analogous offset on
NADCON / OSTN / NTv2 horizontal datum shift grids.

Subtract 0.5 from the row/col index so `data[r, c]` is queried at
its pixel-center coordinate, matching how the main `reproject` pixel
mapping already handles source pixel coords. Cross-checked against
pyproj: New York geoid agrees to ~1e-10 m post-fix vs the 9 cm error
before.

Add `TestGeoidPixelCenterIndexing` with three regressions: a
pixel-center identity test on the real EGM96 grid, a sub-cm pyproj
cross-check at five locations including the antimeridian pole where
the offset error was largest, and a synthetic-grid test for
`_grid_interp_point` so the datum-grid fix doesn't depend on a
downloaded NADCON file.
@github-actions github-actions Bot added the performance PR touches performance-sensitive code label May 27, 2026
CI runners without local PROJ grid downloads and with network access
disabled get a silent no-op transform from pyproj, returning 0 m at
all locations instead of looking the geoid up. The cross-check test
then sees our correct -32.77 m at New York vs pyproj's 0 m and fails.

Probe at New York (~-32.8 m) before iterating sample points; skip if
the magnitude is < 1 m (the no-grid fallback signature).
@brendancol brendancol merged commit 21c21cf into xarray-contrib:main May 28, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

reproject: geoid undulation lookup has half-pixel offset error (up to 2 m)

1 participant