Skip to main content

Efficient processing of cubic Earth-observation (EO) data.

Project description

A Python package for efficient processing of cubic Earth-observation (EO) data 🚀

PyPI License Ruff Tests


GitHub: https://github.com/andesdatacube/cubexpress/ 🌐

PyPI: https://pypi.org/project/cubexpress/ 🛠️


Overview

CubeXpress turns Google Earth Engine into a fast, scriptable source of analysis-ready imagery. Point it at a location, a list of locations, or a polygon, and it discovers the images, mosaics multi-tile scenes, scores them with your cloud metric, and downloads them — handling GEE's size limits, rate limits, and large-scale runs for you.

Everything is built around one idea: a location becomes a RasterTransform (an rt). Give the same functions one rt or a list of them, and CubeXpress picks the right engine automatically.

Key features

  • One API for one or many areasdiscover_images takes a single rt or a list; a list routes through a batched, server-side engine that is dramatically faster than querying point-by-point.
  • Adaptive concurrency — the discovery engine watches for GEE rate limits and raises or lowers its worker count on the fly (AIMD), so large runs stay fast without tripping quotas.
  • Crash-safe checkpoints — pass checkpoint="run.jsonl" and a 250k-tile run resumes exactly where it stopped if it is interrupted.
  • Your cloud score, not oursadd_metrics scores each image with a function you define (e.g. CloudScore+), plus an automatic valid-pixel coverage, computed correctly per-image even across mixed CRSs.
  • Date mosaicking.mosaic(by="date") fuses the multiple tiles of a scene into one image per date.
  • Automatic tiling on downloadexpress splits any request that exceeds GEE's size limit, downloads the tiles in parallel, and merges them back.
  • Polygon-aware clippingexpress_clip downloads only the tiles that touch your polygon (the rest become nodata, saving real download cost) and masks the result to the polygon's shape.

Installation

pip install cubexpress

You need a Google Earth Engine account. Run ee.Initialize(project="your-project-id") before using CubeXpress.


Quick start

A single location

import ee
import cubexpress

ee.Initialize(project="your-project-id")

# A 512x512 patch at 10 m, centered on a point (auto-projected to local UTM)
rt = cubexpress.point_to_rt(lon=-77.06, lat=-9.54, width=512, height=512, scale=10)

# Discover every Sentinel-2 image over it in a date range
table = cubexpress.discover_images(
    "COPERNICUS/S2_HARMONIZED", rt, "2023-01-01", "2023-06-01",
)
print(len(table), "images found")

# Download (RGB), tiling automatically if a scene is too large for GEE
cubexpress.express(table.select_bands("B4", "B3", "B2"), "s2_output")

Many locations at once

Give discover_images a list of rts and it uses the batched, adaptive engine — far faster than looping, and resumable.

coords = [(-77.06, -9.54), (-72.54, -13.16), (-70.05, -12.93)]
rts = [
    cubexpress.point_to_rt(lon=lo, lat=la, width=256, height=256, scale=10)
    for lo, la in coords
]

table = cubexpress.discover_images(
    "COPERNICUS/S2_HARMONIZED", rts, "2023-01-01", "2023-06-01",
    nworkers=8,                  # adapts to GEE rate limits automatically
    checkpoint="run.jsonl",      # resume if the run is interrupted
)

Mosaic, score by clouds, and keep the clear scenes

add_metrics runs your scoring function on GEE. Here a CloudScore+ metric returns the percentage of clear pixels over the ROI.

def cloud_score(image, geometry, source_ids=None):
    csplus = ee.ImageCollection("GOOGLE/CLOUD_SCORE_PLUS/V1/S2_HARMONIZED")
    if source_ids is not None:                    # mosaic: rebuild from sources
        cs = (csplus.filter(ee.Filter.inList("system:index", ee.List(source_ids)))
              .select("cs_cdf").mosaic())
    else:                                         # single tile: by its index
        cs = csplus.filter(ee.Filter.eq("system:index", image.get("system:index"))).first()
        cs = ee.Image(ee.Algorithms.If(cs, cs, ee.Image.constant(0).rename("cs_cdf"))).select("cs_cdf")
    frac = cs.gte(0.65).reduceRegion(
        reducer=ee.Reducer.mean(), geometry=geometry, scale=10, maxPixels=int(1e9)
    ).get("cs_cdf")
    return ee.Number(ee.Algorithms.If(frac, frac, 0)).multiply(100)

