God's Eye View Data Layer Interface: Complete Implementation Guide

The God's Eye View data layer interface is a plain JavaScript contract consisting of init(viewer, options?), enable(), disable(), update(dt), destroy(), and an optional getStats() method that every visual layer must implement.

Every dataset rendered in God's Eye View—whether traffic, CCTV feeds, flights, or satellites—conforms to this unified interface. The architecture decouples data sources from the rendering engine, allowing the layer manager to treat all visual layers identically. This guide examines the interface specification, walks through concrete implementations, and shows how to create custom layers.

Core Interface Methods

The data layer interface requires five mandatory methods and one optional method. These are defined in the header comment of src/data/traffic.js and enforced by convention across all layer files.

init(viewer, options?)

Asynchronously initializes the layer's internal state and attaches it to the Cesium viewer.

  • Parameters: viewer (the global Cesium viewer instance), optional options object
  • Returns: Promise<void> or void
  • Purpose: Create entities, set up network connections, configure initial state
// From src/data/traffic.js - typical init pattern
async init(viewer, options = {}) {
  this.viewer = viewer;
  this.signal = options.signal; // AbortController for cancellation
  this.entities = [];
  // ... fetch initial data, create Cesium entities
}

enable()

Activates the layer for visibility. Called after init() completes successfully.

  • Transitions layer from initialized to active state
  • Typically sets entity.show = true or starts animations
  • Must be idempotent (safe to call multiple times)

disable()

Deactivates the layer without destroying resources.

  • Hides visual elements: entity.show = false
  • Pauses network polling or data updates
  • Preserves state for quick re-enabling

update(dt)

Called on every animation frame with elapsed time in milliseconds.

  • Parameter: dt — delta time since last frame in milliseconds
  • Use cases: animate entities, fetch fresh data, cull distant features, update labels
update(dt) {
  // Throttle network updates
  this.accumulator += dt;
  if (this.accumulator > 5000) { // Every 5 seconds
    this.fetchNewData();
    this.accumulator = 0;
  }
  // Animate existing entities based on dt
  this.entities.forEach(e => e.updatePosition(dt));
}

destroy()

Synchronously or asynchronously cleans up all resources.

  • Removes Cesium entities: viewer.entities.remove(this.entity)
  • Aborts pending fetch() requests via AbortController
  • Clears timers, event listeners, and WebGL resources
  • Must be safe to call even if init() failed partially

getStats() (Optional)

Returns a diagnostic object for debugging and performance monitoring.

getStats() {
  return {
    entityCount: this.entities.length,
    lastFetch: this.lastFetchTime,
    bytesTransferred: this.totalBytes,
    errorCount: this.errors.length
  };
}

Layer Manager Integration

The layer manager at src/data/manager.js orchestrates all registered layers by invoking interface methods in strict order.

Lifecycle Sequence

import layerManager from "./data/manager.js";

// 1. Initialize all registered layers
await layerManager.loadAll(viewer);

// 2. Enable specific layer
layerManager.enable("traffic");

// 3. Per-frame updates (called in render loop)
layerManager.updateAll(deltaTime);

// 4. Disable when hidden
layerManager.disable("traffic");

// 5. Cleanup on application exit
await layerManager.destroyAll();

Internal Manager Implementation

// src/data/manager.js - simplified excerpt
const layerRegistry = {
  traffic: () => import("./traffic.js"),
  cctv: () => import("./cctv.js"),
  flights: () => import("./flights.js"),
  // ... additional layers
};

class LayerManager {
  async loadAll(viewer, options = {}) {
    for (const [name, loader] of Object.entries(layerRegistry)) {
      const module = await loader();
      const layer = module.default;
      await layer.init(viewer, { signal: options.signal });
      this.layers.set(name, layer);
    }
  }

  enable(name) {
    const layer = this.layers.get(name);
    if (layer?.enable) layer.enable();
  }

  disable(name) {
    const layer = this.layers.get(name);
    if (layer?.disable) layer.disable();
  }

  updateAll(dt) {
    for (const layer of this.layers.values()) {
      if (layer.update) layer.update(dt);
    }
  }

  async destroyAll() {
    for (const [name, layer] of this.layers) {
      await layer.destroy?.();
      this.layers.delete(name);
    }
  }

  getStats(name) {
    return this.layers.get(name)?.getStats?.();
  }
}

Built-In Layer Implementations

God's Eye View ships with several reference implementations demonstrating the interface in production contexts.

Traffic Layer (src/data/traffic.js)

The primary example documented in source comments. Implements real-time vehicle tracking with:

  • WebSocket connection for live position updates
  • Entity pooling to minimize garbage collection
  • Distance-based LOD (level-of-detail) rendering

CCTV Layer (src/data/cctv.js)

Surveillance camera feeds using:

  • Video texture projection onto Cesium polygons
  • Frustum visualization for camera sight lines
  • Modal/popup integration on click events

Flights Layer (src/data/flights.js)

Aircraft tracking via ADS-B data:

  • Great-circle interpolation between position reports
  • Altitude-based coloring and scaling
  • Trail/ghost rendering for flight paths

