Composite a basemap under server-side radar images, and stop loop frames stacking (#182) #183
No reviewers
Labels
No labels
area:ai
area:ci-cd
area:notifications
area:observability
area:public-pages
backlog
bug
duplicate
enhancement
help wanted
invalid
question
type:decision
type:feature
type:infra
type:maintenance
type:security
v1.0.1
v1.1.0
v1.2.0
v1.3.0
v2.0.0
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/WeatherBot!183
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/radar-basemap-and-loop-disposal"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #182. Two defects reported against
v2.0.0-rc1while reviewing the Explorer on dev, sharing one root cause, plus a third found while tracing (filed on the issue, not fixed here).The bugs
No basemap.
radar._fetch_noaa_staticrequeststransparent=true, so every stored radar PNG is reflectivity on a transparent background. The dashboard and public pages only look right because Leaflet supplies OSM tiles client-side; the Explorer renders the stored image directly, so echoes float on blank page. Affects every server-side radar image — the #85 alert-time snapshot, #130 periodic frames, the #140 loop, and all notifier attachments.Loop frames stacking.
assemble_gifsaved GIFs without a disposal method, so Pillow defaulted to disposal 0 ("do not dispose") and each frame's transparent pixels left the prior frame's echoes on screen. Reproduced through the real code path — three frames with blobs at x-offsets 0/20/40 rendered as[0],[0,20],[0,20,40]. The SPC outlook GIF shares this assembler and was unaffected only because IEM's maps are opaque, which is why it survived review.The fix
Compositing each frame onto an opaque basemap resolves both — opaque frames cannot accumulate. Esri's ArcGIS export endpoint takes the identical
bbox/bboxSR/imageSR/sizeparams as NOAA's radar export, so the basemap is pixel-aligned by construction: no tile stitching, no reprojection, one cached fetch per location.Key decisions, all recorded on the issue:
BBOX_PADDINGconstant, so a basemap fetched today aligns with a frame captured months ago. This retroactively fixes all 13 months of already-captured history — capture-time compositing could not, and history cannot be recaptured.test_composite_on_basemap_draws_reference_labels_over_the_radarpins the order.disposal=2regardless, as belt-and-braces against a future transparent-frame caller.The public alert page was checked rather than assumed: it links the same
/media/alert-radar/still and renders it as a plain<img>, not a Leaflet overlay, so compositing there is correct with no double-up.Verification
The cumulative-overlay regression test was confirmed to genuinely fail without
disposal=2and pass with it, rather than trusted as a smoke test.ruff check app/ tests/— cleanNot in scope
get_radar_image(animated=True)— which every notifier passes — is a no-op:_fetch_radarignores the flag and always returns a static PNG, while the result is cached ascontent_type="image/gif". So notifier attachments are PNG bytes labelled GIF and the "animated radar" channel option has never animated. Documented on #182; being handled separately.Every radar PNG this app stores was fetched with transparent=true, so it carried reflectivity and nothing else. The dashboard and public pages only looked right because Leaflet supplies OSM tiles underneath client-side; the Explorer renders the stored image directly, so echoes floated on blank page with no coastline or state line to orient against. The same transparency made assemble_gif produce a cumulative smear. Pillow defaults to GIF disposal 0 ("do not dispose"), so each loop frame's transparent pixels left the previous frame's echoes on screen and frame N showed frames 1..N stacked together. Compositing each frame onto an opaque basemap fixes both: opaque frames cannot accumulate. Esri's ArcGIS export endpoint takes the identical bbox params as NOAA's radar export, so the basemap is pixel-aligned by construction — no stitching, no reprojection, one cached fetch per location. The base and boundaries/places layers are cached separately rather than pre-flattened, because the radar composites BETWEEN them. Flattening buries the place labels under the echoes, and it does so exactly where they matter most: the labels a reader needs are the ones under the storm. Compositing is applied at assembly time rather than capture time, so all retained history benefits, not just frames captured from here on. Scoped to the Explorer and the notifier attachments; the dashboard and public radar API endpoints stay bare so their baked-in basemap cannot double up with the Leaflet tile layer. The radar cache key carries the variant so the two can never collide. disposal=2 goes in regardless as belt-and-braces, so a future caller passing transparent frames cannot silently reintroduce the smear. Basemap failures are fail-soft everywhere: a fetch error, a malformed PNG, a Pillow error, or the disable setting all return the original radar bytes rather than raising. This sits on the alert-notification path, so a missing basemap must never cost a notifier its radar image. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>