The chips are labelled "in view" but were tallied from the alert query result.
Alerts are fetched per state — one request instead of one per county — so the
feed routinely carries warnings hundreds of miles away. A Kenosha-centred map
reported "2x Tornado Warning" for tornadoes in Calumet and Winnebago, neither
of them on screen and neither reachable, since the view is bounded to two
counties.
Warnings are now sorted into three states rather than two:
- Unreachable: outside the bounded frame the map can ever show. Not drawn, not
counted. This is what produced the phantom tornado count.
- Reachable but off-screen: drawn, so it can slide into view at the edge as the
frame widens, but not claimed as "in view".
- On screen: tallied into the chips, recomputed on moveend/zoomend, because
framing settles asynchronously and it is the settled zoom that decides what
"in view" means.
Bounds come off the GeoJSON coordinates directly, covering every ring of a
MultiPolygon, rather than building a throwaway layer per feature to ask Leaflet
for an extent.
Asset version bumped to 3 so players holding the cached copy pick this up.
The auto-framing fitted the view to whatever warning polygons were active, so a
storm a few counties away pulled the frame out to cover it and the configured
area shrank to an unreadable corner of a half-state view. On signage that is
read at a glance, a map that wanders is worse than one that shows less.
Framing is now centred and bounded:
- The map never pans. The centre stays on the configured point and only the
zoom responds, because the box handed to fitBounds is symmetric about home.
- Zoom-out is capped at `max_counties` (default 2) county-widths in every
direction, with longitude scaled by cos(lat) so the budget is the same
distance on the ground north and south.
- Warnings entirely outside that box are not chased at all; the configured view
is held. Warnings clearing returns to it rather than staying parked on the
last storm.
- A floor on the frame keeps one small cell overhead from zooming to street
level, and fit padding drops to 24px, which on a PiP-sized overlay was
discarding a third of the width per side.
The overlay assets are served max-age=14400, so a player that had already
loaded them kept the old copy for four hours and silently ignored a redeploy.
The page URL and its script tag now carry a version, documented to be bumped
together.
Tests cover the invariants against the shipped frameFor source rather than a
copy of it: centred after reframing, capped at the county budget, small cells
floored, distant storms not chased.
* Add PIP-Weather-Radar example (TV-style live radar overlay)
A "cut to radar" PiP recipe: a Leaflet map (vendored locally for the
CSP) with a CARTO dark basemap, an animated RainViewer radar loop, and
live NWS warning polygons drawn and color-coded (tornado/severe-tstorm/
flash-flood/flood) with a pulsing "LIVE RADAR" HUD, count chips, and a
legend. Auto-frames the view to the active warning polygon(s).
Two modes: "always" (radar always up) and "on_warning" (default) which
shows the radar only while a qualifying warning covers the configured
point and clears it when the warnings expire — like a station breaking
in during severe weather.
100% keyless / open data: RainViewer radar, CARTO/OSM basemap, NWS
alerts. Zero Node deps; Leaflet is vendored client-side via
vendor-leaflet.sh (gitignored). Offline test covers the warning gate,
color map, RainViewer tile-URL builder, and overlay-URI round-trip.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(radar): note Leaflet is vendored locally, not committed
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Self-contained examples for the PiP overlay API (POST /api/pip), each
with a CSP-safe query-param overlay (external JS), config.example.json,
zero runtime deps, an offline test, and a README:
- PIP-Announce-Broadcast manual one-shot message to a screen/group
- PIP-Weather-Widget Open-Meteo current conditions (keyless)
- PIP-Air-Quality Open-Meteo US AQI widget (keyless)
- PIP-Crypto-Ticker CoinGecko price strip (keyless)
- PIP-News-Ticker scrolling RSS/Atom headlines
- PIP-Room-Status-Calendar ICS-driven Available/Busy room sign
- PIP-Event-Countdown client-side countdown, auto-clears at zero
- PIP-Welcome-Board rotating welcome/birthday cards from CSV
- PIP-Fundraiser-Thermometer goal-progress bar from local/URL JSON
- PIP-QR-Rotator rotating QR codes, encoded client-side
- PIP-Incident-Webhook event-driven: red on firing, clear on resolved
Also includes the CAP-AU (NSW RFS) and US NWS/NOAA emergency-alert
monitors that push expiry-aware PiP overlays.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>