{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Accessing Chloris Biomass data with the Planetary Computer STAC API\n", "\n", "The Chloris Global Biomass 2003–2019 dataset provides estimates of stock and change in aboveground biomass for Earth's terrestrial woody vegetation ecosystems. It covers the period 2003–2019, at annual time steps. The global dataset has an approximate 4.6 km spatial resolution.\n", "\n", "This notebook provides an example of accessing Chloris Biomass data using the Planetary Computer STAC API, inspecting the data assets in the catalog, and doing some simple processing and plotting of the data from the Cloud Optimized GeoTIFF source." ] }, { "cell_type": "markdown", "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. The Planetary Computer Hub is pre-configured to use your API key." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import planetary_computer\n", "import rioxarray\n", "import pystac_client" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Query for available data" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Returned 17 Items\n" ] } ], "source": [ "catalog = pystac_client.Client.open(\n", " \"https://planetarycomputer.microsoft.com/api/stac/v1\",\n", " modifier=planetary_computer.sign_inplace,\n", ")\n", "biomass = catalog.search(collections=[\"chloris-biomass\"])\n", "\n", "all_items = biomass.get_all_items()\n", "\n", "print(f\"Returned {len(all_items)} Items\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our search returned all 17 items in the collection. Each of the items represent a single year, between 2003 and 2019, at a global scale.\n", "\n", "Let's see what assets are associated with this item:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "chloris_biomass_50km_2019:\n", "- biomass: Annual estimates of aboveground woody biomass.\n", "- biomass_wm: Annual estimates of aboveground woody biomass (Web Mercator).\n", "- biomass_change: Annual estimates of changes (gains and losses) in aboveground woody biomass from the previous year.\n", "- biomass_change_wm: Annual estimates of changes (gains and losses) in aboveground woody biomass from the previous year (Web Mercator).\n", "- tilejson: TileJSON with default rendering\n", "- rendered_preview: Rendered preview\n" ] } ], "source": [ "# Grab the first item and print the titles of the assets it contains\n", "item = all_items[0]\n", "print(item.id + \":\")\n", "print(*[f\"- {key}: {asset.title}\" for key, asset in item.assets.items()], sep=\"\\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are 4 assets, though two are duplicates of the same data, provided in Web Mercator projection. One represents the estimate for aboveground woody biomass for the given year (in tonnes), and the other represents the change (in tonnes) from the previous year.\n", "\n", "Let's update our search to include just a specific year." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "datetime = \"2016-01-01\"\n", "biomass = catalog.search(collections=[\"chloris-biomass\"], datetime=datetime)\n", "\n", "# Sign the resulting item so we can access the underlying data assets\n", "item = next(biomass.items())\n", "print(item)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Load the variable of interest\n", "\n", "By inspecting the `raster:bands` array in each asset's extra fields, we can see that this dataset uses a value of 2,147,483,647 for \"nodata\".\n", "So we'll provide the `masked=True` option to `rioxarray` to open the data with the nodata converted to NaNs." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
<xarray.Dataset>\n",
       "Dimensions:      (y: 3600, x: 8640)\n",
       "Coordinates:\n",
       "  * x            (x) float64 -2.001e+07 -2.001e+07 ... 2.001e+07 2.001e+07\n",
       "  * y            (y) float64 1.001e+07 1e+07 9.996e+06 ... -6.665e+06 -6.669e+06\n",
       "    spatial_ref  int64 0\n",
       "Data variables:\n",
       "    biomass      (y, x) float64 ...\n",
       "Attributes:\n",
       "    scale_factor:  1.0\n",
       "    add_offset:    0.0
" ], "text/plain": [ "\n", "Dimensions: (y: 3600, x: 8640)\n", "Coordinates:\n", " * x (x) float64 -2.001e+07 -2.001e+07 ... 2.001e+07 2.001e+07\n", " * y (y) float64 1.001e+07 1e+07 9.996e+06 ... -6.665e+06 -6.669e+06\n", " spatial_ref int64 0\n", "Data variables:\n", " biomass (y, x) float64 ...\n", "Attributes:\n", " scale_factor: 1.0\n", " add_offset: 0.0" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "da = rioxarray.open_rasterio(item.assets[\"biomass\"].href, masked=True)\n", "\n", "# Transform our data array to a dataset by selecting the only data variable ('band')\n", "# renaming it to something useful ('biomass')\n", "ds = da.to_dataset(dim=\"band\").rename({1: \"biomass\"})\n", "ds" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Downsample and render" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For this global plot, it's ok to lose some detail in our rendering. First we'll downsample the entire dataset by a factor of 10 on each spatial dimension and drop any values above the nodata value." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 773 ms, sys: 381 ms, total: 1.15 s\n", "Wall time: 1.35 s\n" ] } ], "source": [ "%%time\n", "factor = 10\n", "coarse_biomass = (\n", " ds.biomass.coarsen(dim={\"x\": factor, \"y\": factor}, boundary=\"trim\").mean().compute()\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With our dataset nicely reduced, we can plot the above ground biomass for the planet in the original sinusoidal projection. " ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "h, w = coarse_biomass.shape\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", "coarse_biomass.plot(cmap=\"Greens\", add_colorbar=False)\n", "\n", "ax.set_title(\"2016 estimated aboveground biomass\")\n", "plt.show();" ] } ], "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.8.13" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }