seriffic's picture
Backend evolution: Phases 1-10 specialists + agentic FSM + Mellea + LiteLLM router
6a82282
"""Probe live Sentinel-1 RTC fetch via Microsoft Planetary Computer.
Sentinel-1 RTC (radiometric terrain corrected) is the SAR product our
TerraMind-NYC model was trained on. Earth Search only hosts Sentinel-1
GRD (raw slant-range, no CRS), which would require us to process the
RTC step ourselves — non-trivial.
Microsoft PC hosts `sentinel-1-rtc` as a STAC collection. Has been
flaky in our prior tests (May 3 evening showed >50% timeout rate).
Re-probing here.
Sovereignty disclosure: PC requires a one-time URL signing per asset,
sponsored by Microsoft. Same Copernicus license on the data. Less
sovereign than Earth Search; less authoritative than ESA CDSE.
Usage:
python3 probe_pc_s1rtc.py --lat 40.7484 --lon -73.9857
"""
from __future__ import annotations
import argparse
import json
import os
import sys
import time
from datetime import datetime, timedelta
os.environ.setdefault("AWS_NO_SIGN_REQUEST", "YES")
os.environ.setdefault("GDAL_DISABLE_READDIR_ON_OPEN", "EMPTY_DIR")
os.environ.setdefault("CPL_VSIL_CURL_USE_HEAD", "NO")
PC = "https://planetarycomputer.microsoft.com/api/stac/v1/"
S1_RTC = "sentinel-1-rtc"
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--lat", type=float, default=40.7484)
ap.add_argument("--lon", type=float, default=-73.9857)
ap.add_argument("--max-age-days", type=int, default=30)
args = ap.parse_args()
from pystac_client import Client
import planetary_computer as pc
import rasterio
from rasterio.warp import transform as warp_transform
from rasterio.windows import from_bounds
print(f"[probe] PC STAC: {PC}", flush=True)
print(f"[probe] lat,lon = ({args.lat}, {args.lon})", flush=True)
today = datetime.utcnow().date()
earliest = (today - timedelta(days=args.max_age_days)).isoformat()
d = 0.01
bbox = (args.lon - d, args.lat - d, args.lon + d, args.lat + d)
t0 = time.time()
err = None
item = None
for attempt in range(4):
try:
client = Client.open(PC)
items = list(client.search(
collections=[S1_RTC],
bbox=bbox,
datetime=f"{earliest}/{today.isoformat()}",
max_items=10,
).items())
if items:
items.sort(key=lambda i: i.properties["datetime"], reverse=True)
item = items[0]
break
err = f"no S1-RTC items in last {args.max_age_days} days"
except Exception as e:
err = f"PC search attempt {attempt+1}: {e}"
print(f"[probe] {err}", flush=True)
time.sleep(2 + 3 * attempt)
if not item:
print(f"[probe] FAIL: {err}", flush=True)
return 1
s1_dt = datetime.fromisoformat(item.properties["datetime"].replace("Z", "+00:00"))
age = (today - s1_dt.date()).days
print(f"[probe] S1-RTC product: {item.id}", flush=True)
print(f"[probe] acquired: {item.properties['datetime']} ({age} days ago)",
flush=True)
print(f"[probe] PC search wall-clock: {time.time()-t0:.2f}s", flush=True)
# Sign + read VV chip window
t = time.time()
try:
signed = pc.sign(item.assets["vv"].href)
with rasterio.open(signed) as src:
print(f"[probe] CRS: {src.crs}, shape: {src.shape}", flush=True)
HALF = 1280 # ~2.56 km
xs, ys = warp_transform(
"EPSG:4326", src.crs,
[args.lon - HALF/85_000.0, args.lon + HALF/85_000.0],
[args.lat - HALF/111_000.0, args.lat + HALF/111_000.0],
)
window = from_bounds(xs[0], ys[0], xs[1], ys[1], src.transform)
chip = src.read(1, window=window, boundless=True, fill_value=0,
out_shape=(256, 256))
print(f"[probe] vv chip read: {time.time()-t:.2f}s, "
f"shape={chip.shape}, dtype={chip.dtype}, "
f"nz_pct={(chip > 0).mean()*100:.1f}%, "
f"mean={float(chip.mean()):.4f}", flush=True)
except Exception as e:
print(f"[probe] vv chip read FAIL: {e}", flush=True)
return 1
print(f"\n[probe] === Result ===", flush=True)
print(json.dumps({
"ok": True,
"source": "microsoft_planetary_computer",
"collection": S1_RTC,
"product_id": item.id,
"s1_acquired_iso": item.properties["datetime"],
"s1_age_days": age,
"elapsed_s": round(time.time() - t0, 2),
"license": "ESA Copernicus License (free for any use, attribution required)",
"host": "Microsoft Planetary Computer (URL-signed, free)",
}, indent=2, default=str))
return 0
if __name__ == "__main__":
sys.exit(main())