Creating Custom Data Layers

Any JavaScript module exporting an object with the interface methods can be registered as a layer.

Complete Custom Layer Example

// src/data/weatherRadar.js
export default {
  // Internal state
  viewer: null,
  entities: [],
  lastUpdate: 0,
  rotation: 0,

  async init(viewer, options = {}) {
    this.viewer = viewer;
    this.signal = options.signal;
    
    // Create radar sweep entity
    this.sweepEntity = viewer.entities.add({
      name: "Weather Radar",
      position: Cesium.Cartesian3.fromDegrees(-95.7129, 37.0902, 0),
      ellipse: {
        semiMinorAxis: 100000, // 100km radius
        semiMajorAxis: 100000,
        material: new Cesium.ColorMaterialProperty(
          new Cesium.Color(0.0, 0.6, 1.0, 0.3)
        ),
        rotation: new Cesium.CallbackProperty(() => this.rotation, false)
      }
    });
    
    this.entities.push(this.sweepEntity);
    await this.fetchRadarData();
  },

  enable() {
    this.entities.forEach(e => e.show = true);
    this.active = true;
  },

  disable() {
    this.entities.forEach(e => e.show = false);
    this.active = false;
  },

  update(dt) {
    if (!this.active) return;
    
    // Animate radar sweep rotation
    this.rotation += dt * 0.001; // 1 radian per second
    
    // Poll for new data every 5 minutes
    this.lastUpdate += dt;
    if (this.lastUpdate > 300000) {
      this.fetchRadarData().catch(console.error);
      this.lastUpdate = 0;
    }
  },

  async fetchRadarData() {
    const response = await fetch("/api/weather/radar", {
      signal: this.signal
    });
    const data = await response.json();
    // Update entity appearances based on precipitation intensity
    this.updateVisualization(data);
  },

  updateVisualization(data) {
    // Implementation: color mapping based on dBZ values
  },

  async destroy() {
    // Clean up entities
    for (const entity of this.entities) {
      this.viewer.entities.remove(entity);
    }
    this.entities = [];
    
    // Abort any pending fetches
    if (this.signal?.abort) {
      this.signal.abort();
    }
  },

  getStats() {
    return {
      entityCount: this.entities.length,
      sweepAngleRad: this.rotation,
      sweepAngleDeg: Cesium.Math.toDegrees(this.rotation) % 360,
      lastDataFetch: this.lastUpdate
    };
  }
};

Registration in Layer Manager

// src/data/manager.js - add to registry
import weatherRadar from "./weatherRadar.js";

const layerRegistry = {
  traffic: () => import("./traffic.js"),
  cctv: () => import("./cctv.js"),
  flights: () => import("./flights.js"),
  satellites: () => import("./satellites.js"),
  weatherRadar, // Static import for custom layer
};

Error Handling and Edge Cases

Robust layer implementations follow these patterns found in src/data/traffic.js:

  • Defensive init(): Check for Cesium viewer availability, handle missing WebGL contexts
  • Graceful destroy(): Use optional chaining (this.entity?.remove()) to handle partial initialization
  • update() throttling: Prevent frame drops from synchronous network I/O
  • AbortController integration: Pass signal through all fetch() calls for cancellation on layer disable

Summary

  • God's Eye View data layer interface mandates five methods—init(), enable(), disable(), update(dt), destroy()—plus optional getStats()
  • The layer manager in src/data/manager.js orchestrates lifecycle across all registered layers uniformly
  • Built-in layers (traffic.js, cctv.js, flights.js) serve as reference implementations of production-quality code
  • Custom layers require only a plain JavaScript object with the interface methods; no inheritance or framework classes needed
  • The architecture enables hot-swappable data sources without modifying core rendering or application logic

Frequently Asked Questions

What happens if a layer doesn't implement all required methods?

The layer manager defensively checks for method existence before invocation (layer.update?.(dt)), so missing methods are silently skipped. However, omitting init() or destroy() typically causes runtime errors or resource leaks. Always implement all five core methods for reliable operation.

Can data layers use async/await throughout their lifecycle?

Yes. init() and destroy() support async operations natively. The update() method may also be async, though the manager does not await its completion to maintain frame rate—fire-and-forget patterns with Promise.catch() are recommended for network calls inside update().

How does God's Eye View handle layer performance monitoring?

The optional getStats() method returns diagnostic objects consumed by the debug UI. Built-in layers expose entity counts, network latency, and memory usage. Aggregate statistics across all active layers are available via layerManager.getAllStats() for identifying bottlenecks.

Is TypeScript support available for the data layer interface?

The codebase uses pure JavaScript without type definitions. For TypeScript projects, you can define the interface as:

interface DataLayer {
  init(viewer: Viewer, options?: { signal?: AbortSignal }): Promise<void> | void;
  enable(): void;
  disable(): void;
  update(dt: number): void;
  destroy(): Promise<void> | void;
  getStats?(): Record<string, unknown>;
}

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 →