The Texas Flooding of July 4, 2025

Analysis of the river flooding that occured along the Guadalupe River in Texas on July 4, 2025 using Sentinel-2 True Color and NDVI to visualize it.
Authors

Aaron Serre (UAH)

Kyle Lesinger (UAH)

Published

November 12, 2025

Run This Notebook

🚀 Launch in Disasters-Hub JupyterHub (requires access)

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

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.

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.


Binder

Table of Contents

Approach

Identify available dates and temporal frequency of observations for a given collection - Sentinel-2 NDVI and True Color

Pass the STAC item into raster API collections endpoint

We’ll visualize two tiles (side-by-side) allowing for comparison of each of the time points using folium.plugins.DualMap

About the Data

Using NASA’s Sentinel-2 satelite provides a great way to to use high resolution data to analyze imagery through different methods. In this notebook, we will retrieve Truecolor and NDVI products from Sentinenel-2 to analyze the tragic Texas floods along the Guadalupe River on July 4, 2025.

True Color: This composite uses the Red, Green, and Blue bands (B4, B3, B2) to create a natural-looking image, primarily used for general mapping and visual inspection of the surface.

NDVI: This is a mathematical index calculated from the Near-Infrared and Red bands, used to quantify and monitor the greenness, health, and density of vegetation.

Terminology

SpatialTemporal Asset Catalog (STAC): This is a specification that standardizes metadata for geospatial assets such as satelite imagery so that they can be used for use.

Application Programming Interface (API): Protocals that is used to communicate between softwares and makes a request (for tiles in out case).

Collection: A set of STAC definied for a specific item (such as Sentinel-2-vars-daily in our case)

Tiles: A square fragment of a satellite image. Together they make an image, but we call up once specific one for this example since th area we are looking at is small.

Install the Required Libraries

#Import the following libraries:

import requests
from folium import Map, TileLayer
import folium.plugins
import folium
from pystac_client import Client

Querying the STAC API

#Define the STAC and RASTER URL's and define the product
STAC_API_URL = "https://dev.openveda.cloud/api/stac" 
RASTER_API_URL = "https://dev.openveda.cloud/api/raster"
collection_name = "sentinel-2-all-vars-daily"

#For True Color:
asset='trueColor'

# Fetch the collection from the STAC API 
catalog = Client.open(STAC_API_URL)
collection = catalog.get_collection(collection_name)

# Print the properties of the collection to the console
collection
<CollectionClient id=sentinel-2-all-vars-daily>
#For NDVI:
# Using the NDVI asset with Magma colormap
ASSET_NAME = "ndviChange"
COLORMAP = "magma"
RESCALE_VALUES = {"min": -1, "max": 1} 

catalog = Client.open(STAC_API_URL)

# Search for items (using the original wide search)
search = catalog.search(
    collections=collection_name,
    datetime=['2025-01-01T00:00:00Z', '2025-07-31T00:00:00Z']
)
items_dict = search.item_collection()


item_post_id = items_dict[0].id
print(f"Found {len(items_dict)} items. Using Post-Event Item ID: {item_post_id}")
Found 31 items. Using Post-Event Item ID: sentinel-2-2025-07-17

Now, we will extract the dates to find the correct date for the before/after of the Guadalupe River

collection.extra_fields["renders"][asset]
items = list(collection.get_items())  # Convert the iterator to a list
print(f"Found {len(items)} items")

# The search function lets you search for items within a specific date/time range
search = catalog.search(
    collections=collection_name,
    datetime=['2025-01-01T00:00:00Z','2025-07-31T00:00:00Z']
)
# Take a look at the items we found
print(f"# items in date range: {len(search.item_collection())}")
for item in search.item_collection():
    print(item)

[item for item in search.item_collection() if item.properties['datetime'] > '2025-01-08T00:00:00Z']

items_dict = search.item_collection()
items_dict
Found 45 items
# items in date range: 31
<Item id=sentinel-2-2025-07-17>
<Item id=sentinel-2-2025-07-10>
<Item id=sentinel-2-2025-07-08>
<Item id=sentinel-2-2025-06-29>
<Item id=sentinel-2-2025-06-28>
<Item id=sentinel-2-2025-06-21>
<Item id=sentinel-2-2025-06-20>
<Item id=sentinel-2-2025-06-19>
<Item id=sentinel-2-2025-06-18>
<Item id=sentinel-2-2025-06-17>
<Item id=sentinel-2-2025-06-14>
<Item id=sentinel-2-2025-06-11>
<Item id=sentinel-2-2025-06-09>
<Item id=sentinel-2-2025-06-08>
<Item id=sentinel-2-2025-04-09>
<Item id=sentinel-2-2025-04-08>
<Item id=sentinel-2-2025-04-07>
<Item id=sentinel-2-2025-03-31>
<Item id=sentinel-2-2025-03-22>
<Item id=sentinel-2-2025-03-19>
<Item id=sentinel-2-2025-03-18>
<Item id=sentinel-2-2025-03-17>
<Item id=sentinel-2-2025-03-13>
<Item id=sentinel-2-2025-03-12>
<Item id=sentinel-2-2025-03-10>
<Item id=sentinel-2-2025-02-25>
<Item id=sentinel-2-2025-02-23>
<Item id=sentinel-2-2025-02-20>
<Item id=sentinel-2-2025-02-17>
<Item id=sentinel-2-2025-02-10>
<Item id=sentinel-2-2025-01-12>
<pystac.item_collection.ItemCollection object at 0x7f9520a182f0>

