Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
## Changes in 0.2.3 (under development)

- **Sentinel-3 SLSTR Level-1 RBT products** are now supported in analysis mode. This
allows data from grids a, b, f, and i — in both nadir and oblique viewing
geometries — to be represented on a unified grid within a single dataset.
- **Sentinel-3 SLSTR datasets** are now terrain-corrected using the elevation
information provided within the product itself.


## Changes in 0.2.2 (from 2025-09-24)

* In analysis mode for Sentinel-3 products, coordinates are now filtered so that only
`"lat"` and `"lon"` remain. Since the data is rectified, non-spatial coordinates lose
`"lat"` and `"lon"` remain. Since the data is rectified, non-spatial coordinates loose
their association with the data after rectification.


Expand Down
17 changes: 15 additions & 2 deletions docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,28 @@ Examples:
#### Sentinel-3

Sentinel-3 products are provided on their **native grid mapping**, where each pixel
is defined by a latitude/longitude pair, forming a **2D irregular grid**.
is defined by a latitude/longitude pair, forming a **2D irregular grid**.

The analysis mode applies the [rectification algorithm in xcube-resampling](https://xcube-dev.github.io/xcube-resampling/guide/#3-rectification) to transform the irregular dataset into a **regular grid** with 1D latitude/longitude coordinates.
The analysis mode applies the [rectification algorithm in xcube-resampling](https://xcube-dev.github.io/xcube-resampling/guide/#3-rectification)
to transform the irregular dataset into a **regular grid** with 1D latitude/longitude
coordinates.

For SLSTR products, a terrain correction is applied during this process. This is
necessary because the original geolocation is corrected only for Earth curvature,
but not for terrain variability caused by topography. See the
[SLSTR product description](https://sentiwiki.copernicus.eu/web/slstr-products)
for details.

For OLCI products, no additional terrain correction is required, as it is already
incorporated in the Level-1 data. See the [OLCI Level-1 product description](https://sentiwiki.copernicus.eu/web/olci-products#OLCIProducts-L1BProducts-ObservationModeS3-OLCI-Products-L1B-OM)
for details.

**Suported Products:**

- [Sentinel-3 OLCI Level-1 EFR](https://stac.browser.user.eopf.eodc.eu/collections/sentinel-3-olci-l1-efr)
- [Sentinel-3 OLCI Level-1 ERR](https://stac.browser.user.eopf.eodc.eu/collections/sentinel-3-olci-l1-err)
- [Sentinel-3 OLCI Level-2 LFR](https://stac.browser.user.eopf.eodc.eu/collections/sentinel-3-olci-l2-lfr)
- [Sentinel-3 SLSTR Level-1 RBT](https://stac.browser.user.eopf.eodc.eu/collections/sentinel-3-slstr-l1-rbt)
- [Sentinel-3 SLSTR Level-2 LST](https://stac.browser.user.eopf.eodc.eu/collections/sentinel-3-slstr-l2-lst)

Example:
Expand Down
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ dependencies:
- requests
- s3fs
- xarray >=2024.10
- xcube-resampling
- xcube-resampling >=0.2.0
- zarr >=2,<3.0
# Development Dependencies - Tools
- black
Expand Down
14,173 changes: 9,736 additions & 4,437 deletions examples/open-sen1.ipynb

Large diffs are not rendered by default.

2,629 changes: 1,198 additions & 1,431 deletions examples/open-sen2.ipynb

Large diffs are not rendered by default.

123,774 changes: 10,513 additions & 113,261 deletions examples/open-sen3.ipynb

Large diffs are not rendered by default.

90 changes: 90 additions & 0 deletions integration/test_sen3_analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Copyright (c) 2025 by EOPF Sample Service team and contributors
# Permissions are hereby granted under the terms of the Apache 2.0 License:
# https://opensource.org/license/apache-2-0.

from collections.abc import Sequence
from unittest import TestCase

import xarray as xr

from integration.helpers import assert_dataset_is_chunked
from xarray_eopf.constants import DEFAULT_ENDPOINT_URL
from xarray_eopf.utils import timeit


allowed_open_time = 1000 # seconds
show_chunking = False

ol1efr_url = (
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202508-s03olcefr/19/"
"products/cpm_v256/S3B_OL_1_EFR____20250819T074058_20250819T074358_"
"20250819T092155_0179_110_106_3420_ESA_O_NR_004.zarr"
)

ol1err_url = (
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202510-s03olcerr-global/"
"19/products/cpm_v256/S3A_OL_1_ERR____20251019T145533_20251019T153950_"
"20251019T165332_2657_131_353______PS1_O_NR_004.zarr"
)

ol2lfr_url = (
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202510-s03olclfr-"
"global/15/products/cpm_v256/S3A_OL_2_LFR____20251015T050206_20251015T050506_"
"20251015T070316_0179_131_290_2340_PS1_O_NR_003.zarr"
)
sl1rbt_url = (
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202510-s03slsrbt-global/"
"16/products/cpm_v256/S3B_SL_1_RBT____20251016T072510_20251016T072810_"
"20251016T092049_0179_112_163_2700_ESA_O_NR_004.zarr"
)
sl2lst_url = (
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202510-s03slslst-eu/16/"
"products/cpm_v256/S3B_SL_2_LST____20251016T215803_20251016T220103_20251017T004323_"
"0179_112_172_0540_ESA_O_NR_004.zarr"
)


class Sentinel3AnalysisTest(TestCase):
def test_open_dataset_sen3_olci_l1_efr(self):
expected_vars = ["oa01_radiance", "oa02_radiance", "oa03_radiance"]
expected_size = (5000, 5269)
self._test_sen3(ol1efr_url, expected_vars, expected_size)

def test_open_dataset_sen3_olci_l1_err(self):
expected_vars = ["oa01_radiance", "oa02_radiance", "oa03_radiance"]
expected_size = (14432, 11065)
self._test_sen3(ol1err_url, expected_vars, expected_size)

def test_open_dataset_sen3_olci_l2_lfr(self):
expected_vars = ["gifapar", "iwv", "otci"]
expected_size = (4790, 5125)
self._test_sen3(ol2lfr_url, expected_vars, expected_size)

def test_open_dataset_sen3_slstr_l1_rbt(self):
expected_vars = ["s1_radiance_an", "s7_bt_in", "s7_bt_io"]
expected_size = (2959, 3308)
self._test_sen3(sl1rbt_url, expected_vars, expected_size)

def test_open_dataset_sen3_slstr_l2_lst(self):
expected_vars = ["lst"]
expected_size = (1473, 1657)
self._test_sen3(sl2lst_url, expected_vars, expected_size)

def _test_sen3(
self, path: str, expected_vars: Sequence[str], expected_size: tuple[int, int]
):
with timeit("open " + path) as result:
# noinspection PyTypeChecker
ds = xr.open_dataset(
path,
engine="eopf-zarr",
chunks={},
)
self.assertTrue(result.time_delta < allowed_open_time)

for expected_var in expected_vars:
self.assertIn(expected_var, ds)

assert_dataset_is_chunked(self, ds, verbose=show_chunking)
for var_name in ds.data_vars:
self.assertEqual(expected_size, ds[var_name].shape[-2:], msg=var_name)
34 changes: 17 additions & 17 deletions integration/test_sen3_native.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,31 @@
from xarray_eopf.utils import timeit

ol1efr_url = (
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202508-s03olcefr/"
"19/products/cpm_v256/S3B_OL_1_EFR____20250819T074058_20250819T074358_"
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202508-s03olcefr/19/"
"products/cpm_v256/S3B_OL_1_EFR____20250819T074058_20250819T074358_"
"20250819T092155_0179_110_106_3420_ESA_O_NR_004.zarr"
)

ol1err_url = (
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202508-s03olcerr/"
"20/products/cpm_v256/S3B_OL_1_ERR____20250820T082222_20250820T090603_"
"20250820T113046_2621_110_121______ESA_O_NR_004.zarr"
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202510-s03olcerr-global/"
"19/products/cpm_v256/S3A_OL_1_ERR____20251019T145533_20251019T153950_"
"20251019T165332_2657_131_353______PS1_O_NR_004.zarr"
)

ol2lfr_url = (
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202508-s03olclfr/"
"20/products/cpm_v256/S3A_OL_2_LFR____20250820T110024_20250820T110324_"
"20250820T125434_0179_129_265_2520_PS1_O_NR_003.zarr"
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202510-s03olclfr-"
"global/15/products/cpm_v256/S3A_OL_2_LFR____20251015T050206_20251015T050506_"
"20251015T070316_0179_131_290_2340_PS1_O_NR_003.zarr"
)
sl1rbt_url = (
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202508-s03slsrbt/"
"20/products/cpm_v256/S3A_SL_1_RBT____20250820T074725_20250820T075025_"
"20250820T095144_0180_129_263_3060_PS1_O_NR_004.zarr"
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202510-s03slsrbt-global/"
"16/products/cpm_v256/S3B_SL_1_RBT____20251016T072510_20251016T072810_"
"20251016T092049_0179_112_163_2700_ESA_O_NR_004.zarr"
)
sl2lst_url = (
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202508-s03slslst/"
"21/products/cpm_v256/S3A_SL_2_LST____20250821T085614_20250821T085914_"
"20250821T110745_0179_129_278_2700_PS1_O_NR_004.zarr"
"https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:202510-s03slslst-eu/16/"
"products/cpm_v256/S3B_SL_2_LST____20251016T215803_20251016T220103_"
"20251017T004323_0179_112_172_0540_ESA_O_NR_004.zarr"
)

allowed_open_time = 5 # seconds
Expand Down Expand Up @@ -65,14 +65,14 @@ def test_open_dataset_sen3_ol1efr_subgroup(self):

def test_open_datatree_sen3_ol1err(self):
self._test_open_datatree_sen3(
ol1err_url, 8, "measurements", {"columns": 1217, "rows": 14893}, 21
ol1err_url, 8, "measurements", {"columns": 1217, "rows": 15098}, 21
)

def test_open_dataset_sen3_ol1err(self):
self._test_open_dataset_sen3(
ol1err_url,
"measurements_oa21_radiance",
{"measurements_columns": 1217, "measurements_rows": 14893},
{"measurements_columns": 1217, "measurements_rows": 15098},
59,
)

Expand All @@ -81,7 +81,7 @@ def test_open_dataset_sen3_ol1err_subgroup(self):
ol1err_url,
"oa21_radiance",
"measurements",
{"columns": 1217, "rows": 14893},
{"columns": 1217, "rows": 15098},
21,
)

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ dependencies = [
"requests",
"s3fs",
"xarray>=2024.10",
"xcube-resampling",
"xcube-resampling>=0.2.0",
"zarr>=2,<3.0"
]

Expand Down
Loading