Commit Graph
1159 Commits
Author SHA1 Message Date
Sameh Khamis 5f96009b67 feat(i18n): an ./i18n entry for an application with its own language choice
startPageLocale, followPageLocale and switchLocale start, follow and switch
the page's language as the standalone application and the globe picker do,
for an application that offers the language elsewhere and passes
localePicker: false. The standalone application uses them.
2026-11-12 22:07:13 -08:00
Sameh Khamis 347e77d1f6 feat(vessels): a selected vessel shows its Open Vessel Photos photo and MMSI
Selecting a vessel asks Open Vessel Photos (a free, keyless API) for its
photo by MMSI, once per MMSI. The CONTACTS view shows it under the vessel's
name and MMSI with the photographer, the photo's open licence and a link to
the vessel's page; only images on Open Vessel Photos' own host are shown, and
an answer about another MMSI is no photo. The vessel source exposes the lookup
as getEnrichment({kind: 'photo'}) and fetchOpenVesselPhoto for applications
with their own vessel source.
2026-11-12 20:10:30 -08:00
Sameh Khamis 1a657e2a64 Keep the library layer namespace in the layer lifecycle package
The core layer lifecycle imported the whole Data Library manifest
contract only to check the lib- prefix, so the layer-lifecycle package
owned a feature module. The namespace rule (prefix, entry id pattern,
the two id helpers) now lives in src/data/libraryLayerIds.js, which the
lifecycle owns; the manifest contract imports and re-exports it, so
library callers are unchanged.
2026-11-12 16:04:55 -08:00
Said Turksever 1a2589d4ef Port the Data Library Browse dialog to Vue and Pinia
The dialog is now DataLibraryDialog.vue (with DataLibraryCard.vue for each
entry), placed with the Browse button by DataLibraryBrowse.vue in
LayerPanelsView and teleported to the body. It renders what the
gev-data-library store holds and sends clicks and typing to the owner.

src/ui/libraryBrowser.js is now the framework-free owner: it presents the
open state, filters, the rows they show, the stat summary and Remove all
through `publish`, and its controls answer the view. Its text helpers are
what the view renders with t(), so every label follows the locale without
rebuilding anything. Only showModal/close and focus moves stay imperative;
focus returns to Browse also when the browser closes the dialog on Escape
itself.

Component tests mount the view over the real owner (open, filters, Add to
Remove keeping focus, Zoom, Remove all, a language switch, clean view).
2026-11-12 23:01:00 +00:00
Said Turksever 24ccdd4dc3 Fix Data Library share restore, dense tile layers and EPC partition reads
- Share links: the compact /share/<code> format carries the switched-on
  library entries (field 29), so Copy Link keeps them; a share opened in the
  same tab restores them; any share decides what is on, so one with every
  library entry off no longer switches on the recipient's stored entries.
- A tile entry too dense at its minimum zoom (Norway cadastre z14, depth z11)
  is hidden below that scale via minimumTerrainLevel instead of allocating
  millions of tiles when zoomed out.
- EPC partitions keep the full extent for exclusion and the 98 % extent for
  ranking only; the old trimmed-only extents are retired.
- Partition index footers that fail transiently are retried; an incomplete
  index is never cached as complete, a complete prior index is kept, and it
  is rebuilt after five minutes.
- Views that leave touching partitions unread are partial; listing and
  footer bodies are capped and ranges validated.
- The Browse dialog relabels after a language switch.
2026-11-12 22:37:34 +00:00
Said Turksever 871b7f0fcc Remove Data Library entries whose licence or provider links are broken
A link check of every manifest found 404 licence or provider pages for
OpenSeaMap seamarks, EMODnet bathymetry and the PDOK, basemap.at, Geneva
SITG, ICGC Catalunya, Luxembourg and Vlaanderen aerial photos, and no
answer from the SEDAC GPW, Minnesota and Bangor hosts. Drop those 11
entries, renumber the unpublished share-token ledger, point the tests at
remaining entries and regenerate DATA_SOURCES.md.
2026-11-12 17:05:21 +00:00
Said TurkseverandMaher Kurdi 11706a68cb Add Amsterdam traffic and ANPR camera positions to the Data Library
The City of Amsterdam's verkeersinformatiesystemen register lists where
its ~500 traffic and ANPR cameras stand, with type and asset number, but
publishes no pictures. That fits a Data Library dataset better than a
CCTV pack: the register answers GeoJSON in WGS84 with CORS headers, so
the entry fetches it browser-direct with no proxy code.

The register names no licence ("public unless stated otherwise"), so the
entry stays operator opt-in, as #251 had it. The dataset and the
position-only camera idea come from @Maher-Reven's #251, which this
replaces; the entry lists them as maintainer and in its attribution.

Co-authored-by: Maher Kurdi <[email protected]>
2026-11-12 12:26:45 +00:00
Said Turksever db80d82b8d Add the Data Library: live regional datasets, tiles and packs
DATA LAYERS keeps its curated core layers; regional and niche sources
live in the Data Library, opened from Browse data library at the foot
of the panel. Each entry is a JSON manifest in src/library/entries/
pointing at a live upstream (query API, provider-hosted file or tile
endpoint), so nothing is bundled and a self-hosted instance hosts no
data. Sources without CORS go through one allowlisted /api/library
route. Kinds: dataset (GeoJSON, CSV, JSON records, ArcGIS, partitioned
GeoParquet), tiles (XYZ, WMS/ArcGIS bbox export, terrarium relief) and
pack. Selections persist and travel in share links as lib=, separate
from the core layer tokens. Library rows get Zoom and Remove icon
chips that work while the layer is off, rendered through RailChips.
Strings are translated in every locale.
2026-11-12 12:26:45 +00:00
Denis Groshev 13818d0b9f Merge upstream main into denis/rail-layer 2026-11-11 18:02:52 +01:00
Denis Groshev 830c080803 feat(rail): add a Rail layer with keyless live trains
Live trains from 30 keyless operator feeds: GPS positions from 29 and
Irish Rail's station reports, shown as the operators publish them. 28
work out of the box; Renfe long and medium distance and KTMB appear once
their pack is built, because their vehicles name no route.

Packs are opt-in (scripts/build-rail-paths.mjs): the operator's own
route lines, stop names, next stops and trip-update delays for live
trains. Nothing downloads when the app starts, no train is placed from a
timetable, and no request carries a deployer's key.
2026-11-11 16:06:22 +01:00
Said Turksever 43893697aa Merge upstream main into pr/street-level-show-action 2026-11-11 12:15:18 +00:00
German Khokhlov 45d9ff1d86 Merge branch 'main' into sv-locale
Translate the 148 strings main added since (mostly the News layer) and
prune the 5 it no longer uses; keep both Hebrew and Swedish in the
language lists.
2026-11-11 13:59:40 +04:00
Said Turksever f878efe22d Merge commit '0ceee8df40b97c6c693e86a4443794cd4ed1ff35' into HEAD 2026-11-11 08:54:28 +00:00
Sameh Khamis 57ff81f723 feat(live): flights, military and vessels follow the world clock when their source declares a timeline
A live source may now declare a timeline: it can answer a snapshot at a
given time (source.timeline = { coverage(), stepMs, subscribe? }, read by
readSnapshotTimeline). While such a layer is on it follows the world clock
and registers the coverage. At the live edge it polls exactly as before;
away from it the layer asks for getSnapshot({ ...query, atMs }) at the step
at or before the clock time and shows that snapshot instead of polling.

