Skip to content

deps(deps): bump the uv-minor-patch group across 1 directory with 4 updates - #174

Merged
lhoupert merged 1 commit into
mainfrom
dependabot/uv/uv-minor-patch-fbb416c6b1
Jun 9, 2026
Merged

lhoupert merged 1 commit into
mainfrom
dependabot/uv/uv-minor-patch-fbb416c6b1

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github May 27, 2026 •

Copy link
Copy Markdown
Contributor

Bumps the uv-minor-patch group with 4 updates in the / directory: pydantic, numpy, cf-xarray and pymdown-extensions.

Updates pydantic from 2.13.3 to 2.13.4

Release notes

Sourced from pydantic's releases.

v2.13.4 2026-05-06

v2.13.4 (2026-05-06)

What's Changed

Packaging

Fixes

Full Changelog: pydantic/pydantic@v2.13.3...v2.13.4

Changelog

Sourced from pydantic's changelog.

v2.13.4 (2026-05-06)

GitHub release

What's Changed

Packaging

Fixes

Commits
  • cf67d4b Fix linting
  • f0d8a21 Prepare release v2.13.4
  • 5e3fe1d Check for pydantic tag pattern in CI
  • 7f9edcc Document tagging conventions
  • b46a0c9 Adapt pydantic-core linker flags on macOS
  • 50629c8 Update to PyPy 7.3.22
  • 8522ebb Preserve RootModel core metadata
  • a37f3af Adapt MISSING sentinel test to work with unreleased typing_extensions ver...
  • 909259a Remove Logfire example in documentation
  • 2c4174c Bump libc from 0.2.155 to 0.2.185
  • See full diff in compare view

Updates numpy from 2.4.4 to 2.4.6

Release notes

Sourced from numpy's releases.

v2.4.6 (May 18, 2026)

NumPy 2.4.6 Release Notes

NumPy 2.4.6 is a quick release that fixes a regression discovered in the 2.4.5 release.

This release supports Python versions 3.11-3.14

Contributors

A total of 4 people contributed to this release. People with a "+" by their names contributed a patch for the first time.

  • !EarlMilktea
  • Charles Harris
  • Sebastian Berg
  • Warren Weckesser

Pull requests merged

A total of 4 pull requests were merged for this release.

  • #31444: MAINT: Prepare 2.4.x for further development
  • #31453: BUG: Fix regression in arr.conj()
  • #31459: BUG: np.linalg.svd(..., hermitian=True) returns non-unitary...
  • #31460: BUG: Don't call INCREF/DECREF on descr in NpyStringAcquireAllocator...

v2.4.5 (May 15, 2026)

NumPy 2.4.5 Release Notes

NumPy 2.4.5 is a patch release that fixes bugs discovered after the 2.4.4 release, has some typing improvements, and maintains infrastructure.

This release supports Python versions 3.11-3.14

Contributors

A total of 17 people contributed to this release. People with a "+" by their names contributed a patch for the first time.

  • Aleksei Nikiforov
  • Anarion Zuo +
  • Ankit Ahlawat
  • Breno Favaretto +
  • Charles Harris
  • Igor Krivenko +
  • Ijtihed Kilani +
  • Joren Hammudoglu
  • Maarten Baert +

... (truncated)

Commits
  • b832a09 Merge pull request #31462 from charris/prepare-2.4.6
  • 57cc147 REL: Prepare for the NumPy 2.4.6 release
  • 0c72b0b Merge pull request #31459 from charris/backport-31347
  • 9778d26 BUG: core: Don't call INCREF/DECREF on descr in NpyStringAcquireAllocator. (#...
  • e0e3876 BUG: core: Don't call INCREF/DECREF on descr in NpyStringAcquireAllocator. (#...
  • d1bffeb BUG: np.linalg.svd(..., hermitian=True) returns non-unitary vh (#31347)
  • 8d8d7e5 Merge pull request #31453 from seberg/issue-31452
  • bddaab7 BUG: Fix regression in arr.conj()
  • 37a1ecc Merge pull request #31444 from charris/begin-2.4.6
  • 3c0e043 MAINT: Prepare 2.4.x for further development
  • Additional commits viewable in compare view

Updates cf-xarray from 0.11.0 to 0.11.1

Release notes

Sourced from cf-xarray's releases.

v0.11.1

What's Changed

Full Changelog: xarray-contrib/cf-xarray@v0.11.0...v0.11.1

Commits

Updates pymdown-extensions from 10.21.2 to 10.21.3

Release notes

Sourced from pymdown-extensions's releases.

10.21.3

  • FIX: Fix regression that allows a snippet to be loaded outside of the base path using directory traversal when restrict_base_path is enabled (the default). Found by @​gistrec.
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore <dependency name> major version will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
  • @dependabot ignore <dependency name> minor version will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
  • @dependabot ignore <dependency name> will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
  • @dependabot unignore <dependency name> will remove all of the ignore conditions of the specified dependency
  • @dependabot unignore <dependency name> <ignore condition> will remove the ignore condition of the specified dependency and ignore conditions

…pdates

Bumps the uv-minor-patch group with 4 updates in the / directory: [pydantic](https://github.com/pydantic/pydantic), [numpy](https://github.com/numpy/numpy), [cf-xarray](https://github.com/xarray-contrib/cf-xarray) and [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions).


Updates `pydantic` from 2.13.3 to 2.13.4
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](pydantic/pydantic@v2.13.3...v2.13.4)

Updates `numpy` from 2.4.4 to 2.4.6
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](numpy/numpy@v2.4.4...v2.4.6)

