Skip to content

geotiff: GPU stripped windowed read of non-georef TIFF emits float64 coords (regression of #1710) #1753

@brendancol

Description

@brendancol

Summary

open_geotiff(path, gpu=True, window=...) on a TIFF with no GeoTIFF tags (no ModelPixelScale / ModelTiepoint / GeoKeyDirectory) returns float64 coords like [-0.5, -1.5, ...] synthesised from the default unit GeoTransform, instead of integer pixel coords [0, 1, 2, ...] that the eager numpy and dask paths emit.

This was fixed in #1710 for the eager numpy path (open_geotiff), the dask path (read_geotiff_dask), and the tiled GPU path (_gpu_apply_window_band). The stripped-fallback branch in read_geotiff_gpu was missed.

Reproduction

import numpy as np
from xrspatial.geotiff import open_geotiff
from xrspatial.geotiff._writer import write

arr = np.arange(64, dtype=np.float32).reshape(8, 8)
path = "/tmp/no_georef.tif"
write(arr, path, compression='none', tiled=False)  # stripped, no georef

eager = open_geotiff(path, window=(0, 0, 4, 4))
dask = open_geotiff(path, chunks=4, window=(0, 0, 4, 4))
gpu = open_geotiff(path, gpu=True, window=(0, 0, 4, 4))

print(eager.y.dtype, eager.y.values)   # int64 [0 1 2 3]
print(dask.y.dtype, dask.y.values)     # int64 [0 1 2 3]
print(gpu.y.dtype, gpu.y.values)       # float64 [-0.5 -1.5 -2.5 -3.5]  <- WRONG

The eager / dask / tiled-GPU paths all guard on geo_info.has_georef. The stripped-GPU fallback in read_geotiff_gpu only checks t is None, which is never true for non-georef files because _extract_transform returns a default GeoTransform() instance with has_georef=False. The default transform has pixel_width=1.0, pixel_height=-1.0, so the PixelIsArea branch produces (c0 + 0.5) * 1.0 + 0 and (r0 + 0.5) * -1.0 + 0 -> [-0.5, -1.5, ...].

Affected backends

  • gpu=True stripped reads with window=...
  • gpu=True, chunks=... stripped reads with window=...

The tiled-GPU and CPU paths are not affected.

Severity

HIGH (Cat 2 - coords preservation). Coord dtype changes silently between the input full-read (int64) and the windowed-read on the GPU backend (float64), breaking backend parity. Downstream coord-based math (clipping, interp, xarray slicing) gets different answers depending on which backend is active.

Test failures

xrspatial/geotiff/tests/test_no_georef_windowed_coords_1710.py:

  • TestGpuWindowedCoords::test_windowed_read_integer_coords
  • TestGpuWindowedCoords::test_dask_cupy_windowed_integer_coords
  • TestBackendParity::test_dtype_parity_windowed

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions