# How Cesium Layer Sync Works for GeoJSON, XYZ, and 3D Tiles in GeoLibre

> Discover how GeoLibre's CesiumLayerSync synchronizes GeoJSON, XYZ, and 3D Tiles by diffing layers and managing Cesium viewer instances for seamless geospatial data visualization.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-08-01

---

**GeoLibre's `CesiumLayerSync` class reconciles the reactive layer store with a Cesium Viewer by diffing `GeoLibreLayer` objects and generating, updating, or destroying the corresponding `ImageryLayer`, `GeoJsonDataSource`, and `Cesium3DTileset` instances.**

GeoLibre bridges 2D and 3D mapping through a synchronization layer that lives in [`packages/map/src/cesium-layer-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/cesium-layer-sync.ts). The `CesiumLayerSync` class implements a reconciliation strategy similar to `MapController.syncLayers`, ensuring that changes to raster sources, vector data, and photorealistic meshes are reflected on the Cesium globe without full re-renders. This article breaks down the internal mechanics of how the library detects changes, manages Cesium object lifecycles, and optimizes performance for mixed 2D/3D workflows.

## The CesiumLayerSync Class

The core implementation resides in **[`packages/map/src/cesium-layer-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/cesium-layer-sync.ts)** and exports the `CesiumLayerSync` class. This class maintains an internal registry of `LayerEntry` objects mapped by layer ID, allowing it to compare previous and current states of the application's layer store. When the `sync(layers)` method receives an updated array of `GeoLibreLayer` objects, it calculates the minimal set of operations needed to bring the Cesium Viewer into alignment with the store.

The class handles three primary responsibilities: validating which layers Cesium can render, determining whether existing objects can be restyled in place or must be rebuilt, and managing the z-order of imagery layers to match the store's sequence.

## Supported Layer Types

Not all GeoLibre layer types translate to Cesium primitives. The helper `isCesiumSupportedLayerType` implements a whitelist check that restricts synchronization to the following mappings:

- **XYZ / Raster / WMS / WMTS** → `ImageryLayer` via `UrlTemplateImageryProvider` or `WebMapServiceImageryProvider`
- **GeoJSON** → `GeoJsonDataSource` with clamp-to-ground styling
- **3D Tiles** → `Cesium3DTileset` for streaming photorealistic or mesh data

Before creating any objects, the `isSupported` function verifies that required data exists—such as a valid tile URL for XYZ sources, a FeatureCollection for GeoJSON, or a tileset.json endpoint for 3D Tiles.

## The LayerEntry Data Structure

Each synchronized layer receives a corresponding **`LayerEntry`** stored in a `Map<string, LayerEntry>` keyed by the layer's `id`. As defined in [`packages/map/src/cesium-layer-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/cesium-layer-sync.ts), this interface tracks:

- **`kind`**: A normalized discriminator (`"imagery"`, `"geojson"`, or `"3dtiles"`) derived by the `entryKind` helper
- **`layer`**: The last seen layer configuration object for diffing purposes
- **`handle`**: The actual Cesium instance (`ImageryLayer`, `DataSource`, or `Cesium3DTileset`)
- **State flags**: Cancellation tokens and cached opacity values to prevent redundant style calculations

This registry enables precise, granular updates rather than clearing and rebuilding the entire scene on every store change.

## Detecting When to Rebuild vs. Restyle

The **`needsRebuild(prev, next)`** method implements the diffing logic that determines whether a layer requires expensive reconstruction or can receive a cheap property update. Rebuilds are triggered when:

- The `type` field changes (e.g., switching from XYZ to GeoJSON)
- Geometry or source URLs change (new tile endpoints, updated 3D Tiles URLs, or modified GeoJSON data)
- Style-related properties change for GeoJSON layers (fill color, stroke width, or marker color)

Crucially, **opacity and visibility changes do not trigger rebuilds**. These are applied later via `applyAppearance` methods, allowing smooth UI interactions like opacity sliders to perform at 60fps without destroying and recreating WebGL resources.

## The Synchronization Flow

The **`sync(layers)`** method executes a three-phase reconciliation process:

### Removing Stale Layers

The first pass iterates over the existing `LayerEntry` registry. Any layer ID no longer present in the incoming `layers` array is destroyed via `destroyEntry`, which properly removes the object from the Cesium Viewer and cancels any pending asynchronous operations.

### Creating and Updating Entries

For each layer in the new list, the sync logic checks:

1. If the layer type is unsupported → destroy any existing entry
2. If no entry exists → invoke `createEntry` (which routes to `createImagery`, `createGeoJson`, or `createTileset`)
3. If `needsRebuild` returns true → destroy the old entry and create a fresh one
4. Otherwise → call `applyAppearance` to update visibility and opacity

During this phase, any newly created imagery layer sets an internal `imageryRebuilt` flag to true, signaling that reordering logic may need to run.

### Reordering Imagery Layers

Because Cesium's `addImageryProvider` always appends to the top of the stack, GeoLibre performs a separate reordering pass. This only executes if `imageryRebuilt` is true or if the layer order has actually changed, avoiding O(n²) sorting operations during opacity drags. The algorithm iterates through the store's layer order and raises each imagery layer to its correct index using Cesium's `raise` methods.

## Creating Cesium Objects by Type

### Imagery Layers (XYZ, WMS, WMTS)

The **`createImagery`** method handles raster sources via `Cesium.ImageryLayer`:

- **WMS layers**: Instantiate `WebMapServiceImageryProvider` using the GetMap parameters stored in the layer object
- **XYZ/Raster/WMTS**: Use `UrlTemplateImageryProvider` with the tile URL template and min/max zoom constraints

