Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

gdalcubes.py gdalcubes.py logo

Python bindings for gdalcubes - On-demand Earth observation data cubes from GDAL image collections.

Installation

You need development files for GDAL, netCDF (C library), SQLite3, libcurl, with CMake ≥ 3.18 and a C++17 compiler.

On macOS:

brew install gdal netcdf

On Debian/Ubuntu:

apt install libgdal-dev libnetcdf-dev libsqlite3-dev libcurl4-openssl-dev

Then install the package:

pip install .

Pre-built wheels bundle GDAL and friends, so most users will not need any of the system packages above once those are published.

Optional extras

gdalcubes.py provides some integrations with the Python ecosystem. These integrations require extra Python dependencies. The table below lists these extra dependencies and when they are used:

Extra Installs For
gdalcubes[xarray] xarray, dask, netCDF4 reading results with to_xarray() and extract_geom()
gdalcubes[tif] rioxarray reading GeoTIFF collections
gdalcubes[stac] pystac-client, planetary-computer building collections from STAC

After selecting the extra dependencies you want, you can install them with the following command:

pip install 'gdalcubes[xarray,tif,stac]'

Getting started

This is the same walkthrough as the R package README, using a public Landsat 8 surface reflectance dataset over the Amazon (~2 GB). The full script is in examples/local-files.py. Below we take it one step at a time.

1. Configure gdalcubes

Set how many threads to use and turn on a progress bar for the long-running steps.

import os
import gdalcubes as gc

gc.set_threads(os.cpu_count() or 1)
gc.set_progress(True)

2. Download the example imagery

import urllib.request
import zipfile

urllib.request.urlretrieve(
    "https://hs-bochum.sciebo.de/s/8XcKAmPfPGp2CYh/download", "L8_Amazon.zip"
)

with zipfile.ZipFile("L8_Amazon.zip") as archive:
    archive.extractall("L8_Amazon")

3. Build an image collection

Scanning the images once extracts their spatial extent, acquisition time, and band layout into an image collection, a small SQLite database that references the files (no second copy of the data).

How metadata is read from file names is described by a collection format. Presets for several Sentinel, Landsat, and MODIS products ship with the package. You can list them with gc.list_collection_formats().

import glob

files = glob.glob("L8_Amazon/**/*.tif", recursive=True)
col = gc.create_image_collection(files, fmt="L8_SR", out="L8.db")

4. Define a data cube view

A data cube view defines the geometry of the target cube independently of the imagery: spatial reference system, extent, pixel size, and temporal resolution.

Here we ask for a coarse overview, the full collection extent at 1 km pixels, one cell per year, and let gdalcubes reproject, crop, resample, and aggregate on the fly.

view = gc.cube_view(
    srs="EPSG:3857",
    extent=col,
    dx=1000,
    dy=1000,
    dt="P1Y",
    aggregation="median",
    resampling="bilinear",
)

5. Create the cube

raster_cube() returns immediately: it is a lazy proxy, nothing is computed yet.

cube = gc.raster_cube(col, view)

print(cube)
# <gdalcubes.Cube 10 band(s) [AEROSOL, B01, ..., RADSAT_QA],
#  t: 2013-01-01..2019-12-31 (n=7), y: ... (n=559), x: ... (n=783), srs: EPSG:3857>

6. Compute a median RGB composite

Operations compose as plain functions and stay lazy. First pick the three bands, then reduce the time axis to a single median per band.

rgb_median = gc.reduce_time(
    gc.select_bands(cube, ["B02", "B03", "B04"]),
    ["median(B02)", "median(B03)", "median(B04)"],
)

7. Compute an NDVI composite

ndvi = gc.apply_pixel(
    gc.select_bands(cube, ["B04", "B05"]),
    "(B05-B04)/(B05+B04)",
    names=["NDVI"],
)

8. Export the result

Evaluation starts here. Write a single netCDF file:

gc.write_netcdf(ndvi, "ndvi.nc")

Or one (optionally cloud-optimized) GeoTIFF per time slice, which any GDAL-based tool (rasterio, QGIS, ...) reads directly:

gc.write_tif(ndvi, "ndvi_tifs", prefix="NDVI_", cog=True)

9. Use the result with xarray