src/time/snapshotTimeline.js owns the following: a seek, time going
backwards or leaving live replaces every contact with the snapshot at the
new time (play waits on it, for at most 8 s); playing on keeps one
snapshot ahead and gates play on it, applying those snapshots as polls so
contacts move smoothly; outside coverage the layer is empty; back at live
it is cleared and polls at once. One load at a time, and a load the
source drops for a newer one is not a failure.

While following, aircraft are drawn at the clock time with no render
delay, records and absences are judged at the clock, track requests carry
atMs, and vessels move between fixes along the great circle. The world
clock counts jumps (seeks and returns to live) in getState().

A source with no timeline never follows the clock, and every layer stays
live, unchanged.
2026-11-10 18:12:53 -08:00
Sameh Khamis 49edc31879 Merge branch 'main' into feat/viewport-context-decision-upstream 2026-11-10 15:54:49 -08:00
Sameh Khamis 100369f9af Merge remote-tracking branch 'origin/main' into feat/viewport-context-decision-upstream 2026-11-10 15:44:17 -08:00
Sameh Khamis 630e1b1998 Hide street-vehicle brackets and labels behind buildings and terrain
The detection overlay only hid a contact behind the Earth's curve, so a
street vehicle behind or under a building, a 3D tile or a hill still had
its bracket and "VEH-nnnn" callout painted over that geometry, while its
own dot was depth-tested and correctly hidden.

A contact a layer marks `depthTested: true` (only Street Traffic does) is
now tested against the depth the scene drew and left out of the overlay
while a drawn surface stands in front of it by more than max(3 m, 2% of
its range). Other layers are untouched: their markers are drawn over the
scene on purpose.

The test runs on the GPU and never waits for it. Each tested contact is
one point drawn inside an occlusion query; the vertex shader looks up the
frame's packed pick depth at the contact's pixel, decodes it and drops the
point when the surface is nearer than the contact's threshold. Query
results are collected on later frames. Reading depth back was measured
and rejected: `scene.pickPosition` per contact costs 0.2 to 1.4 ms each,
and Chromium answers `getBufferSubData` with a round trip to its GPU
process even behind a signalled sync object (1.5 ms on a quiet machine,
15 to 25 ms on a busy one).

- Contacts are retested every 100 to 200 ms, spread across frames, at
  most 256 a frame; with more in view the pace stretches so none starves.
- Hiding is immediate. A hidden contact returns 600 ms after its last
  "hidden" answer, so one on a silhouette edge does not blink, and a
  contact new to the view is held back until its first answer (400 ms at
  most), so orbiting does not flash brackets behind buildings.
- A callout whose contact goes behind something is dropped at once, not
  carried to the next solve and not faded out on the facade.
- These times run on painted frames, so a hidden tab does not expire them.
- A control point in every batch must draw; if it does not, the batch is
  ignored and after three the probe stands down for ten seconds. With no
  pick depth (2D, Columbus view, depth picking off) nothing is hidden.
  A WebGL 1 context reads depth back synchronously about 8 times a second.

