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 ESA WorldCover, TDX/TSX via NASA CSDA (airbus) #2

Merged
merged 3 commits into from
Oct 3, 2024
Merged
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
5 changes: 5 additions & 0 deletions .github/workflows/pixi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ jobs:
cache-write:
${{ github.event_name == 'push' && github.ref_name == 'main' }}

# NOTE: https://github.com/prefix-dev/setup-pixi/issues/136
- name: Ensure Dynamic version
run: |
pip install -e .

- name: Run Pytest
env:
MAXAR_API_KEY: ${{ secrets.MAXAR_API_KEY}}
Expand Down
6 changes: 4 additions & 2 deletions src/coincident/datasets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from __future__ import annotations

from coincident.datasets import maxar, nasa, planetary_computer, usgs
from coincident.datasets import csda, maxar, nasa, planetary_computer, usgs
from coincident.datasets.general import Dataset

# Convenience mapping of string aliases to supported dataset classes
Expand All @@ -22,9 +22,11 @@
nasa.ICESat2(),
nasa.GEDI(),
planetary_computer.COP30(),
planetary_computer.WorldCover(),
csda.TDX(),
]

aliases = [x.alias for x in _datasets]
_alias_to_Dataset = dict(zip(aliases, _datasets, strict=False))

__all__ = ["Dataset", "usgs", "maxar", "nasa", "planetary_computer"]
__all__ = ["Dataset", "usgs", "maxar", "nasa", "planetary_computer", "csda"]
54 changes: 54 additions & 0 deletions src/coincident/datasets/csda.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""
NASA Commercial SmallSat Data Acquisition Program (CSDA)

https://www.earthdata.nasa.gov/esds/csda

TSX/TDX = https://csdap.earthdata.nasa.gov/stac/collections/airbus
# TODO: compare this archive against
# https://api.oneatlas.airbus.com/api-catalog-v2/radar/overview/index.html

Notes
https://github.com/stac-extensions/sar?tab=readme-ov-file#product-type
"""

from __future__ import annotations

from dataclasses import dataclass, field

from coincident.datasets.general import Dataset


@dataclass
class TDX(Dataset):
"""Essential metadata for TanDEM-X / TerraSAR-X
platform =
- PAZ (PAX)
- TerraSAR-X (TSX-1)
- TanDEM-X (TDX-1)

sar:product_type =
- Coregistered Single Look Slant Range Complex (COSSC)
- Geocoded Ellipsoid Corrected (GEC)
- Single Look Slant Range Complex (SSC)
- Enhanced Ellipsoid Corrected (EEC)

sar:instrument_mode =
- Staring Spotlight (ST)
- High Resolution Spotlight (HS)
- Spotlight (SL)
- Stripmap (SM)
- ScanSAR (SC)
- Wide ScanSAR (WSC)

Other properties to filter on:
sar:polarizations = ['HH']
orbitDirection = ASCENDING / DESCENDING
"""

alias: str = "tdx"
has_stac_api: bool = True
collections: list[str] = field(default_factory=lambda: ["airbus"])
search: str = "https://csdap.earthdata.nasa.gov/stac"
start: str = "2007-07-01" # TODO: check this
end: str | None = None
type: str = "sar"
2 changes: 2 additions & 0 deletions src/coincident/datasets/nasa.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
NASA Datasets

https://github.com/nasa/cmr-stac

NOTE: Different search endpoints dependng on DAAC
"""

# from pydantic.dataclasses import dataclass
Expand Down
20 changes: 19 additions & 1 deletion src/coincident/datasets/planetary_computer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
Microsoft Planetary Computer

https://planetarycomputer.microsoft.com/docs/quickstarts/reading-stac/

