Fix ellipsoidal/geoid height datum handling for station and DEM queries - #822
Open
royagrace wants to merge 9 commits into
Open
Fix ellipsoidal/geoid height datum handling for station and DEM queries#822royagrace wants to merge 9 commits into
royagrace wants to merge 9 commits into
Conversation
royagrace
marked this pull request as ready for review
September 2, 2026 20:18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
dem.py:download_demnow requests geoid-referenced GLO-30 DEM heights (dst_ellipsoidal_height=False) instead of ellipsoidal, matching ERA5's convention.llreader.py:_ellipsoidal_to_geometric/_geometric_to_ellipsoidal, a bidirectional height-datum converter (via EPSG:9707, WGS84+EGM96), with a PROJ-CDN fallback if the local EGM96 grid is missing.StationFilenow accepts acrsparam (default4326=geoid; pass4979for ellipsoidal GNSS heights, e.g. UNR MAGNET/IGS20) andupdate_crs()to change it after construction.readZ()onStationFile,RasterRDR, andGeocodedFiletakes a newellipsoidal_heightsflag (defaultFalse=geoid) so callers can request either convention; each converts from its known source datum on demand.delay.py:tropo_delaynow reads heights twice with the correct flag for each use — geoid fortransformPoints/weather-model sampling, ellipsoidal forlos.setPoints/LOS-ECEF geometry — instead of reusing one value for both, which was silently wrong for whichever consumer didn't get its expected convention.losreader.py:LOS.setPoints()renamedheights→ellipsoidal_heightsand documented that it requires true WGS84-ellipsoidal input, since it feedsstate_to_los/lla2ecef, which is geodetic-to-ECEF math and only correct with ellipsoidal height.utilFcns.py(geo_to_ht),models/weatherModel.py(_get_heights), anddelay.py(cubezattrs,transformPointsdocstring) — these compute/label geoid-referenced heights but previously said "ellipsoidal."test_llreader.py).Needs revisiting
RasterRDR/GeocodedFileheight-source datum is assumed, not verified. When heights come from a user-suppliedhgt_fileor an existingis_demfile (rather than a freshly-downloaded DEM), there's no datum actually tracked — we assume geoid to match everything else and documented that assumption inline, but it hasn't been confirmed for real-world files of that kind.self._crsellipsoidal/geoid detection is a heuristic, not a real check:StationFileinfers "ellipsoidal" fromlen(crs.axis_info) >= 3(does the CRS have a vertical axis), not from the CRS's actual datum. This is correct for the two CRSs the API documents (4326,4979), but would misclassify a 3D geoid CRS like EPSG:9707 as ellipsoidal if someone passed that in.RasterRDR.readZ(ellipsoidal_heights=True)raisesNotImplementedErrorif the object was built without alon_file(the incomplete 2-band-raster case), rather than supporting it — flagged, not fixed.BoundingBox/Geocube,_build_cube_ray) not audited. This PR only fixes the point-AOI path (StationFile/RasterRDR/GeocodedFile) that goes throughdelay.py'slos.setPoints(). Whether the raytracing cube-generation path has the same ellipsoidal/geoid mismatch wasn't checked.Motivation and Context
RAiDER was sampling the weather-model delay cube with GNSS station heights that were ellipsoidal (WGS84), while the cube's own z-axis is geoid-referenced (~MSL), like ERA5. This PR normalizes height handling so every height is explicitly geoid or ellipsoidal, and each consumer gets the one it actually needs.
Fixes some issues from PR #805 and all issues from PR #813
How Has This Been Tested?
Ran autopep8 command
Created and ran tests locally in test_llreader.py
Screenshots (if appropriate):
Type of change
Checklist: