"""
Data request interface for downloading satellite data from AWS
"""
import functools
from abc import abstractmethod
from typing import Any, Generic, List, Optional, Tuple, TypeVar, Union
from ..base import DataRequest
from ..data_collections import DataCollection
from ..exceptions import deprecated_class
from .client import AwsDownloadClient
from .data import REQUESTER_PAYS_PARAMS, AwsProduct, AwsTile
from .data_safe import SafeProduct, SafeTile
T = TypeVar("T")
class _BaseAwsDataRequest(DataRequest, Generic[T]):
"""The base class for Amazon Web Service request classes. Common parameters are defined here.
Collects and provides data from AWS.
More information about Sentinel-2 AWS registry: https://registry.opendata.aws/sentinel-2/
"""
def __init__(
self,
*,
bands: Union[None, str, List[str]] = None,
metafiles: Union[None, str, List[str]] = None,
safe_format: bool = False,
**kwargs: Any,
):
"""
:param bands: List of Sentinel-2 bands for request. If `None` all bands will be obtained
:param metafiles: list of additional metafiles available on AWS
(e.g. ``['metadata', 'tileInfo', 'preview/B01', 'TCI']``)
:param safe_format: flag that determines the structure of saved data. If `True` it will be saved in .SAFE format
defined by ESA. If `False` it will be saved in the same structure as the structure at AWS.
:param data_folder: location of the directory where the fetched data will be saved.
:param config: A custom instance of config class to override parameters from the saved configuration.
"""
self.bands = bands
self.metafiles = metafiles
self.safe_format = safe_format
self.aws_service: T
client_class = functools.partial(AwsDownloadClient, boto_params=REQUESTER_PAYS_PARAMS)
super().__init__(client_class, **kwargs)
@abstractmethod
def create_request(self) -> None:
raise NotImplementedError
def get_aws_service(self) -> T:
"""
:return: initialized AWS service class
"""
return self.aws_service
[docs]@deprecated_class(message_suffix="It will remain in the codebase for now, but won't be actively maintained.")
class AwsProductRequest(_BaseAwsDataRequest[AwsProduct]):
"""AWS Service request class for an ESA product."""
def __init__(self, product_id: str, *, tile_list: Optional[List[str]] = None, **kwargs: Any):
"""
:param product_id: original ESA product identification string
(e.g. ``'S2A_MSIL1C_20170414T003551_N0204_R016_T54HVH_20170414T003551'``)
:param tile_list: list of tiles inside the product to be downloaded. If parameter is set to `None` all
tiles inside the product will be downloaded.
:param bands: List of Sentinel-2 bands for request. If `None` all bands will be obtained
:param metafiles: list of additional metafiles available on AWS
(e.g. ``['metadata', 'tileInfo', 'preview/B01', 'TCI']``)
:param safe_format: flag that determines the structure of saved data. If `True` it will be saved in .SAFE format
defined by ESA. If `False` it will be saved in the same structure as the structure at AWS.
:param data_folder: location of the directory where the fetched data will be saved.
:param config: A custom instance of config class to override parameters from the saved configuration.
"""
self.product_id = product_id
self.tile_list = tile_list
super().__init__(**kwargs)
[docs] def create_request(self) -> None:
product_class = SafeProduct if self.safe_format else AwsProduct
self.aws_service = product_class(
self.product_id,
tile_list=self.tile_list,
bands=self.bands,
metafiles=self.metafiles,
config=self.config,
)
self.download_list, self.folder_list = self.aws_service.get_requests()
[docs]@deprecated_class(message_suffix="It will remain in the codebase for now, but won't be actively maintained.")
class AwsTileRequest(_BaseAwsDataRequest[AwsTile]):
"""AWS Service request class for an ESA tile."""
def __init__(
self,
*,
data_collection: DataCollection,
tile: Optional[str] = None,
time: Optional[str] = None,
aws_index: Optional[int] = None,
**kwargs: Any,
):
"""
:param data_collection: A collection of requested AWS data. Supported collections are Sentinel-2 L1C and
Sentinel-2 L2A.
:param tile: tile name (e.g. ``'T10UEV'``)
:param time: tile sensing time in ISO8601 format
:param aws_index: there exist Sentinel-2 tiles with the same tile and time parameter. Therefore, each tile on
AWS also has an index which is visible in their url path. If aws_index is set to `None` the class
will try to find the index automatically. If there will be multiple choices it will choose the
lowest index and inform the user.
:param bands: List of Sentinel-2 bands for request. If `None` all bands will be obtained
:param metafiles: list of additional metafiles available on AWS
(e.g. ``['metadata', 'tileInfo', 'preview/B01', 'TCI']``)
:param safe_format: flag that determines the structure of saved data. If `True` it will be saved in .SAFE
format defined by ESA. If `False` it will be saved in the same structure as the structure at AWS.
:param data_folder: location of the directory where the fetched data will be saved.
:param config: A custom instance of config class to override parameters from the saved configuration.
"""
self.data_collection = data_collection
self.tile = tile
self.time = time
self.aws_index = aws_index
super().__init__(**kwargs)
[docs] def create_request(self) -> None:
if self.tile is None or self.time is None:
raise ValueError("The parameters `tile` and `time` must be set.")
tile_class = SafeTile if self.safe_format else AwsTile
self.aws_service = tile_class(
self.tile,
self.time,
self.aws_index,
bands=self.bands,
metafiles=self.metafiles,
data_collection=self.data_collection,
config=self.config,
)
self.download_list, self.folder_list = self.aws_service.get_requests()