Skip to content

geotiff: 1xN / Nx1 georeferenced writes silently strip transform #1945

@brendancol

Description

@brendancol

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'))                       # None

The same thing happens for Nx1.

Affected code

xrspatial/geotiff/_coords.py:277:

if len(x) < 2 or len(y) < 2:
    return None

xrspatial/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.

  1. 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 prefer attrs['transform'] when the caller supplied one and otherwise raise a clear ValueError.

  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinginput-validationInput validation and error messages

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions