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 key
  • init(viewer, { signal }) – one-time setup (called once)
  • enable(viewer, { signal }) – activate the overlay
  • disable(viewer, { signal }) – deactivate the overlay
  • update(viewer, { signal }) – refresh data on demand

Optional Exports

Additional capabilities include:

  • getStats() – return telemetry for debugging
  • setLifecyclePresentation(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:

  1. Checks that _registrationsFinalized === false (throws if registrations are locked)
  2. Delegates to _registerLayer() for actual processing
  3. _registerLayer() validates that module.id exists and is unique
  4. Throws Error if duplicate IDs are detected
  5. Stores the module in this.layers with 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 = true to prevent subsequent register() 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/*.js exports
  • 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.js centralizes all layer lifecycle operations
  • Layer modules in src/data/*.js export id plus init/enable/disable/update functions
  • Registration occurs via dataManager.register(module) before finalizeRegistrations() locks the system
  • The serialization registry defines how each layer's state persists across sessions
  • Runtime control uses toggle(), setEnabled(), and refreshLayer() 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →