|
| 1 | +"""Datacube STAC extension: cube:dimensions and cube:variables. |
| 2 | +
|
| 3 | +Expected metadata shapes (the contract the file scanners must meet): |
| 4 | +
|
| 5 | + metadata["dimensions"]: dict mapping dimension name to a STAC datacube |
| 6 | + Dimension Object, e.g. |
| 7 | + {"time": {"type": "temporal", "extent": ["2000-01-01T00:00:00Z", |
| 8 | + "2000-12-31T00:00:00Z"]}, |
| 9 | + "lon": {"type": "spatial", "axis": "x", "extent": [-180.0, 180.0]}} |
| 10 | +
|
| 11 | + metadata["variables"]: list of dicts with keys |
| 12 | + name (str, required — entries without it are skipped), |
| 13 | + dimensions (list[str]), units (str), and any of |
| 14 | + description / long_name / standard_name (str). |
| 15 | +
|
| 16 | +The v2.2.0 schema requires cube:dimensions whenever the extension URL is |
| 17 | +declared, so the extension is a no-op unless dimensions are present. |
| 18 | +""" |
| 19 | + |
| 20 | +from __future__ import annotations |
| 21 | + |
| 22 | +from typing import TYPE_CHECKING |
| 23 | + |
| 24 | +from esm_catalog.registry import EXTENSION_URLS |
| 25 | + |
| 26 | +if TYPE_CHECKING: |
| 27 | + import pystac |
| 28 | + |
| 29 | + |
| 30 | +def add_datacube_extension(item: "pystac.Item", metadata: dict) -> None: |
| 31 | + """Inject datacube extension fields into *item* from *metadata*. |
| 32 | +
|
| 33 | + Sets item.properties["cube:dimensions"] (pass-through) and |
| 34 | + item.properties["cube:variables"] (mapped), and appends the datacube |
| 35 | + schema URL to item.stac_extensions (idempotent). No-op when *metadata* |
| 36 | + carries no dimensions. |
| 37 | + """ |
| 38 | + dims = metadata.get("dimensions", {}) |
| 39 | + if not dims: |
| 40 | + return |
| 41 | + |
| 42 | + item.properties["cube:dimensions"] = dims |
| 43 | + |
| 44 | + cube_vars = _to_cube_variables(metadata.get("variables", [])) |
| 45 | + if cube_vars: |
| 46 | + item.properties["cube:variables"] = cube_vars |
| 47 | + |
| 48 | + url = EXTENSION_URLS["datacube"] |
| 49 | + if url not in item.stac_extensions: |
| 50 | + item.stac_extensions.append(url) |
| 51 | + |
| 52 | + |
| 53 | +def _to_cube_variables(variables: list) -> dict: |
| 54 | + """Map scanner variable entries to datacube Variable Objects.""" |
| 55 | + cube_vars = {} |
| 56 | + for v in variables: |
| 57 | + name = v.get("name") |
| 58 | + if not name: |
| 59 | + continue |
| 60 | + entry: dict = {"dimensions": v.get("dimensions", [])} |
| 61 | + if "units" in v: |
| 62 | + entry["unit"] = v["units"] |
| 63 | + for key in ("description", "long_name", "standard_name"): |
| 64 | + if key in v: |
| 65 | + entry["description"] = v[key] |
| 66 | + break |
| 67 | + cube_vars[name] = entry |
| 68 | + return cube_vars |
0 commit comments