Copernicus Emergency Management Service — Rapid Mapping
Copernicus EMS Rapid Mapping (RM) produces on-demand geospatial crisis information — reference maps, delineations, and damage grading — for disasters worldwide. Unlike the International Charter, CEMS exposes a public JSON REST API (no STAC, no auth), so the ETL builds Monty STAC items directly from the API payload. This document maps the CEMS RM object model to Monty STAC items.
Scope: Rapid Mapping only. Risk & Recovery Mapping and EFFIS are out of scope (separate follow-ons). This is the WP1 data-model/ETL mapping; the RSS alert/listener (WP2 event-driven orchestration) is out of scope here.
Collections
| Collection | Code | Monty role | Source for |
|---|---|---|---|
| Copernicus EMS RM — Events | cems-events |
event |
Activation |
| Copernicus EMS RM — Hazards | cems-hazards |
hazard |
Area of Interest (extent refined by the DEL delineation) |
| Copernicus EMS RM — Response | cems-response |
response |
Product (REF / FEP / DEL / GRA) + Situational Report + source imagery (eo-dat) |
| Copernicus EMS RM — Impacts | cems-impacts |
impact |
GRA product damage/exposure statistics |
- Source organisation: Copernicus Emergency Management Service (
CEMS) - Source URL: https://mapping.emergency.copernicus.eu/
- API entry point: https://rapidmapping.emergency.copernicus.eu/backend/dashboard-api/ (public, no auth)
- License: Copernicus data policy — free & open; attribution "© European Union, Copernicus Emergency Management Service"
- Temporal coverage: 2012 onwards (EMSR activation series); 224 RM activations as of 2026-07
No CEMS STAC extension exists. Per the response taxonomy and best practices, CEMS-specific fields are carried under
monty:response_detail(there is nocems:extension). Response items declaremonty(+processingrecommended); source-imagery extensions (sat:/eo:/sar:) live on the linked acquisition items — themselveseo-datResponse items (the taxonomy's Data Product code, §2.1/§3.1), not a separate untyped path — not on the derived REF/FEP/DEL/GRA Response product.
Object model
The activation → product flow
A CEMS Activation (code EMSR{n}, e.g. EMSR847) is opened for a disaster and
scoped by one or more Areas of Interest (AOIs). Each AOI accumulates Products
through the Rapid Mapping lifecycle, and each product may be re-issued as timed
monitoring iterations:
REF (reference) → FEP (first estimate) → DEL (delineation) → GRA (grading)
└─ monitoring 1, 2, … (same type, monitoringNumber++)
A Situational Report (SR) is an ArcGIS StoryMap published at activation level
(reportLink) — a produced report, not a per-AOI geospatial product.
flowchart TD
ACT["<b>Activation</b> (EMSR847)<br/>→ Event (cems-events)"]
ACT -->|"scoped by"| AOI["<b>AOIs</b><br/>(mapping extents)"]
AOI -->|"extent (refined by DEL)"| HAZ["<b>Hazard</b> per AOI<br/>→ (cems-hazards)"]
AOI -->|"accumulate"| PROD["<b>Products</b> REF/FEP/DEL/GRA<br/>→ Response (cems-response)"]
PROD -.->|"monitoring n+1"| PROD
PROD -.->|"DEL delineates"| HAZ
ACT -->|"reportLink (StoryMap)"| SR["<b>Situational Report</b><br/>→ Response eo-sr"]
PROD -->|"GRA stats: per thematic class"| IMP["<b>Impact</b> items<br/>(cems-impacts)"]
ACT -.->|"gdacsId → related(event)"| GD["GDACS Event"]
ACT -.->|"charterNumber → related(event)"| CH["Charter Event"]
Object mapping
| CEMS object | Monty type | Monty id pattern |
Collection |
|---|---|---|---|
| Activation | Event | cems-event-{code} (e.g. cems-event-EMSR847) |
cems-events |
| Area of Interest (AOI) | Hazard | cems-hazard-{code}-aoi{n}-{type} |
cems-hazards |
| Product (REF/FEP/DEL/GRA) | Response | cems-response-{code}-aoi{n}-{type}[-m{k}] |
cems-response |
Situational Report (reportLink) |
Response (eo-sr) |
cems-response-{code}-sr |
cems-response |
Product images[] entry |
Response (eo-dat) |
cems-response-{code}-aoi{n}-dat-{images[].uuid} |
cems-response |
| GRA statistic (per thematic class) | Impact | cems-impact-{code}-aoi{n}-gra-{thematic}[-m{k}] |
cems-impacts |
{code}is the activation code (EMSR847);{n}is the AOInumber;{type}∈ref/fep/del/gra(or the hazard type slug forcems-hazards);-m{k}is appended only for monitoring iterations (monitoringNumber > 0).- The AOI is the hazard-area entity — the direct analog of a Charter Area — so it
maps to a Hazard item (one per AOI, split per hazard code for multi-hazard
activations, as Charter does). Geometry is the AOI
extent, refined to the DEL delineation polygon when a Delineation product exists for that AOI (the DEL is the mapped hazard footprint). Hazard type/codes come from the activationcategory. - The DEL product therefore has a dual role: it is both the
eo-delResponse (the deliverable) and the source of the Hazard geometry — the two items sharemonty:corr_idand cross-link (related).
Data access
The detail endpoint is the ETL unit — one call returns the activation, its AOIs, and every product (with images, stats, layers, downloads):
# Rich activation detail (ETL entry point) — returns {count,next,previous,results:[{…}]}
https://rapidmapping.emergency.copernicus.eu/backend/dashboard-api/public-activations/?code=EMSR847
# Activation list (discovery) — DRF pagination: ?limit=&offset= ; 224 RM activations
https://rapidmapping.emergency.copernicus.eu/backend/dashboard-api/public-activations-info/?limit=50
Important
- Public, no authentication; no rate limit observed during exploration.
- The unified
mapping.emergency.copernicus.eu/activations/api/activations/list has a different shape (categoryis an object{slug,name}, addsdrmPhase) and mixes RM + Risk&Recovery — use therapidmappingdashboard endpoints for RM ETL. - Assets live under
aws_bucket/productsPath; per-productdownloadPath(ZIP) andlayers[](COG) give the deliverables;images[].fileNamenames the source imagery.
Reference fixtures in api-files/: EMSR847 (storm; cross-source),
EMSR871 (flood; has FEP), EMSR842 (wildfire; minimal), and list samples.
Activation → Event
Maps to a Monty Event item (cems-events); Monty extension only.
| CEMS field (activation) | Monty field | Notes |
|---|---|---|
code (EMSR847) |
id (cems-event-EMSR847) |
Prefix cems-event- |
| — | collection: "cems-events" |
Required |
eventTime |
datetime / start_datetime |
Event onset (not activationTime, which is when CEMS was tasked) |
name |
title |
Direct copy |
reason |
description |
Free-text situation summary |
centroid (WKT POINT) |
geometry |
Parse WKT → GeoJSON Point; extent (WKT POLYGON) → bbox |
category (+ subCategory) |
monty:hazard_codes |
Map via Hazard codes |
countries[].name |
monty:country_codes |
Map country name → ISO 3166-1 alpha-3 |
| derived | monty:corr_id |
Standard Monty algorithm (date/ISO3/spatial block/hazard/episode) — not the EMSR code |
| derived | processing:version |
Semantic version of the transformer that generated the item, via the processing: extension |
| derived | processing:software |
Dependency/provenance chain ({"pystac-monty": "<version>"}), via the processing: extension |
gdacsId, charterNumber |
links[rel=related] |
See Cross-source linkage |
reportLink, source page |
links[rel=via] |
Activation page / StoryMap |
Area of Interest → Hazard
Each AOI maps to a Monty Hazard item (cems-hazards); Monty extension only. This
mirrors the Charter Area→Hazard mapping.
| CEMS field (AOI / activation) | Monty field | Notes |
|---|---|---|
number |
id |
cems-hazard-{code}-aoi{n}-{type} |
| — | collection: "cems-hazards" |
Required |
DEL product extent → else AOI extent (WKT) |
geometry |
Latest delivered DEL (highest monitoringNumber with statusCode=F) — the current hazard footprint; fall back to the AOI extent. Updated in place as monitoring DELs arrive |
name |
title |
AOI name (e.g. Kingston) |
activation category (+ subCategory) |
monty:hazard_codes |
One code set per item — split multi-hazard as Charter does; see Hazard codes |
activation countries |
monty:country_codes |
Inherited from the activation |
activation eventTime |
datetime |
Inherited from the activation |
activation max_extent stat (if present) |
monty:hazard_detail.severity_value |
Optional; severity_unit per the stat (e.g. km2). CEMS AOIs carry no explicit severity otherwise |
| parent activation | links[rel=related] (roles: ["event"]) |
../cems-events/cems-event-{code}.json |
| DEL Response for this AOI | links[rel=related] (roles: ["response"]) |
The delineation product the geometry came from |
One Hazard per AOI per hazard code (Charter precedent): for a multi-hazard activation, emit one item per
monty:hazard_codesset, same geometry. Single-category activations (the common case) yield one Hazard per AOI.
Identifying multi-hazard
A CEMS activation carries a single category + subCategory, and DEL products carry
no hazard-type field — so multiple delineation products do not signal multiple hazards
(several DELs in an AOI are monitoring iterations of the same mapping). Multi-hazard is
identified from two other signals:
category+subCategory— the primary hazard (e.g.Storm/Tropical cyclone).- GRA/GRM
statshazard-footprint classes — thematic classes that name a phenomenon rather than an exposed asset reveal secondary hazards. Observed footprint classes:Flooded area,Flood trace,Landslide,Burnt area(e.g. aLandslideclass inside aStormactivation ⇒ a secondary landslide hazard).Maximum of all extentsandNAare aggregates, not hazards.
Emit one Hazard item per distinct hazard code so identified. The primary hazard takes its geometry from the DEL delineation (§ above); a secondary hazard surfaced only via a GRA footprint class takes its geometry from that class's extent where available, else the AOI extent, and its severity from the class figure. Do not infer hazard multiplicity from the DEL count.
Product → Response
Each product maps to a Monty Response item via monty:response_detail.
| CEMS field (product) | Monty field | Notes |
|---|---|---|
type |
monty:response_detail.type |
REF→eo-ref, FEP→eo-fep, DEL→eo-del, GRA→eo-gra. Monitoring/variant type codes (e.g. GRM grading-monitoring) map to their base product code (eo-gra) with monitoring_number set |
code (activation) |
monty:response_detail.source_id |
e.g. EMSR847 (source-system anchor) |
version.statusCode |
monty:response_detail.status |
Full enum (confirmed across all 224 activations): F→published, I→in-production, W→planned, N→ no-impact when version.reason mentions no change of impact/damage/situation, else withdrawn (remote-sensing limitations, cancelled) |
monitoring / monitoringNumber |
monty:response_detail.monitoring_number |
Set only when monitoring=true; iteration links to the prior via rel: prev |
extent (WKT) |
geometry / bbox |
Product footprint (AOI extent) |
| — | monty:response_detail.producer |
Copernicus EMS (mapping provider) |
| — | monty:response_detail.methodology |
human_interpreted (RM is expert-produced) |
| — | monty:response_detail.sendai_targets |
Taxonomy default for the type code |
images[] |
links[rel=related, roles=["response"]] → acquisition item(s) (eo-dat Response, cems-response) |
Source imagery (sensorType, sensorName, resolutionClass, acquisitionTime) carries sat:/eo:/sar: on the acquisition, not on the derived REF/FEP/DEL/GRA Response. related (not derived_from) since the acquisition is itself a Response item — see Response Best Practices §2/§4.4 |
layers[] (vt, thematic layers), downloadPath (ZIP) |
assets |
Web layers (GeoJSON FeatureCollection + sld style, one asset pair per thematic layer — despite the CEMS "VT" naming, these are plain GeoJSON, not a tiled service; no CEMS schema documents this, verified empirically against the live AWS bucket) + downloadable package. The base-image cog layer is also copied here (as basemap), so the item alone renders the raster under its own vector overlay — its canonical copy, with imagery identity/metadata, is the linked acquisition item's data asset (see below) |
| activation page | links[rel=derived_from] |
Upstream CEMS activation provenance |
Acquisition items are
eo-datResponse items (implemented, field coverage partial). Eachimages[]entry becomes its own Response item, idcems-response-{code}-aoi{n}-dat-{uuid}(images[].uuid, not the parent product's own type slug — the acquisition's ownmonty:response_detail.typeiseo-dat, whatever product it's linked from), living incems-responsealongside the REF/FEP/DEL/GRA items it is a siblingrelatedResponse to (roles: ["response"], both ways — see the Product → Response table above). When CEMS publishes a web-optimized COG of the same acquisition underlayers[](matched to theimages[]entry by filename), it becomes this item'sdataasset.images[]only carriessensorType/sensorName/resolutionClass/acquisitionTime/fileName— none of these back a typedsat:/eo:/sar:field with real data today, so the shipped acquisition items declare no such extension;sensorNamemaps to the STAC common-metadataplatformfield, andresolutionClass/fileNameare kept as descriptive text (title/description) pending a schema decision on where they belong. See pystac-monty#166.
Situational Report → one Response per activation, type = eo-sr, whose asset is the
reportLink StoryMap URL. It has no product extent of its own, so geometry/bbox
fall back to the activation extent, or the Event's geometry/bbox if the activation has
none — null geometry is not emitted, since it is not recommended by the STAC specification.
A DEL Response additionally carries a rel: related (roles: ["hazard"]) link to the
cems-hazards item whose geometry it supplied (reciprocal of the Hazard→DEL link above).
Every Response links to its Event and Hazard(s) via related, sharing monty:corr_id.
Do not put damage/exposure statistics in
monty:response_detail— those become separate Impact items (below). Do not setstatusfrom anything butversion.statusCode.
GRA statistics → Impact
Only GRA products carry stats, shaped as
{thematic_class: {sub_class: {unit, total, affected}}}, e.g.:
{ "Estimated population": { "None": { "total": 84000 } },
"Built-up [No.]": { "None": { "affected": 48253 } } }
Not every thematic class is an Impact. Split the classes by kind first:
- Exposure / effect classes → Impact items (one each):
Estimated population,Built-up,Transportation,Land use,Facilities,Blocked road / interruption,Temporary camp, … - Hazard-footprint classes → Hazard (not Impact):
Flooded area,Flood trace,Landslide,Burnt area— these describe the phenomenon extent; they feed the Hazard geometry/severity (see Identifying multi-hazard). - Aggregates → skip:
Maximum of all extents,NA.
Per the Response ↔ Impact boundary rules, for each exposure class emit one Impact item:
monty:impact_detail:category/typefrom the thematic class,value=affected(fallbacktotal),unitfrom the key/unit([No.],[ha],[km]),estimate_type: "primary".- Canonical edge:
Impact → derived_from → Response(the GRA Response),roles: ["response"]. - Both items share the Event's
monty:corr_id. - Guard the
"NA"/ missingtotalcase (skip or emit without a numeric value per boundary rules). - Aggregated activation-level
statsare the sum over AOIs — prefer per-product GRAstatsto avoid double counting; if only activationstatsexist, emit Impacts at Event level.
Tracking over time
A CEMS activation evolves while it is open (closed=false): products are scheduled, then
delivered, re-mapped as monitoring iterations, and occasionally corrected. Three distinct
mechanisms carry that evolution into Monty — do not conflate them.
-
Idempotent upsert — status & geometry maturing (same item). Item ids are deterministic (
cems-response-{code}-aoi{n}-{type}[-m{k}], etc.), so re-ingesting an activation updates the same items in place. A product movingW→I→Fjust flipsmonty:response_detail.status(planned→in-production→published); the Hazard geometry firms up when its DEL delineation is delivered. No new item per status change. -
Monitoring axis — a time series of items (
rel: prev). Each monitoring iteration is a separate product (ownid, owndeliveryTime,monitoringNumber0,1,2…) → a separate Response item (monty:response_detail.monitoring_number) and separate derived Impact items. Chain iteration n to n−1 withrel: prevwithin the same(code, aoiNumber, type); setdatetimefrom the productversion.deliveryTime(orimages[].acquisitionTime). This is the primary "track over time" mechanism — a growing, timestamped chain. -
Revision axis — corrections (
version.number). A product re-issued as a correction (version.number1→2, e.g. "Correction of the legend") supersedes the prior revision of the same monitoring product. The API exposes only the latest revision, so replace in place (optionally record the version via the STAC Versioning extension:predecessor-version/latest-version).
Impact over time therefore comes from the per-product GRA series (each GRA carries its
own deliveryTime + monitoringNumber), yielding one timestamped Impact item per exposure
class per iteration, chained by rel: prev. The aggregated activation stats is a rolling
snapshot with no history — use it only as a current-total cross-check or when per-product
stats are absent (never in addition, to avoid double counting).
The join: every item across both axes shares monty:corr_id — query by it and order by
datetime / monitoring_number to reconstruct the timeline.
Refresh and polling strategy
The detail endpoint is expensive (EMSR847 ≈ 413 KB); the list endpoint is one cheap call that already exposes the change signals. Probed API behaviour:
- One list call returns the full corpus:
?limit=300→ all 224 activations, ~72 KB, ~0.7 s, each item carryinglastUpdate,closed,n_products,n_aois. - No HTTP caching — no
ETag/Last-Modifiedon either endpoint, so conditional304requests are unavailable; change detection must be application-side. - No server-side "updated since" filter —
lastUpdate__gte/updated_after/activationTimeare ignored (still return all 224). - But
?closed=falseworks — returns only the live activations (currently 3). orderingis ignored; default order is code-descending (newest activation first).
Given that, only open activations change, and each closed activation is immutable once closed. The minimal-calls loop:
- Backfill once: page the full list (1 call) → fetch detail for each activation (224
one-time). Persist a watermark per
code:(lastUpdate, n_products, n_aois, closed). - Steady state — cheap poll:
GET ?closed=false(1 tiny call). For each open activation, fetch detail only iflastUpdateadvanced orn_products/n_aoisgrew vs the watermark. Typically 0–few detail calls per cycle instead of 224. - Close-out: when an activation leaves the
closed=falseset (or flipsclosed=true), fetch its detail one final time, then never again. - Reconcile: run the full
?limit=300list on a slow cadence (e.g. daily) to catch brand-new codes and any activation that opened and closed between polls; diff against watermarks.
This turns a naive per-cycle cost of 224 detail fetches (~tens of MB) into one ~72 KB list
call + a handful of detail fetches. The composite (lastUpdate, n_products, n_aois) is the
dirty check (there is no per-product timestamp in the list). A legacy RSS feed exists on the
portal (emergency.copernicus.eu/mapping/…/feed, redirected) and is the intended push
trigger under WP2 — it can replace the frequent ?closed=false poll entirely.
Cross-source linkage
CEMS activations carry hard references to sibling sources. Beyond a shared
monty:corr_id, derive the target Monty item id and emit an explicit rel: related
link so the graph is directly navigable (65/224 sampled activations carry a gdacsId).
| CEMS field | Example | Target Monty id (derivation) | Link |
|---|---|---|---|
gdacsId |
TC1001230 |
GDACS {eventtype}+{eventid}; Monty id {eventid}-{episodeid} in gdacs-events → 1001230-41 (current episode via geteventdata) |
rel: related, roles: ["event"] |
charterNumber (+ charterUrl) |
996 |
Charter Event charter-event-{activation_id} → charter-event-996 (charter-events) |
rel: related, roles: ["event"] (+ ["response"] to Charter VAPs) |
relatedevents |
EMSR codes | cems-event-{code} |
rel: related, roles: ["event"] |
This is a reusable pattern: any cross-reference field that yields a deterministic
source-item id becomes a typed related link, with monty:corr_id as the fallback join.
The edge is reciprocal — the Charter source doc already links VAPs to sibling Responses.
- Emit unconditionally from the deterministic id — a
relatedlink to a not-yet-ingested target is valid STAC and cheaper than gating on presence; it resolves when the sibling source is ingested/back-filled. - GDACS episode:
gdacsIdgives{eventtype}{eventid}but no episode. Resolve the current episode with one cheap call —gdacsapi/api/events/geteventdata?eventtype={t}&eventid={id}returns the currentepisodeid(e.g.TC1001230→ episode 41,iscurrent: true) — and link to{eventid}-{episodeid}. (A bare{eventid}-1would be stale; only fall back to it if the lookup fails.)
Worked, fixture-verified example: gdacs-events/1001230-41
and gdacs-hazards/1001230-41 are built from the
real GDACS geteventdata/getgeometry API responses for the same Tropical Cyclone Melissa activation
(iso3: JAM, MH0306, 21–31 Oct 2025), completing the related link this CEMS example already declares
(#61). Their monty:corr_id (20251021-JAM-1159798-MH0306-41-GCDB, computed with the real
geo_blocks-0.2.parquet lookup) does not match CEMS's (20251026-JAM-983324-MH0306-1-GCDB) — different
date, different block_id, since GDACS's own centroid for this episode sits mid-Atlantic (-60.5, 39.0,
the storm's track position at that point), while CEMS's AOI is Kingston. This is a live, real illustration
of the caveat in #57: corr_id is per-source deterministic and not a cross-source join key. The two items
still correlate correctly under the dynamic Event-to-Event algorithm — shared MH0306/TC/nat-met-sto-tro
(a_overlaps), shared JAM (a_contains), and CEMS's 2025-10-26 falling inside GDACS's 2025-10-21–2025-10-31
window (t_intersects).
Hazard codes
CEMS category (refined by subCategory) maps to Monty hazard codes. UNDRR-ISC 2025 is
required (exactly one per Hazard item); GLIDE and EM-DAT are recommended. The complete CEMS
category vocabulary (from the full activation catalogue, all DRM phases; case-normalise —
Mass Movement/Mass movement, Industrial Accident/Industrial accident occur in both
casings):
CEMS category |
UNDRR-ISC 2025 | GLIDE | EM-DAT | Notes / subCategory refinement |
|---|---|---|---|---|
| Flood | MH0600 | FL | nat-hyd-flo-flo | Riverine flood→MH0604, flash→MH0603, coastal/surge→MH0601 |
| Wildfire | EN0205 | WF | nat-cli-wil-wil | Category default, always nat-cli-wil-wil. subCategory: Forest fire maps to the same general key — no distinct forest/land refinement is implemented |
| Storm | — | — | — | No single UNDRR-ISC chapeau, and subCategory alone is not trusted — resolved by a tropical-signal order across subCategory, gdacsId, and name/reason; see the storm rule below |
| Earthquake | GH0101 | EQ | nat-geo-ear-gro | Ground shaking; tsunami subCat→MH0705/TS |
| Mass Movement | GH0300 | LS | nat-geo-mmd-lan | Landslide (chapeau, matching GDACS/EM-DAT/GLIDE convention); subCategory: Avalanche→MH0801. Rockfall (GH0301) and subsidence (GH0309) are not currently distinguished by subCategory — both fall through to the GH0300 chapeau |
| Volcanic Activity | GH0201 | VO | nat-geo-vol-vol | Eruption (chapeau), always GH0201 — refining by subCategory to ashfall (GH0202) or lahar (GH0204) is not currently implemented |
| Industrial Accident | — | — | tec-ind | Technological — no single chapeau; subCategory: Chemical→TL0301, Explosion→TL0304. Anything else — including gas leak, which is not a distinct key — falls through to the category default, TL0301, not TL0309 |
| Transport accident | — | — | — | Technological, no single chapeau; refined by mode from subCategory — Air→TL0401, Water→TL0403, Rail→TL0404, Road→TL0405 (all AC / tec-tra-*-*) — overridden to CH0203 / AC / tec-ind-oil-oil when name/reason names an oil or fuel spill regardless of the labelled mode. n=1 in the catalogue (EMSR716, labelled Air but "Cargo Ship Oil Spill in Aruba"), so the override is provisional |
| Humanitarian Crisis | — | — | — | Complex/societal emergency — humanitarian crisis is in the manual-review set, so no code is produced at all, not even the bare CE this row used to show (a humanitarian_crisis: ["CE"] entry exists in the code but is unreachable — the manual-review check short-circuits before it's ever consulted). Societal cluster (SO01xx/SO02xx, e.g. SO0103 civil unrest) has codes but none fits as a chapeau (mostly Risk & Recovery, out of core RM scope) |
| Environmental Degradation | — | — | — | Environmental cluster (EN01xx–EN05xx) has codes but none fits as a chapeau; in the manual-review set, no code produced |
| Other | — | — | — | Unclassified in general, but not automatically manual-review — refined by name/reason: a planned mass gathering is excluded as out of scope, an oil or fuel spill resolves to CH0203, and a snow-related impact resolves to MH0405. Anything else still falls through to manual review; see the Other rule below |
The storm rule
CEMS's Storm subCategory is not reliable on its own. Of the 18 activations
labelled Extra-tropical storm in the full 224-activation catalogue,
12 are tropical cyclones: 11 carry a GDACS TC… gdacsId,
and the twelfth, EMSR714 ("Tropical Cyclone BELAL-24 in Réunion"), carries no
gdacsId at all and is identifiable only by name. Mapping the subCategory
label straight to MH0307 would file 12 tropical cyclones as extra-tropical
cyclones — a valid-but-wrong code of exactly the kind that broke cross-source
correlation in #61.
So a tropical signal from any of three sources overrides the subCategory
label, checked in this order:
| Order | Signal | UNDRR-ISC 2025 | GLIDE | EM-DAT |
|---|---|---|---|---|
| 1 | subCategory = Tropical cyclone, hurricane, typhoon |
MH0306 | TC | nat-met-sto-tro |
| 2 | gdacsId starts with TC |
MH0306 | TC | nat-met-sto-tro |
| 3 | name/reason matches (case-insensitive) hurricane|typhoon|tropical|\bTCs?\b and not extra-?tropical |
MH0306 | TC | nat-met-sto-tro |
| 4 | subCategory = Convective storm |
MH0103 | ST | nat-met-sto-sev |
| 5 | subCategory = Extra-tropical storm |
MH0307 | EC | nat-met-sto-ext |
MH0306 / TC / nat-met-sto-tro is kept for the tropical case rather than a
more specific UNDRR code: it is the row this doc and the examples/cems-*
items already use, and it keeps the EMSR847 ↔ GDACS 1001230-41 cross-link
discoverable via a_overlaps. The repo-wide MH0306 vs MH0309 question stays
with #94.
Checked against all 23 Storm activations in the catalogue: the rule
resolves 16 as tropical (4 by subCategory, 11 by gdacsId, 1 by name),
6 as extra-tropical, and 1 as convective, with zero false
positives. The six extra-tropical activations — Windstorm in Lorraine,
Storm in Latvia, Storm in Ireland, Storm in United Kingdom, Storm in Gironde
Coast, and Storm Claudia in Ireland — carry no TC gdacsId and name no
tropical system.
Note
The Convective storm row rests on a single activation, and that
activation contradicts its own label. EMSR860 ("Storm in Leinster
Province, Ireland") carries subCategory: Convective storm, but its own
reason names it Storm Chandra, a named Atlantic extra-tropical windstorm
mapped for flood extent. The rule keeps MH0103 because that is what
subCategory states and the crosswalk row (ST / nat-met-sto-sev →
MH0103) supports it — the contradiction is recorded here as a provisional,
n=1 case. Folding non-tropical storms into MH0307 instead would code
EMSR860 correctly but would mis-code a genuine convective event later.
The Other rule
Five of the seven Other activations in the catalogue are planned mass
gatherings — pre-event reference mapping for crowd and event security, not
disasters:
| Activation | Outcome |
|---|---|
| EMSR707 Cologne Festival, Germany | excluded — planned public event |
| EMSR803 Public event in Austria (Nova Rock) | excluded — planned public event |
| EMSR827 Public Event in Bavaria (Summer Breeze) | excluded — planned public event |
| EMSR862 Olympic Winter Games in Italy | excluded — planned public event |
| EMSR877 Public Event in Burgenland, Austria | excluded — planned public event |
| EMSR741 Oil Spill in Manila Bay, Philippines | CH0203 / AC / tec-ind-oil-oil |
| EMSR791 Snowfall impact in Guria, Georgia | MH0405 (UNDRR-only) |
A planned event has no hazard to resolve, so these five are a deliberate scope exclusion — reported, not dropped silently, and not a transformation failure — the same kind of decision as IFRC-DREF's exclusion table.
EMSR741's reason names an oil-tanker spill ("the MT Terra Nova, capsized in
Manila Bay, possibly spilling fuel oil"), resolved to the same CH0203 /
AC / tec-ind-oil-oil override used for Transport accident above.
EMSR791's reason describes heavy snowfall that has paralysed movement and
cut villages off from essential services — sustained snow, not a blizzard. It
resolves to MH0405 (Snow), which carries no GLIDE or EM-DAT companion in
the crosswalk; a single UNDRR-ISC code is a valid monty:hazard_codes value,
the same pattern as IFRC-DREF's Civil Unrest → SO0103. MH0403 (Blizzard)
and MH0406 (Snow Storm) are both rejected explicitly: promoting sustained
snowfall to a blizzard or a storm would invent precision the source never
claimed.
Corrected 2026-07-16: this table previously used
MH0403(which is Blizzard, not Tropical Cyclone) for storms,MH1301/MH0901/MH1201/TH0300/TH0600(none of which exist in the UNDRR-ISC 2025 list), andGH0301(Falls, not Tsunami). All values above are verified againstdocs/model/taxonomy.mdand its Cross-Classification Mapping table. See IFRCGo/monty-stac-extension#61.Completeness: this table covers the full observed vocabulary (RM currently exercises Flood, Wildfire, Storm, Earthquake, Mass Movement, Volcanic Activity, Industrial/Transport accident, and Other — Volcanic Activity is now confirmed in the corpus by EMSR912's
Pyroclastic flow; Humanitarian Crisis and Environmental Degradation still appear only in the broader catalogue). Any unmapped or newcategory, and any activation whose hazard cannot be resolved by the rules above, is skipped and reported for manual review — never a hard failure.subCategory(detail endpoint only) is the refinement key.
get_canonical_hazard_codes()does not validate this table. That function preserves any code that is already a syntactically valid UNDRR-ISC 2025 code — it does not check that the code is the correct one for the mapped category (see IFRCGo/pystac-monty#168, where USGS shipped the valid-but-wrongGH0311for years undetected). The mapping above must be correct at the source; canonicalisation is formatting, not verification.
Examples
Worked Monty items derived from EMSR847 (Tropical Cyclone Melissa) AOI 1 (Kingston),
demonstrating the full chain — note AOI 1's DEL is statusCode=N (not produced), so the
Hazard geometry falls back to the AOI extent:
cems-event-EMSR847— Event, with cross-sourcerelatedlinks to the GDACS (1001230-41) and Charter (charter-event-996) eventscems-hazard-EMSR847-aoi01-storm— Hazard (tropical cyclone,MH0306)cems-hazard-EMSR847-aoi01-landslide— secondary Hazard (landslide,GH0300) surfaced from the GRALandslidefootprint class; geometry falls back to the AOI extentcems-response-EMSR847-aoi01-gra—eo-graResponse (COG + download assets)cems-response-EMSR847-aoi01-dat-d8deb932-f3c6-42f7-8f31-1dfa61acd40f—eo-datResponse (source imagery, siblingrelatedResponse to theeo-graitem above)cems-impact-EMSR847-aoi01-gra-population— Impact (84 000 people,derived_fromthe GRA Response)
The GDACS side of the cross-source link is also a real worked example, not a placeholder:
gdacs-events/1001230-41— GDACS Event for the same storm (TC1001230, episode 41), built from the realgeteventdataresponsegdacs-hazards/1001230-41— GDACS Hazard, geometry unioned from the realgetgeometryPoly_Redfootprint features
Reference files
Real upstream CEMS API responses, used as mapping fixtures:
api-files/EMSR847-storm-detail.json— Tropical Cyclone Melissa; 39 AOIs, 67 products, monitoring, carriesgdacsIdandcharterNumber/charterUrlapi-files/EMSR871-flood-detail.json— flood; contains FEP + DEL + GRAapi-files/EMSR842-wildfire-detail.json— wildfire; minimal (2 GRA)api-files/rapidmapping-activations-list.json— list endpoint (pagination)api-files/unified-activations-list.json— unified endpoint (different shape)
See FINDINGS.md for the raw familiarisation notes.
Decisions (resolved)
All mapping decisions are settled — none block ETL implementation.
| # | Decision | Resolution |
|---|---|---|
| 1 | statusCode → status |
Enum F/I/W/N (exhaustive — scanned all 224 activations / 2074 products) → published/in-production/planned/withdrawn. N→no-impact when version.reason says no change of impact/damage/situation, else withdrawn (remote-sensing limitations, cancelled). |
| 2 | Product type codes | Types REF/FEP/DEL/GRA/GRM (exhaustive) → eo-ref/eo-fep/eo-del/eo-gra; the monitoring variant GRM → eo-gra + monitoring_number. |
| 3 | Hazard geometry | AOI → Hazard; geometry = latest delivered DEL (statusCode=F, max monitoringNumber), fallback AOI extent, updated in place. |
| 4 | Multi-hazard | Driven by category/subCategory + GRA footprint classes — not DEL count. A secondary hazard with no dedicated polygon uses the AOI extent (coarse) and takes severity from its footprint-class figure. |
| 5 | Response geometry | The product's own extent (precise footprint); AOI extent only as fallback. |
| 6 | Source imagery | Emit linked acquisition items from images[] as eo-dat Response items (carrying resolutionClass/acquisitionTime as descriptive text; sat:/eo:/sar: blocked on upstream field coverage, see pystac-monty#166) in cems-response, linked as sibling related Response items — per the best-practices layer-separation rule; imagery extensions never on the derived Response product. The matching layers[] COG becomes the acquisition item's own asset (its canonical copy) and is also copied onto the derived Response item as basemap, so that item alone renders correctly; thematic vt layers stay on the derived Response item only. |
| 7 | Monitoring & revisions | Monitoring iterations = separate items chained by rel: prev; version.number corrections supersede in place — see Tracking over time. |
| 8 | Impact granularity | Per-product GRA stats series (each timestamped), one Impact per exposure class per iteration; aggregated activation stats is cross-check only. |
| 9 | Cross-source links | Emit related unconditionally from the deterministic target id; resolve the GDACS current episode via one geteventdata call (-1 only if the lookup fails). |
| 10 | Refresh | Watermark on the ?closed=false list; detail only when (lastUpdate, n_products, n_aois) advance — see Refresh & polling strategy. |
All questions are closed against the live data (enums scanned exhaustively; GDACS episode resolution verified). No CEMS OpenAPI/schema endpoint is published, so the enums above are grounded empirically in the full 224-activation corpus rather than a spec.