Merge branch 'main' into feat/datacenters-norway-refresh

This commit is contained in:
German Khokhlov authored and GitHub committed 2026-11-06 15:55:23 +04:00
commit e073e4dbe4
182 files changed
+28226 -558

No files matched your search

+178 -10
View File
@@ -65,6 +65,59 @@ settling. Oblique views below 300 km are left alone.
`QA_BASE_URL=http://localhost:4173 node scripts/qa-zoom-out-guard.mjs` exercises
the real browser controller at desktop and phone widths with keyless fixtures.
Cesium's free look (Shift+drag, which turns the camera in place) works only
below 1,000 km. Above that height a few drags could turn the whole planet out of
frame and leave only stars, so Shift+drag does nothing there. Cesium also turns
the camera in place for a drag that starts on the stars below its trackball
height (7,500 km by default); that height is lowered to 1,000 km, so such a drag
orbits the globe above it, as it does higher up, and a tilt (drag or touch)
while the screen centre is on the stars there tilts about the planet's edge or
does nothing, wherever it starts. Rotation drags that start on the planet, and
zoom, are unchanged. How much of the frame shows the planet's surface is
measured by casting a 32×18 grid of view rays at the ellipsoid; while the scope
mask is on (the default) only the rays inside its keyhole count, since the mask
hides or dims the rest. Voice tilt and in-place turns of a free 3D camera above
1,000 km stop when about a tenth of the frame still shows the surface (or about
as much as it showed when it started), never less than about 1% of the visible
frame, and a view that already shows less only keeps or gains what it has. A
turn that cannot go half a degree further without crossing that limit is refused
with a reason instead of reported as done, and a turn it accepts turns at least
that half degree. A voice tilt to an angle (or a bare tilt, to -45°) that the
limit stops short of is aimed at the angle where it stops, and its result names
that angle (`pitchDeg`) with a `note`. A turn that keeps or gains surface is not refused, even after
the window has been resized, except that near the limit, where the planet's edge
crosses the sample grid, sampling wobble can occasionally refuse one (most often
above about 15,000 km and on phone or ultrawide frames); nothing then turns away
from the planet. A turn that starts where the last voice turn left the camera
keeps that turn's limit, or a higher one if the planet now fills more, so
repeating the request does not take any more; once anything else has moved or
turned the camera, a turn is measured from what is on screen. When a camera move
ends above 1,000 km with no ray on the surface, the camera turns straight down
at the same position and heading. The end of a move is taken from Cesium's
move-end event and from a check of its own that samples the pose every 250 ms
after a move starts and acts once it has held for half a second, because at some
of these poses the camera drifts by a rounding error every frame and Cesium
never sees it stop. That check keeps watching until move-end arrives, recovering
once after each move that comes to rest, and gives up five minutes after the
camera last moved. Limb glow, and a surface sliver up to about one grid cell
thick (some 50 px on a 900 px tall frame), can count as out of view. The
zoom-out guard above keeps wheel and pinch zoom-outs in view; this catches the
other ways a move can end looking away, such as a saved or shared view that did.
Views with more surface than that, such as the oblique orbital Director shots,
are left as they are, and so are tracked or locked cameras, a camera flight in
progress, a Director scene while it plays (holds included), a pointer held
down on the globe (a drag held still; with several fingers down, until the
last is released) and the 2D and Columbus modes; a check a flight, scene or
held pointer defers runs once it ends. A release outside the window that the
page never sees counts as given once the window loses focus, the page is
hidden, the canvas loses the pointer's capture, or the pointer moves with no
button down. A Director
shot loaded or replayed outside a run is not a playing scene. A view that loses
its surface without a camera move (a resize, or turning the scope on) is left as
it is until the next move. A voice turn requested while the camera is tracked is
judged by the pose voice's release of the follow camera will leave, before that
release, so a refused turn keeps the tracking.
Analyst region queries resolve unique Natural Earth names offline without
geocoding, so Gold Coast and Ivory Coast name the West African natural regions.
Duplicate names require a geocoded containment anchor; when several entries for
@@ -98,6 +151,106 @@ across the antimeridian, so they cannot use the strict ±180 check as-is, and
`src/sources/adsbRecords.js` and `server/providers/common/geo.js` keep their own
implementations.
## Wildlife Sightings — iNaturalist and Acartia
**Wildlife Sightings** (`wildlife-sightings`, share-link token `9`) is off by
default in the Wildlife group between Events and Weather. CONFIRMED and six
taxa presets round-trip in share links. Voice is off for this layer for now;
it is outside analyst queries and DETECT. Community observations are not a
census; private records are excluded.
`/api/inaturalist/observations` requests verifiable wildlife in view within
48 h (whales 7 d). Recency uses observation time: the `d1` date filter and a
strict rolling cutoff. Date-only rows qualify only when their day starts
within the window. Close views use a cached random sample of at most 200 rows;
global samples use newest observations first. Observation caches last five
minutes (global ten minutes), with stale fallback for at most one hour.
Observations and grid tiles share a paced priority queue: a four-call burst,
then one request per second, at most four in flight, ≤60/minute and a persisted
8,000/day budget. Superseded consumers leave before dispatch. The recovered
queue tail isolates failures; ordinary grid failures do not fail observations.
Provider 429 cooldown is shared; 5xx cooldown is resource-specific. Failed tiles
retain good cells for at most one hour. Stale replies carry `X-Data-Stale` and
known `Retry-After`. Failures keep filters and drawn rows, reporting DEGRADED
with retry timing; logs exclude request URLs and upstream exception text.
A partial failure still draws what arrived: a failed iNaturalist request, or
one failed half of an antimeridian view, keeps a good Acartia reply and the
other half, retains that area's last iNaturalist rows and reports DEGRADED.
The update fails outright only when every requested source failed. Each failed
source keeps its HTTP status and `Retry-After` (the HTTP status counts when no
`X-Upstream-Status` is sent), and the next request waits out the longest 429 or
5xx cooldown among them. One update requests both sources, so the cooldown
holds the whole layer; a fully successful refresh clears it.
`/api/acartia/sightings` adds Salish Sea marine mammals within 45.5–50.5° N,
126–121° W, cached ten minutes and stale for at most 24 h. A valid empty
snapshot, including one whose rows all fall outside coverage or the species
checks, is a fresh success that clears earlier rows; only a malformed or failed
upstream reply serves stale and starts a retry cooldown. WHALES accepts only
recognized cetaceans; other identified marine mammals appear under ALL or
MAMMALS. Sensitive iNaturalist locations remain 0.2° privacy cells; Acartia
positions are rounded to 0.01° on the server. Acartia comments are never retained.
Observer identity is retained only for photo attribution.
`/api/inaturalist/grid` supplies a dedicated, nonpickable billboard density glow,
with a view-relative density ramp from cool teal to warm amber. Overlapping
Gaussian splats span four logical cell widths; mono and CRT styles follow map
and cockpit vision changes. Empty snapshots paint nothing. Full footprints
stay inside the globe limb with a 5% radius margin; opacity fades from full at
3,000 km to zero at 8,000 km camera altitude. Culling and cached coordinates
add no per-frame work and leave sighting counts intact.
Only real sampled observation rows produce taxon badges at their public
positions. The badges use 28 px screen buckets, a 22 px overlap check and a
5,000-observation cap. Tinted circles have a white Material Symbols glyph and
1.5 px dark outline, remaining 12–22 px at all distances, including selection.
Glyphs: birds `raven`, mammals `pets`, reptiles `egg_alt`, amphibians
`water_drop`, fishes `set_meal`, insects `bug_report`, arachnids `pest_control`,
plants `eco`, fungi `umbrella`, other animals `category`, unknown `question_mark`.
A plain circle displays while the icon font loads. Row counts read
“{n} sightings in view”, using visible grid totals when available.
Views request at most six tiles, coarsening zoom for complete coverage. Server
and client keep bounded 400-tile caches for 15 minutes. Cached, stale and
adjacent-zoom tiles publish before network waits; each arriving tile replaces
its covered area, including healthy empty tiles. Grid tiles publish even while
the independent observation request is pending or failed. Enable waits for one
camera settle; camera acquisition uses moveEnd with a 600 ms debounce. The
documented UTFGrid endpoint's unspecified cell records have a schema guard;
see `KNOWN-ISSUES.md`.
Ambient cards use a fixed 96 px image width and a short creator name credit,
with a bounded frame cache. New cards fill free screen regions; visible cards
survive a random sample refresh until they leave the view or observation
window. The 110/125 km show/hide thresholds provide hysteresis; cards scale
and fade gradually through 120 km. Only painted photos suppress their own
badge and label. Labels yield to painted cards, then compete by ambient score;
repeated translated names within 80 screen pixels yield to higher-priority
content. At most 150 labels enter the host. Titles prefer a word boundary for
ellipses; accessible names remain complete. Selected thumbnails or names use
the shared selected chrome and paint lane, with no canvas source link.
Clicking a badge, label or photo flies to its supplied position and opens a
compact anchored photo card. Selection stays pinned across camera, filter and
sample changes until explicit deselection or layer disable. Its medium image
uses `object-fit: contain`, with names, compact observed age and creator/licence.
The card is the single source link to iNaturalist. Obscured sightings also stay
text-only; Acartia stays text-only with provider credit and whale guidance,
and its source link is always `https://acartia.io/`, never a contributed URL.
Placement avoids actual HUD, toolbar and rail rectangles, panning only when
necessary while preserving zoom. Escape, close or an outside click closes the
card and clears selection. Camera/layout changes reposition the card without
an idle frame listener; shell teardown also removes its host and subscription.
Attribution appears in the layer row and Data attribution popover:
“iNaturalist contributors · licences per record”, plus Acartia when drawn.
Disable/destroy releases the glow collection, texture atlas, timers, queues and
style listeners. Synthetic fixtures cover privacy, acquisition, rendering,
selection and card placement. Deterministic operation checks run in `npm test`;
wall-clock budgets run in `npm run test:perf`. All 11 locales are supported.
## Prohibited & Restricted Airspace — FAA special-use airspace
The Infrastructure group includes **Prohibited & Restricted Airspace** (`drone-airspace`,
@@ -766,9 +919,12 @@ each side, and the empty Voice Preview suggests things to say until voice
starts. Settings opened from chat keep chat's frame and show the shared AI
provider, the quick-commands option and then the voice preferences (Voice
Preview, inactivity timeout and model tier), so they stay in reach while
Preview is off, with Back to chat; voice Settings show Back to voice. Escape steps back from either Settings view, closes Voice
Preview is off, with Back to chat; voice Settings show Back to voice. Switching
AI Provider keeps that view: Settings opened from chat stay chat's Settings,
in chat's frame, for the new provider. Escape steps back from either Settings view, closes Voice
Preview, and does nothing in chat. Closing chat ends a typed session, and the
voice panel's close turns voice off. Asking the assistant from chat to open
voice panel's close turns voice off. Opening chat or closing the voice panel
also clears a voice error left on the dock. Asking the assistant from chat to open
Voice Settings or Voice Preview puts chat away, minimized or not, and shows the
voice panel; the typed conversation and its running reply carry on, and the
chat button brings them back.
@@ -1541,6 +1697,8 @@ are owned by a renderer-free panel component. The layer manager supplies current
snapshots, lifecycle actions and row descriptors. Remount and teardown remove
listeners and row subscriptions; obsolete completions do not repaint old rows.
The clear control presents busy state while its existing action owns the transaction.
The Wildlife group follows Events and precedes Weather; Wildlife Sightings
provides its confirmed toggle, taxa presets and fixed-order, nonempty legend.
## Sky background
@@ -4212,6 +4370,7 @@ its criteria cannot be silently ignored.
| Ski Pistes ⛷ (`ski-pistes`) | OpenSnowMap piste raster tiles (keyless; CC BY-SA tiles, OSM data ODbL; no bulk download) | `src/data/osmOverlays.js` | — (browser-direct tiles) | on view (Cesium tile requests; no polling) |
| Fire Perimeters 🔥 | NIFC WFIGS current interagency perimeters (keyless, paged past the 2000-record cap); InciWeb catalog + incident page origin and update time checks for verified incident-page links | `src/layers/perimeters/` via `src/app/layers/perimeters.js` | `/api/fire-perimeters` + `/api/fire-perimeters/inciweb/*` | 5 min (server caches: catalog 1 h; publication 30 min) |
| Prohibited & Restricted Airspace 🚫 | FAA Special Use Airspace prohibited (P) and restricted (R) zones (keyless FeatureServer, simplified geometry) | `src/layers/droneAirspace/` via `src/app/layers/droneAirspace.js` | `/api/drone-airspace` | 1 h (server cache 1 h; stale-on-error) |
| Wildlife Sightings 🦉 | iNaturalist wildlife observed within 48 h (whales 7 d), per view; Acartia Salish Sea marine mammal coverage (keyless) | `src/layers/wildlifeSightings/` via `src/app/layers/wildlifeSightings.js` | `/api/inaturalist/observations`, `/api/inaturalist/grid`, `/api/acartia/sightings` | camera moveEnd (600 ms debounce; enable settles 300 ms) + 5 min (observations 5 / 10 min; tiles 15 min; stale ≤ 1 h / 24 h) |
GNSS Integrity samples a 250 nm circle around the Earth point under the screen
centre. Camera reads debounce for 600 ms with a monotonic 2.5 s minimum gap;
@@ -4856,7 +5015,7 @@ cannot claim full acceptance.
**Lifecycle and proxy.** Contacts temporarily disables Transit and restores its
prior state on exit; Space Missions refuses enablement while isolating replay
data. Cockpit enters through Contacts and does not independently enable Transit.
data. Cockpit does not need Contacts and does not independently enable Transit.
The proxy fetches registered URLs only, validates redirect origins, bounds
responses and decoding, caches successful snapshots, and backs off after failed
refreshes. A 304 confirms contact without appending history. Missing vehicles
@@ -5587,7 +5746,7 @@ and unreachable upstream (502/504) separately from road geometry.
> branch: **2,281 on Node 25.6.1** (allocation suites skipped) = 2,255 on
> `main` + 26 route pins; the same tree on Node 24 reports 2,295.
- **Camera verbs** (`src/cameraVerbs.js`) — one motion slot, driven per clock tick. `move_camera` orbits/pans/tilts/rotates; `fly_route` is a cinematic dolly along an existing route annotation. `rotate_globe` rotates the world-space camera rigidly around Earth's center using its screen-up axis, preserving geocentric range and framing. Left/right describe visible globe motion, while `rotate` turns the camera in place. Omitted globe direction/mode default to right/continuous; explicit values prevail. Other camera motions keep their once default. Admission requires 3D, no Cockpit/tracking owner and the whole globe inside the existing keyhole circle, using the shared keyhole/projection geometry and vertical camera FOV with a rotation-local bounding-sphere cone for off-axis containment. Canvas-edge clipping alone does not block a rigid rotation or its unchanged Globe View preset. There is no altitude cutoff. An already smaller, fully framed globe rotates directly; repeated nudges, direction and speed changes recheck current framing without demanding a reset merely because ellipsoid altitude varied. A close-up or overlap refuses without moving the camera. When the unchanged Globe View preset fits, voice says “Globe rotation isn’t available from this view. You can say ‘Reset to Globe View and rotate the globe.’” without asking a permission question or automatically resetting. Explicit Reset/Show Globe View commands remain direct; an explicit reset-and-rotate request waits for reset success before requesting rotation. If OpenAI dispatches both tools together, rotation waits for earlier same-response resets and is skipped if any fails or is cancelled. OpenAI rejects later tool calls in the same response after a globe refusal, including calls arriving while globe admission is pending, and preserves response-scoped `tool_choice: none` for the refusal follow-up; Gemini rejects further tool execution, including queued sibling calls, until fresh typed input or admitted speech. Model interruption, advisory captions and microphone toggles do not release that guard. There is no pending Yes/No offer. Outside the refused response, explicit-command interpretation and avoiding repeated resets remain model-guided; a distinct model-issued reset call is not an app-enforced proof of user intent. A failed standalone reset can still be retried by the model, and a reset result does not guarantee the camera has fully settled. Before suggesting a reset, a cloned camera checks the unchanged Globe View destination against the same keyhole/depth rules without moving the live camera. If that preset cannot fit, `globe-reset-unavailable` asks for further zoom and the refusal instructs further zoom instead of a reset; this also covers ultrawide views. Unsupported view geometry or non-WGS84 projection assumptions fail closed without the closer-zoom instruction. Supported camera inputs/navigation intents cancel rotation before their action. Finite radius greater than the ellipsoid's maximum radius proves whole-arc clearance at constant centre-distance; no low-altitude swept solver is needed. The unchanged Globe View reset preset remains 18,000,000 m. Terrain and buildings remain outside the guarantee.
- **Camera verbs** (`src/cameraVerbs.js`) — one motion slot, driven per clock tick. `move_camera` orbits/pans/tilts/rotates; `fly_route` is a cinematic dolly along an existing route annotation. `rotate_globe` rotates the world-space camera rigidly around Earth's center using its screen-up axis, preserving geocentric range and framing. Left/right describe visible globe motion, while `rotate` turns the camera in place. `tilt` swings the camera about the ground under the crosshair, so the place stays centred and the heading is kept. It tilts in place instead with no ground under the crosshair, from beyond 1,000 km of range, or while an entity is tracked (voice releases the follow camera before moving, so a voice tilt from a tracked view still pivots); pitch stays within -89° to -5°, clamped only in the direction of travel so a top-down (-90°) view can tilt up out of it. Angles and limits are the camera's own pitch; at regional range the pivot's pitch is up to about 8° shallower (the Earth curves away), so the swing solves for the pivot pitch that gives the camera the angle asked for. A pivot swing toward the horizon lowers the camera (height above the pivot is range × sin|pitch|), so the swing stops lowering it at 150 m above the pivot (not above the terrain behind the camera) and the rest of the tilt happens in place. Beyond about 17 km of range the swing never hands over to an in-place tilt (that would lift the crosshair onto the sky); it keeps the camera at least 0.5° above the place's own horizon, which from roughly 500 km stops it short of -5° with the place still centred: a tilt to an angle beyond that stops at the angle reached and says so (`pitchDeg` is the angle reached, with a `note`), a continuous tilt stops there, and a further tilt up is refused as at the limit. A zoom during a pivoting tilt scales the range it holds, as for orbit, but a zoom in stops at the range that keeps that 150 m clearance; a zoom that can move no further (that floor, the 80 m floor, or the cap: 1,000 km for a tilt, where pivoting stops, and 5,000 km for an orbit) is reported as not zoomed. A zoom during a tilt to an angle re-judges the place's horizon limit at the new range: when that moves where the tilt ends, the tilt eases from where it is to the new end (holding the camera's pitch through the range change; a camera the zoom leaves past the new range's limit steps down to it with the zoom, since past it the place would drop behind the Earth), and the zoom's result names that angle (`pitchDeg`) with a `note`. Once the tilt continues in place, a zoom moves the camera like any zoom. A tilt with `pitchDeg` (or with neither angle nor direction, which means -45°) eases to that angle; a positive angle means the same angle below the horizon. `pitchDeg` is absolute, not a step: sent with a `direction` it contradicts, the tilt is refused. An angle the camera already holds answers `unchanged` but still takes navigation, so it stops a running motion and releases the follow camera. A tilt left or right is refused. A voice tilt, orbit, pan or rotate sent while a voice `fly_to_location` is still looking up or flying ("fly to London and tilt the camera" or "fly to Paris and orbit it" arrives as two sibling calls) waits for that flight to land, up to 15 s (moving to a later fly-to in the same reply as soon as it begins, whatever the earlier one reports), so it neither cancels the flight nor gets replaced by it; if the flight is cancelled, finds nothing or times out, the user grabs, touches or scrolls the map during it, or another navigation (a location-panel search, share link or file import, which leave the flight flying) takes the camera during it, the motion is not run and says so. A motion that runs after a fly-to of the same reply has already found nothing (Gemini runs one turn's calls one by one, and passes its turn as the reply) is refused the same way rather than moving the old view; a motion in a later reply runs. `stop` and `rotate_globe` do not wait, and a motion sent before its fly-to is not reordered: it runs at once, and the fly-to's result names the motion it stopped (`stoppedMotion`). Orbit still arms (`armed: 'waiting-for-arrival'`) when it starts during any other camera flight, and captures its target once the tween queue has been quiet for 0.8 s. Omitted globe direction/mode default to right/continuous; explicit values prevail. Other camera motions keep their once default. Admission requires 3D, no Cockpit/tracking owner and the whole globe inside the existing keyhole circle, using the shared keyhole/projection geometry and vertical camera FOV with a rotation-local bounding-sphere cone for off-axis containment. Canvas-edge clipping alone does not block a rigid rotation or its unchanged Globe View preset. There is no altitude cutoff. An already smaller, fully framed globe rotates directly; repeated nudges, direction and speed changes recheck current framing without demanding a reset merely because ellipsoid aLine truncated
- **The route dolly is shaped, not linear** (2026-08-20). A trapezoid speed profile (smoothstep up, cruise, smoothstep down, distance taken as the closed-form integral) eases both ends without changing the pace — duration is still `totalM / ROUTE_M_S[speed]`, with one exception: a 0.5 s minimum keeps a degenerate route from being an instant teleport, so routes under 10 m (slow) / 20 m (normal) / 45 m (fast) fly SLOWER than the speed word, never faster. Turns bank up to **10°** (a 90° street corner settles near 7.5°), measured as a triangular pulse over a 4 s window centred on the camera so the roll leads in and unwinds after. Altitude breathes ±20 m around the 260 m mean and lifts up to 26 m into turns. Pitch is LOCKED at −32°; heading comes from a gaze that leads the path by 6.5 s of travel. `prefers-reduced-motion` zeroes the roll and the altitude shaping and keeps the easing.
- **Invariants — do not "fix" these.** (1) **Every** release levels the roll: completion unwinds it through the ease-out envelope, and `interruptCameraMotion` zeroes it synchronously (heading/pitch/position preserved). Cesium keeps the last up vector it was handed, so skipping this leaves the user holding a tilted horizon. (2) Heading is interpolated as an ANGLE about the local up, never as a Cartesian lerp — a lerp cannot cross an antipodal pair, so an out-and-back route looked backwards for the entire return leg; `signedTurnRad` branches the exact-180° case deterministically because the cross product's sign there is a floating-point coin toss. (3) A COLD floor cell is missing data, not flat ground: route vertices carry height 0, so trusting them flew a mountain corridor at 260 m above the ELLIPSOID. The corridor warm is fire-and-forget, so the dolly ARMS — camera untouched, no teleport onto the route — for up to 1.2 s waiting for real floor data, falls back to a rendered-mesh probe (`scene.sampleHeight`) when the DEM stays cold — latched to ONE firing per flight, for exactly the cells the cache could not answer, so a route costs at most 8 `sampleHeight` calls however long it arms — and otherwise holds the launch altitude for the whole route. A corridor is only RESOLVED when every cell is accounted for: one warm cell says nothing about the ground under the other seven, and treating it as an answer let the dolly descend to 460 m over a 1,600 m rendered surface. The probe reads the rendered surface at the current LOD (rooftops and primitives included) — a better estimate than nothing, not a guaranteed upper bound. Never descend blind. The floor is SMOOTHED in both directions for the ride while the hard clearance clamp reads the RAW sample, so a cell boundary cannot pop the eye but a cliff is still cleared on the frame it is seen. A pre-departure floor is adopted whole; one arriving mid-flight is eased onto, and the eye's descent rate is capped.
- Pins: `src/routeCinematics.test.mjs` (26 tests), with `scripts/qa-flyroute-mutations.mjs` reverting each fix individually to prove they are load-bearing (19 named defects); rendered proof `scripts/qa-flyroute-cinema.mjs` (drives the real voice runner, measures the real Cesium camera every frame, writes a labelled contact sheet).
@@ -6079,12 +6238,21 @@ apply to `public/` assets or built-preview serving.
- Panoptic mode shows labels on ALL items (no stride skipping). Tracked/selected items keep bounding box but suppress label (skipLabel flag). Tracked items get enlarged bounding boxes (56×44 vs default 22×14).
- The 3D aircraft toggle reveals `Proximity` and `All` modes and drives both commercial and military aircraft layers. It ships ON in `Proximity`, so the button paints lit and the mode row paints open from markup; the panel is therefore ~36 px taller than before, which Cockpit's Display/Radio strip absorbs through its existing primary-only corridor solver.
- The host-painted tracked-target readout sits above the post-FX layer and follows tracked aircraft/satellites or selected mapped installations using each layer's display-position contract.
- Cockpit entry is gated to the operational Contacts context bundle. The
Context chooser must be in Contacts mode, Live Flights and Military Flights must both
be enabled, and a civilian or military aircraft must be tracked. The visible
Cockpit actions and the `C` shortcut use the same gate, so ordinary standalone
flight-layer selection cannot enter a context-dependent cockpit.
- **Aircraft cockpit view:** selecting a commercial or military aircraft reveals a `COCKPIT` action (`C`). Cockpit mode temporarily releases Cesium's orbit-follow transform and drives a first-person camera from the tracked aircraft's existing smoothed display position and course. Its concave helmet-visor HUD shows callsign, UTC time, coordinates, curved roll/pitch guides, and a seven-division heading tape. Ambient commercial and military AIR contacts use a Cockpit near/far band selected by the shared Display 3D mode: Proximity admits at 150 km and retains to 185 km; All admits at 400 km and retains to 450 km. The shared 3D toggle now applies in Cockpit: Off keeps in-range contacts as rotating 2D aircraft silhouettes, while On lets a ready admitted glTF take over without a drawing gap. The Cockpit model cap remains 60 and can only lower the map budget; in-range contacts that are capped or still loading remain 2D silhouettes instead of degrading to out-of-range dots. Contacts outside the selected band use small rotation-free cyan-white pips for civilian aircraft and amber pips for military. The pilot's own airframe is not drawn in first person, and exiting clears the Cockpit band and restores normal map silhouettes/models. The normal left accordion remains available for Layers and Authoring and keeps the same 26vh HUD-aligned anchor used in map mode, independent of whether the bottom-left Contact card is expanded or collapsed; the right-rail CCTV and Context chooser are hidden to avoid duplicating or overlapping the cockpit presentation. When the intelligence HUD is enabled, its classification, scene summary, collection/orbit metadata, coordinates, and imaging-status text remain visible as reduced peripheral cockpit telemetry; the optical center and flight instruments stay clear, and turning the HUD off still hides it. Ground speed, exact heading, and rendered altitude form one compact lower-center instrument cluster, keeping the horizon and peripheral view open. A second live altitude tape hugs the inside-right visor rim: its rail and nine moving ticks are derived from the same responsive keyhole radius, remain 20 px inside the circular edge even on wide displays, fade deeply at both vertical ends, and move continuously behind a fixed current-altitude pointer; its interval tightens automatically near the surface and it is suppressed on narrow screens. Cockpit mode also has a weather-backed transparent volumetric-cloud pass derived from the supplied FBM/domain-warped R&D shader. It renders at no more than 520×320, uses 24 ray steps/three FBM octaves at 12 FPS, is clipped to the visor, fails clear when Open-Meteo is unavailable, and stops its animation completely on cockpit exit. It does not restore the prior CPU weather canvases, precipitation, scene fog, or any map-mode effect. When Contacts is active for the tracked aircraft, the compact `CONTACT` rail adds the 250 km subject window, four cohort counts, nearest observed/mapped example with relative bearing and distance, freshness, explicit uncertainty, and Previous and Next controls plus its own collapse control. The mirrored right rail is a three-page **cockpit briefing carousel**: source-backed live signals, location-matched regional headlines, and physical-region/current-weather context from bundled Natural Earth polygons and Open-Meteo. It is manual-first: Previous, Next, and direct page controls are always available, and the visible `CYCLE OFF` / `CYCLE ON` control starts or stops the nine-second page cycle. The cycle pauses on hover or keyboard focus and stops while collapsed, hidden, or outside cockpit mode; live signal data continues refreshing either way. Empty news matches and unavailable news use compact text states rather than reserving an empty media frame; partial local data remains explicit. Article links open their original publisher, and no headline is treated as verified risk intelligence. On desktop both cockpit rails use the same width and share a bottom-aligned safe baseline in opposite corners above the peripheral MGRS/GSD/time telemetry, leaving both that text and the lower-center instrument cluster readable. They collapse independently to slim tabs without stopping live data updates. Narrow screens use separated top/bottom fallbacks. Empty-space globe clicks are inert while cockpit owns the camera; `C`, `Escape`, or `EXIT COCKPIT` explicitly exits and restores the same tracked entity and standard follow camera. Unknown feeds remain unknown, selection loss still exits safely without inventing a replacement track, and the cockpit never presents the summary as threat scoring or an all-clear. This is a desktop first-person presentation, not a WebXR session.
- Cockpit entry needs only the layer of the aircraft it rides and that aircraft
tracked: Live Flights for a civilian, Military Flights for a military aircraft
(the tracked info's `layerId`; `cockpitEntryAllowed` in
`src/contextModePolicy.js`). Contacts is not required, and the other aircraft
layer, Live AIS Vessels and every other layer may be turned on or off before
or during a session. A Context mode change still in flight blocks entry until
it settles. Turning off the layer the view rides ends it. Riding an aircraft
while Contacts is on adopts its layer as the user's own, as a direct selection
does (`adoptLayerVisibility`), so Contacts ending, by its own switch or one of
its layers going off, restores the pre-Contacts layers but keeps the ridden
one and the view; Live Signals then drops the rows Contacts built and shows
`CONTEXT STANDBY`. The visible Cockpit actions, the `C` shortcut and voice use
the same gate; voice entry still opens Contacts first, as its layer-targeted
entry navigates through Contacts.
- **Aircraft cockpit view:** selecting a commercial or military aircraft reveals a `COCKPIT` action (`C`) once that aircraft's own layer is on; Contacts is not required. The action shows in Contacts (`#cockpit-entry`) and under the Intel HUD's bottom-right readout (`#cockpit-entry-readout`, with the HUD on, in its Tactical and Cyber layouts), so entry needs no Contacts panel. Turning off the ridden aircraft's layer ends the view (the layer clears its tracking). When an air feed the surrounding picture uses is off, the Cockpit says so instead of blocking: Live Signals leads with `MILITARY FEED OFF · PARTIAL PICTURE` or `FLIGHTS FEED OFF · PARTIAL PICTURE`, and the Contact card shows that line in place of its inputs status, so a partial picture is never presented as complete. `control_cockpit` refuses entry with `entryBlockedReason` `contacts-starting` (a Context change in flight), `subject-layer-off` (the aircraft's layer, or with none tracked any aircraft layer, is off) or `no-tracked-aircraft`. Cockpit mode temporarily releases Cesium's orbit-follow transform and drives a first-person camera from the tracked aircraft's existing smoothed display position and course. Its concave helmet-visor HUD shows callsign, UTC time, coordinates, curved roll/pitch guides, and a seven-division heading tape. Ambient commercial and military AIR contacts use a Cockpit near/far band selected by the shared Display 3D mode: Proximity admits at 150 km and retains to 185 km; All admits at 400 km and retains to 450 km. The shared 3D toggle now applies in Cockpit: Off keeps in-range contacts as rotating 2D aircraft silhouettes, while On lets a ready admitted glTF take over without a drawing gap. The Cockpit model cap remains 60 and can only lower the map budget; in-range contacts that are capped or still loading remain 2D silhouettes instead of degrading to out-of-range dots. Contacts outside the selected band use small rotation-free cyan-white pips for civilian aircraft and amber pips for military. The pilot's own airframe is not drawn in first person, and exiting clears the Cockpit band and restores normal map silhouettes/models. The normal left accordion remains available for Layers and Authoring and keeps the same 26vh HUD-aligned anchor used in map mode, independent of whether the bottom-left Contact card is expanded or collapsed; the right-rail CCTV and Context chooser are hidden to avoid duplicating or overlapping the cockpit presentation. When the intelligence HUD is enabled, its classification, scene summary, collection/orbit metadata, coordinates, and imaging-status text remain visible as reduced peripheral cockpit telemetry; the optical center and flight instruments stay clear, and turning the HUD off still hides it. Ground speed, exact heading, and rendered altitude form one compact lower-center instrument cluster, keeping the horizon and peripheral view open. A second live altitude tape hugs the inside-right visor rim: its rail and nine moving ticks are derived from the same responsive keyhole radius, remain 20 px inside the circular edge even on wide displays, fade deeply at both vertical ends, and move continuously behind a fixed current-altitude pointer; its interval tightens automatically near the surface and it is suppressed on narrow screens. Cockpit mode also has a weather-backed transparent volumetric-cloud pass derived from the supplied FBM/domain-warped R&D shader. It renders at no more than 520×320, uses 24 ray steps/three FBM octaves at 12 FPS, is clipped to the visor, fails clear when Open-Meteo is unavailable, and stops its animation completely on cockpit exit. It does not restore the prior CPU weather canvases, precipitation, scene fog, or any map-mode effect. When Contacts is active for the tracked aircraft, the compact `CONTACT` rail adds the 250 km subject window, four cohort counts, nearest observed/mapped example with relative bearing and distance, freshness, explicit uncertainty, and Previous and Next controls plus its own collapse control. The mirrored right rail is a three-page **cockpit briefing carousel**: source-backed live signals, location-matched regional headlines, and physical-region/current-weather context from bundled Natural Earth polygons and Open-Meteo. It is manual-first: Previous, Next, and direct page controls are always available, and the visible `CYCLE OFF` / `CYCLE ON` control starts or stops the nine-second page cycle. The cycle pauses on hover or keyboard focus and stops while collapsed, hidden, or outside cockpit mode; live signal data continues refreshing either way. Empty news matches and unavailable news use compact text states rather than reserving an empty media frame; partial local data remains explicit. Article links open their original publisher, and no headline is treated as verified risk intelligence. On desktop both cockpit rails use the same width and share a bottom-aligned safe baseline in opposite corners above the peripheral MGRS/GSD/time telemetry, leavinLine truncated
- **Cockpit left-panel clearance:** the Cockpit Contact card and peripheral HUD participate in the adaptive left accordion's live obstacle measurements, including live viewport-height changes. Expanding Layers or Authoring keeps the active panel in the available upper-left corridor with internal scrolling; it does not cover the Contact card, lower Cockpit controls, or Cesium credit line. Outside Cockpit the hidden card does not alter the normal corridor.
- **Cockpit Context scope:** the 250 km radius applies to the air/sea proximity cohorts. Installation counts come only from the currently loaded viewport and are labeled `CURRENT VIEWPORT ONLY` in the cockpit as well as the normal Context panel; neither surface presents them as a complete 250 km installation survey.
- **Cockpit camera anchor:** first-person mode does not write feed-boundary corrections directly into the camera. A cockpit-only inertial anchor advances from the selected aircraft's displayed course and speed, then converges toward the authoritative delayed track with correction capped below forward motion. The displayed kinematics are derived from the same consecutive fix segment as the rendered position, with raw feed speed/course used only as fallback; a transient zero/missing feed speed therefore cannot freeze a visibly moving aircraft after layer enable or a map/cockpit handoff. Rendered altitude continues to come from that interpolated track position. Late ADS-B fixes and short render stalls can remove drift without accelerating or reversing the view. Camera placement runs before scene update/culling at a bounded 20 Hz so a moving cockpit does not force Photoreal 3D Tiles to retraverse on every display frame; textual instruments update at 10 Hz and context/layout work at 4 Hz. Every far Cockpit contact pip shares one stable Cesium texture-atlas entry and skips unused screen-projected course calculations, while only in-range 2D aircraft silhouettes pay the screen-projected rotation cost; ambient glTF collections are hidden/retained rather than synchronously destroyed at cockpit entry, and context rails lay out only on explicit content/state changes and viewport resize. The deliberate 15/30-second layer interpolation delays and per-Cesium-frame position caches remain unchanged.
+18
View File
@@ -10,6 +10,24 @@ For the roadmap and open backlog, see the repository issue tracker.
## Open
### Wildlife grid relies on the iNaturalist UTFGrid cell schema
Status: Open (guarded fallback)
The `/v1/grid/{z}/{x}/{y}.grid.json` endpoint is documented in the v1 and v2
API docs (“Grid Tiles UTFGrid”), but the documented response only defines the
UTFGrid `grid`, `keys` and `data` members; the per-cell records are not
specified. Its 64×64 UTFGrid shape, keys and cell records
are validated in full at the allow-list proxy. A schema change or unavailable
tile retains its last good cells for at most one hour; a cold unavailable grid
uses sampled observations with the same badges and screen buckets, without
claiming complete worldwide coverage. Tests use
synthetic fixtures with the locally inspected tile's exact structure. Retest
and update the schema guard before accepting an upstream shape change. Grid
and observation calls share the paced queue, provider budget and 429 cooldown;
ordinary tile failures do not block observation queries.
### Street traffic can be slow/uneven when panning across dense city blocks
Status: Open (partially mitigated)