mosaics = table.mosaic(by="date")                 # one image per date
scored = cubexpress.add_metrics(mosaics, score_fn=cloud_score)
clear = scored[scored.df.score > 70]              # keep mostly-clear scenes
cubexpress.express(clear.select_bands("B4", "B3", "B2"), "s2_clear")

A polygon (download only what you need)

Discover over the polygon's bounding box, then express_clip downloads only the tiles that intersect the polygon (the rest become nodata) and masks the merged result to the polygon's shape.

import json
from shapely.geometry import shape
from shapely.ops import transform as shp_transform
from pyproj import Transformer
from cubexpress.download.clip_runner import express_clip

geom = shape(json.load(open("district.geojson"))["features"][0]["geometry"])

# One rt = the polygon's bbox, reprojected to its local UTM
rt = cubexpress.polygon_to_rt(geom, scale=10, crs="EPSG:4326")

table = cubexpress.discover_images(
    "COPERNICUS/S2_HARMONIZED", rt, "2023-06-01", "2023-09-01",
)
row = list(table.select_bands("B4", "B3", "B2"))[0]

# Reproject the polygon to the rt's CRS, then download clipped to its shape
to_utm = Transformer.from_crs("EPSG:4326", rt.crs, always_xy=True)
poly_utm = shp_transform(to_utm.transform, geom)

express_clip(row, poly_utm, "district_output")    # outside tiles = nodata; masked to shape

Note: discovery and metrics operate on the polygon's bounding box; the clipping to the irregular shape happens only at download time, in express_clip.


How it works

  • Discovery maps your rts to image footprints server-side in batches, so one request resolves many locations at once.
  • Adaptive concurrency (AIMD) grows the worker pool while GEE is happy and halves it on a rate-limit signal, finding the safe throughput automatically.
  • Retiling reacts to GEE's size error: it learns the bytes-per-pixel from the rejection, splits the request into tiles that fit, downloads them in parallel, and merges them back into one GeoTIFF.
  • Polygon clipping grids the bbox, keeps only tiles intersecting the polygon, fills the rest with nodata (no download), merges, and masks to the shape.

License

This project is licensed under the MIT License.


Built with 🌎 and ❤️ by the CubeXpress team

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cubexpress-0.2.1.tar.gz (61.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cubexpress-0.2.1-py3-none-any.whl (75.8 kB view details)

Uploaded Python 3

File details

Details for the file cubexpress-0.2.1.tar.gz.

File metadata

  • Download URL: cubexpress-0.2.1.tar.gz
  • Upload date:
  • Size: 61.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.12.13 Windows/11

File hashes

Hashes for cubexpress-0.2.1.tar.gz
Algorithm Hash digest
SHA256 e16ea980edf7e598c93a25aad885c8a34b532f005abc7bfbff776ba4ea391f34
MD5 f79bb471c1e2a4bfa07b648dd5c8c1c2
BLAKE2b-256 e15e6b04ba65c3880460e03348a529c7b6793106eec395ea5a22d15587063042

See more details on using hashes here.

File details

Details for the file cubexpress-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: cubexpress-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 75.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.12.13 Windows/11

File hashes

Hashes for cubexpress-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3ada3749c5c5e1196fe69700430cb7ddd98950de75ce431308d0b60ba3ef7aa8
MD5 15e010bdeda3a352265f66d22d9b59d9
BLAKE2b-256 760de7a0ad41fb492e4c813e794b659bb97f5b4f2d41f483fa719d98163c7b99

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page