Switch hosting to Source Coop and improve Zarr v3 support
v0.10.0 (2026-08-25)
This release moves all data hosting to the Source Cooperative and makes the Zarr pipeline work end to end against remote object stores such as source.coop
There are several new model variants availabile, see geotessera info. You can also browse them interactively via Zarr on https://tze.geotessera.org
╭─────────┬──────────────────────┬────────────────┬─────────────╮
│ Version │ Variant │ Repository dir │ Status │
├─────────┼──────────────────────┼────────────────┼─────────────┤
│ 1.0 │ vultr (default) │ v1 │ available │
│ 1.1 │ cambridge (default) │ v1.1-cam │ available │
│ 1.1 │ dclimate │ - │ coming soon │
│ 2.0 │ 2B-L~beta1 (default) │ v2-2B-L~beta1 │ available │
│ 2.0 │ 2B-L~beta2 │ v2-2B-L~beta2 │ available │
╰─────────┴──────────────────────┴────────────────┴─────────────╯
Breaking Changes
-
All NPY downloads now come from the Source Cooperative, fronted by CloudFlare.
Embeddings, landmasks and manifests are served from the public
https://data.source.coop/tessera/tesserarepository over HTTPS,
replacing the retiredtessera-embeddingsAWS S3 bucket. Note that we
will deprecate and shut down older hosting soon as we need the storage
capacity back, so please upgrade to this new library soon. (@avsm) -
botocoreandawscrtare no longer required dependencies asurllib3
is a new direct dependency.s3://locations need the new optionals3
extra (pip install 'geotessera[s3]'), which pulls ins3fsand
botocore(@avsm). -
Convention metadata now comes from
zarr-cm, replacinggeozarr-toolkit.
Stores stampspatial:/proj:at revision r3 andmultiscalesat r2. (@avsm) -
sphinxandcramare no longer runtime dependencies.
Sphinx moved to adocsextra (pip install geotessera[docs])
and cram to thedevdependency group (@avsm) -
The Zarr read API drops
crs=. The store is UTM-native, so
GeoTesseraZarrtakes lon/lat and routes to theutm{NN}group holding
the point, and the.tesseraaccessor takes eastings and northings in
that zone's own CRS. (@avsm) -
The unused
TesseraTileTransformclass and itsgeotessera.tile_transform
module are removed;GeoTesseraZarrdatasets carry plain coordinate
arrays instead. (#305 @aneeshnaik)
New Features
-
zarr_store_urlaccepts version names:zarr_store_url("v2")resolves
to the v2 default variant's store, and explicit store paths still pass
through. (@avsm) -
GeoTesseraZarrandopen_zoneaccept azarr.abc.store.Storeas
well as a URL, and the new publiczarr_store(location)builds the
default retrying store. The store can therefore be wrapped, for
example in zarr's experimentalCacheStorefor a local cache, and
passed in. (@avsm) -
Zarr reads from
http(s)stores go throughobstore, which retries
each request with exponential backoff and jitter — one dropped response
from a busy data server costs a chunk, not the whole read.obstore
is a new dependency. (@avsm) -
Matryoshka depth reads.
sample_points,read_region,iter_region
andread_patchtakedepth=on stores that declare depth arrays
(v2 onwards):depth=16reads the 16-dimensionembeddings_d16
prefix for an eighth of the bytes, dequantised by the sharedscales
as usual. A store without the requested depth raises and lists what
it has. (@avsm) -
read_region_quantizedonGeoTesseraZarrand the.tessera
accessor returns the int8 window and its scales without dequantising,
at a quarter of the bytes ofread_region; combined withdepth=16
a window costs 1/32 of the full float32 mosaic. (@avsm) -
A new
iter_region(bbox, year, strip_rows=...)onGeoTesseraZarrand
the.tesseraaccessor streams a region as row strips of dequantised
pixels with their transforms, downloading the next strip while the
caller works on the current one. (@avsm) -
A new
GeoTesseraZarr.read_patch(lon, lat, year, size_px)returns a
(size_px, size_px, 128)patch centred on a point. A patch within one
UTM zone is sliced from the native grid unresampled; one crossing a zone
boundary is merged onto a patch-centred transverse Mercator grid with
nearest-neighbour resampling, anddst_crs=pins any other output CRS.
read_regionnow warns when its bbox crosses a zone boundary. (@avsm @aneeshnaik) -
The hosted Zarr store is available at
https://data.source.coop/tessera/tessera/zarr/v1.GeoTesseraZarr()
streams from it with no downloads and no configuration (@avsm) -
Seam-aware point reads for
sample_atandsample_points.
Tiles are 0.1 degrees and UTM zones 6 degrees, so round coordinates fall on
tile edges and multiples of 6 fall on zone seams. Our Zarr wrapper now
tries the neighbouring UTM zone within 0.1 degrees of a seam, and
search_pxaccepts the nearest valid pixel within 1 pixel. (@avsm) -
A new
probe()on the store and on the.tesseraaccessor returns
(embedding, status), the status one ofvalid,water,nodataor
outside. Use it to tell open water from a location the store does not
cover, whichsample_atreports alike as NaN. It also rejects a point
beyond the grid instead of snapping it to the nearest edge pixel.
Reported by Srinivasan Keshav (@avsm) -
Per-version default variant allows omitting the dataset-variant.
--dataset-variantnow selects the version's default variant (vultr
for v1,cambridgefor v1.1,2B-L~beta1for v2) so
GeoTessera(dataset_version="v1.1")works.
The known datasets are listed in the new "Known Datasets" table printed by
geotessera info(@avsm) -
geotessera-registry s3scanscans Source Cooperative to list
embeddings. This allows manifests and landmask registries to be
regenerated directly from the Source Cooperative repository. (@avsm) -
zarr-initandzarr-filltake locations rather than paths, so a store on
one S3 node can be filled from tiles on another with no local mirror. (@avsm) -
zarr-fill --zones Nis safe to run as one process per UTM zone against a
shared store, and fills are stateless.
--rewrite-existing-shardsforces a rebuild, needed only when the tile
inventory has grown. (@avsm) -
Each zone group carries stretch statistics collected during the fill, at no
extra I/O: exact mean and covariance sufficient statistics per (zone, year)
plus a weighted 20,000-pixel sample for quantiles. (@avsm) -
zarr-stretch --per-zonecomputes one stretch per UTM zone, and
zarr-global-preview --blend-zone-stretchcolours each chunk with a blend
of its zone's stretch and its neighbour's, weighted by longitude, so zone
boundaries stay seamless by construction.zarr-global-previewbuilds the
EPSG:4326 RGB pyramid from a remote store. (@avsm) -
zarr-init --matryoshka-depths 4,16also stores the first N dimensions of
every embedding as their own arrays, so a client can read a 4- or
16-dimensional prefix without decoding all 128 bands. Depth arrays share
the shard grid withembeddingsand are dequantised by the samescales;
storage overhead is about 16%. Requires matryoshka-ordered dimensions and
is refused below v2, where a prefix would be an arbitrary slice.
zarr-fill,zarr-extendandzarr-scanfollow the store's declaration,
andzarr-global-previewreads its colour bands from the shallowest array
that holds them. (@avsm) -
geotessera-registry zarr-scaninventories a store's shards without
writing anything, classifying each aswritten,missing, orempty.
geotessera-registry zarr-verifychecks a store's contents against the
source tiles it was built from: it samples random (year, tile) pairs,
reads a pixel block from each source.npypair and the same ground
position from the store, and requires embeddings to round-trip exactly,
scales to match.zarr-scanremains the coverage check. (@avsm) -
geotessera-registry zarr-extendappends years to an existing store's
time axis. Time is chunked one year per chunk, making this a metadata-only
edit; years may only be appended, since inserting an earlier one would
renumber every chunk. (@avsm) -
geotessera-registry zarr-consolidatere-consolidates a store's root
metadata after in-place changes and merges the per-zone ingestion
registries into_registry.parquet— the single-writer step that finishes
a parallel sweep. Accepts a remote store URL as well as a local path.
(@avsm) -
open_zone(lon=...)andGeoTesseraZarr.open_zone(lon=...)accept a
whole-number longitude.lon=-3previously raisedTypeError. (@avsm) -
New
geotessera-registry s3syncsubcommand for one incremental pass that
rescans a dataset's npy tree, diffs against the manifest as of the last
successful sync, publishes the fresh manifest, fills only the changed
(zone, year)pairs of the Zarr store (rewriting shards that absorbed
new tiles), and re-renders just the changed zones of the preview year's
RGB pyramid. (@avsm)
Performance
-
sample_pointsreads all points through zarr's own concurrent
pipeline, one coordinate-indexed read per UTM zone instead of one
request per point — about 19x faster on scattered points, more when
clustered. Only unwritten pixels and seam points retry per point.
(@avsm) -
Point sampling no longer rebuilds a pyproj transformer for every point.
(@avsm)
Bug Fixes
-
A single-zone
read_patchreads its window through zarr's own pipeline
instead of a daskreindexthat materialised whole shard chunks — a
64 px patch dropped from minutes to seconds. Its two dask progress
bars (scales, then embeddings) had suggested two UTM zones were being
queried; they were not. Reported by Aneesh Naik. (@avsm) -
The cross-zone
read_patchCRS is returned as WKT with a descriptive
name ("Tessera patch Transverse Mercator lon_0=...") rather than an
anonymous PROJ.4 string. Single-zone patches keep returning the
zone's EPSG code; no EPSG code exists for the patch-centred meridian.
Reported by Aneesh Naik. (@avsm) -
read_regionanditer_regionsize their window from all four corners
of the lon/lat bbox. Northing extremes sit on different corners as the
UTM grid curves away from the central meridian, so the old two-corner
window silently cropped a wide box by up to a couple of kilometres at
the top and bottom. (@avsm) -
coveragerenders a single-source dataset in the website's multi-colour
year palette again, keeping per-source tints for maps that overlay
several versions or variants; the globe viewer shows the matching
legend. (#330 @adpeace) -
Out-of-range scales no longer poison the stretch statistics. (@avsm)
-
zarr-stretchrefuses to persist a stretch that fails its own drift check
unless--allow-drift. It previously warned and saved anyway, which is
how a covariance known to be wrong reached every preview built from it.
(@avsm) -
Fills no longer deadlock against an object store: a forked worker
inheriteds3fs's cached event loop but not the thread running it, and
waited forever on its first call. Workers now start with "spawn" and
reset any inherited fsspec state. (@avsm) -
zarr-global-previewno longer races itself creating the pyramid: zarr's
create is check-then-write, so parallel zone sweeps died on "already
exists" errors. Every creation step is now idempotent. (@avsm) -
Zones at the antimeridian no longer claim the whole grid width. A shard
straddling 180° samples corners near both -180 and +180, so the naive
span claimed every chunk column at that latitude — utm60 enqueued 1.4M
level-0 chunks to reproject 360 shards, and the coarsening pass rewrote
16M chunk slots for utm01's 18.8k real chunks. Both now detect the wrap
and split into two tight ranges; every other zone is bit-identical. (@avsm) -
The reprojection work list comes from the footprints of the shards that
exist rather than the zone's bounding rectangle, which at high latitude
back-projects across most longitudes. (@avsm) -
Pyramid levels keep their per-level
spatial:shapeand
spatial:transform, whichgeozarr-toolkit's layout builder silently
dropped. (@avsm) -
Incremental fills no longer erase neighbouring tiles: a shard write
replaces the whole shard, so touched shards are now rebuilt from every
tile overlapping them. (@avsm) -
Failed shards are no longer recorded as written, so re-running a fill
retries exactly the unfinished work, and a fill with any failed shard
reports an error. (@avsm) -
geotessera-registrypropagates exit status. Command return codes were
discarded, so failures reported success to the shell. (@avsm) -
Build bookkeeping no longer lives inside the store, which now contains
only Zarr. Anything an older build left at the store root is still read
so existing stores resume correctly, but nothing is written back. (@avsm)
Documentation
- The
geotesserapackage docstring now coversGeoTesseraZarralongside the
GeoTIFF export path; it previously described only the latter (@avsm)
Internal
- The globe viewer HTML embedded in
cli.pymoved to
geotessera/templates/globe.html, shipped as package data (@avsm)