Merge branch 'main' into feat/cctv-model-spec-enrichment

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
Sameh Khamis committed 2026-10-15 19:08:55 -07:00
commit a4888d7846
24 files changed
+623 -14

No files matched your search

+36 -3
View File
@@ -339,7 +339,12 @@ Cyclones combine fixed NHC/CPHC status and summary GIS endpoints through a bound
same-origin provider. The status endpoint lacks browser CORS; each installation
fetches keyless public sources at runtime, with no centralized ingestion service.
The five-minute snapshot covers Atlantic and eastern/central North Pacific only.
Current positions, advisory issue time and position time stay distinct. Tracks,
Current positions, advisory issue time and position time stay distinct. NHC may
release an advisory before its nominal time (routinely up to about 30 minutes,
rarely about an hour), so advisory issue and position times up to and including
90 minutes ahead of the server clock are accepted; 90 minutes is this app's
tolerance, not an NHC limit. Later, non-canonical, or more than 12-hour-old
times still reject the status snapshot. Tracks,
forecast lead-hour points and cones render only when all source parts match the
status advisory; a newer status shows its position with geometry pending rather
than relabeling older geometry. The cone represents forecast center uncertainty,
@@ -872,7 +877,13 @@ addition to the locally accumulated trail, never a promise of complete coverage.
Snapshot coverage, completeness and freshness are separate fields. A partially
admitted aircraft snapshot retains absent contacts for up to five minutes before
the usual missed-poll eviction. Unknown snapshot times remain unknown in stats. An invalid nonempty
the usual missed-poll eviction. An OpenSky row with an ICAO24 address but null
longitude and latitude is a contact without a position, not a malformed row: it
leaves the snapshot complete and is listed in `retainIds`, so civil flights
retain that aircraft under the same five-minute bound, unless it is likely
landed, while other absent aircraft fade and leave on the missed-poll limits. A
nonempty snapshot with no positioned rows is still malformed. Military readsb
admission is unchanged. Unknown snapshot times remain unknown in stats. An invalid nonempty
snapshot retains the previous display. Empty vessel refreshes retain the existing
first-connect grace and warm-data behavior. Known source failures have bounded
messages; arbitrary HTTP response bodies are not surfaced as diagnostics.
@@ -1274,6 +1285,28 @@ services, local voice and standalone key setup. Importing them starts no network
acquisition. Browser layer lifecycle, rendering and voice execution stay in their
existing modules.
## Web app install icons
`public/manifest.webmanifest` declares the installable app: standalone display,
`#0a0a0f` background and theme, and 192/512 icons plus a 512 maskable variant.
`index.html` links the manifest, a 180px `apple-touch-icon` and a 32px PNG
favicon; the existing SVG favicon still serves the browser tab. Safari ignores an
SVG apple-touch-icon and substitutes a page screenshot, so the raster is what
makes an installed icon appear at all.
Manifest `start_url`, `scope` and icon paths are relative to the manifest, and
Vite rewrites the `index.html` links under `base`, so a build served from a path
prefix installs and launches inside that prefix.
`node scripts/generate-icons.mjs` renders every raster from `public/logo.svg` and
writes `public/icons/`. Output is committed, so building needs no image
toolchain; re-run the script after changing the logo. Ordinary icons inset the
mark to 78% of the canvas and the maskable variant to 54%, keeping it inside the
circle Android may crop to. `src/tooling/webAppIcons.test.mjs` checks the links,
the manifest fields and that each committed PNG is square at its declared size.
It also builds the install links under `base: '/example/'` and asserts every
emitted link and manifest URL stays inside that base.
## Local build preview
After `npm run build`, `npm run preview` serves the built app with the same data
@@ -3232,7 +3265,7 @@ its criteria cannot be silently ignored.
| CCTV | Austin + Caltrans (CA) + TfL London + Ontario 511 + Fintraffic (FI) + DriveBC (BC) + TxDOT (TX) + Estonia (Tallinn, Tarktee) + Live Traffic NSW + Open Calgary Open Data + Statens vegvesen (NO, stills + live HLS) + King County (WA) Open Data + WSDOT (WA) Open Data + NYC DOT TMC (opt-in) + City of Toronto RESCU + Catalonia (Servei Català de Trànsit) + Street View fallback | `src/data/cctv.js` | `/api/cctv` | 10s (active) |
| Radio | Radio Browser (public-domain station directory) | `src/data/radio.js` | `/api/radio/stations`, `/api/radio/click/:uuid` | 45 min directory refresh |
| Transit 🚌 | Operator GTFS-Realtime VehiclePositions (7 keyless regions, `src/data/transitFeeds.js`) | `src/layers/transit/` via `src/app/layers/transit.js` | `/api/transit` | 15s (poll + delayed playback) |
| Bikeshare 🚲 | GBFS (Lyft + BCycle + Social Bicycles + PBSC incl. Bike Share Toronto + Urban Sharing, Entur, JCDecaux NO) | `src/data/bikeshare.js` | `/api/gbfs` | 60s |
| Bikeshare 🚲 | GBFS (Lyft + BCycle + Social Bicycles + PBSC incl. Bike Share Toronto and Bike Itaú (Brazil) + Urban Sharing, Entur, JCDecaux NO) | `src/data/bikeshare.js` | `/api/gbfs` | 60s |
| Directions 🧭 | OSRM on FOSSGIS servers (OpenStreetMap) | `src/data/directions.js` | `/api/route` (`steps=1`) | on placement / mode change |
| Datacenters ▣ | OSM extract (bundled) | `src/data/localLayers.js` | — | static |
| Dams ▰ | OpenInfraMap/OSM extract (bundled) | `src/data/localLayers.js` | — | static |