mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 19:34:01 +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',
|
||||
() => createApplicationTransit({ surface, source: sources.transit }),
|
||||
() => sessionModel('transit', () => createApplicationTransit({ surface, source: sources.transit })),
|
||||
],
|
||||
[
|
||||
'bikeshare',
|
||||
() => createApplicationBikeshare({ source: sources.bikeshare }),
|
||||
() => sessionModel('bikeshare', () => createApplicationBikeshare({ source: sources.bikeshare })),
|
||||
],
|
||||
[
|
||||
'directions',
|
||||
|
||||
@@ -23,7 +23,10 @@ export function createBikeshareLayer({ services, source, rendererFactory }) {
|
||||
let renderer = null;
|
||||
let initialized = false;
|
||||
const lifecycle = {
|
||||
init(viewer, options = {}) {
|
||||
init(viewer, options) {
|
||||
return this.attachRenderer(viewer, options);
|
||||
},
|
||||
attachRenderer(viewer, options = {}) {
|
||||
if (initialized)
|
||||
throw new Error('Bikeshare layer is already initialized');
|
||||
renderer ??= rendererFactory({
|
||||
@@ -36,12 +39,44 @@ export function createBikeshareLayer({ services, source, rendererFactory }) {
|
||||
Object.assign(parts, renderer);
|
||||
initialized = true;
|
||||
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) {
|
||||
initialized = false;
|
||||
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) {
|
||||
return renderer?.lifecycle.methods.enable(...args);
|
||||
},
|
||||
|
||||
@@ -187,7 +187,7 @@ export function createIngestion({
|
||||
try {
|
||||
const statusMap = await loadCityStationStatus(cityId, generation);
|
||||
if (!isCurrent(cityId)) return;
|
||||
parts.rendering.applyStatusToPoints(cityId, statusMap);
|
||||
parts.rendering?.applyStatusToPoints(cityId, statusMap);
|
||||
layerState._failedCityIds.delete(cityId);
|
||||
refreshed = true;
|
||||
} catch (error) {
|
||||
|
||||
@@ -35,29 +35,6 @@ export function createLifecycle({
|
||||
registerSpriteCollection('bikeshare', layerState._pointCollection);
|
||||
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(
|
||||
BIKESHARE_SELECTED_OVERLAY_SOURCE_ID,
|
||||
false,
|
||||
@@ -79,9 +56,12 @@ export function createLifecycle({
|
||||
*/
|
||||
enable(viewer) {
|
||||
if (layerState._enabled) return;
|
||||
viewer = layerState._viewer;
|
||||
layerState._enabled = true;
|
||||
layerState._failedCityIds.clear();
|
||||
this.show(viewer);
|
||||
},
|
||||
show(viewer) {
|
||||
viewer = layerState._viewer;
|
||||
layerState._pointCollection.show = true;
|
||||
layerState._overlayHost.setVisible(
|
||||
BIKESHARE_SELECTED_OVERLAY_SOURCE_ID,
|
||||
@@ -149,6 +129,23 @@ export function createLifecycle({
|
||||
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. */
|
||||
destroy(viewer) {
|
||||
viewer = layerState._viewer;
|
||||
|
||||
@@ -311,27 +311,6 @@ export function createBikeshareNavaraRenderer(engine, context) {
|
||||
if (destroyed) throw new Error('Bikeshare renderer is destroyed');
|
||||
state._engine = engine;
|
||||
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);
|
||||
selectedSet.setCount(0);
|
||||
normalSet.commit();
|
||||
@@ -345,6 +324,9 @@ export function createBikeshareNavaraRenderer(engine, context) {
|
||||
if (destroyed || state._enabled) return;
|
||||
state._enabled = true;
|
||||
state._error = null;
|
||||
this.show();
|
||||
},
|
||||
show() {
|
||||
state._overlayHost.setVisible(
|
||||
BIKESHARE_SELECTED_OVERLAY_SOURCE_ID,
|
||||
true,
|
||||
@@ -389,6 +371,15 @@ export function createBikeshareNavaraRenderer(engine, context) {
|
||||
state._loading = false;
|
||||
state._loadingOps = 0;
|
||||
},
|
||||
detach() {
|
||||
selection._clearSelection();
|
||||
selectionRemove?.();
|
||||
cameraRemove?.();
|
||||
state._cameraChangedAttached = false;
|
||||
normalSet.destroy();
|
||||
selectedSet.destroy();
|
||||
destroyed = true;
|
||||
},
|
||||
destroy() {
|
||||
if (destroyed) return;
|
||||
this.disable();
|
||||
|
||||
@@ -69,7 +69,7 @@ export function createViewport({ state: layerState, services, parts, camera }) {
|
||||
governorRequestRender('bikeshare-deactivate');
|
||||
parts.ingestion.abortInFlight(layerState._inFlightInfo, 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. */
|
||||
@@ -107,12 +107,12 @@ export function createViewport({ state: layerState, services, parts, camera }) {
|
||||
)
|
||||
return;
|
||||
|
||||
parts.rendering.ensureCityPoints(cityId, stationMap);
|
||||
parts.rendering?.ensureCityPoints(cityId, stationMap);
|
||||
|
||||
// Apply any cached status immediately for snappier initial rendering
|
||||
const cachedStatus = layerState._statusCache.get(cityId)?.statusMap;
|
||||
if (cachedStatus) {
|
||||
parts.rendering.applyStatusToPoints(cityId, cachedStatus);
|
||||
parts.rendering?.applyStatusToPoints(cityId, cachedStatus);
|
||||
}
|
||||
|
||||
const statusMap = await parts.ingestion.loadCityStationStatus(
|
||||
@@ -125,7 +125,7 @@ export function createViewport({ state: layerState, services, parts, camera }) {
|
||||
generation !== layerState._proximityGeneration
|
||||
)
|
||||
return;
|
||||
parts.rendering.applyStatusToPoints(cityId, statusMap);
|
||||
parts.rendering?.applyStatusToPoints(cityId, statusMap);
|
||||
layerState._lastUpdate = Date.now();
|
||||
layerState._failedCityIds.delete(cityId);
|
||||
} catch (error) {
|
||||
@@ -152,7 +152,7 @@ export function createViewport({ state: layerState, services, parts, camera }) {
|
||||
*/
|
||||
|
||||
async function runProximityCheck() {
|
||||
if (!layerState._enabled) return;
|
||||
if (!layerState._enabled || layerState._restoringViewport) return;
|
||||
|
||||
const generation = ++layerState._proximityGeneration;
|
||||
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. */
|
||||
|
||||
function onCameraChanged() {
|
||||
if (!layerState._enabled) return;
|
||||
if (!layerState._enabled || layerState._restoringViewport) return;
|
||||
scheduleProximityCheck();
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -114,7 +114,11 @@ export function createLifecycle({
|
||||
* Create the point collection. Called once at bootstrap.
|
||||
* @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)
|
||||
throw new Error('Transit layer is already initialized');
|
||||
if (typeof rendererFactory !== 'function')
|
||||
@@ -137,27 +141,16 @@ export function createLifecycle({
|
||||
state._viewer = null;
|
||||
throw error;
|
||||
}
|
||||
state._enabled = false;
|
||||
state._activeFeeds.clear();
|
||||
state._feedStatus.clear();
|
||||
state._inFlight.clear();
|
||||
for (const entry of state._vehicles.values()) {
|
||||
entry.detectContact = null;
|
||||
parts.rendering.cancelWake(entry);
|
||||
destroyTrack(entry.track);
|
||||
state._overlayHost.setVisible(TRANSIT_SELECTED_OVERLAY_SOURCE_ID, state._enabled);
|
||||
if (state._enabled) {
|
||||
for (const [key, entry] of state._vehicles) {
|
||||
parts.rendering.addMarker(key, entry);
|
||||
entry.detectContact = null;
|
||||
entry.pathRevision = -1;
|
||||
}
|
||||
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();
|
||||
bindStyleEvents();
|
||||
console.log(
|
||||
@@ -171,9 +164,13 @@ export function createLifecycle({
|
||||
*/
|
||||
enable(viewer) {
|
||||
state._enabled = true;
|
||||
bindVisibility();
|
||||
state._generation += 1;
|
||||
state._error = null;
|
||||
this.bindScene(viewer);
|
||||
},
|
||||
|
||||
bindScene(viewer) {
|
||||
bindVisibility();
|
||||
state._markers.show = true;
|
||||
state._animatedMarkers.show = true;
|
||||
state._maintenanceTimer = setInterval(
|
||||
@@ -199,6 +196,51 @@ export function createLifecycle({
|
||||
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.
|
||||
* @param {Cesium.Viewer} viewer
|
||||
|
||||
@@ -91,6 +91,7 @@ export function createRendering({ state, services, parts }) {
|
||||
);
|
||||
entry.surfaceReady = !!known;
|
||||
entry.heightPending = !known;
|
||||
if (!entry.marker) return;
|
||||
entry.marker.show = !!known && vehicleInView(entry);
|
||||
if (known) {
|
||||
entry.marker.position = state._scratchCartesian;
|
||||
@@ -239,6 +240,7 @@ export function createRendering({ state, services, parts }) {
|
||||
}
|
||||
|
||||
function addMarker(key, entry) {
|
||||
if (!state._markers) return null;
|
||||
const marker = state._markers.add({
|
||||
id: key,
|
||||
position: entry.toCart,
|
||||
|
||||
@@ -316,6 +316,18 @@ export function createTrails({ state, services, parts, source }) {
|
||||
services.render.governorRequestRender('transit-trail-age');
|
||||
}, 1000);
|
||||
}
|
||||
function detachRenderer() {
|
||||
renderer?.destroy();
|
||||
renderer = null;
|
||||
}
|
||||
function rebind() {
|
||||
if (!selected) return;
|
||||
ensureRenderer();
|
||||
selected.pathRevision = -1;
|
||||
style();
|
||||
prepareEntry(selected);
|
||||
update();
|
||||
}
|
||||
function clear() {
|
||||
if (historyRequest) {
|
||||
historyRequest.abort();
|
||||
@@ -338,6 +350,8 @@ export function createTrails({ state, services, parts, source }) {
|
||||
update,
|
||||
style,
|
||||
destroy: clear,
|
||||
detachRenderer,
|
||||
rebind,
|
||||
requestDiagnostics: () => ({
|
||||
requestCount,
|
||||
abortCount,
|
||||
|
||||
@@ -79,7 +79,7 @@ export function createViewport({ state, services, parts }) {
|
||||
globalThis.document?.visibilityState === 'hidden'
|
||||
)
|
||||
return;
|
||||
if (!state._enabled || !parts.renderer?.ready || !state._viewer) return;
|
||||
if (!state._enabled || !parts.renderer?.ready || state._restoringViewport) return;
|
||||
refreshViewBounds();
|
||||
const altitude = getCameraAltitude();
|
||||
state._altitudeGateOpen = altitudeGateOpen(altitude);
|
||||
@@ -119,6 +119,7 @@ export function createViewport({ state, services, parts }) {
|
||||
}
|
||||
|
||||
function onCameraChanged() {
|
||||
if (state._restoringViewport) return;
|
||||
// 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
|
||||
// vehicles under the cursor to start moving again.
|
||||
|
||||
Reference in new issue
Block a user