diff --git a/_quarto.yml b/_quarto.yml index 61e3fc50..900cf53a 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -125,6 +125,7 @@ website: - user-guide/notebooks/tutorials/mapping-fires.ipynb - user-guide/notebooks/tutorials/stac_ipyleaflet.ipynb - user-guide/notebooks/tutorials/zonal-statistics-validation.ipynb + - user-guide/notebooks/tutorials/mapping-slider.ipynb - section: Datasets contents: - user-guide/notebooks/datasets/ocean-npp-timeseries-analysis.ipynb diff --git a/user-guide/notebooks/tutorials/mapping-slider.ipynb b/user-guide/notebooks/tutorials/mapping-slider.ipynb new file mode 100644 index 00000000..229db655 --- /dev/null +++ b/user-guide/notebooks/tutorials/mapping-slider.ipynb @@ -0,0 +1,2454 @@ +{ + "cells": [ + { + "cell_type": "raw", + "id": "0269acc9-f6f7-4f43-ad67-ef7aaa1722c0", + "metadata": {}, + "source": [ + "---\n", + "Tutorial: How to Create Interactive Mapping Comparison Sliders Similar to the VEDA Environment\n", + " - Andrew Blackford \n", + "date: December 10, 2025\n", + "execute:\n", + " freeze: true\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "706f2619-9926-4e59-aca8-154e2b0a7041", + "metadata": {}, + "source": [ + "# Run This Notebook" + ] + }, + { + "cell_type": "markdown", + "id": "50a70c94-ded3-4f57-873d-370e6d925212", + "metadata": {}, + "source": [ + "

🚀 Launch in VEDA JupyterHub (requires access)

\n", + "\n", + "

To obtain credentials to VEDA Hub, follow this link for more information.

" + ] + }, + { + "cell_type": "markdown", + "id": "2486b5c2-f8a8-4df3-82b3-3a67ab296e52", + "metadata": {}, + "source": [ + "
\n", + "Disclaimer: it is highly recommended to run a tutorial within NASA VEDA JupyterHub, which already includes functions for processing and visualizing data specific to VEDA stories. Running the tutorial outside of the VEDA JupyterHub may lead to errors, specifically related to EarthData authentication. Additionally, it is recommended to use the Pangeo workspace within the VEDA JupyterHub, since certain packages relevant to this tutorial are already installed.
\n", + "\n", + "

If you do not have a VEDA Jupyterhub Account you can launch this notebook on your local environment using MyBinder by clicking the icon below.

\n", + "
\n", + "\n", + "\"Binder\" " + ] + }, + { + "cell_type": "markdown", + "id": "a077dad9-1522-4696-ad2f-a46d598f7cd8", + "metadata": {}, + "source": [ + "## Environment Setup" + ] + }, + { + "cell_type": "markdown", + "id": "2bc0a805-3d14-43c5-bd8b-c6e8173b6ed3", + "metadata": {}, + "source": [ + "If running this notebook outside of the VEDA JupyterHub, install the following packages:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "e2c21d4a-a9ee-4aee-82b5-91cbd4beaac9", + "metadata": {}, + "outputs": [], + "source": [ + "# Load libraries\n", + "\n", + "import requests\n", + "import matplotlib.pyplot as plt\n", + "import plotutils as putils\n", + "from pystac_client import Client\n", + "import folium" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "1f7c2671-92cb-427f-8822-54c6a71239bb", + "metadata": {}, + "outputs": [], + "source": [ + "# For retrieving data already catalogued in VEDA STAC\n", + "STAC_API_URL = \"https://openveda.cloud/api/stac\"\n", + "RASTER_API_URL = \"https://openveda.cloud/api/raster\"\n", + "\n", + "# Open STAC client designed for interacting with SpatioTemporal Asset Catalog (STAC) APIs and Catalogs\n", + "client_STAC = Client.open(STAC_API_URL)" + ] + }, + { + "cell_type": "markdown", + "id": "5ba52ecf-ba24-4d4c-86d7-0ac199d9a64d", + "metadata": {}, + "source": [ + "This example will show how to create an interactive mapping slider comparison experience similar to what is used in the VEDA environment. Using an example dataset from the VEDA STAC catalog, this notebook utilizes the Folium package to create this experience. " + ] + }, + { + "cell_type": "markdown", + "id": "1ac296cd-f687-4732-900e-3b9f2d44872d", + "metadata": {}, + "source": [ + "## Processing steps:\n", + "1.) Choose collection ID from the STAC catalog and two dates for analysis
\n", + "2.) Retrieve collection information and items from VEDA STAC catalog
\n", + "3.) Retrieve item statistics and tiling information
\n", + "4.) Plot data
" + ] + }, + { + "cell_type": "markdown", + "id": "f95da6e7-3b7a-4eb0-b54a-2d06894de2d0", + "metadata": {}, + "source": [ + "## Choose variable and retrieve json from VEDA STAC catalogue -HD Black Marble Nighttime Lights" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "2e0f9126-cc19-45ce-aa05-d970a53e61f9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'tilejson': '2.2.0', 'version': '1.0.0', 'scheme': 'xyz', 'tiles': ['https://openveda.cloud/api/raster/collections/delta-disasters-hd-blackmarble-nightlights/items/HD_Blackmarble_Nightlights_2023-03-15/tiles/WebMercatorQuad/{z}/{x}/{y}@1x?assets=cog_default&bidx=1&bidx=2&bidx=3&rescale=0%2C255&resampling=bilinear'], 'minzoom': 0, 'maxzoom': 24, 'bounds': [-90.95641041935056, 32.83238037641129, -90.78794047564391, 32.96655224164907], 'center': [-90.87217544749723, 32.899466309030174, 0]}\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "
\n", + "
\n", + " <CollectionClient id=delta-disasters-hd-blackmarble-nightlights>\n", + "
\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "date_pre = \"2023-03-15\" # Select date before tornado \n", + "\n", + "collection_id = \"delta-disasters-hd-blackmarble-nightlights\"\n", + "\n", + "# ── VEDA Collection Request ─────────────────────────────────────────────────────────────────────────────────────\n", + "\n", + "results_pre = client_STAC.search(collections=[collection_id], datetime=date_pre)\n", + "items_pre = list(results_pre.items())\n", + "item_pre = items_pre[0]\n", + "collection_pre = item_pre.get_collection()\n", + "\n", + "dashboard_render_pre = collection_pre.extra_fields[\"renders\"][\"dashboard\"]\n", + "assets_pre = dashboard_render_pre[\"assets\"][0]\n", + "((vmin_pre, vmax_pre),) = dashboard_render_pre[\"rescale\"]\n", + "bidx_pre = dashboard_render_pre.get(\"bidx\", [3, 2, 1])\n", + "\n", + "# ── VEDA Tile Request ─────────────────────────────────────────────────────────────────────────────────────\n", + "\n", + "response_pre = requests.get(\n", + " f\"{RASTER_API_URL.rstrip('/')}/collections/{collection_id}\"\n", + " f\"/items/{item_pre.id}/WebMercatorQuad/tilejson.json?\"\n", + " f\"&assets={assets_pre}\"\n", + " f\"&bidx={bidx_pre[0]}&bidx={bidx_pre[1]}&bidx={bidx_pre[2]}\"\n", + " f\"&rescale={vmin_pre},{vmax_pre}\"\n", + " f\"&resampling=bilinear\"\n", + ")\n", + "response_pre.raise_for_status()\n", + "tiles_pre = response_pre.json()\n", + "print(tiles_pre)\n", + "\n", + "collection_pre" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "6c15feac-c6b3-457f-9852-9c70913573a6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'tilejson': '2.2.0', 'version': '1.0.0', 'scheme': 'xyz', 'tiles': ['https://openveda.cloud/api/raster/collections/delta-disasters-hd-blackmarble-nightlights/items/HD_Blackmarble_Nightlights_2023-03-25/tiles/WebMercatorQuad/{z}/{x}/{y}@1x?assets=cog_default&bidx=1&bidx=2&bidx=3&rescale=0%2C255&resampling=bilinear'], 'minzoom': 0, 'maxzoom': 24, 'bounds': [-90.95641041935056, 32.83238037641129, -90.78794047564391, 32.96655224164907], 'center': [-90.87217544749723, 32.899466309030174, 0]}\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "
\n", + "
\n", + " <CollectionClient id=delta-disasters-hd-blackmarble-nightlights>\n", + "
\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "date_post = \"2023-03-25\" # Select date after tornado \n", + "\n", + "# ── VEDA Collection Request ─────────────────────────────────────────────────────────────────────────────────────\n", + "\n", + "results_post = client_STAC.search(collections=[collection_id], datetime=date_post)\n", + "\n", + "items_post = list(results_post.items())\n", + "assert len(items_post) != 0, \"No items found\"\n", + "item_post = items_post[0]\n", + "collection_post = item_post.get_collection()\n", + "\n", + "# grab the dashboard render block\n", + "dashboard_render_post = collection_post.extra_fields[\"renders\"][\"dashboard\"]\n", + "\n", + "assets_post = dashboard_render_post[\"assets\"][0]\n", + "((vmin_post, vmax_post),) = dashboard_render_post[\"rescale\"]\n", + "\n", + "# Special handling for RGB bands - post tornado uses different band order (1,2,3)\n", + "bidx_post = dashboard_render_post.get(\"bidx\", [1, 2, 3]) # RGB band order for post\n", + "\n", + "\n", + "# ── VEDA Tile Request for Post-Tornado ─────────────────────────────────────────────────────────────────────\n", + "\n", + "response_post = requests.get(\n", + " f\"{RASTER_API_URL.rstrip('/')}/collections/{collection_id}\"\n", + " f\"/items/{item_post.id}/WebMercatorQuad/tilejson.json?\"\n", + " f\"&assets={assets_post}\"\n", + " f\"&bidx={bidx_post[0]}&bidx={bidx_post[1]}&bidx={bidx_post[2]}\" # RGB bands (1,2,3 for post)\n", + " f\"&rescale={vmin_post},{vmax_post}\"\n", + " f\"&resampling=bilinear\"\n", + ")\n", + "\n", + "response_post.raise_for_status()\n", + "\n", + "tiles_post = response_post.json()\n", + "print(tiles_post)\n", + "\n", + "collection_post\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "23aa08a5-35f3-4d50-81e3-82a19e6360bc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'openstreetmap': 'OpenStreetMap standard tiles',\n", + " 'cartodb-positron': 'Light gray CartoDB basemap (subtle, good for data visualization)',\n", + " 'cartodb-dark': 'Dark CartoDB basemap (good for bright data)',\n", + " 'esri-satellite': 'ESRI satellite imagery without labels',\n", + " 'esri-satellite-labels': 'ESRI satellite imagery with place labels overlay',\n", + " None: 'No basemap (transparent background)'}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# First we will present the different basemaps that we have access to underlay beneath our tile requests\n", + "# For the first map, we will utilize the 'esri-satellite-labels' map layer\n", + "putils.get_available_basemaps()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "fa082f97-636c-4c00-90c7-9af9475d1c3c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
Make this Notebook Trusted to load map: File -> Trust Notebook
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create side-by-side comparison of the effects of the March 24, 2023 tornado using the create_side_by_side_map function\n", + "m_comparison = putils.plot_folium_SidebySide_layer_from_VEDA_STAC(\n", + " tiles_url_left=tiles_pre[\"tiles\"][0], # Pre-tornado (May 20)\n", + " tiles_url_right=tiles_post[\"tiles\"][0], # Post-tornado (May 22)\n", + " center_coords=[32.9069, -90.8785], # Rolling Fork, MS\n", + " zoom_level=13.5,\n", + " title=\"NASA's HD Black Marble Nighttime Lights: Rolling Fork, MS EF-4 Tornado\",\n", + " label_left=\"← Pre-Tornado (March 15)\",\n", + " label_right=\"Post-Tornado (March 25) →\",\n", + " layer_name_left=\"Pre-Tornado\",\n", + " layer_name_right=\"Post-Tornado\",\n", + " opacity=1.0, # Full opacity for satellite imagery\n", + " basemap_style='cartodb-positron', # Light basemap\n", + " height=\"800px\",\n", + " width=\"100%\"\n", + ")\n", + "\n", + "# Display the comparison map\n", + "m_comparison" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "406b215d-cecf-46d6-bd4f-b9d8d50918a0", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.12.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}