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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions openeo_driver/ProcessGraphDeserializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,36 @@ def load_collection(args: dict, env: EvalEnv) -> DriverDataCube:
return env.backend_implementation.catalog.load_collection(collection_id, load_params=load_params, env=env)


@process_registry_100.add_function(spec=read_spec("openeo-processes/experimental/query_stac.json"))
@process_registry_2xx.add_function(spec=read_spec("openeo-processes/experimental/query_stac.json"))
def query_stac(args: ProcessArgs, env: EvalEnv) -> Dict:
url = args.get_required(
"url",
)

temporal_extent = None
spatial_extent = None
if "temporal_extent" in args:
temporal_extent = _extract_temporal_extent(
args, field="temporal_extent", process_id="query_stac"
)
if "spatial_extent" in args:
spatial_extent = _extract_bbox_extent(
args, field="spatial_extent", process_id="query_stac"
)

dry_run_tracer: DryRunDataTracer = env.get(ENV_DRY_RUN_TRACER)
if dry_run_tracer:
_log.warning("Dry run tracer not supported for query_stac")
return {}
else:
return env.backend_implementation.query_stac(
url=url,
spatial_extent=spatial_extent,
temporal_extent=temporal_extent,
env=env
)

def _check_geometry_path_assumption(path: str, process: str, parameter: str):
if isinstance(path, str) and path.lstrip().startswith("{"):
raise ProcessParameterInvalidException(
Expand Down
6 changes: 5 additions & 1 deletion openeo_driver/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import sys
from datetime import datetime, timedelta
from pathlib import Path
from typing import List, Union, NamedTuple, Dict, Optional, Callable, Iterable, Container, Any
from typing import List, Union, NamedTuple, Dict, Optional, Callable, Iterable, Container, Any, Tuple

import flask

Expand All @@ -35,6 +35,7 @@
from openeo_driver.users import User
from openeo_driver.users.oidc import OidcProvider
from openeo_driver.util.date_math import simple_job_progress_estimation
from openeo_driver.util.geometry import BoundingBox
from openeo_driver.util.logging import just_log_exceptions
from openeo_driver.utils import read_json, dict_item, EvalEnv, extract_namedtuple_fields_from_dict, \
get_package_versions
Expand Down Expand Up @@ -985,6 +986,9 @@ def load_result(self, job_id: str, user_id: Optional[str], load_params: LoadPara
def load_stac(self, url: str, load_params: LoadParameters, env: EvalEnv) -> DriverDataCube:
raise NotImplementedError

def query_stac(self, url: str, spatial_extent: Union[Dict, BoundingBox, None], temporal_extent: Tuple[Optional[str], Optional[str]], _env: EvalEnv) -> DriverDataCube:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Align query_stac backend parameter name with caller

ProcessGraphDeserializer.query_stac calls env.backend_implementation.query_stac(..., env=env), but the new backend interface declares the parameter as _env; in Python keyword arguments must match exactly, so backends that inherit the default method (or call super()) will raise TypeError: unexpected keyword argument 'env' instead of the intended NotImplementedError path. This turns unsupported query_stac usage into an internal error rather than a predictable backend capability failure.

Useful? React with 👍 / 👎.

raise NotImplementedError

def load_ml_model(self, job_id: str) -> DriverMlModel:
raise NotImplementedError

Expand Down
189 changes: 189 additions & 0 deletions openeo_driver/specs/openeo-processes/experimental/query_stac.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
{
"id": "query_stac",
"summary": "Queries STAC",
"description": "Queries a STAC to construct a FeatureCollection of STAC Items with the results",
"categories": [
"import"
],
"experimental": true,
"parameters": [
{
"name": "url",
"description": "The URL to a static STAC Collection",
"schema": {
"title": "URL",
"type": "string",
"format": "uri",
"subtype": "uri",
"pattern": "^https?://"
}
},
{
"name": "spatial_extent",
"description": "Limits the data to load to the specified bounding box.",
"schema": [
{
"title": "Bounding Box",
"type": "object",
"subtype": "bounding-box",
"required": [
"west",
"south",
"east",
"north"
],
"properties": {
"west": {
"description": "West (lower left corner, coordinate axis 1).",
"type": "number"
},
"south": {
"description": "South (lower left corner, coordinate axis 2).",
"type": "number"
},
"east": {
"description": "East (upper right corner, coordinate axis 1).",
"type": "number"
},
"north": {
"description": "North (upper right corner, coordinate axis 2).",
"type": "number"
},
"base": {
"description": "Base (optional, lower left corner, coordinate axis 3).",
"type": [
"number",
"null"
],
"default": null
},
"height": {
"description": "Height (optional, upper right corner, coordinate axis 3).",
"type": [
"number",
"null"
],
"default": null
},
"crs": {
"description": "Coordinate reference system of the extent, specified as as [EPSG code](http://www.epsg-registry.org/) or [WKT2 CRS string](http://docs.opengeospatial.org/is/18-010r7/18-010r7.html). Defaults to `4326` (EPSG code 4326) unless the client explicitly requests a different coordinate reference system.",
"anyOf": [
{
"title": "EPSG Code",
"type": "integer",
"subtype": "epsg-code",
"minimum": 1000,
"examples": [
3857
]
},
{
"title": "WKT2",
"type": "string",
"subtype": "wkt2-definition"
}
],
"default": 4326
}
}
}
],
"optional": false
},
{
"name": "temporal_extent",
"description": "Limits the data to load to the specified left-closed temporal interval. Applies to all temporal dimensions. The interval has to be specified as an array with exactly two elements:\n\n1. The first element is the start of the temporal interval. The specified instance in time is **included** in the interval.\n2. The second element is the end of the temporal interval. The specified instance in time is **excluded** from the interval.\n\nThe second element must always be greater/later than the first element. Otherwise, a `TemporalExtentEmpty` exception is thrown.\n\nAlso supports open intervals by setting one of the boundaries to `null`, but never both.",
"schema": [
{
"type": "array",
"subtype": "temporal-interval",
"uniqueItems": true,
"minItems": 2,
"maxItems": 2,
"items": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"subtype": "date-time",
"description": "Date and time with a time zone."
},
{
"type": "string",
"format": "date",
"subtype": "date",
"description": "Date only, formatted as `YYYY-MM-DD`. The time zone is UTC. Missing time components are all 0."
},
{
"type": "null"
}
]
},
"examples": [
[
"2015-01-01T00:00:00Z",
"2016-01-01T00:00:00Z"
],
[
"2015-01-01",
"2016-01-01"
]
]
}
],
"optional": false
}
],
"returns": {
"description": "A GeoJSON FeatureCollection ",
"schema": {
"type": "object",
"subtype": "geojson"
}
},
"examples": [
{
"title": "Load from a STAC API",
"arguments": {
"url": "https://example.com/collections/SENTINEL2",
"spatial_extent": {
"west": 16.1,
"east": 16.6,
"north": 48.6,
"south": 47.2
},
"temporal_extent": [
"2018-01-01",
"2019-01-01"
]
}
}
],
"links": [
{
"rel": "about",
"href": "https://proj.org/usage/projections.html",
"title": "PROJ parameters for cartographic projections"
},
{
"rel": "about",
"href": "http://www.epsg-registry.org",
"title": "Official EPSG code registry"
},
{
"rel": "about",
"href": "http://www.epsg.io",
"title": "Unofficial EPSG code database"
},
{
"href": "http://www.opengeospatial.org/standards/sfa",
"rel": "about",
"title": "Simple Features standard by the OGC"
},
{
"href": "https://www.rfc-editor.org/rfc/rfc3339.html",
"rel": "about",
"title": "RFC3339: Details about formatting temporal strings"
}
]
}