mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-11 05:42:22 +00:00
Merge main into feat/transit-icon-heading
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
commit
dfd4ba04f8
149 files changed
+43917
-668
No files matched your search
+185
-64
@@ -134,7 +134,7 @@ existing batch size and one bounded upstream chunk. Larger requests return HTTP
|
||||
413 before disk-cache loading or upstream work; malformed coordinates remain 400.
|
||||
The browser continues to split larger workloads into sequential 64-point requests.
|
||||
|
||||
Traffic holds continuous rendering only while enabled with animated dots or a pulsing jam heat-line. Empty and high-altitude views release the hold. A failed load leaves no hold only when no animated content is retained: if an earlier load succeeded, ingestion keeps its animated points and the hold stays. Population admission reacquires the hold and resets animation time. Clear, disable, and destroy also release it. Retained fading dots keep animation alive until they are removed. When the last point leaves mid height correction, the correction is settled at once, before the hold is released, so it cannot keep the layer reporting loading.
|
||||
Traffic holds continuous rendering only while enabled with shown animated dots, a pulsing jam heat-line, or road lines whose geometry is still building (replaced lines stay drawn until their successors are ready, so a rebuild never blinks); hidden dots (`dots` off) and settled flow lines hold nothing. Empty and high-altitude views release the hold. A failed load leaves no hold only when no animated content is retained: if an earlier load succeeded, ingestion keeps its animated points and the hold stays. Population admission reacquires the hold and resets animation time. Clear, disable, and destroy also release it. Retained fading dots keep animation alive until they are removed. When the last point leaves mid height correction, the correction is settled at once, before the hold is released, so it cannot keep the layer reporting loading.
|
||||
|
||||
Shared distance checks use `src/geoDistance.js` with `{ lat, lon }` points and
|
||||
the IUGG mean spherical Earth radius, 6371.0088 km. `greatCircleKm` is
|
||||
@@ -250,7 +250,6 @@ style listeners. Synthetic fixtures cover privacy, acquisition, rendering,
|
||||
selection and card placement. Deterministic operation checks run in `npm test`;
|
||||
wall-clock budgets run in `npm run test:perf`. All 11 locales are supported.
|
||||
|
||||
|
||||
## Prohibited & Restricted Airspace — FAA special-use airspace
|
||||
|
||||
The Infrastructure group includes **Prohibited & Restricted Airspace** (`drone-airspace`,
|
||||
@@ -742,6 +741,12 @@ geographic seams and releases on disable; forecast animation is not implied.
|
||||
NHC movement speed is converted from miles per hour to rounded knots by the
|
||||
proxy; advisory wind intensity is already in knots and is passed through.
|
||||
Cyclone markers, labels, tracks and cones are hidden beyond the horizon on every map source.
|
||||
Explicitly selecting a storm publishes one shared `weather-cyclones` record:
|
||||
advisory provenance, source position, the English classification name and,
|
||||
while the proxy serves its cache, the cache fetch time. The automatic row
|
||||
selection does not claim shared context; refreshes do not re-announce it.
|
||||
Clear or disable reports `deliberate`; a storm leaving the feed reports
|
||||
`evicted`, with no fallback.
|
||||
Consult the linked official advisory for safety decisions.
|
||||
|
||||
Voice and HUD snapshots reuse the existing feedState classifier. Analyst follow-ups retain their original data provenance; current-view results append provenance without replacing legacy fields. HUD context and deterministic telemetry include non-nominal feed state.
|
||||
@@ -1553,6 +1558,32 @@ owners. Tuner calculations have a pure entry, with existing layer exports
|
||||
preserved. Destruction removes listeners and state subscriptions before ending
|
||||
tuning; a delayed Enable result cannot reveal or refocus removed controls.
|
||||
|
||||
## Giroc / Webcam Romania CCTV source pack
|
||||
|
||||
`config/cctv_sources.romania.json` is an optional single-camera pack for Giroc,
|
||||
Timiș, Romania. To use it, set `CCTV_SOURCES_FILE=config/cctv_sources.romania.json`
|
||||
in the local `.env` and restart the server. Set `CCTV_FORCE_AUSTIN=1` as well to
|
||||
retain the built-in live packs; despite its historical name, this flag applies
|
||||
to all live packs. The default catalog is unchanged.
|
||||
|
||||
The camera uses Webcam Romania's public HLS stream and JPEG snapshot, fetched
|
||||
at runtime without an API key. Its position, 45.70028947266781, 21.233675914004227,
|
||||
was corrected by the contributor rather than copied from the directory's
|
||||
inaccurate pin. Heading, pitch, field of view, mounting height and ground
|
||||
elevation are uncalibrated priors; the camera retains the RAW PRIOR badge.
|
||||
See `DATA_SOURCES.md` for attribution and provider terms. Webcam Romania's
|
||||
terms grant no reuse licence for the video and name no camera operator; the
|
||||
provider's sanctioned route is its iframe embed. Keep this pack to local use
|
||||
and off hosted deployments unless the operator grants permission.
|
||||
|
||||
The existing HLS puller resolves same-origin relative master/media references
|
||||
and serves local leased playlists and segments. It downloads complete segments
|
||||
without forwarding a browser Range header. Its per-segment streamed-byte cap
|
||||
is 8 MiB: Giroc's roughly ten-second 2702×1520 segments can exceed the former
|
||||
4 MiB cap (one measured 6,437,120 bytes). Retained data remains capped at
|
||||
24 MiB and 12 segments per session, with two sessions maximum. Redirect
|
||||
refusal, origin pinning, timeouts and lease expiry are unchanged.
|
||||
|
||||
## NYC DOT TMC CCTV source pack
|
||||
|
||||
The CCTV catalog can add New York City traffic cameras from the NYC DOT
|
||||
@@ -1568,9 +1599,11 @@ from the payload. There is no heading in the feed, so gizmos use the shared
|
||||
id-hash fallback at low confidence.
|
||||
|
||||
Once enabled, the 250 cameras nearest the five borough cores load.
|
||||
`CCTV_NYC_MAX_SOURCES` sets the pack cap (8–400). The catalog-wide ceiling stays at 4,000 and is shared
|
||||
round-robin: no region is dropped outright, but when the packs together exceed
|
||||
the ceiling, adding a pack trims cameras from the largest packs.
|
||||
`CCTV_NYC_MAX_SOURCES` sets the pack cap (8–400). The default catalog limit is
|
||||
7,350: 4,000 shared round-robin slots, plus room above them for the 1,120
|
||||
Québec locations and 2,230 World image sources. Lower explicit
|
||||
`CCTV_MAX_SOURCES` values are honored; reservations only use the capacity
|
||||
above the 4,000 shared slots.
|
||||
|
||||
## City of Toronto RESCU CCTV source pack
|
||||
|
||||
@@ -1586,8 +1619,9 @@ cameras return a 200 "Image Unavailable" placeholder frame instead of an error.
|
||||
|
||||
By default all ~336 cameras load, nearest downtown first.
|
||||
`CCTV_TORONTO_MAX_SOURCES` sets the pack cap (8–500) and
|
||||
`CCTV_TORONTO_ENABLED=0` turns the pack off. The pack shares the 4,000
|
||||
catalog-wide ceiling round-robin with the other packs.
|
||||
`CCTV_TORONTO_ENABLED=0` turns the pack off. The pack shares the default
|
||||
7,350-source catalog limit with the other packs; Québec and the World image
|
||||
pack have reserved slots and the remaining sources use round-robin allocation.
|
||||
|
||||
## Lithuania CCTV source pack
|
||||
|
||||
@@ -1607,24 +1641,8 @@ video; camera poses use the existing low-confidence heading prior.
|
||||
|
||||
## Taiwan CCTV source pack
|
||||
|
||||
The CCTV catalog includes Taiwan national freeway cameras from the Freeway
|
||||
Bureau, MOTC. One keyless request reads the daily MOTC 2.0 `CCTV.xml` table
|
||||
(~1,800 cameras); the request refuses redirects, has a 15-second deadline and a
|
||||
6 MiB response cap, and uses the shared 15-minute cache and fair catalog
|
||||
allocation. A failed download serves the last good table (up to a day old),
|
||||
or an empty pack, and is not retried for an hour: the catalog host does not
|
||||
answer from some networks outside Taiwan, and without the backoff every refresh
|
||||
there would wait out the connect timeout. The table
|
||||
publishes MJPEG stream URLs; each is mapped to the same camera server's
|
||||
single-JPEG path for the same camera number, and only those exact HTTPS pairs
|
||||
on the four `freeway.gov.tw` camera servers are registered with the frame
|
||||
proxy. Camera keys come from the server and camera number, because published
|
||||
CCTV IDs contain Chinese place names and a few repeat across cameras. Poses use
|
||||
the low-confidence heading prior. `CCTV_TAIWAN_ENABLED=0` disables the pack;
|
||||
`CCTV_TAIWAN_MAX_SOURCES` sets its cap (default 600, bounded to 1–2000), and
|
||||
`CCTV_TAIWAN_URL` overrides the table URL.
|
||||
Gate: `src/data/cctvTaiwan.test.mjs`.
|
||||
|
||||
The CCTV catalog includes Taiwan national freeway cameras from the Freeway Bureau, MOTC. The app first reads the daily MOTC 2.0 CCTV.xml table with a keyless HTTPS request, a 15-second deadline, a 6 MiB response cap, and the shared 15-minute cache. If that catalog is unreachable and no last-good source list exists, the loader uses the Highway Bureau’s public provincial-road CCTV XML feed at https://cctv-maintain.thb.gov.tw/opendataCCTVs.xml. The fallback is capped by the same Taiwan source limit and hourly retry backoff; its MJPEG feeds are restricted to the eight cctv-ss01 through cctv-ss08.thb.gov.tw HTTPS servers. The frame proxy returns one bounded JPEG from each stream, not an unbounded video response. The Highway Bureau open-data page states XML downloads are free and asks users to attribute the source; the app credits Highway Bureau, MOTC. These are provincial-road cameras, not a replacement for the national freeway cameras. CCTV_TAIWAN_ENABLED=0 disables both feeds; CCTV_TAIWAN_MAX_SOURCES sets the shared cap (default 600, bounded to 1–2000), and CCTV_TAIWAN_URL overrides the Freeway Bureau table URL.
|
||||
Gate: src/data/cctvTaiwan.test.mjs and src/data/cctvProxy.test.mjs.
|
||||
## DriveBC CCTV source pack
|
||||
|
||||
The CCTV catalog adds DriveBC highway cameras for British Columbia alongside the
|
||||
@@ -4342,35 +4360,35 @@ nearby place—always use station selection. Unqualified “turn on/start the ra
|
||||
requests use Play; a qualified Play-shaped tool call is normalized to Select so
|
||||
its criteria cannot be silently ignored.
|
||||
|
||||
| Layer | Source | File | Proxy | Update Interval |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | -------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
||||
| Live Flights ✈️ | OpenSky Network; bounded adsb.lol regional fallback | `src/data/flights.js` | `/api/flights` (OAuth + fallback) | 30s |
|
||||
| Military Flights 🎖️ | adsb.lol /v2/mil | `src/data/militaryFlights.js` | `/api/military` | 15s |
|
||||
| Live AIS Vessels 🚢 | AISStream websocket | `src/data/aisLiveVessels.js` | `/api/vessels` | 60s (+800ms visibility pass) |
|
||||
| Mapped Installations ⌖ | OpenFreeMap military areas; optional configured Overpass names and Google Places search | `src/data/militaryInstallations.js` | OpenFreeMap tiles, `/api/military-installations`, `/api/google/text-search` | viewport-driven; transient errors back off, missing capability switches to tiles |
|
||||
| Earthquakes | USGS | `src/data/earthquakes.js` | — | 60s |
|
||||
| Air quality | ECCC MSC GeoMet OGC API (AQHI, Canada only) | `src/layers/airQuality/` | — | 20 min readings, 12 h station catalog |
|
||||
| Satellites | CelesTrak | `src/data/satellites.js` | `/api/celestrak` | 120s |
|
||||
| Space Missions (30d) | Launch Library 2 + CelesTrak | `src/data/rocketLaunches.js` | `/api/launches` + `/api/celestrak/active` | 5 min |
|
||||
| Traffic | Selectable TomTom / OpenStreetMap / Hybrid roads; optional TomTom flow (BYOK) | `src/data/traffic.js` | browser-direct OpenFreeMap tiles; `/api/tomtom` for flow | viewport-driven; capped tile cache |
|
||||
| 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, Kartverket ground heights) + King County (WA) Open Data + WSDOT (WA) Open Data + NYC DOT TMC (opt-in) + City of Toronto RESCU + Catalonia (Servei Català de Trànsit) + Hong Kong Transport Department + FL511 (FL, opt-in) + Freeway Bureau, MOTC (Taiwan) + Houston TranStar (opt-in) + Autostrada del Brennero (IT, A22 stills) + 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, Bike Itaú (Brazil), Bicing (Barcelona) and seven other Spanish cities + Urban Sharing, Entur, JCDecaux (Lillestrøm, Valencia, Seville) + nextbike (AMBici)) | `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/infrastructure.js` | — | static |
|
||||
| Dams ▰ | OpenInfraMap/OSM extract (bundled) | `src/data/infrastructure.js` | — | static |
|
||||
| Submarine Cables ◠ | TeleGeography public map (bundled) | `src/data/telegeographySubmarineCables.js` | — | static |
|
||||
| FIRMS Active Fires ▲ | NASA FIRMS live (VIIRS ×3 NRT + MODIS NRT, trailing 24h) | `src/data/firmsHeatmap.js` | `/api/firms` (`FIRMS_MAP_KEY`) | 10 min (proxy TTL 30 min) |
|
||||
| Street Level 📷 | Mapillary street-level imagery (vector tiles, Graph API, MapillaryJS) | `src/layers/streetLevel/` via `src/app/layers/streetLevel.js` | `/api/mapillary/status`, `/api/mapillary/tiles/coverage/{z}/{x}/{y}` (`MAPILLARY_CLIENT_TOKEN`) | camera-driven (320 ms debounce, ≤9 tiles, server memory cache 24 h) |
|
||||
| Wind 🌬 | NOAA GFS 10 m wind (keyless, 0.25°→1° grid; animated particles) | `src/data/wind.js` | `/api/wind` | 1 h (forecast cycle) |
|
||||
| GNSS Integrity 📡 (`gnss-integrity`) | adsb.lol point snapshot, low navigation accuracy binned per 0.5° cell (GEV NIC/NACp threshold + gpsjam.org formula and bands, keyless, ODbL) | `src/layers/gnss/` via `src/app/layers/gnss.js` | `/api/gnss-integrity` | camera-driven (600 ms debounce, minimum 2.5 s between reads) + 60s poll (server cache 60 s; 30 min rolling window) |
|
||||
| S2 GhostMaps 👻 (`s2-ghostmaps`) | S2 Underground's public GhostMaps repository on GitHub (KMZ intel products, keyless, CC BY-NC-SA 4.0) | `src/layers/ghostMaps/` via `src/app/layers/ghostMaps.js` | `/api/ghostmaps/catalog`, `/api/ghostmaps/events` (server-sent events), `/api/ghostmaps/file/{product}` | branch head checked every 15 s; new commits pushed to the page; 5 min catalog backstop |
|
||||
| Sea Marks ⚓ (`sea-marks`) | OpenSeaMap seamark raster tiles (keyless; CC BY-SA 2.0 tiles, OSM data ODbL) | `src/data/osmOverlays.js` | — (browser-direct tiles) | on view (Cesium tile requests; no polling) |
|
||||
| Ski Pistes ⛷ (`ski-pistes`) | OpenSnowMap piste raster tiles (keyless; CC BY-SA tiles, OSM data ODbL; no bulk download) | `src/data/osmOverlays.js` | — (browser-direct tiles) | on view (Cesium tile requests; no polling) |
|
||||
| Fire Perimeters 🔥 | NIFC WFIGS current interagency perimeters (keyless, paged past the 2000-record cap); InciWeb catalog + incident page origin and update time checks for verified incident-page links | `src/layers/perimeters/` via `src/app/layers/perimeters.js` | `/api/fire-perimeters` + `/api/fire-perimeters/inciweb/*` | 5 min (server caches: catalog 1 h; publication 30 min) |
|
||||
| Prohibited & Restricted Airspace 🚫 | FAA Special Use Airspace prohibited (P) and restricted (R) zones (keyless FeatureServer, simplified geometry) | `src/layers/droneAirspace/` via `src/app/layers/droneAirspace.js` | `/api/drone-airspace` | 1 h (server cache 1 h; stale-on-error) |
|
||||
| Wildlife Sightings 🦉 | iNaturalist wildlife observed within 48 h (whales 7 d), per view; Acartia Salish Sea marine mammal coverage (keyless) | `src/layers/wildlifeSightings/` via `src/app/layers/wildlifeSightings.js` | `/api/inaturalist/observations`, `/api/inaturalist/grid`, `/api/acartia/sightings` | camera moveEnd (600 ms debounce; enable settles 300 ms) + 5 min (observations 5 / 10 min; tiles 15 min; stale ≤ 1 h / 24 h) |
|
||||
| Layer | Source | File | Proxy | Update Interval |
|
||||
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Live Flights ✈️ | OpenSky Network; bounded adsb.lol regional fallback | `src/data/flights.js` | `/api/flights` (OAuth + fallback) | 30s |
|
||||
| Military Flights 🎖️ | adsb.lol /v2/mil | `src/data/militaryFlights.js` | `/api/military` | 15s |
|
||||
| Live AIS Vessels 🚢 | AISStream websocket | `src/data/aisLiveVessels.js` | `/api/vessels` | 60s (+800ms visibility pass) |
|
||||
| Mapped Installations ⌖ | OpenFreeMap military areas; optional configured Overpass names and Google Places search | `src/data/militaryInstallations.js` | OpenFreeMap tiles, `/api/military-installations`, `/api/google/text-search` | viewport-driven; transient errors back off, missing capability switches to tiles |
|
||||
| Earthquakes | USGS | `src/data/earthquakes.js` | — | 60s |
|
||||
| Air quality | ECCC MSC GeoMet OGC API (AQHI, Canada only) | `src/layers/airQuality/` | — | 20 min readings, 12 h station catalog |
|
||||
| Satellites | CelesTrak | `src/data/satellites.js` | `/api/celestrak` | 120s |
|
||||
| Space Missions (30d) | Launch Library 2 + CelesTrak | `src/data/rocketLaunches.js` | `/api/launches` + `/api/celestrak/active` | 5 min |
|
||||
| Traffic | Selectable TomTom / OpenStreetMap / Hybrid roads; optional TomTom flow (BYOK) | `src/data/traffic.js` | browser-direct OpenFreeMap tiles; `/api/tomtom` for flow | viewport-driven; capped tile cache |
|
||||
| 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, Kartverket ground heights) + King County (WA) Open Data + WSDOT (WA) Open Data + NYC DOT TMC (opt-in) + City of Toronto RESCU + Catalonia (Servei Català de Trànsit) + Iceland (Vegagerðin / IRCA) + Hong Kong Transport Department + FL511 (FL, opt-in) + Freeway Bureau, MOTC (Taiwan) + Houston TranStar (opt-in) + Autostrada del Brennero (IT, A22 stills) + 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, Bike Itaú (Brazil), Bicing (Barcelona) and seven other Spanish cities + Urban Sharing, Entur, JCDecaux (Lillestrøm, Valencia, Seville) + nextbike (AMBici)) | `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/infrastructure.js` | — | static |
|
||||
| Dams ▰ | OpenInfraMap/OSM extract (bundled) | `src/data/infrastructure.js` | — | static |
|
||||
| Submarine Cables ◠ | TeleGeography public map (bundled) | `src/data/telegeographySubmarineCables.js` | — | static |
|
||||
| FIRMS Active Fires ▲ | NASA FIRMS live (VIIRS ×3 NRT + MODIS NRT, trailing 24h) | `src/data/firmsHeatmap.js` | `/api/firms` (`FIRMS_MAP_KEY`) | 10 min (proxy TTL 30 min) |
|
||||
| Street Level 📷 | Mapillary street-level imagery (vector tiles, Graph API, MapillaryJS) | `src/layers/streetLevel/` via `src/app/layers/streetLevel.js` | `/api/mapillary/status`, `/api/mapillary/tiles/coverage/{z}/{x}/{y}` (`MAPILLARY_CLIENT_TOKEN`) | camera-driven (320 ms debounce, ≤9 tiles, server memory cache 24 h) |
|
||||
| Wind 🌬 | NOAA GFS 10 m wind (keyless, 0.25°→1° grid; animated particles) | `src/data/wind.js` | `/api/wind` | 1 h (forecast cycle) |
|
||||
| GNSS Integrity 📡 (`gnss-integrity`) | adsb.lol point snapshot, low navigation accuracy binned per 0.5° cell (GEV NIC/NACp threshold + gpsjam.org formula and bands, keyless, ODbL) | `src/layers/gnss/` via `src/app/layers/gnss.js` | `/api/gnss-integrity` | camera-driven (600 ms debounce, minimum 2.5 s between reads) + 60s poll (server cache 60 s; 30 min rolling window) |
|
||||
| S2 GhostMaps 👻 (`s2-ghostmaps`) | S2 Underground's public GhostMaps repository on GitHub (KMZ intel products, keyless, CC BY-NC-SA 4.0) | `src/layers/ghostMaps/` via `src/app/layers/ghostMaps.js` | `/api/ghostmaps/catalog`, `/api/ghostmaps/events` (server-sent events), `/api/ghostmaps/file/{product}` | branch head checked every 15 s; new commits pushed to the page; 5 min catalog backstop |
|
||||
| Sea Marks ⚓ (`sea-marks`) | OpenSeaMap seamark raster tiles (keyless; CC BY-SA 2.0 tiles, OSM data ODbL) | `src/data/osmOverlays.js` | — (browser-direct tiles) | on view (Cesium tile requests; no polling) |
|
||||
| Ski Pistes ⛷ (`ski-pistes`) | OpenSnowMap piste raster tiles (keyless; CC BY-SA tiles, OSM data ODbL; no bulk download) | `src/data/osmOverlays.js` | — (browser-direct tiles) | on view (Cesium tile requests; no polling) |
|
||||
| Fire Perimeters 🔥 | NIFC WFIGS current interagency perimeters (keyless, paged past the 2000-record cap); InciWeb catalog + incident page origin and update time checks for verified incident-page links | `src/layers/perimeters/` via `src/app/layers/perimeters.js` | `/api/fire-perimeters` + `/api/fire-perimeters/inciweb/*` | 5 min (server caches: catalog 1 h; publication 30 min) |
|
||||
| Prohibited & Restricted Airspace 🚫 | FAA Special Use Airspace prohibited (P) and restricted (R) zones (keyless FeatureServer, simplified geometry) | `src/layers/droneAirspace/` via `src/app/layers/droneAirspace.js` | `/api/drone-airspace` | 1 h (server cache 1 h; stale-on-error) |
|
||||
| Wildlife Sightings 🦉 | iNaturalist wildlife observed within 48 h (whales 7 d), per view; Acartia Salish Sea marine mammal coverage (keyless) | `src/layers/wildlifeSightings/` via `src/app/layers/wildlifeSightings.js` | `/api/inaturalist/observations`, `/api/inaturalist/grid`, `/api/acartia/sightings` | camera moveEnd (600 ms debounce; enable settles 300 ms) + 5 min (observations 5 / 10 min; tiles 15 min; stale ≤ 1 h / 24 h) |
|
||||
|
||||
GNSS Integrity samples a 250 nm circle around the Earth point under the screen
|
||||
centre. Camera reads debounce for 600 ms with a monotonic 2.5 s minimum gap;
|
||||
@@ -4424,8 +4442,10 @@ provider draws in one colour (`PROVIDER_COLORS` in
|
||||
sequence is GEV cyan. Share options: `m` (Mapillary), `r` (the other providers' switches by id,
|
||||
e.g. `google-1` for Street View), `p` (`a`/`p`/`f` panoramas), `s` (since, days). Mapillary, Google
|
||||
Street View, KartaView and Panoramax are registered. Without
|
||||
`MAPILLARY_CLIENT_TOKEN` the panel reads KEY REQUIRED and the controls are
|
||||
disabled. With it, coverage draws as z11–14 sequence lines below 60 km; the proxy strips the unused `image` layer from z14
|
||||
`MAPILLARY_CLIENT_TOKEN` (and no Graph proxy) Street Level starts on the first
|
||||
keyless provider (KartaView), including a restored "Mapillary on" unless the link
|
||||
switches the keyless providers off; filter-only updates never change the source.
|
||||
With it, coverage draws as z11–14 sequence lines below 60 km; the proxy strips the unused `image` layer from z14
|
||||
tiles (12 MB → ~80 KB) and shares one upstream fetch between concurrent
|
||||
requests for a tile.
|
||||
|
||||
@@ -4439,7 +4459,7 @@ the camera, nearest first, at most 9 (the halves of a view across
|
||||
the date line count a shared tile once). Lines are draped
|
||||
`GroundPolylinePrimitive`s (`ClassificationType.BOTH`), so they follow terrain
|
||||
and Google 3D tiles alike; cones and the photo marker are ground-clamped
|
||||
billboards with a horizon cull. Without `MAPILLARY_CLIENT_TOKEN` the panel
|
||||
billboards with a horizon cull. When Mapillary is lit without `MAPILLARY_CLIENT_TOKEN` the panel
|
||||
reads KEY REQUIRED, says which key to add and disables the filters; a rejected
|
||||
key reads KEY REJECTED and stops tile requests until the layer goes off; a 429
|
||||
pauses them for its Retry-After.
|
||||
@@ -5332,7 +5352,7 @@ silently demoting every later lookup for the session.
|
||||
- Context is set for reading in every theme: mode, standby and action labels are 10 px, and the panel draws no glow halo. Outside Cyber, the two modes form one segmented control, the standby guidance is plain left-aligned text rather than a dashed placeholder, and the panel's section titles share the side panels' dock-label colour. Cyber keeps its separately framed mode buttons, its red panel titles, and in layout-focus a framed, centred standby card over its radar art.
|
||||
- The expanded desktop header omits the redundant `ON` label; the active mode button carries state. Expanded Contact results also omit the duplicate `GLOBAL CONTEXT` / `CONTEXT ONLY` status row and begin with the selected subject and its 250 km scope. Global Context does not fabricate a selected-entity model preview: the provisional hand-authored aircraft wireframe was removed because it was not geometry extracted from the selected entity's actual asset.
|
||||
- Dependency ownership is reversible: disabling Global Context releases only dependencies it enabled, while user-enabled layers remain on. This also removes the Military-layer suppression handoff when Global Context owned Military, allowing an already-enabled civilian Flights layer to resume its normal mixed rendering. If OpenSky is unavailable and has no last-good cache, Flights requests a capped 250 nm adsb.lol point snapshot around the current view anchor and labels that provenance explicitly; it never relabels military-feed rows as civilian data. If both inputs fail, Flights remains `UNKNOWN`.
|
||||
- Space Missions is replay-isolated: Rocket Launches and Satellites are the only Data Layers permitted while the mode is active. Direct UI and voice entry capture the same pre-entry snapshot; internal dependency and restoration enables do not create a user-owned Context session. Entry waits for incompatible layers to shut down, direct incompatible enables are blocked before lifecycle work, and the entry gate remains active through the complete Rocket Launches enable. A newer same-target ON request takes ownership of the pending entry without releasing its isolation snapshot, including when it arrives while the prior request is awaiting the adoption guard. A caller abort, resource cancellation, newer OFF, or layer teardown waits for exact manager settlement and restores that snapshot without resurrecting Rocket Launches. If an abort lands after only part of a restore settles, Context completes the same exact target without the stale caller signal and then replays newer explicit layer intent. Dedicated Voice Context cancellation reports a stable cancelled result plus the current Context state; generic layer visibility additionally exposes manager phase, reason, successor, and lifecycle details. Once an exact voice visibility intent commits, a newer voice turn cannot relabel it as cancelled while Context settlement completes; pre-commit aborts remain cancellable and final lifecycle mismatches remain failures. Clear Selected Layers reserves its complete captured OFF set before sequential teardown; a newer absolute request of any origin supersedes only its layer reservation and remains authoritative. A rejected layer teardown retains the truthful enabled state, rolls already-stopped siblings back to the captured pre-entry set, and aborts replay; rapid exit/re-entry serializes the full Satellite enabled-state and parameter restore before a new snapshot is taken. Contacts remains additive and restores user-enabled layers normally.
|
||||
- Space Missions is replay-isolated: Rocket Launches and Satellites are the only Data Layers permitted while the mode is active. Direct UI and voice entry capture the same pre-entry snapshot; internal dependency and restoration enables do not create a user-owned Context session. Entry waits for incompatible layers to shut down, direct incompatible enables are blocked before lifecycle work, and the entry gate remains active through the complete Rocket Launches enable. That enable includes the first mission-index load: an HTTP error, timeout, network failure or malformed payload before any index has loaded fails the enable, so Context rolls back to the pre-entry layers instead of committing an empty, isolating mode; the camera returns to its pre-entry view unless navigation took it, and a toast says the mission index could not load. A valid empty index still enters, and a refresh failure after an index loaded keeps its records and the mode. Exiting cancels the layer's own camera flights but never one navigation started. A newer same-target ON request takes ownership of the pending entry without releasing its isolation snapshot, including when it arrives while the prior request is awaiting the adoption guard. A caller abort, resource cancellation, newer OFF, or layer teardown waits for exact manager settlement and restores that snapshot without resurrecting Rocket Launches. If an abort lands after only part of a restore settles, Context completes the same exact target without the stale caller signal and then replays newer explicit layer intent. Dedicated Voice Context cancellation reports a stable cancelled result plus the current Context state; generic layer visibility additionally exposes manager phase, reason, successor, and lifecycle details. Once an exact voice visibility intent commits, a newer voice turn cannot relabel it as cancelled while Context settlement completes; pre-commit aborts remain cancellable and final lifecycle mismatches remain failures. Clear Selected Layers reserves its complete captured OFF set before sequential teardown; a newer absolute request of any origin supersedes only its layer reservation and remains authoritative. A rejected layer teardown retains the truthful enabled state, rolls already-stopped siblings back to the captured pre-entry set, and aborts replay; rapid exit/re-entry serializes the full Satellite enabled-state and parameter restore before a new snapshot is taken. Contacts remains additive and restores user-enabled layers normally.
|
||||
- Enabled Data Layer controls report normalized feed health on the button (`LOADING`, `DEGRADED`, `STALE`, `FALLBACK`, or `UNAVAILABLE`) while the metadata line retains the source and reason. A partial CelesTrak group failure keeps the usable catalog and reports `DEGRADED`; a total outage keeps last-good catalog data visible but reports `UNAVAILABLE`.
|
||||
- On activation it focuses the nearest currently observed aircraft across the civilian and military feeds, with military winning an exact distance tie; if none are available, it focuses an observed AIS vessel. The aircraft search is deliberately uncapped and one refresh-tick retry handles initially empty feeds. This is an attention-priority navigation shortcut, not a high-risk, affiliation, or threat classification.
|
||||
- A selected aircraft, AIS vessel, or mapped installation gets a 250 km **context window** with nearby cohort counts, nearest examples, source labels, and stale/unavailable reasons. It emits `NEARBY` or `UNKNOWN`; no detection, engagement, affiliation, or sensor-activity conclusion is calculated.
|
||||
@@ -5353,7 +5373,7 @@ silently demoting every later lookup for the session.
|
||||
|
||||
### Motion & Symbology Correctness (June 10, 2026)
|
||||
|
||||
- **Flights (commercial + military)** render one poll interval behind real time (30s/15s) and interpolate between two known feed-stamped fixes (OpenSky `time_position`; adsb.lol `receipt − seen_pos`). The display latency is an intentional product decision — do not "fix" it away. The whole fleet dead-reckons at ~12Hz (1m² write gating); aircraft get a 3-poll grace period (faded icon) before removal. When a position epoch pauses, both layers coast for at least 60 seconds of contact grace with an absolute five-minute ceiling. Source backoff marks each contact and the cockpit `STALE`; the cockpit then holds the exact layer position instead of continuing inertial flight. Repeated-position kinematic changes create a forward-only synthetic fix rather than mutating history, and grounded history is lifted only when no owned 3D model already controls its datum. A nominally successful worldwide OpenSky response whose own snapshot epoch is more than two minutes old prefers the existing 250-nm viewport-scoped adsb.lol fallback and labels the source/coverage accordingly when that upstream is available. If the fallback is also unavailable, the layer's freshness/error fields use the source epoch—never the cache receipt time—so the UI reports an old snapshot rather than “just now.”
|
||||
- **Flights (commercial + military)** render one poll interval behind real time (30s/15s) and interpolate between two known feed-stamped fixes (OpenSky `time_position`; adsb.lol `receipt − seen_pos`). The display latency is an intentional product decision — do not "fix" it away. The fleet dead-reckons at ~12Hz (1m² write gating); a contact already hidden behind the horizon whose last position still is re-reckons on a staggered one-in-13-ticks rotation (about once a second) instead, so the worldwide feed's ~13k contacts no longer cost a full pass when well under 1% are on screen. Anything that position puts in view, and any contact with a fleet model, takes the full pass immediately, and hidden-inclusive proximity queries read positions at most about a second old; aircraft get a 3-poll grace period (faded icon) before removal. When a position epoch pauses, both layers coast for at least 60 seconds of contact grace with an absolute five-minute ceiling. Source backoff marks each contact and the cockpit `STALE`; the cockpit then holds the exact layer position instead of continuing inertial flight. Repeated-position kinematic changes create a forward-only synthetic fix rather than mutating history, and grounded history is lifted only when no owned 3D model already controls its datum. A nominally successful worldwide OpenSky response whose own snapshot epoch is more than two minutes old prefers the existing 250-nm viewport-scoped adsb.lol fallback and labels the source/coverage accordingly when that upstream is available. If the fallback is also unavailable, the layer's freshness/error fields use the source epoch—never the cache receipt time—so the UI reports an old snapshot rather than “just now.”
|
||||
- **World-space headings at every angle** (`src/data/iconOrientation.js`): aircraft/vessel icon rotation uses the camera right/up basis per tick (alignedAxis always ZERO), which is exact at screen center and for orthographic/nadir views and remains stable through >180° tracked orbits. Perspective rays vary across the viewport, so off-center contacts at oblique pitch can diverge from an exact finite-difference window projection; a regression test pins that known regime, and field evidence decides whether to adopt exact projection with the basis method as fallback. Fleet rotations refresh on camera-pose change; tracked entities per frame. Billboards are horizon-culled via a shared EllipsoidalOccluder.
|
||||
- **Military/OpenSky reconciliation** (`src/data/militaryRegistry.js`): known-military ICAOs render amber in the flights layer (60s self-poll of the cached mil endpoint when the military layer is off) and are suppressed there while the military layer renders them.
|
||||
- **Satellites**: 838-sat core catalog (stations/visual/GPS/GLONASS/Galileo/GEO), tracking lands ~726km out via `viewFrom` (tracked entity owns a point graphic so the tracking camera engages), rings realign via primitive modelMatrix (no per-second rebuild flicker), optional `setParams({catalog:'dense'})` Starlink mode.
|
||||
@@ -5418,8 +5438,10 @@ silently demoting every later lookup for the session.
|
||||
kill switch `CCTV_FINTRAFFIC_ENABLED=0`) are the fourth pack: one keyless GeoJSON station list
|
||||
covering the whole country, where one _preset_ (a station's fixed view) is one camera — 806
|
||||
GATHERING stations carry 2,256 in-collection presets, prioritized to 300 against seven anchors
|
||||
on the main road spine. `CCTV_MAX_SOURCES` is a 4,000 catalog-wide ceiling shared round-robin
|
||||
across packs, so a lower global cap thins every region instead of starving the last pack.
|
||||
on the main road spine. `CCTV_MAX_SOURCES` defaults to 7,350: 4,000 shared
|
||||
round-robin slots plus 3,350 reserved Québec and World image slots.
|
||||
Reservations only use capacity above the 4,000 shared slots, so at 4,000 or
|
||||
less every pack, Québec and World included, shares the cap round-robin.
|
||||
~1,100 cameras total, all RAW PRIOR poses, stills-first. The catalog lives for 15 minutes; once
|
||||
it expires, `/api/cctv` keeps answering from it while one background refresh reloads every
|
||||
pack, so only the very first load waits for the slowest pack (a live first load took about
|
||||
@@ -5884,8 +5906,11 @@ HTTP refusals and outages remain retryable. Searches share a 12-second total
|
||||
deadline, with Photon requests capped at six seconds each. Caller/application
|
||||
cancellation stops retries and late cache writes. Replacing a location search
|
||||
cancels the previous lookup; disposing its controls cancels the active lookup. Photon uses a soft proximity bias, up to five
|
||||
candidates, and an unbiased retry for name mismatches. A "place, region" query
|
||||
prefers a candidate named exactly for the place whose city, county, state,
|
||||
candidates, and an unbiased retry for name mismatches. Name comparison folds
|
||||
Latin accents while preserving letters and marks in other scripts, so a
|
||||
non-Latin name spelled exactly, marks included, keeps its proximity ranking. A
|
||||
leading "the" counts: "The Hague" does not take a nearby "Hague". A "place, region" query prefers a
|
||||
candidate named exactly for the place whose city, county, state,
|
||||
country or country code (or USA/UK) contains every word of the region, and a
|
||||
settlement or administrative area over a POI when both fit. Nearby results that
|
||||
fail that check are retried unbiased. After the region checks of both passes, a
|
||||
@@ -6008,6 +6033,7 @@ request viewbox and continues searching without that geographic preference.
|
||||
|
||||
- **What it shows.** Events ▸ **S2 GhostMaps** draws the current edition of S2 Underground's KMZ intel products from `github.com/s2underground/GhostMaps`. The products are the Common Intelligence Picture and the 30-day kinetic pull (both on by default), the Border Crisis Map, the Iran Common Intelligence Picture, Iran Kinetic Events, Yemen FLOT, and **Latest Drop**: the newest dated incident file outside those series, `0_Archive`, `Basedata` and `Reference Material`. Each product has a chip in the row. The chip choice is kept in `localStorage` (`gev.ghostmaps.products`) and falls back to the defaults when storage is blocked.
|
||||
- **Editions.** `src/layers/ghostMaps/catalog.js` (pure) matches whole repository paths per product. The newest edition wins by the date written in the file name, then by `Export_N`/`vN`. Undated names, such as the fixed 30-day file, are identified by blob sha. Files over 50 MiB are left out (the 62 MB census KMZ, for example). A renamed folder drops the product rather than matching elsewhere.
|
||||
- **Event age filter.** One **Event age** switch in the S2 GhostMaps row applies to every selected product: 1, 3, 7 (default), 14 or 30 days, or **All** (every marker). The choice is saved separately from product selection in `localStorage` (`gev.ghostmaps.ageDays`); missing, invalid or blocked storage falls back to seven days. Age comes only from the placemark's `Date of Event` (or `EventDate`/`Date`), never from report, prose or KMZ edition dates. Undated, unparseable and future dates stay visible. ArcGIS `M/D/YYYY` dates with optional AM/PM times and ISO dates are read as UTC when no zone is given; a date without a time covers its whole UTC day, so **1d** still shows yesterday's reports. Events age out every minute while the layer is on. Narrowing the window only removes markers; widening restores them from the cached KMZ. An open card stays while its marker is shown.
|
||||
- **Updates.** The repository is not ours, so there is no webhook. `server/providers/ghostMaps.js` starts with the first `/api/ghostmaps` request, so an install that never enables the layer never contacts GitHub.
|
||||
- It reads the branch head from git's smart-HTTP ref advertisement (`/info/refs?service=git-upload-pack`, about 0.5 KB, outside the REST rate limit) every `GHOSTMAPS_POLL_SECONDS` (15 s by default, 5 s minimum). Failures back off from 15 s to 5 min.
|
||||
- Only a new head costs REST calls: one recursive tree and one commit. An optional `GITHUB_TOKEN` raises the REST limit.
|
||||
@@ -6429,7 +6455,26 @@ easier to meet (detection is now on more often), but does not create it.
|
||||
- Road source (`src/layers/traffic/roadModes.js`): the Street Traffic row has
|
||||
TomTom / OSM / Hybrid chips (`roadMode` param, stored in the existing layer
|
||||
state `lo` field, no new share token); `?trafficRoads=tomtom|osm|hybrid`
|
||||
overrides restored state until the user picks a chip. With no stored choice
|
||||
overrides restored state until the user picks a chip. A host may offer
|
||||
fewer modes with `createTrafficSource({ roadModes })` (a non-empty subset,
|
||||
default all three): only offered modes get chips (none for a single mode),
|
||||
and other choices from chips, restores, share links or the query are
|
||||
ignored. Without `osm` the layer never simulates: keyless or with the
|
||||
status probe unreachable, loads fail with "TomTom roads need a TomTom key"
|
||||
or "TomTom roads unavailable while the traffic service is unreachable" and
|
||||
no roads are drawn.
|
||||
- Row toggles (live mode only): after the road-source chips, Lines draws
|
||||
every road with flow as a steady draped line in its bucket color (green
|
||||
free, amber slow, red jam; jam first, then slow, then free, longest first,
|
||||
capped at 1,500; on 3D tiles under photoreal, on terrain or tiles
|
||||
otherwise), and Dots shows or hides the moving dots (hidden dots are kept,
|
||||
are not offered to detection overlays, and hold no render frames).
|
||||
Without live flow only a hidden Dots toggle is offered, so dots hidden by a
|
||||
live choice, a restore or a share link can always be shown again.
|
||||
Defaults: Lines off, Dots on. Both are `lines` / `dots` params stored in the
|
||||
layer state `lo` field (`l`, `d`). Toggling either restyles in place, with
|
||||
no refetch. The owner `jamViz` heat-line prototype is unchanged when Lines
|
||||
is off. With no stored choice
|
||||
the default is Hybrid with a TomTom key and OSM without one; share links
|
||||
without a road-source choice (including older ones) likewise use the
|
||||
installation's default (Hybrid when keyed, OpenStreetMap when keyless). Without a key
|
||||
@@ -7073,3 +7118,79 @@ the container healthy. Checks time out after 10 seconds, with a 30-second
|
||||
startup grace period and three consecutive failures before reporting unhealthy.
|
||||
This reports app HTTP availability, not individual provider health, and does not
|
||||
configure automatic restarts.
|
||||
|
||||
## Québec CCTV integration
|
||||
|
||||
The regular CCTV source loader reads `public/quebec-cameras.json` by default
|
||||
when `CCTV_QUEBEC_ENABLED` is not `0`. The pack contains 1,120 records: 680
|
||||
MTQ highway cameras, 426 Montréal OSM camera locations, and 14 scenic webcams.
|
||||
The default catalog limit is 7,350: it reserves all 1,120 Québec locations and
|
||||
2,230 World image feeds, and keeps 4,000 slots for the existing round-robin
|
||||
camera packs. Reservations only use capacity above those 4,000 slots: a lower
|
||||
explicit `CCTV_MAX_SOURCES` thins every pack round-robin, Québec and World
|
||||
included, instead of taking the shared slots.
|
||||
|
||||
The Québec 511 video is opt-in (`CCTV_QUEBEC_VIDEO_ENABLED=1`) because Québec
|
||||
511's terms for embedding its clips on another site are unconfirmed; the camera
|
||||
locations themselves are CC BY 4.0. With the flag unset, MTQ cameras show
|
||||
their location, credit and a link to the Québec 511 viewer page. With it set,
|
||||
selecting an MTQ camera opens a floating panel with a Play button and a notice
|
||||
that Québec 511 sees the viewer's IP address; the browser contacts Québec 511
|
||||
only after Play, never on selection alone (layer enable, auto-hop, a scene or
|
||||
voice), and a different camera starts unplayed. After Play, the panel plays
|
||||
the official MP4 in a native browser player and requests a second video for the
|
||||
paused map preview. The official feed is a five-second clip refreshed by
|
||||
Québec 511 every 2–4 minutes, not continuous live video; the browser player
|
||||
reloads every two minutes while the tab is visible. The
|
||||
map preview is a DOM overlay attached to the camera's coordinates and scales
|
||||
down with camera distance. Neither video is read into a canvas or Cesium texture:
|
||||
Québec 511 does not grant localhost pixel access, and its MP4 is not available
|
||||
to the app's server proxy. Montréal and scenic entries are regular CCTV camera
|
||||
locations with per-camera source-page links, but no direct frame URLs; those
|
||||
show a location label and open the official/operator page. MTQ positions carry
|
||||
CC BY 4.0 attribution, Montréal positions are attributed to OpenStreetMap
|
||||
contributors under ODbL, and scenic rows retain their position accuracy and
|
||||
provenance. Québec cameras load through the normal CCTV layer.
|
||||
|
||||
## City of Ottawa CCTV source pack
|
||||
|
||||
The CCTV catalog can add City of Ottawa traffic cameras, including the MTO
|
||||
cameras the city carries. Camera images come from the city's Traffic Camera
|
||||
Images API (`traffic.ottawa.ca/opendata/camera`), which needs a certificate
|
||||
from the city's registration form and lets one certificate and user id fetch a
|
||||
camera at most once a minute. Without `OTTAWA_CAMERA_CERTIFICATE` the pack is
|
||||
skipped with no request and one log line. The camera list is the documented
|
||||
keyless `traffic.ottawa.ca/map/service/camera` JSON. Catalog URLs carry no
|
||||
key: the frame proxy adds the certificate and the fixed user id `gev` at fetch
|
||||
time, only for a numeric camera number inside the pinned `/opendata/` path,
|
||||
and serves each camera from one shared server-side frame per minute. Inside
|
||||
that minute a camera with no cached frame gets the placeholder, never the
|
||||
Street View fallback. The feed publishes no bearing, so headings use the
|
||||
id-hash fallback at low confidence. `CCTV_OTTAWA_MAX_SOURCES` caps the pack
|
||||
(default 250, nearest downtown) and `CCTV_OTTAWA_ENABLED=0` turns it off.
|
||||
|
||||
## World camera image feeds
|
||||
|
||||
The default `world-cameras` pack reads only the checked-in, curated
|
||||
`config/cctv_sources.world-image-cameras.json` file. It adds 1,176 Oregon
|
||||
TripCheck and 1,054 MLIT river cameras. Duplicate feeds are excluded because
|
||||
DriveBC, Ottawa, Québec and Iceland (Vegagerðin) already have app packs. NZTA's
|
||||
249 rows are excluded because they were collected from a Journey Planner web
|
||||
cache whose current terms prohibit automated scraping; an authorized API feed
|
||||
or permission is required before adding them. The wider discovery export is
|
||||
not shipped with the app.
|
||||
|
||||
The app validates and pins each still-image URL to its provider directory
|
||||
before the server proxy uses it. Oregon frames refresh no faster
|
||||
than every five minutes; MLIT river frames refresh every ten minutes to reduce
|
||||
requests. Images are not persisted; the proxy reuses frames in a bounded 32 MB
|
||||
in-memory cache until their provider cadence. Set
|
||||
`CCTV_WORLD_CAMERAS_ENABLED=0` to disable the pack.
|
||||
|
||||
Only the curated image-backed rows are bundled. Camera images are fetched from
|
||||
their registered providers at request time and are not included in this data
|
||||
file. The additive `/api/cctv/sources` metadata includes provider source links,
|
||||
browser-only media links, refresh cadence, and location provenance where
|
||||
available. When MLIT serves its recognized adjustment image, `/api/cctv/frame`
|
||||
returns a God’s Eye maintenance card and `/api/cctv/health` reports the camera
|
||||
as degraded with the provider-adjustment reason.
|
||||
Reference in new issue
Block a user