From 346f5f3878f867042251924929808fa129f87bb3 Mon Sep 17 00:00:00 2001 From: Stefaan Lippens Date: Mon, 28 Aug 2023 11:09:06 +0200 Subject: [PATCH] Issue #460 avoid string (return) annotations (large cases) PEP 563: Postponed Evaluation of Annotations --- openeo/metadata.py | 34 +++--- openeo/rest/connection.py | 18 +-- openeo/rest/datacube.py | 227 ++++++++++++++++++-------------------- openeo/rest/vectorcube.py | 28 ++--- 4 files changed, 152 insertions(+), 155 deletions(-) diff --git a/openeo/metadata.py b/openeo/metadata.py index 63f46d34e..43e39d206 100644 --- a/openeo/metadata.py +++ b/openeo/metadata.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import warnings from typing import Any, Callable, List, NamedTuple, Optional, Tuple, Union @@ -32,11 +34,11 @@ def __repr__(self): def __eq__(self, other): return self.__class__ == other.__class__ and self.__dict__ == other.__dict__ - def rename(self, name) -> 'Dimension': + def rename(self, name) -> Dimension: """Create new dimension with new name.""" return Dimension(type=self.type, name=name) - def rename_labels(self, target, source) -> 'Dimension': + def rename_labels(self, target, source) -> Dimension: """ Rename labels, if the type of dimension allows it. @@ -67,7 +69,7 @@ def __init__( self.crs = crs self.step = step - def rename(self, name) -> 'Dimension': + def rename(self, name) -> Dimension: return SpatialDimension(name=name, extent=self.extent, crs=self.crs, step=self.step) @@ -76,7 +78,7 @@ def __init__(self, name: str, extent: Union[Tuple[str, str], List[str]]): super().__init__(type="temporal", name=name) self.extent = extent - def rename(self, name) -> 'Dimension': + def rename(self, name) -> Dimension: return TemporalDimension(name=name, extent=self.extent) @@ -152,7 +154,7 @@ def band_name(self, band: Union[str, int], allow_common=True) -> str: return self.band_names[band] raise ValueError("Invalid band name/index {b!r}. Valid names: {n!r}".format(b=band, n=self.band_names)) - def filter_bands(self, bands: List[Union[int, str]]) -> 'BandDimension': + def filter_bands(self, bands: List[Union[int, str]]) -> BandDimension: """ Construct new BandDimension with subset of bands, based on given band indices or (common) names @@ -162,7 +164,7 @@ def filter_bands(self, bands: List[Union[int, str]]) -> 'BandDimension': bands=[self.bands[self.band_index(b)] for b in bands] ) - def append_band(self, band: Band) -> 'BandDimension': + def append_band(self, band: Band) -> BandDimension: """Create new BandDimension with appended band.""" if band.name in self.band_names: raise ValueError("Duplicate band {b!r}".format(b=band)) @@ -172,7 +174,7 @@ def append_band(self, band: Band) -> 'BandDimension': bands=self.bands + [band] ) - def rename_labels(self, target, source) -> 'Dimension': + def rename_labels(self, target, source) -> Dimension: if source: if len(target) != len(source): raise ValueError( @@ -230,7 +232,7 @@ def __init__(self, metadata: dict, dimensions: List[Dimension] = None): self._temporal_dimension = dim @classmethod - def get_or_create(cls, metadata: Union[dict, 'CollectionMetadata', None]) -> 'CollectionMetadata': + def get_or_create(cls, metadata: Union[dict, "CollectionMetadata", None]) -> CollectionMetadata: """Get or create CollectionMetadata from given argument.""" if isinstance(metadata, cls): return metadata @@ -242,7 +244,7 @@ def __eq__(self, o: Any) -> bool: def _clone_and_update( self, metadata: dict = None, dimensions: List[Dimension] = None, **kwargs - ) -> 'CollectionMetadata': + ) -> CollectionMetadata: """Create a new instance (of same class) with copied/updated fields.""" cls = type(self) if dimensions == None: @@ -397,7 +399,7 @@ def band_common_names(self) -> List[str]: def get_band_index(self, band: Union[int, str]) -> int: return self.band_dimension.band_index(band) - def filter_bands(self, band_names: List[Union[int, str]]) -> 'CollectionMetadata': + def filter_bands(self, band_names: List[Union[int, str]]) -> CollectionMetadata: """ Create new `CollectionMetadata` with filtered band dimension :param band_names: list of band names/indices to keep @@ -409,7 +411,7 @@ def filter_bands(self, band_names: List[Union[int, str]]) -> 'CollectionMetadata for d in self._dimensions ]) - def append_band(self, band: Band) -> 'CollectionMetadata': + def append_band(self, band: Band) -> CollectionMetadata: """ Create new `CollectionMetadata` with given band added to band dimension. """ @@ -419,7 +421,7 @@ def append_band(self, band: Band) -> 'CollectionMetadata': for d in self._dimensions ]) - def rename_labels(self, dimension: str, target: list, source: list = None) -> 'CollectionMetadata': + def rename_labels(self, dimension: str, target: list, source: list = None) -> CollectionMetadata: """ Renames the labels of the specified dimension from source to target. @@ -436,7 +438,7 @@ def rename_labels(self, dimension: str, target: list, source: list = None) -> 'C return self._clone_and_update(dimensions=new_dimensions) - def rename_dimension(self, source: str, target: str) -> 'CollectionMetadata': + def rename_dimension(self, source: str, target: str) -> CollectionMetadata: """ Rename source dimension into target, preserving other properties """ @@ -447,7 +449,7 @@ def rename_dimension(self, source: str, target: str) -> 'CollectionMetadata': return self._clone_and_update(dimensions=new_dimensions) - def reduce_dimension(self, dimension_name: str) -> 'CollectionMetadata': + def reduce_dimension(self, dimension_name: str) -> CollectionMetadata: """Create new metadata object by collapsing/reducing a dimension.""" # TODO: option to keep reduced dimension (with a single value)? self.assert_valid_dimension(dimension_name) @@ -455,7 +457,7 @@ def reduce_dimension(self, dimension_name: str) -> 'CollectionMetadata': dimensions = self._dimensions[:loc] + self._dimensions[loc + 1:] return self._clone_and_update(dimensions=dimensions) - def add_dimension(self, name: str, label: Union[str, float], type: str = None) -> 'CollectionMetadata': + def add_dimension(self, name: str, label: Union[str, float], type: str = None) -> CollectionMetadata: """Create new metadata object with added dimension""" if any(d.name == name for d in self._dimensions): raise DimensionAlreadyExistsException(f"Dimension with name {name!r} already exists") @@ -469,7 +471,7 @@ def add_dimension(self, name: str, label: Union[str, float], type: str = None) - dim = Dimension(type=type or "other", name=name) return self._clone_and_update(dimensions=self._dimensions + [dim]) - def drop_dimension(self, name: str = None) -> 'CollectionMetadata': + def drop_dimension(self, name: str = None) -> CollectionMetadata: """Drop dimension with given name""" dimension_names = self.dimension_names() if name not in dimension_names: diff --git a/openeo/rest/connection.py b/openeo/rest/connection.py index 09eaf23f0..e660b4588 100644 --- a/openeo/rest/connection.py +++ b/openeo/rest/connection.py @@ -1,6 +1,8 @@ """ This module provides a Connection object to manage and persist settings when interacting with the OpenEO API. """ +from __future__ import annotations + import datetime import json import logging @@ -306,7 +308,7 @@ def _get_refresh_token_store(self) -> RefreshTokenStore: self._refresh_token_store = RefreshTokenStore() return self._refresh_token_store - def authenticate_basic(self, username: Optional[str] = None, password: Optional[str] = None) -> "Connection": + def authenticate_basic(self, username: Optional[str] = None, password: Optional[str] = None) -> Connection: """ Authenticate a user to the backend using basic username and password. @@ -431,7 +433,7 @@ def _authenticate_oidc( store_refresh_token: bool = False, fallback_refresh_token_to_store: Optional[str] = None, oidc_auth_renewer: Optional[OidcAuthenticator] = None, - ) -> "Connection": + ) -> Connection: """ Authenticate through OIDC and set up bearer token (based on OIDC access_token) for further requests. """ @@ -465,7 +467,7 @@ def authenticate_oidc_authorization_code( server_address: Optional[Tuple[str, int]] = None, webbrowser_open: Optional[Callable] = None, store_refresh_token=False, - ) -> "Connection": + ) -> Connection: """ OpenID Connect Authorization Code Flow (with PKCE). @@ -489,7 +491,7 @@ def authenticate_oidc_client_credentials( client_id: Optional[str] = None, client_secret: Optional[str] = None, provider_id: Optional[str] = None, - ) -> 'Connection': + ) -> Connection: """ Authenticate with :ref:`OIDC Client Credentials flow ` @@ -528,7 +530,7 @@ def authenticate_oidc_resource_owner_password_credentials( client_secret: Optional[str] = None, provider_id: Optional[str] = None, store_refresh_token: bool = False, - ) -> "Connection": + ) -> Connection: """ OpenId Connect Resource Owner Password Credentials """ @@ -549,7 +551,7 @@ def authenticate_oidc_refresh_token( provider_id: Optional[str] = None, *, store_refresh_token: bool = False, - ) -> "Connection": + ) -> Connection: """ Authenticate with :ref:`OIDC Refresh Token flow ` @@ -594,7 +596,7 @@ def authenticate_oidc_device( use_pkce: Optional[bool] = None, max_poll_time: float = OidcDeviceAuthenticator.DEFAULT_MAX_POLL_TIME, **kwargs, - ) -> "Connection": + ) -> Connection: """ Authenticate with the :ref:`OIDC Device Code flow ` @@ -1311,7 +1313,7 @@ def load_stac( cube.metadata = metadata return cube - def load_ml_model(self, id: Union[str, BatchJob]) -> "MlModel": + def load_ml_model(self, id: Union[str, BatchJob]) -> MlModel: """ Loads a machine learning model from a STAC Item. diff --git a/openeo/rest/datacube.py b/openeo/rest/datacube.py index fc3cb3e54..8473f4c4a 100644 --- a/openeo/rest/datacube.py +++ b/openeo/rest/datacube.py @@ -7,6 +7,8 @@ Symbolic reference to the current data cube, to be used as argument in :py:meth:`DataCube.process()` calls """ +from __future__ import annotations + import datetime import logging import pathlib @@ -62,7 +64,7 @@ class DataCube(_ProcessGraphAbstraction): # TODO: set this based on back-end or user preference? _DEFAULT_RASTER_FORMAT = "GTiff" - def __init__(self, graph: PGNode, connection: 'openeo.Connection', metadata: CollectionMetadata = None): + def __init__(self, graph: PGNode, connection: Connection, metadata: CollectionMetadata = None): super().__init__(pgnode=graph, connection=connection) self.metadata = CollectionMetadata.get_or_create(metadata) @@ -73,7 +75,7 @@ def process( metadata: Optional[CollectionMetadata] = None, namespace: Optional[str] = None, **kwargs, - ) -> "DataCube": + ) -> DataCube: """ Generic helper to create a new DataCube by applying a process. @@ -88,7 +90,7 @@ def process( graph_add_node = legacy_alias(process, "graph_add_node", since="0.1.1") - def process_with_node(self, pg: PGNode, metadata: Optional[CollectionMetadata] = None) -> 'DataCube': + def process_with_node(self, pg: PGNode, metadata: Optional[CollectionMetadata] = None) -> DataCube: """ Generic helper to create a new DataCube by applying a process (given as process graph node) @@ -104,16 +106,16 @@ def process_with_node(self, pg: PGNode, metadata: Optional[CollectionMetadata] = @classmethod @openeo_process def load_collection( - cls, - collection_id: str, - connection: 'openeo.Connection' = None, - spatial_extent: Optional[Dict[str, float]] = None, - temporal_extent: Optional[List[Union[str, datetime.datetime, datetime.date, PGNode]]] = None, - bands: Optional[List[str]] = None, - fetch_metadata=True, - properties: Optional[Dict[str, Union[str, PGNode, typing.Callable]]] = None, - max_cloud_cover: Optional[float] = None, - ) -> 'DataCube': + cls, + collection_id: str, + connection: Connection = None, + spatial_extent: Optional[Dict[str, float]] = None, + temporal_extent: Optional[List[Union[str, datetime.datetime, datetime.date, PGNode]]] = None, + bands: Optional[List[str]] = None, + fetch_metadata=True, + properties: Optional[Dict[str, Union[str, PGNode, typing.Callable]]] = None, + max_cloud_cover: Optional[float] = None, + ) -> DataCube: """ Create a new Raster Data cube. @@ -178,8 +180,7 @@ def load_collection( ) @classmethod - def load_disk_collection(cls, connection: 'openeo.Connection', file_format: str, glob_pattern: str, - **options) -> 'DataCube': + def load_disk_collection(cls, connection: Connection, file_format: str, glob_pattern: str, **options) -> DataCube: """ Loads image data from disk as a DataCube. This is backed by a non-standard process ('load_disk_data'). This will eventually be replaced by standard options such as @@ -246,7 +247,7 @@ def filter_temporal( start_date: Union[str, datetime.datetime, datetime.date] = None, end_date: Union[str, datetime.datetime, datetime.date] = None, extent: Union[list, tuple] = None - ) -> 'DataCube': + ) -> DataCube: """ Limit the DataCube to a certain date range, which can be specified in several ways: @@ -277,7 +278,7 @@ def filter_bbox( crs=None, base=None, height=None, bbox=None - ) -> 'DataCube': + ) -> DataCube: """ Limits the data cube to the specified bounding box. @@ -369,10 +370,7 @@ def filter_bbox( ) @openeo_process - def filter_spatial( - self, - geometries - ) -> 'DataCube': + def filter_spatial(self, geometries) -> DataCube: """ Limits the data cube over the spatial dimensions to the specified geometries. @@ -404,7 +402,7 @@ def filter_spatial( ) @openeo_process - def filter_bands(self, bands: Union[List[Union[str, int]], str]) -> 'DataCube': + def filter_bands(self, bands: Union[List[Union[str, int]], str]) -> DataCube: """ Filter the data cube by the given bands @@ -424,7 +422,7 @@ def filter_bands(self, bands: Union[List[Union[str, int]], str]) -> 'DataCube': band_filter = legacy_alias(filter_bands, "band_filter", since="0.1.0") - def band(self, band: Union[str, int]) -> "DataCube": + def band(self, band: Union[str, int]) -> DataCube: """ Filter out a single band @@ -444,7 +442,7 @@ def band(self, band: Union[str, int]) -> "DataCube": def resample_spatial( self, resolution: Union[float, Tuple[float, float]], projection: Union[int, str] = None, method: str = 'near', align: str = 'upper-left' - ) -> 'DataCube': + ) -> DataCube: return self.process('resample_spatial', { 'data': THIS, 'resolution': resolution, @@ -453,7 +451,7 @@ def resample_spatial( 'align': align }) - def resample_cube_spatial(self, target: "DataCube", method: str = "near") -> 'DataCube': + def resample_cube_spatial(self, target: DataCube, method: str = "near") -> DataCube: """ Resamples the spatial dimensions (x,y) from a source data cube to align with the corresponding dimensions of the given target data cube. @@ -470,8 +468,8 @@ def resample_cube_spatial(self, target: "DataCube", method: str = "near") -> 'Da @openeo_process def resample_cube_temporal( - self, target: "DataCube", dimension: Optional[str] = None, valid_within: Optional[int] = None - ) -> 'DataCube': + self, target: DataCube, dimension: Optional[str] = None, valid_within: Optional[int] = None + ) -> DataCube: """ Resamples one or more given temporal dimensions from a source data cube to align with the corresponding dimensions of the given target data cube using the nearest neighbor method. @@ -495,7 +493,7 @@ def resample_cube_temporal( dict_no_none({"data": self, "target": target, "dimension": dimension, "valid_within": valid_within}) ) - def _operator_binary(self, operator: str, other: Union['DataCube', int, float], reverse=False) -> 'DataCube': + def _operator_binary(self, operator: str, other: Union[DataCube, int, float], reverse=False) -> DataCube: """Generic handling of (mathematical) binary operator""" band_math_mode = self._in_bandmath_mode() if band_math_mode: @@ -515,7 +513,7 @@ def _operator_binary(self, operator: str, other: Union['DataCube', int, float], f"Unsupported operator {operator!r} with `other` type {type(other)!r} (band math mode={band_math_mode})" ) - def _operator_unary(self, operator: str, **kwargs) -> 'DataCube': + def _operator_unary(self, operator: str, **kwargs) -> DataCube: band_math_mode = self._in_bandmath_mode() if band_math_mode: return self._bandmath_operator_unary(operator, **kwargs) @@ -528,7 +526,7 @@ def _apply_operator( other: Optional[Union[int, float]] = None, reverse: Optional[bool] = None, extra_arguments: Optional[dict] = None, - ) -> "DataCube": + ) -> DataCube: """ Apply a unary or binary operator/process, by appending to existing `apply` node, or starting a new one. @@ -568,30 +566,30 @@ def _apply_operator( ) @openeo_process(mode="operator") - def add(self, other: Union['DataCube', int, float], reverse=False) -> 'DataCube': + def add(self, other: Union[DataCube, int, float], reverse=False) -> DataCube: return self._operator_binary("add", other, reverse=reverse) @openeo_process(mode="operator") - def subtract(self, other: Union['DataCube', int, float], reverse=False) -> 'DataCube': + def subtract(self, other: Union[DataCube, int, float], reverse=False) -> DataCube: return self._operator_binary("subtract", other, reverse=reverse) @openeo_process(mode="operator") - def divide(self, other: Union['DataCube', int, float], reverse=False) -> 'DataCube': + def divide(self, other: Union[DataCube, int, float], reverse=False) -> DataCube: return self._operator_binary("divide", other, reverse=reverse) @openeo_process(mode="operator") - def multiply(self, other: Union['DataCube', int, float], reverse=False) -> 'DataCube': + def multiply(self, other: Union[DataCube, int, float], reverse=False) -> DataCube: return self._operator_binary("multiply", other, reverse=reverse) @openeo_process - def normalized_difference(self, other: 'DataCube') -> 'DataCube': + def normalized_difference(self, other: DataCube) -> DataCube: # This DataCube method is only a convenience function when in band math mode assert self._in_bandmath_mode() assert other._in_bandmath_mode() return self._operator_binary("normalized_difference", other) @openeo_process(process_id="or", mode="operator") - def logical_or(self, other: 'DataCube') -> 'DataCube': + def logical_or(self, other: DataCube) -> DataCube: """ Apply element-wise logical `or` operation @@ -601,7 +599,7 @@ def logical_or(self, other: 'DataCube') -> 'DataCube': return self._operator_binary("or", other) @openeo_process(process_id="and", mode="operator") - def logical_and(self, other: "DataCube") -> "DataCube": + def logical_and(self, other: DataCube) -> DataCube: """ Apply element-wise logical `and` operation @@ -611,15 +609,15 @@ def logical_and(self, other: "DataCube") -> "DataCube": return self._operator_binary("and", other) @openeo_process(process_id="not", mode="operator") - def __invert__(self) -> "DataCube": + def __invert__(self) -> DataCube: return self._operator_unary("not") @openeo_process(process_id="neq", mode="operator") - def __ne__(self, other: Union["DataCube", int, float]) -> "DataCube": + def __ne__(self, other: Union[DataCube, int, float]) -> DataCube: return self._operator_binary("neq", other) @openeo_process(process_id="eq", mode="operator") - def __eq__(self, other: Union["DataCube", int, float]) -> "DataCube": + def __eq__(self, other: Union[DataCube, int, float]) -> DataCube: """ Pixelwise comparison of this data cube with another cube or constant. @@ -629,7 +627,7 @@ def __eq__(self, other: Union["DataCube", int, float]) -> "DataCube": return self._operator_binary("eq", other) @openeo_process(process_id="gt", mode="operator") - def __gt__(self, other: Union["DataCube", int, float]) -> "DataCube": + def __gt__(self, other: Union[DataCube, int, float]) -> DataCube: """ Pairwise comparison of the bands in this data cube with the bands in the 'other' data cube. @@ -639,11 +637,11 @@ def __gt__(self, other: Union["DataCube", int, float]) -> "DataCube": return self._operator_binary("gt", other) @openeo_process(process_id="ge", mode="operator") - def __ge__(self, other: Union["DataCube", int, float]) -> "DataCube": + def __ge__(self, other: Union[DataCube, int, float]) -> DataCube: return self._operator_binary("gte", other) @openeo_process(process_id="lt", mode="operator") - def __lt__(self, other: Union["DataCube", int, float]) -> "DataCube": + def __lt__(self, other: Union[DataCube, int, float]) -> DataCube: """ Pairwise comparison of the bands in this data cube with the bands in the 'other' data cube. The number of bands in both data cubes has to be the same. @@ -654,51 +652,51 @@ def __lt__(self, other: Union["DataCube", int, float]) -> "DataCube": return self._operator_binary("lt", other) @openeo_process(process_id="le", mode="operator") - def __le__(self, other: Union["DataCube", int, float]) -> "DataCube": + def __le__(self, other: Union[DataCube, int, float]) -> DataCube: return self._operator_binary("lte", other) @openeo_process(process_id="add", mode="operator") - def __add__(self, other) -> "DataCube": + def __add__(self, other) -> DataCube: return self.add(other) @openeo_process(process_id="add", mode="operator") - def __radd__(self, other) -> "DataCube": + def __radd__(self, other) -> DataCube: return self.add(other, reverse=True) @openeo_process(process_id="subtract", mode="operator") - def __sub__(self, other) -> "DataCube": + def __sub__(self, other) -> DataCube: return self.subtract(other) @openeo_process(process_id="subtract", mode="operator") - def __rsub__(self, other) -> "DataCube": + def __rsub__(self, other) -> DataCube: return self.subtract(other, reverse=True) @openeo_process(process_id="multiply", mode="operator") - def __neg__(self) -> "DataCube": + def __neg__(self) -> DataCube: return self.multiply(-1) @openeo_process(process_id="multiply", mode="operator") - def __mul__(self, other) -> "DataCube": + def __mul__(self, other) -> DataCube: return self.multiply(other) @openeo_process(process_id="multiply", mode="operator") - def __rmul__(self, other) -> "DataCube": + def __rmul__(self, other) -> DataCube: return self.multiply(other, reverse=True) @openeo_process(process_id="divide", mode="operator") - def __truediv__(self, other) -> "DataCube": + def __truediv__(self, other) -> DataCube: return self.divide(other) @openeo_process(process_id="divide", mode="operator") - def __rtruediv__(self, other) -> "DataCube": + def __rtruediv__(self, other) -> DataCube: return self.divide(other, reverse=True) @openeo_process(process_id="power", mode="operator") - def __rpow__(self, other) -> "DataCube": + def __rpow__(self, other) -> DataCube: return self._power(other, reverse=True) @openeo_process(process_id="power", mode="operator") - def __pow__(self, other) -> "DataCube": + def __pow__(self, other) -> DataCube: return self._power(other, reverse=False) def _power(self, other, reverse=False): @@ -716,23 +714,23 @@ def power(self, p: float): return self._power(other=p, reverse=False) @openeo_process(process_id="ln", mode="operator") - def ln(self) -> "DataCube": + def ln(self) -> DataCube: return self._operator_unary("ln") @openeo_process(process_id="log", mode="operator") - def logarithm(self, base: float) -> "DataCube": + def logarithm(self, base: float) -> DataCube: return self._operator_unary("log", base=base) @openeo_process(process_id="log", mode="operator") - def log2(self) -> "DataCube": + def log2(self) -> DataCube: return self.logarithm(base=2) @openeo_process(process_id="log", mode="operator") - def log10(self) -> "DataCube": + def log10(self) -> DataCube: return self.logarithm(base=10) @openeo_process(process_id="or", mode="operator") - def __or__(self, other) -> "DataCube": + def __or__(self, other) -> DataCube: return self.logical_or(other) @openeo_process(process_id="and", mode="operator") @@ -740,8 +738,8 @@ def __and__(self, other): return self.logical_and(other) def _bandmath_operator_binary_cubes( - self, operator, other: "DataCube", left_arg_name="x", right_arg_name="y" - ) -> "DataCube": + self, operator, other: DataCube, left_arg_name="x", right_arg_name="y" + ) -> DataCube: """Band math binary operator with cube as right hand side argument""" left = self._get_bandmath_node() right = other._get_bandmath_node() @@ -758,7 +756,7 @@ def _bandmath_operator_binary_cubes( ) return self.process_with_node(left.clone_with_new_reducer(merged)) - def _bandmath_operator_binary_scalar(self, operator: str, other: Union[int, float], reverse=False) -> 'DataCube': + def _bandmath_operator_binary_scalar(self, operator: str, other: Union[int, float], reverse=False) -> DataCube: """Band math binary operator with scalar value (int or float) as right hand side argument""" node = self._get_bandmath_node() x = {'from_node': node.reducer_process_graph()} @@ -769,7 +767,7 @@ def _bandmath_operator_binary_scalar(self, operator: str, other: Union[int, floa PGNode(operator, x=x, y=y) )) - def _bandmath_operator_unary(self, operator: str, **kwargs) -> 'DataCube': + def _bandmath_operator_unary(self, operator: str, **kwargs) -> DataCube: node = self._get_bandmath_node() return self.process_with_node(node.clone_with_new_reducer( PGNode(operator, x={'from_node': node.reducer_process_graph()}, **kwargs) @@ -786,8 +784,9 @@ def _get_bandmath_node(self) -> ReduceNode: raise BandMathException("Must be in band math mode already") return self._pg - def _merge_operator_binary_cubes(self, operator: str, other: 'DataCube', left_arg_name="x", - right_arg_name="y") -> 'DataCube': + def _merge_operator_binary_cubes( + self, operator: str, other: DataCube, left_arg_name="x", right_arg_name="y" + ) -> DataCube: """Merge two cubes with given operator as overlap_resolver.""" # TODO #123 reuse an existing merge_cubes process graph if it already exists? return self.merge_cubes(other, overlap_resolver=PGNode( @@ -922,7 +921,7 @@ def aggregate_spatial_window( align: str = "upper-left", context: Optional[dict] = None, # TODO arguments: target dimension, context - ) -> "DataCube": + ) -> DataCube: """ Aggregates statistics over the horizontal spatial dimensions (axes x and y) of the data cube. @@ -985,7 +984,7 @@ def apply_dimension( dimension: str = "t", target_dimension: Optional[str] = None, context: Optional[dict] = None, - ) -> "DataCube": + ) -> DataCube: """ Applies a process to all pixel values along a dimension of a raster data cube. For example, if the temporal dimension is specified the process will work on a time series of pixel values. @@ -1072,7 +1071,7 @@ def reduce_dimension( context: Optional[dict] = None, process_id="reduce_dimension", band_math_mode: bool = False, - ) -> "DataCube": + ) -> DataCube: """ Add a reduce process with given reducer callback along given dimension @@ -1111,12 +1110,12 @@ def reduce_dimension( # @openeo_process def chunk_polygon( - self, - chunks: Union[shapely.geometry.base.BaseGeometry, dict, str, pathlib.Path, Parameter, "VectorCube"], - process: Union[str, PGNode, typing.Callable], - mask_value: float = None, - context: Optional[dict] = None, - ) -> 'DataCube': + self, + chunks: Union[shapely.geometry.base.BaseGeometry, dict, str, pathlib.Path, Parameter, VectorCube], + process: Union[str, PGNode, typing.Callable], + mask_value: float = None, + context: Optional[dict] = None, + ) -> DataCube: """ Apply a process to spatial chunks of a data cube. @@ -1153,7 +1152,7 @@ def chunk_polygon( ), ) - def reduce_bands(self, reducer: Union[str, PGNode, typing.Callable, UDF]) -> 'DataCube': + def reduce_bands(self, reducer: Union[str, PGNode, typing.Callable, UDF]) -> DataCube: """ Shortcut for :py:meth:`reduce_dimension` along the band dimension @@ -1161,7 +1160,7 @@ def reduce_bands(self, reducer: Union[str, PGNode, typing.Callable, UDF]) -> 'Da """ return self.reduce_dimension(dimension=self.metadata.band_dimension.name, reducer=reducer, band_math_mode=True) - def reduce_temporal(self, reducer: Union[str, PGNode, typing.Callable, UDF]) -> 'DataCube': + def reduce_temporal(self, reducer: Union[str, PGNode, typing.Callable, UDF]) -> DataCube: """ Shortcut for :py:meth:`reduce_dimension` along the temporal dimension @@ -1173,9 +1172,7 @@ def reduce_temporal(self, reducer: Union[str, PGNode, typing.Callable, UDF]) -> "Use :py:meth:`reduce_bands` with :py:class:`UDF ` as reducer.", version="0.13.0", ) - def reduce_bands_udf( - self, code: str, runtime: Optional[str] = None, version: Optional[str] = None - ) -> "DataCube": + def reduce_bands_udf(self, code: str, runtime: Optional[str] = None, version: Optional[str] = None) -> DataCube: """ Use `reduce_dimension` process with given UDF along band/spectral dimension. """ @@ -1246,7 +1243,7 @@ def apply_neighborhood( size: List[Dict], overlap: List[dict] = None, context: Optional[dict] = None, - ) -> "DataCube": + ) -> DataCube: """ Applies a focal process to a data cube. @@ -1283,7 +1280,7 @@ def apply( self, process: Union[str, typing.Callable, UDF, PGNode], context: Optional[dict] = None, - ) -> "DataCube": + ) -> DataCube: """ Applies a unary process (a local operation) to each value of the specified or all dimensions in the data cube. @@ -1321,7 +1318,7 @@ def apply( ) @openeo_process(process_id="min", mode="reduce_dimension") - def min_time(self) -> 'DataCube': + def min_time(self) -> DataCube: """ Finds the minimum value of a time series for all bands of the input dataset. @@ -1330,7 +1327,7 @@ def min_time(self) -> 'DataCube': return self.reduce_temporal("min") @openeo_process(process_id="max", mode="reduce_dimension") - def max_time(self) -> 'DataCube': + def max_time(self) -> DataCube: """ Finds the maximum value of a time series for all bands of the input dataset. @@ -1339,7 +1336,7 @@ def max_time(self) -> 'DataCube': return self.reduce_temporal("max") @openeo_process(process_id="mean", mode="reduce_dimension") - def mean_time(self) -> "DataCube": + def mean_time(self) -> DataCube: """ Finds the mean value of a time series for all bands of the input dataset. @@ -1348,7 +1345,7 @@ def mean_time(self) -> "DataCube": return self.reduce_temporal("mean") @openeo_process(process_id="median", mode="reduce_dimension") - def median_time(self) -> "DataCube": + def median_time(self) -> DataCube: """ Finds the median value of a time series for all bands of the input dataset. @@ -1357,7 +1354,7 @@ def median_time(self) -> "DataCube": return self.reduce_temporal("median") @openeo_process(process_id="count", mode="reduce_dimension") - def count_time(self) -> "DataCube": + def count_time(self) -> DataCube: """ Counts the number of images with a valid mask in a time series for all bands of the input dataset. @@ -1373,7 +1370,7 @@ def aggregate_temporal( labels: Optional[List[str]] = None, dimension: Optional[str] = None, context: Optional[dict] = None, - ) -> "DataCube": + ) -> DataCube: """ Computes a temporal aggregation based on an array of date and/or time intervals. @@ -1421,7 +1418,7 @@ def aggregate_temporal_period( reducer: Union[str, PGNode, typing.Callable], dimension: Optional[str] = None, context: Optional[Dict] = None, - ) -> "DataCube": + ) -> DataCube: """ Computes a temporal aggregation based on calendar hierarchies such as years, months or seasons. For other calendar hierarchies aggregate_temporal can be used. @@ -1462,7 +1459,7 @@ def aggregate_temporal_period( ) @openeo_process - def ndvi(self, nir: str = None, red: str = None, target_band: str = None) -> 'DataCube': + def ndvi(self, nir: str = None, red: str = None, target_band: str = None) -> DataCube: """ Normalized Difference Vegetation Index (NDVI) @@ -1507,7 +1504,7 @@ def rename_dimension(self, source: str, target: str): ) @openeo_process - def rename_labels(self, dimension: str, target: list, source: list = None) -> 'DataCube': + def rename_labels(self, dimension: str, target: list, source: list = None) -> DataCube: """ Renames the labels of the specified dimension in the data cube from source to target. @@ -1529,7 +1526,7 @@ def rename_labels(self, dimension: str, target: list, source: list = None) -> 'D ) @openeo_process(mode="apply") - def linear_scale_range(self, input_min, input_max, output_min, output_max) -> 'DataCube': + def linear_scale_range(self, input_min, input_max, output_min, output_max) -> DataCube: """ Performs a linear transformation between the input and output range. @@ -1554,7 +1551,7 @@ def linear_scale_range(self, input_min, input_max, output_min, output_max) -> 'D return self.apply(lambda x: x.linear_scale_range(input_min, input_max, output_min, output_max)) @openeo_process - def mask(self, mask: "DataCube" = None, replacement=None) -> "DataCube": + def mask(self, mask: DataCube = None, replacement=None) -> DataCube: """ Applies a mask to a raster data cube. To apply a vector mask use `mask_polygon`. @@ -1574,11 +1571,12 @@ def mask(self, mask: "DataCube" = None, replacement=None) -> "DataCube": @openeo_process def mask_polygon( - self, - mask: Union[shapely.geometry.base.BaseGeometry, dict, str, pathlib.Path, Parameter, "VectorCube"], - srs: str = None, - replacement=None, inside: bool = None - ) -> 'DataCube': + self, + mask: Union[shapely.geometry.base.BaseGeometry, dict, str, pathlib.Path, Parameter, VectorCube], + srs: str = None, + replacement=None, + inside: bool = None, + ) -> DataCube: """ Applies a polygon mask to a raster data cube. To apply a raster mask use `mask`. @@ -1612,11 +1610,11 @@ def mask_polygon( @openeo_process def merge_cubes( - self, - other: 'DataCube', - overlap_resolver: Union[str, PGNode, typing.Callable] = None, - context: Optional[dict] = None, - ) -> 'DataCube': + self, + other: DataCube, + overlap_resolver: Union[str, PGNode, typing.Callable] = None, + context: Optional[dict] = None, + ) -> DataCube: """ Merging two data cubes @@ -1658,7 +1656,7 @@ def merge_cubes( def apply_kernel( self, kernel: Union[np.ndarray, List[List[float]]], factor=1.0, border=0, replace_invalid=0 - ) -> "DataCube": + ) -> DataCube: """ Applies a focal operation based on a weighted kernel to each value of the specified dimensions in the data cube. @@ -1689,7 +1687,7 @@ def apply_kernel( @openeo_process def resolution_merge( self, high_resolution_bands: List[str], low_resolution_bands: List[str], method: str = None - ) -> "DataCube": + ) -> DataCube: """ Resolution merging algorithms try to improve the spatial resolution of lower resolution bands (e.g. Sentinel-2 20M) based on higher resolution bands. (e.g. Sentinel-2 10M). @@ -1796,7 +1794,7 @@ def polygonal_standarddeviation_timeseries( def ard_surface_reflectance( self, atmospheric_correction_method: str, cloud_detection_method: str, elevation_model: str = None, atmospheric_correction_options: dict = None, cloud_detection_options: dict = None, - ) -> 'DataCube': + ) -> DataCube: """ Computes CARD4L compliant surface reflectance values from optical input. @@ -1817,12 +1815,7 @@ def ard_surface_reflectance( }) @openeo_process - def atmospheric_correction( - self, - method: str = None, - elevation_model: str = None, - options: dict = None - ) -> 'DataCube': + def atmospheric_correction(self, method: str = None, elevation_model: str = None, options: dict = None) -> DataCube: """ Applies an atmospheric correction that converts top of atmosphere reflectance values into bottom of atmosphere/top of canopy reflectance values. @@ -1846,7 +1839,7 @@ def save_result( self, format: str = _DEFAULT_RASTER_FORMAT, options: Optional[dict] = None, - ) -> "DataCube": + ) -> DataCube: formats = set(self._connection.list_output_formats().keys()) # TODO: map format to correct casing too? if format.lower() not in {f.lower() for f in formats}: @@ -1865,7 +1858,7 @@ def _ensure_save_result( self, format: Optional[str] = None, options: Optional[dict] = None, - ) -> "DataCube": + ) -> DataCube: """ Make sure there is a (final) `save_result` node in the process graph. If there is already one: check if it is consistent with the given format/options (if any) @@ -2133,7 +2126,7 @@ def execute_local_udf(udf: str, datacube: Union[str, 'xarray.DataArray', 'Xarray def ard_normalized_radar_backscatter( self, elevation_model: str = None, contributing_area=False, ellipsoid_incidence_angle: bool = False, noise_removal: bool = True - ) -> "DataCube": + ) -> DataCube: """ Computes CARD4L compliant backscatter (gamma0) from SAR input. This method is a variant of :py:meth:`~openeo.rest.datacube.DataCube.sar_backscatter`, @@ -2169,7 +2162,7 @@ def sar_backscatter( ellipsoid_incidence_angle: bool = False, noise_removal: bool = True, options: Optional[dict] = None - ) -> "DataCube": + ) -> DataCube: """ Computes backscatter from SAR input. @@ -2299,7 +2292,7 @@ def predict_random_forest(self, model: Union[str, BatchJob, MlModel], dimension: return self.reduce_dimension(dimension=dimension, reducer=reducer, context=model) @openeo_process - def dimension_labels(self, dimension: str) -> "DataCube": + def dimension_labels(self, dimension: str) -> DataCube: """ Gives all labels for a dimension in the data cube. The labels have the same order as in the data cube. diff --git a/openeo/rest/vectorcube.py b/openeo/rest/vectorcube.py index a266c67bb..3548f7f6c 100644 --- a/openeo/rest/vectorcube.py +++ b/openeo/rest/vectorcube.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json import pathlib import typing @@ -30,7 +32,7 @@ class VectorCube(_ProcessGraphAbstraction): A geometry is specified in a 'coordinate reference system'. https://www.w3.org/TR/sdw-bp/#dfn-coordinate-reference-system-(crs) """ - def __init__(self, graph: PGNode, connection: 'Connection', metadata: CollectionMetadata = None): + def __init__(self, graph: PGNode, connection: Connection, metadata: CollectionMetadata = None): super().__init__(pgnode=graph, connection=connection) self.metadata = metadata or self._build_metadata() @@ -51,7 +53,7 @@ def process( metadata: Optional[CollectionMetadata] = None, namespace: Optional[str] = None, **kwargs, - ) -> "VectorCube": + ) -> VectorCube: """ Generic helper to create a new DataCube by applying a process. @@ -66,10 +68,10 @@ def process( @openeo_process def load_geojson( cls, - connection: "openeo.Connection", + connection: Connection, data: Union[dict, str, pathlib.Path, shapely.geometry.base.BaseGeometry, Parameter], properties: Optional[List[str]] = None, - ) -> "VectorCube": + ) -> VectorCube: """ Converts GeoJSON data as defined by RFC 7946 into a vector data cube. @@ -115,9 +117,7 @@ def load_geojson( @classmethod @openeo_process - def load_url( - cls, connection: "openeo.Connection", url: str, format: str, options: Optional[dict] = None - ) -> "VectorCube": + def load_url(cls, connection: Connection, url: str, format: str, options: Optional[dict] = None) -> VectorCube: """ Loads a file from a URL @@ -144,7 +144,7 @@ def run_udf( runtime: Optional[str] = None, version: Optional[str] = None, context: Optional[dict] = None, - ) -> "VectorCube": + ) -> VectorCube: """ Run a UDF on the vector cube. @@ -194,7 +194,7 @@ def _ensure_save_result( self, format: Optional[str] = None, options: Optional[dict] = None, - ) -> "VectorCube": + ) -> VectorCube: """ Make sure there is a (final) `save_result` node in the process graph. If there is already one: check if it is consistent with the given format/options (if any) @@ -328,7 +328,7 @@ def create_job( send_job = legacy_alias(create_job, name="send_job", since="0.10.0") @openeo_process - def filter_bands(self, bands: List[str]) -> "VectorCube": + def filter_bands(self, bands: List[str]) -> VectorCube: """ .. versionadded:: 0.22.0 """ @@ -348,7 +348,7 @@ def filter_bbox( north: Optional[float] = None, extent: Optional[Union[dict, List[float], Tuple[float, float, float, float], Parameter]] = None, crs: Optional[int] = None, - ) -> "VectorCube": + ) -> VectorCube: """ .. versionadded:: 0.22.0 """ @@ -370,7 +370,7 @@ def filter_bbox( @openeo_process def filter_labels( self, condition: Union[PGNode, Callable], dimension: str, context: Optional[dict] = None - ) -> "VectorCube": + ) -> VectorCube: """ .. versionadded:: 0.22.0 """ @@ -384,7 +384,7 @@ def filter_labels( @openeo_process def filter_vector( self, geometries: Union["VectorCube", shapely.geometry.base.BaseGeometry, dict], relation: str = "intersects" - ) -> "VectorCube": + ) -> VectorCube: """ .. versionadded:: 0.22.0 """ @@ -488,7 +488,7 @@ def apply_dimension( dimension: str, target_dimension: Optional[str] = None, context: Optional[dict] = None, - ) -> "VectorCube": + ) -> VectorCube: """ Applies a process to all values along a dimension of a data cube. For example, if the temporal dimension is specified the process will work on the values of a time series.