Skip to content

Promote COG support to ready/stable with compliance and parity gates #2286

@brendancol

Description

@brendancol

Goal

Move COG support from "use with caution" to a clearly documented ready/stable state, starting with a narrow production contract and expanding only when the matching test gates exist.

COG should be split into separate readiness tracks because the risks are different:

  • COG writer: to_geotiff(..., cog=True)
  • COG reader: local COG reads
  • HTTP/range COG reader: remote COG reads, windowed reads, and dask graph reads

Proposed stable scope for the first promotion

Promote only this subset first:

  • Axis-aligned 2D/3D rasters.
  • CPU writer and CPU reader paths.
  • Stable codecs only: none, deflate, lzw, zstd, and packbits if current interop coverage supports it.
  • Internal overviews only.
  • Normal CRS, transform, dtype, nodata, band, and pixel-is-area / pixel-is-point behavior.

Keep these out of the first stable claim:

  • GPU COG read/write.
  • Experimental codecs: lerc, jpeg2000 / j2k, lz4.
  • Internal-only JPEG.
  • Rotated transforms.
  • External .tif.ovr sidecars.
  • File-like destinations with cog=True.
  • Any BigTIFF COG behavior not covered by external validation.

Implementation plan

1. Define the COG readiness contract in code and docs

  • Split broad feature tiering into more precise entries, e.g. writer.cog, reader.local_cog, reader.http_cog.
  • Keep HTTP/range COG separate from local COG because it has additional transport, range, coalescing, and security behavior.
  • Update SUPPORTED_FEATURES only after test gates are in place.

Relevant files:

  • xrspatial/geotiff/_attrs.py
  • docs/source/reference/geotiff.rst
  • examples/user_guide/52_COG_Overview_Generation.ipynb

2. Add an external COG compliance gate for the writer

For COGs written by to_geotiff(..., cog=True), assert external interoperability, not just xrspatial self-read.

Test matrix should cover:

  • Stable codecs.
  • Integer and float dtypes.
  • Single-band and multiband rasters.
  • Nodata sentinel and NaN nodata cases.
  • Pixel-is-area and pixel-is-point georeferencing.
  • Explicit and auto-generated overview levels.
  • Overview resampling modes currently intended to be stable.

Assertions should include:

  • Rasterio can open the file.
  • Base pixels match expected data.
  • Overview count and dimensions match expected levels.
  • Overview pixels match expected values or documented tolerances.
  • CRS, transform, nodata, dtype, band count, and dims survive.
  • TIFF layout is COG-like: tiled layout, overview IFDs, sane tile offsets, IFDs placed before data.
  • Optional when available: validate with GDAL/rio-cogeo and skip cleanly when unavailable.

Likely test files:

  • xrspatial/geotiff/tests/test_cog.py
  • xrspatial/geotiff/tests/test_golden_corpus_overview_cog_1930.py
  • new xrspatial/geotiff/tests/test_cog_writer_compliance.py

3. Promote local COG writer/read before HTTP COG

Acceptance criteria for local COG readiness:

  • xrspatial write COG -> xrspatial read passes.
  • xrspatial write COG -> rasterio read passes.
  • rasterio/GDAL COG fixture -> xrspatial read passes.
  • Stable codec matrix passes.
  • Nodata-aware overviews pass for integer and float rasters.
  • Overview georef inheritance works.
  • Pixel-is-point / pixel-is-area behavior is pinned.
  • BigTIFF COG is either covered and promoted or explicitly remains advanced.

After this gate passes, writer.cog and possibly reader.local_cog can be promoted for the scoped contract.

4. Harden HTTP/range COG separately

HTTP COG should become ready only after range behavior is part of the contract.

Acceptance criteria:

  • Windowed reads fetch only intersecting tiles/strips.
  • Overview reads fetch overview data, not full-resolution data.
  • band= on multiband planar/interleaved COGs returns correct pixels and avoids unnecessary reads where possible.
  • Dask COG reads parse metadata once per graph, not once per chunk task.
  • Tests assert range count and total bytes fetched.
  • Redirect/private-host/security behavior stays fail-closed.
  • Truncated/malformed COGs close HTTP resources reliably.
  • Coalescing behavior is bounded and configurable.

Existing relevant tests:

  • xrspatial/geotiff/tests/test_http_cog_coalesce.py
  • xrspatial/geotiff/tests/test_cog_http_parallel_decode_2026_05_15.py
  • xrspatial/geotiff/tests/test_http_range_validation_1735.py
  • xrspatial/geotiff/tests/test_cog_http_close_on_error_1816.py
  • xrspatial/geotiff/tests/test_http_window_band_planar_1669.py
  • xrspatial/geotiff/tests/test_http_stripped_window_max_pixels_issue_A_1842.py

5. Add COG to the parity/release gate

Create a dedicated COG parity file or extend the backend parity suite with a focused COG layer.

Required rows:

  • xrspatial write COG -> xrspatial eager read
  • xrspatial write COG -> xrspatial dask read
  • xrspatial write COG -> rasterio read
  • golden/rasterio COG fixture -> xrspatial local read
  • golden/rasterio COG fixture -> xrspatial HTTP range read
  • golden/rasterio COG fixture -> xrspatial dask HTTP range read

Keep skip reasons explicit and issue-linked. Avoid silent skips for dependency-gated rows.

Relevant existing gate:

  • xrspatial/geotiff/tests/test_backend_full_parity_2211.py

6. Promote docs and feature tiers last

Only after the gates above pass:

  • Move the scoped COG features from advanced to stable.
  • Document exactly what is stable.
  • Leave unsupported or not-yet-certified combinations marked advanced/experimental.
  • Add release notes describing the COG stability contract.

Done when

  • The scoped COG writer compliance test passes against rasterio and, where available, GDAL/rio-cogeo.
  • Local COG read/write parity is in the release gate.
  • HTTP COG range behavior has explicit byte/range assertions or remains advanced.
  • SUPPORTED_FEATURES reflects the split stable/advanced contract.
  • Docs state the stable COG subset and the remaining caveats.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions