Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a simple dataspace client library #145

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
28 changes: 0 additions & 28 deletions pyproject.toml.rej

This file was deleted.

16 changes: 16 additions & 0 deletions tests/test_10_dataspace_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import datetime
from xarray_sentinel import dataspace_client


def test_DataSpaceClient() -> None:
filter_template = "{start_date_iso} {stop_date_iso} {relative_orbit} {geometry_wkt}"
client = dataspace_client.DataSpaceClient(
products_odata_filter_template=filter_template
)
date = datetime.datetime(2020, 2, 3)
bbox = (0, 1, 2, 3)
expected = "2020-02-03T00:00:00 2020-02-03T00:00:00 22 SRID=4326;POLYGON ((2 1, 2 3, 0 3, 0 1, 2 1))"

res = client.build_sentinel1_products_odata_filter(date, date, 22, bbox)

assert res == expected
93 changes: 93 additions & 0 deletions xarray_sentinel/dataspace_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import datetime
from typing import Any

import requests
import shapely
import typer

DEFAULT_ODATA_URL: str = "https://catalogue.dataspace.copernicus.eu/odata/v1"
DEFAULT_PRODUCTS_ODATA_FILTER_TEMPLATE = (
"((Collection/Name eq 'SENTINEL-1' "
"and (Attributes/OData.CSC.StringAttribute/any(att:att/Name eq 'instrumentShortName' "
"and att/OData.CSC.StringAttribute/Value eq 'SAR') "
"and (contains(Name,'GRD') and contains(Name,'_COG') "
"and OData.CSC.Intersects(area=geography'{geometry_wkt}'))) "
"and (Attributes/OData.CSC.StringAttribute/any(att:att/Name eq 'operationalMode' "
"and att/OData.CSC.StringAttribute/Value eq 'IW') "
"and Attributes/OData.CSC.IntegerAttribute/any(att:att/Name eq 'relativeOrbitNumber' "
"and att/OData.CSC.IntegerAttribute/Value eq {relative_orbit}))) "
"and ContentDate/Start ge {start_date_iso}Z and ContentDate/Start lt {stop_date_iso}Z)"
)


class DataSpaceClient:
def __init__(
self,
odata_url: str = DEFAULT_ODATA_URL,
products_odata_filter_template: str = DEFAULT_PRODUCTS_ODATA_FILTER_TEMPLATE,
) -> None:
self.odata_url = odata_url
self.products_odata_filter_template = products_odata_filter_template

def build_sentinel1_products_odata_filter(
self,
start_date: datetime.datetime,
stop_date: datetime.datetime,
relative_orbit: int,
bbox: tuple[float, float, float, float] = (-180, -90, 180, 90),
) -> str:
geometry = shapely.box(*bbox)
geometry_wkt = f"SRID=4326;{geometry.wkt}"
sentinel1_products_odata_filter = self.products_odata_filter_template.format(
geometry_wkt=geometry_wkt,
start_date_iso=start_date.isoformat(),
stop_date_iso=stop_date.isoformat(),
relative_orbit=relative_orbit,
)
return sentinel1_products_odata_filter

def search_sentinel1_products(
self,
start_date: datetime.datetime,
stop_date: datetime.datetime,
relative_orbit: int,
bbox: tuple[float, float, float, float] = (-180, -90, 180, 90),
limit: int = 100,
) -> Any:
product_url = f"{self.odata_url}/Products"
odata_filter = self.build_sentinel1_products_odata_filter(
start_date, stop_date, relative_orbit, bbox
)
params: dict[str, str | int]
params = {
"$filter": odata_filter,
"$top": limit,
}
resp = requests.get(product_url, params=params)
resp.raise_for_status()
return resp.json()


def get_s3paths(results: list[Any]) -> list[str]:
return sorted([result.get("S3Path") for result in results])


def search_sentinel1_products(
start_date: datetime.datetime = datetime.datetime(2024, 1, 1),
stop_date: datetime.datetime = datetime.datetime(2024, 12, 31),
relative_orbit: int = 22,
bbox: tuple[float, float, float, float] = (6.75, 36.62, 18.48, 47.11),
limit: int = 100,
odata_url: str = DEFAULT_ODATA_URL,
) -> None:
dataspace_client = DataSpaceClient(odata_url)
resutls = dataspace_client.search_sentinel1_products(
start_date, stop_date, relative_orbit, bbox, limit
)
results = get_s3paths(resutls["value"])
for res in results:
print(res)


if __name__ == "__main__":
typer.run(search_sentinel1_products)
3 changes: 2 additions & 1 deletion xarray_sentinel/sentinel1.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from . import conventions, esa_safe

SPEED_OF_LIGHT = 299_792_458 # m / s
ONE_SECOND = np.timedelta64(1, "s")
ONE_SECOND = np.timedelta64(10**9, "ns")


DataArrayOrDataset = TypeVar("DataArrayOrDataset", xr.DataArray, xr.Dataset)
Expand Down Expand Up @@ -544,6 +544,7 @@ def make_azimuth_time(
start=product_first_line_utc_time,
end=product_last_line_utc_time,
periods=number_of_lines,
unit="ns",
)
return azimuth_time.values

Expand Down
Loading