From 3be1efe032f8cd5d16d7cef5ff4bce68fc31263a Mon Sep 17 00:00:00 2001 From: Derek Tan Date: Fri, 26 Jun 2026 15:25:41 +0000 Subject: [PATCH 1/2] Support OLCI Collection 4 (v4.01) L2 water baseline The OLCI Collection 4 (v4.01) processing baseline replaced the OC4Me chlorophyll product (chl_oc4me.nc / chlOc4meData) with chlor_a.nc and added new fluorescence (fluorescence.nc) and inherent optical properties (iop_lsd.nc) products. Manifests from the new baseline no longer contain the chlOc4meData data object, which caused create_item to raise "RuntimeError: Xpath returns no href" and abort item creation. Changes: - Skip water data objects that are absent from the manifest instead of raising, so both the legacy and Collection 4 baselines are handled. - Add the new Collection 4 water data object keys (chlorAData, fluorescenceData, iopLsdData) so they become assets when present. - Make the WFR branch data-driven via a band lookup map and return only the asset keys actually present, fixing a latent misalignment between the returned asset key list and the asset/identifier lists. - Add regression tests covering the missing-data-object case and the new Collection 4 assets. Note: the exact Collection 4 dataObject ID strings and band associations for the new products should be confirmed against a real v4.01 product once available. See: https://user.eumetsat.int/news-events/news/update-to-sentinel-3-olci-level-2-water-processing --- CHANGELOG.md | 5 + src/stactools/sentinel3/constants.py | 12 +++ src/stactools/sentinel3/metadata_links.py | 115 +++++++++++++--------- tests/test_metadata.py | 101 +++++++++++++++++++ 4 files changed, 186 insertions(+), 47 deletions(-) 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..5ff08f6 100644 --- a/src/stactools/sentinel3/constants.py +++ b/src/stactools/sentinel3/constants.py @@ -730,6 +730,18 @@ "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 = [ + "chlorAData", + "fluorescenceData", + "iopLsdData", +] + 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..512ee06 100644 --- a/src/stactools/sentinel3/metadata_links.py +++ b/src/stactools/sentinel3/metadata_links.py @@ -559,55 +559,72 @@ 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:] + # Map each 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. + band_key_map = { + "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"], + } + # 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 band_key_map: + band_key_list = band_key_map[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 +670,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/test_metadata.py b/tests/test_metadata.py index d83935b..11054c5 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -502,6 +502,107 @@ 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_skips_missing_data_object(self): + # The OLCI Collection 4 (v4.01) processing baseline renamed the OC4Me + # chlorophyll product (chl_oc4me.nc / chlOc4meData) to chlor_a.nc, so + # the chlOc4meData data object is absent from newer manifests. Building + # band assets should skip absent data objects rather than raising. + import re + + from lxml import etree + from stactools.core.io.xml import XmlElement + + manifest_path = test_data.get_path( + "data-files/" + "S3A_OL_2_WFR____20210604T001016_20210604T001316_20210604T021918_" + "0179_072_273_1440_MAR_O_NR_003.SEN3" + ) + + metalinks = MetadataLinks(manifest_path) + + # Simulate a newer-baseline manifest by removing the chlOc4meData + # data object and its pointer. + text = re.sub( + r'.*?', + "", + metalinks.manifest_text, + flags=re.S, + ) + text = text.replace('', "") + manifest = XmlElement(etree.fromstring(bytes(text, encoding="utf-8"))) + metalinks.manifest = manifest + + asset_key_list, asset_identifier_list, asset_list = metalinks.create_band_asset( + manifest, skip_nc=True + ) + + self.assertNotIn("chlOc4meData", asset_identifier_list) + # Other water data objects are still present. + self.assertIn("chlNnData", asset_identifier_list) + # 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_olci_2_wfr_collection_4_assets(self): + # The OLCI Collection 4 (v4.01) baseline replaces chl_oc4me.nc with + # chlor_a.nc and adds fluorescence.nc and iop_lsd.nc. Verify these new + # data objects are turned into assets with the expected (kebab-case) + # keys. + from lxml import etree + from stactools.core.io.xml import XmlElement + + from stactools.sentinel3.stac import sen3_to_kebab + + manifest_path = test_data.get_path( + "data-files/" + "S3A_OL_2_WFR____20210604T001016_20210604T001316_20210604T021918_" + "0179_072_273_1440_MAR_O_NR_003.SEN3" + ) + + metalinks = MetadataLinks(manifest_path) + + # Rename chlOc4meData -> chlorAData (chl_oc4me.nc -> chlor_a.nc) and + # append the new fluorescence and iop_lsd data objects. + text = metalinks.manifest_text + text = text.replace("chlOc4meData", "chlorAData").replace( + "chl_oc4me.nc", "chlor_a.nc" + ) + new_objects = ( + '' + '' + '' + '00000000000000000000000000000000' + "" + '' + '' + '' + '00000000000000000000000000000000' + "" + ) + text = text.replace( + "", new_objects + "" + ) + manifest = XmlElement(etree.fromstring(bytes(text, encoding="utf-8"))) + metalinks.manifest = manifest + + asset_key_list, asset_identifier_list, asset_list = metalinks.create_band_asset( + manifest, skip_nc=True + ) + + self.assertNotIn("chlOc4meData", asset_identifier_list) + for key in ("chlorAData", "fluorescenceData", "iopLsdData"): + self.assertIn(key, asset_identifier_list) + + kebab_keys = [sen3_to_kebab(key) for key in asset_key_list] + for kebab in ("chlor-a", "fluorescence", "iop-lsd"): + self.assertIn(kebab, kebab_keys) + + 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( From fe387161faf9c8212017ee394e29602b08f5233a Mon Sep 17 00:00:00 2001 From: Derek Tan Date: Mon, 29 Jun 2026 18:10:20 +0000 Subject: [PATCH 2/2] Use real Collection 4 manifest and correct data object IDs Address review feedback: - Verified the new water data object IDs against the real Collection 4 (v4.01) product that caused the original failure (fetched from the Planetary Computer sentinel3euwest store). The actual IDs are chlor_aData and fluoData (not the previously guessed chlorAData / fluorescenceData); iopLsdData was correct. - Move the water band lookup from a function-scoped dict to the module-level OLCI_L2_WATER_BAND_KEYS constant. - Replace the synthetic regex-based tests with tests that use a real Collection 4 manifest committed under tests/data-files (NetCDF assets stripped to header-only stubs, matching the existing fixtures), plus a command-level test that builds and validates a full item. --- src/stactools/sentinel3/constants.py | 51 +- src/stactools/sentinel3/metadata_links.py | 51 +- .../Oa01_reflectance.nc | Bin 0 -> 4146 bytes .../Oa02_reflectance.nc | Bin 0 -> 4146 bytes .../Oa03_reflectance.nc | Bin 0 -> 4146 bytes .../Oa04_reflectance.nc | Bin 0 -> 4146 bytes .../Oa05_reflectance.nc | Bin 0 -> 4146 bytes .../Oa06_reflectance.nc | Bin 0 -> 4146 bytes .../Oa07_reflectance.nc | Bin 0 -> 4146 bytes .../Oa08_reflectance.nc | Bin 0 -> 4146 bytes .../Oa09_reflectance.nc | Bin 0 -> 4146 bytes .../Oa10_reflectance.nc | Bin 0 -> 4146 bytes .../Oa11_reflectance.nc | Bin 0 -> 4146 bytes .../Oa12_reflectance.nc | Bin 0 -> 4146 bytes .../Oa16_reflectance.nc | Bin 0 -> 4146 bytes .../Oa17_reflectance.nc | Bin 0 -> 4146 bytes .../Oa18_reflectance.nc | Bin 0 -> 4146 bytes .../Oa21_reflectance.nc | Bin 0 -> 4146 bytes .../chl_nn.nc | Bin 0 -> 4146 bytes .../chlor_a.nc | Bin 0 -> 4146 bytes .../fluorescence.nc | Bin 0 -> 4146 bytes .../geo_coordinates.nc | Bin 0 -> 4146 bytes .../instrument_data.nc | Bin 0 -> 4146 bytes .../iop_lsd.nc | Bin 0 -> 4146 bytes .../iop_nn.nc | Bin 0 -> 4146 bytes .../iwv.nc | Bin 0 -> 4146 bytes .../par.nc | Bin 0 -> 4146 bytes .../tie_geo_coordinates.nc | Bin 0 -> 4146 bytes .../tie_geometries.nc | Bin 0 -> 4146 bytes .../tie_meteo.nc | Bin 0 -> 4146 bytes .../time_coordinates.nc | Bin 0 -> 4146 bytes .../trsp.nc | Bin 0 -> 4146 bytes .../tsm_nn.nc | Bin 0 -> 4146 bytes .../w_aer.nc | Bin 0 -> 4146 bytes .../wqsf.nc | Bin 0 -> 4146 bytes .../xfdumanifest.xml | 2713 +++++++++++++++++ tests/test_commands.py | 50 + tests/test_metadata.py | 99 +- 38 files changed, 2832 insertions(+), 132 deletions(-) create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa01_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa02_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa03_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa04_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa05_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa06_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa07_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa08_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa09_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa10_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa11_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa12_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa16_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa17_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa18_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/Oa21_reflectance.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/chl_nn.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/chlor_a.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/fluorescence.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/geo_coordinates.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/instrument_data.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/iop_lsd.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/iop_nn.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/iwv.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/par.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tie_geo_coordinates.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tie_geometries.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tie_meteo.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/time_coordinates.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/trsp.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/tsm_nn.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/w_aer.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/wqsf.nc create mode 100644 tests/data-files/S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_0179_117_187_2520_MAR_O_NT_004.SEN3/xfdumanifest.xml diff --git a/src/stactools/sentinel3/constants.py b/src/stactools/sentinel3/constants.py index 5ff08f6..c43c188 100644 --- a/src/stactools/sentinel3/constants.py +++ b/src/stactools/sentinel3/constants.py @@ -737,11 +737,58 @@ # 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 = [ - "chlorAData", - "fluorescenceData", + "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 512ee06..f62d3d9 100644 --- a/src/stactools/sentinel3/metadata_links.py +++ b/src/stactools/sentinel3/metadata_links.py @@ -559,53 +559,6 @@ def strip_prefix(prefix: str, content: str) -> str: asset_identifier_list.append(asset_key) asset_list.append(asset_obj) elif "_WFR_" in product_type: - # Map each 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. - band_key_map = { - "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"], - } # 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 @@ -617,8 +570,8 @@ def strip_prefix(prefix: str, content: str) -> str: ): if len(manifest.findall(f".//dataObject[@ID='{asset_key}']")) == 0: continue - if asset_key in band_key_map: - band_key_list = band_key_map[asset_key] + 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" ): 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 0000000000000000000000000000000000000000..a1cc394d52f2267c067db3df81fae94825a34036 GIT binary patch literal 4146 zcmeHKU1%It6h7IlZ4y(pv{tBp*I1=!c6WX@*&hic*~z*oNq5agN-JfY%-%Iac6YWj zv$a(eDz;*k`cSa6f+7_vQlF~$pn@;43MwcV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|hV1b7v;aNTDJ=NXccH zGxy$e&OP`1d}sE>l2%;XyQw$U-3{Sb42E)xzaj-2!>?bm%O!0(wst-i>lcghzsrQO zA?=Mp7xCPB2dqL8Uk4{+zDzEV)k7WYgAIRs<+(asGzLLZ(q5Qk zFZKGI2f|bedBU6y=sKV6norDBN>(vM_QDp-;>$OOD?1Or_vzS7NkfY;XGv>q%h}-U zbz)AO2alXuBe;5qYpu_91MQGZXv-mO@uy-Hb-@Qg{)c4{QeT2hs=!{f1pJDEvupgF z`vQiL{_@H0m&ax%i)h{bWPJvJPA&Jm)ejr!Z5)IX8|wK4JWUtP1Kflp-FMFzoFkFV z0Hoa8xTy%2@C6dSN3;uHY=;H9>4N|_BKhpL-)z`OIvxP%LvrrN)BE9lTF?Q=VUIkw z@H~vrT@C>BAlX0n&OunDQ$|6A^tDy*9)%q^rv+N~5(~#*BP|r@0dV-}A8*1rdbEx%Uh#2#<9EWy8m_#xsk~NV?MRGEdOOf<_ zvWP!!Crwivpgyu=(yF2!O%g?>=8FX=6+6SRh_XdzHtr^t);1 z+8xK_upYSy9jIm|D#b*3GNCXjla%B*LV(Zd8IGrWR@3LcJ>XU(MNUXV2`N1zNqHH6 zgUM_rBW1HY<5;ADsNKG%q$(uSij>WvRKh4d6>g}H|9*zLJ}jRrIAmdRV5V|bR`Vw&L@tjfJO-c=Z;Z!4g1n>(Jx zUEzfO;Fvw!aZzMg8EA3Otks9p$y`z$n5)$XhvlT4P7WdC%2@Hh|J?)8`X_5ooIHIj zj$NYxO>MPj%TA3u$)%;_ z-ex0?ArvrXwOj$B$Z}pzWiq-ZFv?UWaR-IIZKLTT4TtAIOqC6q&1$ zxgzJ}tS-r!oGxV)UCF4roK8zR+O3!MZPU6Wsmbd2Hg%~9IlRstj+pMF_4i9AVAEn1 zt;!@Rom5$ZS&Bm>XbnE{7j36$c&ysyrd790zb7PEq@zq3j@9A=tg2qosE|I{$^1io z;h9y>!LHPCxErd|DpP1+);Eu|YTPrd2CtEwlnhuuY|5@-Z;P6>?Ea!$!$8y5IR*ML mreq|h + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 11054c5..6fcc147 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -502,104 +502,41 @@ 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_skips_missing_data_object(self): - # The OLCI Collection 4 (v4.01) processing baseline renamed the OC4Me - # chlorophyll product (chl_oc4me.nc / chlOc4meData) to chlor_a.nc, so - # the chlOc4meData data object is absent from newer manifests. Building - # band assets should skip absent data objects rather than raising. - import re - - from lxml import etree - from stactools.core.io.xml import XmlElement - - manifest_path = test_data.get_path( - "data-files/" - "S3A_OL_2_WFR____20210604T001016_20210604T001316_20210604T021918_" - "0179_072_273_1440_MAR_O_NR_003.SEN3" - ) - - metalinks = MetadataLinks(manifest_path) - - # Simulate a newer-baseline manifest by removing the chlOc4meData - # data object and its pointer. - text = re.sub( - r'.*?', - "", - metalinks.manifest_text, - flags=re.S, - ) - text = text.replace('', "") - manifest = XmlElement(etree.fromstring(bytes(text, encoding="utf-8"))) - metalinks.manifest = manifest - - asset_key_list, asset_identifier_list, asset_list = metalinks.create_band_asset( - manifest, skip_nc=True - ) - - self.assertNotIn("chlOc4meData", asset_identifier_list) - # Other water data objects are still present. - self.assertIn("chlNnData", asset_identifier_list) - # 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_olci_2_wfr_collection_4_assets(self): - # The OLCI Collection 4 (v4.01) baseline replaces chl_oc4me.nc with - # chlor_a.nc and adds fluorescence.nc and iop_lsd.nc. Verify these new - # data objects are turned into assets with the expected (kebab-case) - # keys. - from lxml import etree - from stactools.core.io.xml import XmlElement - + 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/" - "S3A_OL_2_WFR____20210604T001016_20210604T001316_20210604T021918_" - "0179_072_273_1440_MAR_O_NR_003.SEN3" + "S3B_OL_2_WFR____20260301T234556_20260301T234856_20260303T033944_" + "0179_117_187_2520_MAR_O_NT_004.SEN3" ) metalinks = MetadataLinks(manifest_path) - # Rename chlOc4meData -> chlorAData (chl_oc4me.nc -> chlor_a.nc) and - # append the new fluorescence and iop_lsd data objects. - text = metalinks.manifest_text - text = text.replace("chlOc4meData", "chlorAData").replace( - "chl_oc4me.nc", "chlor_a.nc" - ) - new_objects = ( - '' - '' - '' - '00000000000000000000000000000000' - "" - '' - '' - '' - '00000000000000000000000000000000' - "" - ) - text = text.replace( - "", new_objects + "" - ) - manifest = XmlElement(etree.fromstring(bytes(text, encoding="utf-8"))) - metalinks.manifest = manifest - asset_key_list, asset_identifier_list, asset_list = metalinks.create_band_asset( - manifest, skip_nc=True + metalinks.manifest, skip_nc=True ) + # The removed OC4Me product must not appear. self.assertNotIn("chlOc4meData", asset_identifier_list) - for key in ("chlorAData", "fluorescenceData", "iopLsdData"): + # 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", "fluorescence", "iop-lsd"): + 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))