From f56b6b32d7c9c8369ab571bead4905fccae23274 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Thu, 3 Jun 2021 21:11:16 +0200 Subject: [PATCH 1/5] Fixes #404 --- CHANGELOG.md | 2 + pystac/extensions/eo.py | 10 + tests/data-files/eo/eo-sentinel2-item.json | 589 +++++++++++++++++++++ tests/extensions/test_eo.py | 8 + 4 files changed, 609 insertions(+) create mode 100644 tests/data-files/eo/eo-sentinel2-item.json diff --git a/CHANGELOG.md b/CHANGELOG.md index b45bf46be..f6f2fa233 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ ### Fixed +- Fixed returned None by `EOExtension.get_bands` for asset without EO bands + ### Removed ## [1.0.0-beta.3] diff --git a/pystac/extensions/eo.py b/pystac/extensions/eo.py index c8a59cedf..9d9cfe754 100644 --- a/pystac/extensions/eo.py +++ b/pystac/extensions/eo.py @@ -437,6 +437,16 @@ class AssetEOExtension(EOExtension[pystac.Asset]): """If present, this will be a list containing 1 dictionary representing the properties of the owning :class:`~pystac.Item`.""" + def _get_bands(self) -> Optional[List[Band]]: + if BANDS_PROP not in self.properties: + return None + return list( + map( + lambda band: Band(band), + cast(List[Dict[str, Any]], self.properties.get(BANDS_PROP)), + ) + ) + def __init__(self, asset: pystac.Asset): self.asset_href = asset.href self.properties = asset.properties diff --git a/tests/data-files/eo/eo-sentinel2-item.json b/tests/data-files/eo/eo-sentinel2-item.json new file mode 100644 index 000000000..35b3c7f95 --- /dev/null +++ b/tests/data-files/eo/eo-sentinel2-item.json @@ -0,0 +1,589 @@ +{ + "stac_version": "1.0.0", + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v1.0.0/schema.json", + "https://stac-extensions.github.io/processing/v1.0.0/schema.json", + "https://stac-extensions.github.io/raster/v1.0.0/schema.json", + "https://stac-extensions.github.io/sat/v1.0.0/schema.json", + "https://stac-extensions.github.io/version/v1.0.0/schema.json" + ], + "type": "Feature", + "id": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 86.99949043202683, + 69.40964050938386 + ], + [ + 89.79493686530785, + 69.38716009345416 + ], + [ + 89.6733360684267, + 68.4035546435041 + ], + [ + 86.99951263501231, + 68.42491711604748 + ], + [ + 86.99949043202683, + 69.40964050938386 + ], + [ + 86.99949043202683, + 69.40964050938386 + ] + ] + ] + }, + "properties": { + "datetime": "2020-06-15T06:06:41.024Z", + "created": "2020-06-15T08:07:20Z", + "updated": "2021-06-02T06:33:30.2164608Z", + "platform": "sentinel-2a", + "constellation": "sentinel-2", + "mission": "sentinel-2", + "instruments": [ + "msi" + ], + "gsd": 10.0, + "sat:orbit_state": "descending", + "sat:absolute_orbit": 26014, + "sat:relative_orbit": 134, + "sat:platform_international_designator": "2015-000A", + "processing:level": "L1C", + "processing:lineage": "Generation of Level-1C User Product", + "title": "Sentinel-2A MSI L1C 15/06/2020 06:06:41", + "eo:bands": [ + { + "name": "coastal", + "description": "coastal 443nm TOA 60", + "common_name": "coastal", + "center_wavelength": 0.4427, + "solar_illumination": 1884.69 + }, + { + "name": "blue", + "description": "blue 493nm TOA 10", + "common_name": "blue", + "center_wavelength": 0.49269999999999997, + "solar_illumination": 1959.66 + }, + { + "name": "green", + "description": "green 560nm TOA 10", + "common_name": "green", + "center_wavelength": 0.5598, + "solar_illumination": 1823.24 + }, + { + "name": "red", + "description": "red 665nm TOA 10", + "common_name": "red", + "center_wavelength": 0.6646, + "solar_illumination": 1512.06 + }, + { + "name": "rededge70", + "description": "rededge 704nm TOA 20", + "common_name": "rededge", + "center_wavelength": 0.7041000000000001, + "solar_illumination": 1424.64 + }, + { + "name": "rededge74", + "description": "rededge 740nm TOA 20", + "common_name": "rededge", + "center_wavelength": 0.7405, + "solar_illumination": 1287.61 + }, + { + "name": "rededge78", + "description": "rededge 783nm TOA 20", + "common_name": "rededge", + "center_wavelength": 0.7827999999999999, + "solar_illumination": 1162.08 + }, + { + "name": "nir", + "description": "nir 833nm TOA 10", + "common_name": "nir", + "center_wavelength": 0.8328, + "solar_illumination": 1041.63 + }, + { + "name": "nir09", + "description": "nir09 945nm TOA 60", + "common_name": "nir09", + "center_wavelength": 0.9451, + "solar_illumination": 812.92 + }, + { + "name": "cirrus", + "description": "cirrus 1374nm TOA 60", + "common_name": "cirrus", + "center_wavelength": 1.3735, + "solar_illumination": 367.15 + }, + { + "name": "swir16", + "description": "swir16 1614nm TOA 20", + "common_name": "swir16", + "center_wavelength": 1.6137000000000001, + "solar_illumination": 245.59 + }, + { + "name": "swir22", + "description": "swir22 2202nm TOA 20", + "common_name": "swir22", + "center_wavelength": 2.2024, + "solar_illumination": 85.25 + }, + { + "name": "nir08", + "description": "nir08 865nm TOA 20", + "common_name": "nir08", + "center_wavelength": 0.8647, + "solar_illumination": 955.32 + } + ], + "disaster:class": "Acquisition", + "cpe:status": { + "stage": "dataset", + "message": null + }, + "disaster:call_ids": [ + 100 + ], + "disaster:sensor_type": "Optical", + "disaster:resolution_class": "HR", + "cpe:cos2Xml": null, + "description": "
\n\n### Sentinel-2A MSI L1C 15/06/2020 06:06:41\n-----------------------\n\n* Platform **sentinel-2a**\n* Instruments **System.String[]**\n* Sensing Time **15/06/2020 06:06:41**\n* Created **15/06/2020 08:07:20**\n* Updated **02/06/2021 06:33:08**\n", + "cpe:cos2Id": "100-ESA-SENTINEL2-S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720", + "version": "3" + }, + "bbox": [ + 86.99949043202683, + 68.4035546435041, + 89.79493686530785, + 69.40964050938386 + ], + "assets": { + "mtd": { + "type": "application/xml", + "roles": [ + "metadata" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/MTD_MSIL1C.xml", + "file:size": 44162 + }, + "B01": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_B01.jp2", + "gsd": 60.0, + "eo:bands": [ + { + "name": "coastal", + "description": "coastal 443nm TOA 60", + "common_name": "coastal", + "center_wavelength": 0.4427, + "solar_illumination": 1884.69 + } + ], + "raster:bands": [ + { + "nodata": 0.0, + "spatial_resolution": 60.0, + "statistics": { + "minimum": 0.0, + "maximum": 10000.0 + } + } + ], + "file:size": 3761460 + }, + "B02": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_B02.jp2", + "gsd": 10.0, + "eo:bands": [ + { + "name": "blue", + "description": "blue 493nm TOA 10", + "common_name": "blue", + "center_wavelength": 0.49269999999999997, + "solar_illumination": 1959.66 + } + ], + "raster:bands": [ + { + "nodata": 0.0, + "spatial_resolution": 10.0, + "statistics": { + "minimum": 0.0, + "maximum": 10000.0 + } + } + ], + "file:size": 104626138 + }, + "B03": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_B03.jp2", + "gsd": 10.0, + "eo:bands": [ + { + "name": "green", + "description": "green 560nm TOA 10", + "common_name": "green", + "center_wavelength": 0.5598, + "solar_illumination": 1823.24 + } + ], + "raster:bands": [ + { + "nodata": 0.0, + "spatial_resolution": 10.0, + "statistics": { + "minimum": 0.0, + "maximum": 10000.0 + } + } + ], + "file:size": 105510111 + }, + "B04": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_B04.jp2", + "gsd": 10.0, + "eo:bands": [ + { + "name": "red", + "description": "red 665nm TOA 10", + "common_name": "red", + "center_wavelength": 0.6646, + "solar_illumination": 1512.06 + } + ], + "raster:bands": [ + { + "nodata": 0.0, + "spatial_resolution": 10.0, + "statistics": { + "minimum": 0.0, + "maximum": 10000.0 + } + } + ], + "file:size": 109843887 + }, + "B05": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_B05.jp2", + "gsd": 20.0, + "eo:bands": [ + { + "name": "rededge70", + "description": "rededge 704nm TOA 20", + "common_name": "rededge", + "center_wavelength": 0.7041000000000001, + "solar_illumination": 1424.64 + } + ], + "raster:bands": [ + { + "nodata": 0.0, + "spatial_resolution": 20.0, + "statistics": { + "minimum": 0.0, + "maximum": 10000.0 + } + } + ], + "file:size": 32686974 + }, + "B06": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_B06.jp2", + "gsd": 20.0, + "eo:bands": [ + { + "name": "rededge74", + "description": "rededge 740nm TOA 20", + "common_name": "rededge", + "center_wavelength": 0.7405, + "solar_illumination": 1287.61 + } + ], + "raster:bands": [ + { + "nodata": 0.0, + "spatial_resolution": 20.0, + "statistics": { + "minimum": 0.0, + "maximum": 10000.0 + } + } + ], + "file:size": 33739533 + }, + "B07": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_B07.jp2", + "gsd": 20.0, + "eo:bands": [ + { + "name": "rededge78", + "description": "rededge 783nm TOA 20", + "common_name": "rededge", + "center_wavelength": 0.7827999999999999, + "solar_illumination": 1162.08 + } + ], + "raster:bands": [ + { + "nodata": 0.0, + "spatial_resolution": 20.0, + "statistics": { + "minimum": 0.0, + "maximum": 10000.0 + } + } + ], + "file:size": 33756313 + }, + "B08": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_B08.jp2", + "gsd": 10.0, + "eo:bands": [ + { + "name": "nir", + "description": "nir 833nm TOA 10", + "common_name": "nir", + "center_wavelength": 0.8328, + "solar_illumination": 1041.63 + } + ], + "raster:bands": [ + { + "nodata": 0.0, + "spatial_resolution": 10.0, + "statistics": { + "minimum": 0.0, + "maximum": 10000.0 + } + } + ], + "file:size": 120382989 + }, + "B09": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_B09.jp2", + "gsd": 60.0, + "eo:bands": [ + { + "name": "nir09", + "description": "nir09 945nm TOA 60", + "common_name": "nir09", + "center_wavelength": 0.9451, + "solar_illumination": 812.92 + } + ], + "raster:bands": [ + { + "nodata": 0.0, + "spatial_resolution": 60.0, + "statistics": { + "minimum": 0.0, + "maximum": 10000.0 + } + } + ], + "file:size": 3747702 + }, + "B10": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_B10.jp2", + "gsd": 60.0, + "eo:bands": [ + { + "name": "cirrus", + "description": "cirrus 1374nm TOA 60", + "common_name": "cirrus", + "center_wavelength": 1.3735, + "solar_illumination": 367.15 + } + ], + "raster:bands": [ + { + "nodata": 0.0, + "spatial_resolution": 60.0, + "statistics": { + "minimum": 0.0, + "maximum": 10000.0 + } + } + ], + "file:size": 2418729 + }, + "B11": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_B11.jp2", + "gsd": 20.0, + "eo:bands": [ + { + "name": "swir16", + "description": "swir16 1614nm TOA 20", + "common_name": "swir16", + "center_wavelength": 1.6137000000000001, + "solar_illumination": 245.59 + } + ], + "raster:bands": [ + { + "nodata": 0.0, + "spatial_resolution": 20.0, + "statistics": { + "minimum": 0.0, + "maximum": 10000.0 + } + } + ], + "file:size": 32410828 + }, + "B12": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_B12.jp2", + "gsd": 20.0, + "eo:bands": [ + { + "name": "swir22", + "description": "swir22 2202nm TOA 20", + "common_name": "swir22", + "center_wavelength": 2.2024, + "solar_illumination": 85.25 + } + ], + "raster:bands": [ + { + "nodata": 0.0, + "spatial_resolution": 20.0, + "statistics": { + "minimum": 0.0, + "maximum": 10000.0 + } + } + ], + "file:size": 31164855 + }, + "B8A": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_B8A.jp2", + "gsd": 20.0, + "eo:bands": [ + { + "name": "nir08", + "description": "nir08 865nm TOA 20", + "common_name": "nir08", + "center_wavelength": 0.8647, + "solar_illumination": 955.32 + } + ], + "raster:bands": [ + { + "nodata": 0.0, + "spatial_resolution": 20.0, + "statistics": { + "minimum": 0.0, + "maximum": 10000.0 + } + } + ], + "file:size": 33717292 + }, + "PVI": { + "type": "image/jp2", + "roles": [ + "overview" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/QI_DATA/T45WWS_20200615T060641_PVI.jp2", + "file:size": 166248 + }, + "TCI": { + "type": "image/jp2", + "roles": [ + "data", + "reflectance" + ], + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/GRANULE/L1C_T45WWS_A026014_20200615T060638/IMG_DATA/T45WWS_20200615T060641_TCI.jp2", + "file:size": 135093974 + }, + "manifest": { + "type": "text/xml", + "roles": [ + "metadata" + ], + "title": "SAFE Manifest", + "href": "S2A_MSIL1C_20200615T060641_N0209_R134_T45WWS_20200615T080720.SAFE/manifest.safe", + "file:size": 73239 + } + }, + "links": [] +} \ No newline at end of file diff --git a/tests/extensions/test_eo.py b/tests/extensions/test_eo.py index ffcf51799..1eb470ad4 100644 --- a/tests/extensions/test_eo.py +++ b/tests/extensions/test_eo.py @@ -42,6 +42,7 @@ class EOTest(unittest.TestCase): "data-files/eo/sample-bands-in-item-properties.json" ) EO_COLLECTION_URI = TestCases.get_path("data-files/eo/eo-collection.json") + S2_ITEM_URI = TestCases.get_path("data-files/eo/eo-sentinel2-item.json") def setUp(self) -> None: self.maxDiff = None @@ -83,6 +84,13 @@ def test_bands(self) -> None: self.assertEqual(len(EOExtension.ext(item).bands or []), 3) item.validate() + def test_asset_bands_s2(self) -> None: + item = pystac.Item.from_file(self.S2_ITEM_URI) + for key, asset in item.get_assets().items(): + eo_bands = EOExtension.ext(asset).bands + if key == "mtd": + assert eo_bands is None + def test_asset_bands(self) -> None: item = pystac.Item.from_file(self.LANDSAT_EXAMPLE_URI) From 27ac61355ef0a41511ab9ec581788cd4a2d00eca Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 6 Jun 2021 21:09:35 +0200 Subject: [PATCH 2/5] updated changelog with PR link --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6f2fa233..558aa54ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ ### Fixed -- Fixed returned None by `EOExtension.get_bands` for asset without EO bands +- Fixed returned None by `EOExtension.get_bands` for asset without EO bands ([#406](https://github.com/stac-utils/pystac/pull/406)) ### Removed From 217a0988c06bef90539e3ee377255bf5538fb9fe Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Mon, 7 Jun 2021 15:37:43 +0200 Subject: [PATCH 3/5] remove test loop Co-authored-by: Jon Duckworth --- tests/extensions/test_eo.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/extensions/test_eo.py b/tests/extensions/test_eo.py index 1eb470ad4..c7f6e507f 100644 --- a/tests/extensions/test_eo.py +++ b/tests/extensions/test_eo.py @@ -86,11 +86,8 @@ def test_bands(self) -> None: def test_asset_bands_s2(self) -> None: item = pystac.Item.from_file(self.S2_ITEM_URI) - for key, asset in item.get_assets().items(): - eo_bands = EOExtension.ext(asset).bands - if key == "mtd": - assert eo_bands is None - + mtd_asset = item.get_assets()["mtd"] + self.assertIsNone(EOExtension.ext(asset).bands) def test_asset_bands(self) -> None: item = pystac.Item.from_file(self.LANDSAT_EXAMPLE_URI) From 1c806b3fe072ba6282fd892182cbc503bdd50213 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Mon, 7 Jun 2021 15:39:21 +0200 Subject: [PATCH 4/5] mtd_asset --- tests/extensions/test_eo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/extensions/test_eo.py b/tests/extensions/test_eo.py index c7f6e507f..30d52e827 100644 --- a/tests/extensions/test_eo.py +++ b/tests/extensions/test_eo.py @@ -87,7 +87,7 @@ def test_bands(self) -> None: def test_asset_bands_s2(self) -> None: item = pystac.Item.from_file(self.S2_ITEM_URI) mtd_asset = item.get_assets()["mtd"] - self.assertIsNone(EOExtension.ext(asset).bands) + self.assertIsNone(EOExtension.ext(mtd_asset).bands) def test_asset_bands(self) -> None: item = pystac.Item.from_file(self.LANDSAT_EXAMPLE_URI) From fcee6cd5ee3d2b312b086e1977eed19a4a51f8d7 Mon Sep 17 00:00:00 2001 From: Jon Duckworth Date: Mon, 7 Jun 2021 09:51:29 -0400 Subject: [PATCH 5/5] Fix lint error --- tests/extensions/test_eo.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/extensions/test_eo.py b/tests/extensions/test_eo.py index 30d52e827..def803788 100644 --- a/tests/extensions/test_eo.py +++ b/tests/extensions/test_eo.py @@ -88,6 +88,7 @@ def test_asset_bands_s2(self) -> None: item = pystac.Item.from_file(self.S2_ITEM_URI) mtd_asset = item.get_assets()["mtd"] self.assertIsNone(EOExtension.ext(mtd_asset).bands) + def test_asset_bands(self) -> None: item = pystac.Item.from_file(self.LANDSAT_EXAMPLE_URI)