Repository navigation
Description
Summary
A DataArray that is georeferenced but degenerate along one spatial axis (1 row or 1 column, e.g. a single scanline or single profile) silently loses its transform when written with to_geotiff. coords_to_transform returns None whenever either coord array has length < 2, and the writer then falls through and produces a non-georeferenced TIFF. On read, the file comes back with integer pixel coords and no transform attribute, so the user has no signal that the spatial metadata was dropped.
Reproduction
import numpy as np, xarray as xr
from xrspatial.geotiff import to_geotiff, open_geotiff
da = xr.DataArray(
np.arange(8, dtype='float32').reshape(1, 8),
dims=('y', 'x'),
coords={'x': np.linspace(-120.0, -113.0, 8), 'y': np.array([45.0])},
attrs={'crs': 4326},
)
to_geotiff(da, '/tmp/strip.tif')
r = open_geotiff('/tmp/strip.tif')
print(r.coords['x'].values[:3], r.coords['x'].dtype) # [0 1 2] int64
print(r.attrs.get('transform')) # NoneThe same thing happens for Nx1.
Affected code
xrspatial/geotiff/_coords.py:277:
if len(x) < 2 or len(y) < 2:
return Nonexrspatial/geotiff/_writers/eager.py:516-519 calls _transform_from_attr first, then falls back to _coords_to_transform. If both return None, the writer proceeds without raising and produces a non-georeferenced TIFF. The GPU writer at gpu.py:348-350 has the same fall-through.
Fix direction
Two parts.
-
In
coords_to_transform, don't bail when only one axis has length 1. The non-degenerate axis still pins a pixel size, and the file's row/column count fixes the other extent; the writer only needs an origin and a pixel size per axis. For the 1x1 case there is no way to recover pixel size from coords alone, so preferattrs['transform']when the caller supplied one and otherwise raise a clearValueError. -
In the eager, dask, and GPU writer paths, when coord arrays are present but no transform can be derived and no
attrs['transform']was supplied, raise rather than silently writing a non-georeferenced file. A degenerate-shape array with real spatial coords expresses intent to georeference, and the writer should fail closed instead of dropping metadata on the floor.
Round-trip invariant tests covering 1xN, Nx1, and 1x1 writes across numpy, dask+numpy, dask+cupy, and cupy backends.
Activity
geotiff: preserve transform on 1xN / Nx1 georeferenced writes (#1945)
geotiff: preserve transform on 1xN / Nx1 georeferenced writes (#1945)
geotiff: preserve transform on 1xN / Nx1 georeferenced writes (#1945)
geotiff: preserve transform on 1xN / Nx1 georeferenced writes (#1945) (…
geotiff: add cross-library parity test (rasterio + zarr round-trip) (#…
geotiff: add cross-library parity test (rasterio + zarr round-trip) (#…
geotiff: fail closed on 1xN / Nx1 writes without explicit pixel size (#…