Skip to content

Commit

Permalink
clarify type requirements for search intersects parameter, update som…
Browse files Browse the repository at this point in the history
…e documentation (#174)

* clarify type requirements for search intersects parameter, update some documentation
* add python 3.10 to setup.py and github ci config
  • Loading branch information
Phil Varner authored May 26, 2022
1 parent a12663a commit 28adaf1
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 15 deletions.
15 changes: 12 additions & 3 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,21 @@ on:
jobs:
build:
name: build
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9"]
python-version:
- "3.7"
- "3.8"
- "3.9"
- "3.10"
# - "3.11-dev"
os:
- ubuntu-latest
# - windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Bumped PySTAC dependency to >= 1.4.0 [#147](https://github.com/stac-utils/pystac-client/pull/147)
- Search `filter-lang` defaults to `cql2-json` instead of `cql-json`
- Search `filter-lang` will be set to `cql2-json` if the `filter` is a dict, or `cql2-text` if it is a string
- Search parameter `intersects` is now typed to only accept a str, dict, or object that implements `__geo_interface__`

## Removed

Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
STAC Client
===============
# STAC Client <!-- omit in toc -->

[![CI](https://github.com/stac-utils/pystac-client/actions/workflows/continuous-integration.yml/badge.svg)](https://github.com/stac-utils/pystac-client/actions/workflows/continuous-integration.yml)
[![Release](https://github.com/stac-utils/pystac-client/actions/workflows/release.yml/badge.svg)](https://github.com/stac-utils/pystac-client/actions/workflows/release.yml)
Expand Down
4 changes: 2 additions & 2 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ The :meth:`pystac_client.Client.search` method provides an interface for making
.. code-block:: python
>>> from pystac_client import API
>>> api = API.from_file('https://eod-catalog-svc-prod.astraea.earth')
>>> api = API.from_file('https://planetarycomputer.microsoft.com/api/stac/v1')
>>> results = api.search(
... bbox=[-73.21, 43.99, -73.12, 44.05],
... datetime=['2019-01-01T00:00:00Z', '2019-01-02T00:00:00Z'],
Expand Down Expand Up @@ -169,7 +169,7 @@ implementation of this ``"next"`` link parsing assumes that the link follows the
described in the `STAC API - Item Search: Paging <https://github.com/radiantearth/stac-api-spec/tree/master/item-search#paging>`__
section. See the :mod:`Paging <pystac_client.paging>` docs for details on how to customize this behavior.

Query Filter
Query Extension
------------

If the Catalog supports the `Query
Expand Down
28 changes: 21 additions & 7 deletions pystac_client/item_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
r"(?P<remainder>(T|t)\d{2}:\d{2}:\d{2}(\.\d+)?"
r"(?P<tz_info>Z|([-+])(\d{2}):(\d{2}))?)?)?)?")

# todo: add runtime_checkable when we drop 3.7 support
# class GeoInterface(Protocol):
# def __geo_interface__(self) -> dict:
# ...

DatetimeOrTimestamp = Optional[Union[datetime_, str]]
Datetime = Union[Tuple[str], Tuple[str, str]]
DatetimeLike = Union[DatetimeOrTimestamp, Tuple[DatetimeOrTimestamp, DatetimeOrTimestamp],
Expand All @@ -37,7 +42,8 @@
IDsLike = Union[IDs, str, List[str], Iterator[str]]

Intersects = dict
IntersectsLike = Union[str, Intersects, object]
IntersectsLike = Union[str, object, Intersects]
# todo: after 3.7 is dropped, replace object with GeoInterface

Query = dict
QueryLike = Union[Query, List[str]]
Expand Down Expand Up @@ -132,7 +138,9 @@ class ItemSearch:
- ``2017/2018`` expands to ``2017-01-01T00:00:00Z/2018-12-31T23:59:59Z``
- ``2017-06/2017-07`` expands to ``2017-06-01T00:00:00Z/2017-07-31T23:59:59Z``
- ``2017-06-10/2017-06-11`` expands to ``2017-06-10T00:00:00Z/2017-06-11T23:59:59Z``
intersects: A GeoJSON-like dictionary or JSON string. Results filtered to only those intersecting the geometry
intersects: A string or dictionary representing a GeoJSON geometry, or an object that implements a
``__geo_interface__`` property as supported by several libraries including Shapely, ArcPy, PySAL, and
geojson. Results filtered to only those intersecting the geometry.
ids: List of Item ids to return. All other filter parameters that further restrict the number of search results
(except ``limit``) are ignored.
collections: List of one or more Collection IDs or :class:`pystac.Collection` instances. Only Items in one
Expand All @@ -142,11 +150,11 @@ class ItemSearch:
filter_lang: Language variant used in the filter body. If `filter` is a dictionary or not provided, defaults
to 'cql2-json'. If `filter` is a string, defaults to `cql2-text`.
sortby: A single field or list of fields to sort the response by
fields: A list of fields to return in the response. Note this may result in invalid JSON.
Use `get_all_items_as_dict` to avoid errors
max_items: The maximum number of items to get, even if there are more matched items
fields: A list of fields to include in the response. Note this may result in invalid STAC objects, as they
may not have required fields. Use `get_all_items_as_dict` to avoid object unmarshalling errors.
max_items: The maximum number of items to get, even if there are more matched items.
method: The http method, 'GET' or 'POST'
stac_io: An instance of of StacIO for retrieving results. Normally comes from the Client that returns this ItemSearch
stac_io: An instance of StacIO for retrieving results. Normally comes from the Client that returns this ItemSearch
client: An instance of a root Client used to set the root on resulting Items
"""
def __init__(self,
Expand Down Expand Up @@ -409,9 +417,15 @@ def _format_fields(self, value: Optional[FieldsLike]) -> Optional[Fields]:
def _format_intersects(value: Optional[IntersectsLike]) -> Optional[Intersects]:
if value is None:
return None
if isinstance(value, dict):
return deepcopy(value)
if isinstance(value, str):
return json.loads(value)
return deepcopy(getattr(value, '__geo_interface__', value))
if hasattr(value, '__geo_interface__'):
return deepcopy(getattr(value, '__geo_interface__'))
raise Exception(
"intersects must be of type None, str, dict, or an object that implements __geo_interface__"
)

@lru_cache(1)
def matched(self) -> int:
Expand Down
8 changes: 7 additions & 1 deletion tests/test_item_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ def test_intersects_json_string(self):
search = ItemSearch(url=SEARCH_URL, intersects=json.dumps(INTERSECTS_EXAMPLE))
assert search._parameters['intersects'] == INTERSECTS_EXAMPLE

def test_intersects_non_geo_interface_object(self):
with pytest.raises(Exception):
ItemSearch(url=SEARCH_URL, intersects=object())

def test_filter_lang_default_for_dict(self):
search = ItemSearch(url=SEARCH_URL, filter={})
assert search._parameters['filter-lang'] == 'cql2-json'
Expand Down Expand Up @@ -377,7 +381,9 @@ def test_intersects_results(self):

# Geo-interface object
class MockGeoObject:
__geo_interface__ = intersects_dict
@property
def __geo_interface__(self):
return intersects_dict

intersects_obj = MockGeoObject()
search = ItemSearch(url=SEARCH_URL, intersects=intersects_obj, collections='naip')
Expand Down

0 comments on commit 28adaf1

Please sign in to comment.