How Cesium Layer Sync Works for GeoJSON, XYZ, and 3D Tiles in GeoLibre
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. 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 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 →
ImageryLayerviaUrlTemplateImageryProviderorWebMapServiceImageryProvider - GeoJSON →
GeoJsonDataSourcewith clamp-to-ground styling - 3D Tiles →
Cesium3DTilesetfor 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, this interface tracks:
kind: A normalized discriminator ("imagery","geojson", or"3dtiles") derived by theentryKindhelperlayer: The last seen layer configuration object for diffing purposeshandle: The actual Cesium instance (ImageryLayer,DataSource, orCesium3DTileset)- 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
typefield 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:
- If the layer type is unsupported → destroy any existing entry
- If no entry exists → invoke
createEntry(which routes tocreateImagery,createGeoJson, orcreateTileset) - If
needsRebuildreturns true → destroy the old entry and create a fresh one - Otherwise → call
applyAppearanceto 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
WebMapServiceImageryProviderusing the GetMap parameters stored in the layer object - XYZ/Raster/WMTS: Use
UrlTemplateImageryProviderwith 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:
- Pass style options (fill color, stroke color, stroke width, marker color) directly to the loader
- Enable
clampToGroundfor proper terrain draping - Add to
viewer.dataSources - 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:
- Resolve request headers (including Google Photorealistic API keys) via
resolveThreeDTilesRequestHeaders - Construct a
Cesium.Resourceif custom headers are required - Instantiate via
Cesium3DTileset.fromUrlwith the resource and URL - Apply an optional altitude offset via
applyTilesetAltitude, which translates the tileset's model matrix - Set visibility and opacity via
applyAppearance
Managing Appearance and Opacity
Each layer type implements optimized appearance handling:
- Imagery: Direct manipulation of
imageryLayer.showandimageryLayer.alpha - GeoJSON: Visibility toggles the
DataSourceshow property, while opacity requires re-runningapplyGeoJsonStyleto blend fill and stroke colors with the alpha channel - 3D Tiles: Simple show/hide on the
Cesium3DTilesetinstance; 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:
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.tsmanages 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
needsRebuildseparates 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.
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 →