Describe the bug
write_geotiff_gpu silently drops most metadata attrs when writing a CuPy-backed DataArray:
crs_wkt (the WKT-only CRS fallback when no EPSG is recognized)
gdal_metadata / gdal_metadata_xml
extra_tags
image_description
extra_samples
colormap
x_resolution, y_resolution, resolution_unit
It also ignores attrs['transform'], recomputing the GeoTransform from coords via _coords_to_transform. That re-derivation drifts on fractional pixel sizes (e.g. 1 arc-second) because x[1] - x[0] is taken from already-rounded float64 coords. The CPU writer reads attrs['transform'] first and only falls back to _coords_to_transform if absent.
The CRS loss is the worst part. When the file's CRS only resolves to WKT (no recognized EPSG), attrs['crs'] is unset and attrs['crs_wkt'] carries the only CRS info. write_geotiff_gpu reads data.attrs.get('crs') but never 'crs_wkt', so the output has no GeoKeys at all and other readers see an ungeoreferenced file.
Repro
import numpy as np, cupy, xarray as xr, tempfile, os
from xrspatial.geotiff import open_geotiff, write_geotiff_gpu
from xrspatial.geotiff._geotags import _epsg_to_wkt
with tempfile.TemporaryDirectory() as d:
wkt = _epsg_to_wkt(4326)
arr = np.arange(64, dtype=np.float32).reshape(8, 8)
da_gpu = xr.DataArray(
cupy.asarray(arr), dims=['y','x'],
coords={'y': np.arange(8.0), 'x': np.arange(8.0)},
attrs={'crs_wkt': wkt, 'image_description': 'demo',
'extra_samples': [1]},
)
out = os.path.join(d, 'gpu.tif')
write_geotiff_gpu(da_gpu, out, compression='none')
print(sorted(open_geotiff(out).attrs.keys()))
# -> ['transform'] (crs/crs_wkt/image_description/extra_samples all lost)
The corresponding CPU to_geotiff call preserves crs, crs_wkt, extra_samples, extra_tags, gdal_metadata, gdal_metadata_xml, image_description, and transform.
Expected behavior
write_geotiff_gpu should follow the same attrs-resolution logic as the CPU path in to_geotiff: prefer attrs['transform'] over coord-derived transforms, resolve crs from int EPSG, WKT string, or crs_wkt, and forward gdal_metadata*, extra_tags, image_description, extra_samples, colormap, and the resolution tags to the underlying _assemble_tiff writer.
Why this matters
Round-tripping a GPU-read DataArray through to_geotiff (which auto-dispatches to GPU for CuPy data) silently strips CRS-WKT and rich tags. The to_geotiff -> open_geotiff round-trip is documented as preserving these attrs on the CPU path, and #1548 wired up the read side to emit the same keys across all backends; the write side is the remaining asymmetry.
Scope
xrspatial/geotiff/__init__.py::write_geotiff_gpu (the kwargs to _assemble_tiff).
Additional context
Found during the metadata propagation sweep on the geotiff subpackage.
Describe the bug
write_geotiff_gpusilently drops most metadata attrs when writing a CuPy-backed DataArray:crs_wkt(the WKT-only CRS fallback when no EPSG is recognized)gdal_metadata/gdal_metadata_xmlextra_tagsimage_descriptionextra_samplescolormapx_resolution,y_resolution,resolution_unitIt also ignores
attrs['transform'], recomputing the GeoTransform from coords via_coords_to_transform. That re-derivation drifts on fractional pixel sizes (e.g. 1 arc-second) becausex[1] - x[0]is taken from already-rounded float64 coords. The CPU writer readsattrs['transform']first and only falls back to_coords_to_transformif absent.The CRS loss is the worst part. When the file's CRS only resolves to WKT (no recognized EPSG),
attrs['crs']is unset andattrs['crs_wkt']carries the only CRS info.write_geotiff_gpureadsdata.attrs.get('crs')but never'crs_wkt', so the output has no GeoKeys at all and other readers see an ungeoreferenced file.Repro
The corresponding CPU
to_geotiffcall preservescrs,crs_wkt,extra_samples,extra_tags,gdal_metadata,gdal_metadata_xml,image_description, andtransform.Expected behavior
write_geotiff_gpushould follow the same attrs-resolution logic as the CPU path into_geotiff: preferattrs['transform']over coord-derived transforms, resolvecrsfrom int EPSG, WKT string, orcrs_wkt, and forwardgdal_metadata*,extra_tags,image_description,extra_samples,colormap, and the resolution tags to the underlying_assemble_tiffwriter.Why this matters
Round-tripping a GPU-read DataArray through
to_geotiff(which auto-dispatches to GPU for CuPy data) silently strips CRS-WKT and rich tags. Theto_geotiff->open_geotiffround-trip is documented as preserving these attrs on the CPU path, and #1548 wired up the read side to emit the same keys across all backends; the write side is the remaining asymmetry.Scope
xrspatial/geotiff/__init__.py::write_geotiff_gpu(the kwargs to_assemble_tiff).Additional context
Found during the metadata propagation sweep on the geotiff subpackage.