diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index 5c85bc5..7dad676 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -2,9 +2,9 @@ name: ukis-pysat on: push: - branches: [ master ] + branches: [ main ] pull_request: - branches: [ master ] + branches: [ main ] jobs: build: diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 93048c3..499a376 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,7 +1,11 @@ Changelog ========= -[] () +[1.4.0] (2022-03-09) --------------------- +Changed +^^^^^^^ +- Sentinel items closer to standards of stactools #155 + Deleted ^^^^^ - removed own stac api client (pystac-client should be used from now on) diff --git a/requirements.txt b/requirements.txt index 8d13e68..11997cf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ pystac[validation]>=1.0.0 python-dateutil requests sentinelsat>=1.0.1 -shapely~=1.7.1 +shapely pydantic ## raster diff --git a/setup.py b/setup.py index cc13a49..a37be62 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,6 @@ from setuptools import setup, find_packages - with open(r"README.md", encoding="utf8") as f: long_description = f.read() @@ -55,7 +54,7 @@ def get_version(rel_path): "pyproj>=3.0.0", "requests_mock", "utm>=0.7.0", - "sphinx >= 1.3", + "sphinx>=1.3", "sphinx_rtd_theme", ] ) diff --git a/tests/test_data.py b/tests/test_data.py index 81e2691..148e8c0 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -1,21 +1,21 @@ import unittest +from datetime import datetime from pathlib import Path from tempfile import gettempdir + import pystac import requests_mock -from shapely.geometry import Polygon -from datetime import datetime -import os from pkg_resources import resource_filename +from shapely.geometry import Polygon from ukis_pysat._landsat import Product, meta_from_pid, compute_md5 from ukis_pysat.data import Source from ukis_pysat.members import Datahub, Platform, Bands -os.environ["EARTHEXPLORER_USER"] = "Tim" -os.environ["EARTHEXPLORER_PW"] = "TheEnchanter" -os.environ["SCIHUB_USER"] = "Tim" -os.environ["SCIHUB_PW"] = "TheEnchanter" +# os.environ["EARTHEXPLORER_USER"] = "Tim" +# os.environ["EARTHEXPLORER_PW"] = "TheEnchanter" +# os.environ["SCIHUB_USER"] = "Tim" +# os.environ["SCIHUB_PW"] = "TheEnchanter" catalog_path = Path(__file__).parents[0] / "testfiles" / "catalog.json" target_dir = Path(__file__).parents[0] / "testfiles" diff --git a/tests/test_raster.py b/tests/test_raster.py index 5fad748..7da7eee 100644 --- a/tests/test_raster.py +++ b/tests/test_raster.py @@ -9,6 +9,7 @@ from rasterio.coords import BoundingBox from rasterio.transform import from_bounds from shapely.geometry import box + from ukis_pysat.members import Platform from ukis_pysat.raster import Image diff --git a/ukis_pysat/__init__.py b/ukis_pysat/__init__.py index 077efb7..1e1e288 100644 --- a/ukis_pysat/__init__.py +++ b/ukis_pysat/__init__.py @@ -1 +1 @@ -__version__ = "1.3.4" +__version__ = "1.4.0" diff --git a/ukis_pysat/data.py b/ukis_pysat/data.py index c3569c7..72f9346 100644 --- a/ukis_pysat/data.py +++ b/ukis_pysat/data.py @@ -6,9 +6,12 @@ from pathlib import Path from dateutil.parser import parse +from pystac.extensions.projection import ProjectionExtension +from pystac.extensions.sar import SarExtension +from ukis_pysat import utils from ukis_pysat._landsat import Product - +from ukis_pysat.utils import SENTINEL_PROVIDER, LANDSAT_PROVIDER try: import numpy as np @@ -18,7 +21,7 @@ from PIL import Image from pystac.extensions import sat from pystac.extensions.eo import EOExtension - from pystac.extensions.sat import SatExtension + from pystac.extensions.sat import SatExtension, OrbitState from shapely import geometry, wkt, ops from shapely.geometry import shape, mapping except ImportError as e: @@ -223,18 +226,17 @@ def construct_metadata(self, meta, platform): "end_datetime": meta["stop_time"].astimezone(tz=datetime.timezone.utc).isoformat(), }, ) - EOExtension.add_to(item) - SatExtension.add_to(item) - eo_ext = EOExtension.ext(item) - sat_ext = SatExtension.ext(item) + # extensions + sat_ext = SatExtension.ext(item, add_if_missing=True) + relative_orbit = int(f"{meta['wrs_path']}{meta['wrs_row']}") + sat_ext.apply(orbit_state=sat.OrbitState.DESCENDING, relative_orbit=relative_orbit) + eo_ext = EOExtension.ext(item, add_if_missing=True) if "cloudCover" in meta: eo_ext.cloud_cover = round(float(meta["cloud_cover"]), 2) - item.common_metadata.platform = platform.value - - relative_orbit = int(f"{meta['wrs_path']}{meta['wrs_row']}") - sat_ext.apply(orbit_state=sat.OrbitState.DESCENDING, relative_orbit=relative_orbit) + # common + item.common_metadata.providers = [LANDSAT_PROVIDER] else: # Scihub item = pystac.Item( @@ -254,21 +256,24 @@ def construct_metadata(self, meta, platform): .astimezone(tz=datetime.timezone.utc) .isoformat(), }, + stac_extensions=[], ) - EOExtension.add_to(item) - SatExtension.add_to(item) - eo_ext = EOExtension.ext(item) - sat_ext = SatExtension.ext(item) + # extensions + sat_ext = SatExtension.ext(item, add_if_missing=True) + sat_ext.apply( + orbit_state=OrbitState[meta["properties"]["orbitdirection"].upper()], # for enum key to work + relative_orbit=int(meta["properties"]["orbitnumber"]), + ) + + eo_ext = EOExtension.ext(item, add_if_missing=True) if "cloudcoverpercentage" in meta["properties"]: eo_ext.cloud_cover = round(float(meta["properties"]["cloudcoverpercentage"]), 2) - item.common_metadata.platform = platform.value + # common + item.common_metadata.providers = [SENTINEL_PROVIDER] - sat_ext.apply( - orbit_state=sat.OrbitState[meta["properties"]["orbitdirection"].upper()], # for enum key to work - relative_orbit=int(meta["properties"]["orbitnumber"]), - ) + item.common_metadata.platform = platform.value return item diff --git a/ukis_pysat/members.py b/ukis_pysat/members.py index 849fbf5..4cebbd4 100644 --- a/ukis_pysat/members.py +++ b/ukis_pysat/members.py @@ -1,4 +1,5 @@ from enum import Enum + from pydantic import BaseModel diff --git a/ukis_pysat/utils.py b/ukis_pysat/utils.py new file mode 100644 index 0000000..a0317ed --- /dev/null +++ b/ukis_pysat/utils.py @@ -0,0 +1,40 @@ +import pystac +from geojson import Feature +from pystac import ProviderRole +from pystac.extensions.sar import FrequencyBand, SarExtension, Polarization + + +def fill_sar_ext(sar_ext: SarExtension, meta: Feature): + # Fixed properties + sar_ext.frequency_band = FrequencyBand("C") + sar_ext.center_frequency = 5.405 + # rest (like e.g. sar:pixel_spacing_range) cannot be known from API response AFAIK + + # Read properties + sar_ext.instrument_mode = meta["properties"]["sensoroperationalmode"] + sar_ext.product_type = meta["properties"]["producttype"] + + # TODO maybe this is not good, because we often only use one later on + sar_ext.polarizations = [Polarization(p) for p in meta["properties"]["polarisationmode"].split(" ")] + + +# constants +SENTINEL_PROVIDER = pystac.Provider( + name="ESA", + roles=[ + ProviderRole.PRODUCER, + ProviderRole.PROCESSOR, + ProviderRole.LICENSOR, + ], + url="https://earth.esa.int/web/guest/home", +) + +LANDSAT_PROVIDER = pystac.Provider( + name="USGS", + roles=[ + ProviderRole.PRODUCER, + ProviderRole.PROCESSOR, + ProviderRole.LICENSOR, + ], + url="https://earthexplorer.usgs.gov/", +)