PySTAC-Client Introduction#
This notebook shows basic use of pystac-client to open an API, iterate through Collections and Items, and perform simple spatio-temporal searches.
[1]:
# set pystac_client logger to DEBUG to see API calls
import logging
from pystac_client import Client
logging.basicConfig()
logger = logging.getLogger("pystac_client")
logger.setLevel(logging.DEBUG)
Client#
We first connect to an API by retrieving the root catalog, or landing page, of the API with the Client.open function.
[2]:
# STAC API root URL
URL = "https://planetarycomputer.microsoft.com/api/stac/v1"
# custom headers
headers = []
cat = Client.open(URL, headers=headers)
cat
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1 Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
[2]:
CollectionClient#
As with a static catalog the get_collections function will iterate through the Collections in the Catalog. Notice that because this is an API it can get all the Collections through a single call, rather than having to fetch each one individually.
[3]:
for collection in cat.get_collections():
print(collection)
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1/ Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1/collections Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
<CollectionClient id=3dep-lidar-classification>
<CollectionClient id=3dep-lidar-copc>
<CollectionClient id=3dep-lidar-dsm>
<CollectionClient id=3dep-lidar-dtm>
<CollectionClient id=3dep-lidar-dtm-native>
<CollectionClient id=3dep-lidar-hag>
<CollectionClient id=3dep-lidar-intensity>
<CollectionClient id=3dep-lidar-pointsourceid>
<CollectionClient id=3dep-lidar-returns>
<CollectionClient id=3dep-seamless>
<CollectionClient id=alos-dem>
<CollectionClient id=alos-fnf-mosaic>
<CollectionClient id=alos-palsar-mosaic>
<CollectionClient id=aster-l1t>
<CollectionClient id=chesapeake-lc-13>
<CollectionClient id=chesapeake-lc-7>
<CollectionClient id=chesapeake-lu>
<CollectionClient id=chloris-biomass>
<CollectionClient id=cil-gdpcir-cc-by>
<CollectionClient id=cil-gdpcir-cc-by-sa>
<CollectionClient id=cil-gdpcir-cc0>
<CollectionClient id=conus404>
<CollectionClient id=cop-dem-glo-30>
<CollectionClient id=cop-dem-glo-90>
<CollectionClient id=daymet-annual-hi>
<CollectionClient id=daymet-annual-na>
<CollectionClient id=daymet-annual-pr>
<CollectionClient id=daymet-daily-hi>
<CollectionClient id=daymet-daily-na>
<CollectionClient id=daymet-daily-pr>
<CollectionClient id=daymet-monthly-hi>
<CollectionClient id=daymet-monthly-na>
<CollectionClient id=daymet-monthly-pr>
<CollectionClient id=deltares-floods>
<CollectionClient id=deltares-water-availability>
<CollectionClient id=drcog-lulc>
<CollectionClient id=eclipse>
<CollectionClient id=ecmwf-forecast>
<CollectionClient id=era5-pds>
<CollectionClient id=esa-cci-lc>
<CollectionClient id=esa-cci-lc-netcdf>
<CollectionClient id=esa-worldcover>
<CollectionClient id=fia>
<CollectionClient id=fws-nwi>
<CollectionClient id=gap>
<CollectionClient id=gbif>
<CollectionClient id=gnatsgo-rasters>
<CollectionClient id=gnatsgo-tables>
<CollectionClient id=goes-cmi>
<CollectionClient id=goes-glm>
<CollectionClient id=gpm-imerg-hhr>
<CollectionClient id=gridmet>
<CollectionClient id=hgb>
<CollectionClient id=hls2-l30>
<CollectionClient id=hls2-s30>
<CollectionClient id=hrea>
<CollectionClient id=io-biodiversity>
<CollectionClient id=io-lulc>
<CollectionClient id=io-lulc-9-class>
<CollectionClient id=io-lulc-annual-v02>
<CollectionClient id=jrc-gsw>
<CollectionClient id=kaza-hydroforecast>
<CollectionClient id=landsat-c2-l1>
<CollectionClient id=landsat-c2-l2>
<CollectionClient id=met-office-global-deterministic-height>
<CollectionClient id=met-office-global-deterministic-near-surface>
<CollectionClient id=met-office-global-deterministic-pressure>
<CollectionClient id=met-office-global-deterministic-whole-atmosphere>
<CollectionClient id=met-office-uk-deterministic-height>
<CollectionClient id=met-office-uk-deterministic-near-surface>
<CollectionClient id=met-office-uk-deterministic-pressure>
<CollectionClient id=met-office-uk-deterministic-whole-atmosphere>
<CollectionClient id=mobi>
<CollectionClient id=modis-09A1-061>
<CollectionClient id=modis-09Q1-061>
<CollectionClient id=modis-10A1-061>
<CollectionClient id=modis-10A2-061>
<CollectionClient id=modis-11A1-061>
<CollectionClient id=modis-11A2-061>
<CollectionClient id=modis-13A1-061>
<CollectionClient id=modis-13Q1-061>
<CollectionClient id=modis-14A1-061>
<CollectionClient id=modis-14A2-061>
<CollectionClient id=modis-15A2H-061>
<CollectionClient id=modis-15A3H-061>
<CollectionClient id=modis-16A3GF-061>
<CollectionClient id=modis-17A2H-061>
<CollectionClient id=modis-17A2HGF-061>
<CollectionClient id=modis-17A3HGF-061>
<CollectionClient id=modis-21A2-061>
<CollectionClient id=modis-43A4-061>
<CollectionClient id=modis-64A1-061>
<CollectionClient id=ms-buildings>
<CollectionClient id=mtbs>
<CollectionClient id=naip>
<CollectionClient id=nasa-nex-gddp-cmip6>
<CollectionClient id=nasadem>
<CollectionClient id=nested-eagle-conus>
<CollectionClient id=nested-eagle-global>
<CollectionClient id=noaa-c-cap>
<CollectionClient id=noaa-cdr-ocean-heat-content>
<CollectionClient id=noaa-cdr-ocean-heat-content-netcdf>
<CollectionClient id=noaa-cdr-sea-surface-temperature-optimum-interpolation>
<CollectionClient id=noaa-cdr-sea-surface-temperature-whoi>
<CollectionClient id=noaa-cdr-sea-surface-temperature-whoi-netcdf>
<CollectionClient id=noaa-climate-normals-gridded>
<CollectionClient id=noaa-climate-normals-netcdf>
<CollectionClient id=noaa-climate-normals-tabular>
<CollectionClient id=noaa-mrms-qpe-1h-pass1>
<CollectionClient id=noaa-mrms-qpe-1h-pass2>
<CollectionClient id=noaa-mrms-qpe-24h-pass2>
<CollectionClient id=noaa-nclimgrid-monthly>
<CollectionClient id=nrcan-landcover>
<CollectionClient id=planet-nicfi-analytic>
<CollectionClient id=planet-nicfi-visual>
<CollectionClient id=predicted-damage-colombia-2026>
<CollectionClient id=predicted-damage-venezuela-2026>
<CollectionClient id=sentinel-1-grd>
<CollectionClient id=sentinel-1-rtc>
<CollectionClient id=sentinel-2-l2a>
<CollectionClient id=sentinel-3-olci-lfr-l2-netcdf>
<CollectionClient id=sentinel-3-olci-wfr-l2-netcdf>
<CollectionClient id=sentinel-3-slstr-frp-l2-netcdf>
<CollectionClient id=sentinel-3-slstr-lst-l2-netcdf>
<CollectionClient id=sentinel-3-slstr-wst-l2-netcdf>
<CollectionClient id=sentinel-3-sral-lan-l2-netcdf>
<CollectionClient id=sentinel-3-sral-wat-l2-netcdf>
<CollectionClient id=sentinel-3-synergy-aod-l2-netcdf>
<CollectionClient id=sentinel-3-synergy-syn-l2-netcdf>
<CollectionClient id=sentinel-3-synergy-v10-l2-netcdf>
<CollectionClient id=sentinel-3-synergy-vg1-l2-netcdf>
<CollectionClient id=sentinel-3-synergy-vgp-l2-netcdf>
<CollectionClient id=sentinel-5p-l2-netcdf>
<CollectionClient id=terraclimate>
<CollectionClient id=us-census>
<CollectionClient id=usda-cdl>
<CollectionClient id=usgs-lcmap-conus-v13>
<CollectionClient id=usgs-lcmap-hawaii-v10>
Collection Search#
Sometimes, it can be challenging to identify which collection you want to work with. The collection_search method allows you to discover collections by applying search filters that will help you find the specific collection(s) you need. Since many STAC APIs have not implemented the collection search extension, pystac-client will perform a limited client-side filter if the API does not conform to the collection search spec.
[4]:
collection_search = cat.collection_search(
q="ASTER",
)
/home/docs/checkouts/readthedocs.org/user_builds/pystac-client/checkouts/latest/pystac_client/collection_search.py:307: DoesNotConformTo: Server does not conform to COLLECTION_SEARCH, Filtering will be performed client-side where only bbox, datetime, and q arguments are supported
warnings.warn(
The collections method lets you iterate through the results of the search so you can inspect the details of matching collections.
[5]:
for result in collection_search.collections():
print(result.id, f"{collection.description}", sep="\n")
print("\n")
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1/collections?q=ASTER Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
aster-l1t
The [Land Change Monitoring, Assessment, and Projection](https://www.usgs.gov/special-topics/lcmap) (LCMAP) product provides land cover mapping and change monitoring from the U.S. Geological Survey's [Earth Resources Observation and Science](https://www.usgs.gov/centers/eros) (EROS) Center. LCMAP's Science Products are developed by applying time-series modeling on a per-pixel basis to [Landsat Analysis Ready Data](https://www.usgs.gov/landsat-missions/landsat-us-analysis-ready-data) (ARD) using an implementation of the [Continuous Change Detection and Classification](https://doi.org/10.1016/j.rse.2014.01.011) (CCDC) algorithm. All available clear (non-cloudy) U.S. Landsat ARD observations are fit to a harmonic model to predict future Landsat-like surface reflectance. Where Landsat surface reflectance observations differ significantly from those predictions, a change is identified. Attributes of the resulting model sequences (e.g., start/end dates, residuals, model coefficients) are then used to produce a set of land surface change products and as inputs to the subsequent classification to thematic land cover.
This [STAC](https://stacspec.org/en) Collection contains [LCMAP Hawaii Collection 1.0](https://www.usgs.gov/special-topics/lcmap/collection-1-hawaii-science-products), which was released in January 2022 for years 2000-2020. The data are tiled according to the Landsat ARD tile grid and consist of [Cloud Optimized GeoTIFFs](https://www.cogeo.org/) (COGs) and corresponding metadata files. Note that the provided COGs differ slightly from those in the USGS source data. They have been reprocessed to add overviews, "nodata" values where appropriate, and an updated projection definition.
modis-21A2-061
The [Land Change Monitoring, Assessment, and Projection](https://www.usgs.gov/special-topics/lcmap) (LCMAP) product provides land cover mapping and change monitoring from the U.S. Geological Survey's [Earth Resources Observation and Science](https://www.usgs.gov/centers/eros) (EROS) Center. LCMAP's Science Products are developed by applying time-series modeling on a per-pixel basis to [Landsat Analysis Ready Data](https://www.usgs.gov/landsat-missions/landsat-us-analysis-ready-data) (ARD) using an implementation of the [Continuous Change Detection and Classification](https://doi.org/10.1016/j.rse.2014.01.011) (CCDC) algorithm. All available clear (non-cloudy) U.S. Landsat ARD observations are fit to a harmonic model to predict future Landsat-like surface reflectance. Where Landsat surface reflectance observations differ significantly from those predictions, a change is identified. Attributes of the resulting model sequences (e.g., start/end dates, residuals, model coefficients) are then used to produce a set of land surface change products and as inputs to the subsequent classification to thematic land cover.
This [STAC](https://stacspec.org/en) Collection contains [LCMAP Hawaii Collection 1.0](https://www.usgs.gov/special-topics/lcmap/collection-1-hawaii-science-products), which was released in January 2022 for years 2000-2020. The data are tiled according to the Landsat ARD tile grid and consist of [Cloud Optimized GeoTIFFs](https://www.cogeo.org/) (COGs) and corresponding metadata files. Note that the provided COGs differ slightly from those in the USGS source data. They have been reprocessed to add overviews, "nodata" values where appropriate, and an updated projection definition.
[6]:
collection = cat.get_collection("aster-l1t")
collection
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1/collections/aster-l1t Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
[6]:
Items#
The main functions for getting items return iterators, where pystac-client will handle retrieval of additional pages when needed. Note that one request is made for the first ten items, then a second request for the next ten.
[7]:
items = collection.get_items()
# flush stdout so we can see the exact order that things happen
def get_ten_items(items):
for i, item in enumerate(items):
print(f"{i}: {item}", flush=True)
if i == 9:
return
print("First page", flush=True)
get_ten_items(items)
print("Second page", flush=True)
get_ten_items(items)
First page
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1/collections/aster-l1t/items?collections=aster-l1t Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
0: <Item id=AST_L1T_00312272006020322_20150518201805>
1: <Item id=AST_L1T_00312272006020313_20150518201753>
2: <Item id=AST_L1T_00312272006020304_20150518201753>
3: <Item id=AST_L1T_00312272006020256_20150518201805>
4: <Item id=AST_L1T_00312272006013731_20150518201743>
5: <Item id=AST_L1T_00312272006013722_20150518201738>
6: <Item id=AST_L1T_00312272006013714_20150518201738>
7: <Item id=AST_L1T_00312272006013705_20150517144401>
8: <Item id=AST_L1T_00312272006013656_20150517144401>
9: <Item id=AST_L1T_00312272006013647_20150517144406>
Second page
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1/collections/aster-l1t/items?collections=aster-l1t&token=next%3Aaster-l1t%3AAST_L1T_00312272006013647_20150517144406 Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
0: <Item id=AST_L1T_00312272006013638_20150517144356>
1: <Item id=AST_L1T_00312272006013629_20150517144344>
2: <Item id=AST_L1T_00312272006013620_20150517144344>
3: <Item id=AST_L1T_00312272006013612_20150517144400>
4: <Item id=AST_L1T_00312272006013603_20150517144346>
5: <Item id=AST_L1T_00312272006013554_20150517144346>
6: <Item id=AST_L1T_00312272006013545_20150517144346>
7: <Item id=AST_L1T_00312272006013536_20150517144340>
8: <Item id=AST_L1T_00312272006013527_20150517144346>
9: <Item id=AST_L1T_00312272006013519_20150517144336>
API Search#
If the Catalog is an API, we have the ability to search for items based on spatio-temporal properties.
[8]:
# AOI around Delfzijl, in northern Netherlands
geom = {
"type": "Polygon",
"coordinates": [
[
[6.42425537109375, 53.174765470134616],
[7.344360351562499, 53.174765470134616],
[7.344360351562499, 53.67393435835391],
[6.42425537109375, 53.67393435835391],
[6.42425537109375, 53.174765470134616],
]
],
}
# limit sets the # of items per page so we can see multiple pages getting fetched
search = cat.search(
max_items=15,
limit=5,
collections="aster-l1t",
intersects=geom,
datetime="2000-01-01/2010-12-31",
)
items = list(search.items())
len(items)
DEBUG:pystac_client.stac_api_io:POST https://planetarycomputer.microsoft.com/api/stac/v1/search Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '353', 'Content-Type': 'application/json'} Payload: {"limit": 5, "datetime": "2000-01-01T00:00:00Z/2010-12-31T23:59:59Z", "collections": ["aster-l1t"], "intersects": {"type": "Polygon", "coordinates": [[[6.42425537109375, 53.174765470134616], [7.344360351562499, 53.174765470134616], [7.344360351562499, 53.67393435835391], [6.42425537109375, 53.67393435835391], [6.42425537109375, 53.174765470134616]]]}}
DEBUG:pystac_client.stac_api_io:POST https://planetarycomputer.microsoft.com/api/stac/v1/search?collections=aster-l1t Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '421', 'Content-Type': 'application/json'} Payload: {"limit": 5, "datetime": "2000-01-01T00:00:00Z/2010-12-31T23:59:59Z", "collections": ["aster-l1t"], "intersects": {"type": "Polygon", "coordinates": [[[6.42425537109375, 53.174765470134616], [7.344360351562499, 53.174765470134616], [7.344360351562499, 53.67393435835391], [6.42425537109375, 53.67393435835391], [6.42425537109375, 53.174765470134616]]]}, "token": "next:aster-l1t:AST_L1T_00307032006103813_20150515031748"}
DEBUG:pystac_client.stac_api_io:POST https://planetarycomputer.microsoft.com/api/stac/v1/search?collections=aster-l1t Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '421', 'Content-Type': 'application/json'} Payload: {"limit": 5, "datetime": "2000-01-01T00:00:00Z/2010-12-31T23:59:59Z", "collections": ["aster-l1t"], "intersects": {"type": "Polygon", "coordinates": [[[6.42425537109375, 53.174765470134616], [7.344360351562499, 53.174765470134616], [7.344360351562499, 53.67393435835391], [6.42425537109375, 53.67393435835391], [6.42425537109375, 53.174765470134616]]]}, "token": "next:aster-l1t:AST_L1T_00305302006105006_20150514150004"}
[8]:
15
[9]:
# note that this will work in JupyterLab, but not in a Jupyter Notebook
import IPython.display
IPython.display.JSON([i.to_dict() for i in items])
[9]:
<IPython.core.display.JSON object>
[10]:
# this cell can be used in Jupyter Notebook. Use above if using JupyterLab
import json
import uuid
from IPython.display import display_html, display_javascript
class RenderJSON(object):
def __init__(self, json_data):
if isinstance(json_data, dict) or isinstance(json_data, list):
self.json_str = json.dumps(json_data)
else:
self.json_str = json_data
self.uuid = str(uuid.uuid4())
def _ipython_display_(self):
display_html(
'<div id="{}" style="height: 600px; width:100%;font: 12px/18px monospace '
'!important;"></div>'.format(self.uuid),
raw=True,
)
display_javascript(
"""
require(["https://rawgit.com/caldwell/renderjson/master/renderjson.js"],
function() {
renderjson.set_show_to_level(2);
document.getElementById('%s').appendChild(renderjson(%s))
});
"""
% (self.uuid, self.json_str),
raw=True,
)
RenderJSON([i.to_dict() for i in items])