Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
765a0b5
feat: Phase 0 — S1 GRD RTC design, S1Tiling integration, and real-dat…
emmanuelmathot Mar 23, 2026
3803f4c
phase 0: integrate all lessons learned into plan and docs
emmanuelmathot Mar 23, 2026
bea1fe3
phase 1: S1 RTC Pydantic models aligned with S2 pattern
emmanuelmathot Mar 23, 2026
91c8dbc
refactor: improve Pydantic model definitions and streamline imports i…
emmanuelmathot Mar 23, 2026
fa39186
docs: update Phase 1 checklist, add Phase 1 findings, link tracking i…
emmanuelmathot Mar 24, 2026
0f4a808
fix: standardize spatial dimensions to lowercase in S1 RTC models and…
emmanuelmathot Mar 24, 2026
2ff9c21
fix: add 1D spatial coordinate arrays and use lowercase dimension nam…
emmanuelmathot Mar 24, 2026
2478bfd
feat: add detailed implementation plan for Phase 2 GeoTIFF ingestion …
emmanuelmathot Mar 24, 2026
78c72e1
fix: update Phase 2 GeoTIFF ingestion plan to include 1D spatial coor…
emmanuelmathot Mar 24, 2026
67a22ea
feat: Phase 2 — S1 GRD RTC GeoTIFF ingestion pipeline
emmanuelmathot Mar 24, 2026
a7dbf6a
refactor: use zcm.Multiscales typed model per reviewer feedback
emmanuelmathot Mar 24, 2026
7199a50
fix: configure ruff TC001 for Pydantic runtime-evaluated base classes
emmanuelmathot Mar 24, 2026
1ca93f9
feat: implement S1Tiling conditions ingestion and discovery commands …
emmanuelmathot Mar 24, 2026
109c99e
fix(s1-ingest): discover multi-frame acquisitions with a masked times…
lhoupert Jun 9, 2026
a730291
chore: drop noqa made unnecessary by ruff TC config
lhoupert Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
471 changes: 471 additions & 0 deletions .github/prompts/phase2-geotiff-ingestion-plan.md

Large diffs are not rendered by default.

823 changes: 823 additions & 0 deletions .github/prompts/s1-grd-rtc-implementation-plan-v2.md

Large diffs are not rendered by default.

402 changes: 402 additions & 0 deletions analysis/inspect_s1tiling_geotiff.py

Large diffs are not rendered by default.

632 changes: 632 additions & 0 deletions analysis/s1_grd_rtc_prototype.py

Large diffs are not rendered by default.

702 changes: 702 additions & 0 deletions analysis/s1_real_geotiff_to_zarr.py

Large diffs are not rendered by default.

406 changes: 406 additions & 0 deletions analysis/s1tiling_docker_instructions.md

Large diffs are not rendered by default.

123 changes: 123 additions & 0 deletions analysis/s1tiling_eodag4_patch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#!/usr/bin/env python3
"""
Patch S1Tiling 1.4.0 for EODAG 4.0.0 compatibility.

EODAG 4.0.0 introduced several breaking changes for S1Tiling:
1. `productType` kwarg to dag.search() was renamed to `collection`.
Having both causes cop_dataspace to fail silently → falls back to peps.
2. Product properties use STAC names (sat:orbit_state, platform, etc.)
instead of legacy EODAG names (orbitDirection, platformSerialIdentifier, etc.)
3. cop_dataspace OData v4 rejects `polarizationChannels` and `sensorMode`.
4. cop_dataspace requires UPPERCASE orbit direction ("DESCENDING" not "descending").
5. `relativeOrbitNumber` search param silently returns 0 results on cop_dataspace.

This script patches:
- S1FileManager.py: fixes the search() call (issues 1, 3, 4, 5)
- s1/product.py: adds legacy→STAC property name fallback (issue 2)

Usage (inside the Docker container):
python3 /patch/s1tiling_eodag4_patch.py
"""

import pathlib
import re

S1T_PKG = pathlib.Path("/opt/S1TilingEnv/lib/python3.10/site-packages/s1tiling/libs")


def patch_s1filemanager():
"""Fix search() call: add collection param, remove unsupported kwargs."""
fpath = S1T_PKG / "S1FileManager.py"
src = fpath.read_text()

# Replace productType with collection (EODAG 4.0.0 rename).
# Keeping productType alongside collection causes cop_dataspace to fail
# silently, making EODAG fall back to peps.
src = src.replace(
"productType=product_type,",
"collection=product_type,",
1, # only first occurrence
)

# Remove polarizationChannels (unsupported by cop_dataspace OData)
src = re.sub(r"\n\s*# If we have eodag.*\n", "\n", src)
src = re.sub(r"\n\s*polarizationChannels=dag_polarization_param,", "", src)

# Remove sensorMode="IW" (unsupported by cop_dataspace OData)
src = re.sub(r'\n\s*sensorMode="IW",', "", src)

# Remove relativeOrbitNumber (not supported by cop_dataspace OData —
# returns 0 results silently). S1Tiling has post-search filtering for
# relative orbits when len(relative_orbit_list) > 1, and when list
# has exactly 1 element, orbitNumber=None was passed anyway for most configs.
src = re.sub(
r"\n\s*relativeOrbitNumber=dag_orbit_list_param,.*",
"",
src,
)

# cop_dataspace OData requires UPPERCASE orbit direction values
# ("DESCENDING" not "descending"). S1Tiling's k_dir_assoc produces lowercase.
src = src.replace(
"{ 'ASC': 'ascending', 'DES': 'descending' }",
"{ 'ASC': 'ASCENDING', 'DES': 'DESCENDING' }",
)

fpath.write_text(src)
print(f" Patched {fpath.name}")


def patch_product_property():
"""Add STAC property name fallback to product_property()."""
fpath = S1T_PKG / "s1" / "product.py"
src = fpath.read_text()

old = (
"def product_property(prod: EOProduct, key: str, default=None):\n"
' """\n'
" Returns the required (EODAG) product property, "
"or default in the property isn't found.\n"
' """\n'
" res = prod.properties.get(key, default)\n"
" return res"
)

new = (
"def product_property(prod: EOProduct, key: str, default=None):\n"
' """\n'
" Returns the required (EODAG) product property, "
"or default in the property isn't found.\n"
" EODAG 4.0.0 uses STAC property names; "
"fall back to them for legacy keys.\n"
' """\n'
" _FALLBACK = {\n"
' "orbitDirection": "sat:orbit_state",\n'
' "platformSerialIdentifier": "platform",\n'
' "relativeOrbitNumber": "sat:relative_orbit",\n'
' "orbitNumber": "sat:absolute_orbit",\n'
' "polarizationChannels": "sar:polarizations",\n'
' "startTimeFromAscendingNode": "start_datetime",\n'
' "completionTimeFromAscendingNode": "end_datetime",\n'
" }\n"
" res = prod.properties.get(key, None)\n"
" if res is None and key in _FALLBACK:\n"
" res = prod.properties.get(_FALLBACK[key], default)\n"
' if key == "polarizationChannels" and isinstance(res, list):\n'
' res = "+".join(res)\n'
" return res if res is not None else default"
)

if old not in src:
print(f" WARNING: product_property() not found in {fpath.name}, skipping")
return

src = src.replace(old, new)
fpath.write_text(src)
print(f" Patched {fpath.name}")


if __name__ == "__main__":
print("Applying S1Tiling EODAG 4.0.0 compatibility patches...")
patch_s1filemanager()
patch_product_property()
print("Done.")
Loading
Loading