From 345da8ef12c992dc7bf0aab16d872c4390c37993 Mon Sep 17 00:00:00 2001 From: Phil Varner Date: Wed, 1 Jun 2022 10:21:52 -0400 Subject: [PATCH 1/6] deprecate get_all_items_as_dict and get_all_items, add get_items_as_dicts --- CHANGELOG.md | 9 + docs/quickstart.rst | 50 +++--- docs/tutorials/cql2-filter.ipynb | 79 ++------- docs/usage.rst | 52 ++++-- pystac_client/item_search.py | 56 ++++-- ...estItemSearch.test_get_items_as_dicts.yaml | 160 ++++++++++++++++++ tests/test_item_search.py | 11 ++ 7 files changed, 308 insertions(+), 109 deletions(-) create mode 100644 tests/cassettes/test_item_search/TestItemSearch.test_get_items_as_dicts.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index e71254ec..1cab59aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] - TBD +### Deprecated + +- Item Search methods `get_all_items` and `get_all_items_as_dict` are now deprecated. + These have been deprecated because they have the potential to perform a large number + of requests to the server and instantiate a large number of objects in memory. + To a user, this is only visible as a large delay in the method call and/or the + exhaustion of all available memory. The iterator methods `get_items` or + `get_itemcollections` should be used instead. + ### Added - lru_cache to several methods [#167](https://github.com/stac-utils/pystac-client/pull/167) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 8daf209b..7a1e0d8c 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -13,7 +13,7 @@ The ``--matched`` switch performs a search with limit=1 so does not get any Items, but gets the total number of matches which will be output to the screen (if supported by the STAC API). -:: +.. code-block:: console $ stac-client search https://earth-search.aws.element84.com/v0 -c sentinel-s2-l2a-cogs --bbox -72.5 40.5 -72 41 --matched 2179 items matched @@ -21,7 +21,7 @@ the screen (if supported by the STAC API). If the same URL is to be used over and over, define an environment variable to be used in the CLI call: -:: +.. code-block:: console $ export STAC_API_URL=https://earth-search.aws.element84.com/v0 $ stac-client search ${STAC_API_URL} -c sentinel-s2-l2a-cogs --bbox -72.5 40.5 -72 41 --datetime 2020-01-01/2020-01-31 --matched @@ -36,7 +36,7 @@ another process such as `geojsonio-cli `__, or `jq `__. -:: +.. code-block:: console $ stac-client search ${STAC_API_URL} -c sentinel-s2-l2a-cogs --bbox -72.5 40.5 -72 41 --datetime 2020-01-01/2020-01-31 | stacterm cal --label platform @@ -46,7 +46,7 @@ another process such as If the ``--save`` switch is provided instead, the results will not be output to stdout, but instead will be saved to the specified file. -:: +.. code-block:: console $ stac-client search ${STAC_API_URL} -c sentinel-s2-l2a-cogs --bbox -72.5 40.5 -72 41 --datetime 2020-01-01/2020-01-31 --save items.json @@ -77,12 +77,12 @@ The query filter will also accept complete JSON as per the specification. Any number of properties can be included, and each can be included more than once to use additional operators. -:: +.. code-block:: console $ stac-client search ${STAC_API_URL} -c sentinel-s2-l2a-cogs --bbox -72.5 40.5 -72 41 --datetime 2020-01-01/2020-01-31 -q "eo:cloud_cover<10" --matched 10 items matched -:: +.. code-block:: console $ stac-client search ${STAC_API_URL} -c sentinel-s2-l2a-cogs --bbox -72.5 40.5 -72 41 --datetime 2020-01-01/2020-01-31 -q "eo:cloud_cover<10" "eo:cloud_cover>5" --matched 4 items matched @@ -91,37 +91,39 @@ Python ~~~~~~ To use the Python library, first a Client instance is created for a -specific STAC API (use the root URL) +specific STAC API (use the root URL): -:: +.. code-block:: python from pystac_client import Client - catalog = Client.open("https://earth-search.aws.element84.com/v0") + catalog_client = Client.open("https://earth-search.aws.element84.com/v0") -Create a search +Create a search: -:: +.. code-block:: python - mysearch = catalog.search(collections=['sentinel-s2-l2a-cogs'], bbox=[-72.5,40.5,-72,41], max_items=10) + my_search = catalog_client.search(collections=['sentinel-s2-l2a-cogs'], bbox=[-72.5,40.5,-72,41], max_items=10) print(f"{mysearch.matched()} items found") -The ``get_items`` function returns an iterator for looping through he -returned items. +The ``get_items`` generator function can be used to iterate through all resulting items. -:: +.. code-block:: python - for item in mysearch.get_items(): + for item in my_search.get_items(): print(item.id) -To get all of Items from a search as a single `PySTAC -ItemCollection `__ -use the ``get_all_items`` function. The ``ItemCollection`` can then be -saved as a GeoJSON FeatureCollection. +To convert all of Items from a search as a single `PySTAC +ItemCollection `__, +you must first do a limited iteration on the iterator to get a list of Items, and then +create an ItemCollection with that. The ``ItemCollection`` can then be saved as a +GeoJSON FeatureCollection. -Save all found items as a single FeatureCollection +Save all found items as a single FeatureCollection: -:: +.. code-block:: python + + from pystac import ItemCollection - items = mysearch.get_all_items() - items.save_object('items.json') + my_itemcollection = ItemCollection(items = list(my_search.get_items())) + my_itemcollection.save_object('my_itemcollection.json') diff --git a/docs/tutorials/cql2-filter.ipynb b/docs/tutorials/cql2-filter.ipynb index d55f2260..67ba284e 100644 --- a/docs/tutorials/cql2-filter.ipynb +++ b/docs/tutorials/cql2-filter.ipynb @@ -14,7 +14,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 8, "id": "b65de617", "metadata": {}, "outputs": [], @@ -50,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 9, "id": "98942e75", "metadata": {}, "outputs": [], @@ -76,7 +76,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 10, "id": "d8af6334", "metadata": {}, "outputs": [], @@ -125,9 +125,9 @@ " # limit sets the # of items per page so we can see multiple pages getting fetched\n", " params['filter'] = filt\n", " search = cat.search(**params)\n", - " items_json = search.get_all_items_as_dict()\n", + " items = list(search.get_items_as_dicts()) # safe b/c we set max_items = 100\n", " # DataFrame\n", - " items_df = pd.DataFrame(items_to_geodataframe(items_json['features']))\n", + " items_df = pd.DataFrame(items_to_geodataframe(items))\n", " print(f\"{len(items_df.index)} items found\")\n", " field = 'properties.eo:cloud_cover'\n", " return items_df.hvplot(y=field, label=json.dumps(filt), frame_height=500, frame_width=800) " @@ -145,65 +145,22 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 11, "id": "dfc0e759", "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "33 items found\n" + "ename": "TypeError", + "evalue": "'Item' object is not subscriptable", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/var/folders/3q/jbg6x0zx3194zq6_2jbwygjw0000gn/T/ipykernel_73465/2658164751.py\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 4\u001b[0m }\n\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m \u001b[0msearch_fetch_plot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfilt\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m/var/folders/3q/jbg6x0zx3194zq6_2jbwygjw0000gn/T/ipykernel_73465/1512843940.py\u001b[0m in \u001b[0;36msearch_fetch_plot\u001b[0;34m(params, filt)\u001b[0m\n\u001b[1;32m 46\u001b[0m \u001b[0mitems\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtake\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m100\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msearch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_items\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 47\u001b[0m \u001b[0;31m# DataFrame\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 48\u001b[0;31m \u001b[0mitems_df\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mDataFrame\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mitems_to_geodataframe\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mitems\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 49\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"{len(items_df.index)} items found\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 50\u001b[0m \u001b[0mfield\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'properties.eo:cloud_cover'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/var/folders/3q/jbg6x0zx3194zq6_2jbwygjw0000gn/T/ipykernel_73465/3732288798.py\u001b[0m in \u001b[0;36mitems_to_geodataframe\u001b[0;34m(items)\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mitems\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0m_i\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdeepcopy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0m_i\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'geometry'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mshape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_i\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'geometry'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 21\u001b[0m \u001b[0m_items\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_i\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0mgdf\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mGeoDataFrame\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjson_normalize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_items\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: 'Item' object is not subscriptable" ] - }, - { - "data": {}, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.holoviews_exec.v0+json": "", - "text/html": [ - "
\n", - "
\n", - "
\n", - "" - ], - "text/plain": [ - ":Curve [properties.datetime] (properties.eo:cloud_cover)" - ] - }, - "execution_count": 18, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "2290" - } - }, - "output_type": "execute_result" } ], "source": [ @@ -217,7 +174,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "9c2f9ca1", "metadata": {}, "outputs": [ @@ -289,7 +246,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "id": "109f673c", "metadata": {}, "outputs": [ diff --git a/docs/usage.rst b/docs/usage.rst index 644a5922..4a845882 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -159,26 +159,23 @@ requests to a service's "search" endpoint. This method returns a Instances of :class:`~pystac_client.ItemSearch` have 2 methods for iterating over results: -* :meth:`ItemSearch.get_item_collections `: - iterator over *pages* of results, +* :meth:`ItemSearch.get_item_collections + `: iterator over *pages* of results, yielding an :class:`~pystac.ItemCollection` for each page of results. * :meth:`ItemSearch.get_items `: an iterator over - individual Item objects, yielding a :class:`pystac.Item` instance for Item - that matches the search criteria. + individual Item objects, yielding a :class:`pystac.Item` instance for all Items + that match the search criteria. +* :meth:`ItemSearch.get_items_as_dicts `: + iterate over individual results, yielding a :class:`dict` instance representing each + item that matches the search criteria. This eliminates the overhead of creating + class:`pystac.Item` objects with pydantic -In addition three additional convenience methods are provided: +Additionally, the ``matched`` method can be used to access result metadata about +how many total items matched the query: * :meth:`ItemSearch.matched `: returns the number of hits (items) for this search if the API supports the STAC API Context Extension. Not all APIs support returning a total count, in which case a warning will be issued. -* :meth:`ItemSearch.get_all_items `: Rather - than return an iterator, this function will - fetch all items and return them as a single :class:`~pystac.ItemCollection`. -* :meth:`ItemSearch.get_all_items_as_dict - ` : Like `get_all_items` this fetches - all items but returns them as a GeoJSON FeatureCollection dictionary rather than a - PySTAC object. This can be more efficient if only a dictionary of the results is - needed. .. code-block:: python @@ -201,6 +198,35 @@ described in the section. See the :mod:`Paging ` docs for details on how to customize this behavior. +Alternatively, the Items can be returned within ItemCollections, where each +ItemCollection is one page of results retrieved from search: + +.. code-block:: python + + >>> for ic in results.get_item_collections(): + ... for item in ic.items: + ... print(item.id) + S2B_OPER_MSI_L2A_TL_SGS__20190101T200120_A009518_T18TXP_N02.11 + MCD43A4.A2019010.h12v04.006.2019022234410 + MCD43A4.A2019009.h12v04.006.2019022222645 + MYD11A1.A2019002.h12v04.006.2019003174703 + MYD11A1.A2019001.h12v04.006.2019002165238 + +If you do not need the :class:`pystac.Item` instances, you can instead use +:meth:`ItemSearch.get_items_as_dicts ` +to retrive dictionary representation of the items, without incurring the cost of +creating the pydantic Item objects. + +.. code-block:: python + + >>> for item_dict in results.get_items_as_dicts(): + ... print(item_dict["id"]) + S2B_OPER_MSI_L2A_TL_SGS__20190101T200120_A009518_T18TXP_N02.11 + MCD43A4.A2019010.h12v04.006.2019022234410 + MCD43A4.A2019009.h12v04.006.2019022222645 + MYD11A1.A2019002.h12v04.006.2019003174703 + MYD11A1.A2019001.h12v04.006.2019002165238 + Query Extension --------------- diff --git a/pystac_client/item_search.py b/pystac_client/item_search.py index 286bd79f..8ad7b680 100644 --- a/pystac_client/item_search.py +++ b/pystac_client/item_search.py @@ -69,7 +69,9 @@ # from https://gist.github.com/angstwad/bf22d1822c38a92ec0a9#gistcomment-2622319 -def dict_merge(dct: Dict, merge_dct: Dict, add_keys: bool = True) -> Dict: +def dict_merge( + dct: Dict[Any, Any], merge_dct: Dict[Any, Any], add_keys: bool = True +) -> Dict[Any, Any]: """Recursive dict merge. Inspired by :meth:``dict.update()``, instead of @@ -191,7 +193,7 @@ class ItemSearch: sortby: A single field or list of fields to sort the response by 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. + Use `get_items_as_dicts` 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' @@ -540,7 +542,7 @@ def _format_intersects(value: Optional[IntersectsLike]) -> Optional[Intersects]: ) @lru_cache(1) - def matched(self) -> int: + def matched(self) -> Optional[int]: """Return number matched for search Returns the value from the `numberMatched` or `context.matched` field. @@ -566,7 +568,8 @@ def get_item_collections(self) -> Iterator[ItemCollection]: a page of results from the search. Yields: - Iterable[Item] : pystac_client.ItemCollection + ItemCollection : a group of Items matching the search criteria within an + ItemCollection """ for page in self._stac_io.get_pages( self.url, self.method, self.get_parameters() @@ -576,12 +579,12 @@ def get_item_collections(self) -> Iterator[ItemCollection]: def get_items(self) -> Iterator[Item]: """Iterator that yields :class:`pystac.Item` instances for each item matching the given search parameters. Calls - :meth:`ItemSearch.item_collections()` internally and yields from + :meth:`ItemSearch.get_item_collections()` internally and yields from :attr:`ItemCollection.features ` for each page of results. - Return: - Iterable[Item] : Iterate through resulting Items + Yields: + Item : each Item matching the search criteria """ nitems = 0 for item_collection in self.get_item_collections(): @@ -591,14 +594,40 @@ def get_items(self) -> Iterator[Item]: if self._max_items and nitems >= self._max_items: return + def get_items_as_dicts(self) -> Iterator[Dict[str, Any]]: + """Iterator that yields :class:`dict` instances for each item matching + the given search parameters. Calls + :meth:`ItemSearch.get_item_collections()` internally and yields from + :attr:`ItemCollection.features ` for + each page of results. + + Yields: + Item : each Item matching the search criteria + """ + nitems = 0 + for page in self._stac_io.get_pages( + self.url, self.method, self.get_parameters() + ): + for item in page.get("features", []): + yield item + nitems += 1 + if self._max_items and nitems >= self._max_items: + return + @lru_cache(1) - def get_all_items_as_dict(self) -> Dict: - """Convenience method that gets all items from all pages, up to self._max_items, - and returns an array of dictionaries + def get_all_items_as_dict(self) -> Dict[str, Any]: + """DEPRECATED. Use get_items or get_itemcollections instead. + Convenience method that gets all items from all pages, up to + self._max_items, and returns an array of dictionaries. Return: Dict : A GeoJSON FeatureCollection """ + warnings.warn( + "get_all_items_as_dict is deprecated, use get_items or" + " get_itemcollections instead", + DeprecationWarning, + ) features = [] for page in self._stac_io.get_pages( self.url, self.method, self.get_parameters() @@ -611,12 +640,17 @@ def get_all_items_as_dict(self) -> Dict: @lru_cache(1) def get_all_items(self) -> ItemCollection: - """Convenience method that builds an :class:`ItemCollection` from all items + """DEPRECATED. Use get_items or get_itemcollections instead. + Convenience method that builds an :class:`ItemCollection` from all items matching the given search parameters. Return: item_collection : ItemCollection """ + warnings.warn( + "get_all_items is deprecated, use get_items or get_itemcollections instead", + DeprecationWarning, + ) feature_collection = self.get_all_items_as_dict() return ItemCollection.from_dict( feature_collection, preserve_dict=False, root=self.client diff --git a/tests/cassettes/test_item_search/TestItemSearch.test_get_items_as_dicts.yaml b/tests/cassettes/test_item_search/TestItemSearch.test_get_items_as_dicts.yaml new file mode 100644 index 00000000..240cd7a5 --- /dev/null +++ b/tests/cassettes/test_item_search/TestItemSearch.test_get_items_as_dicts.yaml @@ -0,0 +1,160 @@ +interactions: +- request: + body: '{"limit": 10, "bbox": [-73.21, 43.99, -73.12, 44.05], "collections": ["naip"]}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '78' + Content-Type: + - application/json + User-Agent: + - python-requests/2.27.1 + method: POST + uri: https://planetarycomputer.microsoft.com/api/stac/v1/search + response: + body: + string: !!binary | + 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 + headers: + Content-Encoding: + - gzip + Content-Length: + - '2143' + Content-Type: + - application/geo+json + Date: + - Wed, 01 Jun 2022 14:17:23 GMT + Strict-Transport-Security: + - max-age=15724800; includeSubDomains + Vary: + - Accept-Encoding + X-Azure-Ref: + - 09HSXYgAAAAAN/WrHkOHSTrMaVgXSKRyuTU5aMjIxMDYwNjE0MDUxADkyN2FiZmE2LTE5ZjYtNGFmMS1hMDlkLWM5NTlkOWExZTY0NA== + X-Cache: + - CONFIG_NOCACHE + status: + code: 200 + message: OK +- request: + body: '{"limit": 10, "bbox": [-73.21, 43.99, -73.12, 44.05], "collections": ["naip"], + "token": "next:vt_m_4407363_sw_18_h_20160804"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '125' + Content-Type: + - application/json + User-Agent: + - python-requests/2.27.1 + method: POST + uri: https://planetarycomputer.microsoft.com/api/stac/v1/search + response: + body: + string: !!binary | + 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 + headers: + Content-Encoding: + - gzip + Content-Length: + - '2222' + Content-Type: + - application/geo+json + Date: + - Wed, 01 Jun 2022 14:17:24 GMT + Strict-Transport-Security: + - max-age=15724800; includeSubDomains + Vary: + - Accept-Encoding + X-Azure-Ref: + - 09HSXYgAAAADdIeMyUv+4R56ucppR1iHFTU5aMjIxMDYwNjE0MDUxADkyN2FiZmE2LTE5ZjYtNGFmMS1hMDlkLWM5NTlkOWExZTY0NA== + X-Cache: + - CONFIG_NOCACHE + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_item_search.py b/tests/test_item_search.py index 702d2dc3..aae8fffd 100644 --- a/tests/test_item_search.py +++ b/tests/test_item_search.py @@ -605,6 +605,17 @@ def test_get_all_items(self) -> None: item_collection = search.get_all_items() assert len(item_collection.items) == 20 + @pytest.mark.vcr + def test_get_items_as_dicts(self) -> None: + search = ItemSearch( + url=SEARCH_URL, + bbox=(-73.21, 43.99, -73.12, 44.05), + collections="naip", + limit=10, + max_items=20, + ) + assert len(list(search.get_items_as_dicts())) == 20 + class TestItemSearchQuery: @pytest.mark.vcr From 0e356bdef2c0bf6c6fde2b514a8975928e096a5f Mon Sep 17 00:00:00 2001 From: Phil Varner Date: Wed, 1 Jun 2022 10:55:43 -0400 Subject: [PATCH 2/6] update changelog with PR --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cab59aa..28aa28fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. of requests to the server and instantiate a large number of objects in memory. To a user, this is only visible as a large delay in the method call and/or the exhaustion of all available memory. The iterator methods `get_items` or - `get_itemcollections` should be used instead. + `get_itemcollections` should be used instead. [#206](https://github.com/stac-utils/pystac-client/pull/206) ### Added @@ -24,9 +24,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - Better error message when trying to search a non-item-search-conforming catalog [#164](https://github.com/stac-utils/pystac-client/pull/164) -- 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__` +- Search `filter-lang` defaults to `cql2-json` instead of `cql-json` [#169](https://github.com/stac-utils/pystac-client/pull/169) +- Search `filter-lang` will be set to `cql2-json` if the `filter` is a dict, or `cql2-text` if it is a string [#169](https://github.com/stac-utils/pystac-client/pull/169) +- Search parameter `intersects` is now typed to only accept a str, dict, or object that implements `__geo_interface__` [#169](https://github.com/stac-utils/pystac-client/pull/169) ### Fixed @@ -36,7 +36,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Removed -- Client parameter `require_geojson_link` has been removed. +- Client parameter `require_geojson_link` has been removed. [#169](https://github.com/stac-utils/pystac-client/pull/169) ## [v0.3.5] - 2022-05-26 From db8bbad40711e3c2110df723cd636943932d9ed6 Mon Sep 17 00:00:00 2001 From: Phil Varner Date: Wed, 1 Jun 2022 10:57:02 -0400 Subject: [PATCH 3/6] Update docs/usage.rst Co-authored-by: Pete Gadomski --- docs/usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.rst b/docs/usage.rst index 4a845882..3225538d 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -168,7 +168,7 @@ over results: * :meth:`ItemSearch.get_items_as_dicts `: iterate over individual results, yielding a :class:`dict` instance representing each item that matches the search criteria. This eliminates the overhead of creating - class:`pystac.Item` objects with pydantic + class:`pystac.Item` objects Additionally, the ``matched`` method can be used to access result metadata about how many total items matched the query: From 5b7532bdaaf07a3f2b770cbb992ec1af72f0094e Mon Sep 17 00:00:00 2001 From: Phil Varner Date: Wed, 1 Jun 2022 12:33:37 -0400 Subject: [PATCH 4/6] Update docs/usage.rst Co-authored-by: Pete Gadomski --- docs/usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.rst b/docs/usage.rst index 3225538d..8ca88839 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -215,7 +215,7 @@ ItemCollection is one page of results retrieved from search: If you do not need the :class:`pystac.Item` instances, you can instead use :meth:`ItemSearch.get_items_as_dicts ` to retrive dictionary representation of the items, without incurring the cost of -creating the pydantic Item objects. +creating the Item objects. .. code-block:: python From e74227538eab0039c72a2516999a89e2bda877b9 Mon Sep 17 00:00:00 2001 From: Phil Varner Date: Wed, 1 Jun 2022 13:50:16 -0400 Subject: [PATCH 5/6] fix up method name references --- CHANGELOG.md | 7 ++++--- pystac_client/item_search.py | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28aa28fb..6ce9cabb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Deprecated -- Item Search methods `get_all_items` and `get_all_items_as_dict` are now deprecated. +- Item Search methods `get_all_items` and `get_all_items_as_dict` are now deprecated, + and may be removed as early as v0.5.0. These have been deprecated because they have the potential to perform a large number of requests to the server and instantiate a large number of objects in memory. To a user, this is only visible as a large delay in the method call and/or the exhaustion of all available memory. The iterator methods `get_items` or - `get_itemcollections` should be used instead. [#206](https://github.com/stac-utils/pystac-client/pull/206) + `get_item_collections` should be used instead. [#206](https://github.com/stac-utils/pystac-client/pull/206) ### Added @@ -131,7 +132,7 @@ are in a single HTTP session, handle pagination and respects conformance - Update to use PySTAC 1.1.0 - IO changed to use PySTAC's new StacIO base class. - `Search.item_collections()` renamed to `Search.get_item_collections()` -- `Search.item_collections()` renamed to `Search.get_items()` +- `Search.item()` renamed to `Search.get_items()` - Conformance is checked by each individual function that requires a particular conformance - STAC API testing URLs changed to updated APIs - `ItemSearch.get_pages()` function moved to StacApiIO class for general use diff --git a/pystac_client/item_search.py b/pystac_client/item_search.py index 8ad7b680..8f0538de 100644 --- a/pystac_client/item_search.py +++ b/pystac_client/item_search.py @@ -110,7 +110,7 @@ class ItemSearch: No request is sent to the API until a function is called to fetch or iterate through the resulting STAC Items, - either the :meth:`ItemSearch.item_collections` or :meth:`ItemSearch.items` + either the :meth:`ItemSearch.get_item_collections` or :meth:`ItemSearch.get_items` method is called and iterated over. All "Parameters", with the exception of ``max_items``, ``method``, and @@ -579,7 +579,7 @@ def get_item_collections(self) -> Iterator[ItemCollection]: def get_items(self) -> Iterator[Item]: """Iterator that yields :class:`pystac.Item` instances for each item matching the given search parameters. Calls - :meth:`ItemSearch.get_item_collections()` internally and yields from + :meth:`ItemSearch.get_item_collections` internally and yields from :attr:`ItemCollection.features ` for each page of results. @@ -597,7 +597,7 @@ def get_items(self) -> Iterator[Item]: def get_items_as_dicts(self) -> Iterator[Dict[str, Any]]: """Iterator that yields :class:`dict` instances for each item matching the given search parameters. Calls - :meth:`ItemSearch.get_item_collections()` internally and yields from + :meth:`ItemSearch.get_item_collections` internally and yields from :attr:`ItemCollection.features ` for each page of results. @@ -616,16 +616,17 @@ def get_items_as_dicts(self) -> Iterator[Dict[str, Any]]: @lru_cache(1) def get_all_items_as_dict(self) -> Dict[str, Any]: - """DEPRECATED. Use get_items or get_itemcollections instead. + """DEPRECATED. Use :meth:`get_items` or :meth:`get_item_collections` instead. Convenience method that gets all items from all pages, up to - self._max_items, and returns an array of dictionaries. + the number provided by the max_items parameter, and returns an array of + dictionaries. Return: Dict : A GeoJSON FeatureCollection """ warnings.warn( "get_all_items_as_dict is deprecated, use get_items or" - " get_itemcollections instead", + " get_item_collections instead", DeprecationWarning, ) features = [] @@ -640,7 +641,7 @@ def get_all_items_as_dict(self) -> Dict[str, Any]: @lru_cache(1) def get_all_items(self) -> ItemCollection: - """DEPRECATED. Use get_items or get_itemcollections instead. + """DEPRECATED. Use :meth:`get_items` or :meth:`get_item_collections` instead. Convenience method that builds an :class:`ItemCollection` from all items matching the given search parameters. @@ -648,7 +649,8 @@ def get_all_items(self) -> ItemCollection: item_collection : ItemCollection """ warnings.warn( - "get_all_items is deprecated, use get_items or get_itemcollections instead", + "get_all_items is deprecated, use get_items or " + "get_item_collections instead", DeprecationWarning, ) feature_collection = self.get_all_items_as_dict() From 0abeac6797a587d7707e5f90c2244799b99b17fb Mon Sep 17 00:00:00 2001 From: Phil Varner Date: Wed, 1 Jun 2022 15:31:53 -0400 Subject: [PATCH 6/6] rename ItemSearch get_items to items and get_item_collections to item_collections --- CHANGELOG.md | 9 +- docs/quickstart.rst | 6 +- docs/tutorials/cql2-filter.ipynb | 146 ++---------------- docs/usage.rst | 20 +-- pystac_client/client.py | 2 +- pystac_client/collection_client.py | 2 +- pystac_client/item_search.py | 37 ++++- ...> TestItemSearch.test_items_as_dicts.yaml} | 12 +- tests/test_item_search.py | 22 +-- 9 files changed, 78 insertions(+), 178 deletions(-) rename tests/cassettes/test_item_search/{TestItemSearch.test_get_items_as_dicts.yaml => TestItemSearch.test_items_as_dicts.yaml} (96%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ce9cabb..d9397d77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Deprecated -- Item Search methods `get_all_items` and `get_all_items_as_dict` are now deprecated, +- Item Search methods `get_items()` and `get_item_collections()` have been renamed to + `items()` and `item_collections()`. The original methods are now deprecated + and may be removed as early as v0.5.0. [#206](https://github.com/stac-utils/pystac-client/pull/206) +- Item Search methods `get_all_items()` and `get_all_items_as_dict()` are now deprecated, and may be removed as early as v0.5.0. These have been deprecated because they have the potential to perform a large number of requests to the server and instantiate a large number of objects in memory. To a user, this is only visible as a large delay in the method call and/or the - exhaustion of all available memory. The iterator methods `get_items` or - `get_item_collections` should be used instead. [#206](https://github.com/stac-utils/pystac-client/pull/206) + exhaustion of all available memory. The iterator methods `items()` or + `item_collections()` should be used instead. [#206](https://github.com/stac-utils/pystac-client/pull/206) ### Added diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 7a1e0d8c..f21e6ee0 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -106,11 +106,11 @@ Create a search: my_search = catalog_client.search(collections=['sentinel-s2-l2a-cogs'], bbox=[-72.5,40.5,-72,41], max_items=10) print(f"{mysearch.matched()} items found") -The ``get_items`` generator function can be used to iterate through all resulting items. +The ``items()`` generator method can be used to iterate through all resulting items. .. code-block:: python - for item in my_search.get_items(): + for item in my_search.items(): print(item.id) To convert all of Items from a search as a single `PySTAC @@ -125,5 +125,5 @@ Save all found items as a single FeatureCollection: from pystac import ItemCollection - my_itemcollection = ItemCollection(items = list(my_search.get_items())) + my_itemcollection = ItemCollection(items = list(my_search.items())) my_itemcollection.save_object('my_itemcollection.json') diff --git a/docs/tutorials/cql2-filter.ipynb b/docs/tutorials/cql2-filter.ipynb index 67ba284e..9e5a5f08 100644 --- a/docs/tutorials/cql2-filter.ipynb +++ b/docs/tutorials/cql2-filter.ipynb @@ -5,7 +5,7 @@ "id": "e06a27bf", "metadata": {}, "source": [ - "# pystac-client CQL Filtering\n", + "# pystac-client CQL2 Filtering\n", "\n", "This notebook demonstrates the use of pystac-client to use [CQL2 filtering](https://github.com/radiantearth/stac-api-spec/tree/master/fragments/filter). The server needs to support this and advertise conformance as the `https://api.stacspec.org/v1.0.0-rc.1/item-search#filter` class in the `conformsTo` attribute of the root API.\n", "\n", @@ -14,7 +14,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "b65de617", "metadata": {}, "outputs": [], @@ -50,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "98942e75", "metadata": {}, "outputs": [], @@ -76,7 +76,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "d8af6334", "metadata": {}, "outputs": [], @@ -125,7 +125,7 @@ " # limit sets the # of items per page so we can see multiple pages getting fetched\n", " params['filter'] = filt\n", " search = cat.search(**params)\n", - " items = list(search.get_items_as_dicts()) # safe b/c we set max_items = 100\n", + " items = list(search.items_as_dicts()) # safe b/c we set max_items = 100\n", " # DataFrame\n", " items_df = pd.DataFrame(items_to_geodataframe(items))\n", " print(f\"{len(items_df.index)} items found\")\n", @@ -145,24 +145,10 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "dfc0e759", "metadata": {}, - "outputs": [ - { - "ename": "TypeError", - "evalue": "'Item' object is not subscriptable", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/var/folders/3q/jbg6x0zx3194zq6_2jbwygjw0000gn/T/ipykernel_73465/2658164751.py\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 4\u001b[0m }\n\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m \u001b[0msearch_fetch_plot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfilt\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m/var/folders/3q/jbg6x0zx3194zq6_2jbwygjw0000gn/T/ipykernel_73465/1512843940.py\u001b[0m in \u001b[0;36msearch_fetch_plot\u001b[0;34m(params, filt)\u001b[0m\n\u001b[1;32m 46\u001b[0m \u001b[0mitems\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtake\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m100\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msearch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_items\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 47\u001b[0m \u001b[0;31m# DataFrame\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 48\u001b[0;31m \u001b[0mitems_df\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mDataFrame\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mitems_to_geodataframe\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mitems\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 49\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"{len(items_df.index)} items found\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 50\u001b[0m \u001b[0mfield\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'properties.eo:cloud_cover'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/var/folders/3q/jbg6x0zx3194zq6_2jbwygjw0000gn/T/ipykernel_73465/3732288798.py\u001b[0m in \u001b[0;36mitems_to_geodataframe\u001b[0;34m(items)\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mitems\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0m_i\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdeepcopy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0m_i\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'geometry'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mshape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_i\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'geometry'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 21\u001b[0m \u001b[0m_items\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_i\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0mgdf\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mGeoDataFrame\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjson_normalize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_items\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mTypeError\u001b[0m: 'Item' object is not subscriptable" - ] - } - ], + "outputs": [], "source": [ "filt = {\n", " \"op\": \"lte\",\n", @@ -177,64 +163,7 @@ "execution_count": null, "id": "9c2f9ca1", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "92 items found\n" - ] - }, - { - "data": {}, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.holoviews_exec.v0+json": "", - "text/html": [ - "
\n", - "
\n", - "
\n", - "" - ], - "text/plain": [ - ":Curve [properties.datetime] (properties.eo:cloud_cover)" - ] - }, - "execution_count": 19, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "2474" - } - }, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "filt = {\n", " \"op\": \"gte\",\n", @@ -249,64 +178,7 @@ "execution_count": null, "id": "109f673c", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "41 items found\n" - ] - }, - { - "data": {}, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.holoviews_exec.v0+json": "", - "text/html": [ - "
\n", - "
\n", - "
\n", - "" - ], - "text/plain": [ - ":Curve [properties.datetime] (properties.eo:cloud_cover)" - ] - }, - "execution_count": 20, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "2658" - } - }, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "filt = { \n", " \"op\": \"and\",\n", diff --git a/docs/usage.rst b/docs/usage.rst index 8ca88839..79f4f57f 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -159,13 +159,13 @@ requests to a service's "search" endpoint. This method returns a Instances of :class:`~pystac_client.ItemSearch` have 2 methods for iterating over results: -* :meth:`ItemSearch.get_item_collections - `: iterator over *pages* of results, +* :meth:`ItemSearch.item_collections + `: iterator over *pages* of results, yielding an :class:`~pystac.ItemCollection` for each page of results. -* :meth:`ItemSearch.get_items `: an iterator over +* :meth:`ItemSearch.items `: an iterator over individual Item objects, yielding a :class:`pystac.Item` instance for all Items that match the search criteria. -* :meth:`ItemSearch.get_items_as_dicts `: +* :meth:`ItemSearch.items_as_dicts `: iterate over individual results, yielding a :class:`dict` instance representing each item that matches the search criteria. This eliminates the overhead of creating class:`pystac.Item` objects @@ -179,7 +179,7 @@ how many total items matched the query: .. code-block:: python - >>> for item in results.get_items(): + >>> for item in results.items(): ... print(item.id) S2B_OPER_MSI_L2A_TL_SGS__20190101T200120_A009518_T18TXP_N02.11 MCD43A4.A2019010.h12v04.006.2019022234410 @@ -187,7 +187,7 @@ how many total items matched the query: MYD11A1.A2019002.h12v04.006.2019003174703 MYD11A1.A2019001.h12v04.006.2019002165238 -The :meth:`~pystac_client.ItemSearch.get_items` and related methods handle retrieval of +The :meth:`~pystac_client.ItemSearch.items` and related methods handle retrieval of successive pages of results by finding links with a ``"rel"`` type of ``"next"`` and parsing them to construct the next request. The default @@ -203,7 +203,7 @@ ItemCollection is one page of results retrieved from search: .. code-block:: python - >>> for ic in results.get_item_collections(): + >>> for ic in results.item_collections(): ... for item in ic.items: ... print(item.id) S2B_OPER_MSI_L2A_TL_SGS__20190101T200120_A009518_T18TXP_N02.11 @@ -213,13 +213,13 @@ ItemCollection is one page of results retrieved from search: MYD11A1.A2019001.h12v04.006.2019002165238 If you do not need the :class:`pystac.Item` instances, you can instead use -:meth:`ItemSearch.get_items_as_dicts ` -to retrive dictionary representation of the items, without incurring the cost of +:meth:`ItemSearch.items_as_dicts ` +to retrieve dictionary representation of the items, without incurring the cost of creating the Item objects. .. code-block:: python - >>> for item_dict in results.get_items_as_dicts(): + >>> for item_dict in results.items_as_dicts(): ... print(item_dict["id"]) S2B_OPER_MSI_L2A_TL_SGS__20190101T200120_A009518_T18TXP_N02.11 MCD43A4.A2019010.h12v04.006.2019022234410 diff --git a/pystac_client/client.py b/pystac_client/client.py index 3578b4ec..dd1c0dde 100644 --- a/pystac_client/client.py +++ b/pystac_client/client.py @@ -142,7 +142,7 @@ def get_items(self) -> Iterable["Item_Type"]: """ if self._stac_io.conforms_to(ConformanceClasses.ITEM_SEARCH): search = self.search() - yield from search.get_items() + yield from search.items() else: return super().get_items() diff --git a/pystac_client/collection_client.py b/pystac_client/collection_client.py index e217e3d9..cee3cbbe 100644 --- a/pystac_client/collection_client.py +++ b/pystac_client/collection_client.py @@ -30,7 +30,7 @@ def get_items(self) -> Iterable["Item_Type"]: root = self.get_root() if link is not None and root is not None: search = ItemSearch(link.href, method="GET", stac_io=root._stac_io) - yield from search.get_items() + yield from search.items() else: yield from super().get_items() diff --git a/pystac_client/item_search.py b/pystac_client/item_search.py index 8f0538de..f4750534 100644 --- a/pystac_client/item_search.py +++ b/pystac_client/item_search.py @@ -110,7 +110,7 @@ class ItemSearch: No request is sent to the API until a function is called to fetch or iterate through the resulting STAC Items, - either the :meth:`ItemSearch.get_item_collections` or :meth:`ItemSearch.get_items` + either the :meth:`ItemSearch.item_collections` or :meth:`ItemSearch.items` method is called and iterated over. All "Parameters", with the exception of ``max_items``, ``method``, and @@ -193,7 +193,7 @@ class ItemSearch: sortby: A single field or list of fields to sort the response by 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_items_as_dicts` to avoid object unmarshalling errors. + Use `items_as_dicts` 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' @@ -564,6 +564,19 @@ def matched(self) -> Optional[int]: return found def get_item_collections(self) -> Iterator[ItemCollection]: + """DEPRECATED. Use :meth:`ItemSearch.item_collections` instead. + + Yields: + ItemCollection : a group of Items matching the search criteria within an + ItemCollection + """ + warnings.warn( + "get_item_collections() is deprecated, use item_collections() instead", + DeprecationWarning, + ) + return self.item_collections() + + def item_collections(self) -> Iterator[ItemCollection]: """Iterator that yields ItemCollection objects. Each ItemCollection is a page of results from the search. @@ -577,9 +590,21 @@ def get_item_collections(self) -> Iterator[ItemCollection]: yield ItemCollection.from_dict(page, preserve_dict=False, root=self.client) def get_items(self) -> Iterator[Item]: + """DEPRECATED. Use :meth:`ItemSearch.items` instead. + + Yields: + Item : each Item matching the search criteria + """ + warnings.warn( + "get_items() is deprecated, use items() instead", + DeprecationWarning, + ) + return self.items() + + def items(self) -> Iterator[Item]: """Iterator that yields :class:`pystac.Item` instances for each item matching the given search parameters. Calls - :meth:`ItemSearch.get_item_collections` internally and yields from + :meth:`ItemSearch.item_collections` internally and yields from :attr:`ItemCollection.features ` for each page of results. @@ -587,17 +612,17 @@ def get_items(self) -> Iterator[Item]: Item : each Item matching the search criteria """ nitems = 0 - for item_collection in self.get_item_collections(): + for item_collection in self.item_collections(): for item in item_collection: yield item nitems += 1 if self._max_items and nitems >= self._max_items: return - def get_items_as_dicts(self) -> Iterator[Dict[str, Any]]: + def items_as_dicts(self) -> Iterator[Dict[str, Any]]: """Iterator that yields :class:`dict` instances for each item matching the given search parameters. Calls - :meth:`ItemSearch.get_item_collections` internally and yields from + :meth:`ItemSearch.item_collections` internally and yields from :attr:`ItemCollection.features ` for each page of results. diff --git a/tests/cassettes/test_item_search/TestItemSearch.test_get_items_as_dicts.yaml b/tests/cassettes/test_item_search/TestItemSearch.test_items_as_dicts.yaml similarity index 96% rename from tests/cassettes/test_item_search/TestItemSearch.test_get_items_as_dicts.yaml rename to tests/cassettes/test_item_search/TestItemSearch.test_items_as_dicts.yaml index 240cd7a5..98b5b710 100644 --- a/tests/cassettes/test_item_search/TestItemSearch.test_get_items_as_dicts.yaml +++ b/tests/cassettes/test_item_search/TestItemSearch.test_items_as_dicts.yaml @@ -19,7 +19,7 @@ interactions: response: body: string: !!binary | - H4sIAPR0l2IC/+2cW4/iNhSA/wpC6r6USXxJHIdqVKlTddVK7VbbfeoIoQAGsg1JlGRurfa/99hJ + H4sIAGe+l2IC/+2cW4/iNhSA/wpC6r6USXxJHIdqVKlTddVK7VbbfeoIoQAGsg1JlGRurfa/99hJ wIEMDAsst6BhBI5jn1vMF+fY/7Wzl1i0u+1fhJc9JOIuCgIxzPwobHfa47wsbXfv/2v7I6j1mPVn fctCDmVWP33qY95HDPUJwhwj7MoPLiLEhpMHg+gZTrxxqIEp4h2LGq7LsEs6sgjZjBG3Y1kGYowi p9dZEgRaCPzwn7zvRARwZKjLVtT24jjwh54sND+n6sg0EWM4Ms2yOO2aZhx4oci85GUYzeKHTCTG @@ -65,13 +65,13 @@ interactions: Content-Type: - application/geo+json Date: - - Wed, 01 Jun 2022 14:17:23 GMT + - Wed, 01 Jun 2022 19:30:46 GMT Strict-Transport-Security: - max-age=15724800; includeSubDomains Vary: - Accept-Encoding X-Azure-Ref: - - 09HSXYgAAAAAN/WrHkOHSTrMaVgXSKRyuTU5aMjIxMDYwNjE0MDUxADkyN2FiZmE2LTE5ZjYtNGFmMS1hMDlkLWM5NTlkOWExZTY0NA== + - 0Zr6XYgAAAABsVlagafu2RqCzig51hgH7TU5aMjIxMDYwNjE0MDI3ADkyN2FiZmE2LTE5ZjYtNGFmMS1hMDlkLWM5NTlkOWExZTY0NA== X-Cache: - CONFIG_NOCACHE status: @@ -98,7 +98,7 @@ interactions: response: body: string: !!binary | - H4sIAPR0l2IC/+2dbW+rxhKA/4plqf1SB/YFlsVVVKm56lGv1J7q9HzqkWVhe21zigEBebvV+e93 + H4sIAGe+l2IC/+2dbW+rxhKA/4plqf1SB/YFlsVVVKm56lGv1J7q9HzqkWVhe21zigEBebvV+e93 dgGzxg52Ert+I0oie1l2Z2aH9cNkhvzTzZ5j0e13fxFedp+IuygIxDjzo7Db607ztrTb//JP159A r4dsuBhaFnIoo8NUDDEfzocEYYY4suCM0Sh6gt43DjWwSwmlPYsarssIcXqqEbvYJT3LMhBjxCGD Xm1+GCPww7/zKRMRwJGxLlLR24vjwB97stH8mqoj80RM4cg8y+K0b5px4IUi85LncbSI7zORGAt/ @@ -145,13 +145,13 @@ interactions: Content-Type: - application/geo+json Date: - - Wed, 01 Jun 2022 14:17:24 GMT + - Wed, 01 Jun 2022 19:30:47 GMT Strict-Transport-Security: - max-age=15724800; includeSubDomains Vary: - Accept-Encoding X-Azure-Ref: - - 09HSXYgAAAADdIeMyUv+4R56ucppR1iHFTU5aMjIxMDYwNjE0MDUxADkyN2FiZmE2LTE5ZjYtNGFmMS1hMDlkLWM5NTlkOWExZTY0NA== + - 0Z76XYgAAAACu5+MprhBqQrVcK11NG1t4TU5aMjIxMDYwNjE0MDI3ADkyN2FiZmE2LTE5ZjYtNGFmMS1hMDlkLWM5NTlkOWExZTY0NA== X-Cache: - CONFIG_NOCACHE status: diff --git a/tests/test_item_search.py b/tests/test_item_search.py index aae8fffd..f26b5b18 100644 --- a/tests/test_item_search.py +++ b/tests/test_item_search.py @@ -507,7 +507,7 @@ def test_results(self) -> None: max_items=20, limit=10, ) - results = search.get_items() + results = search.items() assert all(isinstance(item, pystac.Item) for item in results) @@ -521,7 +521,7 @@ def test_ids_results(self) -> None: url=SEARCH_URL, ids=ids, ) - results = list(search.get_items()) + results = list(search.items()) assert len(results) == 1 assert all(item.id in ids for item in results) @@ -531,13 +531,13 @@ def test_datetime_results(self) -> None: # Datetime range string datetime_ = "2019-01-01T00:00:01Z/2019-01-01T00:00:10Z" search = ItemSearch(url=SEARCH_URL, datetime=datetime_) - results = list(search.get_items()) + results = list(search.items()) assert len(results) == 33 min_datetime = datetime(2019, 1, 1, 0, 0, 1, tzinfo=tzutc()) max_datetime = datetime(2019, 1, 1, 0, 0, 10, tzinfo=tzutc()) search = ItemSearch(url=SEARCH_URL, datetime=(min_datetime, max_datetime)) - results = search.get_items() + results = search.items() assert all( min_datetime <= item.datetime <= (max_datetime + timedelta(seconds=1)) for item in results @@ -561,7 +561,7 @@ def test_intersects_results(self) -> None: search = ItemSearch( url=SEARCH_URL, intersects=intersects_dict, collections="naip" ) - results = list(search.get_items()) + results = list(search.items()) assert len(results) == 30 # Geo-interface object @@ -574,7 +574,7 @@ def __geo_interface__(self) -> None: search = ItemSearch( url=SEARCH_URL, intersects=intersects_obj, collections="naip" ) - results = search.get_items() + results = search.items() assert all(isinstance(item, pystac.Item) for item in results) @pytest.mark.vcr @@ -588,7 +588,7 @@ def test_result_paging(self) -> None: ) # Check that the current page changes on the ItemSearch instance when a new page is requested - pages = list(search.get_item_collections()) + pages = list(search.item_collections()) assert pages[1] != pages[2] assert pages[1].items != pages[2].items @@ -606,7 +606,7 @@ def test_get_all_items(self) -> None: assert len(item_collection.items) == 20 @pytest.mark.vcr - def test_get_items_as_dicts(self) -> None: + def test_items_as_dicts(self) -> None: search = ItemSearch( url=SEARCH_URL, bbox=(-73.21, 43.99, -73.12, 44.05), @@ -614,7 +614,7 @@ def test_get_items_as_dicts(self) -> None: limit=10, max_items=20, ) - assert len(list(search.get_items_as_dicts())) == 20 + assert len(list(search.items_as_dicts())) == 20 class TestItemSearchQuery: @@ -626,7 +626,7 @@ def test_query_shortcut_syntax(self) -> None: query=["gsd=10"], max_items=1, ) - items1 = list(search.get_items()) + items1 = list(search.items()) search = ItemSearch( url=SEARCH_URL, @@ -634,7 +634,7 @@ def test_query_shortcut_syntax(self) -> None: query={"gsd": {"eq": 10}}, max_items=1, ) - items2 = list(search.get_items()) + items2 = list(search.items()) assert len(items1) == 1 assert len(items2) == 1