{ "cells": [ { "cell_type": "markdown", "id": "657e3281", "metadata": { "tags": [] }, "source": [ "## ESA CCI v4 Access and Visualize" ] }, { "cell_type": "markdown", "id": "eac6b82d", "metadata": { "tags": [] }, "source": [ "Authors: Emile Tenezakis (Development Seed), Rajat Shinde (UAH)\n", "\n", "Date: October 2, 2023\n", "\n", "Description: In this tutorial, we explore accessing and visualizing ESA CCI Version 4 data from the MAAP STAC catalog. We make use of the [stackstac package](https://github.com/gjoseph92/stackstac) that allows us to turn a stack collection imported from the catalog with the [`pystac_client`](https://pystac-client.readthedocs.io/en/stable/) to an xarray dataset, and we plot the time series of the mean aboveground biomass for a selected tile of the dataset across the available temporal range.\n", "\n", "### Run This Notebook\n", "To access and run this tutorial within MAAP's Algorithm Development Environment (ADE), please refer to the [\"Getting started with the MAAP\"](https://docs.maap-project.org/en/latest/getting_started/getting_started.html) section of our documentation.\n", "\n", "Disclaimer: it is highly recommended to run a tutorial within MAAP's ADE, which already includes packages specific to MAAP, such as maap-py. Running the tutorial outside of the MAAP ADE may lead to errors. \n", "\n", "### About The Dataset\n", "This dataset comprises estimates of forest Above-Ground Biomass (AGB) for the years 2017, 2018, 2019 and 2020, version 4. They are derived from a combination of Earth Observation (EO) data, depending on the year, from the Copernicus Sentinel-1 mission, Envisat’s ASAR instrument and JAXA’s Advanced Land Observing Satellite (ALOS-1 and ALOS-2), along with additional information from Earth observation sources. The data has been produced as part of the European Space Agency's (ESA's) Climate Change Initiative (CCI) programme by the Biomass CCI team.\n", "\n", "### Additional Resources\n", "* [Cloud Native Geoguide](https://guide.cloudnativegeo.org/)\n", "* [Examples of Working with COGs](https://guide.cloudnativegeo.org/cloud-optimized-geotiffs/cogs-examples.html)\n", "* [ESA’s Climate Change Initiative Biomass project](https://climate.esa.int/en/odp/#/project/biomass)\n", "* [xarray Documentation](https://docs.xarray.dev/en/stable/index.html)\n", " " ] }, { "cell_type": "markdown", "id": "445b69f2-13ab-4a03-aa41-4fde92c270fa", "metadata": {}, "source": [ "### Importing and Installing Packages\n", "First off, we will install and import the required Python packages if they are not already installed in the workspace." ] }, { "cell_type": "code", "execution_count": 1, "id": "aea4eb22-0867-4876-bd2b-fe74f5dd7bab", "metadata": { "tags": [] }, "outputs": [], "source": [ "# !mamba install -y -c conda-forge stackstac\n", "# !pip install pystac_client" ] }, { "cell_type": "code", "execution_count": 2, "id": "bcc3b0ba", "metadata": { "tags": [] }, "outputs": [], "source": [ "from stackstac import stack, mosaic\n", "import pystac_client" ] }, { "cell_type": "markdown", "id": "e96d41e2-919c-4fc2-9e3e-0fa0f8d692dc", "metadata": {}, "source": [ "### Accessing and Filtering the Items" ] }, { "cell_type": "markdown", "id": "26a45833", "metadata": {}, "source": [ "After installing the require packages, we create a client to access the STAC test catalog." ] }, { "cell_type": "code", "execution_count": 3, "id": "e6917d57", "metadata": { "tags": [] }, "outputs": [], "source": [ "URL = \"https://stac.maap-project.org\"\n", "catalog = pystac_client.Client.open(URL)" ] }, { "cell_type": "markdown", "id": "69ac94df-819e-4be5-9893-3f3d3c519c37", "metadata": {}, "source": [ "### Creating an AOI" ] }, { "cell_type": "markdown", "id": "df045dc1", "metadata": {}, "source": [ "Now, we define a bounding box of interest to find the tile that covers a small region around Manaus, Brazil (Amazon rainforest)." ] }, { "cell_type": "code", "execution_count": 4, "id": "73868a97", "metadata": { "tags": [] }, "outputs": [], "source": [ "# BBox for filtering the items in the collection\n", "bbox = [-55,-6,-54.8,-5.8]" ] }, { "cell_type": "markdown", "id": "6b7e1326-debd-4bb5-8068-c6ba89c8e140", "metadata": {}, "source": [ "We proceed to an item search in the catalog using the `pystac-client`, filtering items covering our area of interest." ] }, { "cell_type": "code", "execution_count": 5, "id": "7bb997f0", "metadata": { "tags": [] }, "outputs": [], "source": [ "stac_collection = catalog.search(\n", " collections=[\"ESACCI_Biomass_L4_AGB_V4_100m\"],\n", " bbox=bbox\n", ")" ] }, { "cell_type": "markdown", "id": "3281f4e8-da53-4fcc-bb3f-bebb43e5143d", "metadata": {}, "source": [ "Let's take a quick look at the results of the search. We can access the link of the location where the data is stored using `href` attribute as shown below." ] }, { "cell_type": "code", "execution_count": 6, "id": "c73362b3-cfb5-48cd-a018-58ca5feb6ef4", "metadata": { "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/opt/conda/lib/python3.10/site-packages/pystac_client/item_search.py:850: FutureWarning: get_all_items() is deprecated, use item_collection() instead.\n", " warnings.warn(\n" ] }, { "data": { "text/plain": [ "'s3://nasa-maap-data-store/file-staging/nasa-map/ESACCI_Biomass_L4_AGB_V4_100m_2020/N00W060_ESACCI-BIOMASS-L4-AGB-MERGED-100m-2020-fv4.0.tif'" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stac_collection.get_all_items()[0].assets['estimates'].href" ] }, { "cell_type": "markdown", "id": "c26df930-3bd2-4bc1-9941-bc7215f3fa22", "metadata": {}, "source": [ "We turn the resulting set of items (we expect one item here in the result, given the size of our bounding box) into an xarray DataArray using `stackstac`." ] }, { "cell_type": "code", "execution_count": 7, "id": "46bf720c-18d7-4d80-960b-998abd4a5255", "metadata": { "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/opt/conda/lib/python3.10/site-packages/stackstac/prepare.py:408: UserWarning: The argument 'infer_datetime_format' is deprecated and will be removed in a future version. A strict version of it is now the default, see https://pandas.pydata.org/pdeps/0004-consistent-to-datetime-parsing.html. You can safely remove this argument.\n", " times = pd.to_datetime(\n" ] } ], "source": [ "#Creating a stack of the filtered items\n", "arr = stack(stac_collection.get_all_items())" ] }, { "cell_type": "markdown", "id": "86a57e55-3d3a-473f-85e5-cbf6b17d198a", "metadata": {}, "source": [ "We can see that our array has the following dimensions : `time` (we have four data points, one for each year - 2017, 2018, 2019 and 2020), `latitude`, `longitude` and `band` (we have two bands : `estimates` with the AGB estimate values, and `std_dev` storing the AGB standard deviation values)." ] }, { "cell_type": "code", "execution_count": 8, "id": "ed6f411a-4643-40a6-b263-f2169845af6a", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/html": [ "
<xarray.DataArray 'stackstac-5517ee4c53a7b2c13f76bf727c00d1fc' (time: 4,\n",
" band: 2,\n",
" y: 11250,\n",
" x: 11251)>\n",
"dask.array<fetch_raster_window, shape=(4, 2, 11250, 11251), dtype=float64, chunksize=(1, 1, 1024, 1024), chunktype=numpy.ndarray>\n",
"Coordinates: (12/13)\n",
" * time (time) datetime64[ns] 2017-01-01 2018-01-01 ... 2020-01-01\n",
" id (time) <U52 'N00W060_ESACCI-BIOMASS-L4-AGB-MERGED-100m-20...\n",
" * band (band) <U18 'estimates' 'standard_deviation'\n",
" * x (x) float64 -60.0 -60.0 -60.0 -60.0 ... -50.0 -50.0 -50.0\n",
" * y (y) float64 0.0 -0.0008889 -0.001778 ... -9.998 -9.999\n",
" proj:epsg int64 4326\n",
" ... ...\n",
" proj:transform object {0.0, 1.0, -60.0, -0.00088888888888, 0.00088888888...\n",
" proj:shape object {11250}\n",
" proj:geometry object {'type': 'Polygon', 'coordinates': [[[-60.0, -9.99...\n",
" title (band) <U49 'Cloud Optimized GeoTIFF of AGB estimates' 'C...\n",
" description (band) <U49 'Cloud Optimized GeoTIFF of AGB estimates' 'C...\n",
" epsg int64 4326\n",
"Attributes:\n",
" spec: RasterSpec(epsg=4326, bounds=(-60.00088888828888, -9.9999999...\n",
" crs: epsg:4326\n",
" transform: | 0.00, 0.00,-60.00|\\n| 0.00,-0.00, 0.00|\\n| 0.00, 0.00, 1.00|\n",
" resolution: 0.00088888888888<xarray.DataArray 'stackstac-5517ee4c53a7b2c13f76bf727c00d1fc' (time: 4,\n",
" band: 2,\n",
" y: 225, x: 225)>\n",
"dask.array<getitem, shape=(4, 2, 225, 225), dtype=float64, chunksize=(1, 1, 225, 225), chunktype=numpy.ndarray>\n",
"Coordinates: (12/13)\n",
" * time (time) datetime64[ns] 2017-01-01 2018-01-01 ... 2020-01-01\n",
" id (time) <U52 'N00W060_ESACCI-BIOMASS-L4-AGB-MERGED-100m-20...\n",
" * band (band) <U18 'estimates' 'standard_deviation'\n",
" * x (x) float64 -55.0 -55.0 -55.0 -55.0 ... -54.8 -54.8 -54.8\n",
" * y (y) float64 -5.801 -5.802 -5.803 ... -5.998 -5.999 -6.0\n",
" proj:epsg int64 4326\n",
" ... ...\n",
" proj:transform object {0.0, 1.0, -60.0, -0.00088888888888, 0.00088888888...\n",
" proj:shape object {11250}\n",
" proj:geometry object {'type': 'Polygon', 'coordinates': [[[-60.0, -9.99...\n",
" title (band) <U49 'Cloud Optimized GeoTIFF of AGB estimates' 'C...\n",
" description (band) <U49 'Cloud Optimized GeoTIFF of AGB estimates' 'C...\n",
" epsg int64 4326\n",
"Attributes:\n",
" spec: RasterSpec(epsg=4326, bounds=(-60.00088888828888, -9.9999999...\n",
" crs: epsg:4326\n",
" transform: | 0.00, 0.00,-60.00|\\n| 0.00,-0.00, 0.00|\\n| 0.00, 0.00, 1.00|\n",
" resolution: 0.00088888888888"
],
"text/plain": [
"