Skip to content

Commit 86a0ad8

Browse files
authored
fix: permissive extent parsing for collections (#787)
* fix: permissive extent parsing for collections * fix: cli collections dump, no tests * chore: update changelog
1 parent 9163eb2 commit 86a0ad8

File tree

5 files changed

+82
-18
lines changed

5 files changed

+82
-18
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1515
### Fixed
1616

1717
- `Client.get_collection` for static catalogs [#782](https://github.com/stac-utils/pystac-client/pull/782)
18+
- Permissive collection reading [#787](https://github.com/stac-utils/pystac-client/pull/787)
1819

1920
## [v0.8.5] - 2024-10-23
2021

pystac_client/cli.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def collections(
124124
else:
125125
raise KeyError("'matched' is not supported for this catalog")
126126
else:
127-
collections_dicts = [c.to_dict() for c in result.collections()]
127+
collections_dicts = list(result.collections_as_dicts())
128128
if save:
129129
with open(save, "w") as f:
130130
f.write(json.dumps(collections_dicts))
@@ -257,8 +257,7 @@ def parse_args(args: list[str]) -> dict[str, Any]:
257257
)
258258
collections_group.add_argument(
259259
"--datetime",
260-
help="Single datetime or begin and end datetime "
261-
"(e.g., 2017-01-01/2017-02-15)",
260+
help="Single datetime or begin and end datetime (e.g., 2017-01-01/2017-02-15)",
262261
)
263262
collections_group.add_argument("--q", help="Free-text search query")
264263
collections_group.add_argument(
@@ -318,8 +317,7 @@ def parse_args(args: list[str]) -> dict[str, Any]:
318317
)
319318
search_group.add_argument(
320319
"--datetime",
321-
help="Single datetime or begin and end datetime "
322-
"(e.g., 2017-01-01/2017-02-15)",
320+
help="Single datetime or begin and end datetime (e.g., 2017-01-01/2017-02-15)",
323321
)
324322
search_group.add_argument(
325323
"--query",

pystac_client/collection_search.py

+31-13
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
Optional,
99
)
1010

11-
from pystac import Collection, TemporalExtent
11+
from pystac import Collection, Extent
1212

1313
from pystac_client._utils import Modifiable, call_modifier
1414
from pystac_client.conformance import ConformanceClasses
@@ -26,7 +26,7 @@
2626
SortbyLike,
2727
)
2828
from pystac_client.stac_api_io import StacApiIO
29-
from pystac_client.warnings import DoesNotConformTo
29+
from pystac_client.warnings import DoesNotConformTo, PystacClientWarning
3030

3131
if TYPE_CHECKING:
3232
from pystac_client import client as _client
@@ -54,26 +54,21 @@ def bboxes_overlap(bbox1: BBox, bbox2: BBox) -> bool:
5454
return xmin1 <= xmax2 and xmin2 <= xmax1 and ymin1 <= ymax2 and ymin2 <= ymax1
5555

5656

57-
def collection_matches(
58-
collection_dict: dict[str, Any],
57+
def _extent_matches(
58+
extent: Extent,
5959
bbox: BBox | None = None,
6060
temporal_interval_str: Datetime | None = None,
61-
q: str | None = None,
62-
) -> bool:
63-
# check for overlap between provided bbox and the collection's spatial extent
64-
collection_bboxes = collection_dict["extent"]["spatial"]["bbox"]
61+
) -> tuple[bool, bool]:
6562
bbox_overlaps = not bbox or (
6663
any(
67-
bboxes_overlap(bbox, collection_bbox)
68-
for collection_bbox in collection_bboxes
64+
bboxes_overlap(bbox, tuple(collection_bbox))
65+
for collection_bbox in extent.spatial.bboxes
6966
)
7067
)
7168

7269
# check for overlap between the provided temporal interval and the collection's
7370
# temporal extent
74-
collection_temporal_extent = TemporalExtent.from_dict(
75-
collection_dict["extent"]["temporal"]
76-
)
71+
collection_temporal_extent = extent.temporal
7772

7873
# process the user-provided temporal interval
7974
search_temporal_interval = (
@@ -110,6 +105,29 @@ def collection_matches(
110105
for collection_temporal_interval in collection_temporal_extent.intervals
111106
)
112107
)
108+
return bbox_overlaps, datetime_overlaps
109+
110+
111+
def collection_matches(
112+
collection_dict: dict[str, Any],
113+
bbox: BBox | None = None,
114+
temporal_interval_str: Datetime | None = None,
115+
q: str | None = None,
116+
) -> bool:
117+
# check for overlap between provided bbox and the collection's spatial extent
118+
try:
119+
extent = Extent.from_dict(collection_dict.get("extent", {}))
120+
except Exception:
121+
warnings.warn(
122+
f"Unable to parse extent from collection={collection_dict.get('id', None)}",
123+
PystacClientWarning,
124+
)
125+
bbox_overlaps = True
126+
datetime_overlaps = True
127+
else:
128+
bbox_overlaps, datetime_overlaps = _extent_matches(
129+
extent, bbox, temporal_interval_str
130+
)
113131

114132
# check for overlap between the provided free-text search query (q) and the
115133
# collection's title, description, and keywords

tests/data/invalid-collection.json

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"id": "planet",
3+
"title": "Planet",
4+
"extent": {},
5+
"license": "proprietary",
6+
"description": "Planet",
7+
"stac_version": "1.0.0",
8+
"item_type_accessor": "pl:item_type",
9+
"links": [
10+
{
11+
"rel": "items",
12+
"type": "application/geo+json",
13+
"href": "https://csdap.earthdata.nasa.gov/stac/collections/planet/items"
14+
},
15+
{
16+
"rel": "parent",
17+
"type": "application/json",
18+
"href": "https://csdap.earthdata.nasa.gov/stac/"
19+
},
20+
{
21+
"rel": "root",
22+
"type": "application/json",
23+
"href": "https://csdap.earthdata.nasa.gov/stac/"
24+
},
25+
{
26+
"rel": "self",
27+
"type": "application/json",
28+
"href": "https://csdap.earthdata.nasa.gov/stac/collections/planet"
29+
},
30+
{
31+
"rel": "http://www.opengis.net/def/rel/ogc/1.0/queryables",
32+
"type": "application/schema+json",
33+
"title": "Queryables",
34+
"href": "https://csdap.earthdata.nasa.gov/stac/collections/planet/queryables"
35+
}
36+
]
37+
}

tests/test_collection_search.py

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
from datetime import datetime
23
from math import ceil
34
from typing import Any
@@ -12,8 +13,10 @@
1213
from pystac_client.collection_search import (
1314
CollectionSearch,
1415
bboxes_overlap,
16+
collection_matches,
1517
temporal_intervals_overlap,
1618
)
19+
from pystac_client.warnings import PystacClientWarning
1720

1821
from .helpers import STAC_URLS, read_data_file
1922

@@ -387,3 +390,10 @@ def test_temporal_intervals_overlap() -> None:
387390
None,
388391
),
389392
)
393+
394+
395+
def test_invalid_collection() -> None:
396+
# https://github.com/stac-utils/pystac-client/issues/786
397+
data = json.loads(read_data_file("invalid-collection.json"))
398+
with pytest.warns(PystacClientWarning):
399+
collection_matches(data)

0 commit comments

Comments
 (0)