Generate SentinelHub cubes
xcube Data Store Framework - Sentinel Hub¶
This notebook provides a walk-through demonstrating how to use xcube and the xcube Sentinel Hub (SH) plugin to read and explore data from the Sentinel Hub cloud API.
Please note:
In order to access data from Sentinel Hub, you need Sentinel Hub API credentials. They may be passed as store parameters (see further below) or exported from environment variables.
In case you have not exported them already, you may also set them by uncommenting the cell below and adjusting the content to your access credentials. However, we do not recommend this method!
Within DeepESDL there is the possiblity to apply for SentinelHub access - please contact the DeepESDL team :)
Brockmann Consult, 2025
This notebook runs with the python environment deepesdl-xcube-1.11.0, please checkout the documentation for help on changing the environment.
This notebook gives an introduction to the xcube-sentinelhub store and additionally shows how to access different data sets through the Sentinel Hub API via xcube:
- Access Sentinel-1 through xcube Sentinel hub Store
- Access Sentinel-2 through xcube Sentinel hub Store
- Access Landsat 8-L1C through xcube Sentinel hub Store
# import os
# os.environ["SH_CLIENT_ID"] = your_sh_client_id
# os.environ["SH_CLIENT_SECRET"] = your_sh_client_secret
# mandatory imports
from xcube.core.store import find_data_store_extensions
from xcube.core.store import get_data_store_params_schema
from xcube.core.store import new_data_store
# Utilities for notebook visualization
import shapely.geometry
import IPython.display
from IPython.display import JSON
import matplotlib.pyplot as plt
Configure matplotlib to display graphs inline directly in the notebook and set a sensible default figure size.
%matplotlib inline
plt.rcParams["figure.figsize"] = 16,12
Check whether the sentinelhub store is among the available stores, if not you need to choose an environment where xcube-sh is included.
JSON({e.name: e.metadata for e in find_data_store_extensions()})
<IPython.core.display.JSON object>
Usually we need more information to get the actual data store object. Which data store parameters are available for the sentinelhub store?
get_data_store_params_schema('sentinelhub')
<xcube.util.jsonschema.JsonObjectSchema at 0x7f947e3e0e50>
Please note the client_id and client_secret parameters mentioned at the beginning.
Provide mandatory parameters to instantiate the store class:
store = new_data_store('sentinelhub', num_retries=400)
Which datasets are provided? (the list may contain both gridded and vector datasets):
list(store.get_data_ids())
['S2L1C', 'S1GRD', 'S2L2A', 'DEM']
Get more info about a specific dataset. This includes a description of the possible open formats:
store.describe_data('S2L2A')
<xcube.core.store.descriptor.DatasetDescriptor at 0x7f947e30e900>
Which parameters must be passsed or are available to open the dataset?
store.get_open_data_params_schema('S2L2A')
<xcube.util.jsonschema.JsonObjectSchema at 0x7f947e35edd0>
There are 3 required parameters, so we need to provide them to open a dataset, one of them being bbox. Let's set a region covering Hamburg:
bbox=[9.7, 53.4, 10.2, 53.7]
Take a look at the bbox in order to make sure the area is correctly set:
IPython.display.GeoJSON(shapely.geometry.box(*bbox).__geo_interface__)
<IPython.display.GeoJSON object>
Now set the other parameters for opening the dataset from the store:
dataset = store.open_data('S2L2A',
variable_names=['B04'],
bbox=bbox,
spatial_res=0.00018,
time_range=('2020-08-10','2020-08-20'),
time_period='1D',
tile_size= [1024, 1024])
dataset
<xarray.Dataset> Size: 277MB
Dimensions: (time: 11, lat: 2048, lon: 3072, bnds: 2)
Coordinates:
* lat (lat) float64 16kB 53.77 53.77 53.77 53.77 ... 53.4 53.4 53.4
* lon (lon) float64 25kB 9.7 9.7 9.7 9.701 ... 10.25 10.25 10.25 10.25
* time (time) datetime64[ns] 88B 2020-08-10T12:00:00 ... 2020-08-20T1...
time_bnds (time, bnds) datetime64[ns] 176B dask.array<chunksize=(11, 2), meta=np.ndarray>
Dimensions without coordinates: bnds
Data variables:
B04 (time, lat, lon) float32 277MB dask.array<chunksize=(1, 1024, 1024), meta=np.ndarray>
Attributes: (12/13)
Conventions: CF-1.7
title: S2L2A Data Cube Subset
history: [{'program': 'xcube_sh.chunkstore.SentinelHubC...
date_created: 2025-10-01T07:48:21.075808
time_coverage_start: 2020-08-10T00:00:00+00:00
time_coverage_end: 2020-08-21T00:00:00+00:00
... ...
time_coverage_resolution: P1DT0H0M0S
geospatial_lon_min: 9.7
geospatial_lat_min: 53.4
geospatial_lon_max: 10.25296
geospatial_lat_max: 53.76864
processing_level: L2AFor further information, a description about the dataset and the bands may be found here: https://docs.sentinel-hub.com/api/latest/data/sentinel-2-l2a/ .
Plot one time stamp of the dataset for our requested variable:
dataset.B04.isel(time=2).plot.imshow(vmin=0, vmax=0.2, cmap='Greys_r')
<matplotlib.image.AxesImage at 0x7f948cf18440>
In case you wonder why we chose Hamburg - well, it is a lovely city!
Let's have a look at the different products, which are available via xcube-sentinelhub.¶
1. Sentinel-1 GRD¶
Setting of AOI bounding box
x1 = 10.00 # degree
y1 = 54.27 # degree
x2 = 11.00 # degree
y2 = 54.60 # degree
bbox = x1, y1, x2, y2
spatial_res = 0.00018 # = 20.038 meters in degree
Sentinel Hub currently supported Sentinel-1 GRD (Ground Range Detected) products: here
store = new_data_store('sentinelhub', num_retries=400)
list(store.get_data_ids())
['S2L1C', 'S1GRD', 'S2L2A', 'DEM']
store.describe_data('S1GRD')
<xcube.core.store.descriptor.DatasetDescriptor at 0x7f948cd9ad50>
cube = store.open_data(
'S1GRD',
variable_names=['VH'],
tile_size=[512, 512],
crs = "EPSG:4326",
spatial_res = spatial_res,
bbox=bbox,
time_range=['2019-06-14', '2019-07-31'],
time_period='2D'
)
cube
<xarray.Dataset> Size: 1GB
Dimensions: (time: 24, lat: 2048, lon: 5632, bnds: 2)
Coordinates:
* lat (lat) float64 16kB 54.64 54.64 54.64 54.64 ... 54.27 54.27 54.27
* lon (lon) float64 45kB 10.0 10.0 10.0 10.0 ... 11.01 11.01 11.01
* time (time) datetime64[ns] 192B 2019-06-15 2019-06-17 ... 2019-07-31
time_bnds (time, bnds) datetime64[ns] 384B dask.array<chunksize=(24, 2), meta=np.ndarray>
Dimensions without coordinates: bnds
Data variables:
VH (time, lat, lon) float32 1GB dask.array<chunksize=(1, 512, 512), meta=np.ndarray>
Attributes: (12/13)
Conventions: CF-1.7
title: S1GRD Data Cube Subset
history: [{'program': 'xcube_sh.chunkstore.SentinelHubC...
date_created: 2025-10-01T07:48:23.913504
time_coverage_start: 2019-06-14T00:00:00+00:00
time_coverage_end: 2019-08-01T00:00:00+00:00
... ...
time_coverage_resolution: P2DT0H0M0S
geospatial_lon_min: 10.0
geospatial_lat_min: 54.27
geospatial_lon_max: 11.01376
geospatial_lat_max: 54.63864
processing_level: L1Bcube.VH.isel(time=1).plot.imshow(cmap='Greys',vmax =0.08, figsize = [16,12])
<matplotlib.image.AxesImage at 0x7f948ccb4550>
2. Sentinel-2 L2A (SCL)¶
Sentinel Hub currently supported Sentinel-2 products: here
store.describe_data('S2L2A')
cube = store.open_data(
'S2L2A',
variable_names=['SCL'],
tile_size=[512, 512],
spatial_res = spatial_res,
bbox=bbox,
time_range=['2018-06-14', '2018-07-31'],
time_tolerance='30m'
)
cube
cube.SCL.isel(time=8, lat=slice(0,2000),lon=slice(0,2000)).plot.imshow(cmap='tab20c')
3. Landsat 8 L1C¶
store = new_data_store('sentinelhub', api_url='https://services-uswest2.sentinel-hub.com', num_retries=400)
list(store.get_data_ids())
store.describe_data('LOTL1')
cube = store.open_data(
'LOTL1',
variable_names=['B04'],
bbox=bbox,
spatial_res=spatial_res,
time_range=['2018-06-14', '2018-07-31'],
)
cube
cube.B04.isel(time=6, lat=slice(0,2000),lon=slice(0,2000)).plot.imshow(cmap='Greys', vmax=0.3)