Requesting Tiles

Here, we will request the tiles. Since we have retrieved the datetime items from the catalog, we will now identify the index 9 (June 17, 2025 - Before) and index 0 (July 17, 2025 - After). These are the dates before and after the event with a tile that covers the Guadalupe River.

#For True Color:


#Before Flood
dashboard_render_pre = collection.extra_fields["renders"][asset]
assets_pre = dashboard_render_pre["assets"][0]
((vmin_pre, vmax_pre),) = dashboard_render_pre["rescale"]
bidx_pre = dashboard_render_pre.get("bidx", [1,2,3])

response_pre = requests.get(
    f"{RASTER_API_URL.rstrip('/')}/collections/{collection_name}"
    f"/items/{items_dict[9].id}/WebMercatorQuad/tilejson.json?"
    f"&assets={assets_pre}"
    f"&bidx={bidx_pre[0]}&bidx={bidx_pre[1]}&bidx={bidx_pre[2]}"
    f"&rescale={vmin_pre},{vmax_pre}"
    f"&resampling=bilinear"
)
response_pre.raise_for_status()
tiles_pre = response_pre.json()


#After Flood
dashboard_render_post = collection.extra_fields["renders"][asset]
assets_post = dashboard_render_post["assets"][0]
((vmin_post, vmax_post),) = dashboard_render_post["rescale"]
bidx_post = dashboard_render_post.get("bidx", [1,2,3])

response_post = requests.get(
    f"{RASTER_API_URL.rstrip('/')}/collections/{collection_name}"
    f"/items/{items_dict[0].id}/WebMercatorQuad/tilejson.json?"
    f"&assets={assets_post}"
    f"&bidx={bidx_post[0]}&bidx={bidx_post[1]}&bidx={bidx_post[2]}"
    f"&rescale={vmin_post},{vmax_post}"
    f"&resampling=bilinear"
)
response_post.raise_for_status()
tiles_post = response_post.json()


#NDVI:

response_ndvi = requests.get(
    f"{RASTER_API_URL.rstrip('/')}/collections/{collection_name}"
    f"/items/{item_post_id}/WebMercatorQuad/tilejson.json?"
    f"&assets={ASSET_NAME}"
    f"&colormap_name={COLORMAP}"
    f"&rescale={RESCALE_VALUES['min']},{RESCALE_VALUES['max']}"
    f"&resampling=bilinear"
)
response_ndvi.raise_for_status()
tiles_ndvi = response_ndvi.json()

Create Maps Using Folium

Now we are ready to plot the tiles on a map, using the follium package at Camp Mystic, one of the locations greatly affected by the flooding

#True Color Side by side

m = folium.plugins.DualMap(
    location=(30.005230182687843, -99.37260735373766), 
    
    zoom_start=16,
    tiles="cartodbdarkmatter"
)

# LEFT MAP 
TileLayer(
    tiles=tiles_pre["tiles"][0],
    attr="VEDA | Sentinel-2 | True Color",
    overlay=True,

).add_to(m.m1)

# RIGHT MAP: 
TileLayer(
    tiles=tiles_post["tiles"][0],
    attr="VEDA | Sentinel-2 | True Color",
    overlay=True,
).add_to(m.m2)



folium.LayerControl().add_to(m.m1)
folium.LayerControl().add_to(m.m2)


m
Make this Notebook Trusted to load map: File -> Trust Notebook

If you look along the river, you can see that there is more dirt and sediment deposited along the river bank where the flooding occured the greatest.

#NDVI Map

CENTER_LAT, CENTER_LON = 30.005230182687843, -99.37260735373766
ZOOM_LEVEL = 16

# Initialize a standard folium.Map
m = Map(
    location=(CENTER_LAT, CENTER_LON),
    zoom_start=ZOOM_LEVEL,
    tiles="cartodbdarkmatter"
)

# Add the Post-Event NDVI Layer
TileLayer(
    tiles=tiles_ndvi["tiles"][0],
    attr=f"VEDA | Sentinel-2 | {ASSET_NAME.upper()} ({COLORMAP})",
    name=f"Post-Event NDVI: {item_post_id}",
    overlay=True,
).add_to(m)

folium.LayerControl().add_to(m)

# Display the map
m
Make this Notebook Trusted to load map: File -> Trust Notebook

Here, purple represents damaged vegetation. You can see the dark purples right along the river. However, you can also see a light purple shading along the banks of the some of the creeks that feed off of the Guadalupe River. This is the damage that was done to the vegetation as a result of the flooding.

Summary

We have successfully visualized how NASA’s Sentinel-2 can view the 2025 Texas flooding using satelite data from Sentinel-2 in this notebook. We have installed the proper libraries, query the STAC API, request tiles to be used, and map the tiles using the follium package. Products such as NDVI and True Color help us observe and analyze disasters to better understand their impacts.

Back to top