Skip to content

Fix windowed read coords of non-georef TIFFs (#1710)#1722

Merged
brendancol merged 3 commits into
xarray-contrib:mainfrom
brendancol:deep-sweep-metadata-geotiff-2026-05-12-a6e1eda6
May 12, 2026
Merged

Fix windowed read coords of non-georef TIFFs (#1710)#1722
brendancol merged 3 commits into
xarray-contrib:mainfrom
brendancol:deep-sweep-metadata-geotiff-2026-05-12-a6e1eda6

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Summary

Fixes #1710. Non-georef TIFFs (files with no GeoTIFF tags) got inconsistent y/x coords between full reads and windowed reads.

Before the fix:

full = open_geotiff(path)              # y dtype int64, [0, 1, 2, ...]
win  = open_geotiff(path, window=...)  # y dtype float64, [-0.5, -1.5, ...]

The split was identical across every backend (numpy, dask+numpy, cupy, dask+cupy), so this was a windowed-vs-full inconsistency, not a backend parity bug. The root cause: windowed-read paths synthesised coords from the default unit GeoTransform and applied the PixelIsArea half-pixel shift to a transform that was never real. The full-read path already had a has_georef=False shortcut in _geo_to_coords that emitted integer pixel coords.

The fix threads has_georef through:

  • open_geotiff eager windowed branch (~line 695)
  • read_geotiff_dask windowed coord computation (~line 1855)
  • _gpu_apply_window_band (~line 2290), used by both pure CuPy and Dask+CuPy reads
  • _populate_attrs_from_geo_info (~line 438), which suppresses the fabricated attrs['transform'] for files with no GeoTIFF tags

After the fix, all four backends agree: integer pixel coords for non-georef files, regardless of full or windowed read. Georef files keep their float64 coords and real transform attr.

Test plan

  • xrspatial/geotiff/tests/test_no_georef_windowed_coords_1710.py adds 15 tests across numpy, dask+numpy, cupy, dask+cupy backends, plus 2 regression-protection tests for the georef case
  • Existing geotiff test suite passes (skipping two pre-existing failures in test_kwarg_behaviour_2026_05_12_v2.py and test_features.py::TestPalette that also fail on main)
  • Round-trip audit: to_geotiff(da_in) -> open_geotiff(...) -> compare attrs/coords across all four backends

Found via the metadata propagation sweep, 2026-05-12.

@github-actions github-actions Bot added the performance PR touches performance-sensitive code label May 12, 2026
@brendancol brendancol requested a review from Copilot May 12, 2026 19:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes inconsistent y/x coordinate generation for non-georeferenced TIFFs (no GeoTIFF transform tags) between full reads and window= reads, aligning all read backends (numpy, dask, cupy, dask+cupy) on integer pixel-index coordinates and preventing emission of a fabricated identity transform attribute.

Changes:

  • Thread has_georef through eager, dask, and GPU windowed coordinate computation so non-georef windowed reads produce integer pixel coords (matching full reads).
  • Suppress attrs['transform'] emission when has_georef=False to avoid leaking a fake identity transform into downstream workflows.
  • Add a comprehensive regression test suite covering eager, dask, GPU, and backend-parity behavior, plus protections for georeferenced files.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
xrspatial/geotiff/__init__.py Aligns windowed coord generation with full-read behavior for non-georef files; gates transform attr emission on has_georef.
xrspatial/geotiff/tests/test_no_georef_windowed_coords_1710.py Adds regression coverage across CPU/dask/GPU backends and georef/non-georef cases.
.claude/sweep-metadata-state.csv Updates sweep metadata state entry to reflect issue #1710.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread xrspatial/geotiff/__init__.py Outdated
Comment on lines +439 to +441
# Skip the transform attr for files that lack any GeoTIFF tags. The
# default unit ``GeoTransform`` is a struct placeholder, not real
# georef -- emitting it leaks an identity transform into attrs and
…oords (xarray-contrib#1710)

A TIFF with no GeoTIFF tags used to get different `y`/`x` coords
depending on whether `window=` was passed. Full reads took the
`has_georef=False` shortcut in `_geo_to_coords` and returned integer
pixel coords with `int64` dtype. Windowed reads skipped that shortcut
and synthesised float64 coords like `[-0.5, -1.5, ...]` from the
default unit `GeoTransform`, because the `PixelIsArea` half-pixel
shift was applied to a transform that was never real.

This patch threads `has_georef` through all three windowed-read paths
(`open_geotiff` eager, `read_geotiff_dask`, `_gpu_apply_window_band`)
and through `_populate_attrs_from_geo_info`. Non-georef files now get
integer pixel coords on both full and windowed reads, and do not emit
a fabricated `attrs['transform']` identity tuple.

Georef files are unaffected: they still get float64 coords with the
half-pixel shift and a real transform attr.

Adds regression coverage in
`xrspatial/geotiff/tests/test_no_georef_windowed_coords_1710.py`
covering numpy, dask+numpy, cupy, and dask+cupy backends.

Closes xarray-contrib#1710.
@brendancol brendancol force-pushed the deep-sweep-metadata-geotiff-2026-05-12-a6e1eda6 branch from 576bf5a to fe9c750 Compare May 12, 2026 19:29
Clarify that the gate is "no transform tags present"
(ModelTransformation/ModelPixelScale/ModelTiepoint/GeoKeys),
not "files lack any GeoTIFF tags".
@brendancol brendancol merged commit a5c28f8 into xarray-contrib:main May 12, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

open_geotiff windowed reads of non-georef TIFFs produce float64 half-pixel-shifted coords

2 participants