How Map Stack Switching Works in Gods-Eye-View: Events and Implementation
Map stack switching in Gods-Eye-View is orchestrated by the MapStackController class in src/mapStackController.js, which emits three custom events—mapstackchange, mapstackerror, and mapstackswitch—to signal state transitions between map data sources.
The Gods-Eye-View visualization platform supports multiple map data sources, from photorealistic tiles to OpenStreetMap layers. Understanding map stack switching is essential for developers customizing the UI or debugging layer transitions. This process is centralized in the MapStackController class, which manages state validation, resource loading, and event broadcasting throughout the application lifecycle.
Core Switching Architecture
The MapStackController Class
The MapStackController class extends EventTarget and serves as the single source of truth for map layer state. It exposes the setStack(stackId, options) method, which initiates all stack transitions. According to the source code in src/mapStackController.js, the controller maintains an internal generation counter to track switch attempts and prevent race conditions during rapid successive calls.
The class validates incoming requests through normalizeStackId, throwing immediately if the requested ID does not exist in the registry returned by getStacks(). This validation occurs at line 86 of src/mapStackController.js before any resource loading begins.
The Switching Lifecycle
The transition follows a strict four-phase lifecycle implemented in src/mapStackController.js:
-
Request Validation: The UI layer invokes
mapStackController.setStack(), passing a stack ID string. The controller verifies the ID against available stacks and increments the internal generation counter. -
State Transition: The
_activateStackmethod (line 158) marks the previous stack as "switching out" and begins asynchronous loading of the new stack's resources, including tilesets, terrain data, and imagery layers. -
Event Emission: Immediately upon initiation, the controller fires the
mapstackswitchevent. Upon completion or failure, it emits eithermapstackchangeormapstackerrorthrough its internal_emitChange,_emitError, and_emitSwitchinghelpers. -
Resolution: The
setStackmethod returns a Promise that resolves at line 112 once the new stack finishes loading, updatingactiveId,lastError, and the generation tracker.
Events Emitted During Stack Switches
MapStackController leverages the native EventTarget interface to broadcast custom events. UI components in src/ui.js register listeners during initialization to update chips, status bars, and share-link metadata.
mapstackswitch
Fired immediately after initiating a switch but before resources finish loading. This event signals the start of transitions and carries a payload containing { fromId, toId, generation }. The UI uses this to display loading indicators while waiting for tile data. This emission occurs in the _emitSwitching helper around line 132 of src/mapStackController.js.
mapstackchange
Dispatched after a successful stack activation when the new layer becomes fully visible. The payload includes { id, label, generation }, allowing listeners to update displayed metadata. This event fires from _emitChange at line 140 of src/mapStackController.js.
mapstackerror
Triggered if resource loading fails due to missing Cesium ion tokens, unavailable terrain endpoints, or network errors. The payload contains { id, error, generation }, enabling error reporting and fallback UI states. This originates in _emitError at line 147 of src/mapStackController.js.
UI Integration and Component Coordination
The UI layer consumes these events through the _setMapStack method defined at line 3505 of src/ui.js. This facade method wraps the controller's API and ensures the visualization state remains synchronized across subsystems.
When mapstackchange fires, the following components react:
- StyleManager: Receives the active stack ID via
styleManager.setMapStack()to apply correct shading rules (e.g., night-day lighting adjustments). - Camera/Scene Policy: Adjusts default altitudes and lighting parameters based on the new data source's characteristics.
- Share Link Generator: Encodes the current stack ID into the URL at line 3837 of
src/ui.js, ensuring permalink restoration of the correct map source.
The application bootstrap in src/main.js instantiates a single MapStackController instance and injects it into dependent subsystems during initialization.
Practical Implementation Example
The following pattern demonstrates programmatic stack switching and event handling:
// Access the controller from the UI instance
const controller = uiInstance.mapStackController;
// Switch to photorealistic tiles
await uiInstance._setMapStack('photoreal');
// Listen for successful transitions
controller.addEventListener('mapstackchange', (e) => {
console.log(`Activated: ${e.detail.label} (gen ${e.detail.generation})`);
});
// Handle loading states
controller.addEventListener('mapstackswitch', (e) => {
showLoadingSpinner(`Switching from ${e.detail.fromId} to ${e.detail.toId}`);
});
// Capture errors
controller.addEventListener('mapstackerror', (e) => {
console.error(`Failed to load ${e.detail.id}:`, e.detail.error);
});
This implementation leverages the Promise returned by setStack for async/await patterns while using event listeners for reactive UI updates.
Summary
- MapStackController in
src/mapStackController.jscentralizes all stack switching logic and state management. - The controller emits three distinct events:
mapstackswitch(start),mapstackchange(success), andmapstackerror(failure). - Event payloads include generation counters to handle race conditions and metadata like stack IDs and error objects.
- The UI layer in
src/ui.jsconsumes these events to synchronize visual components, camera positions, and shareable URLs. - All switching operations return Promises that resolve upon resource loading completion or rejection upon validation errors.
Frequently Asked Questions
How does Gods-Eye-View prevent race conditions during rapid stack switches?
The MapStackController maintains an internal generation counter that increments with every setStack() call. When asynchronous loading completes, the controller compares the current generation against the generation captured at request time. If they do not match, the callback discards the result, ensuring only the most recent switch request affects the UI state.
What error conditions trigger the mapstackerror event?
The event fires when the controller fails to activate a requested stack due to missing Cesium ion tokens, unavailable terrain endpoints, network timeouts, or invalid layer configurations. The error payload includes the target stack ID and the specific Error object, allowing the UI to display targeted failure messages or trigger fallback stacks.
Can external plugins listen for stack changes without modifying src/ui.js?
Yes. Since MapStackController extends EventTarget, any module with access to the controller instance can attach listeners using addEventListener(). The controller instance is exposed through the main application context created in src/main.js, allowing third-party extensions to react to mapstackchange events for custom visualization overlays or analytics tracking.
What is the difference between mapstackswitch and mapstackchange events?
The mapstackswitch event fires immediately when setStack() initiates a transition, providing { fromId, toId, generation } for loading indicators. The mapstackchange event fires only after the new stack's resources finish loading and the layer becomes active, providing { id, label, generation } for final state updates. This separation allows UIs to distinguish between "loading" and "completed" states.
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 →