The Sentinel-2 program provides global imagery in thirteen spectral bands at 10m-60m resolution and a revisit time of approximately five days. This dataset represents the global Sentinel-2 archive, from 2016 to the present, processed to L2A (bottom-of-atmosphere) using Sen2Cor and converted to cloud-optimized GeoTIFF format. The digital elevation model used for terrain correction was Planet DEM 30.
This notebook demonstrates the use of the Planetary Computer STAC API to query for Sentinel-2 tiles.
This notebook works with or without an API key, but you will be given more permissive access to the data with an API key. The Planetary Computer Hub is pre-configured to use your API key.
This notebook works with or without an API key, but you will be given more permissive access to the data with an API key. The Planetary Computer Hub is pre-configured to use your API key.
from pystac.extensions.eo import EOExtension as eo
import pystac_client
import planetary_computer
# Set the environment variable PC_SDK_SUBSCRIPTION_KEY, or set it here.
# The Hub sets PC_SDK_SUBSCRIPTION_KEY automatically.
# pc.settings.set_subscription_key(<YOUR API Key>)
The datasets hosted by the Planetary Computer are available from Azure Blob Storage. We'll use pystac-client to search the Planetary Computer's STAC API for the subset of the data that we care about, and then we'll load the data directly from Azure Blob Storage. We'll specify a modifier
so that we can access the data stored in the Planetary Computer's private Blob Storage Containers. See Reading from the STAC API and Using tokens for data access for more.
catalog = pystac_client.Client.open(
"https://planetarycomputer.microsoft.com/api/stac/v1",
modifier=planetary_computer.sign_inplace,
)
This area is near Jonah Bay, Alaska.
area_of_interest = {
"type": "Polygon",
"coordinates": [
[
[-148.56536865234375, 60.80072385643073],
[-147.44338989257812, 60.80072385643073],
[-147.44338989257812, 61.18363894915102],
[-148.56536865234375, 61.18363894915102],
[-148.56536865234375, 60.80072385643073],
]
],
}
Define the time range to filter images with. Here we use the summer of 2019.
time_of_interest = "2019-06-01/2019-08-01"
search = catalog.search(
collections=["sentinel-2-l2a"],
intersects=area_of_interest,
datetime=time_of_interest,
query={"eo:cloud_cover": {"lt": 10}},
)
# Check how many items were returned
items = search.item_collection()
print(f"Returned {len(items)} Items")
Returned 5 Items
We can now work directly with the PySTAC Items returned by the API. Here we find the least cloudy of the bunch.
least_cloudy_item = min(items, key=lambda item: eo.ext(item).cloud_cover)
print(
f"Choosing {least_cloudy_item.id} from {least_cloudy_item.datetime.date()}"
f" with {eo.ext(least_cloudy_item).cloud_cover}% cloud cover"
)
Choosing S2B_MSIL2A_20190629T212529_R043_T06VVN_20201006T080531 from 2019-06-29 with 0.314306% cloud cover
Get the URL to the Cloud Optimized GeoTIFF image corresponding to the true color composite image.
asset_href = least_cloudy_item.assets["visual"].href
We can now use the HREF to read our data in any tools that can retrieve data from URLs via HTTP GET operations.
For example, here we use rasterio to render the image data over our area of interest:
import rasterio
from rasterio import windows
from rasterio import features
from rasterio import warp
import numpy as np
from PIL import Image
with rasterio.open(asset_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(window=aoi_window)
rasterio gives us data band-interleave format; transpose to pixel-interleave, and downscale the image data for plotting.
img = Image.fromarray(np.transpose(band_data, axes=[1, 2, 0]))
w = img.size[0]
h = img.size[1]
aspect = w / h
target_w = 800
target_h = (int)(target_w / aspect)
img.resize((target_w, target_h), Image.Resampling.BILINEAR)