Skip to content
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Updated search limit constraints to avoid 500s [#15](https://github.com/microsoft/planetary-computer-apis/pull/15)
- Fixed STAC `describedby` and `preview` links [#33](https://github.com/microsoft/planetary-computer-apis/pull/33)
- Default search limit restored to 250 [#36](https://github.com/microsoft/planetary-computer-apis/pull/36)
- Work around issue with LandPage stac_extensions [#37](https://github.com/microsoft/planetary-computer-apis/pull/37)
3 changes: 2 additions & 1 deletion pcstac/pcstac/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,10 @@ async def _search_base(
}
)

# Remove once https://github.com/stac-utils/stac-fastapi/issues/334 is fixed.
async def landing_page(self, **kwargs: Dict[str, Any]) -> LandingPage:
landing = await super().landing_page(**kwargs)
landing["type"] = "Catalog"
del landing["stac_extensions"]
return landing

@classmethod
Expand Down
56 changes: 55 additions & 1 deletion pcstac/pcstac/search.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
from typing import Optional
from typing import List, Optional, Union

import attr
from geojson_pydantic.geometries import (
GeometryCollection,
LineString,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
Polygon,
)
from pydantic import validator
from pydantic.types import conint
from pystac.utils import str_to_datetime
from stac_fastapi.api.models import BaseSearchGetRequest, ItemCollectionUri
from stac_fastapi.pgstac.types.search import PgstacSearch

Expand All @@ -13,6 +24,49 @@ class PCSearch(PgstacSearch):
# Ignore "Illegal type annotation: call expression not allowed"
limit: Optional[conint(ge=1, le=1000)] = DEFAULT_LIMIT # type:ignore

# Can be removed when
# https://github.com/stac-utils/stac-fastapi/issues/187 is closed
intersects: Optional[
Union[
Point,
MultiPoint,
LineString,
MultiLineString,
Polygon,
MultiPolygon,
GeometryCollection,
]
]

@validator("datetime")
def validate_datetime(cls, v: str) -> str:
"""Validate datetime.

Custom to allow for users to supply dates only.
"""
if "/" in v:
values = v.split("/")
else:
# Single date is interpreted as end date
values = ["..", v]

dates: List[str] = []
for value in values:
if value == "..":
dates.append(value)
continue

str_to_datetime(value)
dates.append(value)

if ".." not in dates:
if str_to_datetime(dates[0]) > str_to_datetime(dates[1]):
raise ValueError(
"Invalid datetime range, must match format (begin_date, end_date)"
)

return v


@attr.s
class PCItemCollectionUri(ItemCollectionUri):
Expand Down
15 changes: 15 additions & 0 deletions pcstac/tests/resources/test_conformance.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
from typing import Any, Dict

import pystac
import pytest


def remove_root(stac_object: Dict[str, Any]) -> None:
links = []
for link in stac_object["links"]:
if link["rel"] != "root":
links.append(link)
stac_object["links"] = links


@pytest.mark.asyncio
async def test_landing_page(app_client):
"""Test landing page"""
Expand All @@ -13,6 +24,10 @@ async def test_landing_page(app_client):
# }
# result = set(resp_json)
# assert result == expected

remove_root(resp_json)
pystac.Catalog.from_dict(resp_json).validate()

assert "stac_version" in resp_json

# Make sure OpenAPI docs are linked
Expand Down
50 changes: 50 additions & 0 deletions pcstac/tests/resources/test_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,33 @@ async def test_item_search_temporal_window_get(app_client):
assert resp_json["features"][0]["id"] == first_item["id"]


@pytest.mark.asyncio
async def test_item_search_temporal_window_get_date_only(app_client):
"""Test GET search with spatio-temporal query (core)"""
items_resp = await app_client.get("/collections/naip/items")
assert items_resp.status_code == 200

first_item = items_resp.json()["features"][0]
item_date = datetime.strptime(first_item["properties"]["datetime"], DATETIME_RFC339)
item_date_before = item_date - timedelta(days=1)
item_date_after = item_date + timedelta(days=1)

params = {
"collections": first_item["collection"],
"bbox": ",".join([str(coord) for coord in first_item["bbox"]]),
"datetime": f"{item_date_before.strftime('%Y-%m-%d')}/"
f"{item_date_after.strftime('%Y-%m-%d')}",
}
resp = await app_client.get("/search", params=params)
assert resp.status_code == 200
resp_json = resp.json()
import json

print(json.dumps(resp_json, indent=2))

assert resp_json["features"][0]["id"] == first_item["id"]


@pytest.mark.asyncio
async def test_item_search_post_without_collection(app_client):
"""Test POST search without specifying a collection"""
Expand Down Expand Up @@ -560,3 +587,26 @@ async def test_search_post_page_limits(app_client):
assert resp.status_code == 200
resp_json = resp.json()
assert len(resp_json["features"]) == 12


@pytest.mark.asyncio
async def test_item_search_geometry_collection(app_client):
"""Test POST search by item id (core)"""
aoi = {
"type": "GeometryCollection",
"geometries": [
{"type": "Point", "coordinates": [-67.67578124999999, 4.390228926463396]},
{"type": "Point", "coordinates": [-74.619140625, 4.302591077119676]},
{
"type": "LineString",
"coordinates": [
[-59.765625, 6.227933930268672],
[-63.984375, -2.108898659243126],
],
},
],
}

params = {"collections": ["naip"], "intersects": aoi}
resp = await app_client.post("/search", json=params)
assert resp.status_code == 200
1 change: 0 additions & 1 deletion pctiler/pctiler/colormaps/noaa_c_cap.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from rio_tiler.types import ColorMapType


noaa_c_cap_colormaps: Dict[str, ColorMapType] = {
"c-cap": {
0: (0, 0, 0, 255),
Expand Down