File size: 2,164 Bytes
6a82282
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
"""NWS API — active alerts at a point.

api.weather.gov/alerts/active?point={lat},{lon}, no auth, JSON.
A User-Agent header is required (NWS rate-limits anonymous traffic).

We surface only flood-relevant categories so the doc the reconciler
sees is short and on-topic.
"""
from __future__ import annotations

from typing import Any

import httpx

DOC_ID = "nws_alerts"
CITATION = "NWS public alert API (api.weather.gov/alerts)"

USER_AGENT = "Riprap-NYC/0.1 (civic-flood-tool; +https://huggingface.co/spaces/msradam/riprap-nyc)"

_FLOOD_EVENT_KEYWORDS = (
    "flood", "flash flood", "coastal flood", "high surf", "storm surge",
    "hurricane", "tropical storm", "tornado warning",  # high-impact context
    "rip current",
)


def _is_flood_relevant(event_name: str) -> bool:
    e = (event_name or "").lower()
    return any(k in e for k in _FLOOD_EVENT_KEYWORDS)


def alerts_at(lat: float, lon: float) -> list[dict[str, Any]]:
    r = httpx.get(
        "https://api.weather.gov/alerts/active",
        params={"point": f"{lat:.4f},{lon:.4f}"},
        headers={"User-Agent": USER_AGENT, "Accept": "application/geo+json"},
        timeout=8.0,
    )
    r.raise_for_status()
    out = []
    for f in r.json().get("features", []):
        p = f.get("properties", {}) or {}
        event = p.get("event") or ""
        if not _is_flood_relevant(event):
            continue
        out.append({
            "id": p.get("id"),
            "event": event,
            "severity": p.get("severity"),
            "urgency": p.get("urgency"),
            "certainty": p.get("certainty"),
            "headline": p.get("headline"),
            "sent": p.get("sent"),
            "effective": p.get("effective"),
            "expires": p.get("expires"),
            "sender_name": p.get("senderName"),
            "areaDesc": p.get("areaDesc"),
        })
    return out


def summary_for_point(lat: float, lon: float) -> dict:
    try:
        active = alerts_at(lat, lon)
    except Exception as e:
        return {"n_active": 0, "alerts": [], "error": str(e)}
    return {
        "n_active": len(active),
        "alerts": active,
        "error": None,
    }