{ "cells": [ { "cell_type": "markdown", "id": "6caada0f", "metadata": {}, "source": [ "## Accessing ASTER L1T data with the Planetary Computer STAC API\n", "\n", "The [ASTER](https://terra.nasa.gov/about/terra-instruments/aster) instrument, launched on-board NASA's [Terra](https://terra.nasa.gov/) satellite in 1999, provides multispectral images of the Earth at 15m-90m resolution. ASTER images provide information about land surface temperature, color, elevation, and mineral composition.\n", "\n", "This dataset represents ASTER [L1T](https://lpdaac.usgs.gov/products/ast_l1tv003/) data from 2000-2006. L1T images have been terrain-corrected and rotated to a north-up UTM projection. Data are in [cloud-optimized GeoTIFF](https://www.cogeo.org/) format.\n", "\n", "This notebook demonstrates the use of the Planetary Computer STAC API to query for ASTER scenes." ] }, { "cell_type": "markdown", "id": "d7998b45", "metadata": {}, "source": [ "### Environment setup\n", "\n", "This notebook works with or without an API key, but you will be given more permissive access to the data with an API key.\n", "The [Planetary Computer Hub](https://planetarycomputer.microsoft.com/compute) is pre-configured to use your API key." ] }, { "cell_type": "code", "execution_count": 1, "id": "76771153", "metadata": { "scrolled": true }, "outputs": [], "source": [ "import pystac_client\n", "import planetary_computer\n", "from pystac.extensions.eo import EOExtension as eo\n", "\n", "# Set the environment variable PC_SDK_SUBSCRIPTION_KEY, or set it here.\n", "# The Hub sets PC_SDK_SUBSCRIPTION_KEY automatically.\n", "# pc.settings.set_subscription_key()" ] }, { "cell_type": "markdown", "id": "db50ecc4", "metadata": {}, "source": [ "### Open and explore the ASTER collection\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": 2, "id": "27e8f224", "metadata": { "scrolled": true }, "outputs": [], "source": [ "catalog = pystac_client.Client.open(\n", " \"https://planetarycomputer.microsoft.com/api/stac/v1\",\n", " modifier=planetary_computer.sign_inplace,\n", ")\n", "aster_l1t = catalog.get_child(id=\"aster-l1t\")" ] }, { "cell_type": "markdown", "id": "d5390bb4", "metadata": {}, "source": [ "Let's look at the temporal extent of the collection; the Planetary Computer ASTER L1T dataset contains images from 2000 to 2006:" ] }, { "cell_type": "code", "execution_count": 3, "id": "c6597f65-8bcd-47bf-bb40-28d41c0efc14", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "{'interval': [['2000-03-04T12:00:00Z', '2006-12-31T12:00:00Z']]}" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "aster_l1t.extent.temporal.to_dict()" ] }, { "cell_type": "markdown", "id": "6062637f", "metadata": {}, "source": [ "### Choose a region and time of interest\n", "\n", "For this example we'll search 2002 imagery over an area in Japan:" ] }, { "cell_type": "code", "execution_count": 4, "id": "f7609057", "metadata": { "scrolled": true }, "outputs": [], "source": [ "time_of_interest = \"2002-01-01/2002-12-31\"\n", "\n", "area_of_interest = {\n", " \"type\": \"Polygon\",\n", " \"coordinates\": [\n", " [\n", " [138.4222412109375, 34.90620544067929],\n", " [138.58428955078125, 34.90620544067929],\n", " [138.58428955078125, 35.07271701786369],\n", " [138.4222412109375, 35.07271701786369],\n", " [138.4222412109375, 34.90620544067929],\n", " ]\n", " ],\n", "}" ] }, { "cell_type": "markdown", "id": "51b62a89", "metadata": {}, "source": [ "### Search the collection and explore the results\n", "\n", "We'll use this criteria to perform a search against the STAC API:" ] }, { "cell_type": "code", "execution_count": 5, "id": "bdeda93d-510b-4460-b15b-e64643f1ea21", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Returned 3 Items\n" ] } ], "source": [ "search = catalog.search(\n", " collections=[\"aster-l1t\"],\n", " intersects=area_of_interest,\n", " datetime=time_of_interest,\n", " query={\"eo:cloud_cover\": {\"lt\": 10}},\n", ")\n", "\n", "# Check how many items were returned\n", "items = list(search.get_items())\n", "print(f\"Returned {len(items)} Items\")" ] }, { "cell_type": "markdown", "id": "2b33359e", "metadata": {}, "source": [ "Each of those Item objects represents one ASTER scene. Let's see what assets are available for each ASTER scene:" ] }, { "cell_type": "code", "execution_count": 6, "id": "9d26f794-04ab-4ed3-aed9-bee83e7d810d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['TIR', 'xml', 'SWIR', 'VNIR', 'qa-txt', 'qa-browse', 'tir-browse', 'vnir-browse']\n" ] } ], "source": [ "available_assets = list(aster_l1t.extra_fields[\"item_assets\"].keys())\n", "print(available_assets)" ] }, { "cell_type": "markdown", "id": "2bc7f287", "metadata": {}, "source": [ "### Render a thumbnail image\n", "\n", "ASTER data includes thumbnail images in the 'tir-browse' and 'vnir-browse' assets attached to each item. Let's render one of those thumbnails.\n", "\n", "Each Item has an `href` field containing a URL to the underlying image. For ASTER, these URLs are publicly-accessible, but for some data sets, these URLs may point to private containers, so we demonstrate the use of the [planetary-computer](https://github.com/microsoft/planetary-computer-sdk-for-python) package's `pc.sign` method, which adds a [Shared Access Signature](https://docs.microsoft.com/en-us/azure/storage/common/storage-sas-overview) to the URL, after which it can be used by any tooling that expects a standard URL." ] }, { "cell_type": "code", "execution_count": 7, "id": "41aafb64", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "id=AST_L1T_00310042002014048_20150425134514\tdate=2002-10-04\tcloud %=1.0\n" ] }, { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# from PIL import Image\n", "# from urllib.request import urlopen\n", "from IPython.display import Image\n", "\n", "# Grab the last item and create an instance of the Electro-Optical (eo)\n", "# extension to check cloud cover\n", "item = items[-1]\n", "item_eo = eo.ext(item)\n", "\n", "asset_href = item.assets[\"vnir-browse\"].href\n", "\n", "print(f\"id={item.id}\\tdate={item.datetime.date()}\\tcloud %={item_eo.cloud_cover}\")\n", "\n", "# Downsample a bit for plotting\n", "# image = Image.open(urlopen(asset_href))\n", "# image.resize(size=(image.width // 2, image.height // 2))\n", "Image(url=asset_href)" ] }, { "cell_type": "markdown", "id": "eb3914ae", "metadata": {}, "source": [ "### Render an RGB composite from a complete scene\n", "Of course, most of the time, you don't just want to browse thumbnails, you want to access images. Let's render a composite image from the VNIR bands in one of the items." ] }, { "cell_type": "code", "execution_count": 8, "id": "e6a6a916-406d-4aee-b67b-f40e71b92673", "metadata": {}, "outputs": [], "source": [ "item = min(items, key=lambda item: eo.ext(item).cloud_cover)\n", "swir_asset = item.assets[\"VNIR\"]" ] }, { "cell_type": "markdown", "id": "9c4886ad", "metadata": {}, "source": [ "Let's see what properties are available within each asset. We'll use the `eo` extension to print the bands available in this image:" ] }, { "cell_type": "code", "execution_count": 9, "id": "1e8e588e-8df2-4582-9840-accb6205f99e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "properties: ['href', 'type', 'title', 'eo:bands', 'proj:bbox', 'proj:shape', 'proj:transform', 'roles']\n", "bands: ['VNIR_Band1', 'VNIR_Band2', 'VNIR_Band3N']\n" ] } ], "source": [ "available_assets = list(swir_asset.to_dict().keys())\n", "print(\"properties:\", available_assets)\n", "\n", "available_bands = [band.name for band in eo.ext(swir_asset).bands]\n", "print(\"bands:\", available_bands)" ] }, { "cell_type": "markdown", "id": "6f8816fe", "metadata": {}, "source": [ "Finally, let's render our RGB composite." ] }, { "cell_type": "code", "execution_count": 10, "id": "19b998aa", "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import numpy as np\n", "import rasterio\n", "import matplotlib.pyplot as plt\n", "\n", "# Downsample the scene for plotting\n", "dsfactor = 10\n", "\n", "# Choose bands for our RGB composite\n", "bands = [3, 2, 1]\n", "\n", "# Normalization value for rendering\n", "norm_value = 100\n", "\n", "image_data = []\n", "\n", "with rasterio.open(swir_asset.href, \"r\") as src:\n", "\n", " h = int(src.height // dsfactor)\n", " w = int(src.width // dsfactor)\n", "\n", " for i, band in enumerate(bands):\n", " band_array = src.read(band, out_shape=(1, h, w))\n", " band_array = band_array / norm_value\n", " image_data.append(band_array)\n", " src.close()\n", "\n", "rgb = np.dstack((image_data[0], image_data[1], image_data[2]))\n", "np.clip(rgb, 0, 1, rgb)\n", "\n", "dpi = 100\n", "fig = plt.figure(frameon=False, figsize=(w / dpi, h / dpi), dpi=dpi)\n", "ax = plt.Axes(fig, [0.0, 0.0, 1.0, 1.0])\n", "ax.set_axis_off()\n", "fig.add_axes(ax)\n", "plt.imshow(rgb);" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.6" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 5 }