Skip to content

Round-trip transform, crs, and tag metadata through to_geotiff/open_geotiff #1484

@brendancol

Description

@brendancol

Polish GeoTIFF metadata so transforms, CRS, and tag metadata round-trip cleanly through to_geotiff / open_geotiff. Findings M-1 through M-4 from the geotiff metadata audit.

M-1 / M-2: transform and CRS type stability

open_geotiff() stores attrs['crs'] as an int EPSG code. read_vrt() does the same when the EPSG can be derived from the WKT, otherwise it leaves the WKT in attrs['crs_wkt']. The two paths agree, but neither docstring says so. Document the convention in both functions and in _geo_to_coords. Keep tolerating string CRS values on write so existing callers don't break.

For the transform: open_geotiff() builds y/x coords from the file's GeoTransform, and to_geotiff() reconstructs the transform from coords via _coords_to_transform. That round-trips fine for clean numbers but drifts on fractional or precision-edge transforms. Stash the rasterio 6-tuple on read as attrs['transform'] so to_geotiff() can reuse it directly, falling back to _coords_to_transform only when the attr is missing.

M-3: ColorMap, ExtraSamples, ImageDescription pass-through

_geotags.py already reads ColorMap (exposed as attrs['cmap'] / attrs['colormap_rgba']) but ignores ExtraSamples and ImageDescription. Capture all three on read into attrs['colormap'] (raw uint16 triple), attrs['extra_samples'], and attrs['image_description']. On write, route them through the existing extra_tags pass-through when present.

M-4: integer-with-nodata dtype promotion

open_geotiff() silently promotes integer rasters with a nodata sentinel to float64 with NaN replacing the sentinel. This is documented nowhere. Add it to the docstring along with the dtype=... override.

Tests

  • Round-trip attrs['crs'] and attrs['transform'] through write -> read -> write -> read.
  • ColorMap round-trip on a uint8 indexed raster.
  • ImageDescription round-trip with a known string.
  • uint16 with nodata=65535 reads back as float64 with NaN at the sentinel; passing dtype='uint16' raises ValueError (existing float-to-int guard).

Same polish that landed in reproject (#1462) and resample (#1472).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions