mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +00:00
refactor: separate traffic and bikeshare components
This commit is contained in:
1 parent
3143d00c90
commit
903fe6ba37
45 files changed
+6159
-4363
No files matched your search
@@ -0,0 +1,479 @@
|
||||
import * as Cesium from 'cesium';
|
||||
import {
|
||||
TRAFFIC_TIMING_ENABLED,
|
||||
MAX_WAYPOINTS_PER_ROAD,
|
||||
DOT_HEIGHT_OFFSET,
|
||||
} from './policy.js';
|
||||
|
||||
export function createTiming({ state: layerState, services, parts, source }) {
|
||||
/**
|
||||
* Return development timing counters for the capture harness and inertness test.
|
||||
* This named export is unused by the application and removed from production.
|
||||
* @returns {{enabled:boolean, marksInstalled:number, traceObjectsCreated:number,
|
||||
* uncorrelatedTracesDropped:number}}
|
||||
*/
|
||||
|
||||
function getTrafficTimingDiagnostics() {
|
||||
return {
|
||||
enabled: Boolean(TRAFFIC_TIMING_ENABLED),
|
||||
marksInstalled: layerState._trafficTimingMoveEndRemover ? 1 : 0,
|
||||
traceObjectsCreated: layerState._trafficTimingTracesCreated,
|
||||
uncorrelatedTracesDropped: layerState._trafficTimingDroppedTraces,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Development-only causal timing ───────────────────────
|
||||
|
||||
/**
|
||||
* Return (and optionally update) the current trace's state for a render pass.
|
||||
* This function is only reachable when `TRAFFIC_TIMING_ENABLED` is true.
|
||||
*
|
||||
* @param {Object|null} trace - Correlated load trace.
|
||||
* @param {'major'|'full'} pass - Road-fetch/render pass.
|
||||
* @param {string} [source] - Client cache or proxy/network source.
|
||||
* @returns {Object|null} Mutable pass timing state.
|
||||
*/
|
||||
|
||||
function trafficTimingPass(trace, pass, source) {
|
||||
if (!trace) return null;
|
||||
let state = trace.passes.get(pass);
|
||||
if (!state) {
|
||||
state = {
|
||||
trace,
|
||||
pass,
|
||||
source: source || 'unknown',
|
||||
proxyCache: null,
|
||||
proxyUpstream: null,
|
||||
};
|
||||
trace.passes.set(pass, state);
|
||||
} else if (source) {
|
||||
state.source = source;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
/** Build a structured-clone-safe detail object for User Timing entries. */
|
||||
|
||||
function trafficTimingDetail(state, segment, extra = {}) {
|
||||
return {
|
||||
trafficTiming: true,
|
||||
segment,
|
||||
traceId: state?.trace?.id ?? null,
|
||||
interactionId: state?.trace?.interactionId ?? null,
|
||||
cameraChangeTimestamp: state?.trace?.cameraChangeTimestamp ?? null,
|
||||
generation: state?.trace?.generation ?? null,
|
||||
cacheKey: state?.trace?.cacheKey ?? null,
|
||||
pass: state?.pass || 'load',
|
||||
source: state?.source || 'unknown',
|
||||
proxyCache: state?.proxyCache || null,
|
||||
proxyUpstream: state?.proxyUpstream || null,
|
||||
...extra,
|
||||
};
|
||||
}
|
||||
|
||||
/** Add a uniquely named User Timing mark and return its name. */
|
||||
|
||||
function trafficTimingMark(state, phase, extra = {}, startTime) {
|
||||
const traceId = state?.trace?.id ?? 'interaction';
|
||||
const pass = state?.pass || 'load';
|
||||
const name = `traffic:${traceId}:${pass}:${phase}:${++layerState._trafficTimingSequence}`;
|
||||
const options = { detail: trafficTimingDetail(state, phase, extra) };
|
||||
if (Number.isFinite(startTime)) options.startTime = startTime;
|
||||
performance.mark(name, options);
|
||||
return name;
|
||||
}
|
||||
|
||||
/** Emit a named User Timing measure between two marks. */
|
||||
|
||||
function trafficTimingMeasure(segment, state, start, end, extra = {}) {
|
||||
performance.measure(`traffic:${segment}:${state?.pass || 'load'}`, {
|
||||
start,
|
||||
end,
|
||||
detail: trafficTimingDetail(state, segment, extra),
|
||||
});
|
||||
}
|
||||
|
||||
/** Emit an aggregate-duration measure without pretending its work was contiguous. */
|
||||
|
||||
function trafficTimingAggregate(
|
||||
segment,
|
||||
state,
|
||||
anchorTime,
|
||||
duration,
|
||||
extra = {},
|
||||
) {
|
||||
const start = trafficTimingMark(
|
||||
state,
|
||||
`${segment}-aggregate-start`,
|
||||
extra,
|
||||
anchorTime,
|
||||
);
|
||||
const end = trafficTimingMark(
|
||||
state,
|
||||
`${segment}-aggregate-end`,
|
||||
extra,
|
||||
anchorTime + duration,
|
||||
);
|
||||
trafficTimingMeasure(segment, state, start, end, {
|
||||
aggregate: true,
|
||||
...extra,
|
||||
});
|
||||
}
|
||||
|
||||
/** Clear only this module's stale User Timing entries before a new debug run. */
|
||||
|
||||
function clearTrafficTimingEntries() {
|
||||
const markNames = new Set(
|
||||
performance
|
||||
.getEntriesByType('mark')
|
||||
.filter((entry) => entry.name.startsWith('traffic:'))
|
||||
.map((entry) => entry.name),
|
||||
);
|
||||
const measureNames = new Set(
|
||||
performance
|
||||
.getEntriesByType('measure')
|
||||
.filter((entry) => entry.name.startsWith('traffic:'))
|
||||
.map((entry) => entry.name),
|
||||
);
|
||||
for (const name of markNames) performance.clearMarks(name);
|
||||
for (const name of measureNames) performance.clearMeasures(name);
|
||||
}
|
||||
|
||||
/** Return the stable User Timing mark name for a scheduling interaction. */
|
||||
|
||||
function trafficTimingCameraChangeMarkName(interactionId) {
|
||||
return `traffic:interaction:${interactionId}:last-camera-change`;
|
||||
}
|
||||
|
||||
/** Mint and mark the exact camera-change interaction that armed a debounced load. */
|
||||
|
||||
function markTrafficTimingCameraChange() {
|
||||
const interactionId = ++layerState._trafficTimingSequence;
|
||||
const timestamp = performance.now();
|
||||
const anchor = { interactionId, timestamp };
|
||||
layerState._trafficTimingCurrentAnchor = anchor;
|
||||
performance.mark(trafficTimingCameraChangeMarkName(interactionId), {
|
||||
startTime: timestamp,
|
||||
detail: {
|
||||
trafficTiming: true,
|
||||
segment: 'last-camera-change',
|
||||
interactionId,
|
||||
timestamp,
|
||||
},
|
||||
});
|
||||
return anchor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark Cesium's diagnostic moveEnd notification. Cesium normally emits this
|
||||
* about 500 ms after stillness, so fetch has typically already started and
|
||||
* never waits for this boundary.
|
||||
*/
|
||||
|
||||
function markTrafficTimingMoveEnd() {
|
||||
const diagnosticId = ++layerState._trafficTimingSequence;
|
||||
const timestamp = performance.now();
|
||||
const name = `traffic:diagnostic:${diagnosticId}:camera-move-end`;
|
||||
performance.mark(name, {
|
||||
startTime: timestamp,
|
||||
detail: {
|
||||
trafficTiming: true,
|
||||
segment: 'camera-move-end',
|
||||
diagnosticOnly: true,
|
||||
cameraEventWaitTimeMs: 500,
|
||||
fetchWaitsForMoveEnd: false,
|
||||
timestamp,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Instrumented twin of `parseRoads`. Operation ordering and road output match
|
||||
* the normal function; debug-only clocks accumulate synchronous height and
|
||||
* waypoint-materialization time independently.
|
||||
*/
|
||||
|
||||
function parseRoadsTimed(overpassData, trace) {
|
||||
/* TRACE_ONLY_BEGIN */
|
||||
const _trafficTimingState = trafficTimingPass(
|
||||
trace,
|
||||
trace?.currentPass || 'full',
|
||||
);
|
||||
const _trafficTimingParseStartTime = performance.now();
|
||||
const _trafficTimingParseStart = trafficTimingMark(
|
||||
_trafficTimingState,
|
||||
'road-parse-start',
|
||||
{},
|
||||
_trafficTimingParseStartTime,
|
||||
);
|
||||
/* TRACE_ONLY_END */
|
||||
if (!overpassData || !overpassData.elements) {
|
||||
/* TRACE_ONLY_BEGIN */
|
||||
const _trafficTimingParseEnd = trafficTimingMark(
|
||||
_trafficTimingState,
|
||||
'road-parse-end',
|
||||
{ roadCount: 0 },
|
||||
);
|
||||
trafficTimingMeasure(
|
||||
'road-parse-total',
|
||||
_trafficTimingState,
|
||||
_trafficTimingParseStart,
|
||||
_trafficTimingParseEnd,
|
||||
{ roadCount: 0 },
|
||||
);
|
||||
trafficTimingAggregate(
|
||||
'sample-height-total',
|
||||
_trafficTimingState,
|
||||
_trafficTimingParseStartTime,
|
||||
0,
|
||||
{
|
||||
sampleHeightCalls: 0,
|
||||
sampleHeightMeanMs: 0,
|
||||
distinctCells: 0,
|
||||
roadCount: 0,
|
||||
},
|
||||
);
|
||||
trafficTimingAggregate(
|
||||
'waypoint-materialization',
|
||||
_trafficTimingState,
|
||||
_trafficTimingParseStartTime,
|
||||
0,
|
||||
{ roadCount: 0 },
|
||||
);
|
||||
/* TRACE_ONLY_END */
|
||||
return [];
|
||||
}
|
||||
|
||||
const roads = [];
|
||||
/* TRACE_ONLY_BEGIN */
|
||||
const _trafficTimingSampledCells = new Set();
|
||||
let _trafficTimingSampleHeightCalls = 0;
|
||||
let _trafficTimingSampleHeightMs = 0;
|
||||
let _trafficTimingWaypointMaterializationMs = 0;
|
||||
/* TRACE_ONLY_END */
|
||||
for (const el of overpassData.elements) {
|
||||
if (el.type !== 'way' || !el.geometry || el.geometry.length < 2) continue;
|
||||
|
||||
const rawCoords = el.geometry.map((g) => [g.lon, g.lat]);
|
||||
const simplifyStep =
|
||||
rawCoords.length > MAX_WAYPOINTS_PER_ROAD
|
||||
? Math.ceil(rawCoords.length / MAX_WAYPOINTS_PER_ROAD)
|
||||
: 1;
|
||||
const coords = [];
|
||||
for (let i = 0; i < rawCoords.length; i += simplifyStep) {
|
||||
coords.push(rawCoords[i]);
|
||||
}
|
||||
|
||||
const last = rawCoords[rawCoords.length - 1];
|
||||
const tail = coords[coords.length - 1];
|
||||
if (!tail || tail[0] !== last[0] || tail[1] !== last[1]) {
|
||||
coords.push(last);
|
||||
}
|
||||
if (coords.length < 2) continue;
|
||||
|
||||
const type = el.tags?.highway || 'unclassified';
|
||||
const onewayTag = el.tags?.oneway;
|
||||
const oneway =
|
||||
onewayTag === 'yes' ||
|
||||
onewayTag === '1' ||
|
||||
onewayTag === 'true' ||
|
||||
el.tags?.junction === 'roundabout'
|
||||
? 1
|
||||
: onewayTag === '-1'
|
||||
? -1
|
||||
: 0;
|
||||
|
||||
let baseHeight = 0;
|
||||
const firstCoord = coords[0];
|
||||
if (layerState._viewer?.scene?.sampleHeightSupported && firstCoord) {
|
||||
/* TRACE_ONLY_BEGIN */
|
||||
_trafficTimingSampleHeightCalls += 1;
|
||||
_trafficTimingSampledCells.add(
|
||||
`${firstCoord[1].toFixed(3)},${firstCoord[0].toFixed(3)}`,
|
||||
);
|
||||
/* TRACE_ONLY_END */
|
||||
const carto = Cesium.Cartographic.fromDegrees(
|
||||
firstCoord[0],
|
||||
firstCoord[1],
|
||||
);
|
||||
/* TRACE_ONLY_BEGIN */
|
||||
const _trafficTimingSampleStart = performance.now();
|
||||
/* TRACE_ONLY_END */
|
||||
const sampled = layerState._viewer.scene.sampleHeight(carto);
|
||||
/* TRACE_ONLY_BEGIN */
|
||||
_trafficTimingSampleHeightMs +=
|
||||
performance.now() - _trafficTimingSampleStart;
|
||||
/* TRACE_ONLY_END */
|
||||
if (Number.isFinite(sampled)) baseHeight = sampled;
|
||||
}
|
||||
|
||||
/* TRACE_ONLY_BEGIN */
|
||||
const _trafficTimingMaterializeStart = performance.now();
|
||||
/* TRACE_ONLY_END */
|
||||
const waypoints = coords.map(([lng, lat]) => {
|
||||
const h = baseHeight + DOT_HEIGHT_OFFSET;
|
||||
return Cesium.Cartesian3.fromDegrees(lng, lat, h);
|
||||
});
|
||||
const segmentDist = [];
|
||||
for (let i = 0; i < waypoints.length - 1; i++) {
|
||||
segmentDist.push(
|
||||
Cesium.Cartesian3.distance(waypoints[i], waypoints[i + 1]),
|
||||
);
|
||||
}
|
||||
/* TRACE_ONLY_BEGIN */
|
||||
_trafficTimingWaypointMaterializationMs +=
|
||||
performance.now() - _trafficTimingMaterializeStart;
|
||||
/* TRACE_ONLY_END */
|
||||
|
||||
roads.push({ coords, type, oneway, waypoints, segmentDist });
|
||||
}
|
||||
|
||||
/* TRACE_ONLY_BEGIN */
|
||||
const _trafficTimingMetrics = {
|
||||
roadCount: roads.length,
|
||||
sampleHeightCalls: _trafficTimingSampleHeightCalls,
|
||||
sampleHeightMeanMs: _trafficTimingSampleHeightCalls
|
||||
? _trafficTimingSampleHeightMs / _trafficTimingSampleHeightCalls
|
||||
: 0,
|
||||
distinctCells: _trafficTimingSampledCells.size,
|
||||
};
|
||||
trafficTimingAggregate(
|
||||
'sample-height-total',
|
||||
_trafficTimingState,
|
||||
_trafficTimingParseStartTime,
|
||||
_trafficTimingSampleHeightMs,
|
||||
_trafficTimingMetrics,
|
||||
);
|
||||
trafficTimingAggregate(
|
||||
'waypoint-materialization',
|
||||
_trafficTimingState,
|
||||
_trafficTimingParseStartTime,
|
||||
_trafficTimingWaypointMaterializationMs,
|
||||
_trafficTimingMetrics,
|
||||
);
|
||||
const _trafficTimingParseEnd = trafficTimingMark(
|
||||
_trafficTimingState,
|
||||
'road-parse-end',
|
||||
_trafficTimingMetrics,
|
||||
);
|
||||
trafficTimingMeasure(
|
||||
'road-parse-total',
|
||||
_trafficTimingState,
|
||||
_trafficTimingParseStart,
|
||||
_trafficTimingParseEnd,
|
||||
_trafficTimingMetrics,
|
||||
);
|
||||
/* TRACE_ONLY_END */
|
||||
return roads;
|
||||
}
|
||||
|
||||
/** Resolve a render label into its correlated pass and data source. */
|
||||
|
||||
function trafficTimingRenderState(trace, label) {
|
||||
const pass = label.toLowerCase().includes('major') ? 'major' : 'full';
|
||||
const source = label.startsWith('Cache') ? 'client-cache' : 'proxy';
|
||||
return trafficTimingPass(trace, pass, source);
|
||||
}
|
||||
|
||||
/** Record the first Cesium postRender following a completed dot render. */
|
||||
|
||||
function scheduleTrafficTimingPostRender(
|
||||
state,
|
||||
renderEnd,
|
||||
renderId,
|
||||
renderMetrics,
|
||||
) {
|
||||
if (!layerState._viewer?.scene) return;
|
||||
let remove = null;
|
||||
remove = layerState._viewer.scene.postRender.addEventListener(() => {
|
||||
remove?.();
|
||||
layerState._trafficTimingPostRenderRemovers?.delete(remove);
|
||||
const visibleTime = performance.now();
|
||||
const postRender = trafficTimingMark(
|
||||
state,
|
||||
'next-post-render',
|
||||
{
|
||||
renderId,
|
||||
...renderMetrics,
|
||||
},
|
||||
visibleTime,
|
||||
);
|
||||
const firstVisible = trafficTimingMark(
|
||||
state,
|
||||
'first-visible-pixel',
|
||||
{
|
||||
renderId,
|
||||
visibleBoundary: 'next-postRender',
|
||||
...renderMetrics,
|
||||
},
|
||||
visibleTime,
|
||||
);
|
||||
trafficTimingMeasure(
|
||||
'render-to-post-render',
|
||||
state,
|
||||
renderEnd,
|
||||
postRender,
|
||||
{
|
||||
renderId,
|
||||
visibleBoundary: 'next-postRender',
|
||||
...renderMetrics,
|
||||
},
|
||||
);
|
||||
trafficTimingMeasure(
|
||||
'last-camera-change-to-first-visible',
|
||||
state,
|
||||
state.trace.cameraChangeMark,
|
||||
firstVisible,
|
||||
{ renderId, visibleBoundary: 'next-postRender', ...renderMetrics },
|
||||
);
|
||||
});
|
||||
layerState._trafficTimingPostRenderRemovers?.add(remove);
|
||||
}
|
||||
|
||||
/** Start a scheduling-correlated debug trace, or count and drop an unpaired load. */
|
||||
|
||||
async function loadRoadsForBoundsTimed(bounds, altitude, expectedAnchor) {
|
||||
const generation = layerState._loadGeneration + 1;
|
||||
if (
|
||||
!expectedAnchor ||
|
||||
expectedAnchor !== layerState._trafficTimingCurrentAnchor
|
||||
) {
|
||||
layerState._trafficTimingDroppedTraces += 1;
|
||||
await parts.ingestion.loadRoadsForBounds(bounds, altitude);
|
||||
return;
|
||||
}
|
||||
layerState._trafficTimingCurrentAnchor = null;
|
||||
const clamped = parts.viewport.clampBounds(bounds);
|
||||
const trace = {
|
||||
id: ++layerState._trafficTimingSequence,
|
||||
interactionId: expectedAnchor.interactionId,
|
||||
cameraChangeTimestamp: expectedAnchor.timestamp,
|
||||
cameraChangeMark: trafficTimingCameraChangeMarkName(
|
||||
expectedAnchor.interactionId,
|
||||
),
|
||||
generation,
|
||||
cacheKey: `${clamped.south.toFixed(4)},${clamped.west.toFixed(4)},${clamped.north.toFixed(4)},${clamped.east.toFixed(4)}`,
|
||||
currentPass: null,
|
||||
renderSequence: 0,
|
||||
passes: new Map(),
|
||||
};
|
||||
layerState._trafficTimingTracesCreated += 1;
|
||||
await parts.ingestion.loadRoadsForBounds(bounds, altitude, trace);
|
||||
}
|
||||
return {
|
||||
getTrafficTimingDiagnostics,
|
||||
trafficTimingPass,
|
||||
trafficTimingDetail,
|
||||
trafficTimingMark,
|
||||
trafficTimingMeasure,
|
||||
trafficTimingAggregate,
|
||||
clearTrafficTimingEntries,
|
||||
trafficTimingCameraChangeMarkName,
|
||||
markTrafficTimingCameraChange,
|
||||
markTrafficTimingMoveEnd,
|
||||
parseRoadsTimed,
|
||||
trafficTimingRenderState,
|
||||
scheduleTrafficTimingPostRender,
|
||||
loadRoadsForBoundsTimed,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user