Reading Data from the STAC API

from pystac_client import Client
import json
import planetary_computer as pc
catalog = Client.open("https://planetarycomputer.microsoft.com/api/stac/v1")
collections = catalog.get_children()
for collection in collections:
    print(f"{collection.id} - {collection.title}")
gap - USGS Gap Land Cover
nasadem - NASADEM HGT v001
daymet-annual-na - Daymet Annual North America
hrea - HREA: High Resolution Electricity Access
daymet-monthly-hi - Daymet Monthly Hawaii
daymet-daily-na - Daymet Daily North America
daymet-monthly-na - Daymet Monthly North America
daymet-annual-pr - Daymet Annual Puerto Rico
daymet-daily-pr - Daymet Daily Puerto Rico
daymet-daily-hi - Daymet Daily Hawaii
daymet-monthly-pr - Daymet Monthly Puerto Rico
aster-l1t - ASTER L1T
daymet-annual-hi - Daymet Annual Hawaii
io-lulc - Esri 10-Meter Land Cover
jrc-gsw - JRC Global Surface Water
landsat-8-c2-l2 - Landsat 8 Collection 2 Level-2
mobi - MoBI: Map of Biodiversity Importance
mtbs - MTBS: Monitoring Trends in Burn Severity
terraclimate - TerraClimate
sentinel-2-l2a - Sentinel-2 Level-2A
us-census - US Census
cop-dem-glo-30 - Copernicus DEM GLO-30
cop-dem-glo-90 - Copernicus DEM GLO-90
fia - Forest Inventory and Analysis
gbif - Global Biodiversity Information Facility (GBIF)
goes-cmi - GOES-R Cloud & Moisture Imagery
3dep-seamless - USGS 3DEP Seamless DEMs
alos-dem - ALOS World 3D-30m
gridmet - gridMET
hgb - HGB: Harmonized Global Biomass for 2010
naip - NAIP: National Agriculture Imagery Program
landsat = catalog.get_child("landsat-8-c2-l2")
for band in landsat.extra_fields["summaries"]["eo:bands"]:
    name = band["name"]
    description = band["description"]
    common_name = "" if "common_name" not in band else f"({band['common_name']})"
    ground_sample_distance = band["gsd"]
    print(f"{name} {common_name}: {description} ({ground_sample_distance}m resolution)")
SR_B1 (coastal): coastal (30m resolution)
SR_B2 (blue): visible blue (30m resolution)
SR_B3 (green): visible green (30m resolution)
SR_B4 (red): visible red (30m resolution)
SR_B5 (nir): near-infrared (30m resolution)
SR_B6 (swir16): short-wave infrared (30m resolution)
SR_B7 (swir22): short-wave infrared (30m resolution)
ST_B10 (lwir11): long-wave infrared (100m resolution)
ST_TRAD : thermal radiance (30m resolution)
ST_URAD : upwelled radiance (30m resolution)
ST_ATRAN : atmospheric transmission (30m resolution)
ST_CDIST : distance to nearest cloud (30m resolution)
ST_DRAD : downwelled radiance (30m resolution)
ST_EMIS : emissivity (30m resolution)
ST_EMSD : emissivity standard deviation (30m resolution)
area_of_interest = {
    "type": "Polygon",
    "coordinates": [
        [
            [-122.27508544921875, 47.54687159892238],
            [-121.96128845214844, 47.54687159892238],
            [-121.96128845214844, 47.745787772920934],
            [-122.27508544921875, 47.745787772920934],
            [-122.27508544921875, 47.54687159892238],
        ]
    ],
}

time_range = "2020-12-01/2020-12-31"

search = catalog.search(
    collections=["landsat-8-c2-l2"], intersects=area_of_interest, datetime=time_range
)
items = list(search.get_items())
for item in items:
    print(f"{item.id}: {item.datetime}")
LC08_L2SP_046027_20201229_02_T2: 2020-12-29 18:55:56.738265+00:00
LC08_L2SP_047027_20201220_02_T2: 2020-12-20 19:02:09.878796+00:00
LC08_L2SP_046027_20201213_02_T2: 2020-12-13 18:56:00.096447+00:00
LC08_L2SP_047027_20201204_02_T1: 2020-12-04 19:02:11.194486+00:00
selected_item = sorted(items, key=lambda item: item.properties["eo:cloud_cover"])[0]
for asset_key, asset in selected_item.assets.items():
    print(f"{asset_key:<25} - {asset.title}")
ANG                       - Angle Coefficients File
SR_B1                     - Coastal/Aerosol Band (B1)
SR_B2                     - Blue Band (B2)
SR_B3                     - Green Band (B3)
SR_B4                     - Red Band (B4)
SR_B5                     - Near Infrared Band 0.8 (B5)
SR_B6                     - Short-wave Infrared Band 1.6 (B6)
SR_B7                     - Short-wave Infrared Band 2.2 (B7)
ST_QA                     - Surface Temperature Quality Assessment Band
ST_B10                    - Surface Temperature Band (B10)
MTL.txt                   - Product Metadata File
MTL.xml                   - Product Metadata File (xml)
ST_DRAD                   - Downwelled Radiance Band
ST_EMIS                   - Emissivity Band
ST_EMSD                   - Emissivity Standard Deviation Band
ST_TRAD                   - Thermal Radiance Band
ST_URAD                   - Upwelled Radiance Band
MTL.json                  - Product Metadata File (json)
QA_PIXEL                  - Pixel Quality Assessment Band
ST_ATRAN                  - Atmospheric Transmittance Band
ST_CDIST                  - Cloud Distance Band
QA_RADSAT                 - Radiometric Saturation Quality Assessment Band
thumbnail                 - Thumbnail image
SR_QA_AEROSOL             - Aerosol Quality Analysis Band
reduced_resolution_browse - Reduced resolution browse image
tilejson                  - TileJSON with default rendering
rendered_preview          - Rendered preview
thumbnail_asset = selected_item.assets["thumbnail"]
print(json.dumps(thumbnail_asset.to_dict(), indent=2))
{
  "href": "https://landsateuwest.blob.core.windows.net/landsat-c2/level-2/standard/oli-tirs/2020/047/027/LC08_L2SP_047027_20201204_20210313_02_T1/LC08_L2SP_047027_20201204_20210313_02_T1_thumb_small.jpeg",
  "type": "image/jpeg",
  "title": "Thumbnail image"
}
import planetary_computer as pc
signed_href = pc.sign(thumbnail_asset.href)
from PIL import Image
from urllib.request import urlopen

Image.open(urlopen(signed_href))
area_of_interest = {
    "type": "Polygon",
    "coordinates": [
        [
            [-111.9839859008789, 40.5389819819361],
            [-111.90502166748045, 40.5389819819361],
            [-111.90502166748045, 40.57015381856105],
            [-111.9839859008789, 40.57015381856105],
            [-111.9839859008789, 40.5389819819361],
        ]
    ],
}
range_old = "2010-01-01/2013-01-01"
range_new = "2018-01-01/2020-01-01"
catalog = Client.open("https://planetarycomputer.microsoft.com/api/stac/v1")

search_old = catalog.search(
    collections=["naip"], intersects=area_of_interest, datetime=range_old
)

search_new = catalog.search(
    collections=["naip"], intersects=area_of_interest, datetime=range_new
)

items_old = list(search_old.get_items())
items_new = list(search_new.get_items())

print(f"{len(items_old)} Items found in the 'old' range")
print(f"{len(items_new)} Items found in the 'new' range")
4 Items found in the 'old' range
4 Items found in the 'new' range
from shapely.geometry import shape

area_shape = shape(area_of_interest)
target_area = area_shape.area


def area_of_overlap(item):
    overlap_area = shape(item.geometry).intersection(shape(area_of_interest)).area
    return overlap_area / target_area


item_old = sorted(items_old, key=area_of_overlap, reverse=True)[0]
item_new = sorted(items_new, key=area_of_overlap, reverse=True)[0]
import rasterio
from rasterio import windows
from rasterio import features
from rasterio import warp

import numpy as np
from PIL import Image


def create_image(item):
    print(item.datetime)
    href = pc.sign(item.assets["image"].href)
    with rasterio.open(href) as ds:
        aoi_bounds = features.bounds(area_of_interest)
        warped_aoi_bounds = warp.transform_bounds("epsg:4326", ds.crs, *aoi_bounds)
        aoi_window = windows.from_bounds(transform=ds.transform, *warped_aoi_bounds)
        band_data = ds.read(indexes=[1, 2, 3], window=aoi_window)

    img = Image.fromarray(np.transpose(band_data, axes=[1, 2, 0]))
    w = img.size[0]
    h = img.size[1]
    aspect = w / h

    # Downscale a bit for plotting
    target_w = 800
    target_h = (int)(target_w / aspect)

    return img.resize((target_w, target_h), Image.BILINEAR)
create_image(item_old)
2011-07-20 00:00:00+00:00
create_image(item_new)
2018-08-28 00:00:00+00:00