The renderer internals this depends on are read in one function,
`readSceneDepthSource`, with the engine version they were checked against.
2026-11-10 15:08:26 -08:00
Sameh Khamis 8ff22f1a12 Merge main into feat/shipwrecks
Main added the News layer at the same places: both layers are kept in the
layer names, panel order (News stays last of Events), standalone sources,
voice manifest, credits, DATA_SOURCES and the CURRENT-STATE table. News took
share-link token 00 first, so Shipwrecks moves to 01 (ledger, registry,
CURRENT-STATE and the token tests). The catalog now has 40 layers and the
share-link registry 38. Shipwreck strings are added to the new Hebrew
catalog; the other catalogs keep both sides' strings.
2026-11-10 14:03:39 -08:00
Sameh Khamis 41641cd91a fix(shipwrecks): the page clears shown wrecks as a whole, once even the newest source is too old
Removing one source's wrecks on the page split merged records: a wreck both
sources list is one Wikidata record carrying EMODnet facts, so expiring
Wikidata dropped wrecks EMODnet still had, and expiring EMODnet kept its
facts. The page now keeps every shown wreck, marked stale, until the newest
source passes the limit, then clears them all. Adds a source test for the
reported ages and limit.
2026-11-10 13:40:40 -08:00
Mazeyar Moeini Feizabadi 8f489d203f Merge branch 'main' into denis/gev-news-layer 2026-11-10 22:38:56 +01:00
Sameh Khamis 32e5768954 fix(shipwrecks): take the shipwreck modules out of the GhostMaps package lists
They were listed as owned by the ghostmaps and ghostmaps-source packages,
which they are not part of; ghostmaps-source is meant to stay free of
rendering code, and shipwrecks/index.js imports Cesium. The app groups that
own every layer keep them.
2026-11-10 13:26:23 -08:00
Sameh Khamis 38ba8a3b59 perf(shipwrecks): draw the wreck markers from one billboard collection, on the wreck, and rebuild them only on new data
- The ~8,000 markers were one Cesium entity each; they are now one
  BillboardCollection (as FIRMS, vessels and wildlife sightings do), ordered
  at the bottom of the shared sprite stack so moving traffic draws over it.
- eyeOffset.z was the death toll in metres, so a 1,500-death wreck's marker
  sat 1.5 km toward the camera and clipped or drifted when close. It is gone;
  markers are added by rising toll, so a bigger toll still draws on top.
- The page asks every 6 h but the proxy changes data at most daily: the proxy
  now sends a version that changes with either source's data, and the layer
  rebuilds only when it does.
- Adds a controller test: enable/disable, teardown, draw order, unchanged
  snapshots, a late response after turning off, and the period closing an
  open card.
2026-11-10 13:25:03 -08:00
Mazeyar Moeini FeizabadiandClaude Fable 5.2 bb7e7ecad6 fix(news): Hebrew strings for the News layer and the gate modules in the news boundary
main added the he locale and the news provider now imports the same-site gate.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
2026-11-10 21:33:01 +01:00
Sameh Khamis 05daaf267a Merge branch 'main' into feat/transit-icon-heading 2026-11-10 12:24:46 -08:00
Mazeyar Moeini FeizabadiandClaude Fable 5.2 3342d7a07a Merge origin/main into review/pr-1593
Conflicts: docs/CURRENT-STATE.md layer table, src/ui/layerPanel.js imports,
src/data/dataCredits.test.mjs imports; both sides kept.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
2026-11-10 21:20:15 +01:00
Sameh Khamis 8fec5a76bf feat(flights): a tracked flight's trail splits at gaps, follows the drawn aircraft, and a lost aircraft coasts then reads SIGNAL LOST
The trail no longer joins a day of history into one line. It lifts the pen
across a gap of more than 48 minutes, an implied speed over 1,500 kt, or a
report without altitude (a parked aircraft within 0.1 nm for under 3 h stays
joined), and every history point is kept as its own segment. History never
gets an invented altitude. The live end is the path the aircraft was drawn
along, thinned to its shape, so it no longer cuts corners through turns; a
late report trims what was drawn after it. Old segments are frozen in chunks,
so a 2,000-point trail costs about 0.1 ms a frame.

A tracked aircraft that misses an update shows STALE and is dimmed. It keeps
flying on its last speed and heading for five minutes (at most 1,500 kt),
then stops, dimmed, labelled SIGNAL LOST with the time since its last report,
and is kept for ten minutes after that report so the camera keeps its target.
Aircraft on or near known ground hold at their last report instead of
coasting. Military flights follow the same rules.
2026-11-10 11:30:59 -08:00
German Khokhlov 2919e90c63 Merge branch 'main' into feat/hebrew-i18n 2026-11-10 21:24:21 +04:00
Said Turksever 9c73939425 Street Level: leave an interrupted link and an overtaken view alone (gekh review)
- A pasted or start-up link opens its photos only when its restore's camera
  landed: a drag or search during the restore flight keeps the camera.
