import os
import xarray as xrDiscover EOPF Zarr - Sentinel-3 SLSTR Level-2 LST
Introduction
This tutorial introduces you to the structure of an EOPF Zarr product sample for Sentinel-3 SLSTR Level-2 Land Surface Temperature data. We will demonstrate how to access and open a .zarr product sample with xarray, how to visualise the zarr encoding structure, explore embedded information, and retrieve relevant metadata for further processing.
What we will learn
- ⚙️ How to open a
.zarrfile usingxarray? - 🛰️ The general structure of a Sentinel-3 SLSTR Level-2 LST item
- 🔎 How to access metadata that describes the
.zarrencoding?
Prerequisites
This tutorial uses a re-processed sample dataset from the EOPF Sentinel Zarr Samples Service STAC API that is available for direct access here.
The selected .zarr product is a Sentinel-3 SLSTR Level-2 LST tile from the 29th of May 2025:
- File name:
S3B_SL_2_LST____20250529T093001_20250529T093301_20250623T143718_0179_107_093_2340_ESA_O_NT_004.zarr.
Import libraries
Helper functions
print_gen_structure
This function helps us to retrieve and visualise the names for each of the stored groups inside a .zarr product. As an output, it will print a general overview of elements inside the zarr.
def print_gen_structure(node, indent=""):
print(f"{indent}{node.name}") #allows us access each node
for child_name, child_node in node.children.items(): #loops inside the selected nodes to extract naming
print_gen_structure(child_node, indent + " ") # prints the name of the selected nodesOpen the zarr Store
In a first step, we use the open_datatree() function from the xarray library to open a .zarr store as a DataTree.
Inside, we need to define the following key word arguments:
filename_or_obj: path leading to a.zarrstoreengine:zarr, as it is the encoding structure of the file.chunks: loads the data withDaskusing the engine’s preferred chunk size. If{}the loaded chunks are identical to the format’s original chunk size.
The final print of the DataTree object is commented out, as the display can be quite extensive, showing the entire content within the .zarr. An alternative is to apply a helper function that only displays the higher-level structure as shown in the next code cell.
# Defining the storage path:
url = 'https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202505-s03slslst/29/products/cpm_v256/S3B_SL_2_LST____20250529T093001_20250529T093301_20250623T143718_0179_107_093_2340_ESA_O_NT_004.zarr'
# Opening the .zarr's data tree:
s3_lst_zarr_sample= xr.open_datatree(
url, engine="zarr")If we apply the helper function print_gen_structure on the root of the DataTree object, we will get a listing of the tree-like structure of the object. We can see all Zarr groups, such as measurements, quality and conditions, their sub-groups and content.
print("Zarr Sentinel-3 SLSTR Level-2 LST")
print_gen_structure(s3_lst_zarr_sample.root)
print(' ', 'attributes', list(s3_lst_zarr_sample.attrs.keys()))Zarr Sentinel-3 SLSTR Level-2 LST
None
conditions
auxiliary
orphan
geometry
meteorology
processing
orphan
time
measurements
orphan
quality
orphan
attributes ['other_metadata', 'stac_discovery']
Extract information from Zarr groups
In the next step, we can explore the content of individual .zarr groups. By specifying the name of the group and subgroup and adding it into square brackets, we can extract the content of the relevant group. Let us extract the content of the subgroup orphan under measurements.
As a result, it is visible that Land Surface Temperature is stored as lst, with its respective coordinates in both x and y (EPSG: Respective to UTM zone) and latitude and longitude (EPSG:4326).
The xarray.DataTree structure allows the exploration of additional group-related metadata and information. For example, we can find the chunksize of each array and the coordinates.
# Retrieving the group where LST is stored:
s3_lst_zarr_sample["measurements/orphan"] # Run it yourself for an inteactive overview<xarray.DataTree 'orphan'>
Group: /measurements/orphan
Dimensions: (rows: 1200, columns: 1500, orphan_pixels: 187)
Coordinates:
latitude (rows, orphan_pixels) float64 2MB ...
longitude (rows, orphan_pixels) float64 2MB ...
x (rows, orphan_pixels) float64 2MB ...
y (rows, orphan_pixels) float64 2MB ...
Dimensions without coordinates: rows, columns, orphan_pixels
Data variables:
lst (rows, orphan_pixels) float32 898kB ...Extract Zarr metadata on different levels
Through s3a_lst_zarr_sample.attrs[] we are able to visualise both the stac_discovery and other_metadata included in the zarr store.
For the properties inside stac_discovery for example we can get the parameters included:
# STAC metadata style:
print(list(s3_lst_zarr_sample.attrs["stac_discovery"].keys()))['assets', 'bbox', 'collection', 'geometry', 'id', 'links', 'properties', 'stac_extensions', 'stac_version', 'type']
We are also, able to retrieve specific information by diving deep into the stac_discovery metadata, such as:
print('Date of Item Creation: ', s3_lst_zarr_sample.attrs['stac_discovery']['properties']['created'])
print('Item Bounding Box : ', s3_lst_zarr_sample.attrs['stac_discovery']['bbox'])
print('Sentinel Platform : ', s3_lst_zarr_sample.attrs['stac_discovery']['properties']['platform'])
print('Item Processing Level: ', s3_lst_zarr_sample.attrs['stac_discovery']['properties']['processing:level'])
print('Class ID. : ', s3_lst_zarr_sample.attrs['stac_discovery']['properties']['product:timeliness_category'])Date of Item Creation: 2025-06-23T14:37:18+00:00
Item Bounding Box : [20.6857, 28.6806, 1.68414, 41.9899]
Sentinel Platform : sentinel-3b
Item Processing Level: L2
Class ID. : NT
And from other_metadata, we are able to retrieve the information specific to the instrument variables.
# Complementing metadata:
print(list(s3_lst_zarr_sample.attrs["other_metadata"].keys()))['L0_offset_between_scan_index_and_ISP_scan_count_in', 'absolute_pass_number', 'band_description', 'cycle_number', 'data_information', 'eopf_category', 'ephemeris', 'history', 'i_nadir_first_acquired_pixel', 'i_oblique_first_acquired_pixel', 'in_scan_period_in_microseconds', 'meteo', 'phase_identifier', 'pixel_time_sampling_interval_along_scan_i_in_microseconds', 'product_unit', 'relative_pass_number', 'single_meteofield_synoptic_time_UTC_hours']
Some relevant information included:
print('i Nadir First Pixel :', s3_lst_zarr_sample.attrs['other_metadata']['i_nadir_first_acquired_pixel'])
print('i Oblique First Pixel :',s3_lst_zarr_sample.attrs['other_metadata']['i_oblique_first_acquired_pixel'])
print('Phase Identifier :',s3_lst_zarr_sample.attrs['other_metadata']['phase_identifier'])i Nadir First Pixel : 2469
i Oblique First Pixel : 1124
Phase Identifier : 4
💪 Now it is your turn
As we are able to retrieve several items from the EOPF Sentinel Zarr Samples Service STAC API, let us try the following:
Task
Go to the Sentinel-3 SLSTR Level-2 LST collection and:
- Choose an item of interest.
- Replicate the workflow and explore the item’s metadata. When was it retrieved?
- What are the dimensions of the LST group?
- What are the values of the
bbox(location) of the item?
Conclusion
This tutorial provides an initial understanding of the .zarr structure for a Sentinel-3 SLSTR Level-2 LST product sample. By using the xarray library, we can effectively navigate and inspect the different components within the .zarr format, including its metadata and array organisation.
What’s next?
Now that you have been introduced to the .zarr encoding format, learned its core concepts, and understood the basics of how to explore it, you are prepared for the next step.
In the following chapter we will dive into the chunks concept for zarr and why is it relevant for EO.