{ "cells": [ { "cell_type": "markdown", "id": "732b28cc-f83a-4379-bf35-06d76a803d2c", "metadata": {}, "source": [ "# Visualize GEDI Data using Lonboard\n", "\n", "Authors: Harshini Girish(UAH), Rajat Shinde(UAH), Sheyenne Kirkland(UAH), Alex Mandel(Development Seed), Zac Deziel(Development Seed), Jamison French(Development Seed)\n", "\n", "Date: April 9, 2025\n", "\n", "Description: In the MAAP Algorithm Development Environment (ADE), Lonboard enables interactive geospatial visualization of GEDI calibration and validation data. By transforming CSV-based GEDI field measurements into a GeoDataFrame, we can map biomass and structural metrics across ecosystems with spatial precision. Lonboard not only facilitates intuitive exploration of these ecological patterns but also allows visualization of field measurements at scale, making it an essential tool for analyzing large datasets efficiently across diverse geographic regions.\n", "\n" ] }, { "cell_type": "markdown", "id": "9875093f-09e9-41b2-a2cb-47dc13a0bd0a", "metadata": {}, "source": [ "## Run This Notebook" ] }, { "cell_type": "markdown", "id": "845fbd79-e2a7-40e5-a94b-c1d12d4ed34a", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "id": "558755b1-0bcf-4195-9196-4635404404fb", "metadata": {}, "source": [ "## Additional Resources\n", "\n", "- [Visualizing STAC Items](https://docs.maap-project.org/en/latest/technical_tutorials/visualizing.html): Official MAAP tutorial demonstrating how to visualize spatial footprints from STAC collections using different functions inside the ADE.\n", "\n", "- [MAAP STAC Spatial Coverage Example](https://github.com/MAAP-Project/maap-documentation-examples/blob/main/stac_spatial_coverage/notebooks/plot_items.ipynb): A practical example from MAAP’s documentation repository showing how to retrieve and plot spatial items from a STAC catalog.\n", "\n", "- [Lonboard Examples](https://developmentseed.org/lonboard/latest/examples/): Showcases a variety of interactive visualizations utilizing large geospatial datasets, demonstrating the library's capabilities in rendering complex data efficiently\n" ] }, { "cell_type": "markdown", "id": "ab3e8048-7087-496b-a5e0-202b399a77fe", "metadata": {}, "source": [ "## Install/Import Packages" ] }, { "cell_type": "markdown", "id": "4f2a30d3-7d7b-4795-83b9-24b4b5684fec", "metadata": {}, "source": [ "Make sure the following libraries are installed before running the notebook:" ] }, { "cell_type": "code", "execution_count": null, "id": "2bf6d15a-5b4f-4c2c-b49a-018361982f15", "metadata": {}, "outputs": [], "source": [ "!pip install lonboard pyogrio\n" ] }, { "cell_type": "code", "execution_count": null, "id": "441df1aa-8e86-4aa2-9df9-f24fce1c7bb6", "metadata": {}, "outputs": [], "source": [ "# Standard library\n", "from pprint import pprint\n", "\n", "# Data handling\n", "import numpy as np\n", "import pandas as pd\n", "\n", "# Plotting\n", "import matplotlib.pyplot as plt\n", "import matplotlib as mpl\n", "\n", "# Geospatial tools\n", "import geopandas as gpd\n", "from shapely.geometry import Point\n", "\n", "# STAC/MAAP tools\n", "from pystac_client import Client\n", "from maap.maap import MAAP\n", "import boto3\n", "\n", "# Lonboard visualization\n", "import lonboard as lb\n", "from lonboard import viz, Map, ScatterplotLayer\n", "from lonboard.colormap import apply_continuous_cmap\n" ] }, { "cell_type": "markdown", "id": "10edd7aa-ca6e-4a20-87fc-ae876f3afe67", "metadata": {}, "source": [ "## Initializing the MAAP STAC Endpoint" ] }, { "cell_type": "markdown", "id": "bdfcba9a-1f12-4489-8dcc-73604eee8b8c", "metadata": {}, "source": [ "Before beginning, we’ll form a connection to the MAAP STAC endpoint to set up" ] }, { "cell_type": "code", "execution_count": 3, "id": "2f2585bf-caae-49e0-baa9-b544307f754b", "metadata": {}, "outputs": [], "source": [ "maap = MAAP()\n", "stac_endpoint = \"https://stac.maap-project.org\"\n", "catalog = Client.open(stac_endpoint)\n" ] }, { "cell_type": "markdown", "id": "7d7391ac-8098-438f-8ab1-98275e0934a3", "metadata": {}, "source": [ "## Retrieving the Data\n" ] }, { "cell_type": "markdown", "id": "dd3ab0f0-f16d-46a9-8153-586a796343b4", "metadata": {}, "source": [ "In this example we use the `GEDI_CalVal_Field_Data` STAC collection and extracts the S3 URL of its first asset.\n", "It then reads the remote CSV file directly into a pandas DataFrame using `pd.read_csv()`." ] }, { "cell_type": "code", "execution_count": 4, "id": "4a62b1a3-3358-42d3-b3d5-603011763908", "metadata": {}, "outputs": [], "source": [ "collection_id = \"GEDI_CalVal_Field_Data\"\n", "search_result = catalog.search(collections=[collection_id], max_items=1)\n", "item = list(search_result.items())[0]\n", "s3_url = next(iter(item.assets.values())).href\n", "df = pd.read_csv(s3_url)\n" ] }, { "cell_type": "markdown", "id": "7da8f426-a3a6-4639-875d-63a1d7b91010", "metadata": {}, "source": [ "Note: If users want to use and extract a different dataset, they can list and view available collections using the [STAC Catalog search method](https://docs.maap-project.org/en/latest/technical_tutorials/search/searching_the_stac_catalog.html#Searching-Collections).\n" ] }, { "cell_type": "markdown", "id": "f63a7a5b-1c07-49a3-a121-ff9267ade526", "metadata": {}, "source": [ "## Explore Data" ] }, { "cell_type": "markdown", "id": "a0c9ae4e-8008-44d1-8539-60b9de292128", "metadata": {}, "source": [ "Here the file is read into a pandas DataFrame named `df`. Each row in the CSV corresponds to a record, and columns represent associated attributes." ] }, { "cell_type": "markdown", "id": "370620e4-b9ea-4c50-80ec-ecfbd710c63c", "metadata": {}, "source": [ "This step converts the `longitude` and `latitude` columns into spatial Point objects and wraps them in a `GeoDataFrame(gdf)`." ] }, { "cell_type": "code", "execution_count": 5, "id": "0d4981cb-22fd-4eba-93e5-e6efaf0a7c37", "metadata": {}, "outputs": [], "source": [ "gdf = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df['longitude'], df['latitude']), crs=\"EPSG:4326\")\n" ] }, { "cell_type": "markdown", "id": "c306105c-f929-44c9-88c5-4a8fe736f839", "metadata": {}, "source": [ "## Create and Display the Map" ] }, { "cell_type": "markdown", "id": "7a92019f-85e7-4bce-aaa0-6a8ab371a708", "metadata": {}, "source": [ "This step creates a `ScatterplotLayer` from the GeoDataFrame `gdf` and adds it to a Lonboard Map. The interactive map is rendered in the notebook, displaying all spatial points from your data. For quick visualization, you can also use `viz(gdf)`, which provides the simplest rendering using default styling.\n", "\n" ] }, { "cell_type": "code", "execution_count": 6, "id": "a7f6ffff-88a1-42f8-a9ea-557588ef1751", "metadata": { "scrolled": true }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "10daba45ada3456cbf7243df89bc9c51", "version_major": 2, "version_minor": 1 }, "text/plain": [ "Map(basemap_style=\n", "[\n", " [\n", " …" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "colors = mpl.colormaps[\"viridis_r\"](norm(gdf[\"m.agb\"]), bytes=True)\n", "layer = ScatterplotLayer.from_geopandas(gdf, get_fill_color=colors, radius_min_pixels=3)\n", "Map(layer)\n" ] }, { "cell_type": "markdown", "id": "5cf40439-61c9-49a6-afcc-03c422a9a2fe", "metadata": {}, "source": [ "## Visualizing Unique Species by Color " ] }, { "cell_type": "markdown", "id": "a822e264-8338-4720-9bae-d21fea4d760f", "metadata": {}, "source": [ "This snippet assigns each unique species in the GeoDataFrame a distinct RGB color.\n", "The species are mapped to integer-scaled RGB values and set as the fill color for each point on a `ScatterplotLayer`.\n" ] }, { "cell_type": "code", "execution_count": 20, "id": "e374c5dc-fced-4ac2-b381-481730ce0c71", "metadata": {}, "outputs": [], "source": [ "gdf_cat = gdf.copy()\n", "unique_species = gdf_cat[\"species\"].unique()\n", "color_map = {sp: (np.array(plt.cm.tab20(i % 20)) * 255).astype(int).tolist()\n", " for i, sp in enumerate(unique_species)}\n", "gdf_cat[\"color\"] = gdf_cat[\"species\"].map(color_map)\n", "\n" ] }, { "cell_type": "code", "execution_count": 21, "id": "e283836f-4865-4aef-96a1-fcfd70f62450", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "e97cebd6bcc04e3ab5b7826199fb342b", "version_major": 2, "version_minor": 1 }, "text/plain": [ "Map(layers=[ScatterplotLayer(get_fill_color=\n", "[\n", " [\n", " …" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "layer = lb.ScatterplotLayer.from_geopandas(\n", " gdf_cat,\n", " get_fill_color=np.array(gdf_cat[\"color\"].tolist(), dtype=np.uint8),\n", " radius_min_pixels=4\n", ")\n", "lb.Map(layer)\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.12.7" } }, "nbformat": 4, "nbformat_minor": 5 }