How the Map Stack Controller Manages Imagery Switching and Emits Events
The Map Stack Controller orchestrates seamless transitions between photorealistic Google 3D tiles and traditional globe imagery layers while emitting lifecycle events that keep the UI synchronized with asynchronous provider initialization.
The Map Stack Controller in bilawalsidhu/gods-eye-view serves as the central authority for managing Cesium.js visualization modes. Located in src/mapStackController.js, this class handles the complex coordination required to switch between Google Photorealistic 3D Tiles, Bing aerial imagery, OpenStreetMap, and custom terrain providers while preventing race conditions during rapid user interactions.
Initialization and Core State
The constructor (lines 52-71) establishes the controller's foundation by storing references to the Cesium viewer, an optional Google 3D tileset, and the Cesium Ion access token. It initializes the _switchGen counter as a monotonic generation tracker, creates a cache for imagery providers, and sets up the _onChange and _onError callback hooks. The controller maintains the active imagery layer reference, a terrain mode flag, and validates the initial stack configuration before marking itself ready.
The Imagery Switching Workflow
The setStack(id, {silent}) method (lines 57-73) serves as the public API for requesting imagery changes. When invoked, the controller validates the requested stack ID against available configurations, increments the internal _switchGen counter, and immediately transitions the state to "switching" unless the silent option suppresses events. The method then dispatches to specialized activation handlers based on the stack type.
Activating Photorealistic 3D Tiles
For Google Photorealistic 3D Tiles, the controller invokes _activatePhotoreal(gen) (lines 174-182). This method removes any existing imagery layers from the scene, makes the Google tileset visible, and hides the Cesium globe primitive while preserving the current camera position. The generation parameter gen ensures this activation correlates with the specific switch request that initiated it.
Activating Globe-Based Imagery Stacks
Traditional globe stacks—such as Bing Aerial, Bing Labels, or OSM—flow through _activateGlobeStack(stack, gen) (lines 236-245). This handler first retrieves the appropriate imagery provider via _getImageryProvider(stack) (lines 251-267), which lazily instantiates and caches IonWorldImageryStyle providers for Bing layers or OpenStreetMapImageryProvider for OSM. The method then removes existing imagery, constructs a new Cesium.ImageryLayer, adds it to the viewer, hides the Google tileset, and reveals the globe entity.
Terrain Provider Coordination
The _setWorldTerrainEnabled(enabled, gen) method (lines 297-312) manages elevation data synchronization. When enabled and a Cesium Ion token is present, it installs Cesium World Terrain; otherwise, it falls back to a keyless Re:Earth terrain provider suitable for OSM stacks. This method checks the generation counter before committing terrain changes, allowing newer switch requests to abort outdated terrain fetches.
Event Lifecycle and Silent Mode
The controller emits three distinct event states through the _emitChange helper (lines 332-334): "switching" when activation begins, "ready" upon successful completion, and "error" when provider initialization fails. These events propagate via the onChange callback registered during instantiation, delivering the current state object containing activeId, activeStack, status, and lastError.
When setStack receives {silent: true}, the controller suppresses both the intermediate "switching" event and the final "ready" or "error" notifications. The imagery still changes asynchronously, but UI components remain unaware, enabling batch updates or background preloading scenarios.
Preventing Race Conditions with Generation Tracking
Every call to setStack increments the _switchGen counter. Both _getImageryProvider and _setWorldTerrainEnabled capture this generation value at invocation time and verify it before applying state mutations. If a newer switch has occurred during an asynchronous provider fetch, the stale generation detects the mismatch and aborts the commit. This "M7" pattern guarantees that slower imagery loads cannot overwrite newer user selections.
Implementation Example
import { MapStackController } from './src/mapStackController.js';
// Initialize with callbacks
const mapStack = new MapStackController(viewer, {
googleTileset: google3dTileset,
cesiumToken: process.env.CESIUM_ION_TOKEN,
initialStack: 'photoreal',
onChange: (state) => console.log('Status:', state.status, 'Active:', state.activeId),
onError: (msg, stack) => console.warn('Failed to load:', msg),
});
// Standard switch with events
await mapStack.setStack('bing-aerial');
// Silent switch for background updates
await mapStack.setStack('osm', { silent: true });
// Handling state changes in UI components
function handleMapStackChange(state) {
if (state.status === 'switching') {
showLoadingIndicator();
} else if (state.status === 'ready') {
updateChipSelection(state.activeId);
hideLoadingIndicator();
} else if (state.status === 'error') {
showErrorToast(state.lastError);
}
}
mapStack._onChange = handleMapStackChange;
Summary
- The Map Stack Controller in
src/mapStackController.jsmanages all imagery transitions between Google 3D Tiles and globe-based providers through thesetStack()API. - Generation tracking via
_switchGenprevents race conditions when users switch stacks faster than providers can initialize. - The controller emits "switching", "ready", and "error" events via the
onChangecallback, with an optional silent mode to suppress notifications. - Terrain providers automatically adjust based on stack requirements and token availability through
_setWorldTerrainEnabled(). - Failed globe stack activations automatically fall back to the photorealistic Google 3D view while emitting error details.
Frequently Asked Questions
What specific events does the Map Stack Controller emit during imagery switching?
The controller emits three primary lifecycle events through the onChange callback: "switching" immediately when setStack() is called and validation passes, "ready" after the imagery provider and terrain successfully initialize, and "error" when provider creation or terrain fetching fails. Each event includes the complete state object with activeId, activeStack, and lastError properties.
How does the controller prevent race conditions during rapid stack changes?
Every setStack() invocation increments a monotonic _switchGen counter. Async methods like _getImageryProvider() and _setWorldTerrainEnabled() capture this generation value locally and verify it matches the current controller state before applying changes. If a newer switch has occurred, the stale operation detects the generation mismatch and aborts, ensuring only the latest user selection remains active.
What happens when an imagery provider fails to load?
When _activateGlobeStack() encounters a provider error, the controller captures the failure reason in _unavailableReason, automatically falls back to the photorealistic Google 3D tileset if available, and emits an "error" event via _emitChange() (lines 85-92). This ensures the map never enters a broken state while notifying the application layer through the onError callback.
Can I switch imagery without triggering UI updates?
Yes. Pass {silent: true} as the second argument to setStack(). In silent mode, the controller skips the "switching" event emission and suppresses the final "ready" or "error" notifications, though the underlying imagery and terrain changes still execute asynchronously. This is useful for preloading stacks or batching multiple updates into a single UI refresh.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →