{ "cells": [ { "cell_type": "markdown", "id": "d2583da9-3f1e-40e6-b81d-700f26e89daa", "metadata": { "tags": [] }, "source": [ "## Accessing DRCOG with the Planetary Computer STAC API\n", "\n", "The [Denver Regional Council of Governments (DRCOG) Land Use/Land Cover (LULC)](https://drcog.org/services-and-resources/data-maps-and-modeling/regional-land-use-land-cover-project) \n", "datasets are developed in partnership with the Babbit Center for Land and Water Policy and the Chesapeake Conservancy's Conservation Innovation Center (CIC). DRCOG LULC includes 2018 data at 3.28ft (1m) resolution covering 1,000 square miles and 2020 data at 1ft resolution covering 6,000 square miles of the Denver, Colorado region. The classification data is derived from the USDA's 1m National Agricultural Imagery Program (NAIP) aerial imagery and leaf-off aerial ortho-imagery captured as part of the Denver Regional Aerial Photography Project (6in resolution everywhere except the mountainous regions to the west, which are 1ft resolution).\n", "\n", "In this notebook, we'll demonstrate how to access and work with this data through the Planetary Computer. Documentation for this dataset is available at the [Planetary Computer Data Catalog](https://planetarycomputer.microsoft.com/dataset/drcog-lulc).\n", "\n", "### 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", "\n", "* The [Planetary Computer Hub](https://planetarycomputer.microsoft.com/compute) is pre-configured to use your API key.\n", "* To use your API key locally, set the environment variable `PC_SDK_SUBSCRIPTION_KEY` or use `planetary_computer.settings.set_subscription_key()`" ] }, { "cell_type": "code", "execution_count": 1, "id": "f3f030fa-8639-4b4e-b0a1-f3226375c28e", "metadata": {}, "outputs": [], "source": [ "import pystac_client\n", "from pystac.extensions.item_assets import ItemAssetsExtension\n", "import matplotlib.colors\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import requests\n", "import planetary_computer\n", "import rioxarray\n", "import rich.table" ] }, { "cell_type": "markdown", "id": "527d242e-32b4-49fb-a513-a74ee38495da", "metadata": { "tags": [] }, "source": [ "### Query for data of interest\n", "\n", "We'll query for 2018 DRCOG LULC data. " ] }, { "cell_type": "code", "execution_count": 2, "id": "7e037147-5996-4532-a02c-5ce3d15b9a68", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fetching 2018\n", "Returned 13 Items\n" ] } ], "source": [ "catalog = pystac_client.Client.open(\n", " \"https://planetarycomputer.microsoft.com/api/stac/v1\",\n", " modifier=planetary_computer.sign_inplace,\n", ")\n", "\n", "latitude = 39.55\n", "longitude = -105.78\n", "datetimes = [\"2018\"]\n", "\n", "buffer = 0.6\n", "bbox = [longitude - buffer, latitude - buffer, longitude + buffer, latitude + buffer]\n", "items = dict()\n", "\n", "for datetime in datetimes:\n", " print(f\"Fetching {datetime}\")\n", " search = catalog.search(collections=[\"drcog-lulc\"], bbox=bbox, datetime=datetime)\n", "\n", "items = list(search.get_items())\n", "print(f\"Returned {len(items)} Items\")" ] }, { "cell_type": "markdown", "id": "49363704-3dd9-4f50-a8d2-b4b4c61f8360", "metadata": {}, "source": [ "Each Item contains a single \"data\" asset with a URL to the location of the Asset data on [Azure Blob Storage](https://azure.microsoft.com/en-us/services/storage/blobs/). " ] }, { "cell_type": "code", "execution_count": 3, "id": "7bd73779-0545-4368-bf96-3d9cce489f97", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "https://landcoverdata.blob.core.windows.net/drcog-lulc/2018/DRCOG_2018_LULC_E3070000_N1710000.tif\n" ] } ], "source": [ "asset_href = items[0].assets[\"data\"].href\n", "print(asset_href.split(\"?\")[0])" ] }, { "cell_type": "markdown", "id": "f6ccf07a-33bd-4be7-85c1-4471a17ae176", "metadata": {}, "source": [ "### Available STAC assets and metadata\n", "\n", "Let's check the available assets and metadata for a DRCOG LULC item. " ] }, { "cell_type": "code", "execution_count": 4, "id": "a3f2152a-6925-4a01-b24f-485cca0b9f55", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
       "┃ Key               Title                           ┃\n",
       "┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
       "│ data             │ DRCOG LULC                      │\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", "│ data │ DRCOG LULC │\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 k, asset in items[0].assets.items():\n", " t.add_row(k, asset.title)\n", "t" ] }, { "cell_type": "code", "execution_count": 5, "id": "485ef558-d85e-41b4-898b-d799f205d45f", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
       "┃ Key             Value                                                                                          ┃\n",
       "┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
       "│ created        │ 2022-07-15T15:22:33.322765Z                                                                    │\n",
       "│ datetime       │ None                                                                                           │\n",
       "│ description    │ 2018 Denver Regional Council of Governments (DRCOG) Land Use Land Cover (LULC) at 3.28ft (1m)  │\n",
       "│                │ resolution                                                                                     │\n",
       "│ end_datetime   │ 2018-12-31T23:59:59Z                                                                           │\n",
       "│ mission        │ 2018 DRCOG LULC pilot study covering 1,000 square miles                                        │\n",
       "│ proj:epsg      │ 2232                                                                                           │\n",
       "│ proj:shape     │ [9144, 9144]                                                                                   │\n",
       "│ proj:transform │ [3.28083333333333, 0.0, 3069998.36555848, 0.0, -3.280833333333333, 1740001.0677313006]         │\n",
       "│ start_datetime │ 2018-01-01T00:00:00Z                                                                           │\n",
       "└────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────┘\n",
       "
\n" ], "text/plain": [ "┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", "┃\u001b[1m \u001b[0m\u001b[1mKey \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mValue \u001b[0m\u001b[1m \u001b[0m┃\n", "┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n", "│ created │ 2022-07-15T15:22:33.322765Z │\n", "│ datetime │ None │\n", "│ description │ 2018 Denver Regional Council of Governments (DRCOG) Land Use Land Cover (LULC) at 3.28ft (1m) │\n", "│ │ resolution │\n", "│ end_datetime │ 2018-12-31T23:59:59Z │\n", "│ mission │ 2018 DRCOG LULC pilot study covering 1,000 square miles │\n", "│ proj:epsg │ 2232 │\n", "│ proj:shape │ [9144, 9144] │\n", "│ proj:transform │ [3.28083333333333, 0.0, 3069998.36555848, 0.0, -3.280833333333333, 1740001.0677313006] │\n", "│ start_datetime │ 2018-01-01T00:00:00Z │\n", "└────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────┘\n" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t = rich.table.Table(\"Key\", \"Value\")\n", "for k, v in sorted(items[0].properties.items()):\n", " t.add_row(k, str(v))\n", "t" ] }, { "cell_type": "markdown", "id": "f76a3b43-7827-4e8d-9305-6af33794bbce", "metadata": {}, "source": [ "### Displaying the data\n", "\n", "This dataset includes a preferred colormap mapping raster values to class names. The values and names are available in the collection's `item_asset` field." ] }, { "cell_type": "code", "execution_count": 6, "id": "cd296765-a455-4a92-bd1f-4e29da0344ab", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓\n",
       "┃ Description                             Raster value ┃\n",
       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩\n",
       "│ Structures                             │ 1            │\n",
       "│ Impervious Surfaces                    │ 2            │\n",
       "│ Water                                  │ 3            │\n",
       "│ Prairie/Grassland/Natural Ground Cover │ 4            │\n",
       "│ Tree Canopy                            │ 5            │\n",
       "│ Turf/Irrigated Land                    │ 6            │\n",
       "│ Barren Land                            │ 7            │\n",
       "│ Cropland                               │ 8            │\n",
       "└────────────────────────────────────────┴──────────────┘\n",
       "
\n" ], "text/plain": [ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓\n", "┃\u001b[1m \u001b[0m\u001b[1mDescription \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mRaster value\u001b[0m\u001b[1m \u001b[0m┃\n", "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩\n", "│ Structures │ 1 │\n", "│ Impervious Surfaces │ 2 │\n", "│ Water │ 3 │\n", "│ Prairie/Grassland/Natural Ground Cover │ 4 │\n", "│ Tree Canopy │ 5 │\n", "│ Turf/Irrigated Land │ 6 │\n", "│ Barren Land │ 7 │\n", "│ Cropland │ 8 │\n", "└────────────────────────────────────────┴──────────────┘\n" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Classifications\n", "collection = catalog.get_collection(\"drcog-lulc\")\n", "ia = ItemAssetsExtension.ext(collection)\n", "x = ia.item_assets[\"data\"]\n", "\n", "class_names = {\n", " x[\"description\"]: x[\"value\"] for x in x.properties[\"classification:classes\"]\n", "}\n", "class_values = {v: k for k, v in class_names.items()}\n", "\n", "t = rich.table.Table(\"Description\", \"Raster value\")\n", "for k, v in class_names.items():\n", " t.add_row(k, str(v))\n", "t" ] }, { "cell_type": "markdown", "id": "c42775d1-3587-4864-92c6-6777d5c53b33", "metadata": {}, "source": [ "The Planetary Computer's Data API includes the colormap definition." ] }, { "cell_type": "code", "execution_count": 7, "id": "96865729-e2b5-4de5-a388-ffe9fcfedae3", "metadata": {}, "outputs": [], "source": [ "classmap = requests.get(\n", " f\"https://planetarycomputer.microsoft.com/api/data/v1/legend/classmap/{collection.id}\" # noqa: E501\n", ").json()\n", "\n", "colors = [matplotlib.colors.to_rgba([x / 255 for x in c]) for c in classmap.values()]\n", "cmap = matplotlib.colors.ListedColormap(colors, name=collection.id)\n", "\n", "ticks = np.linspace(1.5, 7.5, 8)\n", "labels = [class_values.get(int(k), \"nodata\") for k in classmap]" ] }, { "cell_type": "markdown", "id": "c009413b-5437-45b7-b96c-b506d201bb46", "metadata": {}, "source": [ "Now we can read and plot the data." ] }, { "cell_type": "code", "execution_count": 8, "id": "35300ed9-e05f-44d5-bf50-c67afea2fa31", "metadata": {}, "outputs": [], "source": [ "item = items[0]\n", "ds = rioxarray.open_rasterio(item.assets[\"data\"].href).squeeze()" ] }, { "cell_type": "code", "execution_count": 9, "id": "1011d3b6-6a61-46e4-8e14-888d9f59c42d", "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots(figsize=(16, 12))\n", "\n", "ds.isel(x=slice(3000, 6000), y=slice(3000, 6000)).plot(ax=ax, cmap=cmap, vmin=1, vmax=8)\n", "ax.set_axis_off()\n", "ax.set(title=None)\n", "\n", "colorbar = fig.axes[1]\n", "colorbar.set_yticks(ticks, labels=labels);" ] } ], "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 }