Chapter 2 described what sensors and operators observe. This chapter describes what a forecasting system says about the future and what a warning authority tells the public: the FloodPredictionRequest and FloodPredictionResult objects, the five-step risk level, the FloodAlert message, its mapping onto the Common Alerting Protocol (CAP) 1.2, and the shelter and route objects that make an evacuation instruction actionable.
All of these objects are defined in PHASE-1 §5.1–5.2 and PHASE-3 §7.2 of WIA-ENE-033, and again as TypeScript types in the reference SDK (api/typescript/src/types.ts). The two sources agree on most points but not all, and the differences matter to anyone who validates messages at a system boundary. Where they diverge, this chapter says so and proposes a reading. Every JSON document in this chapter is an illustrative example: region names, coordinates, populations and probabilities are invented to be internally consistent, not taken from a real event.
FloodPredictionRequestA prediction request answers three questions: where, how far ahead, and with which evidence. It is deliberately small. The caller does not send observations; the forecasting node is expected to already hold, or to fetch, the precipitation, river, dam and drainage records described in Chapter 2. The request only tells the node which of those sources to take into account.
| Field | Type | Spec (PHASE-1 §5.1) | SDK (types.ts) | Meaning |
|---|---|---|---|---|
location | Location | required | required | Target area: regionId, name, coordinates, optional boundary (GeoJSON). The SDK type also allows an optional address. |
timeHorizon | '1h' | '6h' | '24h' | '72h' | required | required | How far ahead the forecast must reach. |
resolution | 'coarse' | 'medium' | 'fine' | required | optional | Requested spatial detail of the result, especially of the inundation footprint. |
includePrecipitation | boolean | required | optional | Use rainfall observations and forecasts. |
includeRiverLevels | boolean | required | optional | Use gauge stage and discharge. |
includeDamStatus | boolean | required | optional | Use reservoir levels, outflow and discharge plans. |
includeDrainage | boolean | required | optional | Use pump-station, sewer and detention-basin status. |
includeTopography | boolean | required | optional | Use terrain data, which is a precondition for an inundation map. |
runSimulation | boolean | required | optional | Run the 2D inundation simulation and scenario analysis. |
simulationScenarios | string[] | optional | optional | Names of the scenarios to evaluate. |
The four horizon values are not arbitrary. The README ties them to different evidence: one to six hours ahead is dominated by radar-based rainfall nowcasting and by water already in the river; six to twenty-four hours needs numerical weather prediction combined with the model; twenty-four to seventy-two hours relies on ensemble weather forecasts. A caller asking for 72h should therefore expect a lower confidence and a wider spread between scenarios than a caller asking for 6h over the same basin. The standard does not define a "custom" horizon; an implementation that needs 12 hours has to choose 24h and read the hourly parts of the result, or extend the enumeration in a profile (Chapter 7).
resolution is qualitative on purpose. The standard does not bind 'fine' to a grid size, because what is achievable depends on the terrain model available to the node. A deployment should publish in its interoperability profile what each value means locally, for example "coarse = catchment-level summary, medium = neighbourhood polygons, fine = street-block polygons". Without such a statement two conforming nodes can return very different maps for the same request.
PHASE-1 lists all six include* flags and runSimulation without a question mark, which makes them required in the specification. The SDK marks them optional. A server written from the spec will reject an SDK request that omits them; a server written from the SDK will accept a request that the spec calls invalid. Until the two are reconciled, the robust approach is:
false. This satisfies both readings.include* flag as true (use the source whenever it is available) and a missing runSimulation as false unless the deployment documents other defaults, and echo the effective settings in logs so that a forecast can later be traced to the evidence it used.The following request asks for a 24-hour forecast with all sources and the three standard scenarios:
{
"location": {
"regionId": "REG-SEOUL-001",
"name": "서울특별시 강남구 (예시 / example)",
"coordinates": { "latitude": 37.5172, "longitude": 127.0473 }
},
"timeHorizon": "24h",
"resolution": "medium",
"includePrecipitation": true,
"includeRiverLevels": true,
"includeDamStatus": true,
"includeDrainage": true,
"includeTopography": true,
"runSimulation": true,
"simulationScenarios": ["bestCase", "mostLikely", "worstCase"]
}
FloodPredictionResultThe result is the forecasting node's statement about one area and one horizon. Its first block identifies the forecast; its second block rates the risk; its third block estimates the footprint of flooding; the remaining blocks explain why and what to do.
predictionId is the stable handle used by GET /api/v1/flood/prediction/{id}. FloodAlert has no field that points back to it in v1.0, so an issuer who derives an alert from a forecast should record the link itself, in its audit log and, for CAP, as a named parameter. timestamp is when the forecast was issued, not when the observations were taken; the observation times live inside contributingFactors. peakTime is the expected time of the worst conditions for the area. All three timestamps follow the ISO 8601 UTC convention of Chapter 2.
probability versus confidenceBoth fields range from 0 to 100 %, and they are easy to confuse. They answer different questions:
probability is the forecast likelihood that the flood event described by riskLevel actually happens within the horizon. It is a statement about the world.confidence is how much the forecasting node trusts its own estimate: how complete the input data were, how much the ensemble members agree, how well the model has performed in similar situations. It is a statement about the forecast.A forecast can combine a high probability with low confidence ("the members that ran all say flooding, but half the gauges were offline") or a low probability with high confidence ("all members agree that the river will stay below the warning stage"). The specification defines neither quantity in more detail, so a deployment must document how it computes them. A reasonable choice is to derive probability from the share of ensemble members exceeding the threshold and confidence from the ensemble uncertainty and data completeness described in Chapter 5. Consumers should never multiply the two into a single number; each drives a different decision: probability the alert level, confidence whether a human should review before issuing.
inundationArea| Field | Unit | Meaning and notes |
|---|---|---|
estimatedArea | km² | Plan area expected to be under water at the peak. |
maxDepth | m | Deepest water within the area. Drives life-safety messages; a small area with deep water can be more dangerous than a large, shallow one. |
averageDepth | m | Mean depth over estimatedArea. The README sample omits it, but both spec and SDK make it required. |
affectedPopulation | persons | Estimated residents inside the footprint, typically from an overlay with census or resident-registration grids. |
affectedBuildings | count | Buildings intersecting the footprint. |
geoJson | GeoJSON | The footprint geometry, normally Polygon or MultiPolygon with [longitude, latitude] positions. The README sample puts the string "..." here, which is not a valid geometry. |
When runSimulation was false, the node has no hydraulic basis for a footprint. It should still return the object, because the schema requires it, but with an empty geometry collection and zero counts, and it should say in recommendations that no simulation was run. Returning a guessed polygon without a simulation is worse than returning none.
The following result is consistent with the request above. The gauge is at 4.2 m against a warning stage of 4.5 m and a danger stage of 5.0 m; it is rising at 0.15 m/h but decelerating, and the forecast peak of 4.8 m stays below the danger stage. The model rates the area at LEVEL-2 with a 65.5 % probability. The upstream reservoir contributes no planned release, so damStatus is an empty array. Note that the key is contributingFactors: the README sample spells it "contributing factors" with a space, which no schema-driven parser will accept.
{
"predictionId": "PRED-2026-000123",
"timestamp": "2026-07-15T01:30:00Z",
"location": {
"regionId": "REG-SEOUL-001",
"name": "서울특별시 강남구 (예시 / example)",
"coordinates": { "latitude": 37.5172, "longitude": 127.0473 }
},
"riskLevel": "LEVEL-2",
"probability": 65.5,
"confidence": 72.0,
"peakTime": "2026-07-15T09:00:00Z",
"inundationArea": {
"estimatedArea": 2.5,
"maxDepth": 0.8,
"averageDepth": 0.3,
"affectedPopulation": 15000,
"affectedBuildings": 3200,
"geoJson": {
"type": "Polygon",
"coordinates": [[
[127.040, 37.500], [127.060, 37.500], [127.060, 37.515],
[127.040, 37.515], [127.040, 37.500]
]]
}
},
"contributingFactors": {
"precipitation": {
"observed": { "current": 25.5, "last1h": 24.0, "last3h": 58.0,
"last6h": 83.0, "last24h": 131.0 },
"forecast": { "next1h": 28.0, "next3h": 70.0, "next6h": 110.0,
"next12h": 150.0, "next24h": 175.0,
"hourly": [28.0, 22.0, 20.0, 16.0, 13.0, 11.0] },
"statistics": { "returnPeriod": 20, "percentile": 95.0, "isExtreme": false }
},
"riverLevels": [{
"riverId": "HAN-001",
"name": "Example tributary gauge",
"stationId": "HAN-001-ST05",
"location": { "latitude": 37.5010, "longitude": 127.0580 },
"current": { "waterLevel": 4.2, "flowRate": 850.0, "velocity": 2.1,
"timestamp": "2026-07-15T01:20:00Z" },
"levels": { "normal": 2.0, "attention": 3.5, "warning": 4.5,
"danger": 5.0, "bankHeight": 6.0 },
"forecast": { "peak": 4.8, "peakTime": "2026-07-15T08:00:00Z",
"hourly": [4.35, 4.48, 4.59, 4.68, 4.75, 4.79] },
"trend": { "rateOfRise": 0.15, "direction": "rising", "acceleration": -0.01 },
"status": { "condition": "attention", "overflowRisk": 20.0 }
}],
"damStatus": [],
"drainage": {
"facilityId": "DRN-GN-007",
"name": "Example pump station",
"type": "pump-station",
"location": { "latitude": 37.5065, "longitude": 127.0512 },
"capacity": { "design": 60.0, "current": 45.0, "utilization": 75.0 },
"pumps": { "total": 8, "active": 6, "standby": 1, "failed": 1, "efficiency": 88.0 },
"operational": { "status": "active",
"lastMaintenance": "2026-05-20T00:00:00Z",
"nextMaintenance": "2026-11-20T00:00:00Z",
"alerts": ["pump-5 failure"] },
"performance": { "flowRate": 45.0, "energyUsage": 1850.0, "reliability": 87.5 }
},
"soilSaturation": 82.0
},
"recommendations": [
"저지대 주민 대피 준비 / Prepare low-lying residents for evacuation",
"지하 주차장 차량 이동 / Move vehicles out of underground car parks",
"배수 시설 가동 점검 / Check drainage facility operation"
],
"evacuationRequired": false,
"affectedAreas": ["REG-SEOUL-001"]
}
Two details in this example deserve attention. First, status.estimatedTimeToOverflow is optional and has been omitted: a straight-line extrapolation (0.8 m to go at 0.15 m/h, about 5.3 hours) would contradict the forecast peak, which never reaches the danger stage. Publishing both numbers would give the public two stories. Second, evacuationRequired is false. At LEVEL-2 the recommended actions are preparation, not movement; the reference SDK's isEvacuationRequired helper only returns true from LEVEL-3 with at least 60 % probability, and Chapter 5 discusses why that threshold is a default rather than a validated rule.
contributingFactorsThis block embeds the observation objects of Chapter 2 exactly as they were used: a PrecipitationData, an array of RiverLevelData, an array of DamStatusData, a single DrainageData and an optional soilSaturation percentage. Embedding rather than referencing is a design choice with consequences. It makes each result self-contained and auditable: a reviewer can see the exact stage and rainfall the model saw at issue time, even if the live data are later corrected. The price is size. A basin with twenty gauges and five reservoirs produces a large document, and every stored forecast carries a copy of the inputs.
Two structural limitations are worth knowing. drainage is a single object, not an array, so a city with many pump stations must either choose the most relevant one or aggregate them; the book recommends reporting the most constraining facility and noting the aggregation in recommendations, and raising the issue as a candidate for v1.1. Second, an empty array is meaningful: "damStatus": [] should mean "no dam influences this area" or "dam data were excluded because includeDamStatus was false", and the two cases should be distinguishable in the logs of the node.
scenariosWhen a simulation is run, the result may carry three ScenarioResult objects: bestCase, mostLikely and worstCase. Each has a name, a probability, a peak time and its own inundationArea.
"scenarios": {
"bestCase": {
"scenarioName": "bestCase",
"probability": 20.0,
"peakTime": "2026-07-15T07:00:00Z",
"inundationArea": { "estimatedArea": 0.4, "maxDepth": 0.3, "averageDepth": 0.1,
"affectedPopulation": 1800, "affectedBuildings": 350,
"geoJson": { "type": "Polygon",
"coordinates": [[[127.048,37.505],[127.052,37.505],[127.052,37.508],[127.048,37.508],[127.048,37.505]]] } }
},
"mostLikely": {
"scenarioName": "mostLikely",
"probability": 60.0,
"peakTime": "2026-07-15T09:00:00Z",
"inundationArea": { "estimatedArea": 2.5, "maxDepth": 0.8, "averageDepth": 0.3,
"affectedPopulation": 15000, "affectedBuildings": 3200,
"geoJson": { "type": "Polygon",
"coordinates": [[[127.040,37.500],[127.060,37.500],[127.060,37.515],[127.040,37.515],[127.040,37.500]]] } }
},
"worstCase": {
"scenarioName": "worstCase",
"probability": 20.0,
"peakTime": "2026-07-15T10:00:00Z",
"inundationArea": { "estimatedArea": 6.1, "maxDepth": 1.6, "averageDepth": 0.6,
"affectedPopulation": 41000, "affectedBuildings": 8900,
"geoJson": { "type": "Polygon",
"coordinates": [[[127.030,37.495],[127.065,37.495],[127.065,37.520],[127.030,37.520],[127.030,37.495]]] } }
}
}
Here the scenario probabilities are weights of the three scenarios (20 %, 60 %, 20 %) and add up to 100 %. The top-level probability (65.5 %) is a different quantity: the likelihood of the event at the stated risk level. The standard does not say whether scenario probabilities must sum to 100 % or how they relate to the top-level value, so a producer must state its convention and a consumer must not assume one. What the standard does make clear is that mostLikely should describe the same footprint as the top-level inundationArea; if they differ, consumers will not know which to draw on a map.
The worst case is the one planners use for shelter capacity and for pre-positioning crews, even when its probability is low. A warning officer who sees a worst case of 41,000 affected persons against shelter space for a small fraction of them has learned something the most-likely scenario does not show.
recommendations, evacuationRequired, affectedAreasrecommendations is a free-text array. It is useful for operators, but it is not machine-actionable, and nothing in the standard fixes the language. In a bilingual deployment, the simplest convention is to carry both languages in each string, as the example does, or to keep recommendations in the operator's language and leave public wording to FloodAlert.instructions. evacuationRequired is the forecasting node's advice; it is not an order. Only a warning authority, through a FloodAlert, tells people to move. affectedAreas lists region identifiers of the same form as location.regionId, which is how a result for a river reach can be fanned out to several municipal areas.
RiskLevel is a string enumeration with the values LEVEL-0 to LEVEL-4. The SDK's formatRiskLevel helper attaches a Korean label, an English label and a display colour to each value. The "typical action" column below follows the README's table.
| Value | Korean label | English label | Colour | Typical action (README) |
|---|---|---|---|---|
LEVEL-0 | 정상 | Normal | green | Routine monitoring |
LEVEL-1 | 관심 | Attention | blue | Watch the situation |
LEVEL-2 | 주의 | Caution | yellow | Inform residents |
LEVEL-3 | 경계 | Warning | orange | Prepare for evacuation |
LEVEL-4 | 심각 | Severe | red | Emergency evacuation |
The Korean labels deliberately echo the four-step national crisis-alert vocabulary (관심/주의/경계/심각), with 정상 added for the quiet state. They are not the official flood forecast terms issued by Korea's flood control offices (홍수주의보 and 홍수경보), and should never be presented as such. Chapter 1 explained the crosswalk; in message design the rule is simple: display the ENE-033 label with its level code, and if an official flood forecast is also in force, cite it as a separate statement from its own issuer.
Colour must never be the only carrier of meaning. Every display of a level should also show the label and the level number, both for colour-blind users and because the same colours carry different meanings in other alerting systems.
The README's level table says LEVEL-1 is reached at the "주의 수위", LEVEL-2 at the "경계 수위" and LEVEL-3 at the "위험 수위". But LEVEL-2 is itself labelled 주의 and LEVEL-3 is labelled 경계. So "주의" names the LEVEL-2 label while "주의 수위" triggers LEVEL-1, and "경계" names the LEVEL-3 label while "경계 수위" triggers LEVEL-2. A reader who matches words will be off by one step. The field names of RiverLevelData.levels (attention, warning, danger, bankHeight) add a third vocabulary, and English "Warning" is the LEVEL-3 label while levels.warning triggers LEVEL-2.
The book therefore states an explicit rule, keyed on field names and never on words. It follows the README's ordering but is a recommendation, not normative v1.0 text:
levels.attention → at least LEVEL-1levels.warning → at least LEVEL-2levels.danger → at least LEVEL-3levels.bankHeight, or overflow imminent → LEVEL-4Apply the rule to both the observed stage and the forecast peak; the result is a floor. The model's own level can raise it but should not lower it below what the gauge already shows.
A compact implementation of this rule, together with the CAP helpers used in Section 3.6, looks like this. Note that it compares levels through a numeric index. Comparing the strings directly ('LEVEL-3' >= 'LEVEL-2') happens to work because the values sort lexically, but it would break silently if a profile ever introduced a value such as LEVEL-10 or a suffix.
import { RiskLevel, ReferenceLevels } from './types';
// Book-recommended rule (not normative in v1.0):
// the observed or forecast stage sets a floor for the risk level,
// using the field names of RiverLevelData.levels, never the Korean labels.
const LEVEL_INDEX: Record<RiskLevel, number> = {
[RiskLevel.LEVEL_0]: 0, [RiskLevel.LEVEL_1]: 1, [RiskLevel.LEVEL_2]: 2,
[RiskLevel.LEVEL_3]: 3, [RiskLevel.LEVEL_4]: 4,
};
const BY_INDEX = [RiskLevel.LEVEL_0, RiskLevel.LEVEL_1, RiskLevel.LEVEL_2,
RiskLevel.LEVEL_3, RiskLevel.LEVEL_4];
export function stageFloor(stage: number, lv: ReferenceLevels): RiskLevel {
if (stage >= lv.bankHeight) return RiskLevel.LEVEL_4;
if (stage >= lv.danger) return RiskLevel.LEVEL_3;
if (stage >= lv.warning) return RiskLevel.LEVEL_2;
if (stage >= lv.attention) return RiskLevel.LEVEL_1;
return RiskLevel.LEVEL_0;
}
// Final level = the higher of the model's level and the stage floor.
export function combine(modelLevel: RiskLevel, floor: RiskLevel): RiskLevel {
return BY_INDEX[Math.max(LEVEL_INDEX[modelLevel], LEVEL_INDEX[floor])];
}
// ENE-033 lower-case enums -> CAP 1.2 capitalised enumerations
const cap = (v: string) => v.charAt(0).toUpperCase() + v.slice(1);
export const toCapSeverity = cap; // 'severe' -> 'Severe'
export const toCapUrgency = cap; // 'expected' -> 'Expected'
export const toCapCertainty = cap; // 'likely' -> 'Likely'
// CAP forbids the "Z" designator; UTC is written as "-00:00".
export function toCapTime(iso: string): string {
return new Date(iso).toISOString().replace(/\.\d{3}Z$/, '-00:00');
}
// GeoJSON rings are [lon, lat]; CAP polygons are "lat,lon" pairs.
export function toCapPolygon(ring: [number, number][]): string {
return ring.map(([lon, lat]) => `${lat},${lon}`).join(' ');
}
Applied to the example, the observed 4.2 m sits between attention (3.5 m) and warning (4.5 m), so the observed floor is LEVEL-1; the forecast peak of 4.8 m passes warning, so the forecast floor is LEVEL-2. The model's LEVEL-2 is consistent with both. Had the model returned LEVEL-1, the combined level would still be LEVEL-2.
FloodAlertA prediction is an internal technical product. An alert is a public act by an authority. The standard keeps them in separate objects for that reason: FloodAlert carries an identified issuer, an explicit validity window and instructions written for people, not for systems. In the reference API, alerts are created through POST /api/v1/alert/create; the SDK's createAlert accepts everything except alertId and timestamp, which the server assigns. The details of the API belong to Chapter 4; here the concern is the content.
| Block | Fields | Purpose |
|---|---|---|
| identity | alertId, timestamp | Unique identifier and issue time, both server-assigned. |
issuer | organizationId, name, authorityLevel | Who is legally speaking. authorityLevel is free text in v1.0 (e.g. national, basin, municipal); a deployment should fix its allowed values. |
alert | level, severity, urgency, certainty | The ENE-033 level plus the three CAP-style qualifiers. |
affectedAreas | regionIds, geoJson, population, buildings | Where the alert applies and how many people and buildings are inside. |
event | type, onset, expiry, peakTime | What kind of flood and when it is expected to start, peak and end. |
instructions | ko, en, actions, evacuationRequired, evacuationZones | Bilingual public text, machine-readable action codes and evacuation zones. |
shelters | array (optional) | Where to go. |
contact | emergency, information, website | Whom to call and where to read more. |
The level says how serious the flood situation is on the ENE-033 scale. The three qualifiers say it in the vocabulary shared by alerting systems worldwide: severity (minor, moderate, severe, extreme) is the expected impact; urgency (immediate, expected, future) is how soon people must act; certainty (observed, likely, possible) is how sure the issuer is. The standard does not define a mapping between level and severity. The table below is an example that a deployment can adopt and publish in its profile; it is not normative.
| Level | Example severity | Derivation of urgency | Derivation of certainty |
|---|---|---|---|
| LEVEL-0 | (no alert; cancel any active one) | — | — |
| LEVEL-1 | minor | from when protective action has to begin (not from event.onset itself): now → immediate; within about the next hour → expected; later → future | stage already above threshold → observed; probability above about 50 % → likely; otherwise possible |
| LEVEL-2 | moderate | ||
| LEVEL-3 | severe | ||
| LEVEL-4 | extreme |
The urgency and certainty rules above follow the CAP 1.2 definitions, where "Expected" means responsive action should be taken soon (within the next hour), "Likely" means a probability above roughly 50 %, and "Possible" means possible but not likely. Using the CAP definitions keeps ENE-033 alerts consistent with every other alert a receiving system handles.
The specification allows four event types, river-flood, urban-flood, coastal-flood and flash-flood, matching the four flood types of Chapter 1. The SDK's FloodEventType enumeration adds a fifth, dam-discharge. A spec-strict validator will reject it. Since a planned release from a reservoir is a common reason to warn downstream residents, the value is useful; until it is added to the spec, the portable choice is to send river-flood and describe the release in the instruction text, and to declare dam-discharge as a profile extension where both sides support it.
instructions.ko and instructions.en are the text people will actually read, so they must be short, concrete and written natively in each language rather than translated word for word. Each should say what to do, where, and until when. actions carries machine-readable codes, for example for a mobile app that highlights the relevant button; the standard does not define a code list, so a deployment must publish its own. evacuationRequired here, unlike the field of the same name in the prediction result, is an instruction, and evacuationZones tells receivers exactly whom it concerns.
onset and expiry bound the validity of the message. An alert without a realistic expiry keeps showing on dashboards long after the water has gone down; an alert that expires too early leaves people without guidance at the peak. Issuers should update the expiry through the update operation rather than letting alerts lapse and re-issuing new ones, so that receivers can follow a single chain of messages.
Two and a half hours after the forecast of Section 3.2, the river has kept rising and a new forecast rates the area LEVEL-3. The district office issues the following alert. Onset is three hours away, but evacuation of the riverside zones has to begin within the hour, so urgency is expected; the probability exceeds 50 % while the stage has not yet crossed danger, so certainty is likely.
{
"alertId": "ALERT-2026-000045",
"timestamp": "2026-07-15T04:00:00Z",
"issuer": {
"organizationId": "ORG-EXAMPLE-GN",
"name": "Example District Disaster Safety Office",
"authorityLevel": "municipal"
},
"alert": {
"level": "LEVEL-3",
"severity": "severe",
"urgency": "expected",
"certainty": "likely"
},
"affectedAreas": {
"regionIds": ["REG-SEOUL-001"],
"geoJson": {
"type": "Polygon",
"coordinates": [[
[127.040, 37.500], [127.060, 37.500], [127.060, 37.515],
[127.040, 37.515], [127.040, 37.500]
]]
},
"population": 15000,
"buildings": 3200
},
"event": {
"type": "river-flood",
"onset": "2026-07-15T07:00:00Z",
"expiry": "2026-07-15T18:00:00Z",
"peakTime": "2026-07-15T09:00:00Z"
},
"instructions": {
"ko": "하천변 저지대 주민은 지정 대피소로 이동하십시오. 지하 공간에 머무르지 마십시오.",
"en": "Residents of low-lying riverside areas should move to a designated shelter. Do not stay in underground spaces.",
"actions": ["evacuate-low-lying", "avoid-underground", "avoid-riverside-roads"],
"evacuationRequired": true,
"evacuationZones": ["ZONE-GN-A", "ZONE-GN-B"]
},
"shelters": [{
"shelterId": "SHL-GN-012",
"name": "Example Elementary School Gym",
"type": "school",
"location": { "latitude": 37.5190, "longitude": 127.0450 },
"address": "(example address)",
"capacity": { "total": 600, "current": 140, "available": 460 },
"facilities": { "restrooms": true, "kitchen": false, "medicalRoom": true,
"powerSupply": true, "water": true, "heating": false,
"cooling": true, "wifi": true },
"accessibility": { "wheelchairAccessible": true, "elevatorAvailable": false,
"parkingAvailable": true, "publicTransport": ["bus"] },
"supplies": { "food": 1200, "water": 1800, "blankets": 500,
"firstAidKits": 20, "emergencyLights": 40 },
"status": { "operational": true, "lastInspection": "2026-06-30T00:00:00Z",
"contact": "(example)", "openingHours": "24h during alert" }
}],
"contact": {
"emergency": "119",
"information": "(example information line)",
"website": "https://alerts.example.org/ALERT-2026-000045"
}
}
sheltersPHASE-3 §7.2 defines the elements of shelters as a flat object: id, name, location, a numeric capacity, a numeric available and facilities as an array of strings. The SDK instead uses the full EvacuationShelter object described in Section 3.7, with shelterId, nested capacity counts and boolean facility flags. The example above uses the SDK form. A receiver that must handle both should accept either shape and normalise it: id ↔ shelterId, capacity ↔ capacity.total, available ↔ capacity.available, and a facility string list ↔ the flags that are true.
The Common Alerting Protocol version 1.2 is an OASIS standard for exchanging public warnings between authorities, broadcasters, mobile networks and applications. ENE-033 does not replace it; it defines the richer domain object from which a CAP message is produced. Any public alert that leaves an ENE-033 system for a national or international aggregator should be sent as CAP.
FloodAlert | CAP 1.2 | Notes |
|---|---|---|
alertId | alert/identifier | Must be unique for the sender. |
issuer.organizationId | alert/sender | CAP expects a globally unique sender identifier; a domain-based form is typical. |
timestamp | alert/sent | Converted to CAP date-time (see 3.6.3). |
| (none) | alert/status, msgType, scope | Supplied by the publisher: normally Actual, Alert (or Update/Cancel), Public. Drills must use Exercise, never Actual. |
issuer.name | info/senderName | Human-readable issuer name. |
event.type | info/event, info/eventCode | Readable event text per language; the ENE-033 value as an event code. |
| (none) | info/category | Required by CAP; not in ENE-033. The publisher chooses a fixed value, for example Met. |
alert.severity/urgency/certainty | info/severity/urgency/certainty | Same words, capitalised. |
alert.level | info/parameter | Carried as a named parameter so that ENE-033 receivers keep the level. |
event.onset / event.expiry | info/onset / info/expires | Direct mapping. |
event.peakTime | info/parameter | CAP has no peak-time element. |
instructions.ko / .en | info/instruction in two info blocks | One info per language, each with its own language code. |
instructions.evacuationRequired | info/responseType | true → Evacuate; otherwise Prepare or Monitor as appropriate. |
contact.website / contact.emergency | info/web / info/contact | Direct mapping. |
affectedAreas.geoJson | area/polygon | Coordinate order must be swapped (see 3.6.3). |
affectedAreas.regionIds | area/geocode | Named geocode; a national area code can be added alongside. |
affectedAreas.population, buildings, shelters | (none) | Not carried in the CAP core; link to them through web or parameter. |
CAP's enumerations are wider than those of ENE-033. Severity and certainty both include Unknown, urgency includes Past and Unknown, and certainty includes Unlikely. Going from ENE-033 to CAP is lossless, because every ENE-033 value has a CAP counterpart. Going the other way, an ENE-033 receiver of CAP messages has to decide what to do with those extra values. The book recommends:
Past urgency: treat as the end of the event; close the corresponding ENE-033 alert rather than creating a new one.Unlikely certainty: do not create an ENE-033 alert; log the message for situational awareness.Unknown in any field: keep the original CAP message, flag it for manual review and never guess a value, because a guessed severity would be presented to the public as a real assessment.The same logic applies to message types. CAP Update and Cancel refer to earlier messages through references; they map to the update and cancel operations of the ENE-033 API. An update should reuse the ENE-033 alertId and produce a new CAP identifier that references the previous one.
Coordinate order. GeoJSON writes positions as [longitude, latitude]. CAP polygons are space-separated "latitude,longitude" pairs in WGS 84, and the first and last pair must be identical. A converter that copies numbers in order will place a Seoul polygon somewhere in Antarctica. The helper toCapPolygon above swaps the order explicitly. CAP polygons also have no holes and are single rings, so a GeoJSON MultiPolygon becomes several polygon elements within the same area, and holes must be dropped or approximated.
Time zone designator. ENE-033 timestamps end in Z. CAP 1.2 does not allow alphabetic time zone designators and writes UTC as -00:00. Strict CAP validators will reject 2026-07-15T04:00:00Z. The helper toCapTime handles the conversion.
<?xml version="1.0" encoding="UTF-8"?>
<alert xmlns="urn:oasis:names:tc:emergency:cap:1.2">
<identifier>ALERT-2026-000045</identifier>
<sender>alerts@example.org</sender>
<sent>2026-07-15T04:00:00-00:00</sent>
<status>Actual</status>
<msgType>Alert</msgType>
<scope>Public</scope>
<info>
<language>ko-KR</language>
<category>Met</category>
<event>하천 홍수</event>
<responseType>Evacuate</responseType>
<urgency>Expected</urgency>
<severity>Severe</severity>
<certainty>Likely</certainty>
<onset>2026-07-15T07:00:00-00:00</onset>
<expires>2026-07-15T18:00:00-00:00</expires>
<senderName>Example District Disaster Safety Office</senderName>
<headline>[경계] 하천 홍수 위험 — 저지대 대피</headline>
<instruction>하천변 저지대 주민은 지정 대피소로 이동하십시오. 지하 공간에 머무르지 마십시오.</instruction>
<web>https://alerts.example.org/ALERT-2026-000045</web>
<eventCode><valueName>WIA-ENE-033:eventType</valueName><value>river-flood</value></eventCode>
<parameter><valueName>WIA-ENE-033:riskLevel</valueName><value>LEVEL-3</value></parameter>
<parameter><valueName>WIA-ENE-033:predictionId</valueName><value>PRED-2026-000124</value></parameter>
<parameter><valueName>WIA-ENE-033:peakTime</valueName><value>2026-07-15T09:00:00-00:00</value></parameter>
<area>
<areaDesc>REG-SEOUL-001 riverside low-lying zones A, B</areaDesc>
<polygon>37.500,127.040 37.500,127.060 37.515,127.060 37.515,127.040 37.500,127.040</polygon>
<geocode><valueName>WIA:regionId</valueName><value>REG-SEOUL-001</value></geocode>
</area>
</info>
<info>
<language>en-US</language>
<!-- category, urgency, severity, certainty, onset, expires and area
must be repeated here exactly as in the ko-KR block; omitted for brevity -->
<event>River flood</event>
<headline>[Warning] River flood risk: evacuate low-lying areas</headline>
<instruction>Residents of low-lying riverside areas should move to a designated shelter. Do not stay in underground spaces.</instruction>
</info>
</alert>
Before any CAP message is published, it should be validated against the CAP 1.2 XML schema as part of the publishing pipeline. Chapter 7 includes this validation in the self-check scenario.
An evacuation instruction without a destination and a safe way to get there is incomplete. The SDK defines two objects for this purpose, EvacuationShelter and EvacuationRoute. They are not part of the PHASE-1 data model text, but they are served by the evacuation endpoints and embedded in alerts, so they belong to the message formats of the standard in practice.
EvacuationShelter| Block | Fields | Notes |
|---|---|---|
| identity | shelterId, name, type, location, address | type: school, community-center, sports-facility, temporary. |
capacity | total, current, available | Persons. available should equal total − current; a receiver should check this and prefer the smaller value if they disagree. |
facilities | eight booleans | restrooms, kitchen, medicalRoom, powerSupply, water, heating, cooling, wifi. |
accessibility | wheelchairAccessible, elevatorAvailable, parkingAvailable, publicTransport[] | Essential for routing people with reduced mobility. |
supplies | food (servings), water (L), blankets, firstAidKits, emergencyLights | Counts; units as annotated in the SDK. |
status | operational, lastInspection, contact, openingHours | A shelter with operational: false must not be listed in an active alert. |
Shelter occupancy changes quickly during an event, while an alert is a message frozen at issue time. The values embedded in an alert should therefore be read as "at time of issue"; applications that show live availability should fetch the shelter from the evacuation endpoint rather than rely on the copy inside the alert. Also note that a shelter must itself lie outside the forecast footprint, including the worst-case footprint where possible. A consistency check that intersects each listed shelter's location with inundationArea.geoJson is cheap and catches embarrassing errors.
EvacuationRouteA route connects an origin (coordinates) with a destination (a full EvacuationShelter). Its path carries the list of coordinates, the distance in metres, the estimatedTime in minutes and a safetyScore from 0 to 100. hazards lists what makes the route dangerous: floodedSegments (each with a start point, end point and water depth in metres), blockedRoads and highRiskAreas. alternatives offers other routes with their own distance, time and safety score, and updates is a time-stamped log of status changes, each with a RouteStatus of clear, caution or blocked.
The standard does not define how safetyScore is computed, and it does not say at what water depth a segment becomes impassable. Those are local engineering and policy decisions, so a deployment must document them. What should be avoided is a route with status clear that nevertheless lists flooded segments: the status and the hazard list must be derived from the same data. A route that crosses any segment the operator considers impassable should be blocked, and the first alternative should be promoted.
FloodPredictionRequest is small: location, horizon, resolution and source flags. Send every flag explicitly, because spec and SDK disagree on whether they are required.FloodPredictionResult separates probability (about the event) from confidence (about the forecast); keep them separate in decisions. Use the key contributingFactors, not the README's spelling with a space.FloodAlert is the public act of an identified issuer; its qualifiers follow CAP definitions. dam-discharge and the shelter shape differ between spec and SDK.-00:00, supply category and message status, and never invent values for CAP Unknown.Chapter 4 shows how these objects travel: the endpoints that accept requests, return results and create, update and cancel alerts.