Lightweight multiscale zarr
Project description
lightweight multiscale zarr pyramids
Python library to create multiscale Zarr pyramids for usage with zarr-layer.
Follows these zarr-conventions:
- multiscales — pyramid structure and resolution levels
- proj: — coordinate reference system (CRS)
- spatial: — affine transform, bounding box, and dimension names
Warning: experimental
Usage
Installation
uv add topozarr
# or
pip install topozarr
Pyramids are computed by topozarr-core, a small Rust kernel (installed automatically as a wheel), and written with zarr-python. An engine="xarray" path and pyramid.as_datatree() are available for Dask-distributed workflows. The tutorial extra includes everything needed to run the examples below:
uv add 'topozarr[tutorial]'
# or
pip install 'topozarr[tutorial]'
Example
import xarray as xr
import xproj # for crs assignment
from topozarr import create_pyramid
# Load the air_temperature Xarray tutorial dataset
ds = xr.tutorial.open_dataset('air_temperature').drop_encoding()
ds = ds.proj.assign_crs(spatial_ref="EPSG:4326")
print(ds)
pyramid = create_pyramid(
ds,
levels=2,
x_dim="lon",
y_dim="lat",
method="mean", # "mean" (default) | "max" | "min" | "sum"
)
print(pyramid.encoding)
# compute and write all levels
pyramid.write("pyramid.zarr")
levels is the total number of resolution levels, including the original. Level 0 is the original (highest) resolution; each subsequent level is coarsened by a factor of 2 per spatial dimension, so the last level is the coarsest.
create_pyramid returns a write plan; pyramid.write(store) does the work: level 0 is copied from the source dataset, then each level is block-reduced from the previously written one, streaming shard-sized regions through the Rust kernel on a thread pool (tune with max_workers). Reduction semantics match xarray.coarsen(boundary="trim") exactly, including skipna handling of NaN / _FillValue nodata.
Visualization hints
Use layer_hints to embed colormap and color range hints for zarr-layer directly in the pyramid metadata:
from topozarr.metadata import ZarrLayerVarConfig
pyramid = create_pyramid(
ds,
levels=2,
x_dim="lon",
y_dim="lat",
layer_hints={"air": ZarrLayerVarConfig(colormap="blues", clim=[230, 310])},
)
These are written into the root zarr-layer metadata key and are optional — omitting layer_hints has no effect on the pyramid structure or encoding.
Chunking
pyramid.encoding holds the chunk and shard sizes per variable per level; pyramid.write applies them automatically.
# Inspect the encoding before writing
print(pyramid.encoding)
The heuristics target ~500KB chunks for web visualization. You can tune shard size with chunks_per_shard, the number of chunks per shard along each spatial dimension (default: 4, giving 4×4 = 16 chunks per shard and ~8MB shards). Valid values are powers of 2: 1, 2, 4, 8, 16, 32. Shards are also the unit of work during pyramid generation, so larger shards mean fewer, bigger reads/writes and higher memory usage.
chunks_per_shard |
chunks/shard | approx shard size |
|---|---|---|
| 1 | 1 | ~500KB |
| 4 | 16 | ~8MB (default) |
| 8 | 64 | ~32MB |
| 16 | 256 | ~128MB |
Pass chunks_per_shard=None to disable sharding entirely.
Writing
pyramid.write accepts anything zarr-python can open — a local path, an ObjectStore, or an icechunk session store:
# Write to object storage
from obstore.store import from_url
from zarr.storage import ObjectStore
store = ObjectStore(from_url(url="s3://carbonplan-scratch/topozarr/aira.zarr", region="us-west-2"))
pyramid.write(store, mode="w")
# Write to Icechunk
import icechunk
storage = icechunk.s3_storage(bucket="<your_bucket>", prefix="<your_prefix>", from_env=True)
repo = icechunk.Repository.create(storage)
session = repo.writable_session("main")
pyramid.write(session.store, mode="w")
session.commit("write pyramid")
Optional: zarrs codec pipeline
Compression codec work can optionally be routed through the Rust zarrs codec pipeline. It plugs in at the codec layer, so it works with any store backend (filesystem, ObjectStore, icechunk):
uv add zarrs
import zarr
zarr.config.set({"codec_pipeline.path": "zarrs.ZarrsCodecPipeline"})
Experimental: native Rust write path
Pyramids are written through zarr-python by default. Passing io="rust" instead encodes and stores the spatial variables natively in the topozarr-core Rust kernel (via the bundled zarrs crate — no extra install), overlapping encode and upload on a shared connection pool:
pyramid.write("s3://bucket/pyramid.zarr", io="rust")
It can be noticeably faster on object stores (~25% on S3 in our benchmarks). Metadata and non-spatial variables still go through zarr-python. Experimental — the API may change; this is a separate write path, unrelated to the zarrs codec pipeline above.
Note:
write()runs in the calling process on a local thread pool — it is not Dask-distributed-aware. A lazy/Dask-backed source is fine, but don't drivewrite()from distributed workers or write the same store concurrently.
Contributing
Clone the repo and install with the test dependency group. Building from source requires a Rust toolchain for the topozarr-core kernel (in core/):
git clone https://github.com/carbonplan/topozarr
cd topozarr
uv sync --group test
Run tests:
uv run pytest -n auto
Run conformance tests against the GeoZarr spec (requires the conformance group):
uv sync --group conformance
uv run pytest -n auto -m conformance
Lint and format:
uv run pre-commit run --all-files
To regenerate the demo datasets in S3 (requires AWS credentials), install the tutorial extra and run the build script:
uv sync --extra tutorial
uv run python scripts/build_demo_data.py --help
License
[!IMPORTANT] This code is licensed under the MIT License - see the LICENSE file for details.
About Us
CarbonPlan is a nonprofit organization that uses data and science for climate action. We aim to improve the transparency and scientific integrity of climate solutions through open data and tools. Find out more at carbonplan.org or get in touch by opening an issue or sending us an email
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file topozarr-0.1.1.tar.gz.
File metadata
- Download URL: topozarr-0.1.1.tar.gz
- Upload date:
- Size: 20.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
054233b60dfbc4f2447bc6530045b54dbd8dd830d8a31ea7671694d0cb989f46
|
|
| MD5 |
0ac85f7bc84406579be1432002e0ed34
|
|
| BLAKE2b-256 |
075d554257f708c091a8e688c45faa0cc2ec4b97ee0df3b1e914d29861ef1b1f
|
File details
Details for the file topozarr-0.1.1-py3-none-any.whl.
File metadata
- Download URL: topozarr-0.1.1-py3-none-any.whl
- Upload date:
- Size: 23.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b69c5d5f9e4bfb7913e452ccd78f2ac89d66bd8c32e60b1d1c7232a63936910
|
|
| MD5 |
9769d4835a8e09e1a0413ce8a7c081db
|
|
| BLAKE2b-256 |
dc76cd19ca875f9a15e8d725958b96c9616ea47a31df659bd1f404e3d1791e00
|