Skip to main content

ZarrLayerProvider

The ZarrLayerProvider allows you to render 2D raster data from Zarr datasets as a Cesium imagery layer. It handles:

  • Reading Zarr metadata and multiscale pyramids
  • WebGL rendering of tiles (fast GPU-based color mapping)
  • Dynamic slicing of dimensions (e.g., time, elevation)
  • Runtime style updates (colormap, scale, opacity)
  • CRS detection (EPSG:4326 / EPSG:3857)
  • Smooth integration with Cesium’s ImageryLayer collection

This provider works as a drop-in replacement for Cesium imagery layers, enabling you to visualize 2D scalar fields (e.g., temperature, salinity, chlorophyll) stored in Zarr format, without any server-side preprocessing or conversion.

When to Use ZarrLayerProvider

Use ZarrLayerProvider when your dataset is:

  • 2D (lat × lon)
  • Optionally has extra dimensions: time, elevation, depth, etc.
  • Stored as a Zarr array, possibly multiscale
  • Represents scalar fields (not vectors)

If you need:


Basic Example

import { Viewer } from 'cesium';
import { ZarrLayerProvider } from '@noc-oi/zarr-cesium';

const viewer = new Viewer('cesiumContainer');

const options = {
url: 'https://example.com/data.zarr',
variable: 'salinity',
colormap: 'viridis',
scale: [30, 40]
};

const layer = await ZarrLayerProvider.createLayer(viewer, options);

viewer.imageryLayers.add(layer);

Layer Options

export interface LayerOptions {
url: string; // Public Zarr store
variable: string; // Zarr array name
scale?: [number, number]; // Min/max for color scaling
colormap?: ColorMapName; // Name from jsColormaps, based on matplotlib colormaps
opacity?: number; // Imagery opacity (0–1)
tileWidth?: number; // Cesium tile size (default 256)
tileHeight?: number; // Cesium tile size (default 256)
minimumLevel?: number; // Min zoom level
maximumLevel?: number; // Max zoom level
dimensionNames?: DimensionNamesProps; // Custom dimension names. If not provided, defaults will be used or identified automatically based on CF conventions.
selectors?: Record<string, ZarrSelectorsProps>; // Initial dimension slices
zarrVersion?: 2 | 3; // Zarr version (auto-detected if not set)
crs?: 'EPSG:4326' | 'EPSG:3857'; // Force CRS (auto-detected if not set)
noDataMin?: number; // Custom no-data minimum value. Overrides _FillValue/missing_value.
noDataMax?: number; // Custom no-data maximum value. Overrides _FillValue/missing_value.
}

Dimension Selectors

If your Zarr dataset has dimensions like: time, level related (e.g. depth, elevation), and others, you can slice them using the selectors option.

You can set an initial slice using:

selectors: {
time: { type: 'index', selected: 0 },
elevation: { type: 'index', selected: 10 }
}

Or slice by value instead of index:

selectors: {
time: { type: 'value', selected: '2020-01-01T00:00Z' },
elevation: { type: 'value', selected: 50 } // meters
}

The provider automatically converts value→index using nearest-neighbor lookup. The default behavior (if no selectors are provided) is to select the first index (0) for each dimension.


Creating a Layer

Static constructor

const imageryLayer = await ZarrLayerProvider.createLayer(viewer, options);
viewer.imageryLayers.add(imageryLayer);

This method:

  1. Creates the provider
  2. Loads Zarr metadata
  3. Detects CRS
  4. Sets tiling scheme
  5. Loads the first dimension slice
  6. Returns a ready-to-use ZarrImageryLayer

Runtime API

When added to Cesium, the layer behaves like a normal ImageryLayer, plus extra Zarr-specific features.

const zbLayer = await ZarrLayerProvider.createLayer(viewer, options);
viewer.imageryLayers.add(zbLayer);

Update Style (colormap, scale and opacity)

  • colormap
zbLayer.updateStyle({ colormap: 'plasma' });

The full list of supported colormaps is available in the Colormaps section.

  • scale range
zbLayer.updateStyle({ scale: [20, 35] });
  • opacity
zbLayer.updateStyle({ opacity: 0.5 });
// or using Cesium ImageryLayer API
// zbLayer.alpha = 0.5;

After updating style, the layer performs a soft refresh so Cesium re-renders the tiles in the viewport with the new style.

Update dimension slicing

zbLayer.updateSelectors({
time: { type: 'index', selected: 5 }
});

You can get the list of current dimension and current selectors values using:

const dimValues = zbLayer.provider.dimensionValues;
const selectors = zbLayer.provider.selectors;

This returns a mapping of dimension names to their list of values, e.g.:

dimNames = {
time: ['2020-01-01T00:00Z', '2020-01-02T00:00Z', ...],
elevation: [0, 10, 20, 30, ...]
}

And the current selectors:

selectors = {
time: { type: 'index', selected: 5 },
elevation: { type: 'index', selected: 2 }
};

And with that, you can build UI controls (sliders, dropdowns) to update the layer dynamically.

Remove Layer

To remove the layer and free resources:

viewer.imageryLayers.remove(zbLayer);
zbLayer.destroy();

It is important to call destroy() after removing the layer from the viewer, because this cleans up all internal resources (abort pending requests, etc.).

Supported CRS

Zarr datasets may store coordinate values in:

  • EPSG:4326 (lat, lon degrees)
  • EPSG:3857 (Web Mercator meters)

The provider detects the CRS automatically using:

  • Zarr metadata
  • consolidated metadata
  • coordinate ranges (West/East > 360 → Web Mercator)

Multiscale (Pyramidal) Zarr

If the dataset contains Zarr multiscale metadata (generated by ndpyramid):

"multiscales": [
{
"datasets": [
{"path": "0"}, {"path": "1"}, {"path": "2"}
]
}
]

The provider automatically:

  • chooses the best level based on Cesium zoom
  • caches levels (LRU = 3)
  • correctly slices each level
  • uses metadata-supported shapes for accurate scaling

No extra configuration is required.


No-data Handling

Automatically applies:

  • _FillValue
  • missing_value
  • valid_min / valid_max
  • or custom range

Example of custom range:

noDataMin: -9999, // defaults is to Zarr attributes and then -9999
noDataMax: 9999 // defaults is to Zarr attributes and then 9999

Performance Notes

  • WebGL tile rendering is extremely fast
  • Multiscale/pyramid datasets dramatically improve speed
  • ImageBitmap acceleration is used when available
  • Uses internal concurrency throttling (4 parallel reads)

Summary

FeatureSupported
2D raster Zarr✔️
Zarr v2 & v3✔️
Time dimension✔️
Elevation/depth✔️
Multiscale pyramids✔️
WebGL GPU shading✔️
Colormap updates✔️
Dynamic slicing✔️
CRS auto-detection✔️

Next Steps