#!/usr/bin/env python # coding: utf-8 # ## Accessing Sentinel-2 L2A data with the Planetary Computer STAC API # # The [Sentinel-2](https://sentinel.esa.int/web/sentinel/missions/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](https://step.esa.int/main/snap-supported-plugins/sen2cor/) and converted to [cloud-optimized GeoTIFF](https://www.cogeo.org/) format. The digital elevation model used for terrain correction was [Planet DEM 30](https://planetobserver.com/global-elevation-data/). # # 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](https://planetarycomputer.microsoft.com/compute) is pre-configured to use your API key. # ### Environment setup # # 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. # In[1]: 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() # ### Data access # # The datasets hosted by the Planetary Computer are available from [Azure Blob Storage](https://docs.microsoft.com/en-us/azure/storage/blobs/). We'll use [pystac-client](https://pystac-client.readthedocs.io/) to search the Planetary Computer's [STAC API](https://planetarycomputer.microsoft.com/api/stac/v1/docs) 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](https://planetarycomputer.microsoft.com/docs/quickstarts/reading-stac/) and [Using tokens for data access](https://planetarycomputer.microsoft.com/docs/concepts/sas/) for more. # In[2]: catalog = pystac_client.Client.open( "https://planetarycomputer.microsoft.com/api/stac/v1", modifier=planetary_computer.sign_inplace, ) # ### Choose a region and time of interest # # This area is near Jonah Bay, Alaska. # In[3]: 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. # In[4]: time_of_interest = "2019-06-01/2019-08-01" # In[5]: 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") # We can now work directly with the [PySTAC](https://github.com/stac-utils/pystac) Items returned by the API. Here we find the least cloudy of the bunch. # In[6]: 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" ) # Get the URL to the [Cloud Optimized GeoTIFF](https://www.cogeo.org/) image corresponding to the true color composite image. # In[7]: 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: # ### Render our AOI from this image # In[8]: 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. # In[9]: 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)