++ ++
GeoZL

Python API

Measure, pick, run.

The high-level API has five calls.

profile ranks recipes for a tile.

graph builds a reusable graph.

compress returns a frame.

decompress returns flat bytes.

coeffs reads what a frame carries.

A 512 by 512 elevation tile over the Mont Blanc massif, a valley running diagonally between two ridges.
GLO‑30 · N45 E006 · 512² ~290 m ~2 280 m

the Python API

one workflow

One tile selects the pipeline. Every matching tile reuses it.

signature profile(tile, *, prior="planar", width=None, planes=None, error=None, reps=5, nodata=None, verify=False) → ProfileResults

priorstr · NonePredictor filter. None tries all; "none" disables prediction.
widthint · NoneRow length; defaults to tile.shape[-1].
planesint · NoneStack count; defaults to B for (B, Y, X).
repsintTimed runs per recipe.
errornumber · str · NoneAbsolute number, relative percentage or full recipe; zero is lossless.
nodatascalar · NoneMissing-value sentinel.
→ resultProfileResultsList-like rows sorted by ratio; print(rows) includes the input and settings.

load the raster

Load one representative tile.Read a 512 × 512 DEM window and cast it to int16.
python · load a 512 × 512 tile
import numpy as np
import rasterio
from rasterio.windows import Window
import geozl

url = "https://copernicus-dem-30m.s3.amazonaws.com/Copernicus_DSM_COG_10_N45_00_E006_00_DEM/Copernicus_DSM_COG_10_N45_00_E006_00_DEM.tif"

with rasterio.open(url) as src:
    tile = src.read(
        1, window=Window(1000, 1000, 512, 512)
    ).round().astype(np.int16)

raw = tile.nbytes
(512, 512) int16 0.52 MB
Elevation tile from the Mont Blanc massif
Mont Blanc massifGLO-30 · 512²
sourceGLO‑30 COG
shape512 × 512
as int16524.3 kB

the graph model

A graph is a reusable codec pipeline. profile finds a recipe; graph builds it unchanged.

A GeoZL recipe as a directed codec graph A raster branches across predictor, layout and backend choices. The highlighted path is planar, zigzag, transpose and entropy, producing a self-describing frame. input predictor map layout backend output raster tileint16 planar delta_n identity zigzag transpose direct entropy zstd field_lz frameself-describing

Recipes are lossless unless error adds quantisation before the predictor.

profile the selection step

profile benchmarks every valid recipe on one representative tile and ranks them by ratio.

The result behaves like a list. print(rows) shows the input geometry, benchmark settings and table together.

Run it once. Pick from the Pareto front: the smallest frame is not always the fastest decode.

python · benchmark one representative tile
rows = geozl.profile(
    tile, prior=None, reps=5
)
print(rows)
best = rows[0]["graph"]
planar>zigzag>transpose>entropy 4.50×
compression ratio vs decode speed 48 candidateshover a square pareto front dominated
2.0× 3.0× 4.0× 500 1000 1500 2000 ↑ ratio decode MB/s →

graph and compress the production step

profile chooses once; graph builds once; compress runs for every tile.

profile once, build once, compress every tile same data family

oncesample tileRepresentative.
onceprofileRanks recipes.
oncegraphBuilds the winner.
per tilecompressRuns the pipeline.
per tileframeDeterministic bytes.
python · choose once, build once, run many times
best = geozl.profile(tile, prior=None)[0]["graph"]
g = geozl.graph(tile, best)

for tile in tiles:
    frame = geozl.compress(tile, graph=g)
524.3 kB → 116.5 kB 4.50×

error bounded-error compression

error trades exact values for a smaller frame under an explicit per-sample bound. Pass 2 for a linear absolute bound or "1%" for a logarithmic relative bound. None and zero stay lossless.

Full LINEAR, LOG and SQRT recipes remain available when you need their advanced options.

Build from data spanning the product; later tiles outside that domain are rejected.

linear · elevation|x − x̂| ≤ V log · SAR backscatter|x − x̂| ≤ P% of x sqrt · optical radiance|x − x̂| ≤ V·σ(x), σ² = a + b·x

SQRT: fit noise once with geozl.lossy.fit_noise(stack), then reuse a and b across tiles.

frame size vs. allowed error
25 50 75 100 0.5 1 2 4 8 16 32 64 ↑ frame kB error, metres → lossless · 116.5 kB 1
1knee8.81×±2.5 m · 59.5 kB · half the lossless frame.

decompress get the array back

decompress(frame) verifies the self-describing frame and returns flat uint8.

Restore dtype and shape with .view and .reshape. Lossless frames round-trip exactly; lossy frames respect their bound.

python · restore the raster
back = geozl.decompress(frame)
back = back.view("int16").reshape(512, 512)
(512, 512) int16

complete workflow measure, pick, run, restore

python
rows = geozl.profile(tile, prior=None)
best = rows[0]["graph"]

g = geozl.graph(tile, best, error=4)
frame = geozl.compress(tile, graph=g)
back = geozl.decompress(frame).view("int16").reshape(512, 512)
9.93× 0.05 MB max error 4 m
The original elevation tile.
original
The tile reconstructed after a lossy round trip, visually identical to the original.
reconstructed
The difference between the two, mostly quantisation noise with structure along the valley floor and the ridges.
error · stretched to ±5 m

Reconstruction looks unchanged; the error view is amplified to ±5 m.