Skip to content

CRS model type guessed from EPSG number range corrupts geographic CRSes outside 4000-4999 #2277

@brendancol

Description

@brendancol

Describe the bug

build_geo_tags in xrspatial/geotiff/_geotags.py decides whether to write GeographicTypeGeoKey or ProjectedCSTypeGeoKey from the EPSG number range:

# xrspatial/geotiff/_geotags.py:1403
if crs_epsg == 4326 or (crs_epsg >= 4000 and crs_epsg < 5000):
    model_type = MODEL_TYPE_GEOGRAPHIC
else:
    model_type = MODEL_TYPE_PROJECTED

EPSG codes aren't allocated by type. Plenty of geographic CRSes live outside the 4000-4999 window. A few examples that get mis-tagged today:

  • EPSG:6318, NAD83(2011) geographic 2D
  • EPSG:7844, GDA2020 geographic 2D
  • EPSG:9057, WGS 84 (G2139) geographic 3D
  • EPSG:8252, NAD83(FBN) geographic 2D

All four get written with ProjectedCSTypeGeoKey. That's CRS corruption at write time. A spec-compliant reader will treat the file as projected with linear units of metres, when the coordinates are actually decimal degrees on an ellipsoid.

The current test suite only covers EPSG 4326 and 32610 (xrspatial/geotiff/tests/test_geotags.py:80), so this hole is untested.

Expected behavior

build_geo_tags should consult pyproj when available and use CRS.from_epsg(crs_epsg).is_geographic to decide the GeoKey. If pyproj isn't installed and the EPSG code isn't one of the few that can be hard-coded with confidence (4326 plus the 4000-4999 geographic block), the writer should raise rather than guess. Silent CRS corruption is worse than an explicit error at write time.

Additional context

Fix sketch:

  1. A helper that returns the model type for an EPSG code, preferring pyproj and falling back to a tight hard-coded allowlist.
  2. Call it from build_geo_tags in place of the range check.
  3. Honour XRSPATIAL_GEOTIFF_STRICT on the fail-closed path so behaviour matches the rest of the geotiff module.
  4. Tests for EPSG 6318 and 7844 (geographic, outside 4000-4999) plus an unknown EPSG with pyproj patched out to exercise the raise path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions