How Data Layers Are Registered in God's Eye View: A Complete Guide to the DataLayerManager System
Data layers in God's Eye View are registered via the DataLayerManager.register() method, which validates unique IDs, stores layer modules in an internal map, and seals the registration list when finalizeRegistrations() is called.
God's Eye View is an open-source geospatial visualization platform that overlays real-time data streams—from CCTV feeds to satellite imagery—onto a 3D globe. Understanding how data layers are registered is essential for extending the platform with new data sources. This article breaks down the registration pipeline implemented in bilawalsidhu/gods-eye-view, referencing actual source paths and the lifecycle methods that govern each layer.
The DataLayerManager Architecture**
The core of layer registration lives in src/data/manager.js. The DataLayerManager class acts as a central registry and controller for all data overlays, enforcing a strict lifecycle from initialization through runtime toggling.
Why a Dedicated Manager Matters
Without centralized registration, multiple data sources would compete for viewer resources, duplicate IDs would cause conflicts, and serialization state would become inconsistent. The manager solves this by:
- Validating layer uniqueness at registration time
- Locking the layer list after initialization to prevent runtime injection attacks
- Mediating all enable/disable/update calls through a single API
Layer Module Structure: The Registration Contract**
Before registration, each data source must implement a standard module interface. These modules live in src/data/*.js and export a predictable set of properties and functions.
Required Exports
Every layer module must provide:
id– a unique string identifier used as the map keyinit(viewer, { signal })– one-time setup (called once)enable(viewer, { signal })– activate the overlaydisable(viewer, { signal })– deactivate the overlayupdate(viewer, { signal })– refresh data on demand
Optional Exports
Additional capabilities include:
getStats()– return telemetry for debuggingsetLifecyclePresentation(presentation)– customize visual feedback during state changes
Here's a production layer module from the codebase:
// src/data/traffic.js – a typical layer module
export const id = 'traffic';
export async function init(viewer, { signal }) {
// Load static resources, establish WebSocket connections
}
export async function enable(viewer, { signal }) {
// Add primitives to Cesium viewer, start animation loops
}
export async function disable(viewer, { signal }) {
// Remove primitives, pause expensive operations
}
export async function update(viewer, { signal }) {
// Fetch fresh data, update visible entities
}
export function getStats() {
return { count: vehicleEntityCount, lastUpdate: timestamp };
}
Additional layer modules like src/data/cctv.js and src/data/satellites.js follow this identical pattern, differing only in their data fetching and rendering logic.
Step-by-Step Registration Process**
The registration flow spans three phases: manager instantiation, layer registration, and finalization.
Phase 1: Create the DataLayerManager
In src/main.js, the application bootstraps the manager early in startup:
// src/main.js – creating the manager
import { DataLayerManager } from './data/manager.js';
const viewer = /* Cesium viewer instance */;
const dataManager = new DataLayerManager(viewer);
The constructor stores the Cesium viewer reference and initializes internal state: an empty layers Map, _registrationsFinalized = false, and empty arrays for lifecycle tracking.
Phase 2: Register Individual Layers
Immediately after instantiation, the application registers each available layer:
// src/main.js – registering production layers
import * as trafficLayer from './data/traffic.js';
import * as cctvLayer from './data/cctv.js';
import * as satellitesLayer from './data/satellites.js';
dataManager.register(trafficLayer);
dataManager.register(cctvLayer);
dataManager.register(satellitesLayer);
The register() method performs validation before accepting a module:
- Checks that
_registrationsFinalized === false(throws if registrations are locked) - Delegates to
_registerLayer()for actual processing _registerLayer()validates thatmodule.idexists and is unique- Throws
Errorif duplicate IDs are detected - Stores the module in
this.layerswith initial state:{ module, initialized: false, enabled: false, … }
Registration is order-sensitive—layers are stored in insertion order and initialized in that sequence during finalization.
Phase 3: Finalize and Lock Registrations
After all production layers are added, the application calls:
// src/main.js – finalizing registration
dataManager.finalizeRegistrations([
{ id: 'traffic', disposition: 'enabled+options' },
{ id: 'cctv', disposition: 'enabled-only' },
{ id: 'satellites', disposition: 'enabled+options' },
]);
The finalizeRegistrations(serializationRegistry) method:
- Verifies every registered layer has a matching disposition entry
- Supports three disposition types:
'enabled-only','enabled+options', and'full-state' - Sets
_registrationsFinalized = trueto prevent subsequentregister()calls - Triggers
init()on each layer in registration order
Once finalized, the layer list is immutable. This design prevents runtime code injection and ensures serialization state remains consistent across sessions.
Runtime Layer Control API**
After registration and finalization, the manager exposes methods to control layer visibility and refresh operations. These are typically invoked from UI handlers in src/ui.js or voice command processors.
Common Operations
| Method | Purpose | Async |
|---|---|---|
toggle(id) |
Flip enabled/disabled state | Yes |
setEnabled(id, boolean) |
Explicitly set visibility | Yes |
refreshLayer(id) |
Request data update | Yes |
getLayerState(id) |
Retrieve current metadata | No |
getRegistry() |
List all registered IDs | No |
Example usage:
// Toggling a layer at runtime (e.g., from UI or voice command)
await dataManager.toggle('traffic'); // flips enabled/disabled
await dataManager.setEnabled('cctv', true); // explicitly enable
await dataManager.refreshLayer('satellites'); // request fresh update
All runtime methods validate that the provided id exists in this.layers and throw descriptive errors for unknown layers.
Error Handling and Validation**
The registration system includes defensive checks at multiple stages:
- Pre-finalization registration: Prevents adding layers after the application state is sealed
- Duplicate ID detection: Catches copy-paste errors in
src/data/*.jsexports - Missing disposition: Ensures every layer has defined serialization behavior
- Unknown layer access: Runtime methods fail fast with clear error messages
These validations make debugging extension development significantly easier, as errors surface during initialization rather than at runtime.
Summary**
- DataLayerManager in
src/data/manager.jscentralizes all layer lifecycle operations - Layer modules in
src/data/*.jsexportidplusinit/enable/disable/updatefunctions - Registration occurs via
dataManager.register(module)beforefinalizeRegistrations()locks the system - The serialization registry defines how each layer's state persists across sessions
- Runtime control uses
toggle(),setEnabled(), andrefreshLayer()mediated through the manager
Frequently Asked Questions**
What happens if I call register() after finalizeRegistrations()?
The manager throws a runtime error. Once finalizeRegistrations() completes, _registrationsFinalized becomes true and all subsequent register() calls are rejected with an explicit exception. This prevents state corruption and ensures the serialization registry remains stable.
Can I unregister a layer dynamically?
No. The current DataLayerManager implementation does not expose an unregister() method. Layer removal would require modifying src/data/manager.js to delete entries from the internal layers Map and handle cleanup ordering. For most use cases, disable() provides sufficient control without full removal.
How does the disposition in finalizeRegistrations() affect behavior?
The disposition controls state serialization. 'enabled-only' persists just the on/off status; 'enabled+options' includes configuration values; 'full-state' captures complete internal data. This determines what restores when a user reloads the application or shares a session link.
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 →