From 8364d5367f2461baa45db43e7930e5214188e01e Mon Sep 17 00:00:00 2001 From: clausmichele Date: Tue, 27 Jun 2023 15:27:02 +0200 Subject: [PATCH 01/13] First draft of load_stac --- .../process_implementations/cubes/__init__.py | 1 + .../process_implementations/cubes/load.py | 144 ++++++++++++++++++ .../process_implementations/exceptions.py | 8 + 3 files changed, 153 insertions(+) create mode 100644 openeo_processes_dask/process_implementations/cubes/load.py diff --git a/openeo_processes_dask/process_implementations/cubes/__init__.py b/openeo_processes_dask/process_implementations/cubes/__init__.py index a6df05fc..aa4ade45 100644 --- a/openeo_processes_dask/process_implementations/cubes/__init__.py +++ b/openeo_processes_dask/process_implementations/cubes/__init__.py @@ -2,5 +2,6 @@ from .aggregate import * from .apply import * from .general import * +from .load import * from .merge import * from .reduce import * diff --git a/openeo_processes_dask/process_implementations/cubes/load.py b/openeo_processes_dask/process_implementations/cubes/load.py new file mode 100644 index 00000000..ae74e5d4 --- /dev/null +++ b/openeo_processes_dask/process_implementations/cubes/load.py @@ -0,0 +1,144 @@ +import datetime +from collections.abc import Iterator +from pathlib import PurePosixPath +from typing import Any, Callable, Dict, List, Optional, Tuple, Union +from urllib.parse import unquote, urljoin, urlparse + +import planetary_computer as pc +import pyproj +import pystac_client +import stackstac +import xarray as xr +from openeo_pg_parser_networkx.pg_schema import * +from stac_validator import stac_validator + +from openeo_processes_dask.process_implementations.cubes._filter import ( + _reproject_bbox, + filter_bands, + filter_bbox, +) +from openeo_processes_dask.process_implementations.data_model import RasterCube +from openeo_processes_dask.process_implementations.exceptions import ( + NoDataAvailable, + TemporalExtentEmpty, +) + +# "NoDataAvailable": { +# "message": "There is no data available for the given extents." +# }, +# "TemporalExtentEmpty": { +# "message": "The temporal extent is empty. The second instant in time must always be greater/later than the first instant in time." +# } +__all__ = ["load_stac"] + + +def load_stac( + url: str, + spatial_extent: BoundingBox = None, + temporal_extent: Optional[ + list[Union[str, datetime.datetime, datetime.date]] + ] = None, + bands: Optional[list[str]] = None, + properties: Optional[dict] = None, + **kwargs, +) -> RasterCube: + modifier = kwargs["modifier"] if "modifier" in kwargs else None + + def validate_stac(url): + stac = stac_validator.StacValidate(url) + is_valid_stac = stac.run() + if not is_valid_stac: + raise Exception( + f"The provided link is not a valid STAC. stac-validator message: {stac.message}" + ) + if len(stac.message) == 1: + try: + asset_type = stac.message[0]["asset_type"] + except: + raise Exception(f"stac-validator returned an error: {stac.message}") + else: + raise Exception( + f"stac-validator returned multiple items, not supported yet. {stac.message}" + ) + return is_valid_stac, asset_type + + is_valid_stac, asset_type = validate_stac(url) + + if not is_valid_stac: + raise Exception("The provided link is not a valid STAC.") + + if asset_type == "COLLECTION": + # If query parameters are passed, try to get the parent Catalog if possible/exists, to use the /search endpoint + if spatial_extent or temporal_extent or bands or properties: + # If query parameters are passed, try to get the parent Catalog if possible/exists, to use the /search endpoint + # url = "https://earth-search.aws.element84.com/v0/collections/sentinel-s2-l2a-cogs" + parsed_url = urlparse(url) + root_url = parsed_url.scheme + "://" + parsed_url.netloc + catalog_url = root_url + url_parts = PurePosixPath(unquote(parsed_url.path)).parts + collection_id = url_parts[-1] + for p in url_parts: + if p != "/": + catalog_url = catalog_url + "/" + p + try: + is_valid_stac, asset_type = validate_stac(catalog_url) + except: + continue + if is_valid_stac and asset_type == "CATALOG": + break + if not is_valid_stac: + raise Exception( + "It was not possible to find the root STAC Catalog starting from the provided Collection." + ) + + ## Check if we are connecting to Microsoft Planetary Computer, where we need to sign the connection + modifier = pc.sign_inplace if "planetarycomputer" in catalog_url else None + + catalog = pystac_client.Client.open(catalog_url, modifier=modifier) + + query_params = {"collections": [collection_id]} + + if spatial_extent is not None: + try: + spatial_extent_4326 = spatial_extent + if spatial_extent.crs is not None: + if not pyproj.crs.CRS(spatial_extent.crs).equals("EPSG:4326"): + spatial_extent_4326 = _reproject_bbox( + spatial_extent, "EPSG:4326" + ) + bbox = [ + spatial_extent_4326.west, + spatial_extent_4326.south, + spatial_extent_4326.east, + spatial_extent_4326.north, + ] + query_params["bbox"] = bbox + except Exception as e: + raise Exception(f"Unable to parse the provided spatial extent: {e}") + + if temporal_extent is not None: + query_params["datetime"] = [ + str(temporal_extent[0].to_numpy()), + str(temporal_extent[1].to_numpy()), + ] + + if properties is not None: + query_params["query"] = properties + + items = catalog.search(**query_params).get_all_items() + stack = stackstac.stack(items) + if spatial_extent is not None: + stack = filter_bbox(stack, spatial_extent) + if bands is not None: + stack = filter_bands(stack, bands) + + return stack + else: + # Load the whole collection wihout filters + raise Exception( + f"No parameters for filtering provided. Loading the whole STAC Collection is not supported yet." + ) + else: + raise Exception( + f"The provided URL is a STAC {asset_type}, which is not yet supported. Please provide a valid URL to a STAC Collection." + ) diff --git a/openeo_processes_dask/process_implementations/exceptions.py b/openeo_processes_dask/process_implementations/exceptions.py index 8e8f5947..42a67bd5 100644 --- a/openeo_processes_dask/process_implementations/exceptions.py +++ b/openeo_processes_dask/process_implementations/exceptions.py @@ -56,3 +56,11 @@ class DimensionMissing(OpenEOException): class BandFilterParameterMissing(OpenEOException): pass + + +class NoDataAvailable(OpenEOException): + pass + + +class TemporalExtentEmpty(OpenEOException): + pass From 0fef5619c73c77fceb31365c312977027d78c58a Mon Sep 17 00:00:00 2001 From: clausmichele Date: Tue, 27 Jun 2023 17:01:53 +0200 Subject: [PATCH 02/13] add load_stac test --- tests/test_load_stac.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/test_load_stac.py diff --git a/tests/test_load_stac.py b/tests/test_load_stac.py new file mode 100644 index 00000000..87311263 --- /dev/null +++ b/tests/test_load_stac.py @@ -0,0 +1,18 @@ +import pytest + +from openeo_processes_dask.process_implementations.cubes.load import load_stac + + +def test_load_stac(temporal_interval, bounding_box): + url = "https://earth-search.aws.element84.com/v0/collections/sentinel-s2-l2a-cogs" + output_cube = load_stac( + url=url, + spatial_extent=bounding_box, + temporal_extent=temporal_interval, + bands=["B02", "B03", "B04", "B08"], + ) + + assert output_cube.openeo is not None + assert len(output_cube[output_cube.openeo.x_dim]) > 0 + assert len(output_cube[output_cube.openeo.y_dim]) > 0 + assert len(output_cube[output_cube.openeo.temporal_dims[0]]) > 0 From 4b860561cb28b948dc0228ee3f5488bdd28fddd1 Mon Sep 17 00:00:00 2001 From: clausmichele Date: Tue, 27 Jun 2023 17:16:05 +0200 Subject: [PATCH 03/13] added stac related libraries --- pyproject.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9a53dbd8..74eceec3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,10 @@ rioxarray = { version = ">=0.12.0,<1", optional = true } odc-algo = { version = "==0.2.3", optional = true } openeo-pg-parser-networkx = { version = ">=2023.5.1", optional = true } odc-geo = { version = "^0.3.2", optional = true } +stac_validator = { version = ">=3.3.1", optional = true } +stackstac = { version = ">=0.4.3", optional = true } +pystac_client = { version = ">=0.6.1", optional = true } +planetary_computer = { version = ">=0.5.1", optional = true } [tool.poetry.group.dev.dependencies] pytest = "^7.2.0" @@ -45,7 +49,7 @@ pre-commit = "^2.20.0" pytest-cov = "^4.0.0" [tool.poetry.extras] -implementations = ["geopandas", "xarray", "dask", "rasterio", "dask-geopandas", "rioxarray", "openeo-pg-parser-networkx", "odc-geo"] +implementations = ["geopandas", "xarray", "dask", "rasterio", "dask-geopandas", "rioxarray", "openeo-pg-parser-networkx", "odc-geo", "stackstac", "planetary_computer", "pystac_client", "stac_validator"] experimental = ["odc-algo"] ml = ["xgboost"] From 61c593798a2eb11d3e18bcd97aece4f1c0d98777 Mon Sep 17 00:00:00 2001 From: clausmichele Date: Wed, 28 Jun 2023 17:52:02 +0200 Subject: [PATCH 04/13] Fix test --- openeo_processes_dask/process_implementations/cubes/load.py | 2 +- tests/test_load_stac.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/openeo_processes_dask/process_implementations/cubes/load.py b/openeo_processes_dask/process_implementations/cubes/load.py index ae74e5d4..6ae45e3c 100644 --- a/openeo_processes_dask/process_implementations/cubes/load.py +++ b/openeo_processes_dask/process_implementations/cubes/load.py @@ -125,7 +125,7 @@ def validate_stac(url): if properties is not None: query_params["query"] = properties - items = catalog.search(**query_params).get_all_items() + items = catalog.search(**query_params).item_collection() stack = stackstac.stack(items) if spatial_extent is not None: stack = filter_bbox(stack, spatial_extent) diff --git a/tests/test_load_stac.py b/tests/test_load_stac.py index 87311263..9c6cd5ca 100644 --- a/tests/test_load_stac.py +++ b/tests/test_load_stac.py @@ -4,12 +4,14 @@ def test_load_stac(temporal_interval, bounding_box): - url = "https://earth-search.aws.element84.com/v0/collections/sentinel-s2-l2a-cogs" + url = ( + "https://planetarycomputer.microsoft.com/api/stac/v1/collections/landsat-c2-l2" + ) output_cube = load_stac( url=url, spatial_extent=bounding_box, temporal_extent=temporal_interval, - bands=["B02", "B03", "B04", "B08"], + bands=["red"], ) assert output_cube.openeo is not None From 2f9d1175c7f0341b8fe6ee00bd6e1a7c97ce9ed8 Mon Sep 17 00:00:00 2001 From: clausmichele Date: Fri, 30 Jun 2023 17:03:37 +0200 Subject: [PATCH 05/13] allow load_stac ITEM --- .../process_implementations/cubes/load.py | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/openeo_processes_dask/process_implementations/cubes/load.py b/openeo_processes_dask/process_implementations/cubes/load.py index 6ae45e3c..c085bc4a 100644 --- a/openeo_processes_dask/process_implementations/cubes/load.py +++ b/openeo_processes_dask/process_implementations/cubes/load.py @@ -1,4 +1,5 @@ import datetime +import json from collections.abc import Iterator from pathlib import PurePosixPath from typing import Any, Callable, Dict, List, Optional, Tuple, Union @@ -16,6 +17,7 @@ _reproject_bbox, filter_bands, filter_bbox, + filter_temporal, ) from openeo_processes_dask.process_implementations.data_model import RasterCube from openeo_processes_dask.process_implementations.exceptions import ( @@ -42,8 +44,6 @@ def load_stac( properties: Optional[dict] = None, **kwargs, ) -> RasterCube: - modifier = kwargs["modifier"] if "modifier" in kwargs else None - def validate_stac(url): stac = stac_validator.StacValidate(url) is_valid_stac = stac.run() @@ -126,18 +126,38 @@ def validate_stac(url): query_params["query"] = properties items = catalog.search(**query_params).item_collection() - stack = stackstac.stack(items) + assets = None + if bands is not None: + assets = bands + stack = stackstac.stack(items, assets=assets) if spatial_extent is not None: stack = filter_bbox(stack, spatial_extent) - if bands is not None: - stack = filter_bands(stack, bands) - return stack + else: # Load the whole collection wihout filters raise Exception( f"No parameters for filtering provided. Loading the whole STAC Collection is not supported yet." ) + + elif asset_type == "ITEM": + stac_api = pystac_client.stac_api_io.StacApiIO() + stac_dict = json.loads(stac_api.read_text(url)) + stac_item = stac_api.stac_object_from_dict(stac_dict) + + assets = None + if bands is not None: + assets = bands + + stack = stackstac.stack(stac_item, assets=assets) + + if spatial_extent is not None: + stack = filter_bbox(stack, spatial_extent) + + if temporal_extent is not None: + stack = filter_temporal(stack, temporal_extent) + return stack + else: raise Exception( f"The provided URL is a STAC {asset_type}, which is not yet supported. Please provide a valid URL to a STAC Collection." From e18242f3319974057f4ca5b97cfdda373c699430 Mon Sep 17 00:00:00 2001 From: clausmichele Date: Wed, 5 Jul 2023 15:42:52 +0200 Subject: [PATCH 06/13] assets should not be None --- openeo_processes_dask/process_implementations/cubes/load.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openeo_processes_dask/process_implementations/cubes/load.py b/openeo_processes_dask/process_implementations/cubes/load.py index c085bc4a..1bb1ecfd 100644 --- a/openeo_processes_dask/process_implementations/cubes/load.py +++ b/openeo_processes_dask/process_implementations/cubes/load.py @@ -126,10 +126,10 @@ def validate_stac(url): query_params["query"] = properties items = catalog.search(**query_params).item_collection() - assets = None if bands is not None: - assets = bands - stack = stackstac.stack(items, assets=assets) + stack = stackstac.stack(items, assets=bands) + else: + stack = stackstac.stack(items) if spatial_extent is not None: stack = filter_bbox(stack, spatial_extent) return stack From 38891124deb3341efd29770d7f81b5f03bcff8d8 Mon Sep 17 00:00:00 2001 From: clausmichele Date: Mon, 10 Jul 2023 11:09:51 +0200 Subject: [PATCH 07/13] Edits after review --- .../process_implementations/cubes/load.py | 72 ++++++++++--------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/openeo_processes_dask/process_implementations/cubes/load.py b/openeo_processes_dask/process_implementations/cubes/load.py index 1bb1ecfd..44a51038 100644 --- a/openeo_processes_dask/process_implementations/cubes/load.py +++ b/openeo_processes_dask/process_implementations/cubes/load.py @@ -1,5 +1,6 @@ import datetime import json +import logging from collections.abc import Iterator from pathlib import PurePosixPath from typing import Any, Callable, Dict, List, Optional, Tuple, Union @@ -10,7 +11,7 @@ import pystac_client import stackstac import xarray as xr -from openeo_pg_parser_networkx.pg_schema import * +from openeo_pg_parser_networkx.pg_schema import BoundingBox, TemporalInterval from stac_validator import stac_validator from openeo_processes_dask.process_implementations.cubes._filter import ( @@ -33,39 +34,38 @@ # } __all__ = ["load_stac"] +logger = logging.getLogger(__name__) + + +def _validate_stac(url): + logger.debug(f"Validating the provided STAC url: {url}") + stac = stac_validator.StacValidate(url) + is_valid_stac = stac.run() + if not is_valid_stac: + raise Exception( + f"The provided link is not a valid STAC. stac-validator message: {stac.message}" + ) + if len(stac.message) == 1: + try: + asset_type = stac.message[0]["asset_type"] + except: + raise Exception(f"stac-validator returned an error: {stac.message}") + else: + raise Exception( + f"stac-validator returned multiple items, not supported yet. {stac.message}" + ) + return asset_type + def load_stac( url: str, spatial_extent: BoundingBox = None, - temporal_extent: Optional[ - list[Union[str, datetime.datetime, datetime.date]] - ] = None, + temporal_extent: Optional[TemporalInterval] = None, bands: Optional[list[str]] = None, properties: Optional[dict] = None, **kwargs, ) -> RasterCube: - def validate_stac(url): - stac = stac_validator.StacValidate(url) - is_valid_stac = stac.run() - if not is_valid_stac: - raise Exception( - f"The provided link is not a valid STAC. stac-validator message: {stac.message}" - ) - if len(stac.message) == 1: - try: - asset_type = stac.message[0]["asset_type"] - except: - raise Exception(f"stac-validator returned an error: {stac.message}") - else: - raise Exception( - f"stac-validator returned multiple items, not supported yet. {stac.message}" - ) - return is_valid_stac, asset_type - - is_valid_stac, asset_type = validate_stac(url) - - if not is_valid_stac: - raise Exception("The provided link is not a valid STAC.") + asset_type = _validate_stac(url) if asset_type == "COLLECTION": # If query parameters are passed, try to get the parent Catalog if possible/exists, to use the /search endpoint @@ -81,12 +81,13 @@ def validate_stac(url): if p != "/": catalog_url = catalog_url + "/" + p try: - is_valid_stac, asset_type = validate_stac(catalog_url) - except: + asset_type = _validate_stac(catalog_url) + except Exception as e: + logger.debug(e) continue - if is_valid_stac and asset_type == "CATALOG": + if asset_type == "CATALOG": break - if not is_valid_stac: + if asset_type != "CATALOG": raise Exception( "It was not possible to find the root STAC Catalog starting from the provided Collection." ) @@ -117,10 +118,13 @@ def validate_stac(url): raise Exception(f"Unable to parse the provided spatial extent: {e}") if temporal_extent is not None: - query_params["datetime"] = [ - str(temporal_extent[0].to_numpy()), - str(temporal_extent[1].to_numpy()), - ] + start_date = None + end_date = None + if temporal_extent[0] is not None: + start_date = str(temporal_extent[0].to_numpy()) + if temporal_extent[1] is not None: + end_date = str(temporal_extent[1].to_numpy()) + query_params["datetime"] = [start_date, end_date] if properties is not None: query_params["query"] = properties From c608433538eb1e7765cb7467916f12799b97e04e Mon Sep 17 00:00:00 2001 From: clausmichele <31700619+clausmichele@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:11:22 +0200 Subject: [PATCH 08/13] Update openeo_processes_dask/process_implementations/cubes/load.py Co-authored-by: Lukas Weidenholzer <17790923+LukeWeidenwalker@users.noreply.github.com> --- openeo_processes_dask/process_implementations/cubes/load.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openeo_processes_dask/process_implementations/cubes/load.py b/openeo_processes_dask/process_implementations/cubes/load.py index 44a51038..524c0dce 100644 --- a/openeo_processes_dask/process_implementations/cubes/load.py +++ b/openeo_processes_dask/process_implementations/cubes/load.py @@ -59,7 +59,7 @@ def _validate_stac(url): def load_stac( url: str, - spatial_extent: BoundingBox = None, + spatial_extent: Optional[BoundingBox] = None, temporal_extent: Optional[TemporalInterval] = None, bands: Optional[list[str]] = None, properties: Optional[dict] = None, From bcd0b4f121d3059c34ac845831d0eef6f58ef9a3 Mon Sep 17 00:00:00 2001 From: clausmichele <31700619+clausmichele@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:11:40 +0200 Subject: [PATCH 09/13] Update openeo_processes_dask/process_implementations/cubes/load.py Co-authored-by: Lukas Weidenholzer <17790923+LukeWeidenwalker@users.noreply.github.com> --- openeo_processes_dask/process_implementations/cubes/load.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openeo_processes_dask/process_implementations/cubes/load.py b/openeo_processes_dask/process_implementations/cubes/load.py index 524c0dce..fb7b29d3 100644 --- a/openeo_processes_dask/process_implementations/cubes/load.py +++ b/openeo_processes_dask/process_implementations/cubes/load.py @@ -63,7 +63,6 @@ def load_stac( temporal_extent: Optional[TemporalInterval] = None, bands: Optional[list[str]] = None, properties: Optional[dict] = None, - **kwargs, ) -> RasterCube: asset_type = _validate_stac(url) From 7b9f120f9b29b7829613d61e3f175700024b1297 Mon Sep 17 00:00:00 2001 From: clausmichele Date: Tue, 11 Jul 2023 15:14:53 +0200 Subject: [PATCH 10/13] Apply review suggestions --- .../process_implementations/cubes/load.py | 83 +++++++++---------- 1 file changed, 40 insertions(+), 43 deletions(-) diff --git a/openeo_processes_dask/process_implementations/cubes/load.py b/openeo_processes_dask/process_implementations/cubes/load.py index fb7b29d3..caf385e3 100644 --- a/openeo_processes_dask/process_implementations/cubes/load.py +++ b/openeo_processes_dask/process_implementations/cubes/load.py @@ -57,6 +57,29 @@ def _validate_stac(url): return asset_type +def _search_for_parent_catalog(url): + parsed_url = urlparse(url) + root_url = parsed_url.scheme + "://" + parsed_url.netloc + catalog_url = root_url + url_parts = PurePosixPath(unquote(parsed_url.path)).parts + collection_id = url_parts[-1] + for p in url_parts: + if p != "/": + catalog_url = catalog_url + "/" + p + try: + asset_type = _validate_stac(catalog_url) + except Exception as e: + logger.debug(e) + continue + if asset_type == "CATALOG": + break + if asset_type != "CATALOG": + raise Exception( + "It was not possible to find the root STAC Catalog starting from the provided Collection." + ) + return catalog_url + + def load_stac( url: str, spatial_extent: Optional[BoundingBox] = None, @@ -70,28 +93,9 @@ def load_stac( # If query parameters are passed, try to get the parent Catalog if possible/exists, to use the /search endpoint if spatial_extent or temporal_extent or bands or properties: # If query parameters are passed, try to get the parent Catalog if possible/exists, to use the /search endpoint - # url = "https://earth-search.aws.element84.com/v0/collections/sentinel-s2-l2a-cogs" - parsed_url = urlparse(url) - root_url = parsed_url.scheme + "://" + parsed_url.netloc - catalog_url = root_url - url_parts = PurePosixPath(unquote(parsed_url.path)).parts - collection_id = url_parts[-1] - for p in url_parts: - if p != "/": - catalog_url = catalog_url + "/" + p - try: - asset_type = _validate_stac(catalog_url) - except Exception as e: - logger.debug(e) - continue - if asset_type == "CATALOG": - break - if asset_type != "CATALOG": - raise Exception( - "It was not possible to find the root STAC Catalog starting from the provided Collection." - ) - - ## Check if we are connecting to Microsoft Planetary Computer, where we need to sign the connection + catalog_url = _search_for_parent_catalog(url) + + # Check if we are connecting to Microsoft Planetary Computer, where we need to sign the connection modifier = pc.sign_inplace if "planetarycomputer" in catalog_url else None catalog = pystac_client.Client.open(catalog_url, modifier=modifier) @@ -129,13 +133,6 @@ def load_stac( query_params["query"] = properties items = catalog.search(**query_params).item_collection() - if bands is not None: - stack = stackstac.stack(items, assets=bands) - else: - stack = stackstac.stack(items) - if spatial_extent is not None: - stack = filter_bbox(stack, spatial_extent) - return stack else: # Load the whole collection wihout filters @@ -146,22 +143,22 @@ def load_stac( elif asset_type == "ITEM": stac_api = pystac_client.stac_api_io.StacApiIO() stac_dict = json.loads(stac_api.read_text(url)) - stac_item = stac_api.stac_object_from_dict(stac_dict) + items = stac_api.stac_object_from_dict(stac_dict) - assets = None - if bands is not None: - assets = bands + else: + raise Exception( + f"The provided URL is a STAC {asset_type}, which is not yet supported. Please provide a valid URL to a STAC Collection or Item." + ) - stack = stackstac.stack(stac_item, assets=assets) + if bands is not None: + stack = stackstac.stack(items, assets=bands) + else: + stack = stackstac.stack(items) - if spatial_extent is not None: - stack = filter_bbox(stack, spatial_extent) + if spatial_extent is not None: + stack = filter_bbox(stack, spatial_extent) - if temporal_extent is not None: - stack = filter_temporal(stack, temporal_extent) - return stack + if temporal_extent is not None and asset_type == "ITEM": + stack = filter_temporal(stack, temporal_extent) - else: - raise Exception( - f"The provided URL is a STAC {asset_type}, which is not yet supported. Please provide a valid URL to a STAC Collection." - ) + return stack From 18e02b2d8b512d67c81cf4dce711568d9edbbd24 Mon Sep 17 00:00:00 2001 From: clausmichele Date: Tue, 11 Jul 2023 16:01:55 +0200 Subject: [PATCH 11/13] bugfix --- openeo_processes_dask/process_implementations/cubes/load.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openeo_processes_dask/process_implementations/cubes/load.py b/openeo_processes_dask/process_implementations/cubes/load.py index caf385e3..4e195079 100644 --- a/openeo_processes_dask/process_implementations/cubes/load.py +++ b/openeo_processes_dask/process_implementations/cubes/load.py @@ -77,7 +77,7 @@ def _search_for_parent_catalog(url): raise Exception( "It was not possible to find the root STAC Catalog starting from the provided Collection." ) - return catalog_url + return catalog_url, collection_id def load_stac( @@ -93,7 +93,7 @@ def load_stac( # If query parameters are passed, try to get the parent Catalog if possible/exists, to use the /search endpoint if spatial_extent or temporal_extent or bands or properties: # If query parameters are passed, try to get the parent Catalog if possible/exists, to use the /search endpoint - catalog_url = _search_for_parent_catalog(url) + catalog_url, collection_id = _search_for_parent_catalog(url) # Check if we are connecting to Microsoft Planetary Computer, where we need to sign the connection modifier = pc.sign_inplace if "planetarycomputer" in catalog_url else None From 3cf8360ad19fa7fc3a4ca9e830f23c555d9b2fd7 Mon Sep 17 00:00:00 2001 From: clausmichele Date: Wed, 12 Jul 2023 09:41:27 +0200 Subject: [PATCH 12/13] Using local file for test --- tests/data/stac/s2_l2a_test_item.json | 1411 +++++++++++++++++++++++++ tests/test_load_stac.py | 8 +- 2 files changed, 1414 insertions(+), 5 deletions(-) create mode 100644 tests/data/stac/s2_l2a_test_item.json diff --git a/tests/data/stac/s2_l2a_test_item.json b/tests/data/stac/s2_l2a_test_item.json new file mode 100644 index 00000000..911fa09a --- /dev/null +++ b/tests/data/stac/s2_l2a_test_item.json @@ -0,0 +1,1411 @@ +{ + "type": "Feature", + "stac_version": "1.0.0", + "id": "S2A_32TPS_20230602_0_L2A", + "properties": { + "created": "2023-06-02T21:15:52.254Z", + "platform": "sentinel-2a", + "constellation": "sentinel-2", + "instruments": [ + "msi" + ], + "eo:cloud_cover": 31.735489, + "proj:epsg": 32632, + "mgrs:utm_zone": 32, + "mgrs:latitude_band": "T", + "mgrs:grid_square": "PS", + "grid:code": "MGRS-32TPS", + "view:sun_azimuth": 150.260515978055, + "view:sun_elevation": 63.2305935349323, + "s2:degraded_msi_data_percentage": 0.1128, + "s2:nodata_pixel_percentage": 0, + "s2:saturated_defective_pixel_percentage": 0, + "s2:dark_features_percentage": 0.851427, + "s2:cloud_shadow_percentage": 5.519527, + "s2:vegetation_percentage": 44.695824, + "s2:not_vegetated_percentage": 6.347982, + "s2:water_percentage": 0.271986, + "s2:unclassified_percentage": 0.524517, + "s2:medium_proba_clouds_percentage": 9.545068, + "s2:high_proba_clouds_percentage": 21.567261, + "s2:thin_cirrus_percentage": 0.62316, + "s2:snow_ice_percentage": 10.053248, + "s2:product_type": "S2MSI2A", + "s2:processing_baseline": "05.09", + "s2:product_uri": "S2A_MSIL2A_20230602T100601_N0509_R022_T32TPS_20230602T162001.SAFE", + "s2:generation_time": "2023-06-02T16:20:01.000000Z", + "s2:datatake_id": "GS2A_20230602T100601_041489_N05.09", + "s2:datatake_type": "INS-NOBS", + "s2:datastrip_id": "S2A_OPER_MSI_L2A_DS_2APS_20230602T162001_S20230602T100832_N05.09", + "s2:granule_id": "S2A_OPER_MSI_L2A_TL_2APS_20230602T162001_A041489_T32TPS_N05.09", + "s2:reflectance_conversion_factor": 0.973537588529029, + "datetime": "2023-06-02T10:18:03.169000Z", + "s2:sequence": "0", + "earthsearch:s3_path": "s3://sentinel-cogs/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A", + "earthsearch:payload_id": "roda-sentinel2/workflow-sentinel2-to-stac/862356d8ee275d311b2b78123d5998be", + "earthsearch:boa_offset_applied": true, + "processing:software": { + "sentinel2-to-stac": "0.1.0" + }, + "updated": "2023-06-02T21:15:52.254Z" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 10.31404819699118, + 46.94615910623302 + ], + [ + 11.755571336057699, + 46.92052771524623 + ], + [ + 11.706241885992638, + 45.933505800162294 + ], + [ + 10.290497179167554, + 45.958273370339676 + ], + [ + 10.31404819699118, + 46.94615910623302 + ] + ] + ] + }, + "links": [ + { + "rel": "self", + "type": "application/geo+json", + "href": "https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a/items/S2A_32TPS_20230602_0_L2A" + }, + { + "rel": "canonical", + "href": "s3://sentinel-cogs/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/S2A_32TPS_20230602_0_L2A.json", + "type": "application/json" + }, + { + "rel": "license", + "href": "https://sentinel.esa.int/documents/247904/690755/Sentinel_Data_Legal_Notice" + }, + { + "rel": "derived_from", + "href": "https://earth-search.aws.element84.com/v1/collections/sentinel-2-l1c/items/S2A_32TPS_20230602_0_L1C", + "type": "application/geo+json" + }, + { + "rel": "parent", + "type": "application/json", + "href": "https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a" + }, + { + "rel": "collection", + "type": "application/json", + "href": "https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a" + }, + { + "rel": "root", + "type": "application/json", + "href": "https://earth-search.aws.element84.com/v1" + }, + { + "rel": "thumbnail", + "href": "https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a/items/S2A_32TPS_20230602_0_L2A/thumbnail" + } + ], + "assets": { + "aot": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/AOT.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "Aerosol optical thickness (AOT)", + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.001, + "offset": 0 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "blue": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/B02.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "Blue (band 2) - 10m", + "eo:bands": [ + { + "name": "blue", + "common_name": "blue", + "description": "Blue (band 2)", + "center_wavelength": 0.49, + "full_width_half_max": 0.098 + } + ], + "gsd": 10, + "proj:shape": [ + 10980, + 10980 + ], + "proj:transform": [ + 10, + 0, + 600000, + 0, + -10, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 10, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "coastal": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/B01.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "Coastal aerosol (band 1) - 60m", + "eo:bands": [ + { + "name": "coastal", + "common_name": "coastal", + "description": "Coastal aerosol (band 1)", + "center_wavelength": 0.443, + "full_width_half_max": 0.027 + } + ], + "gsd": 60, + "proj:shape": [ + 1830, + 1830 + ], + "proj:transform": [ + 60, + 0, + 600000, + 0, + -60, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 60, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "granule_metadata": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/granule_metadata.xml", + "type": "application/xml", + "roles": [ + "metadata" + ] + }, + "green": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/B03.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "Green (band 3) - 10m", + "eo:bands": [ + { + "name": "green", + "common_name": "green", + "description": "Green (band 3)", + "center_wavelength": 0.56, + "full_width_half_max": 0.045 + } + ], + "gsd": 10, + "proj:shape": [ + 10980, + 10980 + ], + "proj:transform": [ + 10, + 0, + 600000, + 0, + -10, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 10, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "nir": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/B08.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "NIR 1 (band 8) - 10m", + "eo:bands": [ + { + "name": "nir", + "common_name": "nir", + "description": "NIR 1 (band 8)", + "center_wavelength": 0.842, + "full_width_half_max": 0.145 + } + ], + "gsd": 10, + "proj:shape": [ + 10980, + 10980 + ], + "proj:transform": [ + 10, + 0, + 600000, + 0, + -10, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 10, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "nir08": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/B8A.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "NIR 2 (band 8A) - 20m", + "eo:bands": [ + { + "name": "nir08", + "common_name": "nir08", + "description": "NIR 2 (band 8A)", + "center_wavelength": 0.865, + "full_width_half_max": 0.033 + } + ], + "gsd": 20, + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "nir09": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/B09.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "NIR 3 (band 9) - 60m", + "eo:bands": [ + { + "name": "nir09", + "common_name": "nir09", + "description": "NIR 3 (band 9)", + "center_wavelength": 0.945, + "full_width_half_max": 0.026 + } + ], + "gsd": 60, + "proj:shape": [ + 1830, + 1830 + ], + "proj:transform": [ + 60, + 0, + 600000, + 0, + -60, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 60, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "red": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/B04.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "Red (band 4) - 10m", + "eo:bands": [ + { + "name": "red", + "common_name": "red", + "description": "Red (band 4)", + "center_wavelength": 0.665, + "full_width_half_max": 0.038 + } + ], + "gsd": 10, + "proj:shape": [ + 10980, + 10980 + ], + "proj:transform": [ + 10, + 0, + 600000, + 0, + -10, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 10, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "rededge1": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/B05.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "Red edge 1 (band 5) - 20m", + "eo:bands": [ + { + "name": "rededge1", + "common_name": "rededge", + "description": "Red edge 1 (band 5)", + "center_wavelength": 0.704, + "full_width_half_max": 0.019 + } + ], + "gsd": 20, + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "rededge2": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/B06.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "Red edge 2 (band 6) - 20m", + "eo:bands": [ + { + "name": "rededge2", + "common_name": "rededge", + "description": "Red edge 2 (band 6)", + "center_wavelength": 0.74, + "full_width_half_max": 0.018 + } + ], + "gsd": 20, + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "rededge3": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/B07.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "Red edge 3 (band 7) - 20m", + "eo:bands": [ + { + "name": "rededge3", + "common_name": "rededge", + "description": "Red edge 3 (band 7)", + "center_wavelength": 0.783, + "full_width_half_max": 0.028 + } + ], + "gsd": 20, + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "scl": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/SCL.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "Scene classification map (SCL)", + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint8", + "spatial_resolution": 20 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "swir16": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/B11.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "SWIR 1 (band 11) - 20m", + "eo:bands": [ + { + "name": "swir16", + "common_name": "swir16", + "description": "SWIR 1 (band 11)", + "center_wavelength": 1.61, + "full_width_half_max": 0.143 + } + ], + "gsd": 20, + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "swir22": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/B12.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "SWIR 2 (band 12) - 20m", + "eo:bands": [ + { + "name": "swir22", + "common_name": "swir22", + "description": "SWIR 2 (band 12)", + "center_wavelength": 2.19, + "full_width_half_max": 0.242 + } + ], + "gsd": 20, + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "thumbnail": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/thumbnail.jpg", + "type": "image/jpeg", + "title": "Thumbnail image", + "roles": [ + "thumbnail" + ] + }, + "tileinfo_metadata": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/tileinfo_metadata.json", + "type": "application/json", + "roles": [ + "metadata" + ] + }, + "visual": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/TCI.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "True color image", + "eo:bands": [ + { + "name": "red", + "common_name": "red", + "description": "Red (band 4)", + "center_wavelength": 0.665, + "full_width_half_max": 0.038 + }, + { + "name": "green", + "common_name": "green", + "description": "Green (band 3)", + "center_wavelength": 0.56, + "full_width_half_max": 0.045 + }, + { + "name": "blue", + "common_name": "blue", + "description": "Blue (band 2)", + "center_wavelength": 0.49, + "full_width_half_max": 0.098 + } + ], + "proj:shape": [ + 10980, + 10980 + ], + "proj:transform": [ + 10, + 0, + 600000, + 0, + -10, + 5200020 + ], + "roles": [ + "visual" + ] + }, + "wvp": { + "href": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/32/T/PS/2023/6/S2A_32TPS_20230602_0_L2A/WVP.tif", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "title": "Water vapour (WVP)", + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "unit": "cm", + "scale": 0.001, + "offset": 0 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "aot-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/AOT.jp2", + "type": "image/jp2", + "title": "Aerosol optical thickness (AOT)", + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.001, + "offset": 0 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "blue-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/B02.jp2", + "type": "image/jp2", + "title": "Blue (band 2) - 10m", + "eo:bands": [ + { + "name": "blue", + "common_name": "blue", + "description": "Blue (band 2)", + "center_wavelength": 0.49, + "full_width_half_max": 0.098 + } + ], + "gsd": 10, + "proj:shape": [ + 10980, + 10980 + ], + "proj:transform": [ + 10, + 0, + 600000, + 0, + -10, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 10, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "coastal-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/B01.jp2", + "type": "image/jp2", + "title": "Coastal aerosol (band 1) - 60m", + "eo:bands": [ + { + "name": "coastal", + "common_name": "coastal", + "description": "Coastal aerosol (band 1)", + "center_wavelength": 0.443, + "full_width_half_max": 0.027 + } + ], + "gsd": 60, + "proj:shape": [ + 1830, + 1830 + ], + "proj:transform": [ + 60, + 0, + 600000, + 0, + -60, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 60, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "green-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/B03.jp2", + "type": "image/jp2", + "title": "Green (band 3) - 10m", + "eo:bands": [ + { + "name": "green", + "common_name": "green", + "description": "Green (band 3)", + "center_wavelength": 0.56, + "full_width_half_max": 0.045 + } + ], + "gsd": 10, + "proj:shape": [ + 10980, + 10980 + ], + "proj:transform": [ + 10, + 0, + 600000, + 0, + -10, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 10, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "nir-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/B08.jp2", + "type": "image/jp2", + "title": "NIR 1 (band 8) - 10m", + "eo:bands": [ + { + "name": "nir", + "common_name": "nir", + "description": "NIR 1 (band 8)", + "center_wavelength": 0.842, + "full_width_half_max": 0.145 + } + ], + "gsd": 10, + "proj:shape": [ + 10980, + 10980 + ], + "proj:transform": [ + 10, + 0, + 600000, + 0, + -10, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 10, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "nir08-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/B8A.jp2", + "type": "image/jp2", + "title": "NIR 2 (band 8A) - 20m", + "eo:bands": [ + { + "name": "nir08", + "common_name": "nir08", + "description": "NIR 2 (band 8A)", + "center_wavelength": 0.865, + "full_width_half_max": 0.033 + } + ], + "gsd": 20, + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "nir09-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/B09.jp2", + "type": "image/jp2", + "title": "NIR 3 (band 9) - 60m", + "eo:bands": [ + { + "name": "nir09", + "common_name": "nir09", + "description": "NIR 3 (band 9)", + "center_wavelength": 0.945, + "full_width_half_max": 0.026 + } + ], + "gsd": 60, + "proj:shape": [ + 1830, + 1830 + ], + "proj:transform": [ + 60, + 0, + 600000, + 0, + -60, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 60, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "red-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/B04.jp2", + "type": "image/jp2", + "title": "Red (band 4) - 10m", + "eo:bands": [ + { + "name": "red", + "common_name": "red", + "description": "Red (band 4)", + "center_wavelength": 0.665, + "full_width_half_max": 0.038 + } + ], + "gsd": 10, + "proj:shape": [ + 10980, + 10980 + ], + "proj:transform": [ + 10, + 0, + 600000, + 0, + -10, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 10, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "rededge1-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/B05.jp2", + "type": "image/jp2", + "title": "Red edge 1 (band 5) - 20m", + "eo:bands": [ + { + "name": "rededge1", + "common_name": "rededge", + "description": "Red edge 1 (band 5)", + "center_wavelength": 0.704, + "full_width_half_max": 0.019 + } + ], + "gsd": 20, + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "rededge2-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/B06.jp2", + "type": "image/jp2", + "title": "Red edge 2 (band 6) - 20m", + "eo:bands": [ + { + "name": "rededge2", + "common_name": "rededge", + "description": "Red edge 2 (band 6)", + "center_wavelength": 0.74, + "full_width_half_max": 0.018 + } + ], + "gsd": 20, + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "rededge3-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/B07.jp2", + "type": "image/jp2", + "title": "Red edge 3 (band 7) - 20m", + "eo:bands": [ + { + "name": "rededge3", + "common_name": "rededge", + "description": "Red edge 3 (band 7)", + "center_wavelength": 0.783, + "full_width_half_max": 0.028 + } + ], + "gsd": 20, + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "scl-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/SCL.jp2", + "type": "image/jp2", + "title": "Scene classification map (SCL)", + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint8", + "spatial_resolution": 20 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "swir16-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/B11.jp2", + "type": "image/jp2", + "title": "SWIR 1 (band 11) - 20m", + "eo:bands": [ + { + "name": "swir16", + "common_name": "swir16", + "description": "SWIR 1 (band 11)", + "center_wavelength": 1.61, + "full_width_half_max": 0.143 + } + ], + "gsd": 20, + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "swir22-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/B12.jp2", + "type": "image/jp2", + "title": "SWIR 2 (band 12) - 20m", + "eo:bands": [ + { + "name": "swir22", + "common_name": "swir22", + "description": "SWIR 2 (band 12)", + "center_wavelength": 2.19, + "full_width_half_max": 0.242 + } + ], + "gsd": 20, + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "scale": 0.0001, + "offset": -0.1 + } + ], + "roles": [ + "data", + "reflectance" + ] + }, + "visual-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/TCI.jp2", + "type": "image/jp2", + "title": "True color image", + "eo:bands": [ + { + "name": "red", + "common_name": "red", + "description": "Red (band 4)", + "center_wavelength": 0.665, + "full_width_half_max": 0.038 + }, + { + "name": "green", + "common_name": "green", + "description": "Green (band 3)", + "center_wavelength": 0.56, + "full_width_half_max": 0.045 + }, + { + "name": "blue", + "common_name": "blue", + "description": "Blue (band 2)", + "center_wavelength": 0.49, + "full_width_half_max": 0.098 + } + ], + "proj:shape": [ + 10980, + 10980 + ], + "proj:transform": [ + 10, + 0, + 600000, + 0, + -10, + 5200020 + ], + "roles": [ + "visual" + ] + }, + "wvp-jp2": { + "href": "s3://sentinel-s2-l2a/tiles/32/T/PS/2023/6/2/0/WVP.jp2", + "type": "image/jp2", + "title": "Water vapour (WVP)", + "proj:shape": [ + 5490, + 5490 + ], + "proj:transform": [ + 20, + 0, + 600000, + 0, + -20, + 5200020 + ], + "raster:bands": [ + { + "nodata": 0, + "data_type": "uint16", + "bits_per_sample": 15, + "spatial_resolution": 20, + "unit": "cm", + "scale": 0.001, + "offset": 0 + } + ], + "roles": [ + "data", + "reflectance" + ] + } + }, + "bbox": [ + 10.290497179167554, + 45.933505800162294, + 11.755571336057699, + 46.94615910623302 + ], + "stac_extensions": [ + "https://stac-extensions.github.io/grid/v1.0.0/schema.json", + "https://stac-extensions.github.io/projection/v1.0.0/schema.json", + "https://stac-extensions.github.io/eo/v1.0.0/schema.json", + "https://stac-extensions.github.io/mgrs/v1.0.0/schema.json", + "https://stac-extensions.github.io/processing/v1.1.0/schema.json", + "https://stac-extensions.github.io/view/v1.0.0/schema.json", + "https://stac-extensions.github.io/raster/v1.1.0/schema.json" + ], + "collection": "sentinel-2-l2a" +} diff --git a/tests/test_load_stac.py b/tests/test_load_stac.py index 9c6cd5ca..cce93573 100644 --- a/tests/test_load_stac.py +++ b/tests/test_load_stac.py @@ -3,18 +3,16 @@ from openeo_processes_dask.process_implementations.cubes.load import load_stac -def test_load_stac(temporal_interval, bounding_box): - url = ( - "https://planetarycomputer.microsoft.com/api/stac/v1/collections/landsat-c2-l2" - ) +def test_load_stac(bounding_box): + url = "./tests/data/stac/s2_l2a_test_item.json" output_cube = load_stac( url=url, spatial_extent=bounding_box, - temporal_extent=temporal_interval, bands=["red"], ) assert output_cube.openeo is not None assert len(output_cube[output_cube.openeo.x_dim]) > 0 assert len(output_cube[output_cube.openeo.y_dim]) > 0 + assert len(output_cube[output_cube.openeo.band_dims[0]]) > 0 assert len(output_cube[output_cube.openeo.temporal_dims[0]]) > 0 From 12d822fb9e99d64259c2a30b01bf9a024aef0c21 Mon Sep 17 00:00:00 2001 From: clausmichele Date: Wed, 12 Jul 2023 16:47:55 +0200 Subject: [PATCH 13/13] Update submodule --- openeo_processes_dask/specs/openeo-processes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openeo_processes_dask/specs/openeo-processes b/openeo_processes_dask/specs/openeo-processes index 965bbaeb..bc6366f5 160000 --- a/openeo_processes_dask/specs/openeo-processes +++ b/openeo_processes_dask/specs/openeo-processes @@ -1 +1 @@ -Subproject commit 965bbaebd4d5984203a0437076c85a66a72a23e0 +Subproject commit bc6366f58eaddd80a37db81d6507fdcfc3a94662