The provider is added to `viewer.imageryLayers`, then `applyAppearance` sets the `show` and `alpha` properties.

### GeoJSON Data Sources

The async **`createGeoJson`** method loads data via `Cesium.GeoJsonDataSource.load`:

1. Pass style options (fill color, stroke color, stroke width, marker color) directly to the loader
2. Enable `clampToGround` for proper terrain draping
3. Add to `viewer.dataSources`
4. Apply appearance, including opacity handling through `applyGeoJsonStyle`

Style updates recompute fill and stroke colors using the layer's `opacity` and `fillOpacity` properties. An `appliedAlpha` guard prevents recalculating identical color values.

### 3D Tilesets

The async **`createTileset`** method handles streaming 3D content:

1. Resolve request headers (including Google Photorealistic API keys) via `resolveThreeDTilesRequestHeaders`
2. Construct a `Cesium.Resource` if custom headers are required
3. Instantiate via `Cesium3DTileset.fromUrl` with the resource and URL
4. Apply an optional altitude offset via `applyTilesetAltitude`, which translates the tileset's model matrix
5. Set visibility and opacity via `applyAppearance`

## Managing Appearance and Opacity

Each layer type implements optimized appearance handling:

- **Imagery**: Direct manipulation of `imageryLayer.show` and `imageryLayer.alpha`
- **GeoJSON**: Visibility toggles the `DataSource` show property, while opacity requires re-running `applyGeoJsonStyle` to blend fill and stroke colors with the alpha channel
- **3D Tiles**: Simple show/hide on the `Cesium3DTileset` instance; opacity is applied to the tileset's style if supported

These updates occur outside the rebuild cycle, ensuring that UI-driven property changes remain responsive.

## Implementation Example

The following TypeScript example demonstrates initializing the sync helper and applying a mixed layer stack:

```typescript
import { CesiumLayerSync } from "@geolibre/map";
import { Viewer } from "cesium";
import type { GeoLibreLayer } from "@geolibre/core";

// Initialize Cesium Viewer
const viewer = new Viewer(document.getElementById("cesiumContainer")!);

// Create the sync instance
const cesiumSync = new CesiumLayerSync(Cesium, viewer);

// Typical store update handler
function onStoreChange(layers: GeoLibreLayer[]) {
  cesiumSync.sync(layers);
}

// Example layer configurations
const layers: GeoLibreLayer[] = [
  // XYZ satellite imagery
  {
    id: "satellite",
    type: "xyz",
    source: { 
      tiles: ["https://server/tile/{z}/{x}/{y}.png"], 
      maxzoom: 19 
    },
    visible: true,
    opacity: 0.9,
  },
  // GeoJSON road network
  {
    id: "roads",
    type: "geojson",
    geojson: { 
      type: "FeatureCollection", 
      features: [] 
    },
    style: { 
      strokeColor: "#ff0000", 
      strokeWidth: 3, 
      fillOpacity: 0.0 
    },
    visible: true,
    opacity: 1.0,
  },
  // 3D city model
  {
    id: "city-3d",
    type: "3d-tiles",
    source: { 
      url: "https://example.com/tileset/tileset.json", 
      altitudeOffset: 20 
    },
    visible: true,
    opacity: 1.0,
  },
];

// Synchronize with the globe
cesiumSync.sync(layers);

```

## Summary

- **CesiumLayerSync** in [`packages/map/src/cesium-layer-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/cesium-layer-sync.ts) manages the lifecycle of Cesium objects based on store state changes
- **Supported types** include XYZ (ImageryLayer), GeoJSON (GeoJsonDataSource), and 3D Tiles (Cesium3DTileset), validated via `isCesiumSupportedLayerType`
- **Rebuild detection** through `needsRebuild` separates expensive reconstruction from cheap property updates, keeping opacity changes performant
- **LayerEntry** registry tracks Cesium handles and previous state to enable precise diffing and cleanup
- **Imagery reordering** runs as a separate optimization pass to maintain z-order without O(n²) penalties during continuous updates

## Frequently Asked Questions

### What triggers a full rebuild of a Cesium layer instead of a style update?

A rebuild occurs when `needsRebuild` detects changes to the layer `type`, source URLs (tile endpoints or GeoJSON data), or GeoJSON-specific style properties like fill and stroke colors. Simple visibility toggles or opacity adjustments are applied in place without destroying the underlying Cesium object.

### How does GeoLibre handle the ordering of multiple raster layers on the Cesium globe?

Because Cesium appends new imagery to the top of the stack by default, GeoLibre tracks whether any imagery layers were rebuilt during the sync cycle. If so, it executes a secondary pass that raises each imagery layer to its correct index according to the store's layer order, ensuring the visual hierarchy matches the application's state.

### How is opacity implemented differently for GeoJSON compared to raster imagery?

Raster imagery uses Cesium's native `alpha` property directly on the `ImageryLayer`. GeoJSON opacity requires recalculating color values through `applyGeoJsonStyle`, which blends the layer's `opacity` and `fillOpacity` settings with the configured stroke and fill colors, then reapplies them to the data source entities.

### Can 3D Tiles layers include authentication headers or API keys?

Yes. The `createTileset` method calls `resolveThreeDTilesRequestHeaders` to assemble authentication headers (such as Google Photorealistic 3D Tiles API keys). These headers are bundled into a `Cesium.Resource` object and passed to `Cesium3DTileset.fromUrl`, ensuring secure access to protected tile endpoints.