Step 8 already evaluated the cube and wrote it to disk, so reading it back costs nothing more. The function to_xarray() reads those files rather than evaluating again, and returns an xarray.Dataset backed by dask:

ds = gc.to_xarray(ndvi)

ds["NDVI"].isel(time=0).mean().compute()

It does not matter which format you wrote, netCDF or the GeoTIFF collection both work.

For a cube that has not been written yet, you can use the to_xarray() giving a path. It evaluates once and reads from there:

ds = gc.to_xarray(rgb_median, "rgb_median.nc")

The resulting xarray.Dataset has one variable per band, dimensions time, y, x, and a crs coordinate, so rioxarray picks up the projection directly.

Load from disk

To load a cube, netCDF or GeoTIFF, as an xarray.Dataset you can use the specialized open functions from gdalcubes.py. For example, to load an existing cube saved as netCDF you can use:

ds = gc.open_xarray("ndvi.nc")

To read a GeoTIFF collection from a directory, you can use:

ds = gc.open_tif_collection("ndvi_tifs", prefix="NDVI_")

Once you load it, you can continue the operations using the xarray / dask infrastructure.

10. Extract time series of feature locations

extract_geom() evaluates a cube and extracts values from feature locations specified in a vector data source (any format GDAL reads, or a geopandas.GeoDataFrame). The function returns a pandas.DataFrame with one row per feature, time step, and pixel. The fid column is the position of the feature in the input, so the rows join back to their features directly:

df = gc.extract_geom(ndvi, "samples.gpkg")

Pass time_column="date" to extract each feature at its own time step only, as recorded in that attribute.

11. Read chunks in memory

A cube is evaluated one chunk at a time, in the chunk size it was created with. The chunks_iter() hands those chunks to Python as the core computes them, so a cube can be processed without writing it to disk first:

for chunk in gc.chunks_iter(ndvi):
    if chunk.status is gc.ChunkStatus.ERROR:
        raise RuntimeError(f"chunk {chunk.info.id} failed to read")

    if chunk.values is None:
        continue

    print(chunk.info.start, chunk.values.shape)

The chunk.values is a C-contiguous float64 array of shape (bands, t, y, x), with NaN for no data and row 0 at the northern edge. It shares the core's own buffer, which it keeps alive, so nothing is copied.

Next, the chunk.info says where the chunk is: start and shape in cube coordinates, left, right, bottom and top in the cube's spatial reference system, and t0 and t1 for its first and last time step. The info.slices gives the same position as slices, which is all it takes to put the chunks back together:

import numpy as np

values = np.full(tuple(ndvi.size), np.nan)

for chunk in gc.chunks_iter(ndvi):
    if chunk.values is not None:
        values[(slice(None), *chunk.info.slices)] = chunk.values

To drive the loop yourself, or to read several chunks at once, use chunks_count() and chunks_read().

Reading releases the GIL while the core works, so Python threads read in parallel, exactly as the core does internally when it evaluates a whole cube:

from concurrent.futures import ThreadPoolExecutor

with ThreadPoolExecutor(max_workers=8) as pool:
    chunks = pool.map(lambda i: gc.chunks_read(ndvi, i), range(gc.chunks_count(ndvi)))

This is not faster than writing the cube, it is just a different way to access the cube data.

Data cubes from STAC

Instead of indexing local files, an image collection can be built directly from STAC search results. No image file is opened, the metadata comes from the STAC items, so indexing thousands of remote COGs is instant. The full example script is in examples/stac-mpc.py.

Install the extra client libraries:

pip install gdalcubes[stac]

1. Search a catalog

Search with any STAC client (here pystac-client). URL signing happens at search time.

import planetary_computer
import pystac_client

# create catalog client
catalog = pystac_client.Client.open(
    "https://planetarycomputer.microsoft.com/api/stac/v1",
    modifier=planetary_computer.sign_inplace,
)

# search
items = catalog.search(
    collections=["sentinel-2-l2a"],
    bbox=[6.1, 50.2, 6.9, 50.8],
    datetime="2023-06/2023-08",
)

items = items.item_collection()

2. Build the collection from the items

from gdalcubes import stac

col = gc.stac_image_collection(
    items, source = "MPC", assets=["B04", "B08", "SCL"]
)

3. Build a cloud-masked cube

