# How to Add Custom Data Layers to God's Eye View: A Complete Implementation Guide

> Learn to add custom data layers to God's Eye View. Implement IDataLayer and register your module with DataLayerManager for a complete guide.

- Repository: [Bilawal Sidhu/gods-eye-view](https://github.com/bilawalsidhu/gods-eye-view)
- Tags: how-to-guide
- Published: 2026-09-13

---

**To add custom data layers to God's Eye View, implement the `IDataLayer` contract with `id`, `name`, `enable()`, and `disable()` methods, then register your module with the `DataLayerManager` in [`src/main.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/main.js).**

God's Eye View is an open-source geospatial visualization platform built on Cesium that structures every live-data source as an independent module. Whether you are integrating a REST API feed, a GeoJSON dataset, or a real-time WebSocket stream, the architecture requires implementing a lightweight interface consumed by the central `DataLayerManager` at runtime. This guide walks through the exact contract defined in the source code and provides a production-ready implementation based on the patterns found in [[`src/data/manager.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/manager.js)](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/manager.js) and [[`src/main.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/main.js)](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/main.js#L4-L7).

## What Is a Data-Layer Module in God's Eye View?

In God's Eye View, a **data-layer module** is a plain JavaScript object that encapsulates a single data source's lifecycle. The `DataLayerManager` (implemented in [`src/data/manager.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/manager.js)) maintains a registry of these modules, constructs the UI toggle panel, and forwards lifecycle callbacks when users enable or disable layers. Each module operates independently, managing its own Cesium data sources, timers, and network requests.

The system distinguishes between **bundled layers** (static GeoJSON files shipped with the app, see [`src/data/localLayers.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/localLayers.js)) and **dynamic layers** (live feeds like the flight tracking implementation in [`src/data/flights.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/flights.js)). Both types implement the same interface, ensuring consistent behavior across the application.

## The IDataLayer Contract Explained

Every custom layer must expose an object adhering to the internal *IDataLayer* contract. The `DataLayerManager` validates these properties during registration in [`src/main.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/main.js).

### Required Properties

- **`id`** (`string`): A unique identifier used for toggle state persistence and UI events. This must be globally unique across all registered layers.
- **`name`** (`string`): The human-readable label displayed in the data-toggle panel.
- **`enable(viewer, dataManager)`** (`function`): Called when the user activates the layer. This function receives the Cesium `viewer` instance and should create a data source (e.g., `Cesium.GeoJsonDataSource`, `Cesium.CzmlDataSource`, or a custom `BillboardCollection`) and add it to the scene using `viewer.scene.primitives.add()`.
- **`disable()`** (`function`): Called when the user deactivates the layer. Must remove the data source from the scene via `viewer.scene.primitives.remove()` and clean up any timers, listeners, or cached data.

### Optional Properties

- **`description`** (`string`): Tooltip text shown on hover in the layer panel.
- **`refresh(force)`** (`function`): Implements manual or forced reload logic. Useful for layers polling external APIs that need a "Refresh" button in the UI.
- **`requiresKey`** (`boolean`): When set to `true`, the layer remains hidden in the UI until a provider key is supplied through the *POWER UP* panel. This gates access to premium or rate-limited APIs.

## Step-by-Step Implementation Guide

Follow these steps to integrate a new data source into the God's Eye View architecture.

### 1. Create the Layer Module

Create a new file under `src/data/` (e.g., [`myCustomLayer.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/myCustomLayer.js)). This file will export a single object conforming to the `IDataLayer` contract. For complex integrations, refer to [[`src/data/flights.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/flights.js)](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/flights.js#L1-L30) as a reference implementation that handles live API polling and billboard management.

### 2. Implement the IDataLayer Interface

Structure your module with the mandatory properties. Use `async` for the `enable` method if fetching remote data, as the manager awaits its completion before updating the UI state.

### 3. Handle Data Loading and Cesium Integration

Inside `enable`, load your data and instantiate the appropriate Cesium data source. For API-protected endpoints, use **Vite's proxy configuration** (`/api/...`) to route requests through the development server. This keeps API keys server-side and prevents exposure in the browser, following the pattern used in [`src/data/flights.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/flights.js) for the OpenSky Network integration.

After parsing the payload, add the resulting Cesium entity collection or primitive to the scene. Store a reference to this object (typically `this._source`) so you can remove it later in `disable`.

### 4. Register with DataLayerManager

Import your module into [[`src/main.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/main.js)](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/main.js#L4-L7) and register it with the `DataLayerManager` instance before the application initializes:

```javascript
// src/main.js
import { myCustomLayer } from './data/myCustomLayer.js';

const dataManager = new DataLayerManager(viewer, { 
  allowQaRegistration: import.meta.env.DEV 
});

dataManager.register(myCustomLayer);

```

Alternatively, for bundled GeoJSON datasets that ship with the repository, add your layer to the array exported by [`src/data/localLayers.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/localLayers.js), which uses helper utilities from [`src/data/createLocalGeoJsonLayer.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/createLocalGeoJsonLayer.js) to generate compliant objects.

## Complete Custom Layer Example

Below is a production-ready implementation that fetches a public GeoJSON feed through a Vite proxy endpoint, visualizes the data using Cesium's `GeoJsonDataSource`, and implements optional refresh functionality.

```javascript
// src/data/myCustomLayer.js
import { GeoJsonDataSource } from 'cesium';

/**
 * Custom data-layer that visualises a public GeoJSON feed.
 * The feed is proxied through `/api/myfeed` so any required API key
 * lives only on the server side (see docs/PROXY.md for the setup).
 */
export const myCustomLayer = {
  id: 'my-custom-geojson',
  name: 'My GeoJSON Feed',
  description: 'Shows points of interest from the public XYZ API.',
  
  // Called when the user enables the layer.
  async enable(viewer) {
    // Fetch the GeoJSON via the Vite dev-server proxy.
    const response = await fetch('/api/myfeed');
    const geojson = await response.json();

    // Create a Cesium data source from the GeoJSON.
    this._source = await GeoJsonDataSource.load(geojson, {
      clampToGround: true,
      markerSize: 12,
      markerColor: Cesium.Color.YELLOW,
    });

    // Add it to the scene.
    viewer.scene.primitives.add(this._source);
  },

  // Called when the user disables the layer.
  disable() {
    if (this._source) {
      this._source.viewer.scene.primitives.remove(this._source);
      this._source = null;
    }
  },

  // Optional: expose a refresh method for a manual reload button.
  async refresh() {
    if (!this._source) return;
    await this.disable();
    await this.enable(this._source.viewer);
  },
};

```

```javascript
// src/main.js – register the new layer
import { myCustomLayer } from './data/myCustomLayer.js';

const dataManager = new DataLayerManager(viewer, { 
  allowQaRegistration: import.meta.env.DEV 
});

dataManager.register(myCustomLayer);

```

## Advanced Configuration Options

**Proxy Configuration for API Security**: When integrating third-party APIs that require authentication, configure Vite's `server.proxy` in [`vite.config.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/vite.config.js) to route requests through `/api/your-endpoint`. The `enable` function then fetches from this local proxy path, ensuring the browser never sees the actual API key. This pattern is demonstrated in [`src/data/flights.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/flights.js) for the OpenSky aviation data.

**Provider Key Gating**: For commercial or rate-limited data sources, set `requiresKey: true` in your layer object. The `DataLayerManager` will hide the toggle until the user enters a valid key in the *POWER UP* panel, at which point `enable` can access the stored credential securely.

## Summary

- **God's Eye View** uses a modular architecture where each data source implements the `IDataLayer` contract consumed by `DataLayerManager`.
- **Required methods**: `enable(viewer, dataManager)` to create and add Cesium data sources, and `disable()` to clean up scene primitives and timers.
- **Registration**: Import and register custom layers in [`src/main.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/main.js) using `dataManager.register()`, or add bundled GeoJSON layers to [`src/data/localLayers.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/localLayers.js).
- **Security**: Use Vite's proxy configuration (`/api/...`) to keep API keys server-side when fetching external data.
- **Optional features**: Implement `refresh()` for manual reload buttons and `requiresKey` for gated access to premium data sources.

## Frequently Asked Questions

### What Cesium data source types work with custom layers?

You can use any Cesium data source or primitive collection. Common choices include `Cesium.GeoJsonDataSource` for geographic features, `Cesium.CzmlDataSource` for time-dynamic data, `Cesium.BillboardCollection` for high-performance point markers, or custom `Entity` objects added directly to `viewer.entities`. The `enable` method receives the full `viewer` instance, giving you access to the entire Cesium API.

### How do I protect API keys when fetching external data?

Configure Vite's development server proxy in [`vite.config.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/vite.config.js) to route requests (e.g., `/api/opensky`) to the target API. Your layer's `enable` method then fetches from the local proxy path (`/api/opensky`) rather than the external endpoint. The proxy attaches the actual API key on the server side, ensuring it never appears in browser network logs or source code, as implemented in [`src/data/flights.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/flights.js).

### Can I create layers that require user authentication?

Yes. Set the optional `requiresKey: true` property in your layer object. The `DataLayerManager` will hide the layer's toggle until the user provides a key through the *POWER UP* panel. Your `enable` method can then retrieve this key from the application's secure storage to authenticate API requests, ensuring the layer only activates after credentials are supplied.

### How do I update data without requiring a full page reload?

Implement the optional `refresh(force)` method in your layer object. This method should re-fetch data and update the Cesium data source in place. When present, God's Eye View automatically exposes a refresh button in the layer's UI panel, allowing users to manually trigger updates or clear cached data without restarting the application.