Skip to main content

COG Virtualization

Status

COG is a first-class read_geo source: read_geo('path.tif') and read_zarr_metadata('path.tif') return georeferenced, type-correct results.

Eider can read a Cloud Optimized GeoTIFF (COG) without downloading or converting it, by presenting the COG to the zarrs pipeline as if it were a Zarr array.

How it works​

  1. Parse the TIFF. geozarr_core/src/cog.rs reads the byte-order mark and IFD offset, then extracts the tags it needs: image width/length (256/257), tile width/length (322/323), and the per-tile offsets and byte counts (324/325).
  2. Synthesize a virtual Zarr array. VirtualCogStore (geozarr_core/src/virtual_store.rs) generates .zmetadata and .zarray JSON in memory describing an array whose chunk shape equals the COG's tile shape.
  3. Map chunks to byte-ranges. A Zarr chunk key of the form "y.x" is translated to the corresponding tile's (offset, length) and served by a single OpenDAL range GET; zarrs then decodes the tile lazily.

resolve_sync_store (geozarr_core/src/store.rs) detects .tif/.tiff paths and instantiates the VirtualCogStore automatically, so the rest of the pipeline is identical to reading a real Zarr array.

Supported & limitations​

Supported: single-band and multi-band GeoTIFFs (up to 4 bands, parsed into an additional band dimension); uncompressed and Deflate-compressed tiles (predictor=1); the GeoTIFF affine (ModelPixelScale/ModelTiepoint or ModelTransformation) and CRS (GeoKeyDirectory). For a geographic CRS (EPSG:4326) the dimensions are lat/lon, so lat_min/lon_max bounding-box pushdown applies; the value column uses the COG's real data type. If the file is a JPEG2000 (.jp2), Eider can parse its structure with fallback heuristics.

Not yet supported: Unsupported bit-depths, sample formats, and compression schemes (LZW/JPEG/WebP, horizontal-differencing predictors) return a clear error at open time rather than silently misreading the data. CRS reprojection is the one exception: projected COGs are read in their native CRS with y/x dimensions, so geographic bounding-box pushdown simply does not apply (no error — the data is read correctly, just without geographic bbox pruning).

STAC​

Single Item​

A single STAC Item is a first-class read_geo source. Eider fetches the Item (local path or http(s)://), composes its COG assets as a virtual Zarr group, and you select one asset:

SELECT * FROM read_geo('item.json', asset := 'red');

If the Item has exactly one COG asset it is selected automatically; with multiple assets and none chosen, read_geo errors listing the available asset names. Each selected asset is read with the full COG pipeline (real dtype, GeoTIFF affine, CRS).

ItemCollection time-stacking​

A STAC ItemCollection / FeatureCollection (a local file or a single http(s):// response) stacks the selected asset across its Items into a 3D [time, lat, lon] array. The time coordinate is epoch seconds parsed from each Item's properties.datetime; Items are sorted ascending, so time_min/time_max (given as epoch seconds) filter by real time:

SELECT * FROM read_geo('itemcollection.json', asset := 'red',
time_min := 1767225600, time_max := 1769904000);

For remote STAC Search APIs, DuckDB's spatial (lat_min/lon_max) and temporal (time_min/time_max) bounding filters are aggressively pushed down to the server by translating them into STAC bbox and datetime query parameters. This ensures Eider only downloads metadata and data for STAC Items that actually intersect your query. The API response is fully paginated natively — Eider will eagerly traverse rel:next links to fetch the full matching collection.

The collection must be grid-uniform — every Item and asset must share the same shape, affine, and CRS, and each asset's data type must be uniform across Items. If anything differs, read_geo errors naming the offending Item rather than silently misaligning slices (there is no regridding or reprojection).

Not yet supported: multi-resolution collections (assets with differing grids); Items without properties.datetime; stacking multiple assets into one array; and STAC Collection/Catalog traversal — these return a clear error.