Home -> Examples -> python

Python bindings · raster · preview

The engine, as a Python function.

Every other demo on this site is TerraServe running as a server. This one is TerraServe as a Python library: render_png() takes a COG window and hands back PNG bytes, no HTTP involved, byte-identical to what the CLI or a live WMS server would draw for the same request.

Preview · v0.1.0

This is a preview, not a 1.0. render_png() covers the raster path only: a COG in, a styled PNG out. Vector layers and text labels (GeoPackage, FlatGeoBuf, SLD) are designed as the next release and aren't built yet, and band-math (NDVI-style expressions) is deferred too. It isn't on PyPI: install the wheel straight from the GitHub release below. Expect the API to still move before a 1.0.

python A Cascais orthophoto rendered to PNG by terraserve.render_png()

Install

One wheel, no PyPI yet.

v0.1.0 is a GitHub release, not a PyPI package. It ships as one abi3 wheel, built once and compatible with CPython 3.9 and every version after it, plus a matching aarch64 wheel for arm. Both are manylinux_2_28, which covers most Linux from the last few years, and PROJ is compiled in, so it imports with no system libproj to install first.

# not on PyPI yet, install straight from the GitHub release
pip install https://github.com/terraops-org/terraserve/releases/download/v0.1.0/terraserve-0.1.0-cp39-abi3-manylinux_2_28_x86_64.whl

# arm64 (aarch64) is the same tag, a separate wheel
pip install https://github.com/terraops-org/terraserve/releases/download/v0.1.0/terraserve-0.1.0-cp39-abi3-manylinux_2_28_aarch64.whl

The exact call

The code that made the screenshot above.

Eight keyword arguments in, PNG bytes out. This is the literal call, same bounding box and same style file the fixtures in TerraServe's own test suite use, that rendered the image at the top of this page.

# the exact call that made the screenshot above
import terraserve

png = terraserve.render_png(
    cog_path="cascais.cog.deflate.tif",
    bbox=(-116201.25, -108717.25, -109034.0, -103918.25),
    crs="EPSG:3763",  src_crs="EPSG:3763",
    width=1680, height=1125,
    style="rgb.json",  resample="bilinear",
)

open("cascais.png", "wb").write(png)
cog_path The COG to read: a local path or s3://bucket/key. Same idea as --cog on the CLI.
bbox The window to render, (minx, miny, maxx, maxy), in crs units.
crs The output projection, e.g. EPSG:3857.
src_crs The COG's own projection, e.g. EPSG:32629. Optional, but be careful: the fallback is EPSG:3763, a leftover default from the pilot fixture this wheel was built against, not a sensible default for your data. Pass it explicitly.
width, height Output size in pixels.
style Path to a style.json: rgb (band passthrough) or pseudocolor (a value ramp). Same file format as --style on the CLI.
resample "nearest" or "bilinear". Optional, defaults to bilinear.
-> returns bytes: the PNG file, in memory. Nothing touches disk unless you write it yourself.

Measured via the CLI, which runs the identical render() call under the hood: the image above, 1680×1125 pixels, bilinear-resampled, renders in about 0.15 s.

Source. A 25 cm RGB aerial orthophoto over Cascais, Portugal (EPSG:3763), the same fixture TerraServe's own correctness tests and pilot benchmark run against.

What v0.1.0 covers

Raster in, PNG out. That's the whole surface, today.

Not a wrapper

render_png() is the engine

The binding calls the same render() and PNG encoder the CLI and the live WMS server use underneath, so the output is byte-identical, not an approximation. The GIL is released for the whole render, so concurrent callers, pygeoapi's worker processes among them, actually run in parallel instead of queuing behind Python's interpreter lock.

Not shipped yet

Vector, labels, NDVI

Today's wheel only reads a COG and draws it as RGB or a pseudocolor ramp. Styled vector layers (GeoPackage / FlatGeoBuf + SLD) and text labels are designed as the next release, not built. Band-math expressions like the live NDVI demo already run in the Rust engine, they're just not wired into this function yet.

Drop into pygeoapi

TerraServeProvider, one YAML block

A type: map provider referenced by its dotted Python path, no pygeoapi core change needed, the same pattern as the mapscript provider it can replace:

# pygeoapi config.yml: a dotted Python path, like the mapscript provider it can replace
providers:
  - type: map
    name: terraserve.pygeoapi.TerraServeProvider
    data: /data/cascais.cog.tif
    options:
      src_crs: EPSG:3763
      style: /data/rgb.json