Skip to main content

Data Preparation for Zarr-Cesium

Although Zarr-Cesium can work with any Zarr dataset, it works best when datasets are prepared with web visualization constraints in mind:

  • Projection
  • Chunking
  • Multiscale pyramids
  • Zarr format

This page summarises the full workflow used for preparing large ocean model datasets for browser-based visualization.


1. Reprojecting Curvilinear / Rotated-Pole Grids

Many ocean and climate models (e.g., NEMO, MOM6) use curvilinear or rotated-pole grids (nav_lat, nav_lon). Browsers expect EPSG:4326 or EPSG:3857.

Use iris or xESMF to reproject:

import xesmf as xe

lon_target = np.linspace(-180, 180, 360)
lat_target = np.linspace(-90, 90, 240)

target_grid = xr.Dataset(
{
"lon": (["lon"], lon_target),
"lat": (["lat"], lat_target),
},
)
regridder = xe.Regridder(
ds_source,
target_grid,
method="bilinear"
)
regridded_ds = regridder(ds_source)

2. Chunking for Web Performance

Chunk size dramatically affects browser speed.

Best practices:

WorkflowIdeal chunk sizeWhy
HPC / analysis10–100 MBEfficient for Dask
Web visualization100–500 KBFast requests, limits latency
Map tiles128×128, 256×256 or 512×512Aligns with screen tiles

Avoid chunking across non-spatial dimensions (time, depth):

reprojected_ds = reprojected_ds.chunk({
"time": 1,
"latitude": 256,
"longitude": 256
})

This lets the browser fetch only the visible slice.


3. Building Multiscale Pyramids

To support seamless zooming, convert your dataset to a pyramid representation using ndpyramid:

from ndpyramid import pyramid_resample

pyramid_ds = pyramid_resample(
reprojected_ds,
x="longitude",
y="latitude",
levels=6,
resampling="nearest",
)

Output structure:

multiscale.zarr/
├── 0/ # lowest resolution
├── 1/ # 2× highres
├── 2/ # 4× highres
└── ...

Zarr-Cesium’s 2D provider automatically selects levels based on zoom. For the 3D providers, you can specify the desired level.

Building a GeoZarr pyramid with TopoZarr

topozarr creates multiscale pyramids that follow the GeoZarr conventions. The following example is adapted from NOC's Visualizing Multiscale Zarr and GeoZarr lesson.

First open a regular latitude/longitude dataset, attach its CRS, and create the pyramid:

import xarray as xr
from topozarr import create_pyramid

ds = xr.open_zarr("ocean_temperature.zarr", consolidated=True)
ds = ds.proj.assign_crs({"EPSG": 4326})

pyramid = create_pyramid(
ds,
levels=4,
x_dim="longitude",
y_dim="latitude",
target_chunk_bytes=512 * 512 * 4,
method="mean",
chunks_per_shard=4,
)

target_chunk_bytes controls the approximate uncompressed chunk size; chunks_per_shard groups chunks into Zarr v3 shards to reduce the number of objects. Choose the downsampling method to suit the variable—for example, mean for continuous fields or nearest for categorical data.

Convert the pyramid to a DataTree and write it with the encoding generated by TopoZarr:

pyramid_tree = pyramid.as_datatree()

pyramid_tree.to_zarr(
"ocean_temperature_pyramid.zarr",
mode="w",
consolidated=True,
encoding=pyramid.encoding,
align_chunks=True,
)

TopoZarr orders the pyramid with level 0 at full resolution and later levels progressively coarser. When loading it in Zarr-Cesium, set multiscaleFormat: 'topozarr'. The 2D provider will then map coarse levels to low Cesium zooms and the full-resolution level to high zooms:

const layer = await ZarrLayerProvider.createLayer(viewer, {
url: 'https://example.com/ocean_temperature_pyramid.zarr',
variable: 'temperature',
multiscaleFormat: 'topozarr'
});

TopoZarr and the GeoZarr conventions are evolving, so pin the Python package versions used by a production conversion workflow and check the TopoZarr API when upgrading.


4. Zarr Format

If you are using Zarr v2, write the final dataset to with consolidated metadata:

pyramid_ds.to_zarr("multiscale.zarr", consolidated=True, zarr_version=2)

This ensures compatibility with Zarr-Cesium and efficient loading in the browser.

If you are using Zarr v3, ensure your Zarr store is accessible via HTTP(S) and follows the Zarr v3 specification:

pyramid_ds.to_zarr("multiscale.zarr", zarr_version=3)

5. Example Dataset

The example dataset used in the demo and documentation is prepared using the above workflow. We used approximately 30 GB of NEMO NPD ocean model output (documentation available at https://noc-msm.github.io/NOC_Near_Present_Day/). The dataset includes:

  • Multiple physical variables
  • Depth and time dimensions
  • A reprojected rotated-pole grid
  • Chunking and multiscale pyramids optimised for web delivery

We also provide a sample atmospheric dataset for testing wind-related visualisations. This dataset contains U/V wind components on a regular latitude–longitude grid, processed with the same workflow, and derived from the ERA5 reanalysis of Hurricane Florence.

A full list of available datasets is provided in the data information file on demo/src/application/data/layers-json.tsx.

A full demo is available:


Summary Workflow

Curvilinear Model Output

Reproject (iris / xESMF)

Rechunk (small spatial chunks)

Build pyramids (ndpyramid or TopoZarr)

Write Zarr v2/v3

Load in browser (Zarr-Cesium)

This pipeline allows real-time, fully client-side visualization of multi-GB geospatial datasets directly in CesiumJS.