Skip to content

Sentinel Hub — Statistical & Batch guide#

This page covers the tabular plane (Statistical) and the scale-out planes (Async, Batch, Batch-Statistical). For the synchronous raster path see Usage.

Statistical → tabular zonal stats#

api="statistical" + geometry= computes zonal statistics over a polygon (or every feature of a FeatureCollection) and writes a tidy CSV. Use a stats recipe (or a custom evalscript with a dataMask band).

from earthlens import EarthLens

polygon = {
    "type": "Polygon",
    "coordinates": [[[14.24, 40.80], [14.27, 40.80],
                     [14.27, 40.83], [14.24, 40.83], [14.24, 40.80]]],
}

el = EarthLens(
    data_source="sentinel-hub",
    start="2020-06-01", end="2020-06-30",
    variables={"sentinel-2-l2a-ndvi-stats": []},
    lat_lim=[40.80, 40.83], lon_lim=[14.24, 14.27],
    path="out", resolution=20,
    api="statistical",
    geometry=polygon,
)
tables = el.download()      # -> ['out/sentinel-2-l2a-ndvi-stats.csv']

The output CSV has one row per (interval × output × band) with columns feature_id, interval_from, interval_to, output, band, min, max, mean, stDev, sampleCount, noDataCount, and the p5 / p50 / p95 percentiles. A FeatureCollection produces one request per feature and stamps each feature's id (or properties.id, else its index) onto its rows.

Combine with aggregate= to get a time series — freq maps to the Statistical aggregation_interval:

from earthlens.aggregate import AggregationConfig
el.download(aggregate=AggregationConfig(freq="1MS", op="mean"))  # one row per month

Async vs local tiling vs Batch#

For a render larger than a single Process request (2500 px/side):

Approach api= Needs S3? Best for
Local tiling "tiling" No Medium/large AOIs you want as one local GeoTIFF
Async "async" Yes ≤ 10000 px/side, delivered to S3
Batch "batch" Yes Continental / global AOIs, server-side tiling

When api= is omitted, the backend picks tiling if no batch_output is set, otherwise async / batch by size.

Configuring S3 delivery (batch_output)#

The Async, Batch, and Batch-Statistical planes deliver server-side to your S3 bucket, which requires an IAM role the Sentinel Hub service can assume. See the Sentinel Hub docs on Batch Processing for the bucket policy / IAM-role setup.

batch_output = {
    "bucket": "s3://my-bucket/sh-output",
    "iam_role_arn": "arn:aws:iam::123456789012:role/sentinelhub",
    "grid_id": 2,            # a Sentinel Hub tiling grid id (Batch only)
}

el = EarthLens(
    data_source="sentinel-hub",
    start="2020-06-01", end="2020-06-30",
    variables={"sentinel-2-l2a-ndvi": []},
    lat_lim=[10.0, 40.0], lon_lim=[0.0, 30.0],   # large AOI
    path="out", resolution=10,
    api="batch",
    batch_output=batch_output,
)
uris = el.download()        # -> ['s3://my-bucket/sh-output']

Batch-Statistical (huge FeatureCollections)#

api="batch-statistical" computes zonal stats over a large FeatureCollection uploaded to S3 as a GeoPackage ("mean NDVI per 50 000 farms"), asynchronously:

batch_output = {
    "input_features": "s3://my-bucket/farms.gpkg",
    "bucket": "s3://my-bucket/stats-out",
    "iam_role_arn": "arn:aws:iam::123456789012:role/sentinelhub",
    "feature_ids": ["farm-1", "farm-2", "..."],
}

el = EarthLens(
    data_source="sentinel-hub",
    start="2020-06-01", end="2020-06-30",
    variables={"sentinel-2-l2a-ndvi-stats": []},
    lat_lim=[40.0, 41.0], lon_lim=[14.0, 15.0],
    path="out", resolution=10,
    api="batch-statistical",
    batch_output=batch_output,
)
tables = el.download()      # -> ['out/sentinel-2-l2a-ndvi-stats.csv']

Batch needs your own S3 bucket

The Batch and Batch-Statistical planes are not exercised in earthlens' live e2e tests because they require a user S3 bucket + IAM role. They are covered by faked-SDK integration tests and documented here; run them against your own bucket to validate end-to-end.