Traffic roads come from OpenFreeMap vector tiles, or TomTom flow tiles with a
key (Hybrid by default). Military areas use OpenFreeMap polygons with a bundled
worldwide name index; wide views show named points. ALPR reads an hourly
OpenStreetMap extract. Cockpit place context and country, state and US county
outlines use bundled Natural Earth and US Census data. Overpass is used only
when an operator configures their own endpoint.
Also: camera arrivals no longer land below ground, keyless terrain retries on
429, bounded relation geometry for configured Overpass, one shared
OpenStreetMap credit, and separate tile and API transports for embedders.
Co-authored-by: theGaryLarson <[email protected]>
Co-authored-by: Milan Khanal <[email protected]>
The Natural Earth and neighborhood packs were loaded with a JSON-attributed
import(). The Vite dev server answers that request with a JavaScript module,
which the browser rejects, so every analyst region scope failed and fell
through to a geocode lookup that could take 12-28 s.
- src/data/bundledJson.js fetches the pack from a new URL(..., import.meta.url)
asset in the browser; under Node the caller's attributed import runs.
- resolveRegionRingForQuery caps the geocode + admin-boundary rung at 3 s and
returns { error: 'region-timeout' }; the lookup keeps running and fills the
cache. The analyst query reports it as code 'region-timeout'. The Nepal
locator opts out of the budget.
- Unit tests for the loader, the timeout and the analyst error; the behavior
harness checks the Gulf of Mexico scope and retries Texas on region-timeout.
Co-authored-by: Bilawal Sidhu <[email protected]>
Adds MODIS NRT to Active Fires and a keyless Fire Perimeters layer (NIFC WFIGS) with
InciWeb incident links, served through a capped, cached /api/fire-perimeters proxy.
Co-authored-by: Gustavo Beneduzi <[email protected]>
Co-authored-by: James Cooke <[email protected]>
The harness called the compatibility facade's private _refreshTogglePanel,
which the layer-lifecycle refactor removed; Transit and Directions call
refreshLayerStats() on the bare lifecycle, which lacked it, so their rows
only repainted on the next tick. Add refreshLayerStats() to the lifecycle
and use it from the harness. With the crash gone, two further checks ran:
the selected-entity paint wait now also waits for Radio's own reveal, and
on phones the title bar sits 16 px from the top so both broadcast waves
stay on-screen.
Co-authored-by: Bilawal Sidhu <[email protected]>
Adds a Local ADS-B layer fed by your own receivers: a browser WebUSB
RTL-SDR (with FM listening) and local 1090/978 MHz decoder feeds
(LOCAL_RECEIVER_FEEDS, local and private addresses only). Local aircraft
draw in magenta with class, silhouettes, 3D models, trails and a position
sanity filter; the click card names the band and input that heard them.
Builds on Sameh Khamis's SDR prototype and the initial GEV port by
Mazeyar Moeini Feizabadi (#134); the decoder-feed design follows the
notes on #57. Closes#57.
Co-authored-by: Sameh Khamis <[email protected]>
Co-authored-by: Mazeyar Moeini Feizabadi <[email protected]>
The right-rail layout pass lifts the Context, CCTV, Weather and Recent
Imagery panel scrollers to measure their natural heights, which clamps
their scroll offset to zero, but it only restored scrollers tagged
data-rail-scroller. A layer that refreshes its Layers-panel row every
second snapped the Context panel back to the top while scrolling. Tag
every lifted scroller and pin the pairing with a test that reads the
measuring CSS.
Co-authored-by: Bilawal Sidhu <[email protected]>
Select a box on the world and browse the last 30 days of open satellite imagery over it, keyless: NASA GIBS Harmonized Landsat Sentinel-2 (30 m) and the VIIRS daily overview (250 m), with a day strip, IMAGE / VS BASEMAP / A / B modes with a swipe divider, PNG export and share links. The before/after swipe from the Nepal scene moves into shared modules so both use it.
Co-authored-by: manjunath22466 <[email protected]>
Reflect the weather stack, analyst records and feed provenance, satellite pass predictions and live camera video that landed since the last full pass.
Co-authored-by: Matt Van Horn <[email protected]>
Wind (NOAA GFS and ECMWF IFS: animated particles, colour field, a reading at map centre), observed weather (NOAA MRMS radar, GOES and NESDIS infrared clouds, lightning density) on one shared history timeline, NHC and CPHC cyclone advisories with tracks and cones, and a WEATHER panel in the right rail. On 3D Tiles the observed products render as raised shells with a sharper window around the view.
Co-authored-by: Gustavo Beneduzi <[email protected]>
Co-authored-by: Daniel Slay <[email protected]>
Co-authored-by: Rehaan Delmotra <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
At 720px and below both panel stacks scroll vertically, so each panel's absolutely positioned glow (negative inset) became scrollable overflow on both axes and drew a horizontal scrollbar band plus a spurious vertical scrollbar whenever CCTV, Context, Data Layers or Scenes was expanded. Pin the glow to its panel box in the narrow-screen block and add a stylesheet test that checks every 720px media block.
## Changes
Follow-up to #596 from the owner's look: a parked transit vehicle turned with the camera. Moving vehicles project their world course into screen space on every camera change, but a vehicle that had never moved on screen could have no display course and was skipped by that pass, leaving its billboard at a screen-anchored angle; rotation invalidation also relied on camera events that a pose change without a revision could miss. Now every visible vehicle with any usable course (derived, held, or the feed's reported bearing) is re-projected at the existing 5 Hz cadence on camera pose changes. Moving playback and the 120°/s course limiter are unchanged.
## How it was verified
- `npm run format:check` (861 files), `npm run check:boundaries` (109 groups), `npm test` (4,068 passing), `npm run build` clean; tracking regression 109/109 in a real browser on Google 3D tiles.
- Node tests: a stationary vehicle with a held course, and one with only a reported bearing, under a camera at heading 0° then 90° yield screen rotations 90° apart.
- Browser harness (`scripts/qa-transit.mjs`, keyed on Google 3D tiles): a parked scripted vehicle orbited by 90° rotates 90.000° on screen; a moving vehicle's rotation still matches its rendered travel; both checks fail on main and pass here. Full keyed run: 201 pass; the nine failures are the per-frame allocation gate already recorded as a follow-up and threshold-edge readability lines on the Boston matrix, both unchanged from #596.
## Changes
Follow-up to #587 after the owner's first look at the merged transit layer on Google 3D tiles.
- **Sensor presets keep the real silhouettes.** Under NVG, thermal (white-hot, black-hot, Ironbow) and noir, vehicles are the same silhouettes as in normal view, filled opaque white with a 2 px dark halo, at the CRT display sizes (26 px fleet, 39 px selected). The solid rounded "signature" bodies are gone.
- **The selected vehicle's trail is visible.** The retained history is now draped onto 3D tiles and terrain (`GroundPolylinePrimitive`, `ClassificationType.BOTH`, the way the traffic heat-lines are drawn), 3 px core over a 5 px dark backing, per-segment age colour, a short draped head clipped to the sprite. Two defects kept it off screen before: Cesium's per-instance attribute getters return copies, so the renderer's show/colour edits were discarded, and a marker did not recover after the camera jumped to it, which hid the trail with it. Trail geometry is now prepared on selection independent of floor resolution and marker visibility; sparse long intervals (rail) keep a bounded marker corridor instead of dropping the interval; classification survives map-source changes.
- **Transit sits in the Movement panel group** between Street Traffic and Bike Share (the panel orders by `PANEL_GROUPS`, not by catalog order; the catalog is moved too).
## How it was verified
- `npm run format:check` (854 files), `npm run check:boundaries`, `npm test` (4,062 passing), `npm run build` clean; tracking regression 109/109 in a real browser on Google 3D tiles.
- Browser harness (`scripts/qa-transit.mjs`), keyed on Google 3D tiles and keyless: keyed 194 pass, with the trail pixel check passing at 300, 600 and 900 m; the remaining keyed failures are the per-frame allocation gate already recorded as a follow-up from #587 (CPU and frame time within budget), four readability lines at threshold edges, and the card-anchor check now counting frames where the selected marker is legitimately off screen; keyless 193 pass with the remaining misses being threshold edges on the thin tram silhouette (centre 0.83–0.88 against 0.85) and one bus ring 0.28 against 0.25 on a pure-white roof.
- New harness checks: `Google 3D selected trail visible at 6 of 8 projected samples` (repeated off/on framebuffer captures at 300/600/900 m, samples outside the sprite footprint, a background-change negative control; fails on main, passes here), a camera-arrival check (select a moving vehicle, `setView` onto it, marker shown and trail body drawn within 2 s), `PANEL_ORDER` pinned, raster tests comparing every sensor raster's mask and aspect ratio to the normal silhouette.
- Direct look on a keyed build: a live CapMetro bus at 30 m/s selected through the layer API draws its draped trail along the highway lane (harness capture `keyed-600m-trail-visible.jpg`).
- Adversarial review by Codex Astra before the last two fix passes (stack-switch classification, marker corridor under the vertex cap, pixel-gate loophole), all three fixed with regressions.
Docs: `docs/CURRENT-STATE.md` and `CHANGELOG.md` updated in this PR. No data-source changes.
Supersedes #218 by @spcpza, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Contributor: @spcpza. His original commit is the base of this branch, and his own re-port onto main (2026-09-15, `5b69011`) was consulted while finishing the height work. That port reads every vehicle's surface from the app's shared ground-floor system (`cachedGroundFloor` / `warmGroundFloor` / `GROUND_FLOOR_LIFT_M`) rather than sampling the scene per layer, keeps a vehicle hidden until its cell answers, warms a bounded batch per poll on a timer instead of per frame, and renders at the ellipsoid above the altitude gate. That is the sounder primitive — one floor system for the app, with the rooftop-versus-street question already settled against a datum-correct DEM prior — and this branch's height work was rebuilt on it, replacing a bespoke cell cache, ground prior and rooftop rule written here. Credit for that direction is his.
Future work (not in this PR), both written up in docs/CURRENT-STATE.md:
- Readable route names. The realtime feeds carry only `route_id`; `route_short_name` / `route_long_name` live in each operator's static GTFS `routes.txt`, and headsigns in `trips.txt`. Doing it properly wants a server-side metadata loader beside the transit proxy with fixed registered static sources, bounded indexes built once and persisted, a slow conditional refresh, and a versioned route dictionary the browser caches. Shipped here: ids are never rewritten, and a cut one carries an ellipsis instead of reading as a typo.
- Below-ground rendering. Subway vehicles are drawn on the surface directly above themselves, because the feeds carry no depth and a train nobody can see is not worth drawing; the card says so plainly. Real treatment wants OSM tunnel geometry per city and a ghosted sprite tethered to the surface. The owner recorded it and left it.
Round four and after (2026-09-15). The owner tested round three under FLIR and NVG, and then asked for the design to be taken further: elegant in performance, beautiful on camera, trails with a cache for public transport, Cesium-native, and a core other moving layers can adopt. Codex Astra reviewed the plan (it confirmed the speed fault: a glide started from the drawn position at receipt time but spanned the gap between report times, so with MBTA reporting every ~10 s against a 15 s poll every second glide ran at 1.5x; it rejected a queued-segment patch), then wrote the design, then implemented it in commit-sized passes with adversarial review between them and real-browser harness runs by the integrator.
What this PR now contains beyond the contributor's original port:
- Delayed playback in a contact-agnostic core (`src/data/contactPlayback.js`): each vehicle keeps a typed, bounded fix history and a display clock that runs at one second per second through it, a feed-derived lag behind real time, holding at its newest fix rather than extrapolating and never sprinting to catch up. Marker, course, motion word, card and trail all read the same displayed sample. The clock has an explicit time-offset and seek API so a rewind is a later feature, not a rewrite; flights and vessels are untouched.
- A selected-vehicle trail (`src/data/contactTrailRenderer.js`, `src/layers/transit/trails.js`): the retained history drawn with batched Cesium primitives, clipped at display time so the head is exactly the sprite, faded by age, in the mode colour (white under sensor presets), on the same surface path as the marker.
- Bounded proxy-side history (`server/providers/transitHistory.js`): fifteen minutes and at most 128 fixes per vehicle with hard byte ceilings, MBTA only for now (opt-in per feed), served by a small endpoint that never fetches upstream, so a selected trail survives a browser reload while the server keeps running.
- Scene work bounded to what is visible and moving, an occluder, cached DETECT candidates, holds on the render loop only while something visible moves.
- Sensor presets: white-hot haloed sprites under NVG/FLIR/noir (black-hot inverts), saturated mode colours under CRT, per-mode bracket tiers in every theme, the selected card keeping its mode colour.
- A discriminating browser harness (`scripts/qa-transit.mjs`) that samples postRender frames, reads the real card anchor, and measures fleet budgets at 800 and 3,000 vehicles.
Not built, recorded as follow-ups: curated route display names (lowest priority per the owner), fleet-wide tails, route snapping, a rewind UI, adoption of the playback core by flights and vessels.
Verified at merge (2026-09-15 morning): unit suite 3,896 passing, build clean, format and boundary gates clean, tracking regression 109/109, browser harness keyless 200 pass / 0 fail (13 sensor checks unexercised because the live view offered fewer than six isolated sprites; every judged sprite passed), keyed on Google 3D tiles 198 pass with the per-frame allocation gate as the only failure. Follow-ups recorded: the keyed build's playback sampler still allocates per frame (CPU and frame time stay within budget; the 3,000-vehicle metro view sits at a 27.6 ms frame interval against a 25 ms budget), the shared detection overlay's per-paint allocation, friendlier route names, fleet-wide tails, a rewind UI, and adopting the playback core for vessels and flights.
Co-authored-by: spcpza <[email protected]>
## Explore the map without losing your target
Tilt and north-up used to stop tracking an aircraft or satellite. Both controls now keep the selected target centered while easing into the new angle over 650 ms, preserving camera range. Tilt switches between top-down and oblique views; north-up takes the shortest rotation. Rapid presses reverse a pending tilt, and pressing north-up during a tilt completes both changes.
New navigation, target selection, map gestures, and teardown cancel the transition. Cockpit keeps its existing camera guard.
## A data panel that follows how you explore
The panel now has five clearly labeled sections:
| Section | Layer order |
| --- | --- |
| Movement | Satellites → Live Flights → Military Flights → Live Vessels → Street Traffic → Bike Share |
| Cameras | Cameras → Mapped ALPR Cameras |
| Infrastructure | Mapped Installations → Data Centers → Submarine Cables → Dams |
| Events | Space Missions → Earthquakes → Active Fires |
| Utilities | Directions → Radio |
Readable panel labels replace CCTV, Bikeshare, Datacenters, and provider-heavy names. Source attribution remains in the subtitles, and toggle accessibility labels match the visible names. Presentation order is independent of layer registration; hidden layers stay hidden and unlisted layers remain available under Other layers.
## Validation
- Camera changes: full Node 24 suite, including allocation gates — 3,699 passing and one skipped.
- Final panel changes: 42 focused tests passed; browser inspection confirmed all sections, ordering, and labels.
- Real Cesium Camera/EntityView tests cover moving aircraft, low/high satellites, cancellation, navigation ownership, and rapid button presses.
- Live browser: UITMSAT-2 remained centered and selected through tilt and north-up, including combined commands.
- Final formatting, package-boundary checks, and production build pass.
- GitHub CI verifies the final head on Node 24, Node 26, and Windows onboarding before merge.
Supersedes #296 by @Matthew-Selvam, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: Matthew-Selvam <[email protected]>
Supersedes #350 by @sendmebits, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: sendmebits <[email protected]>
Supersedes #388 by @KuraPiee, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: KuraPiee <[email protected]>
Supersedes #442 by @yashveeeeeeer, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: Yashveer Singh <[email protected]>
Supersedes #420 by @GladiatorrX9, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: Cyber Knight <[email protected]>
Supersedes #441 by @MiguelGFerreira, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: MiguelGFerreira <[email protected]>
Supersedes #219 by @spcpza, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: spcpza <[email protected]>
Supersedes #235 by @cora-fresh-labs, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Future work (not in this PR): hand-drawn marks do not persist across a reload today, and neither do spoken annotations; there is no storage layer for marks. The natural home is Scenes: a drawing that belongs to a shot and persists per shot, so a scene animation can carry its own marks. Needs an owner.
Co-authored-by: Clint Borle <[email protected]>
Supersedes #438 by @rileygramlich, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: rileygramlich <[email protected]>
Supersedes #408 by @vegettto, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: vegettto <[email protected]>
Supersedes #161 by @michaelhan1208, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: michaelhan1208 <[email protected]>
Supersedes #356 by @Lob26, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: Pedro Lobato <[email protected]>
Supersedes #309 by @SurefireStudios, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: SurefireStudios <[email protected]>
Supersedes #283 by @dajiaohuang, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: dajiaohuang <[email protected]>
Supersedes #239 by @mml-studio, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: Memel <[email protected]>
Supersedes #253 by @Maher-Reven, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: Maher Kurdi <[email protected]>
Supersedes #357 by @Lob26, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: Pedro Lobato <[email protected]>
Supersedes #168 by @daikaginza, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: daikaginza <[email protected]>
Supersedes #293 by @raiyan22, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: raiyan22 <[email protected]>
Supersedes #301 by @VassagoDevteam, ported onto the current component layout with fixes from review.
Verified before merge: format, package boundaries, unit suite, build, and the browser tracking suite; a browser harness for the change where one applies; adversarial review applied.
Co-authored-by: Vassago DevTeam <[email protected]>
Recover across existing Overpass mirrors, reject cached refusals, and serve consistent last-good data to coalesced callers. Preserve the original contribution from PR #165.
Expanding the empty KEY_SETUP_EXTERNAL_KEYS array under set -u is a fatal
'unbound variable' on bash 3.2, which macOS ships as /bin/bash — so a keyless
./scripts/dev-fresh.sh died at the provenance-marker line before doing
anything. The ${arr[*]:-} guard keeps 3.2 alive and leaves populated launches
byte-identical (same idiom the script already uses for DEV_UNSET).
Verified with /bin/bash 3.2.57: the extracted block runs clean under set -u
with no keys (empty CSV) and with keys (ordered comma-joined names); the full
launcher boots keyless to a live server, and exported keys still classify as
externally managed in Provider Settings. A behavioral guard now runs the real
block from the script both ways in the dev-fresh test surface, plus a textual
assertion pinning the 3.2-safe idiom.
The first formal release.
- One-click install via Pinokio; keyless boot lands on a live Esri World
Imagery satellite globe with keyless terrain, with automatic OSM fallback
and graceful degradation when terrain is unavailable.
- Provider Settings (the POWER UP panel): add, replace, or remove API keys
inside the app; credential files made owner-only before any secret is
written; external keys shown read-only; the panel refuses shared or proxied
servers.
- Keyless capability responses for the optional HUD summary and place search.
- Aircraft-identity voice answers cover operator, type, and route, and say so
plainly when enrichment is unavailable.
- README rewritten keyless-first; concise 0.1.0 changelog section; CI
workflow included.
Gates: 2,672 unit tests pass / 0 fail, build clean, tracking regression
108/108, map-source tray QA pass, setup doctor ready.