# Replay map visualization (estimated vs ground-truth tracks) **Task**: AZ-700_replay_map_visualization **Name**: HTML map showing estimated GPS track vs tlog ground-truth track **Description**: New `gps-denied-render-map` console script. Takes a JSONL of estimator output + a tlog (or CSV fallback) and renders a single-file HTML map (folium / Leaflet) with both tracks in distinct colors, start/end markers, and an embedded accuracy summary from AZ-699. **Complexity**: 3 points **Dependencies**: AZ-699 **Component**: cli (offline analysis surface) **Tracker**: AZ-700 **Epic**: AZ-696 ## Problem Today the only feedback from a replay run is a JSONL file. There is no way to visually verify whether the estimator is drifting, jumping, or roughly tracking the real flight. A human reading the JSONL cannot quickly answer "does this make sense geographically?" The user's pipeline explicitly calls for: "and then show both points on the map." ## Outcome - A standalone CLI `gps-denied-render-map` produces a self-contained HTML map of the estimated track + the tlog ground-truth track for any prior replay run. - The map is shareable as a single file (no server required); developers open it locally; AZ-701's HTTP API serves it back to API consumers. ## Scope ### Included - New module `src/gps_denied_onboard/cli/render_map.py`. - New console script `gps-denied-render-map` in `pyproject.toml`. - folium dependency pin in the appropriate `[project.optional-dependencies]` group (NOT in airborne-binary deps — operator-side only). - Default map style + tile provider (OpenStreetMap fallback documented for offline use). - Auto-fit bounds; distance circles (100 m, 50 m) around start point for scale. - Accuracy summary banner (read from `_docs/06_metrics/real_flight_validation_{date}.md` when `--summary` is passed). ### Excluded - Interactive time-slider playback (deferred follow-up). - Embedded altitude profile chart. - Animated marker traversal. ## Acceptance Criteria **AC-1: CLI produces self-contained HTML** Given a JSONL + tlog When `gps-denied-render-map --estimated out.jsonl --truth derkachi.tlog --output map.html` runs Then `map.html` exists, parses as valid HTML, exits 0 **AC-2: Two distinct tracks visible** Given the rendered map opened in a browser When inspected Then it contains exactly two polyline layers (red = truth, blue = estimated) with start/end markers **AC-3: Markers + scale circles** Given the rendered map When parsed Then it contains the start (green) + end (black) markers + 100 m + 50 m scale circles **AC-4: Accuracy summary inclusion** Given `--summary _docs/06_metrics/real_flight_validation_2026-XX-XX.md` When the map renders Then the HTML header contains the accuracy metrics table **AC-5: Offline fallback documented** Given an environment without internet access When the map is rendered with `--offline-tiles` Then tile loading uses a documented fallback (or fails fast with a clear error if no fallback is configured) ## Non-Functional Requirements **Compatibility** - Output HTML must render in Chrome 110+ and Firefox 110+ without console errors. **Performance** - For a 60 s flight (~600 truth points + ~600 estimated points), render time < 5 s on Tier-1 hardware. ## Unit Tests | AC Ref | What to Test | Required Outcome | |--------|-------------|-----------------| | AC-1 | CLI invocation with synthetic data | Output HTML file exists + non-empty | | AC-2 | Parse output HTML | Exactly 2 polyline layers + 4 expected markers | | AC-4 | Summary embed | Markdown summary metrics present in HTML | ## Blackbox Tests | AC Ref | Initial Data/Conditions | What to Test | Expected Behavior | NFR References | |--------|------------------------|-------------|-------------------|----------------| | AC-1 | Real Derkachi replay JSONL + tlog | End-to-end render | HTML opens in browser, both tracks visible | Compat | ## Constraints - folium MUST be in the operator-only dep group; airborne binary cold-start regression test must remain green. - HTML output MUST be self-contained — embedded JS/CSS, no per-page CDN calls in `--offline-tiles` mode. - Console script naming follows the project pattern (`gps-denied-`). ## Risks & Mitigation **Risk 1: folium dep size** - *Risk*: folium pulls ~5 MB of JS. Adding to airborne deps would regress cold-start. - *Mitigation*: optional-dependencies group + ADR-002 build-time exclusion principle. **Risk 2: CDN dependency at render time** - *Risk*: Default folium uses Leaflet via CDN — fails on offline Jetsons. - *Mitigation*: Document `--offline-tiles` flag; provide bundled assets path or fail-fast.