screentinker/Examples/PIP-Weather-Radar/radar-overlay.html
screentinker 89cbcac2cd
Some checks are pending
CI / Unit tests (node --test) (push) Waiting to run
CI / OpenAPI spec lint (push) Waiting to run
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Waiting to run
CI / Boot smoke + version check (push) Waiting to run
Add PIP-Weather-Radar example (TV-style live radar overlay) (#133)
* 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>
2026-06-18 21:05:19 -05:00

57 lines
2.9 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Live Weather Radar</title>
<!-- Vendored Leaflet, served same-origin so the server CSP (script-src 'self') allows it. -->
<link rel="stylesheet" href="/leaflet.css">
<style>
html, body { margin: 0; height: 100%; background: #0b0d10; overflow: hidden; }
#map { position: absolute; inset: 0; background: #0b0d10; }
/* darker, calmer Leaflet attribution to match the TV look */
.leaflet-control-attribution { background: rgba(10,12,16,.6) !important; color: #9aa3ad !important; font-size: 10px; }
.leaflet-control-attribution a { color: #c9d2db !important; }
.hud { position: absolute; left: 0; right: 0; top: 0; z-index: 1000; pointer-events: none;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; color: #fff; }
.bar { display: flex; align-items: center; gap: 14px; padding: 12px 18px;
background: linear-gradient(180deg, rgba(8,10,13,.92), rgba(8,10,13,0)); }
.live { display: flex; align-items: center; gap: 9px; font-weight: 800; letter-spacing: .06em;
text-transform: uppercase; font-size: clamp(15px, 2.4vw, 24px); }
.dot { width: 13px; height: 13px; border-radius: 50%; background: #FF2D2D;
box-shadow: 0 0 0 0 rgba(255,45,45,.7); animation: pulse 1.2s ease-out infinite; }
@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(255,45,45,.7) } 70% { box-shadow: 0 0 0 12px rgba(255,45,45,0) } 100% { box-shadow: 0 0 0 0 rgba(255,45,45,0) } }
.area { font-weight: 600; font-size: clamp(13px, 2vw, 20px); opacity: .92; }
.spacer { flex: 1; }
.clock { font-variant-numeric: tabular-nums; font-weight: 600; font-size: clamp(12px, 1.8vw, 18px);
color: #cfe8ff; opacity: .9; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; padding: 0 18px 10px; }
.chip { pointer-events: none; font-size: clamp(11px, 1.6vw, 15px); font-weight: 700; color: #0b0d10;
padding: 4px 10px; border-radius: 999px; box-shadow: 0 2px 8px rgba(0,0,0,.4); }
.chip.none { background: #2c3340; color: #aeb6c0; font-weight: 600; }
.legend { position: absolute; right: 12px; bottom: 26px; z-index: 1000; pointer-events: none;
background: rgba(10,12,16,.72); border-radius: 10px; padding: 8px 10px; font-family: system-ui, sans-serif;
color: #dfe6ee; font-size: 11px; line-height: 1.5; }
.legend .row { display: flex; align-items: center; gap: 7px; }
.legend .sw { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }
</style>
</head>
<body>
<div id="map"></div>
<div class="hud">
<div class="bar">
<span class="live"><span class="dot"></span>Live Radar</span>
<span class="area" id="area"></span>
<span class="spacer"></span>
<span class="clock" id="clock"></span>
</div>
<div class="chips" id="chips"></div>
</div>
<div class="legend" id="legend"></div>
<script src="/leaflet.js"></script>
<script src="/radar-overlay.js"></script>
</body>
</html>