- A posted view whose photo step was cancelled or superseded no longer closes
  the viewer or flies; one that found a photo it could not load keeps the error.
- openNearest gains `replace` (keep the open photo while it looks, close it
  only when nothing opens, so EXPAND stays) and `proceed()` (asked before a
  found photo loads); the action uses both, so a navigation, a gesture or a
  hiding mode during the lookup loads no billed panorama.
- Mapillary's timeout message is a msg() string with all eleven translations.
2026-11-10 15:12:50 +00:00
Said Turksever 5370b8a268 Merge upstream main into pr/street-level-show-action
gevActions.test.mjs: keep the PR's street-level tests and main's analyst display tests.
2026-11-10 14:59:39 +00:00
German Khokhlov 353eebf224 refactor(tools): normalize ALPR text once, in the records
Every camera find_alpr_cameras returns comes from the tile source, which builds
it through normalizeAlprNode, so the tags are already one line and capped.
Drop the second pass in the tool, its duplicate test and the boundary entry,
and the function check normalizeDisplayText never sees.
2026-11-10 15:37:16 +04:00
German Khokhlov 39246adaf7 Merge remote-tracking branch 'origin/main' into codex/fix-tool-display-text
Conflicts:
- docs/CURRENT-STATE.md: keep main's hidden-tab polling paragraph and the
  display-text normalization paragraph.
- scripts/package-boundaries.json: keep main's voice panel and text chat
  modules and add src/data/displayText.js.
- src/voice/gevActions.test.mjs: keep main's set_panel_open and partial
  analyst card tests and the display-text tests.