The mask argument drops pixels whose SCL class is one of the listed values (cloud, shadow, ...).

view = gc.cube_view(
    srs="EPSG:32632",
    extent=col,
    dx=100,
    dy=100,
    dt="P1M",
    aggregation="median",
    resampling="bilinear",
)

cube = gc.raster_cube(
    col, view, mask={
        "band": "SCL",
        "values": [3, 8, 9, 10]
    }
)

Adapters

Because providers implement STAC in different ways, provider behavior is factored into an explicit adapter, a frozen bundle of pure functions (asset selection and band naming, href → GDAL URI, datetime extraction, item filtering, GDAL configuration) with spec-compliant defaults.

Presets are plain constants (stac.PLANETARY_COMPUTER, stac.EARTH_SEARCH).

You can customize the Default adapter by subclassing or changing attributes:

adapter = stac.DEFAULT.replace(resolve_uri=lambda href: "/vsicurl/" + href)

Note that signed URLs (e.g. Planetary Computer SAS tokens) expire, so STAC-built collections are best treated as ephemeral, the default is a temporary database, rebuilt from a fresh search when needed.

Development

Using uv, from the repository root:

uv pip install -e '.[test,xarray,tif]'   # build and install (re-run after C++ changes)
uv run pytest                            # run the tests
uvx ruff format gdalcubes examples       # format
uvx ruff check gdalcubes                 # lint
uvx mypy gdalcubes                       # type-check

Metadata

Release files for gdalcubes 0.1.0.dev1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gdalcubes 0.1.0.dev1
File Size Uploaded Provenance Actions
gdalcubes-0.1.0.dev1.tar.gz 511.7 kB Without provenance Details for gdalcubes-0.1.0.dev1.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for gdalcubes 0.1.0.dev1
File Interpreter ABI Platform Actions
gdalcubes-0.1.0.dev1-cp314-cp314-pyemscripten_2026_0_wasm32.whl CPython 3.14 CPython 3.14 PyEmscripten 2026.0+ WebAssembly Details for gdalcubes-0.1.0.dev1-cp314-cp314-pyemscripten_2026_0_wasm32.whl
gdalcubes-0.1.0.dev1-cp313-cp313-macosx_26_0_arm64.whl CPython 3.13 CPython 3.13 macOS 26.0+ ARM64 Details for gdalcubes-0.1.0.dev1-cp313-cp313-macosx_26_0_arm64.whl

Total release size: 12.2 MB

Release files / gdalcubes-0.1.0.dev1.tar.gz

Download URL gdalcubes-0.1.0.dev1.tar.gz
Size 511.7 kB
Tags Source
SHA-256 checksum
How to use checksums
8b58a390cb9ec457bd1503bd4c813b301b41ac68ab83058e09dbf5e22cb23ef2
BLAKE2b-256 checksum
How to use checksums
a72cd4822af4691d993ebb2703927f588deb5f11b8c514200dbf084e7d863687
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / gdalcubes-0.1.0.dev1-cp314-cp314-pyemscripten_2026_0_wasm32.whl

Download URL gdalcubes-0.1.0.dev1-cp314-cp314-pyemscripten_2026_0_wasm32.whl
Size 11.1 MB
Tags CPython 3.14 PyEmscripten 2026.0+ WebAssembly
SHA-256 checksum
How to use checksums
c9c913b2c642a4b25d81efdbc09e377922786059b38fc647f5a4276771172bdf
BLAKE2b-256 checksum
How to use checksums
5d6b23a4f905446048f94e42cc5842fe6a277077b69c842b5afd73840b429555
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / gdalcubes-0.1.0.dev1-cp313-cp313-macosx_26_0_arm64.whl

Download URL gdalcubes-0.1.0.dev1-cp313-cp313-macosx_26_0_arm64.whl
Size 652.6 kB
Tags CPython 3.13 macOS 26.0+ ARM64
SHA-256 checksum
How to use checksums
779e37f0df52b117b5fba9bfc8567d427735ab0b29c9006be77cadd7c3c3013b
BLAKE2b-256 checksum
How to use checksums
cfe44650b7b686352da2e567b57d479d76fc9612f14d956e952684c68f1efa44
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for gdalcubes 0.1.0.dev1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page