mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 07:42:23 +00:00
Keep city stations, vehicle tracks and trail history across globe switches
This commit is contained in:
1 parent
c4a97a653c
commit
24190a3e35
11 files changed
+288
-79
No files matched your search
@@ -0,0 +1,127 @@
|
|||||||
|
import puppeteer from 'puppeteer';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { blockPaidQaMaps } from './qa-map-policy.mjs';
|
||||||
|
import { switchGlobe } from './qa-switch-globe.mjs';
|
||||||
|
import { installTransitFixture } from './fixtures/w2/transit.mjs';
|
||||||
|
import { installCombinedFixture } from './fixtures/w2/combined.mjs';
|
||||||
|
const url = new URL(process.argv[2]),
|
||||||
|
bus = process.argv.includes('--transit'),
|
||||||
|
id = bus ? 'transit' : 'bikeshare';
|
||||||
|
if (
|
||||||
|
!['localhost', '127.0.0.1'].includes(url.hostname) ||
|
||||||
|
['', '4173', '4185', '4186', '4187', '4188', '4189', '4194'].includes(
|
||||||
|
url.port,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
throw Error('Supply local keyless preview URL');
|
||||||
|
url.search = '?engine=navara&welcome=0';
|
||||||
|
url.hash =
|
||||||
|
'v=2&lat=42.3554&lon=-71.0605&alt=2700&pitch=-90&heading=0&map=sentinel2&l=';
|
||||||
|
const b = await puppeteer.launch({
|
||||||
|
headless: true,
|
||||||
|
args: ['--use-angle=metal', '--enable-gpu'],
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const p = await b.newPage();
|
||||||
|
await blockPaidQaMaps(p);
|
||||||
|
await p.setViewport({ width: 1280, height: 900 });
|
||||||
|
const errors = [];
|
||||||
|
p.on('pageerror', (e) => {
|
||||||
|
errors.push(e.message);
|
||||||
|
console.log('error', e.message);
|
||||||
|
});
|
||||||
|
const fx = bus
|
||||||
|
? await installTransitFixture(p)
|
||||||
|
: await installCombinedFixture(p);
|
||||||
|
fx.setStep?.(9);
|
||||||
|
await p.goto(url.href);
|
||||||
|
await p.waitForFunction(
|
||||||
|
() =>
|
||||||
|
window.__godsEyeView?.engine?.kind === 'navara' &&
|
||||||
|
document.querySelector('#loading-screen.hidden'),
|
||||||
|
);
|
||||||
|
await p.evaluate(async (id) => {
|
||||||
|
const { dataManager, styleManager, tileset } = window.__godsEyeView;
|
||||||
|
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
||||||
|
throw Error('Expected keyless preview');
|
||||||
|
await styleManager.initialRestorePromise;
|
||||||
|
window.qaUrban = dataManager.layers.get(id).module;
|
||||||
|
await dataManager.setEnabled(id, true, { origin: 'user' });
|
||||||
|
}, id);
|
||||||
|
await p.waitForFunction(() => window.qaUrban.getStats().count > 0, {
|
||||||
|
timeout: 30000,
|
||||||
|
});
|
||||||
|
const handle = await p.waitForFunction(
|
||||||
|
() => {
|
||||||
|
const a = window.__godsEyeView;
|
||||||
|
for (const r of window.qaUrban.getDetectableObjects({
|
||||||
|
maxCount: 500,
|
||||||
|
seed: 0,
|
||||||
|
})) {
|
||||||
|
const s = a.engine.projector.worldToWindow(r.position);
|
||||||
|
if (!s || s.x < 420 || s.x > 860 || s.y < 205 || s.y > 670) continue;
|
||||||
|
const pk = a.engine.picker.pick(s);
|
||||||
|
if (pk?.id === r.sourceId || pk?.id?.id === r.sourceId)
|
||||||
|
return { x: s.x, y: s.y, id: r.sourceId };
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
{ timeout: 30000 },
|
||||||
|
);
|
||||||
|
const target = await handle.jsonValue();
|
||||||
|
await p.mouse.click(target.x, target.y);
|
||||||
|
await p.waitForFunction(
|
||||||
|
(key) =>
|
||||||
|
window.qaUrban
|
||||||
|
.getDetectableObjects({ maxCount: 500, seed: 0 })
|
||||||
|
.some((r) => r.sourceId === key && r.skipLabel),
|
||||||
|
{},
|
||||||
|
target.id,
|
||||||
|
);
|
||||||
|
const reads = () =>
|
||||||
|
fx.requests.filter((path) =>
|
||||||
|
bus
|
||||||
|
? path.includes('/transit/trail/')
|
||||||
|
: path.includes('station_information'),
|
||||||
|
).length;
|
||||||
|
if (bus)
|
||||||
|
await p.waitForFunction(
|
||||||
|
() => window.qaUrban.getDetectableObjects({ maxCount: 500 }).length > 0,
|
||||||
|
);
|
||||||
|
const original = reads();
|
||||||
|
const first = await p.evaluate(() =>
|
||||||
|
window.qaUrban.getAnalystRecords(500).map((v) => v.id),
|
||||||
|
);
|
||||||
|
for (const kind of ['cesium', 'navara']) {
|
||||||
|
await switchGlobe(p, kind);
|
||||||
|
await p.waitForFunction(
|
||||||
|
(key) =>
|
||||||
|
window.qaUrban
|
||||||
|
.getDetectableObjects({ maxCount: 500, seed: 0 })
|
||||||
|
.some((r) => r.sourceId === key && r.skipLabel),
|
||||||
|
{ timeout: 20000 },
|
||||||
|
target.id,
|
||||||
|
);
|
||||||
|
const v = await p.evaluate(
|
||||||
|
(id) => ({
|
||||||
|
same:
|
||||||
|
window.qaUrban ===
|
||||||
|
window.__godsEyeView.dataManager.layers.get(id).module,
|
||||||
|
stats: window.qaUrban.getStats(),
|
||||||
|
ids: window.qaUrban.getAnalystRecords(500).map((v) => v.id),
|
||||||
|
}),
|
||||||
|
id,
|
||||||
|
);
|
||||||
|
console.log(kind, id, v.stats.count, target.id, reads());
|
||||||
|
assert.ok(v.same);
|
||||||
|
assert.deepEqual(v.ids, first);
|
||||||
|
assert.equal(reads(), original);
|
||||||
|
}
|
||||||
|
assert.deepEqual(errors, []);
|
||||||
|
await fx.dispose?.();
|
||||||
|
console.log(
|
||||||
|
'PASS viewport activation, real pick and original city data across two switches',
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await b.close();
|
||||||
|
}
|
||||||
@@ -253,11 +253,11 @@ export function createApplicationCatalog({
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
'transit',
|
'transit',
|
||||||
() => createApplicationTransit({ surface, source: sources.transit }),
|
() => sessionModel('transit', () => createApplicationTransit({ surface, source: sources.transit })),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'bikeshare',
|
'bikeshare',
|
||||||
() => createApplicationBikeshare({ source: sources.bikeshare }),
|
() => sessionModel('bikeshare', () => createApplicationBikeshare({ source: sources.bikeshare })),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'directions',
|
'directions',
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ export function createBikeshareLayer({ services, source, rendererFactory }) {
|
|||||||
let renderer = null;
|
let renderer = null;
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
const lifecycle = {
|
const lifecycle = {
|
||||||
init(viewer, options = {}) {
|
init(viewer, options) {
|
||||||
|
return this.attachRenderer(viewer, options);
|
||||||
|
},
|
||||||
|
attachRenderer(viewer, options = {}) {
|
||||||
if (initialized)
|
if (initialized)
|
||||||
throw new Error('Bikeshare layer is already initialized');
|
throw new Error('Bikeshare layer is already initialized');
|
||||||
renderer ??= rendererFactory({
|
renderer ??= rendererFactory({
|
||||||
@@ -36,12 +39,44 @@ export function createBikeshareLayer({ services, source, rendererFactory }) {
|
|||||||
Object.assign(parts, renderer);
|
Object.assign(parts, renderer);
|
||||||
initialized = true;
|
initialized = true;
|
||||||
try {
|
try {
|
||||||
return renderer.lifecycle.methods.init(viewer, options);
|
state._restoringViewport = Boolean(options.waitForCameraRestore);
|
||||||
|
renderer.lifecycle.methods.init(viewer, options);
|
||||||
|
if (state._enabled) {
|
||||||
|
const selected = state._selectedKey;
|
||||||
|
for (const city of state._activeCityIds) {
|
||||||
|
const stations = state._stationInfoCache.get(city);
|
||||||
|
if (!stations) continue;
|
||||||
|
parts.rendering.ensureCityPoints(city, stations);
|
||||||
|
const status = state._statusCache.get(city)?.statusMap;
|
||||||
|
if (status) parts.rendering.applyStatusToPoints(city, status);
|
||||||
|
}
|
||||||
|
if (selected) parts.selection._selectStation(selected);
|
||||||
|
renderer.lifecycle.methods.show(viewer);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
initialized = false;
|
initialized = false;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
resumeViewport() {
|
||||||
|
if (!state._restoringViewport) return;
|
||||||
|
state._restoringViewport = false;
|
||||||
|
if (state._enabled) parts.viewport?.onCameraChanged();
|
||||||
|
},
|
||||||
|
detachRenderer() {
|
||||||
|
if (!renderer) return;
|
||||||
|
state._restoringViewport = true;
|
||||||
|
clearTimeout(state._cameraDebounceTimer);
|
||||||
|
const selected = state._selectedKey;
|
||||||
|
renderer.lifecycle.methods.detach();
|
||||||
|
state._selectedKey = selected;
|
||||||
|
state._cityRuntime.clear();
|
||||||
|
state._stationRenderMap.clear();
|
||||||
|
state._viewer = state._engine = null;
|
||||||
|
renderer = null;
|
||||||
|
initialized = false;
|
||||||
|
for (const key of ['rendering', 'selection', 'viewport', 'lifecycle']) delete parts[key];
|
||||||
|
},
|
||||||
enable(...args) {
|
enable(...args) {
|
||||||
return renderer?.lifecycle.methods.enable(...args);
|
return renderer?.lifecycle.methods.enable(...args);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ export function createIngestion({
|
|||||||
try {
|
try {
|
||||||
const statusMap = await loadCityStationStatus(cityId, generation);
|
const statusMap = await loadCityStationStatus(cityId, generation);
|
||||||
if (!isCurrent(cityId)) return;
|
if (!isCurrent(cityId)) return;
|
||||||
parts.rendering.applyStatusToPoints(cityId, statusMap);
|
parts.rendering?.applyStatusToPoints(cityId, statusMap);
|
||||||
layerState._failedCityIds.delete(cityId);
|
layerState._failedCityIds.delete(cityId);
|
||||||
refreshed = true;
|
refreshed = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -35,29 +35,6 @@ export function createLifecycle({
|
|||||||
registerSpriteCollection('bikeshare', layerState._pointCollection);
|
registerSpriteCollection('bikeshare', layerState._pointCollection);
|
||||||
layerState._pointCollection.show = false;
|
layerState._pointCollection.show = false;
|
||||||
|
|
||||||
layerState._enabled = false;
|
|
||||||
layerState._cameraDebounceTimer = null;
|
|
||||||
layerState._cameraChangedAttached = false;
|
|
||||||
layerState._altitudeGateEnabled = false;
|
|
||||||
layerState._proximityGeneration = 0;
|
|
||||||
|
|
||||||
layerState._activeCityIds = new Set();
|
|
||||||
layerState._cityRuntime = new Map();
|
|
||||||
layerState._stationInfoCache = new Map();
|
|
||||||
layerState._statusCache = new Map();
|
|
||||||
layerState._inFlightInfo = new Map();
|
|
||||||
layerState._inFlightStatus = new Map();
|
|
||||||
layerState._stationRenderMap = new Map();
|
|
||||||
layerState._clickHandler = null;
|
|
||||||
layerState._selectedKey = null;
|
|
||||||
layerState._selectedEntity = null;
|
|
||||||
layerState._count = 0;
|
|
||||||
layerState._lastUpdate = null;
|
|
||||||
layerState._loading = false;
|
|
||||||
layerState._loadingOps = 0;
|
|
||||||
layerState._failedCityIds.clear();
|
|
||||||
layerState._limitWarned = false;
|
|
||||||
|
|
||||||
layerState._overlayHost.setVisible(
|
layerState._overlayHost.setVisible(
|
||||||
BIKESHARE_SELECTED_OVERLAY_SOURCE_ID,
|
BIKESHARE_SELECTED_OVERLAY_SOURCE_ID,
|
||||||
false,
|
false,
|
||||||
@@ -79,9 +56,12 @@ export function createLifecycle({
|
|||||||
*/
|
*/
|
||||||
enable(viewer) {
|
enable(viewer) {
|
||||||
if (layerState._enabled) return;
|
if (layerState._enabled) return;
|
||||||
viewer = layerState._viewer;
|
|
||||||
layerState._enabled = true;
|
layerState._enabled = true;
|
||||||
layerState._failedCityIds.clear();
|
layerState._failedCityIds.clear();
|
||||||
|
this.show(viewer);
|
||||||
|
},
|
||||||
|
show(viewer) {
|
||||||
|
viewer = layerState._viewer;
|
||||||
layerState._pointCollection.show = true;
|
layerState._pointCollection.show = true;
|
||||||
layerState._overlayHost.setVisible(
|
layerState._overlayHost.setVisible(
|
||||||
BIKESHARE_SELECTED_OVERLAY_SOURCE_ID,
|
BIKESHARE_SELECTED_OVERLAY_SOURCE_ID,
|
||||||
@@ -149,6 +129,23 @@ export function createLifecycle({
|
|||||||
layerState._loadingOps = 0;
|
layerState._loadingOps = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
detach() {
|
||||||
|
const viewer = layerState._viewer;
|
||||||
|
parts.selection._clearSelection();
|
||||||
|
layerState._clickHandler?.destroy();
|
||||||
|
layerState._clickHandler = null;
|
||||||
|
document.removeEventListener('keydown', parts.selection._onKeyDown);
|
||||||
|
unregisterPickOwner('bikeshare');
|
||||||
|
if (layerState._cameraChangedAttached) {
|
||||||
|
viewer.camera.changed.removeEventListener(parts.viewport.onCameraChanged);
|
||||||
|
releaseCameraSensitivity(viewer.camera, 'bikeshare');
|
||||||
|
layerState._cameraChangedAttached = false;
|
||||||
|
}
|
||||||
|
unregisterSpriteCollection?.('bikeshare', layerState._pointCollection);
|
||||||
|
viewer.scene.primitives.remove(layerState._pointCollection);
|
||||||
|
layerState._pointCollection = null;
|
||||||
|
},
|
||||||
|
|
||||||
/** Permanently release primitives, handlers, and the selected host source. */
|
/** Permanently release primitives, handlers, and the selected host source. */
|
||||||
destroy(viewer) {
|
destroy(viewer) {
|
||||||
viewer = layerState._viewer;
|
viewer = layerState._viewer;
|
||||||
|
|||||||
@@ -311,27 +311,6 @@ export function createBikeshareNavaraRenderer(engine, context) {
|
|||||||
if (destroyed) throw new Error('Bikeshare renderer is destroyed');
|
if (destroyed) throw new Error('Bikeshare renderer is destroyed');
|
||||||
state._engine = engine;
|
state._engine = engine;
|
||||||
state._viewer = null;
|
state._viewer = null;
|
||||||
state._enabled = false;
|
|
||||||
state._cameraDebounceTimer = null;
|
|
||||||
state._cameraChangedAttached = false;
|
|
||||||
state._altitudeGateEnabled = false;
|
|
||||||
state._proximityGeneration = 0;
|
|
||||||
state._activeCityIds = new Set();
|
|
||||||
state._cityRuntime = new Map();
|
|
||||||
state._stationInfoCache = new Map();
|
|
||||||
state._statusCache = new Map();
|
|
||||||
state._inFlightInfo = new Map();
|
|
||||||
state._inFlightStatus = new Map();
|
|
||||||
state._stationRenderMap = new Map();
|
|
||||||
state._clickHandler = null;
|
|
||||||
state._selectedKey = null;
|
|
||||||
state._selectedEntity = null;
|
|
||||||
state._count = 0;
|
|
||||||
state._lastUpdate = null;
|
|
||||||
state._loading = false;
|
|
||||||
state._loadingOps = 0;
|
|
||||||
state._error = null;
|
|
||||||
state._limitWarned = false;
|
|
||||||
normalSet.setCount(0);
|
normalSet.setCount(0);
|
||||||
selectedSet.setCount(0);
|
selectedSet.setCount(0);
|
||||||
normalSet.commit();
|
normalSet.commit();
|
||||||
@@ -345,6 +324,9 @@ export function createBikeshareNavaraRenderer(engine, context) {
|
|||||||
if (destroyed || state._enabled) return;
|
if (destroyed || state._enabled) return;
|
||||||
state._enabled = true;
|
state._enabled = true;
|
||||||
state._error = null;
|
state._error = null;
|
||||||
|
this.show();
|
||||||
|
},
|
||||||
|
show() {
|
||||||
state._overlayHost.setVisible(
|
state._overlayHost.setVisible(
|
||||||
BIKESHARE_SELECTED_OVERLAY_SOURCE_ID,
|
BIKESHARE_SELECTED_OVERLAY_SOURCE_ID,
|
||||||
true,
|
true,
|
||||||
@@ -389,6 +371,15 @@ export function createBikeshareNavaraRenderer(engine, context) {
|
|||||||
state._loading = false;
|
state._loading = false;
|
||||||
state._loadingOps = 0;
|
state._loadingOps = 0;
|
||||||
},
|
},
|
||||||
|
detach() {
|
||||||
|
selection._clearSelection();
|
||||||
|
selectionRemove?.();
|
||||||
|
cameraRemove?.();
|
||||||
|
state._cameraChangedAttached = false;
|
||||||
|
normalSet.destroy();
|
||||||
|
selectedSet.destroy();
|
||||||
|
destroyed = true;
|
||||||
|
},
|
||||||
destroy() {
|
destroy() {
|
||||||
if (destroyed) return;
|
if (destroyed) return;
|
||||||
this.disable();
|
this.disable();
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export function createViewport({ state: layerState, services, parts, camera }) {
|
|||||||
governorRequestRender('bikeshare-deactivate');
|
governorRequestRender('bikeshare-deactivate');
|
||||||
parts.ingestion.abortInFlight(layerState._inFlightInfo, cityId);
|
parts.ingestion.abortInFlight(layerState._inFlightInfo, cityId);
|
||||||
parts.ingestion.abortInFlight(layerState._inFlightStatus, cityId);
|
parts.ingestion.abortInFlight(layerState._inFlightStatus, cityId);
|
||||||
parts.rendering.removeCityPoints(cityId);
|
parts.rendering?.removeCityPoints(cityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Deactivate all currently active cities and clear the active set. */
|
/** Deactivate all currently active cities and clear the active set. */
|
||||||
@@ -107,12 +107,12 @@ export function createViewport({ state: layerState, services, parts, camera }) {
|
|||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
parts.rendering.ensureCityPoints(cityId, stationMap);
|
parts.rendering?.ensureCityPoints(cityId, stationMap);
|
||||||
|
|
||||||
// Apply any cached status immediately for snappier initial rendering
|
// Apply any cached status immediately for snappier initial rendering
|
||||||
const cachedStatus = layerState._statusCache.get(cityId)?.statusMap;
|
const cachedStatus = layerState._statusCache.get(cityId)?.statusMap;
|
||||||
if (cachedStatus) {
|
if (cachedStatus) {
|
||||||
parts.rendering.applyStatusToPoints(cityId, cachedStatus);
|
parts.rendering?.applyStatusToPoints(cityId, cachedStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusMap = await parts.ingestion.loadCityStationStatus(
|
const statusMap = await parts.ingestion.loadCityStationStatus(
|
||||||
@@ -125,7 +125,7 @@ export function createViewport({ state: layerState, services, parts, camera }) {
|
|||||||
generation !== layerState._proximityGeneration
|
generation !== layerState._proximityGeneration
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
parts.rendering.applyStatusToPoints(cityId, statusMap);
|
parts.rendering?.applyStatusToPoints(cityId, statusMap);
|
||||||
layerState._lastUpdate = Date.now();
|
layerState._lastUpdate = Date.now();
|
||||||
layerState._failedCityIds.delete(cityId);
|
layerState._failedCityIds.delete(cityId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -152,7 +152,7 @@ export function createViewport({ state: layerState, services, parts, camera }) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
async function runProximityCheck() {
|
async function runProximityCheck() {
|
||||||
if (!layerState._enabled) return;
|
if (!layerState._enabled || layerState._restoringViewport) return;
|
||||||
|
|
||||||
const generation = ++layerState._proximityGeneration;
|
const generation = ++layerState._proximityGeneration;
|
||||||
const altitude = getCameraAltitude(layerState._viewer);
|
const altitude = getCameraAltitude(layerState._viewer);
|
||||||
@@ -208,7 +208,7 @@ export function createViewport({ state: layerState, services, parts, camera }) {
|
|||||||
/** Camera change event handler — triggers a debounced proximity check. */
|
/** Camera change event handler — triggers a debounced proximity check. */
|
||||||
|
|
||||||
function onCameraChanged() {
|
function onCameraChanged() {
|
||||||
if (!layerState._enabled) return;
|
if (!layerState._enabled || layerState._restoringViewport) return;
|
||||||
scheduleProximityCheck();
|
scheduleProximityCheck();
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -114,7 +114,11 @@ export function createLifecycle({
|
|||||||
* Create the point collection. Called once at bootstrap.
|
* Create the point collection. Called once at bootstrap.
|
||||||
* @param {Cesium.Viewer} viewer
|
* @param {Cesium.Viewer} viewer
|
||||||
*/
|
*/
|
||||||
init(viewer, options = {}) {
|
init(viewer, options) {
|
||||||
|
this.attachRenderer(viewer, options);
|
||||||
|
},
|
||||||
|
attachRenderer(viewer, options = {}) {
|
||||||
|
state._restoringViewport = Boolean(options.waitForCameraRestore);
|
||||||
if (parts.renderer)
|
if (parts.renderer)
|
||||||
throw new Error('Transit layer is already initialized');
|
throw new Error('Transit layer is already initialized');
|
||||||
if (typeof rendererFactory !== 'function')
|
if (typeof rendererFactory !== 'function')
|
||||||
@@ -137,27 +141,16 @@ export function createLifecycle({
|
|||||||
state._viewer = null;
|
state._viewer = null;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
state._enabled = false;
|
state._overlayHost.setVisible(TRANSIT_SELECTED_OVERLAY_SOURCE_ID, state._enabled);
|
||||||
state._activeFeeds.clear();
|
if (state._enabled) {
|
||||||
state._feedStatus.clear();
|
for (const [key, entry] of state._vehicles) {
|
||||||
state._inFlight.clear();
|
parts.rendering.addMarker(key, entry);
|
||||||
for (const entry of state._vehicles.values()) {
|
entry.detectContact = null;
|
||||||
entry.detectContact = null;
|
entry.pathRevision = -1;
|
||||||
parts.rendering.cancelWake(entry);
|
}
|
||||||
destroyTrack(entry.track);
|
this.bindScene(viewer);
|
||||||
|
if (!state._restoringViewport) this.repaint();
|
||||||
}
|
}
|
||||||
state._vehicles.clear();
|
|
||||||
parts.height.clear();
|
|
||||||
state._selectedKey = null;
|
|
||||||
state._lastUpdate = null;
|
|
||||||
state._error = null;
|
|
||||||
state._limitWarned = false;
|
|
||||||
state._altitudeGateOpen = false;
|
|
||||||
state._moving.clear();
|
|
||||||
state._heightDirty.clear();
|
|
||||||
state._rotationPose = null;
|
|
||||||
state._rotationDirty = false;
|
|
||||||
state._overlayHost.setVisible(TRANSIT_SELECTED_OVERLAY_SOURCE_ID, false);
|
|
||||||
parts.renderer.restoreOrder();
|
parts.renderer.restoreOrder();
|
||||||
bindStyleEvents();
|
bindStyleEvents();
|
||||||
console.log(
|
console.log(
|
||||||
@@ -171,9 +164,13 @@ export function createLifecycle({
|
|||||||
*/
|
*/
|
||||||
enable(viewer) {
|
enable(viewer) {
|
||||||
state._enabled = true;
|
state._enabled = true;
|
||||||
bindVisibility();
|
|
||||||
state._generation += 1;
|
state._generation += 1;
|
||||||
state._error = null;
|
state._error = null;
|
||||||
|
this.bindScene(viewer);
|
||||||
|
},
|
||||||
|
|
||||||
|
bindScene(viewer) {
|
||||||
|
bindVisibility();
|
||||||
state._markers.show = true;
|
state._markers.show = true;
|
||||||
state._animatedMarkers.show = true;
|
state._animatedMarkers.show = true;
|
||||||
state._maintenanceTimer = setInterval(
|
state._maintenanceTimer = setInterval(
|
||||||
@@ -199,6 +196,51 @@ export function createLifecycle({
|
|||||||
parts.renderer.restoreOrder();
|
parts.renderer.restoreOrder();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
repaint() {
|
||||||
|
state._visibilityAt = 0;
|
||||||
|
state._rotationDirty = true;
|
||||||
|
parts.viewport.runProximityCheck();
|
||||||
|
parts.rendering.refreshVisibility();
|
||||||
|
parts.trails.rebind();
|
||||||
|
parts.selection.refreshSelectedCard(true);
|
||||||
|
for (const feed of state._activeFeeds.values()) parts.renderer.addCredit(feed);
|
||||||
|
},
|
||||||
|
resumeViewport() {
|
||||||
|
if (!state._restoringViewport) return;
|
||||||
|
state._restoringViewport = false;
|
||||||
|
if (state._enabled && parts.renderer) this.repaint();
|
||||||
|
},
|
||||||
|
detachRenderer() {
|
||||||
|
if (!parts.renderer) return;
|
||||||
|
state._restoringViewport = true;
|
||||||
|
unbindVisibility();
|
||||||
|
clearInterval(state._maintenanceTimer);
|
||||||
|
clearTimeout(state._cameraDebounceTimer);
|
||||||
|
clearTimeout(state._visibilityTimer);
|
||||||
|
state._maintenanceTimer = state._cameraDebounceTimer = state._visibilityTimer = null;
|
||||||
|
parts.selection.removeClickHandler();
|
||||||
|
unregisterPickOwner('transit');
|
||||||
|
state._cameraChangedRemove?.();
|
||||||
|
state._cameraChangedRemove = null;
|
||||||
|
if (state._cameraChangedAttached) returnCameraSensitivity();
|
||||||
|
state._cameraChangedAttached = false;
|
||||||
|
state._preRenderRemove?.();
|
||||||
|
state._preRenderRemove = null;
|
||||||
|
parts.trails.detachRenderer();
|
||||||
|
for (const entry of state._vehicles.values()) {
|
||||||
|
parts.rendering.cancelWake(entry);
|
||||||
|
entry.marker = entry.markerCollection = entry.detectContact = null;
|
||||||
|
}
|
||||||
|
state._detectCache = null;
|
||||||
|
state._detectBuiltAt = -Infinity;
|
||||||
|
state._visible.clear();
|
||||||
|
state._moving.clear();
|
||||||
|
if (state._renderHeld) releaseContinuousRender('transit');
|
||||||
|
state._renderHeld = false;
|
||||||
|
parts.renderer.destroy();
|
||||||
|
parts.renderer = state._markers = state._animatedMarkers = state._viewer = null;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide everything, stop polling, drop all vehicles.
|
* Hide everything, stop polling, drop all vehicles.
|
||||||
* @param {Cesium.Viewer} viewer
|
* @param {Cesium.Viewer} viewer
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ export function createRendering({ state, services, parts }) {
|
|||||||
);
|
);
|
||||||
entry.surfaceReady = !!known;
|
entry.surfaceReady = !!known;
|
||||||
entry.heightPending = !known;
|
entry.heightPending = !known;
|
||||||
|
if (!entry.marker) return;
|
||||||
entry.marker.show = !!known && vehicleInView(entry);
|
entry.marker.show = !!known && vehicleInView(entry);
|
||||||
if (known) {
|
if (known) {
|
||||||
entry.marker.position = state._scratchCartesian;
|
entry.marker.position = state._scratchCartesian;
|
||||||
@@ -239,6 +240,7 @@ export function createRendering({ state, services, parts }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addMarker(key, entry) {
|
function addMarker(key, entry) {
|
||||||
|
if (!state._markers) return null;
|
||||||
const marker = state._markers.add({
|
const marker = state._markers.add({
|
||||||
id: key,
|
id: key,
|
||||||
position: entry.toCart,
|
position: entry.toCart,
|
||||||
|
|||||||
@@ -316,6 +316,18 @@ export function createTrails({ state, services, parts, source }) {
|
|||||||
services.render.governorRequestRender('transit-trail-age');
|
services.render.governorRequestRender('transit-trail-age');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
function detachRenderer() {
|
||||||
|
renderer?.destroy();
|
||||||
|
renderer = null;
|
||||||
|
}
|
||||||
|
function rebind() {
|
||||||
|
if (!selected) return;
|
||||||
|
ensureRenderer();
|
||||||
|
selected.pathRevision = -1;
|
||||||
|
style();
|
||||||
|
prepareEntry(selected);
|
||||||
|
update();
|
||||||
|
}
|
||||||
function clear() {
|
function clear() {
|
||||||
if (historyRequest) {
|
if (historyRequest) {
|
||||||
historyRequest.abort();
|
historyRequest.abort();
|
||||||
@@ -338,6 +350,8 @@ export function createTrails({ state, services, parts, source }) {
|
|||||||
update,
|
update,
|
||||||
style,
|
style,
|
||||||
destroy: clear,
|
destroy: clear,
|
||||||
|
detachRenderer,
|
||||||
|
rebind,
|
||||||
requestDiagnostics: () => ({
|
requestDiagnostics: () => ({
|
||||||
requestCount,
|
requestCount,
|
||||||
abortCount,
|
abortCount,
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export function createViewport({ state, services, parts }) {
|
|||||||
globalThis.document?.visibilityState === 'hidden'
|
globalThis.document?.visibilityState === 'hidden'
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
if (!state._enabled || !parts.renderer?.ready || !state._viewer) return;
|
if (!state._enabled || !parts.renderer?.ready || state._restoringViewport) return;
|
||||||
refreshViewBounds();
|
refreshViewBounds();
|
||||||
const altitude = getCameraAltitude();
|
const altitude = getCameraAltitude();
|
||||||
state._altitudeGateOpen = altitudeGateOpen(altitude);
|
state._altitudeGateOpen = altitudeGateOpen(altitude);
|
||||||
@@ -119,6 +119,7 @@ export function createViewport({ state, services, parts }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onCameraChanged() {
|
function onCameraChanged() {
|
||||||
|
if (state._restoringViewport) return;
|
||||||
// The bounds update immediately; the feed proximity check stays debounced.
|
// The bounds update immediately; the feed proximity check stays debounced.
|
||||||
// A person dragging the globe should not wait a third of a second for the
|
// A person dragging the globe should not wait a third of a second for the
|
||||||
// vehicles under the cursor to start moving again.
|
// vehicles under the cursor to start moving again.
|
||||||
|
|||||||
Reference in new issue
Block a user