perf: faster map overlay load #4

Merged
sirius merged 7 commits from perf/map-layer-load into master 2026-08-27 21:24:24 -04:00
2 changed files with 162 additions and 20 deletions
Showing only changes of commit fdd59052c5 - Show all commits

View file

@ -206,6 +206,78 @@ def filter_points_bbox(
return out
_ALERT_KEEP = ("event", "severity", "headline", "areaDesc", "wfo", "source")
def slim_alert_properties(props: dict | None) -> dict:
"""Keep only the fields the map popup reads."""
src = props or {}
return {k: src.get(k) for k in _ALERT_KEEP}
def _walk_coords(coords: Any, acc: list[float]) -> None:
if not coords:
return
first = coords[0]
if isinstance(first, (int, float)):
lon, lat = float(coords[0]), float(coords[1])
acc[0] = min(acc[0], lon)
acc[1] = min(acc[1], lat)
acc[2] = max(acc[2], lon)
acc[3] = max(acc[3], lat)
return
for child in coords:
_walk_coords(child, acc)
def _geom_envelope(geom: dict | None) -> tuple[float, float, float, float] | None:
if not geom or not isinstance(geom, dict):
return None
if geom.get("type") == "GeometryCollection":
env: list[float] | None = None
for g in geom.get("geometries") or []:
e = _geom_envelope(g)
if e is None:
continue
if env is None:
env = list(e)
else:
env[0] = min(env[0], e[0])
env[1] = min(env[1], e[1])
env[2] = max(env[2], e[2])
env[3] = max(env[3], e[3])
return tuple(env) if env else None # type: ignore[return-value]
coords = geom.get("coordinates")
if coords is None:
return None
acc = [180.0, 90.0, -180.0, -90.0]
try:
_walk_coords(coords, acc)
except (TypeError, ValueError, IndexError):
return None
if acc[0] > acc[2]:
return None
return acc[0], acc[1], acc[2], acc[3]
def clip_fc_to_bbox(
fc: dict | None,
minlon: float, minlat: float, maxlon: float, maxlat: float,
) -> dict:
"""Drop features whose geometry envelope misses the viewport. No shapely."""
box = (minlon, minlat, maxlon, maxlat)
out = []
for feat in (fc or {}).get("features") or []:
if not isinstance(feat, dict):
continue
env = _geom_envelope(feat.get("geometry"))
if env is None:
continue
if env[0] <= box[2] and env[2] >= box[0] and env[1] <= box[3] and env[3] >= box[1]:
out.append(feat)
return {"type": "FeatureCollection", "features": out}
def _f(value: object) -> float | None:
if value is None or value == "":
return None
@ -574,42 +646,48 @@ async def fetch_fire_perimeters(bbox: str | None) -> dict:
async def fetch_weather_alerts(area: str | None, bbox: str | None) -> dict:
"""Cached NWS active alerts + IEM storm-based warning polygons."""
async def _load_iem():
try:
data = await _get_json(IEM_SBW)
except Exception:
return {"type": "FeatureCollection", "features": []}
return data if isinstance(data, dict) else {"type": "FeatureCollection", "features": []}
async def _load():
nws_params: dict[str, str] = {"status": "actual"}
clip_box = None
if area:
nws_params["area"] = area.upper()
elif bbox:
minlon, minlat, maxlon, maxlat = quantize_bbox(*parse_bbox(bbox))
nws_params["bbox"] = f"{minlon},{minlat},{maxlon},{maxlat}"
clip_box = quantize_bbox(*parse_bbox(bbox))
nws_params["bbox"] = f"{clip_box[0]},{clip_box[1]},{clip_box[2]},{clip_box[3]}"
nws_fc: dict = {"features": []}
sbw_fc: dict = {"features": []}
try:
nws_fc = await _get_json(NWS_ALERTS, nws_params)
except Exception:
nws_fc = {"features": []}
try:
sbw_fc = await _get_json(IEM_SBW)
except Exception:
sbw_fc = {"features": []}
sbw_fc = await _ttl_get("iem:sbw", 45.0, _load_iem)
features = []
for feat in nws_fc.get("features") or []:
props = feat.get("properties") or {}
if not isinstance(feat, dict):
continue
props = dict(feat.get("properties") or {})
props["source"] = "nws"
feat["properties"] = props
features.append(feat)
for feat in sbw_fc.get("features") or []:
props = feat.get("properties") or {}
features.append({**feat, "properties": slim_alert_properties(props)})
for feat in (sbw_fc or {}).get("features") or []:
if not isinstance(feat, dict):
continue
props = dict(feat.get("properties") or {})
props["source"] = "iem-sbw"
# IEM uses `ps` (phenomenon) / `wfo`; map a display event.
if "event" not in props:
props["event"] = props.get("ps") or "Storm-based warning"
feat["properties"] = props
if bbox:
# Cheap reject: skip if no geometry; keep otherwise (polygons).
if not feat.get("geometry"):
continue
features.append(feat)
return {"type": "FeatureCollection", "features": features}
features.append({**feat, "properties": slim_alert_properties(props)})
merged = {"type": "FeatureCollection", "features": features}
if clip_box:
merged = clip_fc_to_bbox(merged, *clip_box)
elif bbox:
merged = clip_fc_to_bbox(merged, *parse_bbox(bbox))
return merged
key = f"alerts:{area or ''}:{bbox_cell_key(bbox) if bbox else ''}"
return await _ttl_get(key, 30.0, _load)

View file

@ -3,10 +3,12 @@
from live_layers import (
MARKER_FIELDS,
bbox_center_radius_nm,
clip_fc_to_bbox,
filter_points_bbox,
parse_bbox,
quantize_bbox,
rainviewer_tile_url,
slim_alert_properties,
to_marker,
transform_adsb_lol,
transform_ais_frame,
@ -15,6 +17,7 @@ from live_layers import (
transform_wfigs_incidents,
_cache,
_ttl_get,
_wfigs_params,
)
from camera_scraper import parse_caltrans_json
@ -287,3 +290,64 @@ def test_ttl_get_does_not_block_other_keys():
assert _cache["slow"][1] == "S"
assert _cache["fast"][1] == "F"
_cache.clear()
def test_clip_fc_to_bbox_drops_far_features_and_empty_geometry():
fc = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {"event": "near"},
"geometry": {"type": "Point", "coordinates": [-78.7, 35.8]},
},
{
"type": "Feature",
"properties": {"event": "far"},
"geometry": {"type": "Point", "coordinates": [-120.0, 45.0]},
},
{
"type": "Feature",
"properties": {"event": "nogeom"},
"geometry": None,
},
{
"type": "Feature",
"properties": {"event": "poly-overlap"},
"geometry": {
"type": "Polygon",
"coordinates": [[
[-79.0, 35.0], [-78.0, 35.0], [-78.0, 36.0],
[-79.0, 36.0], [-79.0, 35.0],
]],
},
},
],
}
clipped = clip_fc_to_bbox(fc, -79.0, 35.5, -78.0, 36.0)
events = [f["properties"]["event"] for f in clipped["features"]]
assert events == ["near", "poly-overlap"]
def test_slim_alert_properties_keeps_popup_fields_only():
fat = {
"event": "Tornado Warning",
"severity": "Extreme",
"headline": "TORNADO WARNING",
"areaDesc": "Wake",
"wfo": "RAH",
"source": "nws",
"parameters": {"WIND": ["70"]},
"description": "A long narrative " * 40,
"instruction": "Take shelter.",
"geocode": {"SAME": ["037183"]},
}
slim = slim_alert_properties(fat)
assert slim == {
"event": "Tornado Warning",
"severity": "Extreme",
"headline": "TORNADO WARNING",
"areaDesc": "Wake",
"wfo": "RAH",
"source": "nws",
}