diff --git a/CHANGELOG.md b/CHANGELOG.md index d4e0a54..e4908a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,11 @@ number as needed. ### Fixed +- Support the OLCI Collection 4 (v4.01) level 2 water processing baseline: + data objects absent from a manifest (such as the OC4Me chlorophyll product + `chlOc4meData`, removed in Collection 4) are skipped instead of raising a + `RuntimeError`, and the new `chlor_a`, `fluorescence`, and `iop_lsd` + products are turned into assets when present - Use correct EO Extension attribute names and units ([#13](https://github.com/stactools-packages/sentinel3/pull/15)) - Use un-stripped `instance_id` to check for strip granules and apply geometry fix ([#19](https://github.com/stactools-packages/sentinel3/pull/19)) diff --git a/src/stactools/sentinel3/constants.py b/src/stactools/sentinel3/constants.py index 2b30abe..c43c188 100644 --- a/src/stactools/sentinel3/constants.py +++ b/src/stactools/sentinel3/constants.py @@ -730,6 +730,65 @@ "wqsfData", ] +# Additional data objects introduced by the OLCI Collection 4 (v4.01) +# processing baseline for level 2 water products. The OC4Me chlorophyll +# product (chl_oc4me.nc / chlOc4meData) was replaced by chlor_a.nc, and the +# fluorescence (fluorescence.nc) and inherent optical properties (iop_lsd.nc) +# products were added. See: +# https://user.eumetsat.int/news-events/news/update-to-sentinel-3-olci-level-2-water-processing +OLCI_L2_WATER_ASSET_KEYS_C4 = [ + "chlor_aData", + "fluoData", + "iopLsdData", +] + +# Maps each OLCI L2 water data object ID to the OLCI bands it is derived from. +# Data objects without an entry here (annotation files, and the Collection 4 +# chlor_a/fluorescence/iop_lsd products) carry no eo:bands. +OLCI_L2_WATER_BAND_KEYS = { + "chlNnData": [ + "Oa01", + "Oa02", + "Oa03", + "Oa04", + "Oa05", + "Oa06", + "Oa07", + "Oa08", + "Oa09", + "Oa10", + "Oa11", + "Oa12", + "Oa16", + "Oa17", + "Oa18", + "Oa21", + ], + "tsmNnData": [ + "Oa01", + "Oa02", + "Oa03", + "Oa04", + "Oa05", + "Oa06", + "Oa07", + "Oa08", + "Oa09", + "Oa10", + "Oa11", + "Oa12", + "Oa16", + "Oa17", + "Oa18", + "Oa21", + ], + "chlOc4meData": ["Oa03", "Oa04", "Oa05", "Oa06"], + "iopNnData": ["Oa01", "Oa12", "Oa16", "Oa17", "Oa21"], + "iwvData": ["Oa18", "Oa19"], + "trspData": ["Oa04", "Oa06"], + "wAerData": ["Oa05", "Oa06", "Oa17"], +} + SLSTR_L1_ASSET_KEYS = [ "SLSTR_S1_RAD_AN_Data", "SLSTR_S2_RAD_AN_Data", diff --git a/src/stactools/sentinel3/metadata_links.py b/src/stactools/sentinel3/metadata_links.py index c296ce9..f62d3d9 100644 --- a/src/stactools/sentinel3/metadata_links.py +++ b/src/stactools/sentinel3/metadata_links.py @@ -559,55 +559,25 @@ def strip_prefix(prefix: str, content: str) -> str: asset_identifier_list.append(asset_key) asset_list.append(asset_obj) elif "_WFR_" in product_type: - asset_key_list = constants.OLCI_L2_WATER_ASSET_KEYS - for asset_key in asset_key_list: - if asset_key == "chlNnData" or asset_key == "tsmNnData": - band_key_list = [ - "Oa01", - "Oa02", - "Oa03", - "Oa04", - "Oa05", - "Oa06", - "Oa07", - "Oa08", - "Oa09", - "Oa10", - "Oa11", - "Oa12", - "Oa16", - "Oa17", - "Oa18", - "Oa21", - ] - elif asset_key == "chlOc4meData": - band_key_list = ["Oa03", "Oa04", "Oa05", "Oa06"] - elif asset_key == "iopNnData": - band_key_list = [ - "Oa01", - "Oa12", - "Oa16", - "Oa17", - "Oa21", - ] - elif asset_key == "iwvData": - band_key_list = [ - "Oa18", - "Oa19", - ] - elif asset_key == "parData": - band_key_list = [] - elif asset_key == "trspData": - band_key_list = ["Oa04", "Oa06"] - elif asset_key == "wAerData": - band_key_list = ["Oa05", "Oa06", "Oa17"] - elif any( - asset_key == key - for key in constants.OLCI_L2_WATER_ASSET_KEYS[-7:] + # Iterate over both the legacy water keys and the Collection 4 + # (v4.01) additions. Data objects that are not present in this + # particular manifest are skipped, so the same code handles + # both the old and new processing baselines. + present_asset_keys = [] + for asset_key in ( + constants.OLCI_L2_WATER_ASSET_KEYS + + constants.OLCI_L2_WATER_ASSET_KEYS_C4 + ): + if len(manifest.findall(f".//dataObject[@ID='{asset_key}']")) == 0: + continue + if asset_key in constants.OLCI_L2_WATER_BAND_KEYS: + band_key_list = constants.OLCI_L2_WATER_BAND_KEYS[asset_key] + elif asset_key.startswith("Oa") and asset_key.endswith( + "_reflectanceData" ): - band_key_list = [] - else: band_key_list = [asset_key[:4]] + else: + band_key_list = [] asset_location = self.read_href( f".//dataObject[@ID='{asset_key}']//fileLocation" ) @@ -653,8 +623,12 @@ def strip_prefix(prefix: str, content: str) -> str: roles=["data"], extra_fields={"s3:spatial_resolution": asset_resolution}, ) + present_asset_keys.append(asset_key) asset_identifier_list.append(asset_key) asset_list.append(asset_obj) + # Return only the keys actually present in this manifest so the + # asset keys stay aligned with asset_identifier_list/asset_list. + asset_key_list = present_asset_keys elif instrument_bands == constants.SENTINEL_SLSTR_BANDS: if "SL_1_" in product_type: asset_key_list = constants.SLSTR_L1_ASSET_KEYS diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa01_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa01_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa01_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa02_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa02_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa02_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa03_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa03_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa03_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa04_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa04_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa04_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa05_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa05_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa05_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa06_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa06_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa06_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa07_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa07_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa07_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa08_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa08_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa08_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa09_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa09_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa09_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa10_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa10_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa10_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa11_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa11_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa11_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa12_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa12_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa12_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa16_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa16_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa16_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa17_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa17_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa17_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa18_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa18_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa18_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa21_reflectance.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa21_reflectance.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa21_reflectance.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/chl_nn.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/chl_nn.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/chl_nn.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/chlor_a.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/chlor_a.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/chlor_a.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/fluorescence.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/fluorescence.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/fluorescence.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/geo_coordinates.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/geo_coordinates.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/geo_coordinates.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/instrument_data.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/instrument_data.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/instrument_data.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/iop_lsd.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/iop_lsd.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/iop_lsd.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/iop_nn.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/iop_nn.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/iop_nn.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/iwv.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/iwv.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/iwv.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/par.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/par.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/par.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tie_geo_coordinates.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tie_geo_coordinates.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tie_geo_coordinates.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tie_geometries.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tie_geometries.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tie_geometries.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tie_meteo.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tie_meteo.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tie_meteo.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/time_coordinates.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/time_coordinates.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/time_coordinates.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/trsp.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/trsp.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/trsp.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tsm_nn.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tsm_nn.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tsm_nn.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/w_aer.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/w_aer.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/w_aer.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/wqsf.nc b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/wqsf.nc new file mode 100644 index 0000000..a1cc394 Binary files /dev/null and b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/wqsf.nc differ diff --git a/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/xfdumanifest.xml b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/xfdumanifest.xml new file mode 100644 index 0000000..979ccae --- /dev/null +++ b/tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/xfdumanifest.xml @@ -0,0 +1,2713 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2026-03-01T23:45:55.931306Z + 2026-03-01T23:48:55.931306Z + + + + + + + + + 2018-039A + Sentinel-3 + B + + Ocean Land Colour Instrument + Earth Observation + + + + + + + + + + + 20.9456 146.872 20.84 147.527 20.7309 148.178 20.6189 148.832 20.5069 149.486 20.3902 150.136 20.2714 150.785 20.1495 151.434 20.0256 152.081 19.8992 152.728 19.7713 153.376 19.6407 154.02 19.508 154.662 19.3733 155.306 19.2357 155.948 19.0961 156.589 18.9544 157.226 18.8102 157.865 18.6636 158.503 18.5164 159.139 21.1573 159.834 23.8001 160.555 26.4391 161.306 29.0748 162.091 29.2337 161.402 29.3897 160.715 29.542 160.025 29.6916 159.331 29.8366 158.637 29.9786 157.938 30.1158 157.24 30.2501 156.54 30.3808 155.836 30.5068 155.132 30.6303 154.425 30.7498 153.717 30.8659 153.007 30.9773 152.296 31.0824 151.583 31.1865 150.866 31.2857 150.155 31.3821 149.434 31.4725 148.715 28.8395 148.267 26.2078 147.811 23.5745 147.345 20.9456 146.872 + + + + + + + + + + S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3 + OL_2_WFR___ + NT + 004 + OL__L2M.004.00.00 + 20260303T033944 + 551824956 + + 1 + FIRST + 2026-03-01T23:33:16.049516Z + CGS + 2026-03-02T01:05:00.709566Z + 2026-03-02T01:05:21.469254Z + + Operational + + FRAME + 179 + 2520 + + + + + + + + + + + + + + + + + + PARALLEL + 2.000000 + 0 + + 4091 + 4865 + + + + + + + + + ANALYSIS + 148246589858 + true + + 44002 + 294 + 270 + 1 + 64 + + + + 400 + 15 + + + 412.5 + 10 + + + 442.5 + 10 + + + 490 + 10 + + + 510 + 10 + + + 560 + 10 + + + 620 + 10 + + + 665 + 10 + + + 673.75 + 7.5 + + + 681.25 + 7.5 + + + 708.75 + 10 + + + 753.75 + 7.5 + + + 778.75 + 15 + + + 865 + 20 + + + 885 + 10 + + + 1020 + 40 + + + + + + + + + + + 40884 + 187 + 81768 + 374 + 117 + 4 + + + 2026-03-01T23:04:32.931306 + 2026-03-01T23:03:55.931306Z + 2026-03-01T23:03:55.998267 + + 6914835.9069999997 + -1948488.7790000001 + 0.001 + + + -452.91216300000002 + -1576.926886 + 7365.994737 + + + + 2026-03-02T00:45:32.065839 + 2026-03-02T00:44:55.065839Z + 2026-03-02T00:44:55.132764 + + 5423302.6579999998 + -4711697.301 + -0.002 + + + -1082.1978340000001 + -1233.0567229999999 + 7365.9296889999996 + + + + + + + + + + + + + + PASSED + NON_NOMINAL_INPUT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0cbf2c177d0a6678e4468775ed7a538e + + + + + + bddd02660ef8862ee1200a16355acb9d + + + + + + 6fe2f624f52bd9f7a4abdc17364ae08a + + + + + + 800389b6800b78e16190e678ec735fcd + + + + + + df07c9dcf8835f564ad9f1f4957ebd9a + + + + + + 2d1bf75c97bf079c6f63ad177d657947 + + + + + + 4733ac45451415780eabbd16fd60bdd1 + + + + + + 067b8d75df045e632659842aaf401a2c + + + + + + 48726d4a2e30a9f9ccd8c45f7dd56456 + + + + + + c2147fa0386538453c0a8b939f138b75 + + + + + + f15245133094fff67273a0ed5a820349 + + + + + + e23b4932bd152827c0b5588b68007206 + + + + + + 282bb301742b632df07cf1a41fb119a4 + + + + + + 4d82bff8ef1114d3101dd1a7a568520e + + + + + + 57022d94af72e3d193725190f0a8c16c + + + + + + 3be42af08bc3eb5c05d4d0aae7d21c60 + + + + + + 65e7f020d79b790f03aaebbff135a6dd + + + + + + eb8b7a30e36ae3efc7a66b8278de0bf4 + + + + + + 54603bce141e49abe18136d71eca0a50 + + + + + + 046a0e4e3d0f586f476105cb97ae8c95 + + + + + + 2ea4c845650bb3352665694078c3842a + + + + + + 28d10d21f8773b6afd406f9e423d1ee1 + + + + + + 14e95386d9424e516e98dccfb6dc3c1d + + + + + + 2187f8d3b4f3b450aaf9190de9994af4 + + + + + + 6e0e051a6232acee2a8eaf804dc27881 + + + + + + 322fa45557f36d3532bbd9b8f22b6ee2 + + + + + + a4f1040066d7fed272a9025fb352b0f4 + + + + + + 5206f4f15ede0b4fe2264ad816ded6e6 + + + + + + 2c75a0e01996b4fb251c7b2e7bf302e3 + + + + + + 8dbc46a250e2f322f68549633ba6108b + + + + + + c72c4a3120272f304b76ccc68b45686d + + + + + + b093dd1ae877b9078e71cedc4e5bc455 + + + + + + d459029f03377e53ef7b9769b2c11ae9 + + + + + diff --git a/tests/test_commands.py b/tests/test_commands.py index eac9d59..61ad9d4 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -215,6 +215,56 @@ def test_create_olci_2_wfr_item(self): [self.assertTrue(band in band_list) for band in bands_seen] os.remove(f"{tmp_dir}/{item_id}.json") + def test_create_olci_2_wfr_collection_4_item(self): + # Real-world OLCI Collection 4 (v4.01) water product. This baseline + # replaced chl_oc4me.nc with chlor_a.nc and added fluorescence.nc and + # iop_lsd.nc; the missing chlOc4meData data object originally caused + # item creation to fail. + item_id = "S3B_OL_2_WFR_20260301T234556_20260301T234856_0179_117_187_2520" + granule_href = test_data.get_path( + "data-files/" + "S3B_OL_2_WFR____" + "20260301T234556_20260301T234856_20260303T033944_" + "0179_117_187_2520_MAR_O_NT_004.SEN3" + ) + + with self.subTest(granule_href): + with TemporaryDirectory() as tmp_dir: + cmd = ["sentinel3", "create-item", granule_href, tmp_dir] + self.run_command(cmd) + + jsons = [p for p in os.listdir(tmp_dir) if p.endswith(".json")] + self.assertEqual(len(jsons), 1) + fname = jsons[0] + + item = pystac.Item.from_file(os.path.join(tmp_dir, fname)) + + item.validate() + + self.assertEqual(item.id, item_id) + + # The Collection 4 products are present and the removed OC4Me + # product is not. + self.assertIn("chlor-a", item.assets) + self.assertIn("fluo", item.assets) + self.assertIn("iop-lsd", item.assets) + self.assertNotIn("chl-oc4me", item.assets) + + band_list = [value.name for value in SENTINEL_OLCI_BANDS.values()] + + bands_seen = set() + + for _, asset in item.assets.items(): + self.assertTrue("/./" not in asset.href) + self.assertTrue(is_absolute_href(asset.href)) + asset_eo = EOExtension.ext(asset) + bands = asset_eo.bands + if bands is not None: + bands_seen |= set(b.name for b in bands) + + [self.assertTrue(band in band_list) for band in bands_seen] + os.remove(f"{tmp_dir}/{item_id}.json") + def test_create_slstr_1_rbt_item(self): item_id = "S3A_SL_1_RBT_20210930T220914_20210930T221214_0180_077_043_5400" granule_href = test_data.get_path( diff --git a/tests/test_metadata.py b/tests/test_metadata.py index d83935b..6fcc147 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -502,6 +502,44 @@ def test_parses_olci_2_wfr_metadata_properties(self): self.assertIn(k, s3_props) self.assertEqual(s3_props[k], v) + def test_olci_2_wfr_collection_4_baseline(self): + # The OLCI Collection 4 (v4.01) processing baseline replaced the OC4Me + # chlorophyll product (chl_oc4me.nc / chlOc4meData) with chlor_a.nc and + # added fluorescence.nc and iop_lsd.nc. This uses a real-world manifest + # from the baseline that originally caused + # "RuntimeError: Xpath returns no href" to ensure the new products are + # turned into assets and the removed product no longer breaks parsing. + from stactools.sentinel3.stac import sen3_to_kebab + + manifest_path = test_data.get_path( + "data-files/" + "S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_" + "0179_117_187_2520_MAR_O_NT_004.SEN3" + ) + + metalinks = MetadataLinks(manifest_path) + + asset_key_list, asset_identifier_list, asset_list = metalinks.create_band_asset( + metalinks.manifest, skip_nc=True + ) + + # The removed OC4Me product must not appear. + self.assertNotIn("chlOc4meData", asset_identifier_list) + # The Collection 4 products must be present as assets. + for key in ("chlor_aData", "fluoData", "iopLsdData"): + self.assertIn(key, asset_identifier_list) + # Legacy water products that remain in Collection 4 are still present. + self.assertIn("chlNnData", asset_identifier_list) + + kebab_keys = [sen3_to_kebab(key) for key in asset_key_list] + for kebab in ("chlor-a", "fluo", "iop-lsd"): + self.assertIn(kebab, kebab_keys) + + # The returned key list must stay aligned with the assets so that + # downstream zipping does not misalign asset keys and assets. + self.assertEqual(asset_key_list, asset_identifier_list) + self.assertEqual(len(asset_identifier_list), len(asset_list)) + def test_parses_slstr_1_rbt_metadata_properties(self): # Get the path of the test xml manifest_path = test_data.get_path(