Skip to content

Commit 0f7b2a5

Browse files
committed
feat: add S1 GRD integration guide and quickstart
- S1 quickstart example script for local testing - Comprehensive guide covering S1 vs S2 differences - Documents collection registry, preview generation, workflow params
1 parent f4223c6 commit 0f7b2a5

File tree

2 files changed

+163
-0
lines changed

2 files changed

+163
-0
lines changed

docs/s1-guide.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Sentinel-1 GRD Pipeline
2+
3+
Quick guide to process Sentinel-1 Ground Range Detected (GRD) data through the GeoZarr pipeline.
4+
5+
## Quick Start
6+
7+
```bash
8+
# Local conversion
9+
python examples/s1_quickstart.py
10+
11+
# Or run the full workflow on cluster
12+
kubectl apply -f workflows/examples/run-s1-test.yaml -n devseed-staging
13+
```
14+
15+
## S1 vs S2 Differences
16+
17+
| Feature | Sentinel-2 L2A | Sentinel-1 GRD |
18+
|---------|----------------|----------------|
19+
| **Groups** | `/quality/l2a_quicklook/r10m` | `/measurements` |
20+
| **Extra flags** | `--crs-groups /quality/...` | `--gcp-group /conditions/gcp` |
21+
| **Chunk size** | 4096 | 2048 |
22+
| **Polarizations** | RGB bands | VH, VV, HH, HV |
23+
| **Preview query** | True color formula | Single-band grayscale |
24+
25+
## Collection Registry
26+
27+
S1 config in `scripts/get_conversion_params.py`:
28+
29+
```python
30+
"sentinel-1-l1-grd": {
31+
"pattern": "sentinel-1-l1-grd*",
32+
"conversion": {
33+
"groups": "/measurements",
34+
"extra_flags": "--gcp-group /conditions/gcp",
35+
"spatial_chunk": 2048,
36+
"tile_width": 512,
37+
},
38+
}
39+
```
40+
41+
## Preview Generation
42+
43+
S1 items get grayscale preview with polarization detection:
44+
45+
```python
46+
# Auto-detects VH/VV/HH/HV from assets
47+
variables=/S01SIWGRD_..._VH/measurements:grd&bidx=1&rescale=0,219
48+
```
49+
50+
See `scripts/augment_stac_item.py:_encode_s1_preview_query()` for implementation.
51+
52+
## Test Data
53+
54+
EODC STAC has S1 test items:
55+
```bash
56+
curl "https://stac.core.eopf.eodc.eu/collections/sentinel-1-l1-grd/items?limit=5"
57+
```
58+
59+
## Workflow Parameters
60+
61+
```yaml
62+
arguments:
63+
parameters:
64+
- name: source_url
65+
value: "https://stac.core.eopf.eodc.eu/collections/sentinel-1-l1-grd/items/S1C_..."
66+
- name: item_id
67+
value: "S1C_IW_GRDH_20251008_test"
68+
- name: register_collection
69+
value: "sentinel-1-l1-grd-dp-test"
70+
```
71+
72+
## Known Issues
73+
74+
- GCP reprojection can fail for some S1 tiles (data-model issue)
75+
- Memory requirements higher than S2 (recommend 16GB limit)
76+
- TiTiler rendering needs polarization-specific rescaling
77+
78+
## Next Steps
79+
80+
- Add S1 benchmarks to compare with S2 performance
81+
- Document optimal chunk sizes for different S1 modes (IW/EW/SM)
82+
- Add S1-specific validation rules

examples/s1_quickstart.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env python3
2+
"""Quick S1 GRD to GeoZarr conversion example.
3+
4+
Demonstrates end-to-end S1 pipeline:
5+
1. Fetch S1 item from STAC
6+
2. Convert to GeoZarr
7+
3. Register in STAC catalog
8+
4. Augment with preview links
9+
"""
10+
11+
import subprocess
12+
import sys
13+
from pathlib import Path
14+
15+
16+
def run_s1_pipeline(
17+
stac_url: str = "https://stac.core.eopf.eodc.eu",
18+
item_id: str = "S1C_IW_GRDH_1SDV_20251008T163126_20251008T163151_004473_008DBA_9AB4",
19+
output_dir: Path = Path("./s1_output"),
20+
) -> int:
21+
"""Run S1 GRD pipeline locally."""
22+
23+
output_dir.mkdir(exist_ok=True)
24+
geozarr_path = output_dir / f"{item_id}_geozarr.zarr"
25+
26+
print(f"🛰️ Processing S1 item: {item_id}")
27+
28+
# Step 1: Get source URL
29+
print("\n1️⃣ Fetching STAC item...")
30+
cmd = [
31+
"python",
32+
"scripts/get_zarr_url.py",
33+
f"{stac_url}/collections/sentinel-1-l1-grd/items/{item_id}",
34+
]
35+
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
36+
source_url = result.stdout.strip()
37+
print(f" Source: {source_url}")
38+
39+
# Step 2: Convert to GeoZarr
40+
print("\n2️⃣ Converting to GeoZarr...")
41+
cmd = [
42+
"eopf-geozarr",
43+
"convert",
44+
source_url,
45+
str(geozarr_path),
46+
"--groups",
47+
"/measurements",
48+
"--gcp-group",
49+
"/conditions/gcp",
50+
"--spatial-chunk",
51+
"2048",
52+
"--verbose",
53+
]
54+
subprocess.run(cmd, check=True)
55+
print(f" ✓ Created: {geozarr_path}")
56+
57+
# Step 3: Validate
58+
print("\n3️⃣ Validating GeoZarr...")
59+
cmd = ["eopf-geozarr", "validate", str(geozarr_path)]
60+
subprocess.run(cmd, check=True)
61+
print(" ✓ Valid GeoZarr")
62+
63+
print("\n✅ S1 pipeline complete!")
64+
print(f" Output: {geozarr_path}")
65+
print("\n Next steps:")
66+
print(" - Upload to S3")
67+
print(" - Register in STAC catalog")
68+
print(" - View in titiler-eopf")
69+
70+
return 0
71+
72+
73+
if __name__ == "__main__":
74+
try:
75+
sys.exit(run_s1_pipeline())
76+
except subprocess.CalledProcessError as e:
77+
print(f"\n❌ Pipeline failed: {e}", file=sys.stderr)
78+
sys.exit(1)
79+
except KeyboardInterrupt:
80+
print("\n⚠️ Interrupted", file=sys.stderr)
81+
sys.exit(130)

0 commit comments

Comments
 (0)