Roadmap & AI¶
What copernicus-ems could become, and in which order. The library today does one thing well: it turns Rapid Mapping into trustworthy labelled geometry — observed flooded, observed dry, not observed, with the time the satellite saw it. That is exactly the input most AI work on floods is missing, so the AI features below build on it instead of beside it.
Two rules for everything on this page
- The core stays small. One dependency (
httpx), typed, offline tests. AI and geo features arrive as optional extras (copernicus-ems[geo],[ml],[mcp]) or as separate packages, never as new hard dependencies. - Numbers are computed, never generated. When a language model is involved, it orchestrates and explains; areas, counts, dates and metrics come from code over the real layers.
Where the value is¶
flowchart LR
RM["Rapid Mapping API"] --> CORE["copernicus-ems core<br/>activations · products · layers · masks"]
CORE --> GEO["[geo]<br/>masks as GeoJSON · GeoParquet · stats"]
GEO --> ML["[ml]<br/>tri-state label rasters · chips<br/>Sentinel-1/2 pairing"]
GEO --> EVAL["[ml]<br/>mask-aware metrics<br/>CSI · POD · FAR · IoU"]
GEO --> MCP["[mcp]<br/>MCP server for LLM agents"]
MCP --> REP["Situation reports<br/>(LLM narrative over computed facts)"]
ML --> FM["Fine-tune notebooks<br/>Prithvi · TerraMind · Clay"]
Priorities at a glance¶
| # | Feature | Kind | Impact | Effort | Target |
|---|---|---|---|---|---|
| 1 | CLI (copernicus-ems fetch / list / export) |
core | high | S | 0.3 |
| 2 | Observation masks as GeoJSON, filter by date, parallel downloads | core | high | S | 0.3 |
| 3 | [geo] extra: area stats, GeoParquet export |
geo | high | M | 0.3 / 0.4 |
| 4 | Mask-aware evaluation metrics for flood models | AI | very high | M | 0.4 |
| 5 | Tri-state label rasters and ML chips | AI | very high | M | 0.4 |
| 6 | MCP server: Rapid Mapping as tools for LLM agents | AI | high | S–M | 0.5 |
| 7 | Sentinel-1/2 pairing via Copernicus Data Space STAC | AI | very high | L | 0.3 |
| 8 | Fine-tuning notebooks on EO foundation models | AI | medium | M | 0.6 |
| 9 | LLM situation reports from computed facts | AI | medium | M | later |
| 10 | Watch mode: new monitoring products → webhook / event | core | medium | M | later |
| 11 | Legacy activations (before 2023) | core | high | L | later |
S = days, M = one or two weeks, L = more.
Foundations (0.3)¶
These are not AI, but every AI feature below needs them.
Done¶
- Command-line tool:
copernicus-ems list / fetch / export,argparseonly, no new dependency;export --maskswrites the observation masks too. See Getting started. Product.aoi_extent: the mask as a GeoJSON geometry, no shapely needed (wkt_to_geojson).activations(since=, until=): a season, not the whole archive.- Parallel layer downloads:
concurrency=4by default, gentle on an API that already answers 502. MASS_MOVEMENT_EVENT_TYPEnext toFLOOD_EVENT_TYPE.
Still open¶
- Typed features: a
TypedDictfor the Rapid Mapping attributes (event_type,notation,obj_desc,det_method,area). - Burnt-area preset: the wildfire
event_typeand notations, once checked on real wildfire activations (EMSR830, EMSR873). - ~~GeoParquet output~~ for
export: done in 0.4 (parquetextra).
[geo] extra¶
With shapely and pyarrow installed:
observed_dry(event)— the mask minus the flooded polygons;summary(events)— flooded km² per product, bbox, observation time;to_geoparquet(...)/to_geopackage(...)— one file per country, tagged with activation, product andobserved_time, readable by GeoPandas, DuckDB and QGIS.
AI features¶
1. Mask-aware evaluation metrics (0.4) — the differentiator¶
Done
copernicus_ems.ml.evaluate / evaluate_geometry: see
Geometry & ML.
Every flood model (a forecast, a hazard map, a segmentation network) ends up compared with observed extents. The common mistake is to score it on the whole map, counting as false alarms the areas nobody observed. The library already knows the three states; it should score with them.
from copernicus_ems.ml import evaluate
scores = evaluate(prediction, events, resolution_m=20)
# scores.csi, scores.pod, scores.far, scores.iou — only inside the AOI
# scores.unobserved_predicted_km2 — reported, never counted as false alarm
- Input: a raster (
numpy+ transform) or polygons. - Output: the contingency table restricted to the observation mask,
per product and aggregated, with the
observed_timeeach score refers to — so lead time is measured from when the water was seen. - Why first: it is where Limen already uses this data, the logic is small and testable offline, and no other open library does it.
2. Tri-state label rasters and ML chips (0.4)¶
Done
rasterize and chips, saved as .npy with a JSON sidecar or as
GeoTIFF (to_geotiff, Chip.save(file_format="tif")). See
Geometry & ML.
Turn products into training data for segmentation models:
| Pixel value | Meaning | Source |
|---|---|---|
1 |
flooded | inside an observedEventA polygon |
0 |
dry | inside the AOI, no polygon |
255 |
ignore | outside the AOI |
from copernicus_ems.ml import rasterize, chips
label = rasterize(event, resolution_m=10, crs="EPSG:32633")
for chip in chips(label, size=512, min_flooded=0.01):
chip.save("dataset/") # GeoTIFF + JSON sidecar with provenance
The 255 = ignore value maps directly to ignore_index in PyTorch losses,
so a model is never punished for "predicting water where nobody looked".
This is what makes Rapid Mapping a better label source than an AOI-only
dataset.
3. MCP server: Rapid Mapping for LLM agents (0.5)¶
Done
copernicus-ems mcp, with the tools below (and find_scenes) and a
situation_report prompt. See MCP server.
Expose the library as a Model Context Protocol server, so an agent (Claude, or any MCP client) can answer questions such as "Which floods were mapped in Tuscany in 2024, and how large were they?" from the real data.
| Tool | Returns |
|---|---|
list_activations(country, since, until, categories) |
code, name, dates, category |
activation_summary(code) |
per product: label, observed_time, flooded km², polygon count, bbox |
compare_products(code, aoi) |
how the extent changed across monitoring updates |
export_observed_events(code, format) |
a path to a GeoJSON / GeoParquet file — not the polygons |
Design notes:
- Tools return summaries and file paths, never 6,000 raw polygons: an agent needs facts, not geometry dumps in its context.
- It reuses the disk cache, so repeated questions cost nothing.
- Shipped as
copernicus-ems[mcp](officialmcpPython SDK) with acopernicus-ems mcpcommand, usable from Claude Desktop / Claude Code with one line of configuration.
4. Sentinel-1/2 pairing¶
Done: finding the scenes
find_scenes, passes, geo.coverage, copernicus-ems scenes and
the MCP tool find_scenes. See Open imagery. Downloading
and reading the rasters stay with the user (Data Space account,
rasterio).
Found on the way: products are mostly made from commercial images
(Product.images), so the open scene is a neighbour in time, and the
nearest one may cover a sliver of the AOI.
Labels are only half of a dataset. For each product, find the satellite
scene it was made from (or the nearest Sentinel-1 SAR / Sentinel-2
acquisition to observed_time over the AOI) through the
Copernicus Data Space STAC API, and
emit image + label pairs.
- SAR (Sentinel-1) first: it sees through clouds, which is why most flood maps are made from it.
- Output as a STAC catalogue so the dataset is reusable by any EO tooling.
- Large effort and new credentials (Data Space needs an account), so it
belongs in a separate package or the
[ml]extra, not in the core.
4b. Sentinel-1 backscatter on the labels' grid¶
Done
copernicus-ems pairs and copernicus_ems.sentinelhub ([cdse]
extra): analysis-ready Sentinel-1 from the Sentinel Hub Process API of
the Copernicus Data Space, cut to the label grid, written to the data
directory with the labels and a manifest; PU estimated and budgeted
before any request. See Sentinel-1 backscatter.
Then copernicus-ems dataset: aligned chips, index.csv and
train / val / test by area (no AOI in two splits), offline.
Next with the same account: the Statistical API (water time series over an AOI between EMS maps, kept apart from the analysts' ground truth), the Copernicus DEM and a pre-event reference scene as model features. BYOC only for serving rasters of our own (e.g. Limen's forecasts) next to Sentinel-1.
5. Fine-tuning notebooks on EO foundation models (0.6)¶
First notebook done
01_flood_segmentation_s1: pairs → dataset split by
area → a VV threshold and a per-pixel classifier on CPU, scored with
evaluate. The foundation-model fine-tuning (Prithvi-EO-2.0 /
TerraMind via TerraTorch, GPU) uses the same dataset and the same
scores: that is the next notebook.
With pairs from (4) and labels from (2), show how to fine-tune an open
Earth-observation foundation model for flood segmentation — for example
Prithvi,
TerraMind or Clay — and score it with the metrics of (1). This is
documentation, not library code: a notebook in examples/, compared with
the Sen1Floods11 baseline.
6. Situation reports from computed facts (later)¶
An LLM writes the narrative; the library supplies every number.
sequenceDiagram
participant U as User
participant A as LLM agent
participant T as copernicus-ems tools
U->>A: "Report on EMSR664"
A->>T: activation_summary("EMSR664")
T-->>A: products, km², observed_time, bbox
A->>T: compare_products("EMSR664", "AOI01")
T-->>A: extent per monitoring update
A-->>U: Narrative + table, every figure from T, source credited
Guardrails: the report template cites each figure's tool call, always includes the Copernicus attribution, and states the limits (delineation is a lower bound; outside the AOI is unknown, not dry).
What we would not do¶
| Idea | Why not |
|---|---|
| Embedding search over activations | A few hundred activations with structured fields: a filter is exact, an embedding is not. |
An LLM to classify Storm vs Flood |
event_type on the features already answers it, deterministically. |
| "Filling in" floods outside the AOI with a model | It would turn unknown into invented — the opposite of what the observation mask is for. Fine as research, never as data the library returns. |
| Generating geometry with an LLM | Geometry comes from analysts and satellites; models only consume it. |
Operations and quality¶
Contract tests against the live API, weekly in CI (
.github/workflows/live.yml,tests/test_live.py): the fields the parser reads, the activation list, the EMSR762 layer against its fixture, the Sentinel catalogue. A failure opens an issue labelledlive-api.- Watch mode: poll active activations, emit an event (webhook, file, MCP notification) when a new monitoring product appears.
- Property-based tests (Hypothesis) for the parsers.
- Documentation: this site, built from
docs/and the docstrings on every push tomain.
Contributing¶
Each row of the priority table can become an issue. Items 1–2 are good first contributions; 4 and 5 are the most valuable for the ML community. See CONTRIBUTING.md.