mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 07:42:23 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6521fd2316 | ||
|
|
346a08a42e |
No files matched your search
+1
-1
@@ -176,7 +176,7 @@ VITE_AIS_LIVE_LABEL_MAX_ROWS=900
|
||||
AISSTREAM_API_KEY=
|
||||
# Optional AISStream subscription filters.
|
||||
# AISSTREAM_BOUNDING_BOXES=[[[-90,-180],[90,180]]]
|
||||
# AISSTREAM_MESSAGE_TYPES=PositionReport,StandardClassBPositionReport,ExtendedClassBPositionReport,ShipStaticData,StaticDataReport,AidsToNavigationReport
|
||||
# AISSTREAM_MESSAGE_TYPES=PositionReport,StandardClassBPositionReport,ExtendedClassBPositionReport,ShipStaticData,StaticDataReport
|
||||
# Feed watchdog: silence (ms) after which the feed is REPORTED stale; the socket
|
||||
# is recycled at 2.5x that. Default 120000. The watchdog self-arms only for the
|
||||
# default worldwide subscription — a narrow filter above can be legitimately
|
||||
|
||||
+2
-17
@@ -1,13 +1,13 @@
|
||||
# Data Sources & Attribution
|
||||
|
||||
God's Eye View's **code** is [MIT](LICENSE)-licensed. **The MIT grant covers the source code only — it does NOT extend to third-party data or visual assets.** Every third-party source keeps its own license and terms. This file documents the live and bundled data sources. The bundled 3D models are the project's own procedural fleet models, MIT-licensed with the code ([`public/models/README.md`](public/models/README.md)).
|
||||
God's Eye View's **code** is [MIT](LICENSE)-licensed. **The MIT grant covers the source code only — it does NOT extend to third-party data or visual assets.** Every third-party source keeps its own license and terms. This file documents the live and bundled data sources; bundled 3D-model provenance is recorded in [`public/models/README.md`](public/models/README.md).
|
||||
|
||||
How to read this:
|
||||
|
||||
- **The non-permissive datasets are carved out, not omitted.** Some bundled data (e.g. TeleGeography, CC BY-NC-SA) isn't MIT-compatible. Rather than hide it, we **bundle it with a clear license carve-out** so the app works out of the box — but it stays under the provider's terms.
|
||||
- **If your use doesn't fit a dataset's license, remove that dataset.** Most importantly: TeleGeography is **NonCommercial** — commercial users must delete it (or license it from TeleGeography). It's one self-contained folder.
|
||||
- **Attribution is shown in-app** and listed here. Keep it intact. The required Google/Cesium credit renders on the on-globe credit line (bottom-left, `#cesium-credits`), and every per-layer credit below is registered into the expandable **"Data attribution"** lightbox on that line (`src/data/dataCredits.js` → `viewer.creditDisplay.addStaticCredit`). Both stay visible in clean-view and recording modes.
|
||||
- **The bundled 3D models are the project's own.** Every model under `public/models/` is a procedural fleet model generated by the project maintainers and covered by the repository's MIT license; [`public/models/README.md`](public/models/README.md) describes the set.
|
||||
- **Bundled model attribution lives beside the model files.** [`public/models/README.md`](public/models/README.md) records each shipped model's creator, source, license, and modification status.
|
||||
|
||||
---
|
||||
|
||||
@@ -419,7 +419,6 @@ The [Bhote Koshi event pack](public/events/bhote-koshi-2026/README.md), under `p
|
||||
| **Natural Earth countries, states and provinces** (260 admin-0/map units + 4,587 admin-1 polygons) | `natural_earth/` | **Public domain** | ✅ (no restrictions) | "Made with Natural Earth" (courtesy credit — not legally required) |
|
||||
| **Natural Earth disputed areas** (11 of the source's 99 features, hand-picked; [list](src/data/local_data/natural_earth/README.md#disputed-areas-disputed_areasjson)) | `natural_earth/` | **Public domain** | ✅ (no restrictions) | "Made with Natural Earth" (courtesy credit — not legally required) |
|
||||
| **Natural Earth populated places** (7,342 points: name, position, rank) | `natural_earth/` | **Public domain** | ✅ (no restrictions) | "Made with Natural Earth" (courtesy credit — not legally required) |
|
||||
| **Wikidata cities** (31,827 settlements of 15,000 people or more: name, position, population, country) | `wikidata_cities/` | **CC0 1.0** (public domain dedication) | ✅ (no restrictions) | "City, town and village names from Wikidata (CC0)" (courtesy credit — not legally required) |
|
||||
| **US Census Bureau counties** (3,235 county polygons) | `us_census_counties/` | **Public domain** | ✅ (no restrictions) | "U.S. Census Bureau" (courtesy — not legally required) |
|
||||
| **US Census Bureau places** (32,629 city, town and CDP polygons) | `us_census_places/` | **Public domain** | ✅ (no restrictions) | "U.S. Census Bureau" (courtesy — not legally required) |
|
||||
| **Who's On First neighborhoods** (61,441 polygons) | `wof_neighborhoods/` | Per source: CC BY, ODC-By, Open Government licences, CC0, PDDL, public domain (folder `ATTRIBUTION.md`) | ✅ (attribution) | "Neighborhood boundaries: Who's On First and contributors"; per-source credits in the folder |
|
||||
@@ -519,20 +518,6 @@ its width ("Sichuan, China · near Chengdu"), offline, with the states and provi
|
||||
the division. Public domain; credited "Made with Natural Earth" with the other packs. Rebuild:
|
||||
`node scripts/build-admin-packs.mjs --only places`; provenance in the folder README.
|
||||
|
||||
### Wikidata cities (`wikidata_cities/`)
|
||||
|
||||
Settlements of 15,000 people or more that Wikidata states a population and coordinates for,
|
||||
read once from the Wikidata Query Service by `scripts/build-wikidata-cities.mjs`: one row per
|
||||
place with its English label, latitude and longitude to 3 decimals, largest stated population,
|
||||
ISO 3166-1 country code and Wikidata id (31,827 places, 1.6 MB). The pack is read the
|
||||
first time a place name is looked up and gives the nearest city, the places in view and a city
|
||||
found by name (`findPlace`: "Paris, France", or the nearest namesake in view). Groupings of
|
||||
settlements (metropolitan areas, agglomerations) and places that no longer stand are left out
|
||||
unless they are also a living city. Names match without accents ("Sao Paulo" finds São Paulo).
|
||||
CC0; credited "City, town and village names from Wikidata (CC0)" as a courtesy once a name from
|
||||
the pack is shown. Rebuild: `node scripts/build-wikidata-cities.mjs`; provenance in the folder
|
||||
README. The app never calls Wikidata itself.
|
||||
|
||||
### Sky background (`sky/`)
|
||||
|
||||
Sources, licences, the changes CC BY asks to state, hashes and build commands
|
||||
|
||||
@@ -56,6 +56,7 @@ In short: the MIT grant does not extend to any third-party data. See
|
||||
DATA_SOURCES.md for the full per-source license + attribution summary, and
|
||||
remove any dataset whose terms don't fit your use.
|
||||
|
||||
The 3D models under public/models/ are the project's procedural fleet
|
||||
models, generated from code by the project maintainers, and are covered by
|
||||
the MIT license above like the source code. See public/models/README.md.
|
||||
The 3D models under public/models/ are also third-party works and are NOT
|
||||
licensed under MIT. They remain under their individual licenses. See
|
||||
public/models/README.md for the author, source, license, modification notice,
|
||||
and attribution covering each model file.
|
||||
@@ -60,7 +60,7 @@ Start with the included data sources, then add your own. Each layer is a separat
|
||||
- **📡 Contacts:** A 250 km roster of everything near your target — step through live aircraft and drop into any cockpit.
|
||||
- **🎯 Click-to-track anything:** Camera locks on, draws a fading trail, surfaces full metadata — and a tracked fire or vessel hands you off to the nearest live camera in one click.
|
||||
- **🖊️ Voice whiteboard:** Speak annotations onto the world — real boundary polygons, marks, and routes.
|
||||
- **🛫 3D fleet:** 122 procedural models drawn life size by type — airliners, business jets, helicopters, drones, ships, buses, trams and trains — and every contact swaps from glyph to 3D model as you close in.
|
||||
- **🛫 3D hangar:** Real per-class aircraft models — 787, ATR-72, Citation, Bell 206, MQ-9 — and a tracked contact swaps from glyph to 3D model as you close in.
|
||||
- **🎨 Reskin reality:** GLSL sensor looks over the normal globe — CRT, NVG, FLIR/thermal, Noir.
|
||||
- **🟩 Detection overlay:** Screen-space bounding boxes and IDs on everything in view.
|
||||
- **🎖️ Military HUD:** Tactical heads-up display with intelligence-style telemetry.
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
- When the globe stops drawing after a render error or a lost WebGL context,
|
||||
an alert dialog says so instead of leaving a frozen or black globe behind
|
||||
working controls. Retry restarts drawing (Reload after a lost context), and
|
||||
Reset view reopens the app without the view restored from the address bar.
|
||||
Cesium's "Rendering has stopped" panel no longer appears in the app
|
||||
(German, @gekh, #1579).
|
||||
@@ -1,12 +0,0 @@
|
||||
- Let an application add its own Cockpit subjects through
|
||||
`services.cockpitSubjects`. A provider supplies the subject's position,
|
||||
heading and HUD (in the page's language, via `getLocale` and
|
||||
`onLocaleChange` from `./i18n`); the camera can follow it without the
|
||||
ground clamp, anchored exactly on it, and a generic HUD adds source rows, a
|
||||
banner, a veil (off under reduced motion) and a bottom progress line. The
|
||||
Rail and Transit layers expose read and hold APIs so a selected train or
|
||||
vehicle can be ridden, and `./layers/rail/paths` is exported; that export
|
||||
and the read APIs are experimental. `docs/APPLICATION.md` shows a minimal
|
||||
provider. GEV itself registers none, and entry to the built-in aircraft,
|
||||
satellite and vessel views is unchanged; a provider's subject takes entry
|
||||
when it was selected after them (Denis Groshev, @ZloyS, #1686).
|
||||
@@ -1,5 +0,0 @@
|
||||
- Fix the translations of the strings added since the last review (News,
|
||||
Data Library, Wildlife Sightings, Shipwrecks, Rail, the vessel and orbital
|
||||
cockpits) in all twelve languages: neutral side labels and counts no longer
|
||||
break the sentence, terms follow each glossary, and Traditional Chinese
|
||||
follows Simplified Chinese again (German, @gekh, #1698).
|
||||
@@ -0,0 +1,4 @@
|
||||
- Cut the stutter while zooming and panning with Street Traffic on in dense
|
||||
cities. Coarse roads are no longer clipped against every detail tile in one
|
||||
task, roads already parsed in a load are not parsed again, and ground floors
|
||||
are read once per cell (mazy1998, #1705).
|
||||
@@ -1,8 +0,0 @@
|
||||
- Draw every 3D model from the project's procedural fleet set: 121 models
|
||||
(about 2 MB, MIT like the code) chosen per vehicle by type and drawn life
|
||||
size, replacing the eight third-party aircraft models. Flights, Military
|
||||
Flights and Local ADS-B pick a model by ICAO type, class and emitter
|
||||
category; Vessels, Transit and Rail now draw models too, under the same 3D
|
||||
toggle and distances, a ship at its AIS length and beam and a tram, bus or
|
||||
train in its mode or category colour. The whole set loads once when a layer
|
||||
starts (samehkhamis, #1714).
|
||||
@@ -1,3 +0,0 @@
|
||||
- Draw a vessel whose feed sends no length at its ship type's typical size, so
|
||||
a cruise ship or container ship no longer shows as its type's smallest model
|
||||
(samehkhamis, #1715).
|
||||
@@ -1,5 +0,0 @@
|
||||
- Collapse the CCTV panel when the layer is switched off. It stayed open with
|
||||
an empty frame and a meta line that still described the last camera; the
|
||||
meta line of a disabled layer now reads the catalog count. Contacts, Space
|
||||
Missions and Clear selected data layers leave the panel as it is
|
||||
(asturksever, #1723).
|
||||
@@ -1,5 +0,0 @@
|
||||
- Draw airport ground vehicles and ground stations as what they are instead of
|
||||
the generic plane. An airport vehicle (type `ZVEH`) is a fire truck, pushback
|
||||
tug, van or car by its callsign, a ground station (`GRND`) is a new 11.8 m
|
||||
radio mast, and one with no altitude stands on the ground instead of at the
|
||||
10 km airborne default (samehkhamis, #1729).
|
||||
@@ -1,4 +0,0 @@
|
||||
- Draw airport ground vehicles as real shapes instead of stacked boxes: the
|
||||
car is an operations SUV, the van a high-roof panel van, the fire truck an
|
||||
airport crash tender with turrets and the pushback a low tug with a glazed
|
||||
cab, all on the ground at their built size (samehkhamis, #1730).
|
||||
@@ -1,3 +0,0 @@
|
||||
- Draw aircraft models in their own mesh colours instead of a 94% flat
|
||||
white (amber for military) over the model; the tracked and emergency
|
||||
colours now tint the mesh without hiding its paint (samehkhamis, #1731).
|
||||
@@ -1,4 +0,0 @@
|
||||
- An aircraft that reports no ground track (parked or taxiing, from adsb.lol
|
||||
or a local receiver) now faces its reported true heading instead of north,
|
||||
so one taxiing south no longer looks like it is moving backwards
|
||||
(samehkhamis, #1732).
|
||||
@@ -1,11 +0,0 @@
|
||||
- Draw aids to navigation as buoys instead of ships: a buoy, beacon, RACON,
|
||||
light or mark from the vessel feed (or any AIS station numbered 99MIDxxxx)
|
||||
is now the fleet set's buoy of its IALA mark (cardinal, port or starboard
|
||||
hand, safe water, isolated danger or special mark), standing still with no
|
||||
heading, and its icon is a disc rather than a chevron. Its card and readout
|
||||
show its kind of mark with no speed or heading. AISStream now subscribes to
|
||||
aid-to-navigation reports and names each by its kind of mark and its full
|
||||
name; the feed lists every ship ahead of any aid, and `vessels_in_area`
|
||||
counts ships only. A virtual aid (a mark broadcast where nothing physical
|
||||
stands) keeps its disc, is never drawn as a buoy, and its card says it is
|
||||
virtual; vessel sources may mark any record `virtual` (samehkhamis, #1733).
|
||||
@@ -1,10 +0,0 @@
|
||||
- Draw transit vehicles as their kind, not one of five: trolleybuses, cable
|
||||
cars, funiculars, gondolas and monorails are modes of their own, with their
|
||||
own icon, colour, card word and fleet model. A vehicle record may carry its
|
||||
route's GTFS `route_type` (`routeType`), which settles its mode before the
|
||||
feed's route hints and picks the finer model (a coach for 200, a high-speed
|
||||
set for 101). Prague's trolleybuses and Petřín funicular, and Bucharest,
|
||||
Cluj and Turin's trams, trolleybuses and metro lines, now read as such from
|
||||
the route ids in each operator's routes.txt. Le Havre's funicular, which its
|
||||
routes.txt types as a bus, is a named correction that outranks the route
|
||||
type (samehkhamis, #1734).
|
||||
@@ -1,5 +0,0 @@
|
||||
- Move Visual to the left rail, after Data Layers, Utilities and Authoring, and
|
||||
lead the right column with Context; an open Context or CCTV panel takes the
|
||||
column's full height. A switch in each panel header moves that side's panels
|
||||
over their rail to the screen edge, and the scroll wheel at a screen edge
|
||||
steps through that side's panels (manjunath22466, samehkhamis, #1735).
|
||||
@@ -1,9 +0,0 @@
|
||||
- Fix three time readings on the world timeline. A scrub near now goes live
|
||||
only within two minutes of now, however long the track: with a layer whose
|
||||
source covers years, any time in the last few weeks used to snap to live.
|
||||
Fire cards and fire details count a detection's age from the end of the
|
||||
24 hours shown, and from now only when live, so a window that ended months
|
||||
ago reads "new 3h" rather than "new 105d". Weather and satellite cloud
|
||||
frames stay shown, and the latest reads late, for at least one and a half
|
||||
of the feed's own frame spacings, so a feed whose frames come an hour apart
|
||||
no longer goes blank for half of every hour (samehkhamis, #1736).
|
||||
@@ -1,7 +0,0 @@
|
||||
- Let a host application decide where live contacts are drawn.
|
||||
`createApplicationCatalog({ placement })` takes a placement provider whose
|
||||
`layer(kind)` hands Flights, Military Flights, AIS Vessels, Transit and Rail
|
||||
a `place(id, wallMs)` (and optionally `observe(id, fix)`, `forget(id)`):
|
||||
each layer tells it every live fix it accepts and draws a contact where it
|
||||
places it. Without one, or for a contact it does not place, every layer
|
||||
moves as before (samehkhamis, #1737).
|
||||
@@ -1,4 +0,0 @@
|
||||
- Group the Visual panel into Look, Map source, Overlay and Effects with a
|
||||
Reset at its foot, and add a Scope opacity slider (50–100%) that share links
|
||||
carry when it is below full; Reset keeps the HUD layout, and the HUD gets
|
||||
shadows when Scope is off (manjunath22466, samehkhamis, #1738).
|
||||
@@ -1,4 +0,0 @@
|
||||
- Give the desktop cockpit the main page's side rails: Data Layers alone on the
|
||||
left, and Display and Radio as a right rail that opens one panel at a time
|
||||
beside it, at the same height, with Contact and Live Signals restyled to
|
||||
match (manjunath22466, samehkhamis, #1739).
|
||||
@@ -1,3 +0,0 @@
|
||||
- Add a search and a filter (On / Off and category) to the Data Layers panel,
|
||||
matching layer names in the UI language, and a section index down its side
|
||||
that jumps to each group (manjunath22466, samehkhamis, #1740).
|
||||
@@ -1,6 +0,0 @@
|
||||
- Let an application that composes Core use its translator and word Core's
|
||||
messages its own way. The `./i18n` entry exports `t`, `tn`, `msg`,
|
||||
`translateKnown` and the number and date formatters, and
|
||||
`setMessageOverrides({ en: {…}, de: {…} })` shows the application's text for
|
||||
any catalog key, in the active locale or else in English (samehkhamis,
|
||||
#1741).
|
||||
@@ -1,3 +0,0 @@
|
||||
- Bundle 31,827 cities from Wikidata (CC0) for offline place lookups: the
|
||||
nearest city, the cities around a point and a city by name, matched without
|
||||
accents (manjunath22466, samehkhamis, #1743).
|
||||
@@ -1,4 +0,0 @@
|
||||
- A weather source can name its provider for each product with
|
||||
`sourceName(kind)`; the Rain radar, Lightning density and Satellite clouds
|
||||
rows show it until a manifest names its own, instead of always saying NOAA
|
||||
nowCOAST (samehkhamis, #1744).
|
||||
@@ -1,51 +0,0 @@
|
||||
- Timeline marks have labels. Hovering a tick shows its label (a launch's
|
||||
mission name, or an earthquake's magnitude and place, such as
|
||||
`M6.4 · 113 km SSE of Lata, Solomon Islands`), and a tap on or within 6 px
|
||||
of a tick goes exactly to its mark rather than to the point tapped; a tap
|
||||
elsewhere and a drag work as before. The arrow keys still step from mark to
|
||||
mark, and the handle then tells the mark's label with the time to screen
|
||||
readers and shows it over the track while focused. A layer's world clock
|
||||
coverage may give each mark as `{ time, label, id, kind }` as well as a
|
||||
bare time. `worldClock.coverage()` keeps `marks` as the sorted times and
|
||||
adds `markDetails`, the merged marks themselves, merged on their whole
|
||||
label; a label longer than `WORLD_CLOCK_MARK_LABEL_LENGTH` (60 characters,
|
||||
exported from `gods-eye-view/time`) is then cut, ending in `…`, never
|
||||
splitting an emoji. A zoom or a pan clears the hovered label, and a label
|
||||
is never wider than the track (samehkhamis, #1745).
|
||||
- The timeline marks only events: launches, and earthquakes of magnitude 6
|
||||
and up while the Earthquakes layer is on (about one every two days
|
||||
worldwide). It no longer draws a tick for every weather frame, which came
|
||||
every few minutes and made the track a wall of dashes; the weather frames
|
||||
still set the time the timeline covers, and Play still waits at each next
|
||||
frame until it loads. With no mark ahead, the arrow keys step along the
|
||||
track. The map still draws every earthquake, and an earthquake layer never
|
||||
widens the timeline beyond its strong quakes' own times. The Earthquakes
|
||||
layer registers with the world clock (`gods-eye-view/time`) once it is
|
||||
initialised, and has time to show only while on;
|
||||
`gods-eye-view/layers/earthquakes` adds `EARTHQUAKE_MARK_MAGNITUDE`,
|
||||
`earthquakeMagnitudeTitle`, `earthquakeMarkLabel` and `earthquakeCoverage`.
|
||||
A weather row's `step` param goes to the previous or next weather frame
|
||||
(samehkhamis, #1745).
|
||||
- The main timeline is the only time control. The Weather panel no longer
|
||||
has a slider, step buttons, Latest or Play of its own; it shows the frame
|
||||
time instead: the newest frame and its age while live, the timeline time
|
||||
alone at a past time; while live it shows the newest frame even when the
|
||||
products are suspended (reduced motion, a hidden page, no imagery host).
|
||||
The timeline's Play runs the world clock at the chosen speed for weather
|
||||
too, stopping at each next frame until it loads, rather than stepping the
|
||||
observed frames every two seconds and going round from the last to the
|
||||
first. A weather row's `play` and `latest` params move the world clock as
|
||||
the timeline does: `play` runs at the timeline's chosen speed, from the
|
||||
start when live or at the end, and does nothing while every product the
|
||||
row shows is suspended or while there is no time to show. The world clock
|
||||
keeps a chosen rate while paused and live (`goLive()` no longer sets it
|
||||
back to 1), so any play runs at it, and the timeline's speed shows the
|
||||
clock's rate whoever set it (the nearest offered speed for another rate).
|
||||
A weather layer made without a weather clock leaves the world clock alone
|
||||
until it is first enabled, does nothing once destroyed, and destroying it
|
||||
leaves the world clock's time where it is. **Breaking:** the catalog's
|
||||
`weatherClock` (`gods-eye-view/application/layers`) no longer has
|
||||
`setTarget`, `step`, `latest`, `play`, `pause` or `togglePlay`, its state
|
||||
no longer has `playing`, and it no longer attaches itself to the world
|
||||
clock as a driver; move time with the world clock (`gods-eye-view/time`,
|
||||
also `weatherClock.world`) instead (samehkhamis, #1745).
|
||||
@@ -1,5 +0,0 @@
|
||||
- Move satellites every frame. Points were propagated once a second and held
|
||||
still in between, so at 1 min/s they jumped about 45 px a second; each now
|
||||
glides from its last SGP4 sample (position, velocity, gravity and its rate
|
||||
of change) to the world time, resampled every second and whenever time
|
||||
moves 30 s from it, within about 10 m of SGP4 (Sameh Khamis, #1746).
|
||||
@@ -1,5 +0,0 @@
|
||||
- Move vessels every frame. Vessels between fixes, and live vessels a host
|
||||
placement places, were redrawn at most every 250 ms with a frame asked for
|
||||
once a second; they are now redrawn every frame, and the next frame is asked
|
||||
for once the fastest drawn vessel will have moved half a pixel on screen
|
||||
(Sameh Khamis, #1747).
|
||||
+1
-150
@@ -187,28 +187,7 @@ instances. Configure sources with `application/sources` before registration or
|
||||
state restoration, and release them after the consumers stop. The catalog
|
||||
constructor (`application/layers`) also requires `wind`, `weather` and
|
||||
`cyclones` sources with `getSnapshot` for the Weather layers, and owns the
|
||||
observed-history clock those layers share for the catalog's lifetime. That
|
||||
clock, the catalog's `weatherClock`, follows the world clock (`gods-eye-view/time`)
|
||||
and has no time controls of its own: move time with the world clock's `seek`,
|
||||
`play`, `pause` and `goLive` (`weatherClock.world` is the one it follows).
|
||||
|
||||
A layer gives the world clock's timeline the time it can show with
|
||||
`worldClock.register({ id, coverage })` (`gods-eye-view/time`), which returns
|
||||
the unregister; call `worldClock.coverageChanged()` when that time changes.
|
||||
`coverage()` returns null while the layer has none, or
|
||||
`{ start, end, marks? }` in epoch ms. `marks` are the moments worth going to,
|
||||
drawn as ticks the timeline's arrow keys step to and a tap on or within 6 px of
|
||||
snaps to. Each mark is a number, or `{ time, label?, id?, kind? }`: `label` is
|
||||
short text (cut to 60 characters, ending in `…`) shown when the pointer is on
|
||||
the tick and told with the time while the handle is on the mark, `id` the layer's own key for the event and `kind`
|
||||
what sort of event it is (`'launch'`, `'earthquake'`). The two forms may be
|
||||
mixed; a mark without a finite time, and a `label`, `id` or `kind` that is not
|
||||
a non-empty string, are ignored. `worldClock.coverage()` returns the union of
|
||||
the registered layers, `{ start, end, marks, markDetails }`, or null: `marks`
|
||||
is every mark's time once, in order, as numbers, and `markDetails` the marks in
|
||||
time order, each a frozen `{ time, label?, id?, kind? }` holding only the
|
||||
fields given, with marks given alike by several layers once and a bare time
|
||||
left out where a mark with more to say shares it.
|
||||
observed-history clock those layers share for the catalog's lifetime.
|
||||
A `weather` source may also supply decoded frames: an optional
|
||||
`getFrameImage({ product, time, size, bbox, signal })` returns a whole-extent or
|
||||
detail image and an optional `getFrameTile({ product, time, z, x, y, size,
|
||||
@@ -216,12 +195,6 @@ signal })` returns one tile or null for a transparent tile, each as an
|
||||
`ImageBitmap`, `ImageData`, canvas or `VideoFrame` of exactly the requested
|
||||
size, which the layer draws once and closes. Without them the layers read the
|
||||
image and tile routes (`WeatherFrameFeed` in `src/layers/weather/source.js`).
|
||||
It may also name its source with an optional `sourceName(kind)`, `kind` being
|
||||
`'radar'`, `'lightning'` or `'satellite'`: the layer's row shows that name until
|
||||
a manifest names its own, and NOAA nowCOAST when the source names none. The
|
||||
name is read once, when the layer is built, and held to the same rule as a
|
||||
manifest's `display.source`. The map credit still comes from each manifest's
|
||||
`display.credit`, so a source that is not nowCOAST sets that too.
|
||||
|
||||
`application/services` accepts boundary, terrain, regional-context, weather and
|
||||
summary services; its weather service supplies cockpit local conditions, not the
|
||||
@@ -257,125 +230,3 @@ starts and switches the language with the `./i18n` entry (`startPageLocale`,
|
||||
`localePicker: false` to `createApplicationControls` instead of editing the
|
||||
markup. Node tests that import application modules register the component
|
||||
loader with `node --import=<core package>/build/register-vue`.
|
||||
|
||||
### Cockpit subjects
|
||||
|
||||
Core's Cockpit rides a tracked aircraft, the tracked satellite and the
|
||||
selected AIS vessel. An application adds kinds of its own with
|
||||
`services.cockpitSubjects` on `createApplicationControls`: a list of
|
||||
providers, checked once when the controls are built
|
||||
(`src/cockpitSubjectProviders.js`). A malformed provider, or one with a member
|
||||
that throws when read (a getter), is left out with a warning and the rest are
|
||||
kept. A provider's subject takes entry when it was selected after every
|
||||
built-in subject present (a tracked aircraft or satellite, a selected vessel),
|
||||
whose own fixed order is unchanged, and needs the provider's `layerId` on; the
|
||||
provider then holds its subject, hands the camera a frame and the generic HUD
|
||||
a view model on every update, and releases it on exit. Provider methods are
|
||||
synchronous and are called on the provider, so a class instance's methods keep
|
||||
their `this`; one that throws or returns a promise is treated as having
|
||||
failed. What a method returns may carry getters: one that throws when read
|
||||
makes a candidate none, a frame malformed (the view ends) and a HUD view keep
|
||||
the last; a worded member (labels, entry face, look) that throws keeps its
|
||||
last good words.
|
||||
|
||||
```js
|
||||
import { getLocale, onLocaleChange } from 'gods-eye-view/i18n';
|
||||
import { pointAtS } from 'gods-eye-view/layers/rail/paths';
|
||||
|
||||
const words = {
|
||||
en: { view: 'Ride view', exit: 'Exit ride view', exitText: 'EXIT RIDE' },
|
||||
de: {
|
||||
view: 'Fahrtansicht',
|
||||
exit: 'Fahrtansicht verlassen',
|
||||
exitText: 'VERLASSEN',
|
||||
},
|
||||
};
|
||||
|
||||
/** Ride the Rail layer's selected train; `rail` is its layer instance. */
|
||||
const ride = {
|
||||
kind: 'example-ride',
|
||||
layerId: 'rail',
|
||||
entryFace: { icon: 'train', label: 'RIDE' },
|
||||
// A function is read again when the page's language changes.
|
||||
labels: () => words[getLocale()] ?? words.en,
|
||||
candidate() {
|
||||
const train = rail.getSelectedTrain();
|
||||
return (
|
||||
train && {
|
||||
id: train.key,
|
||||
name: train.label,
|
||||
selectedAt: train.selectedAtMs,
|
||||
}
|
||||
);
|
||||
},
|
||||
// Only a train bound to its line can be ridden: read() needs its path.
|
||||
isEntryAllowed: () => Boolean(rail.getSelectedTrain()?.path),
|
||||
hold: (id) => rail.setCockpitSubject(id),
|
||||
read(nowMs, ctx) {
|
||||
const train = rail.getCockpitSubject();
|
||||
if (!train?.path) return null; // ends the view
|
||||
const point = pointAtS(train.path.line, train.path.s);
|
||||
return {
|
||||
id: train.key,
|
||||
latitude: point.lat,
|
||||
longitude: point.lon,
|
||||
heightM: train.position.railHeightM, // null stands it on the shared floor
|
||||
name: train.label,
|
||||
headingDeg:
|
||||
train.path.direction < 0 ? point.bearingDeg + 180 : point.bearingDeg,
|
||||
speedMps: train.motion.reportedSpeedMps ?? train.motion.derivedSpeedMps,
|
||||
// The layer already moves the train smoothly: sit on it.
|
||||
anchor: 'exact',
|
||||
eye: { upM: 3.5 },
|
||||
};
|
||||
},
|
||||
hud: (frame) => ({ title: frame.name }),
|
||||
release(id, { restore }) {
|
||||
rail.setCockpitSubject(null);
|
||||
if (restore) rail.focusTrain(id);
|
||||
},
|
||||
};
|
||||
|
||||
createApplicationControls({ /* ... */ services: { cockpitSubjects: [ride] } });
|
||||
```
|
||||
|
||||
The `./i18n` entry gives `getLocale()` and `onLocaleChange()`:
|
||||
Core translates only its own strings, so a provider words its labels, entry
|
||||
face and HUD for the page's language (labels may be functions, as above; the
|
||||
HUD is read again on a change). `gods-eye-view/layers/rail/paths` is the
|
||||
Rail layer's pure path module (`createRailPath`, `pointAtS`, `projectOnPath`)
|
||||
for a provider placing a train along its line. The frame, HUD view and
|
||||
candidate fields are documented in `src/cockpitSubjectProviders.js`; the Rail
|
||||
and Transit read APIs in `src/layers/rail/cockpitSubject.js` and
|
||||
`src/layers/transit/cockpitSubject.js`. A snapshot read again while nothing in
|
||||
it changed is the one read before, so `read` may take one on every update; a
|
||||
train's `path.line` is a frozen copy of its line, for `pointAtS` and
|
||||
`projectOnPath`. The `layers/rail/paths` export and the
|
||||
Rail and Transit read APIs (`getSelectedTrain`, `getSelectedVehicle`,
|
||||
`getCockpitSubject` and the snapshots they return, `path.line` among them)
|
||||
are experimental and may change without notice.
|
||||
|
||||
A candidate's `selectedAt` is a token that changes each time the user selects
|
||||
the subject, even the same one again, so entry counts it as selected then; a
|
||||
new `id` under the same token is the same selection (a train's selection
|
||||
moving to another feed's copy of it). The Rail and Transit snapshots give one,
|
||||
`selectedAtMs`: when the user last selected the train or vehicle by a click or
|
||||
`focusTrain`/`focusVehicle`. It is null after a deselection and for one
|
||||
selected only by a hold, since a hold is not the user's selection.
|
||||
|
||||
A frame with `heightM: null` stands on the shared floor, or where it last
|
||||
stood while that floor is cold. If no height has been known since entry, the
|
||||
view waits, showing ACQUIRING SURFACE, for at most five seconds
|
||||
(`COCKPIT_GROUND_WAIT_TIMEOUT_MS`), then ends and releases the subject.
|
||||
|
||||
`hold(id)` returns `true` once it holds the subject, or `false` to refuse
|
||||
entry; any other answer, `undefined` included, refuses entry too, with one
|
||||
warning. A refused hold, and one that throws or returns a promise, is followed
|
||||
by `release(id, { restore: false })`, again once a returned promise settles,
|
||||
so `release` must be harmless for a subject it does not hold and when called
|
||||
more than once.
|
||||
|
||||
Leaving the view does not track again an aircraft that was tracked before
|
||||
entry, as with the bridge and orbital views: when the user leaves by the exit
|
||||
control, the map goes back to the ridden subject through
|
||||
`release(id, { restore: true })`.
|
||||
@@ -293,9 +293,9 @@ the user's later collapse choice, including body remounts. Mark the body
|
||||
`max-height` and puts the scroll offset back afterwards. Panel bodies only
|
||||
scroll on an open-card change, with one write, never on a refresh.
|
||||
|
||||
Compose `createRailCards({ container, document, onParams, onOpen })`. Rails
|
||||
have no time controls of their own; the world timeline moves time. Cards
|
||||
reconcile `{ id, title, badge, open, compact,
|
||||
Compose `createRailCards({ container, document, onParams, onOpen })` and
|
||||
`createRailTimeline({ container, document, onCommit, onPreview, onStep,
|
||||
onLatest, onPlay })`. Cards reconcile `{ id, title, badge, open, compact,
|
||||
compactStatus, blocks }` in place. Their full-width native button headers expose
|
||||
`aria-expanded` and `aria-controls`; articles expose `data-open`. The feature
|
||||
adapter owns accordion state. Generic cards do not select data or enable layers.
|
||||
@@ -330,9 +330,10 @@ consumers. They expose `summary.settings`, `summary.actions` and optional
|
||||
subscriptions still refresh the cards. Cards own configuration and readings.
|
||||
|
||||
Weather orders cyclones, wind, then a bordered Observed history group containing
|
||||
the frame time and active radar, satellite and lightning cards. The group
|
||||
heading and scope name its active products. The frame time shows whenever any
|
||||
observed product is enabled and has a frame; the world timeline moves time.
|
||||
the timeline and active radar, satellite and lightning cards. The group heading
|
||||
and scope name its active products. The timeline stays visible whenever any
|
||||
observed product is enabled; fewer than two ticks disables transport. Pass
|
||||
`heading: false` to the timeline when the containing group owns the heading.
|
||||
Exactly one active card is open. Header clicks and newly enabled ids choose it
|
||||
(the last new entry wins a batch); refreshes and clock ticks retain it. First
|
||||
appearance uses cyclones with storms, otherwise the first card. Disabling the
|
||||
@@ -340,7 +341,7 @@ open layer falls back to the first remaining card. A WeakMap per document keeps
|
||||
explicit choices through body remounts without adding share state. Card opening,
|
||||
storm selection and layer enabling remain independent.
|
||||
|
||||
Optional card/badge class names provide feature styling. Card titles use
|
||||
Optional card/badge/slider class names provide feature styling. Card titles use
|
||||
`.data-name` typography; meta/time/badges use `.data-toggle-meta` typography;
|
||||
setting/result labels use `.panel-title`; chips retain `.data-toggle-chip`.
|
||||
Reserve one status line, even without a legend. Use app tokens. Wind's footer
|
||||
|
||||
+38
-401
@@ -365,26 +365,6 @@ The source factories are exported at `layers/directions/source` and
|
||||
`layers/recent-imagery/source`. Configuration is applied at construction time;
|
||||
this does not introduce live source replacement.
|
||||
|
||||
Application catalog composition also accepts an optional `placement`, a
|
||||
host-supplied placement provider (`src/data/placementProvider.js`): an object
|
||||
whose `layer(kind)` returns, for `aircraft`, `military`, `vessels`, `transit`
|
||||
or `rail`, either null or `{ place(id, wallMs), observe?(id, fix),
|
||||
forget?(id) }`. While live, each of those layers tells it every fix it accepts
|
||||
(time, position, the height it draws the fix at, reported speed, course and
|
||||
ground state) and draws a contact where `place` puts it: position, height
|
||||
(the layer's own when none is given), course and speed. A null answer, a
|
||||
provider that throws, a kind it leaves out, or no provider at all keeps the
|
||||
layer's own motion, unchanged; following the world clock, the layers never
|
||||
ask it. Transit and rail vehicles a provider places are advanced every frame
|
||||
in view while their placed position changes; one it holds still lets the
|
||||
render loop idle and is asked again each second, moving on once its placement
|
||||
does. A placed transit vehicle is drawn at its placed point, and a placed
|
||||
train where it is placed rather than on its bound railway line. Live vessels
|
||||
a provider places move on between refreshes: redrawn in place every frame,
|
||||
with the next frame asked for as soon as the fastest drawn vessel will have
|
||||
moved half a pixel on screen, at least once a second while the provider places
|
||||
any drawn vessel, and none once the layer is off.
|
||||
|
||||
Application catalog composition accepts an optional `streetLevelProviders` list.
|
||||
An explicit list replaces the default Mapillary provider; an empty list removes
|
||||
all imagery providers. Without that option, Mapillary is registered only when
|
||||
@@ -647,139 +627,6 @@ panel that holds its row, and `set_panel_open` reaches it as
|
||||
`utilities-panel` or by "utilities", "draw", "drawing" or "import". On a phone
|
||||
it sits collapsed in the Layers sheet under Data Layers.
|
||||
|
||||
## The Visual panel — November 14, 2026
|
||||
|
||||
Visual (`#pp-toggles`, `src/ui/views/panels/DisplayPanel.vue`) is headed
|
||||
VISUAL, has the edge switch in its header like the other side panels, and is
|
||||
grouped from the top: Look, the six style presets with their 1–6 keys, and
|
||||
Map source, the map source chips (`VisualPresets.vue`); Overlay, the HUD,
|
||||
detection, style parameters, 3D aircraft, Scope and Clean UI; Effects,
|
||||
Celestial, Bloom and Sharpen; and Reset to default at the foot. Each control
|
||||
is a card (`src/ui/styles/visual-panel.css`) whose header row ends in an
|
||||
on/off pill worded in the current language (Clean UI, an action, has none;
|
||||
the buttons carry their state as `aria-pressed`). Under the Cyber HUD the
|
||||
cards keep Cyber's own red skin. The presets and chips call the
|
||||
same Display store actions as the dock's Visual Presets tray, which stays on
|
||||
desktop and phone as before, along with Location; voice `Visual Presets`
|
||||
still opens that tray. On a phone the More sheet shows that tray right above
|
||||
Visual, so Visual leaves out its own presets and chips there. In Cockpit,
|
||||
the groups that Cockpit's Display popover takes leave the panel as before and
|
||||
keep their own styling there.
|
||||
|
||||
Scope has an Opacity slider (`#scope-opacity-slider`, 50–100%, default 100%;
|
||||
`SCOPE_OPACITY_MIN_PCT` in `src/ui/visualSettings.js`, since a fainter mask
|
||||
reads as Scope off while labels still fade outside the keyhole)
|
||||
that fades the whole mask (`setScopeMaskOpacity` in `src/scopeMask.js`, the
|
||||
canvas's CSS opacity) without repainting it or changing the altitude
|
||||
terminus; it is disabled while Scope is off and keeps its value. Share links
|
||||
carry it as `sco` in the long form and field 30 in the compact form, written
|
||||
only below 100%, so links made before it keep their codes and open at full
|
||||
opacity, and a link without it sets the opacity back to 100%; a link asking
|
||||
for less than 50% opens at 50%. Scenes do not
|
||||
save it: a scene shot leaves the opacity as it is.
|
||||
|
||||
Reset to default (`resetVisual` in `src/ui/displayBindings.js`) brings back
|
||||
the out-of-the-box look: the Normal style, the first-run post defaults
|
||||
(bloom off, sharpen on at 49%, the HUD shown, Dense detection at 75%
|
||||
with its fade and outside opacity, the celestial ring off), Elastic
|
||||
allocation, Scope on at 100% feather and 100% opacity, and 3D aircraft on in
|
||||
Proximity. The HUD layout follows the Normal style, as choosing it does: the
|
||||
Tactical HUD, except that the Cyber HUD stays Cyber. Inside Contacts, which
|
||||
lends detection for the session, the reset's detection becomes what leaving
|
||||
Contacts puts back and what a share link carries. It leaves the map source, the pinned scope terminus, Sonar's
|
||||
settings, other styles' parameters, the layers and the panels alone.
|
||||
|
||||
With Scope off the side rails, open panels and the dock's trays carry a soft
|
||||
drop shadow and the rails' labels and icons a text shadow
|
||||
(`src/ui/styles/scope-off-shadows.css`), so their edges hold against bright
|
||||
imagery.
|
||||
|
||||
## Side rails and the screen edge — November 14, 2026
|
||||
|
||||
Visual (`#pp-toggles`, the display settings, rail item `palette`) is the last
|
||||
panel of the left column, after Data Layers, Utilities and Authoring, and
|
||||
opens from the left rail like them (`PanelLayoutController`, falling back to
|
||||
the right column in a host without a left stack). Context leads the right
|
||||
column, followed by CCTV, Street Level and the other right panels. An open
|
||||
Context or CCTV panel takes the column's full height; the other right panels
|
||||
keep the content-sized layout and its hysteresis band. With nothing open, a
|
||||
rail's icons and their names show as two glass pieces side by side; once a
|
||||
panel opens, the icons stay in place and a highlight slides to the open one.
|
||||
|
||||
Each collapsible panel header has an edge switch (`UiPanelEdge.vue`,
|
||||
`src/ui/panelEdge.js`): it moves that side's panels over their rail to the
|
||||
screen edge, with the rail as a row of icons above the open panel, and back.
|
||||
The choice is per side, kept in `localStorage` (`gev.panelEdge`), and applies
|
||||
on the desktop shell only: phones and Cockpit keep their own layouts and do
|
||||
not show the switch.
|
||||
|
||||
Scrolling the wheel at a screen edge (`src/ui/railEdgeScroll.js`, from the
|
||||
edge to just past the rail, at most 84 px) steps that side's rail to its next
|
||||
or previous panel instead of zooming the globe, wrapping round at either end.
|
||||
One gesture is one step, trackpad coasting included, with at least half a
|
||||
second between steps; a step that comes too soon is held until then and is
|
||||
dropped if the pointer leaves the strip. Panels, controls, dialogs and the
|
||||
world timeline in the strip keep their own wheel. The strip is off on phones
|
||||
and touch screens, while the rails are hidden (clean view, recording), and it
|
||||
follows the rails' sides in a right-to-left language. On a phone, Visual is in
|
||||
the More sheet with the dock's Visual Presets, as before.
|
||||
|
||||
## Wikidata cities pack — November 15, 2026
|
||||
|
||||
`src/data/local_data/wikidata_cities/cities.json` bundles 31,827 settlements of
|
||||
15,000 people or more from Wikidata (CC0, 1.6 MB): name, position, population,
|
||||
country and Wikidata id. `src/data/wikidataCities.js` reads it on the first
|
||||
lookup and answers `nearestCity` (the largest city within 12 km, else the
|
||||
nearest within 60 km), `citiesAround`, `findPlace` ("Paris, France", or the
|
||||
nearest namesake within 150 km of the view, else the largest) and
|
||||
`countryName`. Names match without case or accents ("lodz" finds Łódź). The
|
||||
app never calls Wikidata; `scripts/build-wikidata-cities.mjs` rebuilds the
|
||||
pack and fails if well-known cities are missing or a known grouping such as
|
||||
the New York metropolitan area gets in. Nothing shows names from it yet; the
|
||||
Context panel is its first reader. Sources and licence: `DATA_SOURCES.md`.
|
||||
|
||||
## Data Layers finder and section index — November 15, 2026
|
||||
|
||||
The Data Layers header has a search button and a filter button
|
||||
(`src/ui/views/panels/LayerFinder.vue`). Search opens a field under the
|
||||
header that matches each layer's name on screen and its English name; Escape
|
||||
closes it and leaves the panel open. The filter is a small popover with two
|
||||
groups, Status (All layers, On, Off: whether the layer is enabled) and
|
||||
Category (the list's groups, kept by their key, so a language change keeps
|
||||
the choice). The arrows, Home and End move through it and Escape closes it.
|
||||
Rows and groups that are left out hide; chips under the header show the
|
||||
active filters and clear them, and "No layers match" says when nothing is
|
||||
left. Neither the search nor the filter is saved or put in share links. A
|
||||
voice request that points at a hidden layer clears the finder first
|
||||
(`gev:layer-finder-clear`).
|
||||
|
||||
A column of ticks down the list's inline end
|
||||
(`src/ui/views/panels/LayerSectionIndex.vue`) has one tick per group still
|
||||
shown; hovering or focusing a tick names its group beside it, on whichever
|
||||
side has room, and clicking scrolls the list there. The tick for the group in
|
||||
view is lit. Phones do not show it.
|
||||
|
||||
## Cockpit side panels — November 14, 2026
|
||||
|
||||
On the desktop, the cockpit's side panels match the main page's
|
||||
(`src/ui/styles/cockpit-panels.css`). The left rail shows Data Layers only:
|
||||
Authoring, Utilities and Visual, with their panels, are hidden, since the
|
||||
cockpit's own Display panel stands in for Visual. Display and Radio
|
||||
(`CockpitUtilityControls.vue`) become a right rail of two icons with their
|
||||
names beside them, and each opens one panel at a time beside the rail, with a
|
||||
header and a "−" to close it. The rail's buttons are disclosures
|
||||
(`aria-expanded`, `aria-controls`) and go through the `toggleCockpitUtility`
|
||||
action (`src/ui/radioBindings.js`); closing a panel gives focus back to the
|
||||
rail. Both rails start at the same height: the left one in CSS, and Display
|
||||
and Radio through the cockpit layout pass, whose floor
|
||||
(`COCKPIT_UTILITY_MIN_TOP_PX` / `_RATIO` in `src/ui/cockpitPresentation.js`)
|
||||
matches it, so an open panel still stops short of the briefing. Contact and
|
||||
Live Signals are shorter, in the same glass, with the briefing's rows and
|
||||
weather tiles tightened; Regional News stays a briefing page. In a right-to-left language, the rail keeps its
|
||||
physical side as the cockpit's other windows do. The Cyber HUD and the phone
|
||||
keep their own cockpit layouts, with the Display and Radio launchers as
|
||||
before, and edge mode does not apply in the cockpit.
|
||||
|
||||
## God's Eye View in conversations — October 2, 2026
|
||||
|
||||
Tool answers that can be shown in God's Eye View include a view: camera, layers,
|
||||
@@ -872,8 +719,6 @@ Display's native Scope checkbox defaults ON with feather at 100%, adjustable
|
||||
from 0–200%. OFF disables the slider while retaining its value. HUD layout
|
||||
changes preserve both settings; share links and scenes restore them. The
|
||||
historical missing-`scf` fallback remains 35%; explicit 0% keeps the hard edge.
|
||||
Its Opacity slider (0–100%, default 100%) fades the mask; share links
|
||||
restore it (`sco`), scenes do not (see The Visual panel).
|
||||
|
||||
With Scope OFF, Tactical and Minimal add text-sized black backplates
|
||||
at 70% opacity behind telemetry, vertical metadata and scene chrome. Original
|
||||
@@ -1782,14 +1627,7 @@ Earthquake rendering is exposed through `./layers/earthquakes`. The layer owns
|
||||
its entities and request lifecycle; the application supplies the overlay host
|
||||
and snapshot source. The standalone adapter keeps the existing USGS daily feed,
|
||||
M2.5+ filtering, static discs, magnitude labels and analyst records. Disabling or
|
||||
destroying the layer cancels pending work and ignores late results. While on,
|
||||
the layer registers its quakes of magnitude 6 and up
|
||||
(`EARTHQUAKE_MARK_MAGNITUDE`) with the world clock as timeline marks, spanning
|
||||
only their own times, and none while off; the map still draws every M2.5+
|
||||
quake. Each mark is labelled with the magnitude as the map shows it and the
|
||||
feed's place, cut to 40 characters (`earthquakeMarkLabel` in
|
||||
`src/layers/earthquakes/model.js`: `M6.4 · 113 km SSE of Lata, Solomon
|
||||
Islands`, or `M6.4` with no place).
|
||||
destroying the layer cancels pending work and ignores late results.
|
||||
|
||||
## Rendered-mesh floor readings — November 9, 2026
|
||||
|
||||
@@ -1868,7 +1706,7 @@ Applications supply the existing floor/snap, geoid, picking, sprite, camera,
|
||||
trail, focus, readout, context, aircraft presentation and render services.
|
||||
The factory never constructs a second application registry. Configure a source
|
||||
before initialization; replacing it while initialized is rejected. Model loads
|
||||
use the supplied asset resolver, including the up-front fleet set load. Enrichment receives
|
||||
use the supplied asset resolver, including the preload path. Enrichment receives
|
||||
an abort signal and cannot update a later lifecycle after destruction. Existing
|
||||
camera, terrain floor, trail, selection and measured model-size policies remain.
|
||||
|
||||
@@ -2000,8 +1838,7 @@ Playing, the layer stays a snapshot ahead of the time, and play does not pass a
|
||||
snapshot still loading for more than 8 seconds. Those snapshots apply as polls
|
||||
do, so contacts move on smoothly: aircraft are drawn at the clock's time
|
||||
rather than behind it, and vessels between two fixes move along the great
|
||||
circle, never past the newer one, redrawn every frame with frames asked for
|
||||
as for placed live vessels (vessels out of view move on every 250 ms). Records, absences and the Layers panel's age
|
||||
circle, never past the newer one. Records, absences and the Layers panel's age
|
||||
are judged at the clock's time. Outside the coverage the layer shows no
|
||||
contacts and loads nothing; while the coverage is unknown it loads nothing and
|
||||
clears nothing. A snapshot that fails clears the time and is asked again after
|
||||
@@ -2371,83 +2208,6 @@ the subject (tracking stopped, Satellites off) or tracking another target
|
||||
ends the view without refocusing. `src/ui/cockpitOrbitalView.test.mjs`
|
||||
covers entry, camera, zoom, look, HUD and exit.
|
||||
|
||||
### Application subjects
|
||||
|
||||
An application can add its own first-person subjects beside the aircraft,
|
||||
vessel and satellite: `services.cockpitSubjects` to `createApplicationControls`,
|
||||
a list of subject providers (`src/cockpitSubjectProviders.js`, checked once by
|
||||
the coordinator; a malformed one, or one with a member that throws when read,
|
||||
is left out with a warning and the rest are kept). A provider names
|
||||
its layer, an entry face and labels in the page's language, and supplies four
|
||||
operations: `candidate()` (what entry would ride now), `hold(id)`,
|
||||
`read(nowMs, ctx)` (a camera frame each update) and `hud(frame, ctx)` (a HUD
|
||||
view model), with `release(id, {restore})` on exit. Entry needs the provider's
|
||||
layer on, no Context change in flight and the provider's own
|
||||
`isEntryAllowed(candidate)`; voice stays aircraft-only and is refused, with
|
||||
the reason, while a provider's view is in use.
|
||||
|
||||
The built-in kinds keep their fixed entry order (a tracked aircraft, then the
|
||||
tracked satellite, then the selected vessel), so without providers entry is
|
||||
unchanged. A provider's candidate takes entry only when it was selected after
|
||||
every built-in subject present, by when the Cockpit first saw it or saw it
|
||||
selected again (its `selectedAt` changing): a train picked after an aircraft
|
||||
was tracked takes entry, and an aircraft tracked after it takes it back.
|
||||
Providers' candidates are ordered most recently selected first, then by
|
||||
priority; one first seen together with a built-in subject comes after it. The
|
||||
first in that order decides alone: when its gate is shut there is no entry
|
||||
rather than the next subject's.
|
||||
|
||||
A frame places the eye `eye.upM` above `heightM` (the shared floor where the
|
||||
frame gives none, or where it last stood while that floor is cold; with
|
||||
neither, the camera stays where it is and the HUD reads ACQUIRING SURFACE for
|
||||
at most `COCKPIT_GROUND_WAIT_TIMEOUT_MS`, after which the view ends and the
|
||||
subject is released: no height stands in for a floor never read), slews toward `headingDeg` at the provider's rate, and may
|
||||
turn off the floor clamps (`groundClamp: false`, for a subject inside a
|
||||
structure) or keep the anchor exactly on the subject (`anchor: 'exact'`, for a
|
||||
layer that already moves it smoothly; a frame with no `speedMps` is always
|
||||
exact, since an inertial anchor runs on at the frame's speed); `cameraIntervalMs: 0` updates the
|
||||
camera every rendered frame. A frame that is null, malformed or for another
|
||||
subject ends the view (`restoreTracking: false`), as an aircraft tracked
|
||||
meanwhile and the provider's layer turning off do; the exit control asks the
|
||||
provider to restore its subject. The generic HUD shows the provider's kicker,
|
||||
title, meta line, speed with its unit and tag, readout cards, always-visible
|
||||
source rows, banner, route card and Live Signals rows, a veil that darkens the
|
||||
picture under the HUD (with passing light streaks, which stop under reduced
|
||||
motion), arrow-key look-around when the provider asks for it, and a progress
|
||||
line along the bottom edge (`src/cockpitProgress.js`: close ticks merge,
|
||||
labels never overlap). Core translates none of the provider's text: a host
|
||||
words it for the page's language (`gods-eye-view/i18n`:
|
||||
`getLocale`, `onLocaleChange`). Its entry face, labels and look may be
|
||||
functions, read again when they are shown and on a change of language, as the
|
||||
HUD view is.
|
||||
|
||||
The Rail layer offers a read API for such a view: `getSelectedTrain()` and
|
||||
`getCockpitSubject()` (a snapshot of the train: where it is along its line,
|
||||
which way it runs (`pathBinding.js` learns it), its rail height, speed as
|
||||
reported or derived, position source, feed credit and stops, and
|
||||
`selectedAtMs`, when the user last selected it by a click or `focusTrain`,
|
||||
even again; null after a deselection and for a train only a hold selected),
|
||||
`setCockpitSubject(key)` (its marker, 3D model and card hidden, the selection kept, its
|
||||
feed held, clicks and Escape left to the view; the held copy stays the one
|
||||
drawn of its train while held, another feed's copy hidden, and it keeps
|
||||
moving, still selected, when its category is turned off) and
|
||||
`focusTrain(key)` (false for a train the layer does not draw); its pure path
|
||||
module is exported as `gods-eye-view/layers/rail/paths`. That export
|
||||
and these read APIs are experimental and may change without notice. A snapshot read again while nothing in it changed is
|
||||
the one read before, and its `path.line` is a frozen copy. Transit
|
||||
offers the same for a vehicle (`getSelectedVehicle`, `getCockpitSubject`,
|
||||
`setCockpitSubject`, `focusVehicle`): a held vehicle keeps playing when it is
|
||||
out of the camera's view, and its sprite, 3D model, card, trail and detection
|
||||
box are hidden; released, it is out of play until a visibility sweep finds it in
|
||||
view. Its snapshot's `position.groundHeightM` is the floor where playback draws
|
||||
it (the drawn marker's height while it is placed, else its segment's two
|
||||
reports' floors at the playback fraction), not the newest report's, and null while that floor is unknown. Rail's `path.direction` is the
|
||||
way the drawn train runs: once playback has placed it, a report that runs back
|
||||
turns it only when playback reaches that report. `docs/APPLICATION.md` (Cockpit subjects) shows a minimal provider. `src/ui/cockpitSubjectView.test.mjs`,
|
||||
`src/cockpitSubjectProviders.test.mjs`, `src/cockpitProgress.test.mjs`,
|
||||
`src/layers/rail/cockpitSubject.test.mjs` and the Transit lifecycle tests
|
||||
cover them.
|
||||
|
||||
## Context coordination
|
||||
|
||||
Context controls own Contacts/Space Missions state, entry and exit transactions,
|
||||
@@ -2469,17 +2229,6 @@ shown without a fade once it is decoded (or after one second if its decode never
|
||||
settles), so a new camera's preview is blank only while its frame loads. Disposal
|
||||
releases listeners, subscriptions, image handlers, summary timers and chip-hide timers.
|
||||
|
||||
The panel opens when a camera becomes active and collapses when the layer is
|
||||
switched off, from its own switch or anywhere else. The panel chrome applies
|
||||
the collapse, so a panel the desktop accordion only folded has its saved and
|
||||
shared state closed too. Clear selected data layers and Context isolation
|
||||
(Contacts, Space Missions) switch the layer off without the user asking: the
|
||||
panel and its share preference stay as they are, and the layer comes back to an
|
||||
open panel. Only the switch-off isolation makes counts (`suspendsLayer`, true
|
||||
while `_clearLayersOutsideContextMode` switches the layer off): the user
|
||||
switching CCTV off inside Contacts closes the panel as anywhere else. A panel opened by hand while the layer is off stays open, and its
|
||||
meta line reads the catalog count, not the last active camera.
|
||||
|
||||
## UI disposal
|
||||
|
||||
UI disposal releases the CCTV subscription, command-dock observer, legacy drag
|
||||
@@ -2671,9 +2420,8 @@ is validated against Cesium 1.138.0 and must be revalidated on upgrades.
|
||||
|
||||
`scripts/qa-anime-sky.mjs` checks asset readiness, visible Normal sky and
|
||||
Anime band coverage against a Vite dev server.
|
||||
`scripts/qa-sky-model-lighting.mjs` compares a lit fleet airliner
|
||||
(`public/models/fleet/aircraft/a320.glb`) against black and white environment
|
||||
maps in isolated scenes.
|
||||
`scripts/qa-sky-model-lighting.mjs` compares a lit `airplane.glb` against
|
||||
black and white environment maps in isolated scenes.
|
||||
|
||||
## Model atmosphere on Apple Metal
|
||||
|
||||
@@ -2694,53 +2442,6 @@ future driver fix restores the effect with no code change; iOS/iPadOS platform
|
||||
detection is the backstop for when no probe context can be created. Applied
|
||||
during viewer construction, before any tile builds a draw command.
|
||||
|
||||
## Render failure notice
|
||||
|
||||
When the globe stops drawing, the scene chrome says so instead of leaving a
|
||||
black globe behind working controls. `installRenderFailureNotice()`
|
||||
(`src/ui/renderFailure.js`) listens for Cesium's `renderError` and the canvas'
|
||||
`webglcontextlost`, logs the error, and publishes a notice that
|
||||
`RenderFailure.vue` shows as an alert dialog with focus on its first action.
|
||||
The app's viewer uses `showRenderLoopErrors: false`, so Cesium's dead-end
|
||||
"Rendering has stopped" panel no longer appears; `applicationViewerOptions()`
|
||||
keeps it by default for other callers.
|
||||
|
||||
- **Retry** restarts the render loop. The notice stays until a frame starts
|
||||
and ends without an error: Cesium raises `postRender` after a failed frame
|
||||
too.
|
||||
- **Reload**, after a lost context, reloads the page; the address keeps the
|
||||
view.
|
||||
- **Reset view** opens `appRootUrl()`, without the hash or `/share/<code>`
|
||||
path; the query, such as `?lang=`, is kept.
|
||||
|
||||
Pitfalls:
|
||||
|
||||
- An error thrown outside Scene's error handling (a frame listener, `onTick`,
|
||||
a resize) stops the loop without a `renderError`. The notice wraps the
|
||||
widget's `render()` and `resize()` and raises it as one, once even with
|
||||
`rethrowRenderErrors`.
|
||||
- A throwing listener leaves its Cesium event mid-raise, with listener changes
|
||||
only queued. The wrapper finishes those raises first (`finishRaise()` over
|
||||
`frameEvents()`), so a layer turned off or on meanwhile behaves.
|
||||
- A render error marks the loop stopped while its next frame is still queued.
|
||||
The wrapper keeps the loop marked running, so a Retry or a shown tab
|
||||
(`src/app/tools.js`) before that frame starts no second loop.
|
||||
- After a lost context, later render errors are only logged, so the notice
|
||||
keeps saying why. The listener neither prevents nor restores the loss.
|
||||
- When the globe draws again, focus goes back where it was, or to the globe's
|
||||
canvas, which the map's keys read as the map; the canvas is focusable only
|
||||
until focus leaves or the globe is pressed. Focus the user moved stays put.
|
||||
- The notice sits above floating panels such as the Québec camera viewer, and
|
||||
its buttons are 44 px tall on touch screens.
|
||||
|
||||
An inline conversation panel never reloads or navigates its host's page: it
|
||||
offers Retry after a render error only, and no notice after a lost context,
|
||||
where the panel runtime's own notice applies (`src/app/globePanelRuntime.js`).
|
||||
Its timer (`keepPanelRendering()`) draws only while the render loop is on and
|
||||
logs nothing itself, so each render error is logged once, by the notice. An
|
||||
error a timer frame throws is caught there: it has already been raised as a
|
||||
render error.
|
||||
|
||||
## Map Source control ownership
|
||||
|
||||
Map Source controls own chip listeners, source-state subscriptions and selection
|
||||
@@ -3588,8 +3289,7 @@ reads `NASA FIRMS · LIVE · updated …`. A source may instead answer the 24 ho
|
||||
to a set time, naming its end as `at` (epoch ms): the row then reads
|
||||
`NASA FIRMS · 24 h to MM-DD HH:MM UTC`, `getStats()` reports `at`, and
|
||||
`get_active_fires` says "in the 24 hours to" that time and reports it as
|
||||
`window_end`, and card and detail ages count back from that time instead of
|
||||
now. The dev proxy never sets `at`.
|
||||
`window_end`. The dev proxy never sets `at`.
|
||||
|
||||
The FIRMS CSV parser rejects impossible acquisition calendar dates before the proxy filters records to the trailing 24 hours, while retaining valid UTC acquisition times.
|
||||
FIRMS ages round to whole hours before choosing a unit, so a 47.6-hour age reads "2d", not "48h"; with detections filtered to 24 hours, this shows mostly in the `STALE · cached {age}` stats line.
|
||||
@@ -3906,22 +3606,20 @@ This is the current runtime/source-of-truth snapshot for the project.
|
||||
> 24.09 m aft (70 % of the model's rendered envelope, so inside it), 2.22 m
|
||||
> below centre (6 %), and 2e-9 m off the centreline.
|
||||
> - **The tracked trail attaches to REAL HULL, aft and below (2026-08-23):**
|
||||
> `FLEET_TRAIL_ANCHORS` (`src/data/fleetTrailAnchors.js`, generated by
|
||||
> `scripts/measure-fleet-trail-anchors.mjs`) holds, per fleet aircraft and
|
||||
> helicopter model and in RAW glTF coordinates (see the transform-chain entry
|
||||
> below), the point of the hull's CENTRELINE PROFILE (its centreline slice)
|
||||
> closest to the aft-belly AABB corner. It is NOT the corner: a bounding-box
|
||||
> corner is empty space (4.80 m off the nearest triangle on the airliner
|
||||
> asset this was first measured on, 6.14 m on the jet), so the trail ended in
|
||||
> mid air beside the aircraft. The head segment
|
||||
> `MODEL_TRAIL_ANCHOR_NATIVE` (`src/data/modelVisualAnchor.js`) holds, per GLB
|
||||
> and in RAW glTF coordinates (see the transform-chain entry below), the point
|
||||
> of the hull's CENTRELINE PROFILE (its y = 0 slice) closest to the aft-belly
|
||||
> AABB corner. It is NOT the corner: a bounding-box corner is empty
|
||||
> space, 4.80 m off the nearest triangle on `airplane.glb` and 6.14 m on
|
||||
> `jet.glb`, so the trail ended in mid air beside the aircraft. The head segment
|
||||
> is drawn from a point behind the aircraft to this anchor, so the anchor must
|
||||
> stay aft (83–96 % of each aft extreme) or the segment enters at the tail and
|
||||
> stops inside the fuselage. `fleetModels.test.mjs` reads the POSITION BUFFERS —
|
||||
> stops inside the fuselage. `modelScale.test.mjs` reads the POSITION BUFFERS —
|
||||
> real vertices and triangles, because accessor min/max cannot tell a corner
|
||||
> from a surface — and re-derives on-hull, aft, lowest-at-its-station,
|
||||
> on-centreline, and the construction itself. Re-measure, never re-guess.
|
||||
> - **The trail anchor rides CESIUM'S transform chain, not a hand-rolled one
|
||||
> (2026-08-23 regression fix):** `FLEET_TRAIL_ANCHORS` stores RAW
|
||||
> (2026-08-23 regression fix):** `MODEL_TRAIL_ANCHOR_NATIVE` stores RAW
|
||||
> glTF coordinates and `modelAnchorWorld()` assembles
|
||||
> `modelMatrix × components.transform × axisCorrection` — the same chain
|
||||
> `ModelSceneGraph` renders with, built from Cesium's own exported
|
||||
@@ -3956,7 +3654,7 @@ This is the current runtime/source-of-truth snapshot for the project.
|
||||
> ENVELOPE, and between them the drawn start slides along the segment so the
|
||||
> visible length grows CONTINUOUSLY from zero. Three earlier cuts are recorded
|
||||
> because their shape matters: testing segment LENGTH against the radius HID
|
||||
> real trail (58 m aft of an airliner's 34.41 m envelope gives a 33.98 m
|
||||
> real trail (58 m aft of `airplane.glb`'s 34.41 m envelope gives a 33.98 m
|
||||
> segment, suppressed, though ~23.6 m of it is open air); CLIPPING at the
|
||||
> envelope and keeping only what lay outside stopped a moving trail visibly
|
||||
> SHORT of the aircraft, since a bounding sphere encloses a lot of empty space
|
||||
@@ -4942,8 +4640,8 @@ down | auth-failed` (plus the unchanged `missing-key`/`unsupported`) with
|
||||
> Cesium entity carries no label graphic.
|
||||
> - **Phase 5 ISS and tracked-satellite labels:** The ISS path and large red
|
||||
> point remain native, while persistent `ISS` text is a one-entry moving
|
||||
> ambient-label host source. Its getter reads the point's own position, which
|
||||
> moves every frame, eliminating the former second ISS
|
||||
> ambient-label host source. Its getter reads the already-propagated point
|
||||
> cache, preserving the 1 Hz fleet epoch and eliminating the former second ISS
|
||||
> propagation. Satellite tracking continues to publish exactly one protected
|
||||
> tracked-lane card through `gevLabelModel` and `_trackedDisplayCached`; the
|
||||
> tracked entity is point-only. Tracking ISS suppresses the ambient entry and
|
||||
@@ -5132,9 +4830,8 @@ activeCameraCardEnabled: true })` may publish it through the retained
|
||||
> - **Flights 3D/cockpit hardening:** the destination-direction cue is an
|
||||
> inline SVG rather than a remotely loaded Material Icons ligature, so a
|
||||
> blocked or late font can no longer expand the literal word `navigation`
|
||||
> across the cockpit. A grounded aircraft stands its fleet model's scaled
|
||||
> clearance (the catalog's `clearance`, measured below the model's origin)
|
||||
> above the ground.
|
||||
> across the cockpit. The grounded `airplane.glb` belly offset is calibrated
|
||||
> to the current asset's measured Y-up bounds (`0.063` native units).
|
||||
> Contact Previous/Next and Live Signals aircraft handoffs now
|
||||
> re-seed the first-person camera anchor at the selected flight immediately;
|
||||
> bounded feed-correction smoothing remains scoped to the same aircraft.
|
||||
@@ -6032,26 +5729,7 @@ Helsinki, the Netherlands, Norway, South East Queensland, Rio de Janeiro
|
||||
Ontario — Toronto (TTC surface vehicles; the subway publishes no
|
||||
positions), MiWay, Hamilton HSR, Durham Region, YRT/Viva, Guelph,
|
||||
Burlington, Barrie and Kingston. Mode silhouettes
|
||||
identify buses, trolleybuses, trams, cable cars, subways, monorails, trains,
|
||||
funiculars, gondolas (aerial lifts), ferries and unclassified vehicles.
|
||||
|
||||
A vehicle's mode comes from, in order: the feed's fleet-number hint
|
||||
(`vehicleMode`, which knows a bus standing in on a streetcar route); the feed's
|
||||
correction for a route its own routes.txt mistypes (`routeCorrections`: Le
|
||||
Havre's funicular `8-FUNI`, which LiA's routes.txt types as a bus); the GTFS
|
||||
`route_type` of its route, when its record carries one as `routeType` (basic
|
||||
0–12 or extended 100–1799, each extended type taken as the basic mode it is a
|
||||
kind of: 200 coach is a bus, 101 high-speed is rail, 405 is a monorail); the
|
||||
feed's route-id hint (`routeMode`); and the feed's default (`transitModeFor`,
|
||||
`src/data/transitFeeds.js`). The GTFS-Realtime decoder carries no route type, so
|
||||
a record from the bundled proxy is classified by the hints alone. Route hints
|
||||
read from each operator's routes.txt cover Prague's trolleybus lines and Petřín
|
||||
funicular, and Bucharest, Cluj-Napoca and Turin's trams, trolleybuses and metro
|
||||
lines (and Turin's Sassi–Superga rack tramway, typed a funicular in its
|
||||
routes.txt). Le Havre's funicular is a correction, not read from routes.txt: its
|
||||
route hint and its correction both say funicular. Vilnius publishes no route ids in its positions, so
|
||||
its trolleybuses read as buses. Retained history classifies its epochs the same
|
||||
way, so a trail joins the live vehicle.
|
||||
identify buses, trams, subways, trains, ferries and unclassified vehicles.
|
||||
|
||||
Retained transit history uses the same outlier quarantine as live observations and
|
||||
preserves the active live bracket. Rejected reports do not publish route or mode
|
||||
@@ -6117,10 +5795,9 @@ External halos are 1/1.5 px in normal, 1.25/1.25 px in CRT and 2/2 px in sensors
|
||||
final unpadded display size. Each mode has an outline with a front (a nose, a
|
||||
bow or a chamfered cab) for a vehicle with a known course and a symmetric
|
||||
outline for one without, which would otherwise sit at rotation 0 and point
|
||||
north; a vehicle switches to the pointed glyph when a course arrives. Eleven modes, two sizes, three
|
||||
profiles and the two outlines cap the raster cache at 132 variants. Normal mode colours are bus `#5EF08A`,
|
||||
trolleybus `#B6F04A`, tram `#FFC24A`, cable car `#FF8F4A`, subway `#FF4538`, monorail `#FF6FB8`, rail
|
||||
`#D9A6FF`, funicular `#E8C98A`, gondola `#7FA8FF`, ferry `#5FD6FF`, and unknown
|
||||
north; a vehicle switches to the pointed glyph when a course arrives. Six modes, two sizes, three
|
||||
profiles and the two outlines cap the raster cache at 72 variants. Normal mode colours are bus `#5EF08A`, tram
|
||||
`#FFC24A`, subway `#FF4538`, rail `#D9A6FF`, ferry `#5FD6FF`, and unknown
|
||||
`#D8DDE5`. Selection keeps the mode colour. Sensors reuse each normal silhouette's exact
|
||||
outer path, filled opaque white without interior panel lines, with an opaque
|
||||
`#05080C` halo. Only the external padding grows; the unpadded silhouette remains
|
||||
@@ -7004,7 +6681,7 @@ silently demoting every later lookup for the session.
|
||||
`activeCameraCardEnabled` presentation option can publish the retained
|
||||
protected-card path. Disable tears the tier down completely.
|
||||
Coverage/viewshed semantics unchanged.
|
||||
- **Satellites**: every point moves every frame. Each satellite's SGP4 sample (position, velocity, gravity and its rate of change; `src/layers/satellites/motion.js`) is reckoned forward or back to the world time and turned into the Earth-fixed frame, so points glide at any play speed instead of stepping once a second. Core satellites take a fresh sample every second, or as soon as the world time is 30 s from their sample (fast play, a seek), which keeps every point within about 10 m of SGP4; Starlink extras are sampled round-robin, more per frame the faster time plays, and a point more than two minutes from its sample waits for the next one. Hidden points are not moved until shown. Orbit rings rotate about Earth's Z by ΔGMST every ~1s (exact inertial→ECEF compensation; no SGP4 re-runs); the tracked satellite propagates per frame with one shared epoch for dot/label/camera. Verified: ISS holds <1km perpendicular to its ring while tracked. Pass prediction (`src/data/satellitePass.js`; `getNextSatellitePass`, `getNextIssPass`) scans at 20s, bisects rise/set to ~0.2s, fits the peak with a parabola, and marks a pass visible when the satellite is outside a cylindrical Earth shadow while the observer's Sun is at or below −6°. `next_iss_pass` preserves its geometric-pass ordering and appends visibility metadata. General `next_satellite_pass` accepts a loaded NORAD ID or unambiguous name, searches 24 hours and optionally requires estimated visibility. These estimates do not account for weather or satellite brightness.
|
||||
- **Satellites**: orbit rings rotate about Earth's Z by ΔGMST every ~1s (exact inertial→ECEF compensation; no SGP4 re-runs); the tracked satellite propagates per frame with one shared epoch for dot/label/camera. Verified: ISS holds <1km perpendicular to its ring while tracked. Pass prediction (`src/data/satellitePass.js`; `getNextSatellitePass`, `getNextIssPass`) scans at 20s, bisects rise/set to ~0.2s, fits the peak with a parabola, and marks a pass visible when the satellite is outside a cylindrical Earth shadow while the observer's Sun is at or below −6°. `next_iss_pass` preserves its geometric-pass ordering and appends visibility metadata. General `next_satellite_pass` accepts a loaded NORAD ID or unambiguous name, searches 24 hours and optionally requires estimated visibility. These estimates do not account for weather or satellite brightness.
|
||||
- **Space Missions (30d)**: recent launches render as bounded shared-host, horizon-occluded mission markers using Launch Library 2 v2.3 detailed records. Enabling the layer selects the unified right-side Context panel's Space Missions mode and enables the required satellite layer. Before applying its temporary dense/hidden Satellite mode, Space Missions snapshots the complete standalone Satellite parameter set and exact enabled-layer set. Disabling Space Missions from either Context or the left Data Layers rail restores those parameters and the exact prior enabled state, so a Satellite layer that was already on stays on while a mode-owned dependency returns off; enabling Satellites by itself remains independent. Selecting a mission isolates its launch-to-orbit transfer and dashed satellite orbit, fills that same panel with navigation/details, and animates a small phase-colored marker along the exact displayed Cartesian samples. Marker hit testing drill-picks through photorealistic tiles so the depth-test-free tactical dot remains reliably selectable; its text is non-interactive shared-host presentation. The selected pad is the camera's zoom pivot: the overview remains centered on its launch site, wheel zoom approaches that site instead of drifting elsewhere, and camera pitch progressively changes from global nadir to an oblique close 3D view. Its protected shared-host callout remains visible and expands to include both mission and launch-site names. `FOCUS` flies directly to a 12 km oblique frame around the selected launch site and retains the same anchored zoom/orbit behavior. `REPLAY ASCENT` resets the selected marker at the pad, frames it from an oblique third-person angle, and follows it through the mission-specific compressed ascent directly into one orbital lap at the default `1×` rate. A live `0.25×`–`4×` slider changes ascent and orbit playback speed; adjusting it mid-replay preserves the current path position and historical mission timestamp. Re-entry/recovery cannot be inserted into replay. At orbit insertion the camera smoothly pulls back over the first fifth of the orbital replay and pitches to a globe-scale nadir view while continuing to target the moving replay point. Replay Cancel, mission navigation, deselection, layer disable, and data refresh all release camera ownership. Reconstructed paths are one continuous 128-sample geodetic curve: horizontal departure begins near zero while altitude rises quickly, then the climb progressively bends toward insertion without the former hard 120 km corner. Unmatched orbit fallbacks are smooth planar inclined rings rather than longitude/latitude ground-track curves. The panel lists disclosed payload names, types, operators/manufacturers, mass, multiplicity, and destination when supplied; an empty LL2 payload collection is shown as `CLASSIFIED / MULTI-PAYLOAD`. Launcher, spacecraft, and recoverable payload stages appear in a compact stage table with serial/flight/reuse details, recovery outcome/type, destination, and final coordinates when those records exist; an empty recovery collection omits the section. Stage recoveries with confirmed coordinates use those coordinates; return-to-launch-site records use the pad; downrange-only records receive an explicitly labeled estimated endpoint along the ascent azimuth. Available endpoints render as static 2 px dashed descent/recovery paths with a fixed final-position dot and an estimated atmospheric-interface segment when applicable. The ascent is geodetically densified above the ellipsoid toward the orbit's nearest insertion point, then rendered with `ArcType.NONE`, so it neither cuts through Earth nor separates from the marker. Because LL2 does not normally supply continuous ascent telemetry, pad-to-insertion paths without upstream trajectory samples are labeled `RECONSTRUCTED ESTIMATE` / `ASCENT ESTIMATE`; only supplied trajectory samples receive the replay wording. Selected-orbit framing fits the complete ring, rear-side linework uses normal scene depth occlusion, and current distance, speed, and callout data come only from a launch-year-validated satellite match. Both the loaded-catalog and active-TLE lookups read the launch year from the TLE international designator through one parser (`src/sources/tle.js`); a blank or malformed designator is an unknown year, which skips the year check rather than rejecting the match. Speed is the magnitude of the SGP4 inertial velocity vector at the same propagation epoch as position, displayed in km/s with km/h available as hover detail. Unavailable operator, site, launch-time, orbit, current-altitude, and speed values omit their detail rows instead of reserving panel space with placeholders. Newly launched payloads absent from the core operational groups use CelesTrak's cached active TLE feed as a lookup-only fallback; weak constellation-name matches are rejected. The replay callout maps compressed animation progress onto Launch Library's mission-relative timeline,Line truncated
|
||||
When no mission is selected, the Context panel presents a scrollable newest-first roster of every launch in the rolling window, including the smaller 5 px operator-colored marker, provider, and launch date. Hovering or keyboard-focusing a row shows four compact cyan corner brackets on both that roster row and its corresponding globe dot, rotates the globe at the current zoom to center it, and gives its label declutter priority without selecting it; the globe label remains unbracketed. Selecting a roster row invokes the same mission isolation and full-orbit framing as clicking its globe marker. The replay vehicle is one screen-space SVG/CSS HUD overlay rather than separate Cesium billboard, label, and reticle graphics. It is hidden during ordinary Focus and manual close views, where the standard selected launch-site label remains visible, and exists only while ascent replay is active. Its fixed pixel scale is shared by ascent and insertion, so camera range never resizes the rocket on screen before the phase boundary. Generic Launch Library pad names are reduced to their identifying suffix, and replay timestamps use a cyan state title over unprefixed white UTC date/time values. `REPLAY ASCENT` holds the unframed cyan/white rocket at the pad for a real-time `T−10` countdown, transitions through `LIFTOFF`, and attaches six tapered cyan/white ellipse waves directly below it from liftoff through insertion to convey thrust without adding scene geometry. While replay is active, the single start button is replaced by compact Play, Pause, and Cancel icon controls. Pause freezes countdown or mission time, vehicle/stage positions, camera target, labels, and thrust-wave animation; Play resumes from that exact frame, and Cancel releases replay camera ownership. The rocket and thrust group rotates from the path's live screen-space tangent, so its nose follows the visible ascent curve while the adjacent text remains upright. The camera begins as a close oblique launch chase, then smoothly widens between roughly 120 and 420 km vehicle altitude into a higher oblique context view that keeps the moving rocket targeted while exposing the ascent bend and orbit connection. At insertion the rocket/thrust glyph is replaced by the fixed-size cyan orbit dot, which the camera follows through the existing globe-scale orbit pullback. The chase camera limits per-frame yaw changes across heading wraps so it cannot abruptly cross in front of the vehicle and make ascent read in reverse; the replay-speed slider affects mission playback but not countdown duration.
|
||||
Mission world text has no native `LabelGraphics`: overview launch markers publish at most 48 ambient candidates for a 24-winner budget, while selecting a mission clears that overview source and publishes its launch-site callout, stage re-entry annotations, live/estimated payload-position readout, and orbit annotation as protected selected-lane entries. The source retains the exact former strings and colors. Static anchors reuse the Cartesian values used to build their mission geometry; the moving payload entry reads the layer's per-frame live-position cache; catalog-backed orbit annotation positions are cached in the same one-second matrix update that realigns the orbit primitive. Keyhole edge fade, horizon culling, final collision placement, and UI exclusion are owned by the shared host. Deselect restores the bounded overview, and refresh, disable, and destroy replace or clear both mission sources.
|
||||
@@ -7613,9 +7290,8 @@ pins and share links already supply complete boxes.
|
||||
normal four-samples-per-250-ms budget. Retry timestamps more than the maximum
|
||||
30-second backoff ahead of the current clock are ignored. A smaller rollback
|
||||
can retain a bounded retry delay of up to 30 seconds.
|
||||
- Commercial and military aircraft use the same high-level FLEET model regime: 2D billboards when zoomed out, optional glTF models when closer, controlled by the DISPLAY rail `3D` toggle and `Proximity` / `All` modes. Every model is one of the project's procedural fleet models (`public/models/fleet/`, MIT, 122 GLBs, about 2 MB in all; `public/models/README.md`), chosen per aircraft by `aircraftModelSpec` (`src/data/fleetModels.js`): the first of its ICAO type's callsign rules in `types.json` (`aircraft_callsign`) its callsign matches, else its type's entry, else the model of its `classifyAircraft` class for a type the map lacks, else its ADS-B emitter category's, else the default airliner. It is drawn LIFE SIZE: the type's published length over the model's built length (`src/layers/flights/modelSpec.js`, shared by Flights, Military Flights and Local ADS-B), and a grounded aircraft stands the model's scaled clearance above the ground. An airport ground vehicle (feed type `ZVEH`) is a fire truck (`FIRE…`), pushback tug (`PUSH…`, `TUG…`), van (`DEICER…`, `SQUID…`, `SQB…`) or else a car by its callsign, and a ground station (`GRND`) is an 11.8 m radio mast; these models (catalog category `other`) are drawn at their built size, and such a contact with no altitude stands on the ground instead of at the airborne default (`src/layers/flights/records.js`). `classifyAircraft` still picks the 2D icons. The whole set loads once, up front, when a layer that draws it starts (`loadFleetModelSet`, which logs `[Fleet] N/122 models (X MB) loaded in N ms`); models are built from those bytes, never fetched mid-session, and an aircraft stays its billboard until the set has loaded. Every aircraft model draws its mesh's own colours (`aircraftModelBlend`, `src/data/aircraftModelLighting.js`): Cesium's `ColorBlendMode.HIGHLIGHT` with white, in Flights, Military Flights and Local ADS-B alike; the tracked colour (cyan, or amber in Military Flights) and the emergency red multiply into the mesh, so its paint still shows. IR boost draws them flat white (`MIX` at `1.0`). Under NVG/FLIR (map preset or Cockpit vision) models render unlit flat-white at full alpha and scene fog is disabled (fog otherwise blacks out distant models with the globe hidden); state restores on exit.
|
||||
- Vessels, Transit and Rail draw fleet models under the same DISPLAY rail `3D` toggle, at the same altitude ceiling, caps and add/keep radii as the aircraft (`FLEET_MODEL_POLICY`, `src/layers/flights/policy.js`; `createFleetModelLayer`, `src/data/fleetModelInstances.js`). A vessel's model follows its AIS ship type and is drawn at its reported length and beam (AIS static dimensions A+B and C+D, now carried on vessel rows as `length` and `beam`), standing at the sea surface, heading by true heading then course. An aid to navigation is not a hull: a vessel record whose type names a buoy, beacon, mark, RACON or light, or whose MMSI is an aid-to-navigation station's (`99MIDxxxx`), is `isAidToNavigation` (`src/data/vesselTypes.js`) and is drawn as the set's `buoys` model of the IALA mark its type names (`aidToNavigationModel`, `src/data/fleetModels.js`: cardinal N/E/S/W, port or starboard hand, preferred channel by its side, safe water, isolated danger, special mark; a beacon, light, light vessel, RACON or reference point with no mark of its own is the special mark), else a red can (`DEFAULT_AID_MODEL`), at its built size and facing north whatever heading it reports. A type is an aid's when it is one of the 32 kinds an aid report names or uses a word for one (buoy, beacon, cardinal, safe water, RACON, lighthouse and so on); a vessel that works on aids (`Buoy Tender`, `Buoy Layer`) is not one. Its icon is a bone-white ringed disc (`AID_TO_NAVIGATION_STYLE`, `src/data/vesselLabels.js`) instead of a chevron, never rotated, and its card, selected card and HUD readout (`src/layers/vessels/cards.js`) show its kind alone, whole, in that colour: no speed or heading, since a fixed mark has neither. AISStream subscribes to `AidsToNavigationReport` (message 21) by default, and the server names each report's aid type code (0 to 31) as its kind (`aidToNavigationTypeName`: `20` is `Cardinal Mark N`) rather than reading it as a ship type, and its name runs on into the report's `NameExtension`. A virtual aid (the report's `VirtualAtoN` flag: a mark broadcast where nothing physical stands) carries `virtual: true` on its row, and on its vessel source record (the live-source contract's `virtual`, `src/sources/live/contract.js`, false unless a source says true; any source may set it). It keeps its disc but never gets a model (`vesselModelSpec` returns null for it, and the vessel layer gathers no model item for it), and its card and readout add `VIRTUAL` after its kind. `aisStreamRows` (`server/providers/vessels/ais-store.js`) puts every vessel ahead of every aid, so a capped request (the layer asks for 12,000 rows) never gives up a ship for a buoy. The `vessels_in_area` tool (and the brief built on it) counts ships only: it leaves aids out of its rows, counts, breakdowns and speed stats. A transit vehicle's model follows its route's GTFS `route_type` when its record carries one and that type is a kind of the vehicle's mode (`types.json` `transit.route_types`: 200 a coach, 101 a high-speed set, 106 a regional DMU, 710 a double-decker), else its layer mode (`transit.modes`: bus, trolleybus, tram, cable car, subway, monorail, rail, funicular, gondola or ferry; a vehicle of `unknown` mode keeps its icon); a fleet hint that overrules the route (a bus on a tram route) leaves the type unused. A monorail and a gondola are raised by their `transit.elevation` lifts. A train's model follows its rail category and mode (rail, metro or tram). The bundled GTFS-Realtime proxy carries no route type, so its vehicles draw their mode's model; a source that adds `routeType` reaches the finer ones. Both stand on the road or rail top they are drawn on, without the 1.5 m `GROUND_FLOOR_LIFT_M` their icons carry, and are painted in their mode or category colour where the model's `_TINT` attribute marks it. A vessel model is clipped at its waterline (one clipping plane per model, at the model origin's horizontal plane), so its hull below the water never shows through a plain globe, which Cesium does not depth-test models against. Under the NVG/FLIR styles (the Flights `irBoost` parameter) these models render unlit flat white, as aircraft models do. Each vehicle's icon stays the visual until its model is loaded, ready and placed; a model is picked as its icon is, and a vehicle drawn as its model keeps its selection card and its place in the detection overlay.
|
||||
- **Aircraft models skip Cesium's dynamic environment map (2026-10-24).** Flights, Military Flights and Local ADS-B pass `environmentMapOptions: AIRCRAFT_ENVIRONMENT_MAP_OPTIONS` (`{ enabled: false }`, `src/data/aircraftModelLighting.js`) to every drawn model. Otherwise each `Model` renders the sky around it and reads it back from the GPU (`readPixels`) when it is first placed, and again whenever it moves more than 1 km, the scene's atmosphere settings change, or (under sunlight dynamic lighting) an hour of scene time passes (`DynamicEnvironmentMapManager`, Cesium 1.138). The models are lit from Cesium's default spherical harmonics instead, with no specular environment map, so the sky's model-lighting cube map no longer reaches aircraft. `src/data/aircraftModelLighting.test.mjs` pins the option at every drawn model call; every model is built from the fleet set loaded up front, so there is no separate preload.
|
||||
- Commercial and military aircraft use the same high-level FLEET model regime: 2D billboards when zoomed out, optional glTF models when closer, controlled by the DISPLAY rail `3D` toggle and `Proximity` / `All` modes. Since 2026-08-16 (Hangar fleet) models are PER-CLASS: real CC-BY GLBs for light/bizjet/turboprop/widebody/helicopter/uav (`CLASS_MODEL_REAL` in `src/data/aircraftClass.js` — meters-baked, scale 1, per-model belly/radius; provenance in `public/models/README.md`), the shared `airplane.glb` for the remaining civilian classes, and the military layer maps weight classes (real GLBs / 747 heavies / `jet.glb` fastjets, per-model heading offsets, flat amber or emergency red). Textured civilians carry a HEAVY tint, not a light one: `MODEL_COLOR_BLEND_AMOUNT` is `0.94` in BOTH layers under Cesium's `ColorBlendMode.MIX`, so the class colour supplies 94% of the surface and the asset's own texture ~6%. The visual direction is clean light silhouettes with only a weak diffuse contribution from the approved textures, so liveries deliberately do NOT read. IR boost raises the blend to a full `1.0`. Under NVG/FLIR (map preset or Cockpit vision) models render unlit flat-white at full alpha and scene fog is disabled (fog otherwise blacks out distant models with the globe hidden); state restores on exit.
|
||||
- **Aircraft models skip Cesium's dynamic environment map (2026-10-24).** Flights, Military Flights and Local ADS-B pass `environmentMapOptions: AIRCRAFT_ENVIRONMENT_MAP_OPTIONS` (`{ enabled: false }`, `src/data/aircraftModelLighting.js`) to every drawn model. Otherwise each `Model` renders the sky around it and reads it back from the GPU (`readPixels`) when it is first placed, and again whenever it moves more than 1 km, the scene's atmosphere settings change, or (under sunlight dynamic lighting) an hour of scene time passes (`DynamicEnvironmentMapManager`, Cesium 1.138). With the class colour supplying 94% of the surface, the models are lit from Cesium's default spherical harmonics instead, with no specular environment map, so the sky's model-lighting cube map no longer reaches aircraft. `src/data/aircraftModelLighting.test.mjs` pins the option at every drawn model call; the two preload calls only warm the glTF cache and are never drawn.
|
||||
- The DISPLAY-rail `3D` toggle is the user-facing activation path for both aircraft layers. Their small approved GLBs build their render resources without Cesium's frame-spread job queue, preventing continuous Photorealistic 3D Tiles streaming from starving model readiness; model caps, tracking, camera, and fallback billboards are unchanged.
|
||||
- **The `3D` toggle DEFAULTS ON in `proximity` on a first run.** Proximity is itself the budget — models appear only below `MODEL_ALT_CEIL_M` and only for the nearest `MODEL_MAX` in view — so the default costs nothing at globe scale, and `all` remains a deliberate opt-in. A fresh boot runs NO layer-state restoration (`LayerStateCoordinator.start()` returns early with neither a share payload nor stored state), so four independent initializers decide what a first-run operator sees and must agree: `booleanOption('models3d', 'e', true)` in `src/data/layerState.js`, `_models3dEnabled = true` in BOTH flight layers, `this._models3dEnabled = true` in `src/ui/aircraftDisplay.js`, and the `active` / `visible` classes on `#models3d-toggle` / `#models3d-mode-row` in `index.html`. All four are pinned together in `src/data/layerState.test.mjs`. Explicit state still wins: because the codec omits default-valued options, `models3d: false` is now what travels in a link (`lo=…f.e.0`) and restores OFF at both aircraft layers. **Consequence for returning users:** a stored `gev:layer-state:v2` blob is a FULL options snapshot, so a session that wrote one before this change carries `models3d:false` and keeps 3D off until the operator flips it (or clears the key) — the durable snapshot is treated as the recipient's own state, by design.
|
||||
- **Consequence of the flip on the recorded tracked/fleet inversion:** the 150–800 km band where surrounding contacts draw as models while the SELECTED one is still a glyph is now what an operator sees WITHOUT arming anything. The inversion itself is unchanged and still deliberate (see `src/data/trackedModelRegime.js`); only its reachability changed.
|
||||
@@ -8199,7 +7875,7 @@ The Space Missions roster prioritizes data-rich records using available mission,
|
||||
When no live catalog track is available, the mission view marks the approximate orbital ring as `PROJECTED ORBIT` in purple and renders the ascent-to-insertion transfer in green; catalog-backed satellite orbits remain cyan.
|
||||
|
||||
The replay vehicle is a smaller solid cyan silhouette without the former orange flame; its initial pad anchor uses photoreal terrain, globe height, or launch elevation fallback so it remains above the surface during tile loading. Replay begins at a close launch-complex range so pad detail remains visible before the camera widens into the ascent context view. Its initial camera heading is perpendicular to the ascent/orbit direction for a profile view, then eases into tangent tracking. Small screen-space reprojection changes are damped for the animated marker on both ascent and orbit, while large camera or phase changes snap to the authoritative path position.
|
||||
Space Missions keeps the Satellite layer available for catalog/TLE matching but suppresses its standalone fleet points and orbit rings. While those visuals are hidden, their sampling, per-frame point moves, and one-second orbit-matrix rotation are suspended; selected mission telemetry continues to propagate independently. The selected mission's live or estimated satellite marker uses fractional wall-clock time, so it moves continuously rather than creating a once-per-second position discontinuity and one-frame photoreal globe LOD pulse.
|
||||
Space Missions keeps the Satellite layer available for catalog/TLE matching but suppresses its standalone fleet points and orbit rings. While those visuals are hidden, their per-frame dense propagation, one-second core point-buffer rewrite, and one-second orbit-matrix rotation are suspended; selected mission telemetry continues to propagate independently. The selected mission's live or estimated satellite marker uses fractional wall-clock time, so it moves continuously rather than creating a once-per-second position discontinuity and one-frame photoreal globe LOD pulse.
|
||||
|
||||
During ascent replay, the camera, Cesium callbacks, and HTML vehicle overlay share one replay sample per rendered frame. The tracked overlay is projected directly from that shared position instead of applying a second screen-space lag filter, preventing the vehicle and globe from repeatedly advancing and snapping back.
|
||||
|
||||
@@ -8541,63 +8217,24 @@ Wind supports selectable GFS/IFS models with persisted model choice. Each fetch
|
||||
Weather combines the GFS/ECMWF surface forecast with three observation layers:
|
||||
NOAA Rain radar (CONUS), Satellite clouds (regional GOES or slower global infrared),
|
||||
and 15-minute lightning density (Americas and Pacific). One transient observed
|
||||
clock moves all enabled observation rows with the world clock; the world
|
||||
timeline is the only time control. Each product's advertised times span time on
|
||||
that timeline but are not marks on it: the timeline marks only events (launches,
|
||||
and earthquakes of magnitude 6 and up while that layer is on), so its arrow keys
|
||||
step to those, or along the track where there are none. A mark may carry a
|
||||
label (a launch's mission name, cut to 27 characters in capitals as the mission
|
||||
index shows names; an earthquake's magnitude and place). Marks merge on their
|
||||
whole label; one longer than 60 characters (graphemes, so an emoji is never
|
||||
split) then shows cut, ending in `…`, and a label is never wider than the
|
||||
track. A zoom or
|
||||
a pan clears a hovered label. The pointer within
|
||||
6 px of a tick shows its label over the track, and a tap there goes to the
|
||||
mark's time exactly; a tap elsewhere goes to the time under it and a drag
|
||||
scrubs. With the handle on a mark, its label follows the time in the handle's
|
||||
`aria-valuetext` and shows over the track while the handle has focus. The
|
||||
coverage contract is in [APPLICATION.md](./APPLICATION.md). A weather row's `step`
|
||||
param goes to the previous or next weather frame. Each product selects its
|
||||
nearest frame at or before the world time, never a future frame or an
|
||||
interpolation.
|
||||
clock drives all enabled observation rows. Earlier/Later use the sorted union of
|
||||
non-suspended products' advertised times; each product selects its nearest frame
|
||||
at or before the requested UTC time, never a future frame or an interpolation.
|
||||
The maximum gap is 30 minutes for radar, regional infrared and lightning, and
|
||||
3 hours for hourly global infrared, but never less than one and a half of the
|
||||
feed's median frame spacing over its latest frames, so a regional feed whose
|
||||
frames come an hour apart holds each for 90 minutes; the latest frame reads late
|
||||
after the same floor (20 minutes for radar and regional infrared, 45 for
|
||||
lightning, 4 hours for global infrared). A missing eligible frame hides that product
|
||||
3 hours for hourly global infrared. A missing eligible frame hides that product
|
||||
and reports the gap; rows show actual displayed UTC time with synced/nearest labels.
|
||||
Manifest rollover preserves the requested history time, even when frames expire.
|
||||
|
||||
Live follows each product's own newest frame, so its timestamps can differ.
|
||||
The timeline's Play runs the world clock at the chosen speed and stops at each
|
||||
product's next frame until that frame loads (for at most eight seconds), so
|
||||
every frame shows. Hidden documents, reduced motion and unavailable imagery
|
||||
hosts suspend products. Re-enabling or
|
||||
Latest follows each product's own newest frame, so its timestamps can differ.
|
||||
Playback advances two seconds after all products settle, wraps, and stops with
|
||||
fewer than two union times or when all products are suspended. Hidden documents,
|
||||
reduced motion and unavailable imagery hosts suspend products. Re-enabling or
|
||||
restoring a host joins the retained history target. The wind row explicitly says
|
||||
“Forecast · does not follow history” while the observed clock is in history mode.
|
||||
The Weather panel's frame time shows the newest frame and its age while live
|
||||
(each product's newest, suspended products included), and the timeline time
|
||||
alone at a past time.
|
||||
The catalog exposes `weatherClock` and destroys it with its lifetime signal. It
|
||||
has no time controls of its own: `weatherClock.world` is the world clock it
|
||||
follows, where time moves. A weather row's `play`, `latest` and `step` params
|
||||
move that world clock as the timeline does: `play` runs at the speed chosen on
|
||||
the timeline (the world clock keeps its rate while paused and live, and the
|
||||
timeline's speed selector shows the clock's rate, or the nearest offered speed
|
||||
for a rate a host set), from the start of the covered time when live or at its
|
||||
end, and does nothing while every product the row shows is suspended or while
|
||||
there is no covered time. A weather layer built without a
|
||||
shared clock builds one of its own on the page's world clock when first
|
||||
enabled (`createWeatherClock({ shared: false })`) and keeps it for its
|
||||
lifetime; destroying it leaves the world's time where it is, and enabling it
|
||||
after that does nothing, and only the last
|
||||
shared weather clock destroyed sends the world live. Neither has a frame replay
|
||||
of its own.
|
||||
The catalog exposes `weatherClock` and destroys it with its lifetime signal.
|
||||
|
||||
Source, opacity and coverage navigation live in the WEATHER panel's cards, not
|
||||
in the rows; the panel shows the frame time and has no time controls. The
|
||||
viewer clock is untouched.
|
||||
Source, opacity, coverage navigation and transport controls live in the WEATHER
|
||||
panel's cards and timeline, not in the rows. The viewer clock is untouched.
|
||||
Observation layers are off by default; share/persist includes appearance and product, while history
|
||||
is transient and shared links open latest. Rendering retains the previous frame
|
||||
when an incoming frame fails; no eligible historical frame instead hides imagery.
|
||||
|
||||
@@ -83,21 +83,6 @@ menu.onChoose = (code) => switchLocale(code); // switched in place and stored (i
|
||||
|
||||
`switchLocale` does what a choice in the globe picker does, and returns `'unchanged'` for the active language. `onLocaleChange` tells the application's own menu to mark the new language.
|
||||
|
||||
The same entry exports the translator Core's text goes through (`t`, `tn`, `msg`, `translateKnown`, `formatNumber`, `formatDate`, `formatRelativeTime`), so the application's own text follows the same language. To show its own wording for one of Core's messages, it overrides it by the catalog key, in English and in any locale it words:
|
||||
|
||||
```js
|
||||
import { setMessageOverrides } from '<core package>/i18n';
|
||||
|
||||
setMessageOverrides({
|
||||
en: { 'Download scene JSON': 'Download scene file' },
|
||||
de: { 'Download scene JSON': 'Szenendatei herunterladen' },
|
||||
});
|
||||
```
|
||||
|
||||
Keys are the catalogs' (`context|text` for a context, the English plural for `tn`). A `t` entry is text; a `tn` entry is `{one, other, …}` forms, chosen by the plural rules of the locale they are worded in; an entry of the other kind is ignored. An overridden message shows the application's text in the active locale, else its English: an English-only override replaces Core's translations of that message too, so word every locale that should keep its own. Each call replaces the earlier overrides and `{}` removes them.
|
||||
|
||||
Call it before views render. `onLocaleChange` listeners word their text again at once, but a view already drawn keeps its text until it next renders. Import it from the `./i18n` entry only: a second copy of the module (a path alias into Core's sources) keeps its own overrides and language.
|
||||
|
||||
## Rules
|
||||
|
||||
- **Pass literals.** `t('Close')`, not `t(label)` (unless `label` holds a `msg()` value) and never a template literal with `${…}`.
|
||||
|
||||
@@ -148,7 +148,6 @@ probability of about 255 in 256 (many of them already fail as base64url).
|
||||
| 27 | `styleParams` | LEN | style | packed `(token, value × 100)` pairs |
|
||||
| 28 | `worldTime` | epoch | none (live) | zigzag seconds since 2026-01-01T00:00:00Z |
|
||||
| 29 | `library` | LEN | none | Data Library ledger tokens, two base-36 characters each, concatenated |
|
||||
| 30 | `scopeOpacityPct` | uint | 100 | 0–100, how strongly the scope mask shows |
|
||||
|
||||
Field 1 absent means `thing` when field 13 is present and `place` otherwise,
|
||||
so v1 never writes it; a later view kind writes its code.
|
||||
@@ -171,10 +170,6 @@ tokens in `src/library/ledger.json` (the long form's `lib=ab.cd`). A share
|
||||
without it switches no library entry on; the recipient's added entries stay
|
||||
in their panel, off. Tokens this build does not know are skipped.
|
||||
|
||||
Field 30 is how strongly the scope mask shows, from the Visual panel's
|
||||
Opacity slider (the long form's `sco`). It is written only below 100, so a
|
||||
link made before it, which opens at full opacity, keeps its code.
|
||||
|
||||
Flag bits (field 9): 0 `bloom`, 1 `sharpen`, 2 `hudVisible`, 3
|
||||
`celestialRing` (each defaulting by style), 4 `scopeEnabled` (default on).
|
||||
|
||||
|
||||
@@ -153,9 +153,9 @@ a feature supplying only its accent.
|
||||
`t()`, which follows the locale as the view renders; the views carry no
|
||||
`data-i18n*` markers, which `npm run i18n` rejects in a template. Shared builders become components first and
|
||||
keep their imperative entry points until their owners are components:
|
||||
the rail cards and card blocks (`src/ui/rail/`) render through
|
||||
Vue's synchronous `render()` behind `createRailCards()` and
|
||||
`createRailCardBlocks()`. Order: Scenes (done: `SceneOptions` and
|
||||
the rail cards, card blocks and timeline (`src/ui/rail/`) render through
|
||||
Vue's synchronous `render()` behind `createRailCards()`,
|
||||
`createRailCardBlocks()` and `createRailTimeline()`. Order: Scenes (done: `SceneOptions` and
|
||||
`SceneShotList` over the `gev-scenes` store; the panel's buttons, status
|
||||
line, progress bar and the runtime chip render the panel SceneControls
|
||||
presents there, and the buttons and file chooser call its actions), Weather (done: the owner
|
||||
|
||||
@@ -4,7 +4,7 @@ Catalog: `src/i18n/locales/zh-Hant.json`. General rules: [TRANSLATORS.md](../TRA
|
||||
|
||||
`zh-Hant` is the Simplified Chinese catalog (`zh-CN`, credited to abiyeenzo, #914, and EDIT-Aeterna, #979) written in Traditional characters with OpenCC character conversion. Wording and terminology are the same as in `zh-CN`: change a term in [zh-CN.md](./zh-CN.md) and the `zh-CN` catalog first, then convert. Only the script differs.
|
||||
|
||||
After `s2tw` conversion, fix the characters OpenCC picks wrongly: 年曆史 → 年歷史, 下遊 → 下游, 並道 → 併道, 發佈 → 發布, 公佈 → 公布, 控制檯 → 控制台, and 周 → 週 where it means a week (1 週/秒).
|
||||
After `s2tw` conversion, fix the characters OpenCC picks wrongly: 年曆史 → 年歷史, 下遊 → 下游, 並道 → 併道, 發佈 → 發布, 公佈 → 公布.
|
||||
|
||||
## Style
|
||||
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
with the sources: src/materialSymbolsSubset.test.mjs fails when a source
|
||||
names a glyph this list is missing, because a missing glyph does not draw
|
||||
a box — the ligature never forms and the element renders the literal word. -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20,400,0,0&icon_names=adjust,align_horizontal_left,align_horizontal_right,arrow_drop_down,arrow_forward,arrow_left,arrow_right,bolt,bug_report,category,chevron_left,chevron_right,close,close_fullscreen,dark_mode,delete,directions_boat,draw,east,eco,egg_alt,filter_list,flare,flight,handyman,layers,layers_clear,light_mode,local_fire_department,location_on,more_horiz,movie,my_location,navigation,normal,on,open_in_full,palette,partly_cloudy_day,pause,pentagon,pest_control,pets,play_arrow,public,question_mark,radar,radio,radio_button_checked,radio_button_unchecked,raven,restart_alt,right_panel_close,right_panel_open,rocket_launch,satellite_alt,search,set_meal,settings,skip_next,skip_previous,streetview,timeline,traffic,tune,umbrella,upload_file,videocam,view_in_ar,visibility,visibility_off,water_drop,zoom_in_map" rel="stylesheet" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20,400,0,0&icon_names=adjust,arrow_drop_down,arrow_forward,arrow_left,arrow_right,bolt,bug_report,category,chevron_left,chevron_right,close,close_fullscreen,dark_mode,delete,directions_boat,draw,east,eco,egg_alt,flare,flight,handyman,layers,layers_clear,light_mode,local_fire_department,location_on,more_horiz,movie,my_location,navigation,normal,on,open_in_full,partly_cloudy_day,pause,pentagon,pest_control,pets,play_arrow,public,question_mark,radar,radio,raven,right_panel_close,right_panel_open,rocket_launch,satellite_alt,search,set_meal,settings,skip_next,skip_previous,streetview,timeline,traffic,tune,umbrella,upload_file,videocam,view_in_ar,visibility,visibility_off,water_drop,zoom_in_map" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="gev-view" data-gev-view="scene-chrome"></div>
|
||||
|
||||
@@ -298,7 +298,6 @@
|
||||
},
|
||||
"./sources/rail-service": "./src/sources/railService.js",
|
||||
"./layers/rail/source": "./src/layers/rail/source.js",
|
||||
"./layers/rail/paths": "./src/data/railPaths.js",
|
||||
"./sources/map-features": "./src/sources/overpassFeatures.js",
|
||||
"./server/providers/mapillary": {
|
||||
"node": "./server/providers/mapillary.js"
|
||||
|
||||
+17
-36
@@ -1,39 +1,20 @@
|
||||
# Bundled 3D Models
|
||||
# Bundled 3D Model Attribution
|
||||
|
||||
Every 3D model shipped in this directory is one of the project's procedural
|
||||
fleet models under [`fleet/`](fleet/): aircraft, helicopters, ships, buoys,
|
||||
transit and rail vehicles, and a few other airborne and ground objects. They
|
||||
are generated from code by the project maintainers, contain no third-party
|
||||
artwork, and are covered by the repository's [MIT license](../../LICENSE)
|
||||
like the source code.
|
||||
The model files in this directory are third-party visual assets. They are not
|
||||
covered by the repository's MIT source-code license; each remains available
|
||||
under the license listed below.
|
||||
|
||||
| Path | What it holds |
|
||||
|---|---|
|
||||
| `fleet/catalog.json` | Every model by name: its file, category, built length, beam and bounds (metres, in Cesium's Z-up model frame: x along the length with the nose toward −X, y across the beam, z up), the clearance below its origin, and its 2D icon class. |
|
||||
| `fleet/types.json` | Which model draws which vehicle, and at what length: ICAO aircraft and helicopter type codes, ADS-B emitter categories, AIS ship-type and length ranges, GTFS route types, transit modes and rail categories, with the defaults. |
|
||||
| `fleet/aircraft/`, `fleet/helicopters/`, `fleet/ships/`, `fleet/buoys/`, `fleet/transit/`, `fleet/other/` | The models, one GLB each (EXT_meshopt_compression and KHR_mesh_quantization, vertex colours, no textures). |
|
||||
| File | Original work and creator | Source | License | Project modifications |
|
||||
|---|---|---|---|---|
|
||||
| `airplane.glb` | “boeing 747” by [zairiq-123](https://sketchfab.com/zairiq-123) | [Sketchfab model](https://sketchfab.com/3d-models/boeing-747-9b16672038ba48f98e6d80a159044ed9) | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) | Substantially modified and optimized for God's Eye View, including geometry/material simplification and coordinate/orientation preparation. The former 24× runtime calibration is baked into the mesh; location, rotation, and scale are applied; the bounding-box centre is at the origin; and the model uses glTF +Y-up with its nose toward local −X. Geometry remains uncompressed so the aircraft asset does not compete with photogrammetry for Draco worker capacity. |
|
||||
| `jet.glb` | “Private Jet” by [Nick the Name](https://sketchfab.com/Nick_The_Name) | [Sketchfab model](https://sketchfab.com/3d-models/private-jet-cbdd1de6ced9461e950eafaa302cc82b) | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) | Repackaged as a glTF binary for the project's military-flight visualization. Imported hierarchy transforms are baked into the meshes; location, rotation, and scale are applied; the bounding-box centre is at the origin; and the model uses meter scale, glTF +Y-up, and the shared nose −X convention. Existing materials are preserved. |
|
||||
| `bell206.glb` | “Bell 206 JetRanger” by [terran4627](https://sketchfab.com/terran4627) | [Sketchfab model](https://sketchfab.com/3d-models/bell-206-jetranger-d2f7ba1d671549d4b26aaf834139a1dd) | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) | Optimized for God's Eye View: geometry/material simplification, textures resized to 256 px WebP, orientation/scale vertex-baked to real-world meters (Y-up, nose −X), origin centered. |
|
||||
| `c172.glb` | “Cessna 172” by [e737](https://sketchfab.com/e0057537) | [Sketchfab model](https://sketchfab.com/3d-models/cessna-172-64cddaee5aff470682659a8c08525046) | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) | Optimized for God's Eye View: geometry/material simplification, textures resized to 256 px WebP, orientation/scale vertex-baked to real-world meters (Y-up, nose −X), origin centered. |
|
||||
| `citation2.glb` | “1990 Cessna Citation, Texture Detailed, Exterior” by [BlenderCommunityHead](https://sketchfab.com/aboodgoudagad) | [Sketchfab model](https://sketchfab.com/3d-models/1990-cessna-citation-texture-detailed-exterior-a78839624fe64900a8352cb23462350a) | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) | Optimized for God's Eye View: geometry/material simplification, textures resized to 256 px WebP, orientation/scale vertex-baked to real-world meters (Y-up, nose −X), origin centered. |
|
||||
| `mq9.glb` | “MQ-9” by [IProZenoN](https://sketchfab.com/IProZenoN) | [Sketchfab model](https://sketchfab.com/3d-models/mq-9-fabe963feb354c5584b51f9c470c3f7e) | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) | Optimized for God's Eye View: geometry/material simplification, textures resized to 256 px WebP, orientation/scale vertex-baked to real-world meters (Y-up, nose −X), origin centered. |
|
||||
| `b789.glb` | “Boeing 787-9” by [Nobilis 2](https://sketchfab.com/nobilishornet2) | [Sketchfab model](https://sketchfab.com/3d-models/boeing-787-9-b6711e2e698e4e469675c1154a50b7a3) | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) | Optimized for God's Eye View: geometry/material simplification, textures resized to 256 px WebP, orientation/scale vertex-baked to real-world meters (Y-up, nose −X), origin centered. |
|
||||
| `atr72.glb` | “ATR 72 - 600” by [Oyan3D](https://sketchfab.com/oyan3D) | [Sketchfab model](https://sketchfab.com/3d-models/atr-72-600-1e1a7186f7444d288675262fcee44744) | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) | Optimized for God's Eye View: geometry/material simplification, textures removed with dominant material colors baked into PBR factors (flat “abstracted” style), orientation/scale vertex-baked to real-world meters (Y-up, nose −X), origin centered. |
|
||||
|
||||
How the layers use them:
|
||||
|
||||
- Each layer draws a vehicle with the model its type selects, scaled to the
|
||||
type's published length (a vessel to its reported AIS length and beam), so
|
||||
every model is drawn life size. Ground objects, balloons and obstacles in
|
||||
the `other` category are drawn at the size they are built.
|
||||
- An aid to navigation (a vessel record whose type names a buoy, beacon, IALA
|
||||
mark, RACON or light, or whose MMSI is an aid station's `99MIDxxxx`) is
|
||||
drawn as the `buoys` model of its mark (cardinal north, east, south or west,
|
||||
port or starboard hand, safe water, isolated danger, special mark), a red
|
||||
can when its type names no mark, at its built size and with no heading
|
||||
(`vesselModelSpec`, `aidToNavigationModel`).
|
||||
- An aircraft model's origin is its axis; it stands that model's scaled
|
||||
clearance above the ground. A ship's origin is its waterline, and a transit
|
||||
or rail vehicle's the road or rail top (a monorail or gondola is raised by
|
||||
its `transit.elevation` entry, for a source that names those modes).
|
||||
- Transit and rail models carry a `_TINT` vertex attribute that marks where
|
||||
the route or category colour is painted.
|
||||
- The whole set loads once, up front, when a layer that draws it starts; no
|
||||
model is fetched while a session runs.
|
||||
|
||||
`src/data/fleetModels.js` reads the set and chooses each vehicle's model;
|
||||
`src/data/fleetModels.test.mjs` checks that every model `types.json` names is
|
||||
in the catalog and on disk, and that no GLB exceeds 3 MB.
|
||||
CC BY 4.0 permits sharing and adaptation, including commercial use, provided
|
||||
appropriate credit is retained, the license is linked, and modifications are
|
||||
identified. These credits do not imply endorsement by the original creators.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loaded 100 of 502 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user