diff --git a/CHANGELOG.md b/CHANGELOG.md index 21544fa..2562818 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ of current runtime behavior, see [`docs/CURRENT-STATE.md`](docs/CURRENT-STATE.md ### Fixed +- Street Traffic no longer remains in `SYNCING ROAD NETWORK` when the public + Overpass mirrors are unavailable. Exact bounded traffic road queries now use + OpenStreetMap's standard map endpoint first, translate its XML response into + the existing Overpass-shaped payload, and retain the generic Overpass mirror + rotation as a fallback. TomTom flow tiles and their attribution remain + unchanged. - Mapped-site outages show their scheduled retry countdown and distinguish known Overpass rate limits, timeouts, and query failures. Search feedback no longer claims a refresh succeeded while the layer is unavailable or loading. diff --git a/DATA_SOURCES.md b/DATA_SOURCES.md index faa351d..59b7544 100644 --- a/DATA_SOURCES.md +++ b/DATA_SOURCES.md @@ -24,7 +24,7 @@ How to read this: | **The Space Devs — Launch Library 2 v2.3** | Recent launch, payload, stage, and recovery metadata for Space Missions (30d) | [The Space Devs terms of use](https://github.com/TheSpaceDevs/Tutorials/blob/main/faqs/faq_TSD.md#terms-of-use): data may be used and shared in any form; avoid forwarding it without added value; attribution is encouraged (not mandatory). [Official API limits](https://ll.thespacedevs.com/docs/): 15 unauthenticated calls/hour; optional token | "Launch Library 2 — The Space Devs" (courtesy attribution) | | **Esri World Imagery** (ArcGIS Online tile service) | The keyless satellite basemap — the default landing when no Google/ion credential is configured, and the "Esri Satellite" map stack | [Esri Master Agreement](https://www.esri.com/en-us/legal/terms/full-master-agreement): the public World Imagery service is usable in public-facing apps with attribution; no key is required for this classic endpoint, but Esri governs and can change access — an app at scale should review current ArcGIS Location Platform terms | "Powered by Esri — Source: Esri, Maxar, Earthstar Geographics, and the GIS User Community" (provider carries the service's own credit line) | | **USGS** | Earthquakes | U.S. public domain | "Data courtesy of the U.S. Geological Survey" | -| **OpenStreetMap (Overpass API)** | Road geometry for traffic | ODbL 1.0 | "© OpenStreetMap contributors" | +| **OpenStreetMap (Standard Map API + Overpass API)** | Road geometry for traffic | ODbL 1.0 | "© OpenStreetMap contributors" | | **TomTom Traffic API** (flow vector tiles) | Live congestion coloring for the traffic layer (optional, BYOK) | [TomTom for Developers terms](https://developer.tomtom.com) (proprietary, your own key; free tier currently 200K tile requests/month — see [current pricing](https://docs.tomtom.com/pricing/)) | "Traffic flow data © TomTom" — registered when live mode activates | | **OpenStreetMap (Overpass API)** | Viewport-bounded mapped installation context for Global Context | ODbL 1.0 | "© OpenStreetMap contributors" (incomplete mapped context) | | **OpenStreetMap (Nominatim)** | Reverse-geocoded place label in the cockpit Local Info page | ODbL 1.0 + Nominatim usage policy | "© OpenStreetMap contributors" | @@ -47,6 +47,7 @@ How to read this: - **TfL JamCams.** The camera list comes from the keyless `api.tfl.gov.uk` endpoint (an optional `TFL_APP_KEY` raises its rate limit); frames come from TfL's public S3 bucket. The "Powered by TfL Open Data" attribution is required by TfL's terms and is registered in the Data attribution popover. - **Radio Browser.** `/api/radio/stations` discovers official API mirrors, makes bounded and coalesced healthy/geolocated HTTPS-station queries, caches the normalized public-domain directory for 45 minutes, and may serve the last good catalog for up to seven days during an outage. Refreshes must meet minimum accepted-query and station coverage before replacing a warm catalog; schema-valid responses whose rows all fail the product's health policy do not count as successful queries. A usable partial cold catalog is explicitly `DEGRADED`, and malformed or empty successful payloads are rejected atomically. Every directory and click-count request rejects redirects, validates all resolved addresses as globally routable (including reserved/documentation IPv4 and special/non-global IPv6 exclusions), and pins the TLS connection to a validated address. Only MP3/AAC non-HLS directory rows with public HTTPS stream targets are returned; favicons are intentionally omitted. Pressing play connects one browser audio element directly to the selected broadcaster and calls the directory's click counter through known-ID-only `POST /api/radio/click/:uuid`. GEV never proxies, caches, records, bundles, or redistributes audio. Radio Browser supplies station-level tags, not dependable current-song or upcoming-program metadata, so Radio filtering never claims either. Direct playback exposes the listener's IP address to the broadcaster, whose own stream terms apply. - **TomTom Traffic.** Optional and BYOK: without `TOMTOM_API_KEY` the traffic layer runs its built-in simulation and no TomTom data (or attribution) appears. With a key, flow vector tiles are fetched through the server-side `/api/tomtom` proxy (120 s cache + a daily tile-budget governor — `TOMTOM_DAILY_TILE_BUDGET`, default 40,000, a configurable application safety ceiling, not a guarantee of staying within TomTom's monthly free allowance; TomTom's [current pricing](https://docs.tomtom.com/pricing/) lists 200K free tile requests per month) and the "Traffic flow data © TomTom" credit is registered in the Data attribution popover the moment live mode activates. TomTom data is served live and cached only transiently (≤120 s TTL under `.gev-cache/`, gitignored) — it is not bundled or redistributed. One 23 KB point-in-time tile snapshot is committed as a decode-test fixture (`src/data/fixtures/`, © TomTom, never served to the app). +- **OpenStreetMap road geometry.** Street Traffic's exact, bounded highway query uses the standard `/api/0.6/map` endpoint first, parses its XML response server-side into the existing Overpass-shaped road payload, and keeps the established four-mirror Overpass rotation as a fallback. Other Overpass-backed features continue to use the generic query path. Road geometry is fetched at runtime, is not bundled or redistributed, and remains covered by the in-app "© OpenStreetMap contributors" attribution. - **Re:Earth Terrain.** Keyless (no API key). Used two ways: (1) `src/mapStackController.js` swaps in a `Cesium.CesiumTerrainProvider` pointed at Re:Earth's `cesium-mesh/ellipsoid` quantized-mesh endpoint for globe stacks without a Cesium ion token (e.g. OSM), replacing a flat `EllipsoidTerrainProvider`; falls back to the flat provider if the endpoint can't be reached. (2) The server-side `/api/terrain/heights` proxy (disk-cached, serve-stale) resolves per-point ellipsoidal ground height for entity placement. Both are best-effort with a keyless-safe fallback (bundled EGM96 geoid math) if Re:Earth is unreachable. - **Global Context installation context.** `/api/military-installations` queries only an allow-listed subset of OSM `military=*` and `landuse=military` features inside a maximum 10° non-dateline viewport. It caches and may serve stale mapped context, but it is neither a global installation database nor evidence of capability, activity, or absence. User-requested Google Places results remain separately sourced candidates unless their returned types explicitly establish military classification; generic offices, museums, and similarly ambiguous matches are excluded from military proximity counts. - **Cockpit regional briefing.** `/api/regional-brief` rounds aircraft coordinates into 0.1° cache cells, caches results for five minutes, and serializes Nominatim calls at no more than one request per second. Google News RSS is queried with the resolved locality/region first; GDELT is used only when that RSS query fails or is empty. Google's published Google News terms restrict that source to personal, noncommercial use, so commercial deployments must disable/replace it or obtain separate permission; GDELT permits commercial dataset use with citation. The Data attribution popover identifies the active headline sources; article links retain publisher attribution. Headlines are location-query matches, not verified incidents, risk rankings, or evidence that a location is safe. Empty, partial, stale, and unavailable source states remain distinct. Open-Meteo supplies current conditions independently of the news source. `WX OFF` disables cockpit weather rendering only; the Local Info briefing still fetches its source-backed weather values and displays the required linked Open-Meteo credit. diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 1f4d861..ff50607 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -2030,6 +2030,7 @@ silently demoting every later lookup for the session. - `/api/overpass` fans out across four public mirrors. `overpassPayloadIsData()` governs cache reads, writes, and stale fallback: only a 2xx that is neither rate-limited nor a body-level runtime error qualifies. Previously stored refusals are ignored on both fresh and stale reads, so upgrading does not require manually clearing the disk cache. - HTTP refusals such as 406 now rotate alongside the existing network, rate-limit, and runtime-error cases. A refusal from one mirror no longer prevents reaching healthy alternatives or persists under the seven-day road/month-long boundary cache TTLs. Concurrent identical queries share one mirror sequence; if it fails, both the initiating and joined callers can use the same last-good data. - A refusal every mirror agrees on is still reported with the first mirror's status and body, so a genuinely malformed query says what upstream said — but only after every mirror has had the chance to answer it. `fetchOverpassPayload` takes injectable endpoints and fetch so the rotation is tested without a live mirror (`src/overpassProxy.test.mjs`). +- Street Traffic's exact bounded highway queries take a separate 12-second path through OpenStreetMap's standard `/api/0.6/map` endpoint. The server parses its XML nodes, ways, and tags into the existing Overpass-shaped road payload, so `traffic.js` and TomTom matching do not need a second client contract. If the OSM map request fails — or the query is not the traffic shape — the established four-mirror Overpass path remains the fallback; generic Overpass-backed features are unchanged. ### Share-link v2 layer state (August 2026) @@ -2543,10 +2544,11 @@ easier to meet (detection is now on more often), but does not create it. is configured (env or Keychain `tomtom-api`/`api-key`), which enables `live` mode: TomTom flow vector tiles via the budget-governed `/api/tomtom` proxy (`.gev-cache/tomtom/`, 120 s TTL, `TOMTOM_DAILY_TILE_BUDGET` default 40k/day), - decoded client-side (`flowTiles.js`), matched onto Overpass roads + decoded client-side (`flowTiles.js`), matched onto OSM roads (`flowMatch.js`), and rendered as green/amber/red dot color + speed/density scaling (`trafficFlowStyle.js`); closures spawn no dots; unmatched roads stay - white. Road fetch bounds center on the camera look-at point (`trafficBounds.js`). + white. Road fetch bounds center on the camera look-at point (`trafficBounds.js`); + the proxy uses the bounded OSM map path before falling back to Overpass. - Development captures opened with `?trafficDebug=1` mint an interaction anchor from the exact `camera.changed` event that arms each debounced load, then emit scheduling-correlated User Timing entries for production `response.json`, road diff --git a/src/data/traffic.js b/src/data/traffic.js index 484f599..3f4ec7e 100644 --- a/src/data/traffic.js +++ b/src/data/traffic.js @@ -18,15 +18,17 @@ import { holdContinuousRender, releaseContinuousRender } from '../renderGovernor * @file Street Traffic — animated dots along OSM road polylines, colored by * live TomTom congestion when a key is configured. * - * Road geometry: OSM Overpass API (free, no auth). Fetches road polylines for - * the camera viewport, spawns PointPrimitives that lerp along pre-computed - * Cartesian3 waypoints. Camera-gated: only active below ~8 km altitude. + * Road geometry: OpenStreetMap's standard map endpoint for bounded traffic + * queries, with the server-side Overpass proxy as fallback. Fetches road + * polylines for the camera viewport, spawns PointPrimitives that lerp along + * pre-computed Cartesian3 waypoints. Camera-gated: only active below ~8 km + * altitude. * * Two modes (decided once per session via `/api/tomtom/status`): * - `sim` (keyless default): white dots at hardcoded per-road-class speeds — * the original simulation, byte-identical behavior. * - `live`: TomTom flow tiles (`flowTiles.js`) are matched onto the same - * Overpass roads (`flowMatch.js`); matched roads color/slow/densify their + * OSM roads (`flowMatch.js`); matched roads color/slow/densify their * dots by real congestion (`trafficFlowStyle.js`), closed roads spawn no * dots, and unmatched roads keep the simulated white. *