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).
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()storesattrs['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 inattrs['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()buildsy/xcoords from the file's GeoTransform, andto_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 asattrs['transform']soto_geotiff()can reuse it directly, falling back to_coords_to_transformonly when the attr is missing.M-3: ColorMap, ExtraSamples, ImageDescription pass-through
_geotags.pyalready reads ColorMap (exposed asattrs['cmap']/attrs['colormap_rgba']) but ignores ExtraSamples and ImageDescription. Capture all three on read intoattrs['colormap'](raw uint16 triple),attrs['extra_samples'], andattrs['image_description']. On write, route them through the existingextra_tagspass-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 thedtype=...override.Tests
attrs['crs']andattrs['transform']through write -> read -> write -> read.dtype='uint16'raises ValueError (existing float-to-int guard).Same polish that landed in reproject (#1462) and resample (#1472).