Ejemplo n.º 1
0
    def test_results(self):
        search = ItemSearch(
            url=SEARCH_URL,
            collections='naip',
            max_items=20,
            limit=10,
        )
        results = search.items()

        assert all(isinstance(item, pystac.Item) for item in results)
Ejemplo n.º 2
0
    def test_intersects_param(self):
        # Dict input
        search = ItemSearch(url=SEARCH_URL, intersects=INTERSECTS_EXAMPLE)
        assert search.search_parameters_post[
            'intersects'] == INTERSECTS_EXAMPLE

        # JSON string input
        search = ItemSearch(url=SEARCH_URL,
                            intersects=json.dumps(INTERSECTS_EXAMPLE))
        assert search.search_parameters_post[
            'intersects'] == INTERSECTS_EXAMPLE
Ejemplo n.º 3
0
    def test_ids_results(self):
        ids = [
            'm_3510836_se_12_060_20180508_20190331',
            'm_3510840_se_12_060_20180504_20190331'
        ]
        search = ItemSearch(
            url=SEARCH_URL,
            ids=ids,
        )
        results = list(search.items())

        assert len(results) == 2
        assert all(item.id in ids for item in results)
Ejemplo n.º 4
0
    def test_result_paging(self):
        search = ItemSearch(
            url=SEARCH_URL,
            bbox=(-73.21, 43.99, -73.12, 44.05),
            collections='naip',
            limit=10,
            max_items=20,
        )

        # Check that the current page changes on the ItemSearch instance when a new page is requested
        pages = list(search.item_collections())

        assert pages[1] != pages[2]
        assert pages[1].features != pages[2].features
Ejemplo n.º 5
0
    def test_method(self):
        # Default method should be GET...
        search = ItemSearch(url=ASTRAEA_URL)
        assert search.method == 'GET'

        # ...unless the "intersects" argument is present.
        search = ItemSearch(url=ASTRAEA_URL, intersects=INTERSECTS_EXAMPLE)
        assert search.method == 'POST'

        # "method" argument should take precedence over presence of "intersects"
        search = ItemSearch(url=ASTRAEA_URL,
                            method='GET',
                            intersects=INTERSECTS_EXAMPLE)
        assert search.method == 'GET'
Ejemplo n.º 6
0
    def test_datetime_param(self):
        # Single timestamp input
        search = ItemSearch(url=ASTRAEA_URL, datetime='2020-02-01T00:00:00Z')
        assert search.search_parameters_post['datetime'] == (
            '2020-02-01T00:00:00Z', )

        # Timestamp range input
        search = ItemSearch(
            url=ASTRAEA_URL,
            datetime='2020-02-01T00:00:00Z/2020-02-02T00:00:00Z')
        assert search.search_parameters_post['datetime'] == (
            '2020-02-01T00:00:00Z', '2020-02-02T00:00:00Z')

        # Timestamp list input
        search = ItemSearch(
            url=ASTRAEA_URL,
            datetime=['2020-02-01T00:00:00Z', '2020-02-02T00:00:00Z'])
        assert search.search_parameters_post['datetime'] == (
            '2020-02-01T00:00:00Z', '2020-02-02T00:00:00Z')

        # Open timestamp range input
        search = ItemSearch(url=ASTRAEA_URL,
                            datetime='2020-02-01T00:00:00Z/..')
        assert search.search_parameters_post['datetime'] == (
            '2020-02-01T00:00:00Z', '..')

        start = datetime(2020, 2, 1, 0, 0, 0, tzinfo=tzutc())
        end = datetime(2020, 2, 2, 0, 0, 0, tzinfo=tzutc())

        # Single datetime input
        search = ItemSearch(url=ASTRAEA_URL, datetime=start)
        assert search.search_parameters_post['datetime'] == (
            '2020-02-01T00:00:00Z', )

        # Datetime range input
        search = ItemSearch(url=ASTRAEA_URL, datetime=[start, end])
        assert search.search_parameters_post['datetime'] == (
            '2020-02-01T00:00:00Z', '2020-02-02T00:00:00Z')

        # Open datetime range input
        search = ItemSearch(url=ASTRAEA_URL, datetime=(start, None))
        assert search.search_parameters_post['datetime'] == (
            '2020-02-01T00:00:00Z', '..')

        # Localized datetime input (should be converted to UTC)
        start_localized = datetime(2020,
                                   2,
                                   1,
                                   0,
                                   0,
                                   0,
                                   tzinfo=gettz('US/Eastern'))
        search = ItemSearch(url=ASTRAEA_URL, datetime=start_localized)
        assert search.search_parameters_post['datetime'] == (
            '2020-02-01T05:00:00Z', )
Ejemplo n.º 7
0
    def test_collections_param(self, astraea_api):
        # Single ID string
        search = ItemSearch(url=ASTRAEA_URL, collections='naip')
        assert search.search_parameters_post['collections'] == ('naip', )

        # Comma-separated ID string
        search = ItemSearch(url=ASTRAEA_URL, collections='naip,landsat8_l1tp')
        assert search.search_parameters_post['collections'] == (
            'naip', 'landsat8_l1tp')

        # List of ID strings
        search = ItemSearch(url=ASTRAEA_URL,
                            collections=['naip', 'landsat8_l1tp'])
        assert search.search_parameters_post['collections'] == (
            'naip', 'landsat8_l1tp')

        # Generator of ID strings
        def collectioner():
            yield from ['naip', 'landsat8_l1tp']

        search = ItemSearch(url=ASTRAEA_URL, collections=collectioner())
        assert search.search_parameters_post['collections'] == (
            'naip', 'landsat8_l1tp')

        collection = astraea_api.get_child('landsat8_l1tp')

        # Single pystac.Collection
        search = ItemSearch(url=ASTRAEA_URL, collections=collection)
        assert search.search_parameters_post['collections'] == (
            'landsat8_l1tp', )

        # Mixed list
        search = ItemSearch(url=ASTRAEA_URL, collections=[collection, 'naip'])
        assert search.search_parameters_post['collections'] == (
            'landsat8_l1tp', 'naip')
Ejemplo n.º 8
0
    def test_ids_param(self):
        # Single ID
        search = ItemSearch(url=ASTRAEA_URL,
                            ids='m_3510836_se_12_060_20180508_20190331')
        assert search.search_parameters_post['ids'] == (
            'm_3510836_se_12_060_20180508_20190331', )

        # Comma-separated ID string
        search = ItemSearch(
            url=ASTRAEA_URL,
            ids=
            'm_3510836_se_12_060_20180508_20190331,m_3510840_se_12_060_20180504_20190331'
        )
        assert search.search_parameters_post['ids'] == (
            'm_3510836_se_12_060_20180508_20190331',
            'm_3510840_se_12_060_20180504_20190331')

        # List of IDs
        search = ItemSearch(url=ASTRAEA_URL,
                            ids=[
                                'm_3510836_se_12_060_20180508_20190331',
                                'm_3510840_se_12_060_20180504_20190331'
                            ])
        assert search.search_parameters_post['ids'] == (
            'm_3510836_se_12_060_20180508_20190331',
            'm_3510840_se_12_060_20180504_20190331')

        # Generator of IDs
        def ids():
            yield from [
                'm_3510836_se_12_060_20180508_20190331',
                'm_3510840_se_12_060_20180504_20190331'
            ]

        search = ItemSearch(url=ASTRAEA_URL, ids=ids())
        assert search.search_parameters_post['ids'] == (
            'm_3510836_se_12_060_20180508_20190331',
            'm_3510840_se_12_060_20180504_20190331')
Ejemplo n.º 9
0
    def test_bbox_param(self):
        # Tuple input
        search = ItemSearch(url=ASTRAEA_URL, bbox=(-104.5, 44.0, -104.0, 45.0))
        assert search.search_parameters_post['bbox'] == (-104.5, 44.0, -104.0,
                                                         45.0)

        # List input
        search = ItemSearch(url=ASTRAEA_URL, bbox=[-104.5, 44.0, -104.0, 45.0])
        assert search.search_parameters_post['bbox'] == (-104.5, 44.0, -104.0,
                                                         45.0)

        # String Input
        search = ItemSearch(url=ASTRAEA_URL, bbox='-104.5,44.0,-104.0,45.0')
        assert search.search_parameters_post['bbox'] == (-104.5, 44.0, -104.0,
                                                         45.0)

        # Generator Input
        def bboxer():
            yield from [-104.5, 44.0, -104.0, 45.0]

        search = ItemSearch(url=ASTRAEA_URL, bbox=bboxer())
        assert search.search_parameters_post['bbox'] == (-104.5, 44.0, -104.0,
                                                         45.0)
Ejemplo n.º 10
0
    def test_datetime_results(self):
        # Datetime range string
        datetime_ = '2019-01-01T00:00:01Z/2019-01-01T00:00:10Z'
        search = ItemSearch(url=SEARCH_URL, datetime=datetime_)
        results = list(search.items())
        assert len(results) == 12

        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.items()
        assert all(min_datetime <= item.datetime <= (max_datetime +
                                                     timedelta(seconds=1))
                   for item in results)
Ejemplo n.º 11
0
    def test_intersects_results(self):
        # GeoJSON-like dict
        intersects_dict = {
            'type':
            'Polygon',
            'coordinates': [[[-73.21, 43.99], [-73.21, 44.05], [-73.12, 44.05],
                             [-73.12, 43.99], [-73.21, 43.99]]]
        }
        search = ItemSearch(url=SEARCH_URL,
                            intersects=intersects_dict,
                            collections='naip')
        results = list(search.items())
        assert len(results) == 30

        # Geo-interface object
        class MockGeoObject:
            __geo_interface__ = intersects_dict

        intersects_obj = MockGeoObject()
        search = ItemSearch(url=SEARCH_URL,
                            intersects=intersects_obj,
                            collections='naip')
        results = search.items()
        assert all(isinstance(item, pystac.Item) for item in results)
Ejemplo n.º 12
0
    def search(self,
               *,
               limit: Optional[int] = None,
               bbox: Optional[BBoxLike] = None,
               datetime: Optional[DatetimeLike] = None,
               intersects: Optional[IntersectsLike] = None,
               ids: Optional[IDsLike] = None,
               collections: Optional[CollectionsLike] = None,
               max_items: Optional[int] = None,
               method: Optional[str] = None,
               next_resolver: Optional[Callable] = None) -> ItemSearch:
        """Query the ``/search`` endpoint using the given parameters.

        This method returns an :class:`~pystac_api.ItemSearch` instance, see that class's documentation
        for details on how to get the number of matches and iterate over results. All keyword arguments are passed
        directly to the :class:`~pystac_api.ItemSearch` instance.

        .. warning::

            This method is only implemented if the API conforms to the
            `STAC API - Item Search <https://github.com/radiantearth/stac-api-spec/tree/master/item-search>`__ spec
            *and* contains a link with a ``"rel"`` type of ``"search"`` in its root catalog.
            If the API does not meet either of these criteria, this method will raise a :exc:`NotImplementedError`.

        Parameters
        ----------
        limit : int, optional
            The maximum number of items to return *per page*. Defaults to ``None``, which falls back to the limit set
            by the service.
        bbox: list or tuple or Iterator or str, optional
            May be a list, tuple, or iterator representing a bounding box of 2D or 3D coordinates. Results will be
            filtered to only those intersecting the bounding box.
        datetime: str or datetime.datetime or list or tuple or Iterator, optional
            Either a single datetime or datetime range used to filter results. You may express a single datetime using
            a :class:`datetime.datetime` instance or a `RFC 3339-compliant <https://tools.ietf.org/html/rfc3339>`__
            timestamp. Instances of :class:`datetime.datetime` may be either timezone aware or unaware. Timezone aware
            instances will be converted to a UTC timestamp before being passed to the endpoint. Timezone unaware
            instances are assumed to represent UTC timestamps.
            You may represent a datetime range using a ``"/"`` separated string as described in the spec, or a list,
            tuple, or iterator of 2 timestamps or datetime instances. For open-ended ranges, use either ``".."``
            (``'2020-01-01:00:00:00Z/..'``, ``['2020-01-01:00:00:00Z', '..']``) or a value of ``None``
            (``['2020-01-01:00:00:00Z', None]``).
        intersects: str or dict, optional
            A GeoJSON-like dictionary or JSON string. Results will be filtered to only those intersecting the geometry
        ids: list, optional
            List of Item ids to return. All other filter parameters that further restrict the number of search results
            (except ``limit``) are ignored.
        collections: list, optional
            List of one or more Collection IDs or :class:`pystac.Collection` instances. Only Items in one of the
            provided Collections will be searched
        max_items : int or None, optional
            The maximum number of items to return from the search. *Note that this is not a STAC API - Item Search
            parameter and is instead used by the client to limit the total number of returned items*.
        method : str or None, optional
            The HTTP method to use when making a request to the service. This must be either ``"GET"``, ``"POST"``, or
            ``None``. If ``None``, this will default to ``"POST"`` if the ``intersects`` argument is present and
            ``"GET"`` if not. If a ``"POST"`` request receives a ``405`` status for the response, it will automatically
            retry with a ``"GET"`` request for all subsequent requests.
        next_resolver: Callable, optional
            A callable that will be used to construct the next request based on a "next" link and the previous request.
            Defaults to using the :func:`~pystac_api.paging.simple_stac_resolver`.

        Returns
        -------
        results : ItemSearch

        Raises
        ------
        NotImplementedError
            If the API does not conform to the `Item Search spec
            <https://github.com/radiantearth/stac-api-spec/tree/master/item-search>`__ or does not have a link with
            a ``"rel"`` type of ``"search"``.
        """
        if not self.conforms_to(ConformanceClasses.STAC_API_ITEM_SEARCH):
            spec_name = ConformanceClasses.STAC_API_ITEM_SEARCH.name
            spec_uris = '\n\t'.join(
                ConformanceClasses.STAC_API_ITEM_SEARCH.all_uris)
            msg = f'This service does not conform to the {spec_name} spec and therefore the search method is not ' \
                  f'implemented. Services must publish one of the following conformance URIs in order to conform to ' \
                  f'this spec (preferably the first one):\n\t{spec_uris}'
            raise NotImplementedError(msg)

        search_link = self.get_single_link('search')
        if search_link is None:
            raise NotImplementedError(
                'No link with a "rel" type of "search" could be found in this services\'s '
                'root catalog.')

        return ItemSearch(search_link.target,
                          limit=limit,
                          bbox=bbox,
                          datetime=datetime,
                          intersects=intersects,
                          ids=ids,
                          collections=collections,
                          max_items=max_items,
                          method=method,
                          next_resolver=next_resolver,
                          conformance=list(self.conformance))