Updates `cf-xarray` from 0.11.0 to 0.11.1
- [Release notes](https://github.com/xarray-contrib/cf-xarray/releases)
- [Commits](xarray-contrib/cf-xarray@v0.11.0...v0.11.1)

Updates `pymdown-extensions` from 10.21.2 to 10.21.3
- [Release notes](https://github.com/facelessuser/pymdown-extensions/releases)
- [Commits](facelessuser/pymdown-extensions@10.21.2...10.21.3)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.13.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: uv-minor-patch
- dependency-name: numpy
  dependency-version: 2.4.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: uv-minor-patch
- dependency-name: cf-xarray
  dependency-version: 0.11.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: uv-minor-patch
- dependency-name: pymdown-extensions
  dependency-version: 10.21.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: uv-minor-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file python:uv Pull requests that update python:uv code labels May 27, 2026
@dependabot @github

dependabot Bot commented on behalf of github May 27, 2026

Copy link
Copy Markdown
Contributor Author

Assignees

The following users could not be added as assignees: developmentseed/data-model-developers. Either the username does not exist or it does not have the correct permissions to be added as an assignee.

Please fix the above issues or remove invalid values from dependabot.yml.

@dependabot dependabot Bot added dependencies Pull requests that update a dependency file python:uv Pull requests that update python:uv code labels May 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Security Audit Report

View workflow run

Bandit — Static Security Analysis (Security tab)

2 issue(s) found: 2 low

✅ No issues at or above HIGH severity.

2 low issue(s) below threshold not shown in table.

pip-audit — Dependency Vulnerabilities (Security tab)

Package Version ID Fix Versions Description
idna 3.13 CVE-2026-45409 3.15 This is the same issue as CVE-2024-3651, however the original remediation in 2024 was not a complete fix. Payloads such

1 vulnerability/vulnerabilities found (1 fixable) across 1 package(s).


Result: ❌ Blocking issues found — see details above.

@lhoupert
lhoupert merged commit c67bcec into main Jun 9, 2026
4 of 5 checks passed
@lhoupert
lhoupert deleted the dependabot/uv/uv-minor-patch-fbb416c6b1 branch June 9, 2026 12:26
@github-actions github-actions Bot mentioned this pull request Jun 9, 2026
emmanuelmathot added a commit that referenced this pull request Jun 16, 2026
* chore: release 0.9.0

* feat: implement scale-offset and data type casting via codecs

* fix: fix dependency declaration

* chore: use latest version of cast value

* chore: make cast-value a project dependency

* test: expand test coverage

* fix: upgrade pytest to 9.0.3 (CVE-2025-71176)

Fixes insecure /tmp directory handling on UNIX systems.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* - Use zarr-python's presumptive implementation of scale-offset
- use scalar map for handling NaN
- ensure that downsampled arrays use scalar map + cast value
- improve tests across parametrization of relelvant functions

* feat: add store-root spatial:bbox and tighten minispec requirements (#164)

* feat: add store-root spatial:bbox and tighten minispec requirements

Introduces a GeoZarr "Store Root" layer in the minispec so clients can read
a summary footprint without walking into child groups, and tightens the
multiscale profile so `spatial:bbox` at the root and `spatial:transform` +
`spatial:shape` on every layout entry are mandatory.

Adds a new `geozarr.store` pydantic module (`GeoZarrStoreAttrs`,
`GeoZarrMultiscaleGroupAttrs`, `GeoZarr`) enforcing the tightened profile,
and updates the S2 converter to union child-group bboxes into an EPSG:4326
footprint written at the store root.

Closes #156. Addresses the clear parts of #163; the array-level and
non-multiscale-group parts of that issue need further clarification.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* feat: update GeoZarr store model and optimize S2 converter for improved bbox handling

* Addresses review feedback on #164:

- Adds a "GeoZarr Hierarchy & Identification" subsection to the Store Root
  section of the minispec, codifying d-v-b's proposed rules: single root,
  root prefix ends with `.zarr`, suffix occurs at most once in the
  hierarchy, and explicit terminal-path conditions.
- Promotes the store-root CRS to mandatory: at least one of `proj:code`,
  `proj:wkt2`, `proj:projjson` MUST be set; there is no implicit EPSG:4326
  default. Per @vincentsarago's review.
- Promotes `zarr_conventions` declaration at the store root from
  RECOMMENDED to required.
- Converter now writes `proj:code: "EPSG:4326"` at the root alongside
  `spatial:bbox`.
- Pydantic `GeoZarrStoreAttrs` enforces the new CRS-required rule.

Cross-links the new hierarchy + spatial:extent follow-ups upstream:
zarr-developers/geozarr-spec#132 (hierarchy & root identification, also
addresses #124 URL parsing) and #133 (STAC-style spatial:extent).

---------

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>

* chore: uv.lock

* refactor: use zarr-python 3.2.0

* chore: lockfile

* fix: pin fill value based on data type instead of relying on xarray

* test: update tests to consistently use nan fill value for floats

* chore: bump urllib3 to 2.7.0

* chore: skip quicklook groups (#165)

* chore: group dependabot updates for actions and pip (#160)

* chore: group dependabot updates for actions and pip

- Group all GitHub Actions bumps (minor, patch, major) into one PR
- Group minor and patch pip bumps into one PR; major bumps remain
  ungrouped for individual review

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* chore: change dependabot schedule day to wednesday

* ci: add comment_on option to security auditing action

* ci: switch dependabot Python ecosystem from pip to uv

uv ecosystem reads uv.lock directly, enabling Dependabot to raise PRs
for lockfile-pinned versions including security patches.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

---------

Co-authored-by: Claude Sonnet 4.6 <[email protected]>

* fix: derive coarse spatial transforms from coordinates (#168)

* fix: derive coarse spatial transforms from coordinates

* refactor: improve function definitions for clarity and consistency

* chore: release 0.10.0 (#162)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* enh: deprecate v0, fix fill values and sanitize attributes (#172)

* fix(converter): align convert layout to S2, strip _eopf_attrs, fix _FillValue (#171)

- Remove TMS layout and /0 /1 /2 numeric overview groups from the general
  'convert' CLI; produce S2-style sibling r{2**level} overviews instead.
  'convert-s2-optimized' continues to emit the same layout.
- Drop the '--tile-width' CLI option.
- Sanitize array attrs in all converters: strip stale '_eopf_attrs' and
  source-only '_FillValue' / 'dtype' / 'valid_min' / 'valid_max' on decoded
  floats; rewrite 'units: digital_counts' -> 'units: 1'. The sanitizer now
  returns a new dict and callers reassign 'attrs', so stale keys are
  actually removed (a previous '.update()' pattern left them in place).
- Sanitize coord attrs too (datetime coords in /conditions/meteorology
  were leaking '_eopf_decode_datetime64' via '_eopf_attrs').
- Set '_FillValue' properly for float measurements so xarray's CF
  encoder produces the base64-NaN representation needed for
  'use_zarr_fill_value_as_mask=True' round-trip (xarray#11345). Fixes #171.
- Remove dead 'utils.encode_cf_fill_value' (no callers).
- Tests: open each r{N} multiscale level separately; migrate
  'test_multiscales_round_trip' from deprecated tms.Multiscales to
  zcm.Multiscales (with tuplify_json to handle extra='allow' fields).
- Regenerate snapshot fixtures; verified 0 '_eopf_attrs',
  'tile_matrix*', or 'digital_counts' markers across S2A/S2B/S2C.

* test: add guardrails for converter output attrs (#171)

Walks the snapshotted GroupSpec JSON fixtures and asserts:
- no '_eopf_attrs' anywhere;
- no 'tile_matrix*' markers (TMS removal);
- no 'units: digital_counts' on float arrays (decoded scale/offset);
- every float array under '/measurements/' has '_FillValue' set
  (required for CF NaN-mask round-trip, xarray#11345).

* docs: deprecate v0 references, document r{N} overview layout (#171)

- Drop 'tile_width' parameter from API examples (option removed from CLI/API).
- Replace '/measurements/r10m/{0,1,2}' nested-pyramid examples with the
  current flat 'r{2**level}' sibling-group layout.
- Remove the V0 vs V1 split in converter.md / examples.md / quickstart.md;
  the general 'convert' command now produces the same flat layout as
  'convert-s2-optimized'. The S2-optimized section is reframed as a
  feature description rather than a 'V1 vs V0' comparison.
- Update architecture.md multiscales snippet to the new model
  ('layout' / 'asset' / 'derived_from' / 'transform').
- Update faq.md inspection snippet to walk the new layout.

* refactor: clean up code formatting and enhance attribute handling in geozarr conversion

* fix(cli-e2e): drop deprecated --tile-width flag from tests and example

* refactor: remove deprecated v0 layout, sanitize attributes, and fix _FillValue handling

* test: add _FillValue masking roundtrip test (#172)

Addresses @vincentsarago's PR review: assert float arrays written with
the converter's _FillValue convention round-trip through xarray's
use_zarr_fill_value_as_mask=True so NaN cells come back masked.

* test: drive _FillValue masking test through create_geozarr_dataset (#172)

Replace the standalone xarray write with a real converter run: build a
small float DataTree, invoke create_geozarr_dataset, then reopen the
output with use_zarr_fill_value_as_mask=True and assert masking on the
nodata patch. Exercises the converter's _FillValue + encoding path
end-to-end, per @vincentsarago's review.

* fix: green CI for #172 — S2 convert flat layout + idna CVE (#177)

* fix(security): bump idna 3.13 -> 3.17 to clear CVE-2026-45409

pip-audit in the Security Audit workflow blocks on idna 3.13
(CVE-2026-45409, incomplete fix of CVE-2024-3651; fixed in 3.15).
Bump the transitive pin via uv lock. pip-audit now reports no
known vulnerabilities.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(cli): route Sentinel-2 convert through the optimized flat layout

Generic `convert` emitted GeoZarr-0.4 multiscales with native data at the
group root and overviews as nested r{2**N} sub-groups. That tree cannot be
opened by xr.open_datatree (an overview child's x/y conflict with the
parent's inherited x/y), so the `info` and `validate` CLI commands crashed
on the converter's own Sentinel-2 output.

Detect Sentinel-2 inputs and delegate to the existing, tested
convert_s2_optimized path, which emits flat sibling r{N}m levels (the layout
PR #172 documents). Non-S2 inputs keep the generic create_geozarr_dataset
path. Detection is best-effort and never aborts conversion.

Regenerate the geozarr_examples structure snapshots to the flat layout.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* test: make CLI e2e assertions debuggable and fix multiscale round-trip

- The CLI logs errors to stdout via structlog; assert on stdout+stderr in
  test_cli_e2e so a non-zero exit shows the actual error instead of an
  empty stderr.
- test_multiscale_attrs_round_trip tuplified only one side of its equality,
  so flat-layout level groups carrying list-valued attrs (e.g. spatial:bbox)
  broke a check that is only about JSON list/tuple normalisation. Tuplify
  both sides; the model still round-trips losslessly.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Loïc Houpert <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>

* ci(deps): bump the actions group across 1 directory with 8 updates (#167)

Bumps the actions group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [actions/checkout](https://github.com/actions/checkout) | `5.0.1` | `6.0.2` |
| [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) | `7.6.0` | `8.1.0` |
| [actions/cache](https://github.com/actions/cache) | `5.0.4` | `5.0.5` |
| [codecov/codecov-action](https://github.com/codecov/codecov-action) | `5.5.3` | `6.0.0` |
| [googleapis/release-please-action](https://github.com/googleapis/release-please-action) | `4.4.0` | `5.0.0` |
| [actions/upload-artifact](https://github.com/actions/upload-artifact) | `6.0.0` | `7.0.1` |
| [actions/download-artifact](https://github.com/actions/download-artifact) | `7.0.0` | `8.0.1` |
| [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) | `1.13.0` | `1.14.0` |



Updates `actions/checkout` from 5.0.1 to 6.0.2
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@93cb6ef...de0fac2)

Updates `astral-sh/setup-uv` from 7.6.0 to 8.1.0
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](astral-sh/setup-uv@37802ad...0880764)

Updates `actions/cache` from 5.0.4 to 5.0.5
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](actions/cache@6682284...27d5ce7)

Updates `codecov/codecov-action` from 5.5.3 to 6.0.0
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](codecov/codecov-action@1af5884...57e3a13)

Updates `googleapis/release-please-action` from 4.4.0 to 5.0.0
- [Release notes](https://github.com/googleapis/release-please-action/releases)
- [Changelog](https://github.com/googleapis/release-please-action/blob/main/CHANGELOG.md)
- [Commits](googleapis/release-please-action@16a9c90...45996ed)

Updates `actions/upload-artifact` from 6.0.0 to 7.0.1
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@b7c566a...043fb46)

Updates `actions/download-artifact` from 7.0.0 to 8.0.1
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@37930b1...3e5f45b)

Updates `pypa/gh-action-pypi-publish` from 1.13.0 to 1.14.0
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](pypa/gh-action-pypi-publish@ed0c539...cef2210)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: 5.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: actions/checkout
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: astral-sh/setup-uv
  dependency-version: 8.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: codecov/codecov-action
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: googleapis/release-please-action
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: pypa/gh-action-pypi-publish
  dependency-version: 1.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* deps(deps-dev): bump mypy from 1.20.2 to 2.1.0 (#170)

Bumps [mypy](https://github.com/python/mypy) from 1.20.2 to 2.1.0.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](python/mypy@v1.20.2...v2.1.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-version: 2.1.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* deps(deps): bump the uv-minor-patch group across 1 directory with 4 updates (#174)

Bumps the uv-minor-patch group with 4 updates in the / directory: [pydantic](https://github.com/pydantic/pydantic), [numpy](https://github.com/numpy/numpy), [cf-xarray](https://github.com/xarray-contrib/cf-xarray) and [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions).


Updates `pydantic` from 2.13.3 to 2.13.4
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](pydantic/pydantic@v2.13.3...v2.13.4)

Updates `numpy` from 2.4.4 to 2.4.6
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](numpy/numpy@v2.4.4...v2.4.6)

Updates `cf-xarray` from 0.11.0 to 0.11.1
- [Release notes](https://github.com/xarray-contrib/cf-xarray/releases)
- [Commits](xarray-contrib/cf-xarray@v0.11.0...v0.11.1)

Updates `pymdown-extensions` from 10.21.2 to 10.21.3
- [Release notes](https://github.com/facelessuser/pymdown-extensions/releases)
- [Commits](facelessuser/pymdown-extensions@10.21.2...10.21.3)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.13.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: uv-minor-patch
- dependency-name: numpy
  dependency-version: 2.4.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: uv-minor-patch
- dependency-name: cf-xarray
  dependency-version: 0.11.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: uv-minor-patch
- dependency-name: pymdown-extensions
  dependency-version: 10.21.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: uv-minor-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(deps): bump aiohttp to >=3.14.0 to resolve CVE-2026-34993 and CVE-2026-47265 (#182)

The Security Audit job's pip-audit step flagged aiohttp 3.13.5 for
CVE-2026-34993 and CVE-2026-47265 (both fixed in 3.14.0). Raise the
floor and refresh the lock to 3.14.1.

Co-authored-by: Claude Opus 4.8 <[email protected]>

* chore: release 0.10.1 (#181)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* ci(deps): bump the actions group with 3 updates (#185)

Bumps the actions group with 3 updates: [actions/checkout](https://github.com/actions/checkout), [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) and [codecov/codecov-action](https://github.com/codecov/codecov-action).


Updates `actions/checkout` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@de0fac2...df4cb1c)

Updates `astral-sh/setup-uv` from 8.1.0 to 8.2.0
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](astral-sh/setup-uv@0880764...fac544c)

Updates `codecov/codecov-action` from 6.0.0 to 6.0.1
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](codecov/codecov-action@57e3a13...e79a696)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: astral-sh/setup-uv
  dependency-version: 8.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: codecov/codecov-action
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Davis Vann Bennett <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: Emmanuel Mathot <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
lhoupert added a commit that referenced this pull request Sep 18, 2026
…uilders (#216)

* feat: Phase 0 — S1 GRD RTC design, S1Tiling integration, and real-data GeoTIFF→GeoZarr V3 prototype

- Implementation plan with Zarr V3 store structure, conventions, and phased roadmap
- S1Tiling Docker instructions with EODAG 4.0.0 patch (5 breaking changes fixed)
- GeoTIFF inspector script for S1Tiling output validation
- Synthetic prototype validating sharding, resize/append, overview generation
- Real-data conversion: 3 acquisitions (31TCH) → 1.8 GB Zarr store with 6 overview levels
- Key findings: chunk divisibility constraint, datetime format, coverage variability

* phase 0: integrate all lessons learned into plan and docs

- Update §2 array metadata example to actual zarr-python 3.1.1 on-disk format
  (sharding codec in codecs array, chunk_grid holds shard shape, inner chunks 366)
- Update conditions group: per-orbit naming (gamma_area_{orbit}), mark LIA and
  incidence_angle as aspirational (not confirmed as S1Tiling outputs)
- Add Operational Findings subsection: EODAG 4.0.0 patch, DEM tile extent,
  GAMMA_AREA filename format
- Fix docker instructions: CALIBRATION tag is GammaNaughtRTC (not gamma_naught_rtc)
- Mark Phase 0 checklist fully complete
- Update §9 API corrections to reflect plan is now corrected

* phase 1: S1 RTC Pydantic models aligned with S2 pattern

- Add src/eopf_geozarr/data_api/s1_rtc.py — Zarr V3 Pydantic models for
  S1 GRD γ0T RTC GeoZarr stores, using pyz.v3 GroupSpec/ArraySpec with
  TypedDict members (same pattern as s2.py uses pyz.v2)
- Models: S1RtcRoot, S1RtcOrbitGroup, S1RtcNativeResolutionDataset,
  S1RtcOverviewResolutionDataset, S1RtcConditionsGroup
- Validation: convention UUIDs, spatial:dimensions, multiscales layout,
  required data arrays (vv/vh/border_mask), gamma_area presence
- Add tests/_test_data/s1_rtc_examples/s1-grd-rtc-31TCH.json — realistic
  fixture with 3 timesteps, 6 overview levels, 3 gamma_area conditions
- Add tests/test_data_api/test_s1_rtc.py — 11 tests: round-trip, structure
  validation, negative cases (missing orbit, r10m, UUIDs, etc.)
- Add conftest fixture s1_rtc_json_example parametrized over all fixtures

* refactor: improve Pydantic model definitions and streamline imports in S1 RTC module

* docs: update Phase 1 checklist, add Phase 1 findings, link tracking issue #139

* fix: standardize spatial dimensions to lowercase in S1 RTC models and test cases

* fix: add 1D spatial coordinate arrays and use lowercase dimension names in S1 GRD RTC prototype

* feat: add detailed implementation plan for Phase 2 GeoTIFF ingestion pipeline

* fix: update Phase 2 GeoTIFF ingestion plan to include 1D spatial coordinate arrays and enforce lowercase dimension names

* feat: Phase 2 — S1 GRD RTC GeoTIFF ingestion pipeline

Productionise the GeoTIFF → Zarr V3 ingestion pipeline for S1Tiling
gamma-naught RTC outputs.

New module: src/eopf_geozarr/conversion/s1_ingest.py (~500 lines)
- extract_geotiff_metadata(): rasterio-based metadata extraction with
  tag validation and S1Tiling datetime normalisation
- create_s1_store(): Zarr V3 store creation with full GeoZarr conventions
  (multiscales, proj:, spatial:), sharded arrays, 1D spatial coordinate
  arrays at every resolution level
- ingest_s1tiling_acquisition(): main API — create-or-open store, read
  GeoTIFFs, generate overviews (average/nearest), write all levels,
  append coordinate variables. CRS/shape consistency validation on append.
- consolidate_s1_store(): post-batch metadata consolidation
- discover_s1tiling_acquisitions(): file discovery with grouping

Tests: tests/test_s1_rtc_ingest.py (27 tests)
- Metadata extraction, store creation, ingestion (create + append),
  data integrity, xarray roundtrip, CRS/shape mismatch rejection,
  consolidation, file discovery

Uses zarr_cm CMO dicts for convention metadata (not hardcoded UUIDs).
Reuses calculate_aligned_chunk_size() from utils.py.
Exports wired in conversion/__init__.py.

Refs: #139

* refactor: use zcm.Multiscales typed model per reviewer feedback

- Replace dict[str, Any] multiscales field with zcm.Multiscales import
- Remove inline MultiscalesTransform/ScaleLevel/Multiscales classes
- Update spatial:dimensions validator to lowercase ['y', 'x'] (Python 3.12)
- Remove typing_extensions import (native in Python 3.12)
- Update test assertions for Pydantic model attribute access
- Auto-format fixes from ruff (imports, fixture parens, line length)

* fix: configure ruff TC001 for Pydantic runtime-evaluated base classes

- Add [tool.ruff.lint.flake8-type-checking] runtime-evaluated-base-classes
  for pydantic.BaseModel so Pydantic field type imports aren't flagged
- Remove 4 stale noqa comments auto-fixed by ruff

* feat: implement S1Tiling conditions ingestion and discovery commands in CLI

* feat(stac): add build_s1_rtc_stac_item and generate-stac-s1 CLI (#173)

* feat(stac): add build_s1_rtc_stac_item and generate-stac-s1 CLI command

Adds eopf_geozarr.stac.s1_rtc.build_s1_rtc_stac_item, which reads a
consolidated S1 GRD RTC Zarr V3 store and returns a pystac.Item with
SAR/SAT/projection extensions, WGS84 bbox derived via pyproj, and
vv/vh asset sub-paths. Ascending orbit is preferred when both are present.

Also adds the generate-stac-s1 CLI subcommand that prints the item as JSON.
8 unit tests cover all acceptance criteria from the plan.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix: support s3:// URIs in S1Tiling discovery and ingest (#175)

Four functions in s1_ingest.py silently failed for s3:// paths:
- discover_s1tiling_acquisitions / discover_s1tiling_conditions used
  pathlib.Path.glob(), which normalises s3:// to s3:/ and returns 0 matches
- ingest_s1tiling_acquisition / ingest_s1tiling_conditions coerced paths via
  Path(), corrupting s3:// URIs, and called .exists() which always returns
  False for S3 (existence checks now use path_exists() from fs_utils)

Adds three private helpers:
- _list_tifs(): uses s3fs.S3FileSystem.glob for s3:// prefixes
- _coerce_input_path(): preserves str for s3://, returns Path otherwise
- _rasterio_env(): rasterio.Env(AWSSession) context for s3:// paths;
  strips https:// from AWS_ENDPOINT_URL since GDAL expects hostname only

Tested: rasterio.open("s3://...") confirmed working on OVH S3 with
AWS_S3_ENDPOINT or AWS_ENDPOINT_URL set; hostname-only required by GDAL.

Refs #139

Co-authored-by: Claude Sonnet 4.6 <[email protected]>

* fix(stac): point vv/vh asset hrefs to orbit group root; add tile_matrix_set

Per Emmanuel's review on #173:
- vv/vh asset hrefs now point to {store}/{orbit} (the zarr group that carries
  multiscales), matching how S2 reflectance assets point to measurements/reflectance.
  TiTiler reads tile_matrix_set from that group's multiscales attributes.
- create_s1_store now writes tile_matrix_set into the orbit group's multiscales
  so TiTiler can discover the tiling scheme without error.
- Updated test_asset_hrefs to assert the new orbit-group href.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix(ingest): add proj:code to resolution group attrs for TiTiler compatibility

TiTiler's _validate_zarr checks ds.rio.crs on the native resolution group
(r10m). Without proj:code in the resolution group's attrs, rioxarray returns
None and the group is excluded → groups=[] → bounds unpack error.

S2 zarr already has proj:code at the resolution group level; mirror that for S1.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix(s1): write CF grid_mapping, drop tile_matrix_set from RTC store (#176)

The deployed titiler-eopf reader resolves a group's CRS via rioxarray,
which reads a CF spatial_ref/crs_wkt coordinate -- not the GeoZarr
proj:code attr. S1 GRD RTC stores carried only proj:code, so rio.crs
was None and every multiscale group was rejected (HTTP 500 on
render/info). The S2 converter already writes a spatial_ref
grid-mapping; this brings S1 in line.

Also stop writing tile_matrix_set into the orbit-group multiscales: the
data-model owner confirmed it is not part of the S1 GRD RTC data model,
and the data_api reader already treats it as optional (MISSING default).

- add _add_grid_mapping() using pyproj CRS.to_cf() (the same source
  rioxarray uses, so no hard-coded projection) and call it for every
  resolution level (both store-creation paths) and the conditions group
- remove _create_tile_matrix_set() and its multiscales entry
- tests: assert no tile_matrix_set, spatial_ref present + rio.crs
  resolves to the native EPSG, conditions group carries grid_mapping

This fixes at the source what data-pipeline currently works around in
ingest_v1_s1_rtc.py (_patch_cf_grid_mapping / _patch_tile_matrix_limits).

Co-authored-by: Claude Opus 4.8 <[email protected]>

* Merge #172 (deprecate v0, fix fill values, sanitize attrs) into s1-rtc (#178)

* chore: release 0.9.0

* feat: implement scale-offset and data type casting via codecs

* fix: fix dependency declaration

* chore: use latest version of cast value

* chore: make cast-value a project dependency

* test: expand test coverage

* fix: upgrade pytest to 9.0.3 (CVE-2025-71176)

Fixes insecure /tmp directory handling on UNIX systems.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* - Use zarr-python's presumptive implementation of scale-offset
- use scalar map for handling NaN
- ensure that downsampled arrays use scalar map + cast value
- improve tests across parametrization of relelvant functions

* feat: add store-root spatial:bbox and tighten minispec requirements (#164)

* feat: add store-root spatial:bbox and tighten minispec requirements

Introduces a GeoZarr "Store Root" layer in the minispec so clients can read
a summary footprint without walking into child groups, and tightens the
multiscale profile so `spatial:bbox` at the root and `spatial:transform` +
`spatial:shape` on every layout entry are mandatory.

Adds a new `geozarr.store` pydantic module (`GeoZarrStoreAttrs`,
`GeoZarrMultiscaleGroupAttrs`, `GeoZarr`) enforcing the tightened profile,
and updates the S2 converter to union child-group bboxes into an EPSG:4326
footprint written at the store root.

Closes #156. Addresses the clear parts of #163; the array-level and
non-multiscale-group parts of that issue need further clarification.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* feat: update GeoZarr store model and optimize S2 converter for improved bbox handling

* Addresses review feedback on #164:

- Adds a "GeoZarr Hierarchy & Identification" subsection to the Store Root
  section of the minispec, codifying d-v-b's proposed rules: single root,
  root prefix ends with `.zarr`, suffix occurs at most once in the
  hierarchy, and explicit terminal-path conditions.
- Promotes the store-root CRS to mandatory: at least one of `proj:code`,
  `proj:wkt2`, `proj:projjson` MUST be set; there is no implicit EPSG:4326
  default. Per @vincentsarago's review.
- Promotes `zarr_conventions` declaration at the store root from
  RECOMMENDED to required.
- Converter now writes `proj:code: "EPSG:4326"` at the root alongside
  `spatial:bbox`.
- Pydantic `GeoZarrStoreAttrs` enforces the new CRS-required rule.

Cross-links the new hierarchy + spatial:extent follow-ups upstream:
zarr-developers/geozarr-spec#132 (hierarchy & root identification, also
addresses #124 URL parsing) and #133 (STAC-style spatial:extent).

---------

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>

* chore: uv.lock

* refactor: use zarr-python 3.2.0

* chore: lockfile

* fix: pin fill value based on data type instead of relying on xarray

* test: update tests to consistently use nan fill value for floats

* chore: bump urllib3 to 2.7.0

* chore: skip quicklook groups (#165)

* chore: group dependabot updates for actions and pip (#160)

* chore: group dependabot updates for actions and pip

- Group all GitHub Actions bumps (minor, patch, major) into one PR
- Group minor and patch pip bumps into one PR; major bumps remain
  ungrouped for individual review

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* chore: change dependabot schedule day to wednesday

* ci: add comment_on option to security auditing action

* ci: switch dependabot Python ecosystem from pip to uv

uv ecosystem reads uv.lock directly, enabling Dependabot to raise PRs
for lockfile-pinned versions including security patches.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

---------

Co-authored-by: Claude Sonnet 4.6 <[email protected]>

* fix: derive coarse spatial transforms from coordinates (#168)

* fix: derive coarse spatial transforms from coordinates

* refactor: improve function definitions for clarity and consistency

* chore: release 0.10.0 (#162)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(converter): align convert layout to S2, strip _eopf_attrs, fix _FillValue (#171)

- Remove TMS layout and /0 /1 /2 numeric overview groups from the general
  'convert' CLI; produce S2-style sibling r{2**level} overviews instead.
  'convert-s2-optimized' continues to emit the same layout.
- Drop the '--tile-width' CLI option.
- Sanitize array attrs in all converters: strip stale '_eopf_attrs' and
  source-only '_FillValue' / 'dtype' / 'valid_min' / 'valid_max' on decoded
  floats; rewrite 'units: digital_counts' -> 'units: 1'. The sanitizer now
  returns a new dict and callers reassign 'attrs', so stale keys are
  actually removed (a previous '.update()' pattern left them in place).
- Sanitize coord attrs too (datetime coords in /conditions/meteorology
  were leaking '_eopf_decode_datetime64' via '_eopf_attrs').
- Set '_FillValue' properly for float measurements so xarray's CF
  encoder produces the base64-NaN representation needed for
  'use_zarr_fill_value_as_mask=True' round-trip (xarray#11345). Fixes #171.
- Remove dead 'utils.encode_cf_fill_value' (no callers).
- Tests: open each r{N} multiscale level separately; migrate
  'test_multiscales_round_trip' from deprecated tms.Multiscales to
  zcm.Multiscales (with tuplify_json to handle extra='allow' fields).
- Regenerate snapshot fixtures; verified 0 '_eopf_attrs',
  'tile_matrix*', or 'digital_counts' markers across S2A/S2B/S2C.

* test: add guardrails for converter output attrs (#171)

Walks the snapshotted GroupSpec JSON fixtures and asserts:
- no '_eopf_attrs' anywhere;
- no 'tile_matrix*' markers (TMS removal);
- no 'units: digital_counts' on float arrays (decoded scale/offset);
- every float array under '/measurements/' has '_FillValue' set
  (required for CF NaN-mask round-trip, xarray#11345).

* docs: deprecate v0 references, document r{N} overview layout (#171)

- Drop 'tile_width' parameter from API examples (option removed from CLI/API).
- Replace '/measurements/r10m/{0,1,2}' nested-pyramid examples with the
  current flat 'r{2**level}' sibling-group layout.
- Remove the V0 vs V1 split in converter.md / examples.md / quickstart.md;
  the general 'convert' command now produces the same flat layout as
  'convert-s2-optimized'. The S2-optimized section is reframed as a
  feature description rather than a 'V1 vs V0' comparison.
- Update architecture.md multiscales snippet to the new model
  ('layout' / 'asset' / 'derived_from' / 'transform').
- Update faq.md inspection snippet to walk the new layout.

* refactor: clean up code formatting and enhance attribute handling in geozarr conversion

* fix(cli-e2e): drop deprecated --tile-width flag from tests and example

* refactor: remove deprecated v0 layout, sanitize attributes, and fix _FillValue handling

* test: add _FillValue masking roundtrip test (#172)

Addresses @vincentsarago's PR review: assert float arrays written with
the converter's _FillValue convention round-trip through xarray's
use_zarr_fill_value_as_mask=True so NaN cells come back masked.

* test: drive _FillValue masking test through create_geozarr_dataset (#172)

Replace the standalone xarray write with a real converter run: build a
small float DataTree, invoke create_geozarr_dataset, then reopen the
output with use_zarr_fill_value_as_mask=True and assert masking on the
nodata patch. Exercises the converter's _FillValue + encoding path
end-to-end, per @vincentsarago's review.

* fix: green CI for #172 — S2 convert flat layout + idna CVE (#177)

* fix(security): bump idna 3.13 -> 3.17 to clear CVE-2026-45409

pip-audit in the Security Audit workflow blocks on idna 3.13
(CVE-2026-45409, incomplete fix of CVE-2024-3651; fixed in 3.15).
Bump the transitive pin via uv lock. pip-audit now reports no
known vulnerabilities.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(cli): route Sentinel-2 convert through the optimized flat layout

Generic `convert` emitted GeoZarr-0.4 multiscales with native data at the
group root and overviews as nested r{2**N} sub-groups. That tree cannot be
opened by xr.open_datatree (an overview child's x/y conflict with the
parent's inherited x/y), so the `info` and `validate` CLI commands crashed
on the converter's own Sentinel-2 output.

Detect Sentinel-2 inputs and delegate to the existing, tested
convert_s2_optimized path, which emits flat sibling r{N}m levels (the layout
PR #172 documents). Non-S2 inputs keep the generic create_geozarr_dataset
path. Detection is best-effort and never aborts conversion.

Regenerate the geozarr_examples structure snapshots to the flat layout.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* test: make CLI e2e assertions debuggable and fix multiscale round-trip

- The CLI logs errors to stdout via structlog; assert on stdout+stderr in
  test_cli_e2e so a non-zero exit shows the actual error instead of an
  empty stderr.
- test_multiscale_attrs_round_trip tuplified only one side of its equality,
  so flat-layout level groups carrying list-valued attrs (e.g. spatial:bbox)
  broke a check that is only about JSON list/tuple normalisation. Tuplify
  both sides; the model still round-trips losslessly.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Davis Vann Bennett <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: Emmanuel Mathot <[email protected]>

---------

Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Davis Vann Bennett <[email protected]>
Co-authored-by: Emmanuel Mathot <[email protected]>

* Revert "feat(stac): add build_s1_rtc_stac_item and generate-stac-s1 CLI (#173)" (#179)

This reverts commit f749368ac3870790a69b250926f0af1aa6b58d0e.

* fix(s1-ingest): discover multi-frame acquisitions with a masked timestamp (#183) (#184)

discover_s1tiling_acquisitions returned 0 acquisitions for multi-frame S1Tiling
products: their GeoTIFF filenames mask the acquisition time as '…txxxxxx', which
the filename regex (\d{8}t\d{6}) rejected, so parse_s1tiling_filename returned
None and every file was skipped — silently, with no error.

- Relax the acq_stamp pattern to accept a masked time (\d{6}|x{6}), so masked
  files are no longer dropped.
- In discover, when the stamp is masked, resolve the real YYYYMMDDtHHMMSS stamp
  from the GeoTIFF ACQUISITION_DATETIME tag (via extract_geotiff_metadata) and
  use it for grouping + downstream STAC datetime.

This is the durable upstream fix for the data-pipeline #237 workaround (which
renamed masked products before discovery); that workaround can now be retired.

Closes #183.

Co-authored-by: Claude Opus 4.8 <[email protected]>

* chore: merge main into s1-tiling (#190)

* chore: release 0.9.0

* feat: implement scale-offset and data type casting via codecs

* fix: fix dependency declaration

* chore: use latest version of cast value

* chore: make cast-value a project dependency

* test: expand test coverage

* fix: upgrade pytest to 9.0.3 (CVE-2025-71176)

Fixes insecure /tmp directory handling on UNIX systems.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* - Use zarr-python's presumptive implementation of scale-offset
- use scalar map for handling NaN
- ensure that downsampled arrays use scalar map + cast value
- improve tests across parametrization of relelvant functions

* feat: add store-root spatial:bbox and tighten minispec requirements (#164)

* feat: add store-root spatial:bbox and tighten minispec requirements

Introduces a GeoZarr "Store Root" layer in the minispec so clients can read
a summary footprint without walking into child groups, and tightens the
multiscale profile so `spatial:bbox` at the root and `spatial:transform` +
`spatial:shape` on every layout entry are mandatory.

Adds a new `geozarr.store` pydantic module (`GeoZarrStoreAttrs`,
`GeoZarrMultiscaleGroupAttrs`, `GeoZarr`) enforcing the tightened profile,
and updates the S2 converter to union child-group bboxes into an EPSG:4326
footprint written at the store root.

Closes #156. Addresses the clear parts of #163; the array-level and
non-multiscale-group parts of that issue need further clarification.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* feat: update GeoZarr store model and optimize S2 converter for improved bbox handling

* Addresses review feedback on #164:

- Adds a "GeoZarr Hierarchy & Identification" subsection to the Store Root
  section of the minispec, codifying d-v-b's proposed rules: single root,
  root prefix ends with `.zarr`, suffix occurs at most once in the
  hierarchy, and explicit terminal-path conditions.
- Promotes the store-root CRS to mandatory: at least one of `proj:code`,
  `proj:wkt2`, `proj:projjson` MUST be set; there is no implicit EPSG:4326
  default. Per @vincentsarago's review.
- Promotes `zarr_conventions` declaration at the store root from
  RECOMMENDED to required.
- Converter now writes `proj:code: "EPSG:4326"` at the root alongside
  `spatial:bbox`.
- Pydantic `GeoZarrStoreAttrs` enforces the new CRS-required rule.

Cross-links the new hierarchy + spatial:extent follow-ups upstream:
zarr-developers/geozarr-spec#132 (hierarchy & root identification, also
addresses #124 URL parsing) and #133 (STAC-style spatial:extent).

---------

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>

* chore: uv.lock

* refactor: use zarr-python 3.2.0

* chore: lockfile

* fix: pin fill value based on data type instead of relying on xarray

* test: update tests to consistently use nan fill value for floats

* chore: bump urllib3 to 2.7.0

* chore: skip quicklook groups (#165)

* chore: group dependabot updates for actions and pip (#160)

* chore: group dependabot updates for actions and pip

- Group all GitHub Actions bumps (minor, patch, major) into one PR
- Group minor and patch pip bumps into one PR; major bumps remain
  ungrouped for individual review

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* chore: change dependabot schedule day to wednesday

* ci: add comment_on option to security auditing action

* ci: switch dependabot Python ecosystem from pip to uv

uv ecosystem reads uv.lock directly, enabling Dependabot to raise PRs
for lockfile-pinned versions including security patches.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

---------

Co-authored-by: Claude Sonnet 4.6 <[email protected]>

* fix: derive coarse spatial transforms from coordinates (#168)

* fix: derive coarse spatial transforms from coordinates

* refactor: improve function definitions for clarity and consistency

* chore: release 0.10.0 (#162)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* enh: deprecate v0, fix fill values and sanitize attributes (#172)

* fix(converter): align convert layout to S2, strip _eopf_attrs, fix _FillValue (#171)

- Remove TMS layout and /0 /1 /2 numeric overview groups from the general
  'convert' CLI; produce S2-style sibling r{2**level} overviews instead.
  'convert-s2-optimized' continues to emit the same layout.
- Drop the '--tile-width' CLI option.
- Sanitize array attrs in all converters: strip stale '_eopf_attrs' and
  source-only '_FillValue' / 'dtype' / 'valid_min' / 'valid_max' on decoded
  floats; rewrite 'units: digital_counts' -> 'units: 1'. The sanitizer now
  returns a new dict and callers reassign 'attrs', so stale keys are
  actually removed (a previous '.update()' pattern left them in place).
- Sanitize coord attrs too (datetime coords in /conditions/meteorology
  were leaking '_eopf_decode_datetime64' via '_eopf_attrs').
- Set '_FillValue' properly for float measurements so xarray's CF
  encoder produces the base64-NaN representation needed for
  'use_zarr_fill_value_as_mask=True' round-trip (xarray#11345). Fixes #171.
- Remove dead 'utils.encode_cf_fill_value' (no callers).
- Tests: open each r{N} multiscale level separately; migrate
  'test_multiscales_round_trip' from deprecated tms.Multiscales to
  zcm.Multiscales (with tuplify_json to handle extra='allow' fields).
- Regenerate snapshot fixtures; verified 0 '_eopf_attrs',
  'tile_matrix*', or 'digital_counts' markers across S2A/S2B/S2C.

* test: add guardrails for converter output attrs (#171)

Walks the snapshotted GroupSpec JSON fixtures and asserts:
- no '_eopf_attrs' anywhere;
- no 'tile_matrix*' markers (TMS removal);
- no 'units: digital_counts' on float arrays (decoded scale/offset);
- every float array under '/measurements/' has '_FillValue' set
  (required for CF NaN-mask round-trip, xarray#11345).

* docs: deprecate v0 references, document r{N} overview layout (#171)

- Drop 'tile_width' parameter from API examples (option removed from CLI/API).
- Replace '/measurements/r10m/{0,1,2}' nested-pyramid examples with the
  current flat 'r{2**level}' sibling-group layout.
- Remove the V0 vs V1 split in converter.md / examples.md / quickstart.md;
  the general 'convert' command now produces the same flat layout as
  'convert-s2-optimized'. The S2-optimized section is reframed as a
  feature description rather than a 'V1 vs V0' comparison.
- Update architecture.md multiscales snippet to the new model
  ('layout' / 'asset' / 'derived_from' / 'transform').
- Update faq.md inspection snippet to walk the new layout.

* refactor: clean up code formatting and enhance attribute handling in geozarr conversion

* fix(cli-e2e): drop deprecated --tile-width flag from tests and example

* refactor: remove deprecated v0 layout, sanitize attributes, and fix _FillValue handling

* test: add _FillValue masking roundtrip test (#172)

Addresses @vincentsarago's PR review: assert float arrays written with
the converter's _FillValue convention round-trip through xarray's
use_zarr_fill_value_as_mask=True so NaN cells come back masked.

* test: drive _FillValue masking test through create_geozarr_dataset (#172)

Replace the standalone xarray write with a real converter run: build a
small float DataTree, invoke create_geozarr_dataset, then reopen the
output with use_zarr_fill_value_as_mask=True and assert masking on the
nodata patch. Exercises the converter's _FillValue + encoding path
end-to-end, per @vincentsarago's review.

* fix: green CI for #172 — S2 convert flat layout + idna CVE (#177)

* fix(security): bump idna 3.13 -> 3.17 to clear CVE-2026-45409

pip-audit in the Security Audit workflow blocks on idna 3.13
(CVE-2026-45409, incomplete fix of CVE-2024-3651; fixed in 3.15).
Bump the transitive pin via uv lock. pip-audit now reports no
known vulnerabilities.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(cli): route Sentinel-2 convert through the optimized flat layout

Generic `convert` emitted GeoZarr-0.4 multiscales with native data at the
group root and overviews as nested r{2**N} sub-groups. That tree cannot be
opened by xr.open_datatree (an overview child's x/y conflict with the
parent's inherited x/y), so the `info` and `validate` CLI commands crashed
on the converter's own Sentinel-2 output.

Detect Sentinel-2 inputs and delegate to the existing, tested
convert_s2_optimized path, which emits flat sibling r{N}m levels (the layout
PR #172 documents). Non-S2 inputs keep the generic create_geozarr_dataset
path. Detection is best-effort and never aborts conversion.

Regenerate the geozarr_examples structure snapshots to the flat layout.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* test: make CLI e2e assertions debuggable and fix multiscale round-trip

- The CLI logs errors to stdout via structlog; assert on stdout+stderr in
  test_cli_e2e so a non-zero exit shows the actual error instead of an
  empty stderr.
- test_multiscale_attrs_round_trip tuplified only one side of its equality,
  so flat-layout level groups carrying list-valued attrs (e.g. spatial:bbox)
  broke a check that is only about JSON list/tuple normalisation. Tuplify
  both sides; the model still round-trips losslessly.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Loïc Houpert <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>

* ci(deps): bump the actions group across 1 directory with 8 updates (#167)

Bumps the actions group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [actions/checkout](https://github.com/actions/checkout) | `5.0.1` | `6.0.2` |
| [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) | `7.6.0` | `8.1.0` |
| [actions/cache](https://github.com/actions/cache) | `5.0.4` | `5.0.5` |
| [codecov/codecov-action](https://github.com/codecov/codecov-action) | `5.5.3` | `6.0.0` |
| [googleapis/release-please-action](https://github.com/googleapis/release-please-action) | `4.4.0` | `5.0.0` |
| [actions/upload-artifact](https://github.com/actions/upload-artifact) | `6.0.0` | `7.0.1` |
| [actions/download-artifact](https://github.com/actions/download-artifact) | `7.0.0` | `8.0.1` |
| [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) | `1.13.0` | `1.14.0` |



Updates `actions/checkout` from 5.0.1 to 6.0.2
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/93cb6efe18208431cddfb8368fd83d5badbf9bfd...de0fac2e4500dabe0009e67214ff5f5447ce83dd)

Updates `astral-sh/setup-uv` from 7.6.0 to 8.1.0
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/37802adc94f370d6bfd71619e3f0bf239e1f3b78...08807647e7069bb48b6ef5acd8ec9567f424441b)

Updates `actions/cache` from 5.0.4 to 5.0.5
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/668228422ae6a00e4ad889ee87cd7109ec5666a7...27d5ce7f107fe9357f9df03efb73ab90386fccae)

Updates `codecov/codecov-action` from 5.5.3 to 6.0.0
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/1af58845a975a7985b0beb0cbe6fbbb71a41dbad...57e3a136b779b570ffcdbf80b3bdc90e7fab3de2)

Updates `googleapis/release-please-action` from 4.4.0 to 5.0.0
- [Release notes](https://github.com/googleapis/release-please-action/releases)
- [Changelog](https://github.com/googleapis/release-please-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/release-please-action/compare/16a9c90856f42705d54a6fda1823352bdc62cf38...45996ed1f6d02564a971a2fa1b5860e934307cf7)

Updates `actions/upload-artifact` from 6.0.0 to 7.0.1
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/b7c566a772e6b6bfb58ed0dc250532a479d7789f...043fb46d1a93c77aae656e7c1c64a875d1fc6a0a)

Updates `actions/download-artifact` from 7.0.0 to 8.0.1
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/37930b1c2abaa49bbe596cd826c3c89aef350131...3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c)

Updates `pypa/gh-action-pypi-publish` from 1.13.0 to 1.14.0
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e...cef221092ed1bacb1cc03d23a2d87d1d172e277b)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: 5.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: actions/checkout
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: astral-sh/setup-uv
  dependency-version: 8.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: codecov/codecov-action
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: googleapis/release-please-action
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: pypa/gh-action-pypi-publish
  dependency-version: 1.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* deps(deps-dev): bump mypy from 1.20.2 to 2.1.0 (#170)

Bumps [mypy](https://github.com/python/mypy) from 1.20.2 to 2.1.0.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.20.2...v2.1.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-version: 2.1.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* deps(deps): bump the uv-minor-patch group across 1 directory with 4 updates (#174)

Bumps the uv-minor-patch group with 4 updates in the / directory: [pydantic](https://github.com/pydantic/pydantic), [numpy](https://github.com/numpy/numpy), [cf-xarray](https://github.com/xarray-contrib/cf-xarray) and [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions).


Updates `pydantic` from 2.13.3 to 2.13.4
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.13.3...v2.13.4)

Updates `numpy` from 2.4.4 to 2.4.6
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v2.4.4...v2.4.6)

Updates `cf-xarray` from 0.11.0 to 0.11.1
- [Release notes](https://github.com/xarray-contrib/cf-xarray/releases)
- [Commits](https://github.com/xarray-contrib/cf-xarray/compare/v0.11.0...v0.11.1)

Updates `pymdown-extensions` from 10.21.2 to 10.21.3
- [Release notes](https://github.com/facelessuser/pymdown-extensions/releases)
- [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.21.2...10.21.3)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.13.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: uv-minor-patch
- dependency-name: numpy
  dependency-version: 2.4.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: uv-minor-patch
- dependency-name: cf-xarray
  dependency-version: 0.11.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: uv-minor-patch
- dependency-name: pymdown-extensions
  dependency-version: 10.21.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: uv-minor-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(deps): bump aiohttp to >=3.14.0 to resolve CVE-2026-34993 and CVE-2026-47265 (#182)

The Security Audit job's pip-audit step flagged aiohttp 3.13.5 for
CVE-2026-34993 and CVE-2026-47265 (both fixed in 3.14.0). Raise the
floor and refresh the lock to 3.14.1.

Co-authored-by: Claude Opus 4.8 <[email protected]>

* chore: release 0.10.1 (#181)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* ci(deps): bump the actions group with 3 updates (#185)

Bumps the actions group with 3 updates: [actions/checkout](https://github.com/actions/checkout), [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) and [codecov/codecov-action](https://github.com/codecov/codecov-action).


Updates `actions/checkout` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/de0fac2e4500dabe0009e67214ff5f5447ce83dd...df4cb1c069e1874edd31b4311f1884172cec0e10)

Updates `astral-sh/setup-uv` from 8.1.0 to 8.2.0
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/08807647e7069bb48b6ef5acd8ec9567f424441b...fac544c07dec837d0ccb6301d7b5580bf5edae39)

Updates `codecov/codecov-action` from 6.0.0 to 6.0.1
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/57e3a136b779b570ffcdbf80b3bdc90e7fab3de2...e79a6962e0d4c0c17b229090214935d2e33f8354)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: astral-sh/setup-uv
  dependency-version: 8.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: codecov/codecov-action
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Davis Vann Bennett <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: Emmanuel Mathot <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(s1-rtc): STAC item builders + ingest fixes for TiTiler rendering (#180)

* feat(stac): add build_s1_rtc_stac_item and generate-stac-s1 CLI command

Adds eopf_geozarr.stac.s1_rtc.build_s1_rtc_stac_item, which reads a
consolidated S1 GRD RTC Zarr V3 store and returns a pystac.Item with
SAR/SAT/projection extensions, WGS84 bbox derived via pyproj, and
vv/vh asset sub-paths. Ascending orbit is preferred when both are present.

Also adds the generate-stac-s1 CLI subcommand that prints the item as JSON.
8 unit tests cover all acceptance criteria from the plan.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix: support s3:// URIs in S1Tiling discovery and ingest (#175)

Four functions in s1_ingest.py silently failed for s3:// paths:
- discover_s1tiling_acquisitions / discover_s1tiling_conditions used
  pathlib.Path.glob(), which normalises s3:// to s3:/ and returns 0 matches
- ingest_s1tiling_acquisition / ingest_s1tiling_conditions coerced paths via
  Path(), corrupting s3:// URIs, and called .exists() which always returns
  False for S3 (existence checks now use path_exists() from fs_utils)

Adds three private helpers:
- _list_tifs(): uses s3fs.S3FileSystem.glob for s3:// prefixes
- _coerce_input_path(): preserves str for s3://, returns Path otherwise
- _rasterio_env(): rasterio.Env(AWSSession) context for s3:// paths;
  strips https:// from AWS_ENDPOINT_URL since GDAL expects hostname only

Tested: rasterio.open("s3://...") confirmed working on OVH S3 with
AWS_S3_ENDPOINT or AWS_ENDPOINT_URL set; hostname-only required by GDAL.

Refs #139

Co-authored-by: Claude Sonnet 4.6 <[email protected]>

* fix(stac): point vv/vh asset hrefs to orbit group root; add tile_matrix_set

Per Emmanuel's review on #173:
- vv/vh asset hrefs now point to {store}/{orbit} (the zarr group that carries
  multiscales), matching how S2 reflectance assets point to measurements/reflectance.
  TiTiler reads tile_matrix_set from that group's multiscales attributes.
- create_s1_store now writes tile_matrix_set into the orbit group's multiscales
  so TiTiler can discover the tiling scheme without error.
- Updated test_asset_hrefs to assert the new orbit-group href.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix(ingest): add proj:code to resolution group attrs for TiTiler compatibility

TiTiler's _validate_zarr checks ds.rio.crs on the native resolution group
(r10m). Without proj:code in the resolution group's attrs, rioxarray returns
None and the group is excluded → groups=[] → bounds unpack error.

S2 zarr already has proj:code at the resolution group level; mirror that for S1.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix(s1): write CF grid_mapping, drop tile_matrix_set from RTC store (#176)

The deployed titiler-eopf reader resolves a group's CRS via rioxarray,
which reads a CF spatial_ref/crs_wkt coordinate -- not the GeoZarr
proj:code attr. S1 GRD RTC stores carried only proj:code, so rio.crs
was None and every multiscale group was rejected (HTTP 500 on
render/info). The S2 converter already writes a spatial_ref
grid-mapping; this brings S1 in line.

Also stop writing tile_matrix_set into the orbit-group multiscales: the
data-model owner confirmed it is not part of the S1 GRD RTC data model,
and the data_api reader already treats it as optional (MISSING default).

- add _add_grid_mapping() using pyproj CRS.to_cf() (the same source
  rioxarray uses, so no hard-coded projection) and call it for every
  resolution level (both store-creation paths) and the conditions group
- remove _create_tile_matrix_set() and its multiscales entry
- tests: assert no tile_matrix_set, spatial_ref present + rio.crs
  resolves to the native EPSG, conditions group carries grid_mapping

This fixes at the source what data-pipeline currently works around in
ingest_v1_s1_rtc.py (_patch_cf_grid_mapping / _patch_tile_matrix_limits).

Co-authored-by: Claude Opus 4.8 <[email protected]>

* Merge #172 (deprecate v0, fix fill values, sanitize attrs) into s1-rtc (#178)

* chore: release 0.9.0

* feat: implement scale-offset and data type casting via codecs

* fix: fix dependency declaration

* chore: use latest version of cast value

* chore: make cast-value a project dependency

* test: expand test coverage

* fix: upgrade pytest to 9.0.3 (CVE-2025-71176)

Fixes insecure /tmp directory handling on UNIX systems.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* - Use zarr-python's presumptive implementation of scale-offset
- use scalar map for handling NaN
- ensure that downsampled arrays use scalar map + cast value
- improve tests across parametrization of relelvant functions

* feat: add store-root spatial:bbox and tighten minispec requirements (#164)

* feat: add store-root spatial:bbox and tighten minispec requirements

Introduces a GeoZarr "Store Root" layer in the minispec so clients can read
a summary footprint without walking into child groups, and tightens the
multiscale profile so `spatial:bbox` at the root and `spatial:transform` +
`spatial:shape` on every layout entry are mandatory.

Adds a new `geozarr.store` pydantic module (`GeoZarrStoreAttrs`,
`GeoZarrMultiscaleGroupAttrs`, `GeoZarr`) enforcing the tightened profile,
and updates the S2 converter to union child-group bboxes into an EPSG:4326
footprint written at the store root.

Closes #156. Addresses the clear parts of #163; the array-level and
non-multiscale-group parts of that issue need further clarification.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* feat: update GeoZarr store model and optimize S2 converter for improved bbox handling

* Addresses review feedback on #164:

- Adds a "GeoZarr Hierarchy & Identification" subsection to the Store Root
  section of the minispec, codifying d-v-b's proposed rules: single root,
  root prefix ends with `.zarr`, suffix occurs at most once in the
  hierarchy, and explicit terminal-path conditions.
- Promotes the store-root CRS to mandatory: at least one of `proj:code`,
  `proj:wkt2`, `proj:projjson` MUST be set; there is no implicit EPSG:4326
  default. Per @vincentsarago's review.
- Promotes `zarr_conventions` declaration at the store root from
  RECOMMENDED to required.
- Converter now writes `proj:code: "EPSG:4326"` at the root alongside
  `spatial:bbox`.
- Pydantic `GeoZarrStoreAttrs` enforces the new CRS-required rule.

Cross-links the new hierarchy + spatial:extent follow-ups upstream:
zarr-developers/geozarr-spec#132 (hierarchy & root identification, also
addresses #124 URL parsing) and #133 (STAC-style spatial:extent).

---------

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>

* chore: uv.lock

* refactor: use zarr-python 3.2.0

* chore: lockfile

* fix: pin fill value based on data type instead of relying on xarray

* test: update tests to consistently use nan fill value for floats

* chore: bump urllib3 to 2.7.0

* chore: skip quicklook groups (#165)

* chore: group dependabot updates for actions and pip (#160)

* chore: group dependabot updates for actions and pip

- Group all GitHub Actions bumps (minor, patch, major) into one PR
- Group minor and patch pip bumps into one PR; major bumps remain
  ungrouped for individual review

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* chore: change dependabot schedule day to wednesday

* ci: add comment_on option to security auditing action

* ci: switch dependabot Python ecosystem from pip to uv

uv ecosystem reads uv.lock directly, enabling Dependabot to raise PRs
for lockfile-pinned versions including security patches.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

---------

Co-authored-by: Claude Sonnet 4.6 <[email protected]>

* fix: derive coarse spatial transforms from coordinates (#168)

* fix: derive coarse spatial transforms from coordinates

* refactor: improve function definitions for clarity and consistency

* chore: release 0.10.0 (#162)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(converter): align convert layout to S2, strip _eopf_attrs, fix _FillValue (#171)

- Remove TMS layout and /0 /1 /2 numeric overview groups from the general
  'convert' CLI; produce S2-style sibling r{2**level} overviews instead.
  'convert-s2-optimized' continues to emit the same layout.
- Drop the '--tile-width' CLI option.
- Sanitize array attrs in all converters: strip stale '_eopf_attrs' and
  source-only '_FillValue' / 'dtype' / 'valid_min' / 'valid_max' on decoded
  floats; rewrite 'units: digital_counts' -> 'units: 1'. The sanitizer now
  returns a new dict and callers reassign 'attrs', so stale keys are
  actually removed (a previous '.update()' pattern left them in place).
- Sanitize coord attrs too (datetime coords in /conditions/meteorology
  were leaking '_eopf_decode_datetime64' via '_eopf_attrs').
- Set '_FillValue' properly for float measurements so xarray's CF
  encoder produces the base64-NaN representation needed for
  'use_zarr_fill_value_as_mask=True' round-trip (xarray#11345). Fixes #171.
- Remove dead 'utils.encode_cf_fill_value' (no callers).
- Tests: open each r{N} multiscale level separately; migrate
  'test_multiscales_round_trip' from deprecated tms.Multiscales to
  zcm.Multiscales (with tuplify_json to handle extra='allow' fields).
- Regenerate snapshot fixtures; verified 0 '_eopf_attrs',
  'tile_matrix*', or 'digital_counts' markers across S2A/S2B/S2C.

* test: add guardrails for converter output attrs (#171)

Walks the snapshotted GroupSpec JSON fixtures and asserts:
- no '_eopf_attrs' anywhere;
- no 'tile_matrix*' markers (TMS removal);
- no 'units: digital_counts' on float arrays (decoded scale/offset);
- every float array under '/measurements/' has '_FillValue' set
  (required for CF NaN-mask round-trip, xarray#11345).

* docs: deprecate v0 references, document r{N} overview layout (#171)

- Drop 'tile_width' parameter from API examples (option removed from CLI/API).
- Replace '/measurements/r10m/{0,1,2}' nested-pyramid examples with the
  current flat 'r{2**level}' sibling-group layout.
- Remove the V0 vs V1 split in converter.md / examples.md / quickstart.md;
  the general 'convert' command now produces the same flat layout as
  'convert-s2-optimized'. The S2-optimized section is reframed as a
  feature description rather than a 'V1 vs V0' comparison.
- Update architecture.md multiscales snippet to the new model
  ('layout' / 'asset' / 'derived_from' / 'transform').
- Update faq.md inspection snippet to walk the new layout.

* refactor: clean up code formatting and enhance attribute handling in geozarr conversion

* fix(cli-e2e): drop deprecated --tile-width flag from tests and example

* refactor: remove deprecated v0 layout, sanitize attributes, and fix _FillValue handling

* test: add _FillValue masking roundtrip test (#172)

Addresses @vincentsarago's PR review: assert float arrays written with
the converter's _FillValue convention round-trip through xarray's
use_zarr_fill_value_as_mask=True so NaN cells come back masked.

* test: drive _FillValue masking test through create_geozarr_dataset (#172)

Replace the standalone xarray write with a real converter run: build a
small float DataTree, invoke create_geozarr_dataset, then reopen the
output with use_zarr_fill_value_as_mask=True and assert masking on the
nodata patch. Exercises the converter's _FillValue + encoding path
end-to-end, per @vincentsarago's review.

* fix: green CI for #172 — S2 convert flat layout + idna CVE (#177)

* fix(security): bump idna 3.13 -> 3.17 to clear CVE-2026-45409

pip-audit in the Security Audit workflow blocks on idna 3.13
(CVE-2026-45409, incomplete fix of CVE-2024-3651; fixed in 3.15).
Bump the transitive pin via uv lock. pip-audit now reports no
known vulnerabilities.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(cli): route Sentinel-2 convert through the optimized flat layout

Generic `convert` emitted GeoZarr-0.4 multiscales with native data at the
group root and overviews as nested r{2**N} sub-groups. That tree cannot be
opened by xr.open_datatree (an overview child's x/y conflict with the
parent's inherited x/y), so the `info` and `validate` CLI commands crashed
on the converter's own Sentinel-2 output.

Detect Sentinel-2 inputs and delegate to the existing, tested
convert_s2_optimized path, which emits flat sibling r{N}m levels (the layout
PR #172 documents). Non-S2 inputs keep the generic create_geozarr_dataset
path. Detection is best-effort and never aborts conversion.

Regenerate the geozarr_examples structure snapshots to the flat layout.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* test: make CLI e2e assertions debuggable and fix multiscale round-trip

- The CLI logs errors to stdout via structlog; assert on stdout+stderr in
  test_cli_e2e so a non-zero exit shows the actual error instead of an
  empty stderr.
- test_multiscale_attrs_round_trip tuplified only one side of its equality,
  so flat-layout level groups carrying list-valued attrs (e.g. spatial:bbox)
  broke a check that is only about JSON list/tuple normalisation. Tuplify
  both sides; the model still round-trips losslessly.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Davis Vann Bennett <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: Emmanuel Mathot <[email protected]>

* feat(s1-stac): add render extension with dual-pol RGB composite preview

The previous S1 RTC preview rendered a single VH band as grayscale with an
incorrect rescale (0,219), unsuitable for linear gamma0 RTC values. Declare a
render-extension `renders.rgb` config producing the standard dual-pol false-
colour composite (R=VV, G=VH, B=VV/VH ratio) with rescale 0–0.1 and tilesize
256, referencing the preferred orbit group. Downstream titiler-based services
consume this to generate previews/tiles/tilejson.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* refactor(s1-stac): emit a single rescale pair in the RGB render

A single [min, max] pair applies to all bands in titiler, so emitting three
identical pairs was redundant (and forced collapse logic in the consumer).
Same rendered output.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(s1-rtc): TEMPORARY name store as s1-rtc-{tile} to match titiler render path (#246)

titiler-eopf reconstructs the store path as
s3://{bucket}/tests-output/{collection}/{item_id}.zarr and ignores the STAC asset
href, so the GeoZarr cube must be named after the item id (s1-rtc-{tile}) for new
tiles to preview. Parse the tile from the s1-rtc- prefix (was s1-grd-rtc-); the
item id (s1-rtc-{tile}) is unchanged, so filename == item id by construction.

Pairs with the data-pipeline direct-write change (store written at the tests-output
path) which replaces the #250 auto-copy. Dev-phase, no consumers — disposable test
data. Revert to s1-grd-rtc- when titiler-eopf#108 (resolve store from href) lands.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(s1-rtc): build STAC item from a non-consolidated cube store

build_s1_rtc_stac_item opened the cube with zarr.open_consolidated, which
raises `ValueError: Consolidated metadata ... not found` when the store lacks
root consolidated metadata. A per-tile cube grown by appending a time-slice to
an existing same-orbit group ends up in exactly that state — re-consolidating an
append on the S3 store is unreliable — so live STAC registration failed on the
2nd+ same-orbit acquisition for a tile (it broke the Pyrenees S1 RTC soak).

The builder must not require consolidated metadata: titiler reads these stores
fine without it. Fall back to a direct zarr.open_group(mode="r") read when the
consolidated metadata is absent; re-raise any other ValueError.

Adds a regression test: build from a 2-slice same-orbit store created with
consolidate=False (fails before the fix in open_consolidated, passes after).

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(s1-rtc): CF-encode `time` at every level for datetime-based rendering (#192) (#193)

Per-acquisition previews render via titiler `sel=time={INTEGER index}` — a
positional index that breaks once a cube's time axis goes non-monotonic (a
cross-run append of an earlier-dated scene; proved on 31TEH: `[06-08, 06-07]`
→ the 06-07 item has no preview). Keeping positional indices correct would need
cube reorder + full re-registration on every append (not scalable).

The deployed titiler (v0.5.0) already does label-based `da.sel(..., method=...)`
via `open_datatree(decode_times=True)`; the only gap is the cube's `time` array,
which is a bare int64 with no CF datetime metadata, so it can't be a datetime
index. Encoding it lets previews select by exact datetime, which works even on a
non-monotonic axis — no reorder, register only the new item.

titiler picks a multiscale level by zoom (previews use a coarse level), so `time`
must resolve there. Putting it only at the orbit-group level and inheriting fails
to open while r10m stays bare int64 (AlignmentError: int64 vs datetime64 on the
shared dim). So encode `time` consistently at EVERY level:
- new `_create_time_coordinate_array` + `TIME_CF_ATTRS` (units/calendar/
  standard_name); created at every level in create + append-group paths.
- the append write resizes/writes `time` at every level (was r10m only).
- absolute_orbit/relative_orbit/platform stay at r10m (not selected on).
- stored dtype stays int64 ns, so register_per_acquisition's raw read is unaffected.

Adds TestTimeCFDatetime incl. exact datetime `.sel` on a non-monotonic axis at
the native and a coarse level. 55 passed.

Refs EOPF-Explorer/data-model#192

Co-authored-by: Claude Opus 4.8 <[email protected]>

* fix(s1-rtc): enrich STAC items & consolidate construction into the library (#195) (#196)

* fix(s1-rtc): enrich cube STAC item + fix vv/vh asset model (#195)

The S1 RTC cube STAC item carried duplicate vv/vh assets with byte-identical
hrefs (bug #1), exposed no asset for the descending orbit group of a dual-orbit
cube (bug #2), and was missing most of the metadata the S2 L2A reference carries.

Rework `build_s1_rtc_stac_item`:

- Asset model: replace the two indistinguishable vv/vh assets with one
  `gamma0-rtc-backscatter-{asc,desc}` asset per present orbit group, exposing
  VV/VH as named STAC 1.1 `bands` (no more duplicate hrefs), plus a
  `border-mask-{orbit}` asset for the valid-data mask. Each γ⁰ asset carries
  data_type/nodata/unit/gsd.
- Identity: add `constellation`, `instruments: [c-sar]`, `gsd` (platform stays
  per-acquisition — a cube can mix S1A/S1C).
- Projection: add proj:bbox, and proj:shape/proj:transform (best-effort from the
  r10m group attrs).
- Datacube extension: cube:dimensions (time as a bounded extent — not a values
  list, the cube grows by appending — plus x/y) + cube:variables.
- Descriptive: title/description; created (earliest acquisition, stable across
  rebuilds) / updated (build time) via the timestamps extension.
- Fold the corrected render rescale (0.0,0.2) into `_rgb_render`.

Out of scope (tracked follow-ups): statistics (#157), processing:software / DEM
lineage, and per-product CDSE fields are not in the store; per-acquisition item
construction is moved in a follow-up commit.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* feat(s1-rtc): move per-acquisition + coverage STAC construction into the library

Consolidate the *pure* S1 RTC STAC construction in eopf_geozarr.stac.s1_rtc so
item shape has a single owner (the library); registration (STAC-API upsert, S3
alternates, gateway rewrite, TiTiler links, CDSE discovery) stays in
data-pipeline.

Adds, ported from the data-pipeline scripts (deployment URLs stripped):

- acquisition_id(tile_id, when)
- build_s1_rtc_per_acquisition_items(store, *, orbit, collection_id) ->
  list[pystac.Item]: one single-datetime item per cube time slice of an orbit,
  reoriented to that orbit (sat:orbit_state + render expression + only that
  orbit's γ⁰/mask assets), per-slice platform, datacube structure dropped (a
  single acquisition is not a cube). Deployment-agnostic: carries the render
  config + datetime; the registration layer derives the cube-endpoint TiTiler
  links (sel=time={datetime}) from it.
- Slice / pick_slice / slice_coverages: coverage-based preview-slice selection
  (reads border_mask at r720m).

Ported the construction unit tests (pick_slice, slice_coverages, and the
per-acquisition construction subset); the link/thumbnail/alternate-asset and
rescale-override tests stay in data-pipeline as registration concerns.

Follow-up (separate data-pipeline PR): bump the data-model pin, delete the moved
functions from register_per_acquisition.py / register_v1_s1_rtc.py, and rewire
them to consume this library output.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(s1-rtc): per-acquisition items carry their own orbit footprint

Review follow-up. A per-acquisition item cloned the cube base, so on a dual-orbit
cube every item inherited the WGS84 union bbox/geometry of both orbits and the
preferred (ascending) orbit's proj:bbox — advertising a footprint wider than, and
a data bbox from the wrong orbit relative to, the acquisition's own orbit.

Recompute bbox/geometry/proj:bbox from the run orbit's spatial:bbox (factored the
bbox->polygon construction into `_bbox_to_geometry`, shared with the cube builder),
and give per-acquisition items a single-acquisition description rather than the
cube's "datacube" wording.

Tests: per-acq footprint = run orbit (not the union); `created` = earliest
acquisition (idempotent across rebuilds); ValueError on an invalid or absent orbit.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(s1-rtc): omit orbit_state on dual-orbit cubes; expose datacube dimension sizes

Two review observations from inspecting the live items:

- sat:orbit_state is single-valued, so on a dual-orbit cube (both ascending and
  descending groups present) a single value mislabels half the slices. Set it
  (and declare the SAT extension) only for a single-orbit cube; per-acquisition
  items, which are single-orbit, still carry the real per-orbit value. Ensure
  per-acq items declare the SAT extension even when cloned from a dual-orbit base
  that omitted it.

- cube:dimensions previously carried only `extent`, so the number of elements per
  dimension wasn't visible. The time axis is irregularly sampled, so list its
  discrete `values` (count = number of acquisitions); the regular x/y axes get a
  `step` (size derivable from extent; exact pixel count is also in proj:shape).

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(s1-rtc): datacube time axis carries extent only (drop values enumeration)

Enumerating every acquisition as cube:dimensions.time.values does not scale as the
cube is appended over the mission. No STAC extension provides a scalable temporal
element-count field (datacube temporal_dimension offers only values/step/extent,
and step is null for S1's irregular sampling; projection proj:shape is spatial-only;
raster has no dimension shape). So the time axis carries a bounded `extent` only.

x/y element counts remain available via proj:shape (exact) and the datacube x/y
extent+step. The number of acquisitions is obtained from the per-acquisition
collection (STAC search count), which stays correct as the cube grows.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(s1-rtc): correct datacube variable_type, platform value, created, time values

Review of the live items surfaced several metadata issues:

- cube:variables used `type` (a non-standard, silently-ignored key) instead of the
  datacube extension's `variable_type`; border_mask is now `auxiliary`, vv/vh `data`.
- per-acquisition `platform` carried the store's short code (e.g. `s1a`); normalize
  to the STAC convention `sentinel-1a` (mirrors the Sentinel-2 reference).
- `created` (timestamps ext) was set to an acquisition time, which misuses the field
  (it means the item's creation instant); omit it and keep `updated` = build time.
- the cube's irregular time axis now lists its discrete `values` (the acquisition
  instants) so the number of time steps is visible; the regular x/y axes stay
  extent + step (their pixel count is in proj:shape).

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* refactor(s1-rtc): robust EPSG parse for datacube reference_system + clarify per-acq proj inheritance

Review follow-ups (no behaviour change for the EPSG:NNNN stores in use):
- derive the datacube reference_system via pyproj.CRS(...).to_epsg() instead of
  splitting the proj:code string, so non-"EPSG:NNNN" CRS forms don't break it.
- comment why per-acquisition items recompute proj:bbox from their orbit but
  inherit proj:code/shape/transform (the MGRS grid is shared across orbits).

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* docs(s1-rtc): note the dual-orbit time axis spans both orbits in the datacube

A dual-orbit cube merges two per-orbit sub-cubes (disjoint time axes) onto a shared
grid. Modelling orbit as a dimension would imply a sparse, mostly-empty orbit×time
grid; instead orbit stays an attribute of each acquisition, conveyed via the per-orbit
assets. Add a `description` on the time dimension so the merged axis isn't misread.
Single-orbit cubes …
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file python:uv Pull requests that update python:uv code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant