Visualize GEDI Data using Lonboard
Authors: Harshini Girish(UAH), Rajat Shinde(UAH), Sheyenne Kirkland(UAH), Alex Mandel(Development Seed), Zac Deziel(Development Seed), Jamison French(Development Seed)
Date: April 9, 2025
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.
Run This Notebook
To access and run this tutorial within MAAP’s Algorithm Development Environment (ADE), please refer to the “Getting started with the MAAP” section of our documentation.
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.
Additional Resources
Visualizing STAC Items: Official MAAP tutorial demonstrating how to visualize spatial footprints from STAC collections using different functions inside the ADE.
MAAP STAC Spatial Coverage Example: A practical example from MAAP’s documentation repository showing how to retrieve and plot spatial items from a STAC catalog.
Lonboard Examples: Showcases a variety of interactive visualizations utilizing large geospatial datasets, demonstrating the library’s capabilities in rendering complex data efficiently
Install/Import Packages
Make sure the following libraries are installed before running the notebook:
[ ]:
!pip install lonboard pyogrio
[ ]:
# Standard library
from pprint import pprint
# Data handling
import numpy as np
import pandas as pd
# Plotting
import matplotlib.pyplot as plt
import matplotlib as mpl
# Geospatial tools
import geopandas as gpd
from shapely.geometry import Point
# STAC/MAAP tools
from pystac_client import Client
from maap.maap import MAAP
import boto3
# Lonboard visualization
import lonboard as lb
from lonboard import viz, Map, ScatterplotLayer
from lonboard.colormap import apply_continuous_cmap
Initializing the MAAP STAC Endpoint
Before beginning, we’ll form a connection to the MAAP STAC endpoint to set up
[3]:
maap = MAAP()
stac_endpoint = "https://stac.maap-project.org"
catalog = Client.open(stac_endpoint)
Retrieving the Data
In this example we use the GEDI_CalVal_Field_Data STAC collection and extracts the S3 URL of its first asset. It then reads the remote CSV file directly into a pandas DataFrame using pd.read_csv().
[4]:
collection_id = "GEDI_CalVal_Field_Data"
search_result = catalog.search(collections=[collection_id], max_items=1)
item = list(search_result.items())[0]
s3_url = next(iter(item.assets.values())).href
df = pd.read_csv(s3_url)
Note: If users want to use and extract a different dataset, they can list and view available collections using the STAC Catalog search method.
Explore Data
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.
This step converts the longitude and latitude columns into spatial Point objects and wraps them in a GeoDataFrame(gdf).
[5]:
gdf = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df['longitude'], df['latitude']), crs="EPSG:4326")
Create and Display the Map
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.
[6]:
from lonboard import viz
viz(gdf)
[6]:
[7]:
layer = ScatterplotLayer.from_geopandas(gdf,
get_fill_color=[0, 255, 0],
radius_min_pixels=4
)
Map(layer)
[7]:
Normalize and Visualizing with a Colormap
In visualizations, normalization ensures that variables with different units or magnitudes (like biomass or elevation) can be accurately mapped to color gradients or other visual elements for intuitive comparison.
This prints the list of column names in the GeoDataFrame gdf. It helps you quickly inspect what data attributes are available — such as spatial coordinates, biomass, species names, or timestamps.
[8]:
print(gdf.columns)
Index(['project', 'plot', 'subplot', 'survey', 'private', 'date', 'region',
'vegetation', 'map', 'mat', 'pft.modis', 'pft.name', 'wwf.ecoregion',
'latitude', 'longitude', 'p.sample', 'p.stemmap', 'p.origin',
'p.orientation', 'p.shape', 'p.majoraxis', 'p.minoraxis', 'p.geom',
'p.epsg', 'p.area', 'p.mindiam', 'sp.geom', 'sp.ix', 'sp.iy',
'sp.shape', 'sp.area', 'sp.mindiam', 'pai', 'lai', 'cover', 'dft',
'agb', 'agb.valid', 'agb.lower', 'agb.upper', 'agbd.ha',
'agbd.ha.lower', 'agbd.ha.upper', 'sn', 'snd.ha', 'sba', 'sba.ha',
'swsg.ba', 'h.t.max', 'sp.agb', 'sp.agb.valid', 'sp.agbd.ha',
'sp.agbd.ha.lower', 'sp.agbd.ha.upper', 'sp.sba.ha', 'sp.swsg.ba',
'sp.h.t.max', 'l.project', 'l.instr', 'l.epsg', 'l.date', 'g.fp',
'tree.date', 'family', 'species', 'pft', 'wsg', 'wsg.sd', 'tree',
'stem', 'x', 'y', 'z', 'status', 'allom.key', 'a.stem', 'h.t',
'h.t.mod', 'd.stem', 'd.stem.valid', 'd.ht', 'c.w', 'm.agb',
'sp.majoraxis', 'sp.minoraxis', 'geometry'],
dtype='object')
The m.agb column represents mean above-ground biomass (AGB), estimating the total mass of living plant material above the soil surface within a plot.
This sets the color scale range by extracting the 10th and 90th percentile values of m.agb, which helps reduce the influence of outliers and emphasize meaningful variation in the data.
[9]:
norm = mpl.colors.Normalize(*gdf["m.agb"].quantile([0.1, 0.9]))
[10]:
colors = mpl.colormaps["viridis_r"](norm(gdf["m.agb"]), bytes=True)
layer = ScatterplotLayer.from_geopandas(gdf, get_fill_color=colors, radius_min_pixels=3)
Map(layer)
[10]:
Visualizing Unique Species by Color
This snippet assigns each unique species in the GeoDataFrame a distinct RGB color. The species are mapped to integer-scaled RGB values and set as the fill color for each point on a ScatterplotLayer.
[20]:
gdf_cat = gdf.copy()
unique_species = gdf_cat["species"].unique()
color_map = {sp: (np.array(plt.cm.tab20(i % 20)) * 255).astype(int).tolist()
for i, sp in enumerate(unique_species)}
gdf_cat["color"] = gdf_cat["species"].map(color_map)
[21]:
layer = lb.ScatterplotLayer.from_geopandas(
gdf_cat,
get_fill_color=np.array(gdf_cat["color"].tolist(), dtype=np.uint8),
radius_min_pixels=4
)
lb.Map(layer)
[21]: