{ "cells": [ { "cell_type": "markdown", "id": "d2583da9-3f1e-40e6-b81d-700f26e89daa", "metadata": {}, "source": [ "## Accessing MODIS temperature data with the Planetary Computer STAC API\n", "\n", "The planetary computer hosts three temperature-related MODIS 6.1 products:\n", "\n", "- Land Surface Temperature/Emissivity Daily (11A1)\n", "- Land Surface Temperature/Emissivity 8-Day (11A2)\n", "- Land Surface Temperature/3-Band Emissivity 8-Day (21A2)\n", "\n", "For more information about the products themselves, check out the User Guides at the [bottom of this document](#user-guides)." ] }, { "cell_type": "markdown", "id": "6c7da081-b792-45f2-a428-ea8f9ecbda08", "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 is pre-configured to use your API key." ] }, { "cell_type": "code", "execution_count": 1, "id": "f3f030fa-8639-4b4e-b0a1-f3226375c28e", "metadata": { "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "import odc.stac\n", "import planetary_computer\n", "import pystac_client\n", "import rich.table" ] }, { "cell_type": "markdown", "id": "b316997a-789d-440a-bb48-646ebab08bab", "metadata": {}, "source": [ "### Data access\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": "a956b2c3-4a9b-434d-9f9e-76621a7d920e", "metadata": {}, "outputs": [], "source": [ "catalog = pystac_client.Client.open(\n", " \"https://planetarycomputer.microsoft.com/api/stac/v1\",\n", " modifier=planetary_computer.sign_inplace,\n", ")" ] }, { "cell_type": "markdown", "id": "527d242e-32b4-49fb-a513-a74ee38495da", "metadata": { "tags": [] }, "source": [ "### Query for available data\n", "\n", "MODIS is a global dataset with a variety of products available within each larger category (vegetation, snow, fire, temperature, and reflectance). The [MODIS group](https://planetarycomputer.microsoft.com/dataset/group/modis) contains a complete listing of available collections. Each collection's format follows`modis-{product}-061`, where `product` is the MODIS product id. The `-061` suffix indicates that all of the MODIS collections are part of the [MODIS 6.1 update](https://atmosphere-imager.gsfc.nasa.gov/documentation/collection-61).\n", "\n", "\n", "Let's access Land Surface Temperature/Emissivity Daily (11A1) data over Boise, Idaho in 2021. We'll get four images for the midseasonal months: March, June, September, and December. " ] }, { "cell_type": "code", "execution_count": 3, "id": "63ddd01e-2ae3-4b5d-af8f-cdcbb56f0423", "metadata": { "vscode": { "languageId": "python" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'March': , 'June': , 'September': , 'December': }\n" ] } ], "source": [ "# Boise, Idaho\n", "latitude = 43.6\n", "longitude = -116.2\n", "buffer = 1\n", "bbox = [longitude - buffer, latitude - buffer, longitude + buffer, latitude + buffer]\n", "year = \"2021\"\n", "months = {\n", " \"March\": \"03\",\n", " \"June\": \"06\",\n", " \"September\": \"09\",\n", " \"December\": \"12\",\n", "}\n", "items = dict()\n", "\n", "# Fetch the collection of interest and print available items\n", "for name, number in months.items():\n", " datetime = f\"{year}-{number}\"\n", " search = catalog.search(\n", " collections=[\"modis-11A1-061\"],\n", " bbox=bbox,\n", " datetime=datetime,\n", " )\n", " items[name] = search.get_all_items()[0]\n", "\n", "print(items)" ] }, { "cell_type": "markdown", "id": "f94d0d75-a7cb-42c0-b7a0-bb686c195298", "metadata": {}, "source": [ "### Available assets\n", "\n", "Each item has several available assets, including the original HDF file and a Cloud-optimized GeoTIFF of each subdataset." ] }, { "cell_type": "code", "execution_count": 4, "id": "6ae6f2b1-33c3-4857-a2e6-d54067ab91c6", "metadata": { "vscode": { "languageId": "python" } }, "outputs": [ { "data": { "text/html": [ "
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
       "┃ Key               Title                                                               ┃\n",
       "┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
       "│ hdf              │ Source data containing all bands                                    │\n",
       "│ QC_Day           │ Quality control for daytime LST and emissivity                      │\n",
       "│ Emis_31          │ Band 31 emissivity                                                  │\n",
       "│ Emis_32          │ Band 32 emissivity                                                  │\n",
       "│ QC_Night         │ Quality control for nighttime LST and emissivity                    │\n",
       "│ metadata         │ Federal Geographic Data Committee (FGDC) Metadata                   │\n",
       "│ LST_Day_1km      │ Daily daytime 1km grid Land-surface Temperature                     │\n",
       "│ Clear_day_cov    │ Day clear-sky coverage                                              │\n",
       "│ Day_view_angl    │ View zenith angle of daytime Landsurface Temperature                │\n",
       "│ Day_view_time    │ (local solar) Time of daytime Land-surface Temperature observation  │\n",
       "│ LST_Night_1km    │ Daily nighttime 1km grid Land-surface Temperature                   │\n",
       "│ Clear_night_cov  │ Night clear-sky coverage                                            │\n",
       "│ Night_view_angl  │ View zenith angle of nighttime Landsurface Temperature              │\n",
       "│ Night_view_time  │ (local solar) Time of nighttime Landsurface Temperature observation │\n",
       "│ tilejson         │ TileJSON with default rendering                                     │\n",
       "│ rendered_preview │ Rendered preview                                                    │\n",
       "└──────────────────┴─────────────────────────────────────────────────────────────────────┘\n",
       "
\n" ], "text/plain": [ "┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", "┃\u001b[1m \u001b[0m\u001b[1mKey \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mTitle \u001b[0m\u001b[1m \u001b[0m┃\n", "┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n", "│ hdf │ Source data containing all bands │\n", "│ QC_Day │ Quality control for daytime LST and emissivity │\n", "│ Emis_31 │ Band 31 emissivity │\n", "│ Emis_32 │ Band 32 emissivity │\n", "│ QC_Night │ Quality control for nighttime LST and emissivity │\n", "│ metadata │ Federal Geographic Data Committee (FGDC) Metadata │\n", "│ LST_Day_1km │ Daily daytime 1km grid Land-surface Temperature │\n", "│ Clear_day_cov │ Day clear-sky coverage │\n", "│ Day_view_angl │ View zenith angle of daytime Landsurface Temperature │\n", "│ Day_view_time │ (local solar) Time of daytime Land-surface Temperature observation │\n", "│ LST_Night_1km │ Daily nighttime 1km grid Land-surface Temperature │\n", "│ Clear_night_cov │ Night clear-sky coverage │\n", "│ Night_view_angl │ View zenith angle of nighttime Landsurface Temperature │\n", "│ Night_view_time │ (local solar) Time of nighttime Landsurface Temperature observation │\n", "│ tilejson │ TileJSON with default rendering │\n", "│ rendered_preview │ Rendered preview │\n", "└──────────────────┴─────────────────────────────────────────────────────────────────────┘\n" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t = rich.table.Table(\"Key\", \"Title\")\n", "for key, asset in items[\"March\"].assets.items():\n", " t.add_row(key, asset.title)\n", "t" ] }, { "cell_type": "markdown", "id": "eb2d2026-8988-4a62-8d01-05b7e5d3003c", "metadata": {}, "source": [ "### Loading the data" ] }, { "cell_type": "markdown", "id": "fcd49bc9-32ac-4b10-83f8-e9fbd2a870ac", "metadata": {}, "source": [ "For this example, we'll visualize the temperature data over Boise, Idaho. Let's grab each fire mask cover COG and load them into an xarray using [odc-stac](https://github.com/opendatacube/odc-stac). The MODIS coordinate reference system is a [sinusoidal grid](https://modis-land.gsfc.nasa.gov/MODLAND_grid.html), which means that views in a naïve XY raster look skewed. For visualization purposes, we reproject to a [spherical Mercator projection](https://wiki.openstreetmap.org/wiki/EPSG:3857) for intuitive, north-up visualization." ] }, { "cell_type": "code", "execution_count": 5, "id": "a8fc2966-2787-4f20-bb2d-e47d4829cec5", "metadata": { "vscode": { "languageId": "python" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
<xarray.DataArray 'LST_Day_1km' (time: 4, y: 616, x: 446)>\n",
       "array([[[277.98, 280.14, 280.14, ..., 276.98, 278.24, 278.24],\n",
       "        [277.98, 277.98, 280.14, ..., 276.98, 276.98, 278.24],\n",
       "        [277.78, 277.98, 277.98, ..., 274.68, 276.98, 276.98],\n",
       "        ...,\n",
       "        [301.66, 301.66, 300.78, ..., 302.42, 302.42, 303.1 ],\n",
       "        [300.3 , 301.66, 301.66, ..., 303.86, 302.42, 302.42],\n",
       "        [300.26, 300.26, 300.66, ..., 302.84, 302.84, 302.84]],\n",
       "\n",
       "       [[310.84, 311.98, 311.98, ...,   0.  ,   0.  ,   0.  ],\n",
       "        [310.84, 310.84, 311.98, ...,   0.  ,   0.  ,   0.  ],\n",
       "        [309.56, 310.84, 310.84, ..., 300.98,   0.  ,   0.  ],\n",
       "        ...,\n",
       "        [322.68, 322.68, 323.64, ..., 328.64, 328.64, 328.64],\n",
       "        [323.3 , 322.68, 322.68, ..., 328.52, 328.64, 328.64],\n",
       "        [320.56, 320.56, 322.6 , ..., 327.88, 327.88, 327.88]],\n",
       "\n",
       "       [[294.5 , 295.36, 295.36, ..., 296.5 , 299.5 , 299.5 ],\n",
       "        [294.5 , 294.5 , 295.36, ..., 296.5 , 296.5 , 299.5 ],\n",
       "        [293.1 , 294.5 , 294.5 , ..., 293.08, 296.5 , 296.5 ],\n",
       "        ...,\n",
       "        [303.96, 303.96, 304.62, ..., 307.92, 307.92, 308.26],\n",
       "        [302.38, 303.96, 303.96, ..., 308.94, 307.92, 307.92],\n",
       "        [303.16, 303.16, 305.38, ..., 308.78, 308.78, 308.78]],\n",
       "\n",
       "       [[260.72, 261.08, 261.08, ...,   0.  ,   0.  ,   0.  ],\n",
       "        [260.72, 260.72, 261.08, ...,   0.  ,   0.  ,   0.  ],\n",
       "        [260.18, 260.72, 260.72, ...,   0.  ,   0.  ,   0.  ],\n",
       "        ...,\n",
       "        [  0.  ,   0.  ,   0.  , ..., 264.72, 264.72, 264.86],\n",
       "        [  0.  ,   0.  ,   0.  , ..., 264.7 , 264.72, 264.72],\n",
       "        [  0.  ,   0.  ,   0.  , ..., 265.74, 265.74, 265.74]]])\n",
       "Coordinates:\n",
       "  * y            (y) float64 5.559e+06 5.558e+06 ... 5.252e+06 5.251e+06\n",
       "  * x            (x) float64 -1.305e+07 -1.305e+07 ... -1.282e+07 -1.282e+07\n",
       "    spatial_ref  int32 3857\n",
       "  * time         (time) datetime64[ns] 2021-03-31 2021-06-30 ... 2021-12-31
" ], "text/plain": [ "\n", "array([[[277.98, 280.14, 280.14, ..., 276.98, 278.24, 278.24],\n", " [277.98, 277.98, 280.14, ..., 276.98, 276.98, 278.24],\n", " [277.78, 277.98, 277.98, ..., 274.68, 276.98, 276.98],\n", " ...,\n", " [301.66, 301.66, 300.78, ..., 302.42, 302.42, 303.1 ],\n", " [300.3 , 301.66, 301.66, ..., 303.86, 302.42, 302.42],\n", " [300.26, 300.26, 300.66, ..., 302.84, 302.84, 302.84]],\n", "\n", " [[310.84, 311.98, 311.98, ..., 0. , 0. , 0. ],\n", " [310.84, 310.84, 311.98, ..., 0. , 0. , 0. ],\n", " [309.56, 310.84, 310.84, ..., 300.98, 0. , 0. ],\n", " ...,\n", " [322.68, 322.68, 323.64, ..., 328.64, 328.64, 328.64],\n", " [323.3 , 322.68, 322.68, ..., 328.52, 328.64, 328.64],\n", " [320.56, 320.56, 322.6 , ..., 327.88, 327.88, 327.88]],\n", "\n", " [[294.5 , 295.36, 295.36, ..., 296.5 , 299.5 , 299.5 ],\n", " [294.5 , 294.5 , 295.36, ..., 296.5 , 296.5 , 299.5 ],\n", " [293.1 , 294.5 , 294.5 , ..., 293.08, 296.5 , 296.5 ],\n", " ...,\n", " [303.96, 303.96, 304.62, ..., 307.92, 307.92, 308.26],\n", " [302.38, 303.96, 303.96, ..., 308.94, 307.92, 307.92],\n", " [303.16, 303.16, 305.38, ..., 308.78, 308.78, 308.78]],\n", "\n", " [[260.72, 261.08, 261.08, ..., 0. , 0. , 0. ],\n", " [260.72, 260.72, 261.08, ..., 0. , 0. , 0. ],\n", " [260.18, 260.72, 260.72, ..., 0. , 0. , 0. ],\n", " ...,\n", " [ 0. , 0. , 0. , ..., 264.72, 264.72, 264.86],\n", " [ 0. , 0. , 0. , ..., 264.7 , 264.72, 264.72],\n", " [ 0. , 0. , 0. , ..., 265.74, 265.74, 265.74]]])\n", "Coordinates:\n", " * y (y) float64 5.559e+06 5.558e+06 ... 5.252e+06 5.251e+06\n", " * x (x) float64 -1.305e+07 -1.305e+07 ... -1.282e+07 -1.282e+07\n", " spatial_ref int32 3857\n", " * time (time) datetime64[ns] 2021-03-31 2021-06-30 ... 2021-12-31" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data = odc.stac.load(\n", " items.values(),\n", " crs=\"EPSG:3857\",\n", " bands=\"LST_Day_1km\",\n", " resolution=500,\n", " bbox=bbox,\n", ")\n", "\n", "raster = items[\"March\"].assets[\"LST_Day_1km\"].extra_fields[\"raster:bands\"]\n", "data = data[\"LST_Day_1km\"] * raster[0][\"scale\"]\n", "data" ] }, { "cell_type": "markdown", "id": "f76a3b43-7827-4e8d-9305-6af33794bbce", "metadata": {}, "source": [ "### Displaying the data\n", "\n", "Let's display the temperature for each month. " ] }, { "cell_type": "code", "execution_count": 6, "id": "1c2f3428-31a7-4ba0-90fa-5844e8297474", "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "g = data.plot.imshow(cmap=\"magma\", col=\"time\", vmin=250, vmax=325, size=4)\n", "datetimes = data.time.to_pandas().dt.strftime(\"%B\")\n", "\n", "for ax, datetime in zip(g.axes.flat, datetimes):\n", " ax.set_title(datetime)" ] }, { "cell_type": "markdown", "id": "bc98fe25-807c-495b-a6e5-d19b5d61de8b", "metadata": {}, "source": [ "### Change detection\n", "\n", "Now let's see how the temperature changes between seasons." ] }, { "cell_type": "code", "execution_count": 7, "id": "451a3711-69f7-484d-bafe-f77bdc3b8ef6", "metadata": { "vscode": { "languageId": "python" } }, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "change = data.diff(dim=\"time\").assign_coords(time=[\"Winter\", \"Spring\", \"Summer\"])\n", "change.plot.imshow(cmap=\"coolwarm_r\", col=\"time\");" ] }, { "cell_type": "markdown", "id": "f10b7df2-0064-43d7-b8d6-1862caaa6b36", "metadata": {}, "source": [ "### User guides\n", "\n", "- MOD11: https://lpdaac.usgs.gov/documents/715/MOD11_User_Guide_V61.pdf\n", "- MOD21: https://lpdaac.usgs.gov/documents/1398/MOD21_User_Guide_V61.pdf\n" ] } ], "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 }