Skip to content
This repository has been archived by the owner on Jul 24, 2024. It is now read-only.

Commit

Permalink
Merge pull request #3 from CS-SI/develop
Browse files Browse the repository at this point in the history
v0.3.0
  • Loading branch information
sbrunato authored Mar 26, 2021
2 parents 381f648 + 763ab7b commit d2e6ace
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 82 deletions.
20 changes: 20 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Release history
---------------

0.3.0 (2021-03-26)
++++++++++++++++++

- Search using rounded coords in WKT geometries for shorter request URL
- Package building updated to fix integration with eodag
- Various minor fixes

0.2.0 (2021-03-18)
++++++++++++++++++

- Update and align to eodag 2.1.0
- Project moved to github

0.1.0 (2018-07-30)
++++++++++++++++++

- First release
25 changes: 16 additions & 9 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
.. image:: https://badge.fury.io/py/eodag-sentinelsat.svg
:target: https://badge.fury.io/py/eodag-sentinelsat

.. image:: https://img.shields.io/pypi/l/eodag-sentinelsat.svg
:target: https://pypi.org/project/eodag-sentinelsat/

.. image:: https://img.shields.io/pypi/pyversions/eodag-sentinelsat.svg
:target: https://pypi.org/project/eodag-sentinelsat/

eodag-sentinelsat
=================

Expand All @@ -7,21 +16,19 @@ search and download EO products from catalogs implementing the
`SciHub / Copernicus Open Access Hub interface <https://scihub.copernicus.eu/userguide/WebHome>`_.
It is basically a wrapper around `sentinelsat <https://sentinelsat.readthedocs.io>`_, enabling it to be used on eodag.

.. image:: https://eodag.readthedocs.io/en/latest/_static/eodag_bycs.png
:target: https://github.com/CS-SI/eodag

|

Installation
============

* If you already have a particular version of eodag installed on your system::
eodag-sentinelsat is on `PyPI <https://pypi.org/project/eodag-sentinelsat/>`_::

python -m pip install eodag-sentinelsat

* If you don't have eodag installed and want it installed and knowing about sentinelsat plugin or if you want to
develop on this repository::

python -m pip install eodag-sentinelsat[standalone]

The standalone install will install eodag itself along the way


Contribute
==========
Expand All @@ -30,7 +37,7 @@ If you intend to contribute to eodag-sentinelsat source code::

git clone https://github.com/CS-SI/eodag-sentinelsat.git
cd eodag-sentinelsat
python -m pip install -e .[standalone,dev]
python -m pip install -e .[dev]
pre-commit install
tox

Expand Down
17 changes: 17 additions & 0 deletions eodag_sentinelsat/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# eodag-sentinelsat, a plugin for searching and downloading products from Copernicus Scihub
# Copyright 2021, CS GROUP - France, http://www.c-s.fr
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Sentinelsat plugin to EODAG."""
138 changes: 77 additions & 61 deletions eodag_sentinelsat.py → eodag_sentinelsat/eodag_sentinelsat.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
import zipfile
from datetime import datetime

from eodag.api.search_result import SearchResult
from eodag.plugins.apis.base import Api
from eodag.plugins.search.qssearch import ODataV4Search
from eodag.utils import get_progress_callback
from sentinelsat import SentinelAPI
from eodag.utils.exceptions import MisconfiguredError, RequestError
from sentinelsat import SentinelAPI, SentinelAPIError

logger = logging.getLogger("eodag.plugins.apis.sentinelsat")

Expand All @@ -35,6 +37,13 @@ class SentinelsatAPI(Api, ODataV4Search):
Api that enables to search and download EO products from catalogs implementing the SchiHub interface.
It is basically a wrapper around sentinelsat, enabling it to be used on eodag.
We use the API to download data. Available keywords are:
[area, date, raw, area_relation, order_by, limit, offset, **keywords]
https://sentinelsat.readthedocs.io/en/stable/api.html#sentinelsat.SentinelAPI.query
The keywords are those that can be found here:
https://sentinelsat.readthedocs.io/en/stable/api.html#opensearch-example
"""

def __init__(self, provider, config):
Expand Down Expand Up @@ -94,6 +103,22 @@ def query(self, items_per_page=None, page=None, count=True, **kwargs):
)
logger.info("No results found !")

except SentinelAPIError as ex:
# TODO: change it to ServerError when ssat 0.15 will be published !
"""
SentinelAPIError -- the parent, catch-all exception. Only used when no other more specific exception
can be applied.
SentinelAPILTAError -- raised when retrieving a product from the Long Term Archive.
ServerError -- raised when the server responded in an unexpected manner, typically due to undergoing
maintenance.
UnauthorizedError -- raised when attempting to retrieve a product with incorrect credentials.
QuerySyntaxError -- raised when the query string could not be parsed on the server side.
QueryLengthError -- raised when the query string length was excessively long.
InvalidKeyError -- raised when product with given key was not found on the server.
InvalidChecksumError -- MD5 checksum of a local file does not match the one from the server.
"""
raise RequestError(ex) from ex

return eo_products, len(eo_products)

def download(self, product, auth=None, progress_callback=None, **kwargs) -> str:
Expand All @@ -106,35 +131,19 @@ def download(self, product, auth=None, progress_callback=None, **kwargs) -> str:
:param kwargs: Not used, just here for compatibility reasons
:return: Downloaded product path
"""
# Init Sentinelsat API if needed (connect...)
self._init_api()

# Download all products
prod_id = product.properties["id"]
product_info = self.api.download_all(
[prod_id], directory_path=self.config.outputs_prefix
prods = self.download_all(
SearchResult(
[
product,
]
),
auth,
progress_callback,
**kwargs
)

# Only select the downloaded products
product_info = product_info[0][prod_id]

# Extract them if needed
if self.config.extract and product_info["path"].endswith(".zip"):
logger.info("Extraction activated")
with zipfile.ZipFile(product_info["path"], "r") as zfile:
fileinfos = zfile.infolist()
with get_progress_callback() as bar:
bar.max_size = len(fileinfos)
bar.unit = "file"
bar.desc = "Extracting files from {}".format(product_info["path"])
bar.unit_scale = False
bar.position = 2
for fileinfo in fileinfos:
zfile.extract(fileinfo, path=self.config.outputs_prefix)
bar(1)
return product_info["path"][: product_info["path"].index(".zip")]
else:
return product_info["path"]
# Manage the case if nothing has been downloaded
return prods[0] if len(prods) > 0 else ""

def download_all(
self, search_result, auth=None, progress_callback=None, **kwargs
Expand All @@ -154,44 +163,51 @@ def download_all(

# Download all products
prod_ids = [prod.properties["uuid"] for prod in search_result.data]
product_info = self.api.download_all(
success, _, _ = self.api.download_all(
prod_ids, directory_path=self.config.outputs_prefix
)

# Only select the downloaded products
paths = []
for prod_id in prod_ids:
info = product_info[0][prod_id]

# Extract them if needed
if self.config.extract and info["path"].endswith(".zip"):
logger.info("Extraction activated")
with zipfile.ZipFile(info["path"], "r") as zfile:
fileinfos = zfile.infolist()
with get_progress_callback() as bar:
bar.max_size = len(fileinfos)
bar.unit = "file"
bar.desc = "Extracting files from {}".format(info["path"])
bar.unit_scale = False
bar.position = 2
for fileinfo in fileinfos:
zfile.extract(fileinfo, path=self.config.outputs_prefix)
bar(1)
paths.append(info["path"][: info["path"].index(".zip")])
else:
paths.append(info["path"])

# Only extract the successfully downloaded products
paths = [self.extract(prods) for prods in success.values()]
return paths

def _init_api(self):
def extract(self, product_info: dict) -> str:
"""
Extract products if needed.
:param product_info: Product info
:return: Path (archive or extracted according to the config)
"""
# Extract them if needed
if self.config.extract and product_info["path"].endswith(".zip"):
logger.info("Extraction activated")
with zipfile.ZipFile(product_info["path"], "r") as zfile:
fileinfos = zfile.infolist()
with get_progress_callback() as bar:
bar.max_size = len(fileinfos)
bar.unit = "file"
bar.desc = "Extracting files from {}".format(product_info["path"])
bar.unit_scale = False
bar.position = 2
for fileinfo in fileinfos:
zfile.extract(fileinfo, path=self.config.outputs_prefix)
bar(1)
return product_info["path"][: product_info["path"].index(".zip")]
else:
return product_info["path"]

def _init_api(self) -> None:
"""Initialize Sentinelsat API if needed (connection and link)."""
if not self.api:
logger.debug("Initializing Sentinelsat API")
self.api = SentinelAPI(
self.config.credentials["username"],
self.config.credentials["password"],
self.config.endpoint,
)
try:
logger.debug("Initializing Sentinelsat API")
self.api = SentinelAPI(
self.config.credentials["username"],
self.config.credentials["password"],
self.config.endpoint,
)
except KeyError as ex:
raise MisconfiguredError(ex) from ex
else:
logger.debug("Sentinelsat API already initialized")

Expand Down Expand Up @@ -242,7 +258,7 @@ def update_keyword(self, **kwargs):
)

# Footprint
if "area" in qp:
qp["area"] = qp.pop("area").wkt
if "area" in qp and isinstance(qp["area"], list):
qp["area"] = qp["area"][0]

return qp, provider_product_type
Loading

0 comments on commit d2e6ace

Please sign in to comment.