fix(osm): stop using public Overpass by default (#648) (#742)

Traffic roads come from OpenFreeMap vector tiles, or TomTom flow tiles with a
key (Hybrid by default). Military areas use OpenFreeMap polygons with a bundled
worldwide name index; wide views show named points. ALPR reads an hourly
OpenStreetMap extract. Cockpit place context and country, state and US county
outlines use bundled Natural Earth and US Census data. Overpass is used only
when an operator configures their own endpoint.

Also: camera arrivals no longer land below ground, keyless terrain retries on
429, bounded relation geometry for configured Overpass, one shared
OpenStreetMap credit, and separate tile and API transports for embedders.

Co-authored-by: theGaryLarson <[email protected]>
Co-authored-by: Milan Khanal <[email protected]>
This commit is contained in:
authored and GitHub committed 2026-09-28 19:07:57 -05:00
1 parent 81eb44340d
commit f4fcf267c6
200 files changed
+26665 -1977

No files matched your search

+172 -11
View File
@@ -1,3 +1,8 @@
import {
normalizeRoadMode,
resolveRoadMode,
TRAFFIC_ROAD_MODES,
} from './roadModes.js';
import {
trafficBucketTier,
trafficStyleProfile,
@@ -5,6 +10,33 @@ import {
import { TRAFFIC_TIMING_ENABLED } from './policy.js';
export function createControls({ state: layerState, services, parts, source }) {
/** One status line: feed, drawn road source, then degraded-coverage notes. */
function roadStatusLabel(feed) {
const source = layerState._roadSource;
if (layerState._roadError)
return `UNAVAILABLE · ${source} · Roads unavailable`;
const notes = [feed.loadingLabel];
if (!layerState._liveMode || feed.error) notes.push(`Roads: ${source}`);
// An explicit TomTom or Hybrid choice without a key draws OpenStreetMap.
if (
!layerState._liveMode &&
['tomtom', 'hybrid'].includes(layerState._roadMode)
)
notes.push(
layerState._flowStatusUnavailable
? `${layerState._roadMode === 'tomtom' ? 'TomTom roads' : 'Hybrid'} unavailable while the traffic service is unreachable`
: layerState._roadMode === 'tomtom'
? 'TomTom roads need a TomTom key'
: 'Hybrid needs a TomTom key',
);
if (layerState._surfacePending) notes.push('Local surface still loading');
if (layerState._roadWarning) notes.push(layerState._roadWarning);
else if (layerState._roadPartial) notes.push('Partial coverage');
if (layerState._detailError) notes.push('Detailed roads unavailable');
else if (layerState._detailLimited) notes.push('Reduced detail coverage');
return notes.join(' · ');
}
const { getFlowSessionStats } = source;
const methods = {
@@ -14,19 +46,48 @@ export function createControls({ state: layerState, services, parts, source }) {
icon: '🚗',
source: 'OpenStreetMap',
source: 'OpenStreetMap / TomTom',
/** @type {number} Zero — layer is self-managed via camera listener + preRender */
updateInterval: 0,
/**
* Update user-adjustable parameters (density and speed scaling).
* Update user-adjustable parameters (road source, density and speed scaling).
*
* @param {Object} [params]
* @param {'tomtom'|'osm'|'hybrid'|null} [params.roadMode] - Road source; null = default.
* @param {number} [params.densityScale] - Dot density multiplier (clamped 0.2–2.5).
* @param {number} [params.speedScale] - Dot speed multiplier (clamped 0.3–3.0).
* @param {{origin?:string}} [options] - Intent origin; restores never beat `?trafficRoads=`.
*/
setParams(params = {}) {
setParams(params = {}, { origin } = {}) {
const previousDensity = layerState._densityScale;
const previousSpeed = layerState._speedScale;
if (
Object.hasOwn(params, 'roadMode') &&
(params.roadMode === null || normalizeRoadMode(params.roadMode))
) {
const explicit = ['user', 'voice', 'tool'].includes(origin);
const mode =
!explicit && layerState._roadModeUrlOverride
? layerState._roadModeUrlOverride
: params.roadMode;
if (explicit) layerState._roadModeUrlOverride = null;
if (mode !== layerState._roadMode) {
layerState._roadMode = mode;
// Redraw from scratch: a different source means different roads.
parts.ingestion.cancelActiveFetch();
clearTimeout(layerState._retryTimer);
layerState._retryTimer = null;
layerState._loadGeneration++;
layerState._fetching = false;
layerState._lastBounds = null;
layerState._lastViewCenter = null;
parts.animation.clearDots();
if (layerState._enabled)
parts.viewport.onCameraChanged({ immediate: true });
}
}
if (typeof params.densityScale === 'number') {
layerState._densityScale = Math.max(
0.2,
@@ -39,6 +100,23 @@ export function createControls({ state: layerState, services, parts, source }) {
Math.min(3.0, params.speedScale),
);
}
if (layerState._speedScale !== previousSpeed) {
const ratio = layerState._speedScale / previousSpeed;
for (const dot of layerState._dots) {
dot.baseMps *= ratio;
dot.mps *= ratio;
}
}
if (
layerState._densityScale !== previousDensity &&
layerState._enabled &&
layerState._roads.length
)
parts.rendering.renderRoadsForAltitude(
layerState._roads,
layerState._lastRenderAltitude,
'Density',
);
// Live-mode treatment of roads TomTom has no flow data for:
// 'sim' (default) keeps them as today's white ambient dots — colored =
// real data, white = simulation; 'hide' spawns nothing on them (strict
@@ -70,6 +148,7 @@ export function createControls({ state: layerState, services, parts, source }) {
*/
getParams() {
return {
roadMode: layerState._roadMode,
densityScale: layerState._densityScale,
speedScale: layerState._speedScale,
uncoveredRoads: layerState._uncoveredMode,
@@ -78,6 +157,28 @@ export function createControls({ state: layerState, services, parts, source }) {
};
},
/** Road-source chips on the Street Traffic row (TomTom / OSM / Hybrid). */
getRowControls() {
const selected =
layerState._roadMode || resolveRoadMode(null, layerState._liveMode);
const needsKey = layerState._liveMode ? '' : ' (needs a TomTom key)';
const titles = {
tomtom: `TomTom roads only, each with its live flow${needsKey}`,
osm: 'OpenStreetMap roads, with TomTom flow matched when available',
hybrid: `TomTom roads first; OpenStreetMap fills the rest, simulated${needsKey}`,
};
const labels = { tomtom: 'TomTom', osm: 'OSM', hybrid: 'Hybrid' };
return {
chips: TRAFFIC_ROAD_MODES.map((mode) => ({
id: `roads-${mode}`,
label: labels[mode],
title: titles[mode],
active: mode === selected,
params: { roadMode: mode },
})),
};
},
/**
* Return a sub-sampled list of active dot positions for detection overlays
* (e.g. CCTV bounding-box rendering).
@@ -106,7 +207,7 @@ export function createControls({ state: layerState, services, parts, source }) {
if (!pos) continue;
const entry = {
position: pos,
id: `VEH-${String(i).padStart(4, '0')}`,
id: `VEH-${String(layerState._dots[i].id).padStart(4, '0')}`,
type: 'VEH',
};
// Live mode: the detection bracket carries the congestion signal —
@@ -124,38 +225,94 @@ export function createControls({ state: layerState, services, parts, source }) {
return result;
},
/** Visit stable dot identities and their owning geometry without allocating snapshots. */
visitMotionDots(visitor) {
for (let i = 0; i < layerState._dots.length; i++) {
const dot = layerState._dots[i];
visitor(dot.id, dot.road.key, dot.point, dot.road);
}
},
/**
* Return current layer statistics for UI status chips.
* `mode` is the CONFIGURED source — 'live' (a TomTom key is present) or
* 'sim' (keyless simulation, which the manager renders as a FALLBACK chip);
* `error` carries this instant's health, so a live-configured layer whose
* flow feed went down reads DEGRADED with the reason instead of a stale
* LIVE coverage number. `flowCoveragePct` is matched roads / roads with any
* flow candidates (0–100 int); `tilesFetched` counts flow-tile requests
* LIVE coverage number. `flowCoveragePct` is shown dots on matched roads /
* all shown dots (0–100 int), excluding closures; `tilesFetched` counts flow-tile requests
* issued to the proxy this session (decode-cache hits excluded).
* @returns {{count:number, lastUpdate:number|null, loading:boolean,
* mode:'live'|'sim', error:string|null, flowCoveragePct:number,
* tilesFetched:number}}
*/
getLoadingDiagnostics() {
let movingWaypoints = 0,
maxResidualM = 0;
for (const road of layerState._roads) {
if (!road.heightMoving) continue;
for (let i = 0; i < road.waypoints.length; i++) {
const a = road.waypoints[i],
b = road.targetWaypoints[i];
const residual = Math.hypot(a.x - b.x, a.y - b.y, a.z - b.z);
if (residual >= 0.01) movingWaypoints++;
maxResidualM = Math.max(maxResidualM, residual);
}
}
return {
fetching: Boolean(layerState._fetching),
flowPending: layerState._flowPending,
surfaceRefining: Boolean(layerState._surfaceRefining),
populationPending: Boolean(layerState._populationPending),
heightPending: Boolean(layerState._heightPending),
generation: layerState._loadGeneration,
surfaceRevision: layerState._surfaceRevision,
lastPaintStage: layerState._lastPaintStage || null,
movingWaypoints,
maxResidualM,
};
},
getStats() {
// Outstanding flow work counts as loading: the paint race can leave a
// TomTom request in flight after the roads have settled, and the shared
// loading batch has to stay open long enough to announce its failure.
const loading = layerState._fetching || layerState._flowPending > 0;
const loading =
layerState._fetching ||
layerState._flowPending > 0 ||
Boolean(layerState._surfaceRefining) ||
Boolean(layerState._populationPending) ||
Boolean(layerState._heightPending);
const { free, slow, jam, sim } = layerState._bucketCounts;
const matched = free + slow + jam;
const flowCoveragePct =
matched + sim > 0 ? Math.round((100 * matched) / (matched + sim)) : 0;
const feed = parts.model.trafficFeedPresentation({
liveMode: layerState._liveMode,
fetching: loading,
flowError: layerState._flowError,
coveragePct: layerState._flowCoveragePct,
coveragePct: flowCoveragePct,
statusUnavailable: layerState._flowStatusUnavailable,
roadSource: layerState._roadSource,
});
return {
count: layerState._count,
motion: { ...layerState._motion },
surfaceBudgetOmitted: layerState._surfaceBudgetOmitted || 0,
lastUpdate: layerState._lastUpdate,
loading,
mode: feed.mode,
error: layerState._roadError || feed.error,
flowCoveragePct: layerState._flowCoveragePct,
error:
layerState._roadError ||
layerState._roadWarning ||
layerState._detailError ||
feed.error,
roadBounds: layerState._lastBounds,
surfacePending: layerState._surfacePending || 0,
roadWarning: layerState._roadWarning || null,
detailError: layerState._detailError || null,
detailLimited: Boolean(layerState._detailLimited),
flowCoveragePct,
tilesFetched: getFlowSessionStats().tilesFetched,
...(TRAFFIC_TIMING_ENABLED
? { trafficTiming: parts.timing.getTrafficTimingDiagnostics() }
@@ -180,7 +337,11 @@ export function createControls({ state: layerState, services, parts, source }) {
// in one had better be the honest one. This is also where LIVE vs
// SIMULATED mode is surfaced, and it must never imply a live feed the
// layer does not have.
loadingLabel: feed.loadingLabel,
roadMode: resolveRoadMode(layerState._roadMode, layerState._liveMode),
roadModeRequested: layerState._roadMode,
roadSource: layerState._roadSource,
source: layerState._roadSource,
loadingLabel: roadStatusLabel(feed),
};
},
};