- https://planetarycomputer.microsoft.com/dataset/esa-worldcover
- https://planetarycomputer.microsoft.com/dataset/cop-dem-glo-30
"""

from __future__ import annotations
Expand All @@ -10,6 +13,8 @@

from coincident.datasets.general import Dataset

STACAPI = "https://planetarycomputer.microsoft.com/api/stac/v1"


@dataclass
class COP30(Dataset):
Expand All @@ -18,7 +23,20 @@ class COP30(Dataset):
alias: str = "cop30"
has_stac_api: bool = True
collections: list[str] = field(default_factory=lambda: ["cop-dem-glo-30"])
search: str = "https://planetarycomputer.microsoft.com/api/stac/v1"
search: str = STACAPI
start: str | None = None # NOTE: has 'representative' datetime of 2021-04-22
end: str | None = None
type: str = "sar"


@dataclass
class WorldCover(Dataset):
"""Essential metadata for ESA WorldCover"""

alias: str = "worldcover"
has_stac_api: bool = True
collections: list[str] = field(default_factory=lambda: ["esa-worldcover"])
search: str = STACAPI
start: str = "2020-01-01"
end: str = "2021-12-31"
type: str = "lulc"
15 changes: 11 additions & 4 deletions src/coincident/search/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ def search(
aoi = None

# STAC API Searches
# NOTE: refactor so that search config lives alongside dataset? easier to add, would need conditionals below
if dataset.has_stac_api:
stac_api_kwargs = {**kwargs, **dataset.stac_kwargs} # NOTE: override order?
stac_api_kwargs["datetime"] = datetime
Expand All @@ -108,13 +109,19 @@ def search(
# Client-side reduce to only acquisitions having stereo pairs
gf = gf.loc[gf.stereo_pair_identifiers.str[0].dropna().index]

elif dataset.alias in ["icesat-2", "gedi"]:
client = _stac.configure_nasa_client(dataset.search) # type: ignore[arg-type]
# elif dataset.alias in ["icesat-2", "gedi"]:
# client = _stac.configure_stac_client(dataset.search) # type: ignore[arg-type]
# results = _stac.search(client, **stac_api_kwargs)
# gf = _stac.to_geopandas(results)

elif dataset.alias in ["cop30", "worldcover"]:
client = _stac.configure_mspc_client(dataset.search) # type: ignore[arg-type]
results = _stac.search(client, **stac_api_kwargs)
gf = _stac.to_geopandas(results)

elif dataset.alias in ["cop30"]:
client = _stac.configure_mspc_client(dataset.search) # type: ignore[arg-type]
# Generic STAC endpoint w/o additional config
else: # icesat-2, gedi, worldcover,
client = _stac.configure_stac_client(dataset.search) # type: ignore[arg-type]
results = _stac.search(client, **stac_api_kwargs)
gf = _stac.to_geopandas(results)

Expand Down
4 changes: 2 additions & 2 deletions src/coincident/search/_stac.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def configure_maxar_client(area_based_calc: bool = True) -> pystac_client.client
return client


def configure_nasa_client(url: str) -> pystac_client.client.Client:
def configure_stac_client(url: str) -> pystac_client.client.Client:
""" """
return pystac_client.Client.open(url=url)

Expand All @@ -123,5 +123,5 @@ def configure_mspc_client(url: str) -> pystac_client.client.Client:
""" """
return pystac_client.Client.open(
url=url,
modifier=modifier,
modifier=modifier, # planetary_computer.sign_inplace?
)
18 changes: 18 additions & 0 deletions tests/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ def test_maxar_search(aoi):
assert "browse" in gf.columns


# NASA
# =======
@network
def test_icesat2_search(aoi):
gf = m.search.search(
Expand All @@ -91,13 +93,29 @@ def test_gedi_search(aoi):
assert len(gf) == 33


@network
def test_tdx_search(aoi):
gf = m.search.search(dataset="tdx", intersects=aoi, datetime=["2009", "2020"])
assert len(gf) == 48


# MS PLANETARY COMPUTER
# =======
@network
def test_cop30_search(aoi):
gf = m.search.search(dataset="cop30", intersects=aoi)
# expected_cols = ['geometry','gsd','datetime','platform','proj:epsg','proj:shape','proj:transform','stac_id','data']
assert len(gf) == 4


@network
def test_worldcover_search(aoi):
gf = m.search.search(dataset="worldcover", intersects=aoi, datetime="2020")
assert len(gf) == 4


# USGS
# =======
@network
def test_wesm_search(aoi):
gf = m.search.search(
Expand Down
Loading