Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

earth and related environmental sciences

Sentinel-2 L1C MSI Zarr Product Exploration

Explore how to open, visualise and plot Sentinel-2 L1C MSI EOPF Zarr format

Authors
Affiliations
Eurac Research
Eurac Research
ESA EOPF Zarr Logo

🚀 Launch in JupyterHub

Run this notebook interactively with all dependencies pre-installed

Introduction

This notebook demonstrates how to open, explore, and visualize Sentinel-2 Level 1C MSI products stored in EOPF Zarr format, including plotting RGB images and analyzing geolocation data.

Import modules

ERROR 1: PROJ: proj_create_from_database: Open of /home/mclaus@eurac.edu/micromamba/envs/eopf-zarr3/share/proj failed

Open the product

/home/mclaus@eurac.edu/micromamba/envs/eopf-zarr3/lib/python3.11/site-packages/xarray_sentinel/esa_safe.py:7: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
  import pkg_resources
/tmp/ipykernel_3440119/938497852.py:2: FutureWarning: In a future version, xarray will not decode the variable 'step' into a timedelta64 dtype based on the presence of a timedelta-like 'units' attribute by default. Instead it will rely on the presence of a timedelta64 'dtype' attribute, which is now xarray's default way of encoding timedelta64 values.
To continue decoding into a timedelta64 dtype, either set `decode_timedelta=True` when opening this dataset, or add the attribute `dtype='timedelta64[ns]'` to this variable on disk.
To opt-in to future behavior, set `decode_timedelta=False`.
  dt = xr.open_datatree(remote_product_path, engine="zarr", chunks={})
/tmp/ipykernel_3440119/938497852.py:2: FutureWarning: In a future version, xarray will not decode the variable 'step' into a timedelta64 dtype based on the presence of a timedelta-like 'units' attribute by default. Instead it will rely on the presence of a timedelta64 'dtype' attribute, which is now xarray's default way of encoding timedelta64 values.
To continue decoding into a timedelta64 dtype, either set `decode_timedelta=True` when opening this dataset, or add the attribute `dtype='timedelta64[ns]'` to this variable on disk.
To opt-in to future behavior, set `decode_timedelta=False`.
  dt = xr.open_datatree(remote_product_path, engine="zarr", chunks={})

Overview of the product content

<Figure size 640x480 with 2 Axes>

The original data is stored in uint16, with scale and offset values to be applied manually.
Xarray applies the scale and offset automatically, providing reflectance values by default.
You can still get the original Digital Numbers (DNs) in uint16 by setting the mask_and_scale = False parameter when opening the product, e.g.:

dt = xr.open_datatree(remote_product_path, engine="zarr", chunks={}, mask_and_scale=False)

We can get some info about the oroginal encoding with the following code:

{'chunks': (305, 305), 'preferred_chunks': {'y': 305, 'x': 305}, 'compressor': Blosc(cname='zstd', clevel=3, shuffle=BITSHUFFLE, blocksize=0), 'filters': None, '_FillValue': 0, 'scale_factor': 0.0001, 'add_offset': -0.1, 'dtype': dtype('uint16')}

If we are interested in some band statistics, they can easily be computed using the xarray methods.
Remember that calling .compute() triggers the data download and processing of the result.

(1.6631, 0.0746, 0.27504428200304576)

Plot a RGB image

We can compose a new object for visualization, taking only the red (b04), green (b03) and blue (b02) bands.

Decimation is also applied, which reduces the data we consider for visualization, speeding up the process. Decimation is performed using the .isel xarray method.

.isel(x=slice(None, None, 10), y=slice(None, None, 10))

Finally, we rescale the data for a more meaningful visualization.

<Figure size 640x480 with 1 Axes>

Explore product geolocation

The Zarr product metadata contains information about the product geolocation we can explore in various ways.

Interactive map

The following code snippet shows an interactive map with the tile’s footprint

Loading...

Non-interactive map

The following code snippet shows the location of the tile on a global map.

<Figure size 1000x500 with 1 Axes>

Compute radiances

From: https://s2.pages.eopf.copernicus.eu/pdfs-adfs/MSI/L1/PDFS_S2_MSI_L1C.myst.html#product-overview

radiance=reflectance×cos⁡(radians(SunZenithAngle))×solarIrradiance×Uπ\text{radiance} = \text{reflectance} \times \cos\left(\text{radians}(\text{SunZenithAngle})\right) \times \text{solarIrradiance} \times \frac{U}{\pi} $ ]

1.03411047670495

Be carefull, Sun Zenith Angle is expressed on the angles grid (5km), it needs to be reprojected on the 10m grid for computing radiances cosinus is applied now because we can not interpolate angles using a linear interpolation (discontinuity at 0°).
On the other hand, cosines can be interpolated:

Loading...
Loading...

Interpolate sza on the angles grid to the 10m grid, using the xarray .interp_like() method, which interpolates our input data to match the given target.

Loading...

For simplicity, radiance computation assume that reflectances equal numerical counts

Loading...

Visualization

Visualize Dask computational graph to compute radiance values. For more info, see the Dask docs: https://docs.dask.org/en/stable/graphviz.html#visualize-task-graphs

<IPython.core.display.Image object>

Visualize the resulting radiance values

<Figure size 640x480 with 2 Axes>