2026-11-10 13:58:41 +04:00
manjunath22466 26b268a06c chore: merge main into viewport diagnostics pr (#835) 2026-11-10 12:36:12 +05:30
manjunath22466 1cd21d784a Merge main into viewport diagnostics PR (#835) 2026-11-10 10:45:23 +05:30
Sameh Khamis 9fe27c2d75 Merge branch 'main' into sameh/flight-photo 2026-11-09 21:13:16 -08:00
Sameh Khamis 1a8defbfc8 feat(flights): show the tracked aircraft's Planespotters photo in Contacts
Tracking a civil flight asks Planespotters for that aircraft's photo, once
per aircraft, straight from the browser. The CONTEXT panel's CONTACTS view
shows it under the flight's name with the photographer's credit, as a plain
link to the photo's page. Only images on plnspttrs.net and pages on
www.planespotters.net are ever shown or linked. Military aircraft and a
converted TR-3B are never looked up.

Flight lookups (type, route, photo) whose source failed temporarily are
asked again after a 60-second cool-down; an answer, found or not, and a
permanent refusal stay final for the session. A route belongs to the
callsign it was looked up for, so a callsign change drops the old route.

Browser QA scripts that track a flight answer the photo lookup themselves,
so they never call the third party.
2026-11-09 20:36:03 -08:00
Sameh Khamis 4dc51927bc perf(traffic): build road lines one set at a time
With Lines on, every progressive paint of a loading city rebuilt all road
lines: up to three ground-line primitives for as many as 1,500 roads, built
asynchronously. Builds started faster than they finished, and each kept the
set it replaced in the scene until it was ready, so sets piled up.

One set now builds at a time. A rebuild asked for while lines are building
replaces the one waiting, and the newest starts when the build finishes.
Lines replaced by a finished build are removed before the next one starts.
Turning Lines off removes the lines at once and drops the waiting rebuild.
2026-11-09 20:31:28 -08:00
Meir Michanie f79ceb690f i18n(he): apply review (#1594)
Before merging: singular 'one' forms from the review table (44 entries),
counters keep their order across the slash (מתוך, as Arabic المن), the
lightning layer says ברקים (the legend now counts ברק strikes, the help
text keeps 110°E Latin), configured-elsewhere keys are promised
untouched, Punk is פאנק רוק, dock/panel side labels follow the mirrored
layout, and the HUD-summary English guard covers he (NON_LATIN_LOCALES,
tested) — documented in Adding a language along with format-scope.

Suggestions and nits: restricted is מוגבל, stage recovery is החבה not
שחזור, voice-inactivity names the behavior, lane directions use
attributes (השמאלי) and around is לאחור, Ironbow and KM/S stay Latin in
same-as-english, {age} no longer doubles לפני, typos and garbled words
fixed (תנאים, ידנית, ודאות, מקלט, זמניים, ערוץ 14, מעונן חלקית, אין אות
קריאה, Tab לתצוגה מקדימה, השאר ריק, היוועץ), counted units shortened
(שנ' דק' שע' ימ'), terminology unified (briefing תדריך, forecast תחזית,
course מהלך, אונייה, שריפה), rtl.css shares the mono redirect under
:is(:lang(ar), :lang(he)) and its comments match the rules,
worldOverlayTokens adds Noto Sans Hebrew to the canvas card fonts, he.md
gets the fraction-aware plural table, iw/iw-IL map to he (tested).
2026-11-09 14:41:36 -06:00
Meir Michanie 584732c3ec i18n(he): complete and ship Hebrew
- Add the full catalog (2940 entries keyed by the English text; 75 plural
  entries carry one/two/other per Intl.PluralRules('he')), reviewed for
  meaning, terms, separators and label length.
- Register he: he-IL and he map to it; it joins RTL_LOCALES so <html
  dir=rtl> mirrors the chrome through logical properties and rtl.css.
- rtl.css: Noto Sans Hebrew (loaded with the other Google fonts) leads the
  mono and sans stacks, tracking released like Arabic, HUDs keep JetBrains
  Mono for Latin telemetry.
- same-as-english: instrument codes and HUD readouts stay as printed
  (HDG/BRG/CRS/FOV/CRT/NVG/FLIR/CAL/SRC, MW units, Sentinel-2, km/h...).
- docs/translations/he.md: style, plurals and glossary.
- docs: TRANSLATORS.md and CURRENT-STATE.md updated; normalizeLocale test
  covers he-IL.
2026-11-09 14:41:36 -06:00
Kaj ObstandClaude Opus 5.5 cdccf88bbb i18n(sv): address review (export buttons, singular, consistent terms)
- EXP. {slot} and EXPORT so the Recent Imagery export buttons fit and A
  and B read apart; EXPORT listed as same-as-English.
- {count} HÖRT / hört for one aircraft; the plural rule in sv.md no
  longer lists HÖRDA.
- INTE FARA ÖVER throughout; VÄ/HÖ for left/right (V is west on the
  compass); STYRD KURS vs KURS Ö. GRUND; MIK in the first-run tip.
- Label-first counts for sightings and standby cameras; neuter audio
  states; OSÄKER on chip and status line; KN for knots; the ellipsis
  character; Ladda ned and VÄNTLÄGE throughout; Olagligt ingripande;
  FÖRHANDSVY for the preview flag and authoring heading.
- sv.json in the format scope; Swedish in language-code order in
  TRANSLATORS.md; glossary row reduced to incident → händelse; 29
  letterless entries dropped from same-as-english.json.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 19:25:24 +01:00
German Khokhlov 015e3ff7fc Merge branch 'main' into fix/node26-allocation-gates 2026-11-09 20:19:29 +04:00
German Khokhlov e22348a8e1 Merge branch 'main' into refactor/tile-budget 2026-11-09 19:43:58 +04:00
Said Turksever cbb0a391f2 Merge branch 'main' into feat/transit-icon-heading 2026-11-09 15:06:36 +00:00
German Khokhlov 8b0b975e18 Merge branch 'main' into sv-locale 2026-11-09 18:45:01 +04:00
Denis Groshev 0fdde159cb Merge branch 'main' into denis/gev-news-layer
# Conflicts:
#	DATA_SOURCES.md
#	docs/CURRENT-STATE.md
#	src/data/local_data/natural_earth/README.md
#	src/i18n/same-as-english.json
#	style.css
2026-11-09 15:35:56 +01:00
German Khokhlov 9d7fe8fcaa Merge branch 'main' into feat/opensky-compress-cached-body 2026-11-09 18:30:32 +04:00
Kaj Obst 6a6cee4c21 Merge remote-tracking branch 'origin/main' into sv-locale 2026-11-09 12:53:37 +01:00
Sameh Khamis e954734379 fix(cockpit): a place pack that will not load ends the loading batch failed; flags keep their neighbours out from under the border and out of a scaled flag's edge; a new ride asks again for a sheet that failed 2026-11-08 19:45:01 -08:00
Sameh Khamis 5a6cf22be1 feat(cockpit): the owner flags load at startup from one generated 15 KB sheet; the loading status names the place packs while they load on first lookup 2026-11-08 19:35:30 -08:00
Denis Groshev dd02068ce9 feat(cockpit): list orbit's sites and satellites in Live Signals
The orbital Context card is gone and the Contact card stays hidden in orbit
again. Live Signals page 1 had nothing in orbit but TRACK ACQUIRED, because
orbit feeds no Contacts snapshot; it now lists what is around the
satellite, built the way the aircraft rows are, five rows with stable keys
rebuilt once a second. Rows already shown keep their places while they
still qualify, so a site passing under a low orbit is not pulled from under
the pointer.

Site rows come from the Mapped Installations layer (getNearby, asked only
as far as the reach extends) and focus their site, or release it on the
focused site's row, which reads as the subject does. A site the layer drops
during its frequent reloads stays listed for 10 s, since sites do not move.
With the layer off nothing is listed and a SITES STANDBY row says why.
Satellite rows come from the satellites layer's bounded nearest query,
measured from the satellite itself, and ride the satellite; RETURN goes
back to the one before, skipping one that has left the catalogue. While
the camera hops to a new satellite only TRACK ACQUIRED and RETURN show, so
nothing is measured from the old position. An arrow pressed while a focus
eases back takes the arrows' own clock. Site and satellite rows name their
own actions.
2026-11-09 01:56:30 +01:00
Denis Groshev 567c833582 feat(cockpit): keep the orbital camera on a ground target
In the satellite orbital view a single click on a mapped ground point (a
ground point of a layer that is on, such as a Mapped Installations site, a
fire or a local data pack feature) keeps the camera on it. Each frame the
look is aimed from the satellite's position to the point's Earth-fixed
position in the track-up nadir frame the arrows tilt, so the point stays
at the centre as the satellite moves. The swing onto the point, or between
points, uses the arrows' easing on a slower clock and then holds the point
exactly; reduced motion cuts.

A click elsewhere, FOCUS on the focused point, NADIR, Home or 0 ends the
focus and the view eases back to nadir; an arrow ends it and tilts on from
where the view is. The focus also ends, easing back, when the point leaves
the ±45° reach or the satellite's horizon, when its layer is turned off or
its record is dropped, and with the view. While focused the readout gives
the single off-nadir angle to the point (OFF-NADIR 23.4°), names it in a
polite live region, and the swath reads approximate as for a tilt. Clicks
still reach Cesium's own handler and never untrack the satellite; a click
a tool owns is ignored.

Under the readout, IN REACH lists the mapped points facing the satellite
that are within the reach or in the frame, nearest to nadir first and at
most 12, rebuilt once a second. A row opens the point's position, source
and OpenStreetMap tags (or its layer's properties); FOCUS focuses it,
switches to it, or ends the focus held on it, and is unavailable for a
point in frame but beyond the reach.
2026-11-09 01:56:29 +01:00