Access Sentinel-1 in Analysis Mode¶
xarray-eopf is a Python package that extends xarray with a custom backend called "eopf-zarr". This backend enables seamless access to ESA EOPF data products stored in the Zarr format, presenting them as analysis-ready data structures.
In this notebook, we demonstrate how to use the xarray-eopf backend to access Sentinel-1 EOPF Zarr products in analysis mode. All data access is lazy, meaning that data is only loaded when required—for example, during plotting or when writing to storage.
For a general introduction to the xarray EOPF backend, see the introduction notebook. For an example of the native mode, see the Sentinel-1 native mode notebook.
- 🐙 GitHub: EOPF Sample Service – xarray-eopf
- ❗ Issue Tracker: Submit or view issues
- 📘 Documentation: xarray-eopf Docs
(Main_Features_XEOPF_SEN1_ANALYSIS)=
Main Features of the Analysis Mode for Sentinel-1¶
The Sentinel-1 analysis workflow supports:
Sentinel-1 GRD¶
Sentinel-1 GRD products are provided in radar geometry with the coordinates
azimuth_time and ground_range. To generate an analysis-ready dataset, the
following processing steps are performed:
- Radiometric Calibration: Raw pixel values (DN) are converted to calibrated backscatter using the
beta_noughtcalibration lookup table (LUT). - Geometric Terrain Correction (GTC): The data is geocoded by inverse geocoding using satellite orbit information and a Digital Elevation Model (DEM). This involves solving the zero-Doppler equation to map radar coordinates onto a georeferenced grid.
- Radiometric Terrain Correction (RTC): Optionally compensates for terrain-induced radiometric effects such as foreshortening, layover, and slope-dependent brightness variations.
Reference
📖 D. Small, Flattening Gamma: Radiometric Terrain Correction for SAR Imagery
Sentinel-1 SLC¶
Sentinel-1 SLC products are provided as complex-valued bursts in radar geometry
with the coordinates azimuth_time and slant_range_time. To generate an
analysis-ready dataset, the following processing steps are performed:
- Radiometric Calibration: Each burst is calibrated using the
beta_noughtcalibration LUT. The complex-valued SLC data is converted to amplitude (abs(slc)). - TOPSAR Debursting: Individual bursts are aligned and merged into a continuous image. Burst overlaps are identified from the product metadata and removed.
- Geometric Terrain Correction (GTC): Identical to the GRD workflow.
- Radiometric Terrain Correction (RTC): Identical to the GRD workflow.
Sentinel-1 OCN¶
Sentinel-1 OCN products are provided on a geographic swath grid, where each pixel is defined by a latitude/longitude pair, forming a two-dimensional curvilinear (irregular) grid.
Analysis mode applies the xcube-resampling rectification algorithm to transform this irregular grid into a regular (rectilinear) grid with one-dimensional latitude and longitude coordinates.
Analysis Mode Features¶
The following features are available for all Sentinel-1 product types:
- Default operation mode for the
"eopf-zarr"backend - Lazy processing using Dask
- Flexible variable selection using explicit variable names or regular expressions
- Configurable interpolation via
interp_methods(nearest,bilinear)
Features specific to GRD and SLC¶
Flexible output grid definition
- Supply a DEM directly via the
demparameter (xarray.DataArray). - Or define the target grid using
crs,bbox, andresolution. - If no DEM is supplied, the CopDEM COG (30 m) is retrieved automatically via the CDSE STAC API (requires CDSE S3 credentials).
- Supply a DEM directly via the
Optional Radiometric Terrain Correction (RTC) via
apply_rtc=False.Configurable radar footprint scaling through
footprint_scale_factor, which controls how radar pixels contribute to the output grid.- GRD default:
(3.0, 3.0)to account for the resolution difference (e.g. ~10 m GRD vs. ~30 m DEM). - SLC default:
(3.0, 15.0)to account for the resolution difference (e.g. ~10 m × ~2 m SLC vs. ~30 m DEM).
- GRD default:
Features specific to OCN¶
- Rectification from a 2D curvilinear grid to a rectilinear grid
- Spatial subsetting via
bbox - Reprojection to arbitrary CRSs via
crs(default: WGS84 / EPSG:4326)
For a complete description of the available opening parameters, see the Analysis Mode documentation and the Sentinel-1 Analysis Mode guide.
Import Modules¶
The eopf-zarr backend is registered as an xarray plugin. Import xarray as usual, plus helper libraries used in this notebook.
import os
import matplotlib.pyplot as plt
import pystac_client
import xarray as xr
from xcube_resampling.utils import reproject_bbox
Open a Sentinel-1 Level-1 GRD Product in Analysis Mode¶
We begin with an example that accesses a Sentinel-1 Level-1 GRD product in analysis mode.
Find a Sentinel-1 GRD Zarr Sample via STAC¶
To obtain a product URL, you can use the STAC Browser to search for a Sentinel-1 GRD tile.
%%time
bbox = [20.6, 37.6, 21., 38.]
catalog = pystac_client.Client.open("https://stac.core.eopf.eodc.eu")
items = list(
catalog.search(
collections=["sentinel-1-l1-grd"],
bbox=bbox,
datetime=["2026-05-04", "2026-05-04"],
).items()
)
items
CPU times: user 18.6 ms, sys: 208 μs, total: 18.8 ms Wall time: 250 ms
[<Item id=S1A_IW_GRDH_1SDV_20260504T164004_20260504T164028_064370_081B0C_B8F0>]
item = items[0]
item
Open Sentinel-1 GRD with default parameters¶
We can now open the Sentinel-1 product in analysis mode. Since this is the default, the op_mode parameter does not need to be specified.
The following cell returns a lazy dataset. For demonstration purposes, we restrict the data to a small spatial subset by specifying a bounding box.
Before proceeding, CDSE S3 credentials must be configured to enable access to the CopDEM (30 m) dataset from CDSE. Instructions for generating credentials are available here.
os.environ.update(
{
"AWS_ACCESS_KEY_ID": "xxx",
"AWS_SECRET_ACCESS_KEY": "xxx",
}
)
%%time
ds = xr.open_dataset(
item.assets["product"].href,
engine="eopf-zarr",
bbox=bbox,
chunks={}
)
ds
CPU times: user 6 s, sys: 1.39 s, total: 7.39 s Wall time: 16.5 s
<xarray.Dataset> Size: 17MB
Dimensions: (lat: 1441, lon: 1441)
Coordinates:
* lat (lat) float64 12kB 38.0 38.0 38.0 38.0 ... 37.6 37.6 37.6 37.6
* lon (lon) float64 12kB 20.6 20.6 20.6 20.6 ... 21.0 21.0 21.0 21.0
spatial_ref int64 8B ...
Data variables:
gamma0_vv (lat, lon) float32 8MB dask.array<chunksize=(1441, 1441), meta=np.ndarray>
gamma0_vh (lat, lon) float32 8MB dask.array<chunksize=(1441, 1441), meta=np.ndarray>As an example, we plot the calibrated VV polarization band. Plotting triggers lazy loading and computation.
%%time
ds.gamma0_vv.plot(robust=True)
/home/konstantin/micromamba/envs/xarray-eopf/lib/python3.13/site-packages/dask/_task_spec.py:768: RuntimeWarning: divide by zero encountered in divide return self.func(*new_argspec)
CPU times: user 2.62 s, sys: 802 ms, total: 3.42 s Wall time: 3.66 s
<matplotlib.collections.QuadMesh at 0x7854ec1392b0>
Open Sentinel-1 GRD in UTM grid¶
In the following we show different ways to open a Sentinel-1 GRD product in analysis mode.
We can define the target gridmapping in UTM using the resolution, crs,and bbox parameters.
%%time
crs_utm = "EPSG:32631"
bbox_utm = reproject_bbox(bbox, "EPSG:4326", "EPSG:32631")
ds = xr.open_dataset(
item.assets["product"].href,
engine="eopf-zarr",
bbox=bbox_utm,
crs=crs_utm,
resolution=30, # meters
chunks={}
)
ds
CPU times: user 8.56 s, sys: 3.01 s, total: 11.6 s Wall time: 20.1 s
<xarray.Dataset> Size: 21MB
Dimensions: (x: 1482, y: 1730)
Coordinates:
* x (x) float64 12kB 2.051e+06 2.051e+06 ... 2.095e+06 2.095e+06
* y (y) float64 14kB 4.362e+06 4.362e+06 ... 4.31e+06 4.31e+06
spatial_ref int64 8B ...
Data variables:
gamma0_vv (y, x) float32 10MB dask.array<chunksize=(1730, 1482), meta=np.ndarray>
gamma0_vh (y, x) float32 10MB dask.array<chunksize=(1730, 1482), meta=np.ndarray>We can again visualize one polarization channel (for example vv).
%%time
ds.gamma0_vv.plot(robust=True)
CPU times: user 3.46 s, sys: 1.12 s, total: 4.58 s Wall time: 4.24 s
<matplotlib.collections.QuadMesh at 0x7854d45c3890>
Turn off radiometric terrain correction (RTC)¶
In the next example we turn off the RTC and select on the vv band.
%%time
ds = xr.open_dataset(
item.assets["product"].href,
engine="eopf-zarr",
bbox=bbox,
variables=["vv"],
apply_rtc=False,
chunks={}
)
ds
CPU times: user 3.12 s, sys: 1.65 s, total: 4.77 s Wall time: 9.98 s
<xarray.Dataset> Size: 8MB
Dimensions: (lat: 1441, lon: 1441)
Coordinates:
* lat (lat) float64 12kB 38.0 38.0 38.0 38.0 ... 37.6 37.6 37.6 37.6
* lon (lon) float64 12kB 20.6 20.6 20.6 20.6 ... 21.0 21.0 21.0 21.0
spatial_ref int64 8B ...
Data variables:
beta0_vv (lat, lon) float32 8MB dask.array<chunksize=(1441, 1441), meta=np.ndarray>In the following plot you can clearly see the structure of the mountainous terrain, since the backscatter variation has not been corrected for the terrain.
%%time
ds.beta0_vv.plot(robust=True)
CPU times: user 1.2 s, sys: 233 ms, total: 1.43 s Wall time: 3.51 s
<matplotlib.collections.QuadMesh at 0x7854cc5ee850>
Interpolation Method: Nearest Neighbor¶
In the next example, we set the interpolation method to nearest, which is applied during both Geometric Terrain Correction (GTC) and Radiometric Terrain Correction (RTC).
This approach improves computational performance but reduces accuracy compared to bilinear interpolation, as it avoids spatial smoothing and instead assigns the value of the closest pixel.
%%time
ds = xr.open_dataset(
item.assets["product"].href,
engine="eopf-zarr",
bbox=bbox,
interp_methods="nearest",
chunks={}
)
ds
CPU times: user 4.14 s, sys: 1.17 s, total: 5.31 s Wall time: 11 s
<xarray.Dataset> Size: 17MB
Dimensions: (lat: 1441, lon: 1441)
Coordinates:
* lat (lat) float64 12kB 38.0 38.0 38.0 38.0 ... 37.6 37.6 37.6 37.6
* lon (lon) float64 12kB 20.6 20.6 20.6 20.6 ... 21.0 21.0 21.0 21.0
spatial_ref int64 8B ...
Data variables:
gamma0_vv (lat, lon) float32 8MB dask.array<chunksize=(1441, 1441), meta=np.ndarray>
gamma0_vh (lat, lon) float32 8MB dask.array<chunksize=(1441, 1441), meta=np.ndarray>%%time
ds.gamma0_vv.plot(robust=True)
CPU times: user 1.31 s, sys: 290 ms, total: 1.6 s Wall time: 3.39 s
<matplotlib.collections.QuadMesh at 0x7854cc647d90>
Open a Sentinel-1 Level-1 SLC Product in Analysis Mode¶
Next, we show an example that accesses a Sentinel-1 Level-2 SLC product in analysis mode.
Find a Sentinel-1 SLC Zarr Sample via STAC¶
To obtain a product URL, you can use the STAC Browser to search for a Sentinel-1 SLC tile.
%%time
catalog = pystac_client.Client.open("https://stac.core.eopf.eodc.eu")
items = list(
catalog.search(
collections=["sentinel-1-l1-slc"],
bbox=bbox,
datetime=["2026-05-04", "2026-05-04"],
).items()
)
items
CPU times: user 14.8 ms, sys: 1.88 ms, total: 16.7 ms Wall time: 2.75 s
[<Item id=S1A_IW_SLC__1SDV_20260504T164002_20260504T164029_064370_081B0C_287A>]
item = items[0]
%%time
ds = xr.open_dataset(
item.assets["product"].href,
engine="eopf-zarr",
bbox=bbox,
chunks={}
)
ds
CPU times: user 12 s, sys: 1.3 s, total: 13.3 s Wall time: 1min 26s
<xarray.Dataset> Size: 17MB
Dimensions: (lat: 1441, lon: 1441)
Coordinates:
* lat (lat) float64 12kB 38.0 38.0 38.0 38.0 ... 37.6 37.6 37.6 37.6
* lon (lon) float64 12kB 20.6 20.6 20.6 20.6 ... 21.0 21.0 21.0 21.0
spatial_ref int64 8B ...
Data variables:
gamma0_vh (lat, lon) float32 8MB dask.array<chunksize=(1441, 1441), meta=np.ndarray>
gamma0_vv (lat, lon) float32 8MB dask.array<chunksize=(1441, 1441), meta=np.ndarray>As an example, we plot the calibrated VV polarization band. Plotting triggers lazy loading and computation.
%%time
ds.gamma0_vv.plot(robust=True)
CPU times: user 4.55 s, sys: 925 ms, total: 5.48 s Wall time: 13.6 s
<matplotlib.collections.QuadMesh at 0x7853fc1d0410>
The same functionalities available for GRD products are also supported for SLC products, including the selection of different CRSs, bounding boxes, spatial resolutions, and interpolation methods.
Open a Sentinel-1 Level-2 OCN Product in Analysis Mode¶
Next, we show an example that accesses a Sentinel-1 Level-2 OCN product in analysis mode.
Find a Sentinel-1 OCN Zarr Sample via STAC¶
To obtain a product URL, you can use the STAC Browser to search for a Sentinel-1 OCN tile.
%%time
bbox = [3.0, 39.4, 3.2, 39.6]
catalog = pystac_client.Client.open("https://stac.core.eopf.eodc.eu")
items = list(
catalog.search(
collections=["sentinel-1-l2-ocn"],
bbox=bbox,
datetime=["2025-07-01", "2025-08-01"],
).items()
)
items
CPU times: user 12.9 ms, sys: 1.2 ms, total: 14.1 ms Wall time: 207 ms
[<Item id=S1A_IW_OCN__2SDV_20250730T055348_20250730T055413_060309_077EBE_1CA3>]
item = items[0]
%%time
ds = xr.open_dataset(
item.assets["product"].href,
engine="eopf-zarr",
chunks={},
)
ds
CPU times: user 156 ms, sys: 5.71 ms, total: 161 ms Wall time: 2.99 s
<xarray.Dataset> Size: 874kB
Dimensions: (lon: 293, lat: 212)
Coordinates:
* lon (lon) float64 2kB 1.09 1.101 1.113 ... 4.432 4.444
* lat (lat) float64 2kB 40.63 40.62 ... 38.74 38.73
spatial_ref int64 8B ...
Data variables:
wind_direction (lat, lon) float32 248kB dask.array<chunksize=(212, 293), meta=np.ndarray>
wind_speed (lat, lon) float32 248kB dask.array<chunksize=(212, 293), meta=np.ndarray>
inversion_quality (lat, lon) uint8 62kB dask.array<chunksize=(212, 293), meta=np.ndarray>
percentage_bright_points (lat, lon) float32 248kB dask.array<chunksize=(212, 293), meta=np.ndarray>
wind_quality (lat, lon) uint8 62kB dask.array<chunksize=(212, 293), meta=np.ndarray>
Attributes: (2)%%time
fig, ax = plt.subplots(1, 2, figsize=(12, 5))
ds.wind_speed.plot(ax=ax[0], cmap="viridis")
ds.wind_direction.plot(ax=ax[1], cmap="twilight", vmin=0, vmax=360)
plt.tight_layout()
CPU times: user 705 ms, sys: 52.8 ms, total: 757 ms Wall time: 1.18 s
We can also request the data in a different CRS, and define the target grid mapping by using the parameters resolution, and bbox.
%%time
ds = xr.open_dataset(
item.assets["product"].href,
engine="eopf-zarr",
crs="EPSG:32631",
resolution=1_000, # meters
bbox=[4e5, 4.3e6, 6e5, 4.5e6],
variables=["wind_direction", "wind_speed"],
chunks={},
)
ds
CPU times: user 283 ms, sys: 3.23 ms, total: 286 ms Wall time: 1.32 s
<xarray.Dataset> Size: 323kB
Dimensions: (x: 200, y: 200)
Coordinates:
* x (x) float64 2kB 4.005e+05 4.015e+05 ... 5.985e+05 5.995e+05
* y (y) float64 2kB 4.5e+06 4.498e+06 ... 4.302e+06 4.3e+06
spatial_ref int64 8B ...
Data variables:
wind_direction (y, x) float32 160kB dask.array<chunksize=(200, 200), meta=np.ndarray>
wind_speed (y, x) float32 160kB dask.array<chunksize=(200, 200), meta=np.ndarray>
Attributes: (2)%%time
fig, ax = plt.subplots(1, 2, figsize=(12, 5))
ds.wind_speed.plot(ax=ax[0], cmap="viridis")
ds.wind_direction.plot(ax=ax[1], cmap="twilight", vmin=0, vmax=360)
plt.tight_layout()
CPU times: user 189 ms, sys: 16.2 ms, total: 205 ms Wall time: 278 ms
Conclusion¶
This notebook demonstrates how to access Sentinel-1 EOPF Zarr samples in analysis mode using the xarray-eopf plugin.
The main characteristics of analysis mode are:
- Analysis mode is the default
op_modeand therefore does not need to be specified explicitly. - Data is accessed lazily and processed on demand for a user-defined CRS, bounding box, and spatial resolution, enabling efficient subsetting, reprojection, and resampling.
Sentinel-1 Level-1 GRD¶
The analysis workflow for Sentinel-1 Level-1 GRD includes:
- Radiometric calibration using the product LUTs.
- Geometric Terrain Correction (GTC) through inverse geocoding using orbit information and a DEM.
- Optional Radiometric Terrain Correction (RTC) to compensate for terrain-induced radiometric distortions.
- A DEM can either:
- be provided explicitly by the user, or
- be retrieved automatically from the CDSE STAC API and resampled to user-defined CRS, bounding box, and spatial resolution.
- If a DEM is provided, its grid definition determines the output grid.
Sentinel-1 Level-1 SLC¶
The analysis workflow for Sentinel-1 Level-1 SLC includes:
- Radiometric calibration of each burst using the product LUTs.
- TOPSAR debursting, in which individual bursts are merged into a continuous image.
- Geometric Terrain Correction (GTC) through inverse geocoding using orbit information and a DEM.
- Optional Radiometric Terrain Correction (RTC) to compensate for terrain-induced radiometric distortions.
- A DEM can either:
- be provided explicitly by the user, or
- be retrieved automatically from the CDSE STAC API and resampled to user-defined CRS, bounding box, and spatial resolution.
- If a DEM is provided, its grid definition determines the output grid.
- Apart from the additional TOPSAR debursting step, the same configuration options as for GRD are available.
Sentinel-1 Level-2 OCN¶
The analysis workflow for Sentinel-1 Level-2 OCN includes:
- Rectification from the native irregular latitude/longitude swath grid to a regular target grid.
- User-defined output grids via the
crs,bbox, andresolutionparameters. - Automatic inclusion of quality flag variables by default.
- Optional variable selection using the
variablesparameter.
Note: This notebook focuses exclusively on analysis mode. To learn more about native mode, see the Sentinel-1 native mode notebook.