Skip to content

Skip mask IFDs when resolving overview_level (#1504)#1518

Open
brendancol wants to merge 1 commit intoxarray-contrib:mainfrom
brendancol:fix-1504-overview-filter
Open

Skip mask IFDs when resolving overview_level (#1504)#1518
brendancol wants to merge 1 commit intoxarray-contrib:mainfrom
brendancol:fix-1504-overview-filter

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Closes #1504.

open_geotiff(path, overview_level=N) was indexing the IFD list directly. When a TIFF puts a transparency-mask IFD (NewSubfileType bit 2) between the full-res IFD and the overviews, as some GDAL COGs do, overview_level=1 returned the 1-bit mask instead of the first overview.

The reader now filters out IFDs whose NewSubfileType has bit 2 set before indexing, matching what GDAL and rasterio do. An out-of-range overview_level raises ValueError with the actual count of non-mask IFDs (and any mask IFDs present), instead of silently clamping to the last IFD.

Changes:

  • IFD.subfile_type and IFD.is_mask properties on the parsed IFD
  • select_overview_ifd helper in _header.py
  • routed the four overview_level call sites (local CPU read, COG-over-HTTP, GPU read, _read_geo_info) through the helper

Tests in xrspatial/geotiff/tests/test_overview_filter.py write a 3-IFD TIFF with a mask wedged between the full-res IFD and an overview, then check that open_geotiff(..., overview_level=1) returns the overview rather than the mask. The ValueError path and a normal 4-IFD COG (no masks) are covered too.

open_geotiff(path, overview_level=N) was indexing the IFD list directly.
When a TIFF puts a transparency-mask IFD (NewSubfileType bit 2) between
the full-res IFD and the overviews, as some GDAL COGs do,
overview_level=1 returned the 1-bit mask instead of the first overview.

The reader now filters out IFDs whose NewSubfileType has bit 2 set
before indexing, matching what GDAL and rasterio do. Out-of-range
overview_level raises ValueError with the actual count of non-mask IFDs
(and any masks) instead of silently clamping to the last IFD.

- IFD.subfile_type and IFD.is_mask properties on parsed IFDs
- select_overview_ifd helper in _header.py
- routed the four overview_level call sites (CPU read, COG-over-HTTP,
  GPU read, _read_geo_info) through the helper
@github-actions github-actions Bot added the performance PR touches performance-sensitive code label May 8, 2026
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.

overview_level indexes IFDs blindly, returns wrong layer when mask/transparency IFDs are interleaved

1 participant