Skip to content

Commit 4b5765f

Browse files
docs(phase3): mark NWS ingestor done + design notes
1 parent c7ac146 commit 4b5765f

2 files changed

Lines changed: 43 additions & 1 deletion

File tree

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ design note. The daily build routine picks up the next unchecked phase.
1313
`HazardEvent`, tests against a recorded fixture.
1414
- [x] **Phase 2 — NASA FIRMS wildfire ingestor.** Active-fire feed → `HazardEvent`
1515
(handles FRP/confidence fields).
16-
- [ ] **Phase 3 — NOAA / NWS alerts ingestor.** Severe-weather alerts → `HazardEvent`.
16+
- [x] **Phase 3 — NOAA / NWS alerts ingestor.** Severe-weather alerts → `HazardEvent`.
1717

1818
## Event-driven core
1919
- [ ] **Phase 4 — Change-detection + dedup store.** State by `dedup_key`; only new/changed

docs/NOTES_phase3.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Phase 3 — NOAA / NWS alerts ingestor (design notes)
2+
3+
## What landed
4+
- `aegis/ingest/nws.py`:
5+
- `parse_nws_feature` / `parse_nws_feed` — pure GeoJSON-in, `HazardEvent`-out, matching
6+
the USGS/FIRMS shape so the change-detection layer stays source-agnostic.
7+
- `_centroid` — area-weighted (shoelace) polygon centroid with a vertex-mean fallback.
8+
- `fetch_nws` — the thin live wrapper around https://api.weather.gov/alerts/active.
9+
- `tests/fixtures/nws_sample.geojson` (4 alerts: polygon severe-weather, polygon flood,
10+
point tornado, and a zone-only null-geometry alert) + `tests/test_nws.py` (12).
11+
ruff + pytest green; the third source now feeds the same canonical schema.
12+
13+
## Decisions & trade-offs
14+
- **Categorical severity → numeric magnitude.** CAP severity is `Extreme/Severe/Moderate/
15+
Minor/Unknown`. Mapping the first four to `4..1` (and `Unknown → None`, like a missing
16+
reading) lets weather alerts sort and threshold alongside quake magnitude and fire FRP
17+
without a special case downstream. `magnitude` stays nullable, so "Unknown" is honestly
18+
absent rather than silently a zero.
19+
- **Polygons reduced to one representative point.** `HazardEvent` is point-based (it's what
20+
proximity/H3 scoring in Phases 8–9 consume), so each alert polygon collapses to its
21+
**area-weighted centroid** via the shoelace formula — correct for non-convex rings, unlike
22+
a naive vertex average. Degenerate rings (zero signed area: a line or repeated point) fall
23+
back to the vertex mean instead of dividing by zero. The full polygon is preserved in
24+
`raw` for later spatial work.
25+
- **Zone-only alerts are dropped, not guessed.** A large share of NWS alerts carry *no*
26+
geometry — they reference affected NWS zones by URI. Inventing a centroid for those would
27+
fabricate a location, so (exactly like a USGS feature with null geometry) `parse_nws_feed`
28+
drops them. Resolving zone URIs to shapes is a deliberate later enhancement, not a guess
29+
made here.
30+
- **`User-Agent` is mandatory.** api.weather.gov refuses requests without a descriptive
31+
`User-Agent`; `fetch_nws` always sends one (overridable) plus `Accept: application/geo+json`.
32+
- **Timestamp precedence onset → effective → sent.** "When the hazard is in effect" is the
33+
most decision-relevant time, so `onset` wins, then `effective`, then `sent`; all are ISO-8601
34+
with an offset and normalized to UTC. Parsing is fixture-tested (14:00 −05:00 → 19:00 UTC).
35+
- **Flood family routed to its own type.** Flood/flash-flood/coastal-flood/hydrologic events
36+
map to `HazardType.FLOOD`; everything else is `SEVERE_WEATHER`. Keeps the flood track
37+
separable for scoring without a second ingestor.
38+
39+
## Next
40+
Phase 4: the change-detection + dedup store keyed by `dedup_key` — now that three sources
41+
emit the same schema, only genuinely new or changed events should advance, so enrichment and
42+
scoring never reprocess a feed that hasn't moved.

0 commit comments

Comments
 (0)