# Provide STAC and RASTER API endpoints
STAC_API_URL = "https://dev.openveda.cloud/api/stac"
RASTER_API_URL = "https://dev.openveda.cloud/api/raster"
# Declare collection of interest - sentinel-2 daily data
collection_name = "sentinel-2-all-vars-daily"Analyzing the Effects of June 2025 New Mexico Wildfires
🚀 Launch in Disasters-Hub JupyterHub (requires access)
To obtain credentials to VEDA Hub, follow this link for more information.
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.
Table of Contents
- Approach
- Terminology
- Install the Required Libraries
- About the Data: Sentinel-2 True Color/Color IR
- Query the STAC API for Sentinel-2
- Fetch Imagery from Raster API for Sentinel-2
- Generate Map for Sentinel-2
- About the Data: Normalized Burn Ratio Difference (dNBR)
- Query the STAC API for dNBR
- Fetch Imagery from Raster API for dNBR
- Generate Map for dNBR
- About the Data: VEG-ANOM-MAX
- Query the STAC API Max Vegetation Anomaly
- Fetch Imagery from Raster API for Max Vegetation Anomoly
- Generate Map for Max Vegetation Anomoly
- Summary
NASA provided satellite imagery at the request of federal and state emergency management officials in response to the Trout Fire near Silver City, New Mexico, in late June 2025. Satellite images assist in search and rescue, evacuation planning, and understanding the scope and development of the fire as it was ongoing.
The Trout Fire was caused by a lightning strike, burned over 47,000 acres, prompted evacuations, and destroyed two homes.
In this notebook, we will explore Sentinel-2, Normalized Burn Ratio Difference (dNBR), and Opera Disturbance Alert datasets, and how they were used in this Disasters article to monitor the effects of wildfires.
Approach
- Identify available dates and temporal frequency of observations for collections pertaining to the NM wildfire event
- Pass the STAC item into raster API
collectionendpoint - We’ll visualize tiles for each of the times/dates of interest using
folium - We will repeat this process for three different satellite products to show the data capabilities available.
Terminology
Navigating data via the Disasters API, you will encounter terminology that is different from browsing in a typical filesystem. We’ll define some terms here which are used throughout this notebook. - catalog: All datasets available at the /stac endpoint - collection: A specific dataset, e.g. CarbonTracker-CH₄ Isotopic Methane Inverse Fluxes - item: One granule in the dataset, e.g. one monthly file of methane inverse fluxes - asset: A variable available within the granule, e.g. microbial, fossil, or pyrogenic methane fluxes - STAC API: SpatioTemporal Asset Catalogs - Endpoint for fetching metadata about available datasets - Raster API: Endpoint for fetching data itself, for imagery and statistics
Install the Required Libraries
Required libraries are pre-installed on the GHG Center Hub. If you need to run this notebook elsewhere, please install them with this line in a code cell:
%pip install requests folium pystac_client branca matplotlib --quiet
::: {#728bcdf2-f81c-4122-8808-cd46782153fa .cell execution_count=8}
``` {.python .cell-code}
#for querying
import requests
from pystac_client import Client
#for mapping
import folium
import folium.plugins
from folium.plugins import DualMap
from folium import Map, TileLayer
from branca.element import Template, MacroElement
import branca.colormap as cm
import matplotlib.cm as mpl_cm:::
About the Data: Sentinel-2 True Color/Color IR
The True Color RGB composite provides a product of how the surface would look to the naked eye from space. The RGB is created using the red, green, and blue channels of the respective instrument.
The Color Infrared composite is created using the near-infrared, red, and green channels, allowing for the ability to see areas impacted by the fires. The near-infrared gives the ability to see through thin clouds. Healthy vegetation is shown as red, water is in blue.
These data will allow us to view the burn scar caused by the fire and compare it to the pre-fire landscape.
Query the STAC API for Sentinel-2
# 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- type "Collection"
- id "sentinel-2-all-vars-daily"
- stac_version "1.1.0"
- description "Sentinel-2 is a multispectral optical imaging mission providing high-resolution imagery for disaster monitoring and environmental assessment. This collection includes multiple spectral band products and derived indices: true color and false color composites for visual analysis, NDVI for vegetation health monitoring, burn ratio for fire damage assessment, MNDWI for water detection, and cloud masks for data quality filtering."
links[] 5 items
0
- rel "items"
- href "https://dev.openveda.cloud/api/stac/collections/sentinel-2-all-vars-daily/items"
- type "application/geo+json"
1
- rel "parent"
- href "https://dev.openveda.cloud/api/stac/"
- type "application/json"
2
- rel "root"
- href "https://dev.openveda.cloud/api/stac"
- type "application/json"
- title "VEDA (Visualization, Exploration, and Data Analysis) STAC API"
3
- rel "self"
- href "https://dev.openveda.cloud/api/stac/collections/sentinel-2-all-vars-daily"
- type "application/json"
4
- rel "http://www.opengis.net/def/rel/ogc/1.0/queryables"
- href "https://dev.openveda.cloud/api/stac/collections/sentinel-2-all-vars-daily/queryables"
- type "application/schema+json"
- title "Queryables"
stac_extensions[] 3 items
- 0 "https://stac-extensions.github.io/render/v1.0.0/schema.json"
- 1 "https://stac-extensions.github.io/item-assets/v1.0.0/schema.json"
- 2 "https://stac-extensions.github.io/authentication/v1.1.0/schema.json"
tenant[] 1 items
- 0 "nasa-disasters"
renders
ndvi
assets[] 1 items
- 0 "ndvi"
- nodata 0
rescale[] 1 items
0[] 2 items
- 0 -1
- 1 1
mndwi
assets[] 1 items
- 0 "mndwi"
- nodata 999
rescale[] 1 items
0[] 2 items
- 0 -1
- 1 1
colorIR
bidx[] 3 items
- 0 1
- 1 2
- 2 3
assets[] 1 items
- 0 "colorIR"
- nodata 0
rescale[] 1 items
0[] 2 items
- 0 0
- 1 255
burnRatio
assets[] 1 items
- 0 "burnRatio"
- nodata -9999
rescale[] 1 items
0[] 2 items
- 0 -1
- 1 1
cloudMask
assets[] 1 items
- 0 "cloudMask"
- nodata 0
rescale[] 1 items
0[] 2 items
- 0 0
- 1 1
trueColor
bidx[] 3 items
- 0 1
- 1 2
- 2 3
assets[] 1 items
- 0 "trueColor"
- nodata 0
rescale[] 1 items
0[] 2 items
- 0 0
- 1 255
ndviChange
assets[] 1 items
- 0 "ndviChange"
- nodata 0
rescale[] 1 items
0[] 2 items
- 0 -1
- 1 1
shortwaveIR
bidx[] 3 items
- 0 1
- 1 2
- 2 3
assets[] 1 items
- 0 "shortwaveIR"
- nodata 0
rescale[] 1 items
0[] 2 items
- 0 0
- 1 255
naturalColor
bidx[] 3 items
- 0 1
- 1 2
- 2 3
assets[] 1 items
- 0 "naturalColor"
- nodata 0
rescale[] 1 items
0[] 2 items
- 0 0
- 1 255
item_assets
ndvi
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "NDVI"
- description "Normalized Difference Vegetation Index measuring vegetation health and density using near-infrared and red bands."
mndwi
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "MNDWI"
- description "Modified Normalized Difference Water Index for detecting and mapping surface water bodies and flood extent."
colorIR
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Color Infrared"
- description "False color infrared composite (NIR, Red, Green) emphasizing vegetation health and vigor in red tones."
burnRatio
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Burn Ratio"
- description "Normalized Burn Ratio (NBR) index highlighting burned areas by comparing near-infrared and shortwave infrared bands for fire damage assessment."
cloudMask
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Cloud Mask"
- description "Cloud and cloud shadow mask identifying pixels obscured by atmospheric conditions for data quality filtering."
trueColor
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "True Color"
- description "True color RGB composite using visible wavelengths for natural-looking imagery of Earth's surface."
ndviChange
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "NDVI Change"
- description "Normalized Difference Vegetation Index change between dates measuring vegetation health and density using near-infrared and red bands."
shortwaveIR
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Shortwave Infrared"
- description "Shortwave infrared composite highlighting moisture content and penetrating smoke for fire and drought monitoring."
naturalColor
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Natural Color"
- description "Natural color composite using visible bands (Red, Green, Blue) for intuitive visual interpretation of surface features."
- dashboard:is_periodic False
- dashboard:time_interval "P1D"
auth:schemes
oidc
- type "openIdConnect"
- openIdConnectUrl "https://keycloak.delta-backend.xyz/realms/veda/.well-known/openid-configuration"
- title "Sentinel-2 Daily"
extent
spatial
bbox[] 1 items
0[] 4 items
- 0 -119.1975416533983
- 1 -30.82134245583779
- 2 -49.85229056436889
- 3 38.84899835984862
temporal
interval[] 1 items
0[] 2 items
- 0 "2015-03-13T00:00:00Z"
- 1 "2025-07-17T00:00:00Z"
- license "CC0-1.0"
providers[] 1 items
0
- name "NASA"
summaries
By looking at the documentation for the Sentinel-2 imagery for this event we can see the range of dates that are of interest for this event.
# The search function lets you search for items within a specific date/time range
search = catalog.search(
collections=collection_name,
datetime=['2025-06-09T00:00:00Z','2025-06-29T00:00:00Z']
)
items = search.item_collection()
# Print how many items we found in our search
print(f"# items found: {len(items)}")# items found: 10
# Examine the first item in the collection
# Keep in mind that a list starts from 0, 1, 2... therefore items[0] is referring to the first item in the list/collection
items = search.item_collection()
items[0]- type "Feature"
- stac_version "1.1.0"
stac_extensions[] 2 items
- 0 "https://stac-extensions.github.io/raster/v1.1.0/schema.json"
- 1 "https://stac-extensions.github.io/projection/v2.0.0/schema.json"
- id "sentinel-2-2025-06-29"
geometry
- type "Polygon"
coordinates[] 1 items
0[] 5 items
0[] 2 items
- 0 -108.87342828381848
- 1 32.37738839044249
1[] 2 items
- 0 -106.97877645825533
- 1 32.37738839044249
2[] 2 items
- 0 -106.97877645825533
- 1 33.46711546874811
3[] 2 items
- 0 -108.87342828381848
- 1 33.46711546874811
4[] 2 items
- 0 -108.87342828381848
- 1 32.37738839044249
bbox[] 4 items
- 0 -108.87342828381848
- 1 32.37738839044249
- 2 -106.97877645825533
- 3 33.46711546874811
properties
- datetime "2025-06-29T00:00:00Z"
links[] 4 items
0
- rel "collection"
- href "https://dev.openveda.cloud/api/stac/collections/sentinel-2-all-vars-daily"
- type "application/json"
1
- rel "parent"
- href "https://dev.openveda.cloud/api/stac/collections/sentinel-2-all-vars-daily"
- type "application/json"
2
- rel "root"
- href "https://dev.openveda.cloud/api/stac"
- type "application/json"
- title "VEDA (Visualization, Exploration, and Data Analysis) STAC API"
3
- rel "self"
- href "https://dev.openveda.cloud/api/stac/collections/sentinel-2-all-vars-daily/items/sentinel-2-2025-06-29"
- type "application/geo+json"
assets
colorIR
- href "s3://nasa-disasters/drcs_activations_new/Sentinel-2/colorIR/202506_Fire_NM_SilverCity_S2C_colorInfrared_2025-06-29_day.tif"
- type "image/tiff; application=geotiff"
- title "Color Infrared"
- description "False color infrared composite (NIR, Red, Green) emphasizing vegetation health and vigor in red tones."
proj:bbox[] 4 items
- 0 -108.87342828381848
- 1 32.37738839044249
- 2 -106.97877645825533
- 3 33.46711546874811
- proj:epsg 4326
proj:shape[] 2 items
- 0 10725
- 1 18647
raster:bands[] 3 items
0
- scale 1.0
- nodata 0.0
- offset 0.0
- sampling "area"
- data_type "uint8"
histogram
- max 255.0
- min 29.0
- count 11
buckets[] 10 items
- 0 1563
- 1 3770
- 2 3233
- 3 4626
- 4 33247
- 5 178749
- 6 171022
- 7 80871
- 8 24883
- 9 28600
statistics
- mean 172.690855391621
- stddev 31.398073897514557
- maximum 255
- minimum 29
- valid_percent 87.9675562393888
1
- scale 1.0
- nodata 0.0
- offset 0.0
- sampling "area"
- data_type "uint8"
histogram
- max 255.0
- min 24.0
- count 11
buckets[] 10 items
- 0 3706
- 1 6513
- 2 36476
- 3 94506
- 4 141958
- 5 136890
- 6 70575
- 7 13186
- 8 2727
- 9 24027
statistics
- mean 139.08797053701343
- stddev 38.417676022316265
- maximum 255
- minimum 24
- valid_percent 87.9675562393888
2
- scale 1.0
- nodata 0.0
- offset 0.0
- sampling "area"
- data_type "uint8"
histogram
- max 255.0
- min 25.0
- count 11
buckets[] 10 items
- 0 4012
- 1 8393
- 2 97965
- 3 213189
- 4 159128
- 5 16947
- 6 2734
- 7 2015
- 8 2228
- 9 23953
statistics
- mean 115.65447900724511
- stddev 36.66006012155539
- maximum 255
- minimum 25
- valid_percent 87.9675562393888
proj:geometry
- type "Polygon"
coordinates[] 1 items
0[] 5 items
0[] 2 items
- 0 -108.87342828381848
- 1 32.37738839044249
1[] 2 items
- 0 -106.97877645825533
- 1 32.37738839044249
2[] 2 items
- 0 -106.97877645825533
- 1 33.46711546874811
3[] 2 items
- 0 -108.87342828381848
- 1 33.46711546874811
4[] 2 items
- 0 -108.87342828381848
- 1 32.37738839044249
proj:transform[] 9 items
- 0 0.00010160625438747018
- 1 0.0
- 2 -108.87342828381848
- 3 0.0
- 4 -0.00010160625438747032
- 5 33.46711546874811
- 6 0.0
- 7 0.0
- 8 1.0
roles[] 2 items
- 0 "data"
- 1 "layer"
trueColor
- href "s3://nasa-disasters/drcs_activations_new/Sentinel-2/trueColor/202506_Fire_NM_SilverCity_S2C_trueColor_2025-06-29_day.tif"
- type "image/tiff; application=geotiff"
- title "True Color"
- description "True color RGB composite using visible wavelengths for natural-looking imagery of Earth's surface."
proj:bbox[] 4 items
- 0 -108.87342828381848
- 1 32.3773883904425
- 2 -106.97877645825533
- 3 33.46711546874811
- proj:epsg 4326
proj:shape[] 2 items
- 0 10725
- 1 18647
raster:bands[] 3 items
0
- scale 1.0
- nodata 0.0
- offset 0.0
- sampling "area"
- data_type "uint8"
histogram
- max 255.0
- min 48.0
- count 11
buckets[] 10 items
- 0 3608
- 1 5340
- 2 8652
- 3 22687
- 4 39202
- 5 58012
- 6 79236
- 7 88450
- 8 79517
- 9 144641
statistics
- mean 200.22185153349895
- stddev 44.6477179772496
- maximum 255
- minimum 48
- valid_percent 87.76544593590832
1
- scale 1.0
- nodata 0.0
- offset 0.0
- sampling "area"
- data_type "uint8"
histogram
- max 255.0
- min 45.0
- count 11
buckets[] 10 items
- 0 3125
- 1 4555
- 2 11395
- 3 49906
- 4 93135
- 5 126748
- 6 122520
- 7 66039
- 8 16783
- 9 35139
statistics
- mean 167.0375142865239
- stddev 37.391312765369086
- maximum 255
- minimum 45
- valid_percent 87.76544593590832
2
- scale 1.0
- nodata 0.0
- offset 0.0
- sampling "area"
- data_type "uint8"
histogram
- max 255.0
- min 39.0
- count 11
buckets[] 10 items
- 0 3050
- 1 8287
- 2 39973
- 3 107656
- 4 173618
- 5 133135
- 6 25989
- 7 4992
- 8 1807
- 9 30838
statistics
- mean 141.70152547015653
- stddev 37.01677070914419
- maximum 255
- minimum 39
- valid_percent 87.76544593590832
proj:geometry
- type "Polygon"
coordinates[] 1 items
0[] 5 items
0[] 2 items
- 0 -108.87342828381848
- 1 32.3773883904425
1[] 2 items
- 0 -106.97877645825533
- 1 32.3773883904425
2[] 2 items
- 0 -106.97877645825533
- 1 33.46711546874811
3[] 2 items
- 0 -108.87342828381848
- 1 33.46711546874811
4[] 2 items
- 0 -108.87342828381848
- 1 32.3773883904425
proj:transform[] 9 items
- 0 0.00010160625438747006
- 1 0.0
- 2 -108.87342828381848
- 3 0.0
- 4 -0.00010160625438747006
- 5 33.46711546874811
- 6 0.0
- 7 0.0
- 8 1.0
roles[] 2 items
- 0 "data"
- 1 "layer"
- collection "sentinel-2-all-vars-daily"
# Restructure our items into a dictionary where keys are the datetime items
# Then we can query more easily by date/time, e.g. "2020"
items_dict = {item.properties["datetime"][:10]: item for item in items}Now we will look for the possible prodcuts to choose from under assets and make a variable to store the name.
asset_name = "colorIR" #or "trueColor"Fetch Imagery from Raster API for Sentinel-2
There are several dates in dates for this event, but by trial-and-error we can find pre-fire and post-fire images over the Trout Fire.
# Specify two date/times that you would like to visualize, using the format of items_dict.keys()
dates = ["2025-06-09", "2025-06-29"]# Extract collection name and item ID for the first date
observation_date_1 = items_dict[dates[0]]
collection_id = observation_date_1.collection_id
item_id = observation_date_1.id
# Select relevant asset (microbial CH4 emissions)
object = observation_date_1.assets[asset_name]
raster_bands = object.extra_fields.get("raster:bands", [{}])
# Print the raster bands' information
raster_bands[{'scale': 1.0,
'nodata': 0.0,
'offset': 0.0,
'sampling': 'area',
'data_type': 'uint8',
'histogram': {'max': 255.0,
'min': 31.0,
'count': 11,
'buckets': [1167,
2047,
1637,
1983,
26820,
174344,
172968,
101871,
30539,
17116]},
'statistics': {'mean': 174.80425718012714,
'stddev': 27.317252333798095,
'maximum': 255,
'minimum': 31,
'valid_percent': 87.95561863327674}},
{'scale': 1.0,
'nodata': 0.0,
'offset': 0.0,
'sampling': 'area',
'data_type': 'uint8',
'histogram': {'max': 255.0,
'min': 30.0,
'count': 11,
'buckets': [2540,
6060,
54441,
96029,
140881,
130869,
73605,
13715,
2127,
10225]},
'statistics': {'mean': 137.3293169359764,
'stddev': 33.809314113255354,
'maximum': 255,
'minimum': 30,
'valid_percent': 87.95561863327674}},
{'scale': 1.0,
'nodata': 0.0,
'offset': 0.0,
'sampling': 'area',
'data_type': 'uint8',
'histogram': {'max': 255.0,
'min': 30.0,
'count': 11,
'buckets': [2777,
11381,
131473,
208710,
147941,
13304,
2085,
1347,
1396,
10078]},
'statistics': {'mean': 112.29378576868265,
'stddev': 28.289548883427734,
'maximum': 255,
'minimum': 30,
'valid_percent': 87.95561863327674}}]
observation_date_1- type "Feature"
- stac_version "1.1.0"
stac_extensions[] 2 items
- 0 "https://stac-extensions.github.io/raster/v1.1.0/schema.json"
- 1 "https://stac-extensions.github.io/projection/v2.0.0/schema.json"
- id "sentinel-2-2025-06-09"
geometry
- type "Polygon"
coordinates[] 1 items
0[] 5 items
0[] 2 items
- 0 -108.87342891912581
- 1 32.37731504449202
1[] 2 items
- 0 -106.97869343172056
- 1 32.37731504449202
2[] 2 items
- 0 -106.97869343172056
- 1 33.46711546874811
3[] 2 items
- 0 -108.87342891912581
- 1 33.46711546874811
4[] 2 items
- 0 -108.87342891912581
- 1 32.37731504449202
bbox[] 4 items
- 0 -108.87342891912581
- 1 32.37731504449202
- 2 -106.97869343172056
- 3 33.46711546874811
properties
- datetime "2025-06-09T00:00:00Z"
links[] 4 items
0
- rel "collection"
- href "https://dev.openveda.cloud/api/stac/collections/sentinel-2-all-vars-daily"
- type "application/json"
1
- rel "parent"
- href "https://dev.openveda.cloud/api/stac/collections/sentinel-2-all-vars-daily"
- type "application/json"
2
- rel "root"
- href "https://dev.openveda.cloud/api/stac"
- type "application/json"
- title "VEDA (Visualization, Exploration, and Data Analysis) STAC API"
3
- rel "self"
- href "https://dev.openveda.cloud/api/stac/collections/sentinel-2-all-vars-daily/items/sentinel-2-2025-06-09"
- type "application/geo+json"
assets
colorIR
- href "s3://nasa-disasters/drcs_activations_new/Sentinel-2/colorIR/202506_Fire_NM_SilverCity_S2C_colorInfrared_2025-06-09_day.tif"
- type "image/tiff; application=geotiff"
- title "Color Infrared"
- description "False color infrared composite (NIR, Red, Green) emphasizing vegetation health and vigor in red tones."
proj:bbox[] 4 items
- 0 -108.87342891912581
- 1 32.37731504449202
- 2 -106.97869343172056
- 3 33.46711546874811
- proj:epsg 4326
proj:shape[] 2 items
- 0 10731
- 1 18657
raster:bands[] 3 items
0
- scale 1.0
- nodata 0.0
- offset 0.0
- sampling "area"
- data_type "uint8"
histogram
- max 255.0
- min 31.0
- count 11
buckets[] 10 items
- 0 1167
- 1 2047
- 2 1637
- 3 1983
- 4 26820
- 5 174344
- 6 172968
- 7 101871
- 8 30539
- 9 17116
statistics
- mean 174.80425718012714
- stddev 27.317252333798095
- maximum 255
- minimum 31
- valid_percent 87.95561863327674
1
- scale 1.0
- nodata 0.0
- offset 0.0
- sampling "area"
- data_type "uint8"
histogram
- max 255.0
- min 30.0
- count 11
buckets[] 10 items
- 0 2540
- 1 6060
- 2 54441
- 3 96029
- 4 140881
- 5 130869
- 6 73605
- 7 13715
- 8 2127
- 9 10225
statistics
- mean 137.3293169359764
- stddev 33.809314113255354
- maximum 255
- minimum 30
- valid_percent 87.95561863327674
2
- scale 1.0
- nodata 0.0
- offset 0.0
- sampling "area"
- data_type "uint8"
histogram
- max 255.0
- min 30.0
- count 11
buckets[] 10 items
- 0 2777
- 1 11381
- 2 131473
- 3 208710
- 4 147941
- 5 13304
- 6 2085
- 7 1347
- 8 1396
- 9 10078
statistics
- mean 112.29378576868265
- stddev 28.289548883427734
- maximum 255
- minimum 30
- valid_percent 87.95561863327674
proj:geometry
- type "Polygon"
coordinates[] 1 items
0[] 5 items
0[] 2 items
- 0 -108.87342891912581
- 1 32.37731504449202
1[] 2 items
- 0 -106.97869343172056
- 1 32.37731504449202
2[] 2 items
- 0 -106.97869343172056
- 1 33.46711546874811
3[] 2 items
- 0 -108.87342891912581
- 1 33.46711546874811
4[] 2 items
- 0 -108.87342891912581
- 1 32.37731504449202
proj:transform[] 9 items
- 0 0.00010155627846948921
- 1 0.0
- 2 -108.87342891912581
- 3 0.0
- 4 -0.00010155627846948902
- 5 33.46711546874811
- 6 0.0
- 7 0.0
- 8 1.0
roles[] 2 items
- 0 "data"
- 1 "layer"
trueColor
- href "s3://nasa-disasters/drcs_activations_new/Sentinel-2/trueColor/202506_Fire_NM_SilverCity_S2C_trueColor_2025-06-09_day.tif"
- type "image/tiff; application=geotiff"
- title "True Color"
- description "True color RGB composite using visible wavelengths for natural-looking imagery of Earth's surface."
proj:bbox[] 4 items
- 0 -108.87342891912581
- 1 32.37731504449202
- 2 -106.97869343172056
- 3 33.46711546874811
- proj:epsg 4326
proj:shape[] 2 items
- 0 10731
- 1 18657
raster:bands[] 3 items
0
- scale 1.0
- nodata 0.0
- offset 0.0
- sampling "area"
- data_type "uint8"
histogram
- max 255.0
- min 51.0
- count 11
buckets[] 10 items
- 0 2225
- 1 3927
- 2 11078
- 3 29024
- 4 36704
- 5 56057
- 6 74439
- 7 87987
- 8 84284
- 9 143546
statistics
- mean 200.4881525721228
- stddev 44.00330264921176
- maximum 255
- minimum 51
- valid_percent 87.75317672962647
1
- scale 1.0
- nodata 0.0
- offset 0.0
- sampling "area"
- data_type "uint8"
histogram
- max 255.0
- min 48.0
- count 11
buckets[] 10 items
- 0 1942
- 1 2886
- 2 16216
- 3 62153
- 4 96732
- 5 127597
- 6 124850
- 7 62837
- 8 15478
- 9 18580
statistics
- mean 165.45196317198562
- stddev 34.129329160357095
- maximum 255
- minimum 48
- valid_percent 87.75317672962647
2
- scale 1.0
- nodata 0.0
- offset 0.0
- sampling "area"
- data_type "uint8"
histogram
- max 255.0
- min 42.0
- count 11
buckets[] 10 items
- 0 1964
- 1 9516
- 2 57189
- 3 118063
- 4 172531
- 5 119174
- 6 29610
- 7 4963
- 8 1601
- 9 14660
statistics
- mean 138.5040423525944
- stddev 31.33891173315184
- maximum 255
- minimum 42
- valid_percent 87.75317672962647
proj:geometry
- type "Polygon"
coordinates[] 1 items
0[] 5 items
0[] 2 items
- 0 -108.87342891912581
- 1 32.37731504449202
1[] 2 items
- 0 -106.97869343172056
- 1 32.37731504449202
2[] 2 items
- 0 -106.97869343172056
- 1 33.46711546874811
3[] 2 items
- 0 -108.87342891912581
- 1 33.46711546874811
4[] 2 items
- 0 -108.87342891912581
- 1 32.37731504449202
proj:transform[] 9 items
- 0 0.00010155627846948929
- 1 0.0
- 2 -108.87342891912581
- 3 0.0
- 4 -0.00010155627846948929
- 5 33.46711546874811
- 6 0.0
- 7 0.0
- 8 1.0
roles[] 2 items
- 0 "data"
- 1 "layer"
- collection "sentinel-2-all-vars-daily"
# Make a GET request to retrieve information for your first date/time
tile_pre = requests.get(
f"{RASTER_API_URL}/collections/{collection_id}/items/{item_id}/WebMercatorQuad/tilejson.json?"
f"&assets={asset_name}"
).json()
# Print the properties of the retrieved granule to the console
tile_pre{'tilejson': '2.2.0',
'version': '1.0.0',
'scheme': 'xyz',
'tiles': ['https://dev.openveda.cloud/api/raster/collections/sentinel-2-all-vars-daily/items/sentinel-2-2025-06-09/tiles/WebMercatorQuad/{z}/{x}/{y}@1x?assets=colorIR'],
'minzoom': 0,
'maxzoom': 24,
'bounds': [-108.87342891912581,
32.37731504449202,
-106.97869343172056,
33.46711546874811],
'center': [-107.92606117542319, 32.92221525662006, 0]}
# Repeat the above for your second date/time
observation_date_2 = items_dict[dates[1]]
# Extract collection name and item ID
collection_id = observation_date_2.collection_id
item_id = observation_date_2.id
# Make a GET request to retrieve information for your second date/time
tile_post = requests.get(
f"{RASTER_API_URL}/collections/{collection_id}/items/{item_id}/WebMercatorQuad/tilejson.json?"
f"&assets={asset_name}"
).json()
# Print the properties of the retrieved granule to the console
tile_post{'tilejson': '2.2.0',
'version': '1.0.0',
'scheme': 'xyz',
'tiles': ['https://dev.openveda.cloud/api/raster/collections/sentinel-2-all-vars-daily/items/sentinel-2-2025-06-29/tiles/WebMercatorQuad/{z}/{x}/{y}@1x?assets=colorIR'],
'minzoom': 0,
'maxzoom': 24,
'bounds': [-108.87342828381848,
32.37738839044249,
-106.97877645825533,
33.46711546874811],
'center': [-107.9261023710369, 32.922251929595305, 0]}
We will then use the tile URL prepared above to create a simple visualization for both time steps using folium. In the visualization you can zoom in and out of the map’s focus area and compare the burn scar to the pre-fire image side-by-side.
Generate Map for Sentinel-2
We will use the folium package to generate visualizations. folium allows the user to zoom in to see the high-resolution detail of the imagery. The following code block will plot both of our data onto a dual map and fit a title.
# Set initial zoom and map for Trout Fire
m = folium.plugins.DualMap(location=(32.97, -108.15), zoom_start=11)
# June 9 2025
map_layer_pre = TileLayer(
tiles=tile_pre["tiles"][0],
attr="VEDA",
opacity=0.8,
)
map_layer_pre.add_to(m.m1)
# June 29 2025
map_layer_post = TileLayer(
tiles=tile_post["tiles"][0],
attr="VEDA",
opacity=0.8,
)
map_layer_post.add_to(m.m2)
# Properly styled title overlay for DualMap
title_html = f'''
<div style="
position: fixed;
top: 75px; left: 0; width: 100%;
text-align: center;
font-size: 20px;
font-weight: bold;
background-color: rgba(255, 255, 255, 0.7);
padding: 5px;
z-index: 9999;
">
Sentinel-2 Imagery Pre Fire ({dates[0]}) and Post Fire ({dates[1]})
</div>
'''
m.get_root().html.add_child(folium.Element(title_html))
mFollowing the same process, we will review visualizing imagery from two more STAC collections. First, we will explore dNBR.
About the Data: Normalized Burn Ratio Difference (dNBR)
NBR is defined mathematically as (NIR – SWIR)/(NIR + SWIR) where NIR is near-infrared and SWIR is short-wave infrared. dNBR is computed by the difference between the pre-fire NBR and the post-fire NBR. NBR is commonly used as a proxy to indicate areas which have charred vegetation. Darker areas (more negative values) in the NBR image more strongly represent the presence of burned vegetation. Since the dNBR considers the condition of the scene before the fire occurred, the resulting value has been used as a proxy for burn severity. Higher dNBR values represent a proxy for greater burn severity. Negative dNBR values may represent a re-greening of or growth of vegetation in between pre and post imagery.
More information on dNBR can be found here: https://un-spider.org/advisory-support/recommended-practices/recommended-practice-burn-severity/in-detail/normalized-burn-ratio.
dNBR data may be computed while the fire is in progress. This is intentionally done to prioritize rapid data availability for proactive disaster response but means data can change over the course of the fire.
dNBR is produced by NASA’s Observational Products for End-Users from Remote Sensing Analysis (OPERA) program, which generates surface products derived from satellite data. Therefore, dNBR data will be found in an opera collection.
Query the STAC API for dNBR
# Fetch STAC collection
collection_name_opera_subdaily = "opera-all-vars-subdaily"
catalog = Client.open(STAC_API_URL)
collection = catalog.get_collection(collection_name_opera_subdaily)
# Print the properties of the collection to the console
collection- type "Collection"
- id "opera-all-vars-subdaily"
- stac_version "1.1.0"
- description "OPERA (Observational Products for End-Users from Remote Sensing Analysis) provides subdaily Earth observation products for disaster monitoring. This collection includes surface water extent mapping from SAR and HLS sources, and burn severity assessment using delta Normalized Burn Ratio for rapid fire response and flood monitoring."
links[] 5 items
0
- rel "items"
- href "https://dev.openveda.cloud/api/stac/collections/opera-all-vars-subdaily/items"
- type "application/geo+json"
1
- rel "parent"
- href "https://dev.openveda.cloud/api/stac/"
- type "application/json"
2
- rel "root"
- href "https://dev.openveda.cloud/api/stac"
- type "application/json"
- title "VEDA (Visualization, Exploration, and Data Analysis) STAC API"
3
- rel "self"
- href "https://dev.openveda.cloud/api/stac/collections/opera-all-vars-subdaily"
- type "application/json"
4
- rel "http://www.opengis.net/def/rel/ogc/1.0/queryables"
- href "https://dev.openveda.cloud/api/stac/collections/opera-all-vars-subdaily/queryables"
- type "application/schema+json"
- title "Queryables"
stac_extensions[] 3 items
- 0 "https://stac-extensions.github.io/render/v1.0.0/schema.json"
- 1 "https://stac-extensions.github.io/item-assets/v1.0.0/schema.json"
- 2 "https://stac-extensions.github.io/authentication/v1.1.0/schema.json"
tenant[] 1 items
- 0 "nasa-disasters"
renders
WTR
assets[] 1 items
- 0 "WTR"
rescale[] 1 items
0[] 2 items
- 0 0
- 1 255
dnbr
assets[] 1 items
- 0 "dnbr"
rescale[] 1 items
0[] 2 items
- 0 -1
- 1 1
hls-DSWx
assets[] 1 items
- 0 "hls-DSWx"
rescale[] 1 items
0[] 2 items
- 0 0
- 1 255
item_assets
WTR
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Water Classification"
- description "Surface water extent classification layer derived from SAR or optical imagery."
dnbr
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Delta Normalized Burn Ratio"
- description "Delta Normalized Burn Ratio showing burn severity from pre- and post-fire imagery comparison."
hls-DSWx
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "HLS Dynamic Surface Water Extent"
- description "HLS Dynamic Surface Water Extent product showing water presence and classification."
- dashboard:is_periodic False
- dashboard:time_interval "P1S"
auth:schemes
oidc
- type "openIdConnect"
- openIdConnectUrl "https://keycloak.delta-backend.xyz/realms/veda/.well-known/openid-configuration"
- title "OPERA All Variables Subdaily"
extent
spatial
bbox[] 1 items
0[] 4 items
- 0 -124.13852907879547
- 1 -32.71812239144448
- 2 -49.82862066116967
- 3 37.94758795791628
temporal
interval[] 1 items
0[] 2 items
- 0 "2024-01-25T02:07:15Z"
- 1 "2025-06-22T17:45:00Z"
- license "CC0-1.0"
providers[] 1 items
0
- name "NASA Disasters Program"
roles[] 1 items
- 0 "host"
- url "https://disasters.openveda.cloud"
summaries
datetime[] 9 items
- 0 "2024-01-25T02:07:15Z"
- 1 "2024-01-25T14:08:49Z"
- 2 "2024-02-06T02:07:15Z"
- 3 "2024-02-06T13:43:47Z"
- 4 "2024-04-21T13:31:51Z"
- 5 "2024-05-18T13:22:31Z"
- 6 "2024-05-21T13:31:51Z"
- 7 "2025-06-21T18:05:00Z"
- 8 "2025-06-22T17:45:00Z"
# The search function lets you search for items within a specific date/time range
search = catalog.search(
collections=collection_name_opera_subdaily,
datetime=['2025-06-09T00:00:00Z','2025-06-29T00:00:00Z']
)
items = search.item_collection()
# Print how many items we found in our search
print(f"# items found: {len(items)}")# items found: 2
# Examine the first item in the collection
# Keep in mind that a list starts from 0, 1, 2... therefore items[0] is referring to the first item in the list/collection
items = search.item_collection()
items[0]- type "Feature"
- stac_version "1.1.0"
stac_extensions[] 2 items
- 0 "https://stac-extensions.github.io/raster/v1.1.0/schema.json"
- 1 "https://stac-extensions.github.io/projection/v2.0.0/schema.json"
- id "opera-2025-06-22T17:45:00"
geometry
- type "Polygon"
coordinates[] 1 items
0[] 5 items
0[] 2 items
- 0 -108.23813471556193
- 1 32.87367428279499
1[] 2 items
- 0 -108.01506342300563
- 1 32.87367428279499
2[] 2 items
- 0 -108.01506342300563
- 1 33.04157419951851
3[] 2 items
- 0 -108.23813471556193
- 1 33.04157419951851
4[] 2 items
- 0 -108.23813471556193
- 1 32.87367428279499
bbox[] 4 items
- 0 -108.23813471556193
- 1 32.87367428279499
- 2 -108.01506342300563
- 3 33.04157419951851
properties
- datetime "2025-06-22T17:45:00Z"
links[] 4 items
0
- rel "collection"
- href "https://dev.openveda.cloud/api/stac/collections/opera-all-vars-subdaily"
- type "application/json"
1
- rel "parent"
- href "https://dev.openveda.cloud/api/stac/collections/opera-all-vars-subdaily"
- type "application/json"
2
- rel "root"
- href "https://dev.openveda.cloud/api/stac"
- type "application/json"
- title "VEDA (Visualization, Exploration, and Data Analysis) STAC API"
3
- rel "self"
- href "https://dev.openveda.cloud/api/stac/collections/opera-all-vars-subdaily/items/opera-2025-06-22T17:45:00"
- type "application/geo+json"
assets
dnbr
- href "s3://nasa-disasters/drcs_activations_new/OPERA/dnbr/202506_Fire_NM_dnbr_57334_2025-06-22T17:45:00Z.tif"
- type "image/tiff; application=geotiff"
- title "Delta Normalized Burn Ratio"
- description "Delta Normalized Burn Ratio showing burn severity from pre- and post-fire imagery comparison."
proj:bbox[] 4 items
- 0 -108.23813471556193
- 1 32.87367428279499
- 2 -108.01506342300563
- 3 33.04157419951851
- proj:epsg 4326
proj:shape[] 2 items
- 0 563
- 1 748
raster:bands[] 1 items
0
- scale 1.0
- nodata -9999.0
- offset 0.0
- sampling "area"
- data_type "float64"
histogram
- max 0.8671119969273169
- min -0.2561326292706968
- count 11
buckets[] 10 items
- 0 5
- 1 18181
- 2 254157
- 3 100968
- 4 34731
- 5 10027
- 6 2388
- 7 409
- 8 19
- 9 13
statistics
- mean 0.07357712462488378
- stddev 0.09410669164114314
- maximum 0.8671119969273169
- minimum -0.2561326292706968
- valid_percent 99.94633409637068
proj:geometry
- type "Polygon"
coordinates[] 1 items
0[] 5 items
0[] 2 items
- 0 -108.23813471556193
- 1 32.87367428279499
1[] 2 items
- 0 -108.01506342300563
- 1 32.87367428279499
2[] 2 items
- 0 -108.01506342300563
- 1 33.04157419951851
3[] 2 items
- 0 -108.23813471556193
- 1 33.04157419951851
4[] 2 items
- 0 -108.23813471556193
- 1 32.87367428279499
proj:transform[] 9 items
- 0 0.00029822365315012515
- 1 0.0
- 2 -108.23813471556193
- 3 0.0
- 4 -0.00029822365315012515
- 5 33.04157419951851
- 6 0.0
- 7 0.0
- 8 1.0
roles[] 2 items
- 0 "data"
- 1 "layer"
- collection "opera-all-vars-subdaily"
# Restructure our items into a dictionary where keys are the datetime items
# Then we can query more easily by date/time, e.g. "2020"
items_dict = {item.properties["datetime"][:10]: item for item in items}asset_name = "dnbr"Fetch Imagery from Raster API for dNBR
I will choose one of the two dates in dates to visualize.
# Specify date that you would like to visualize, using the format of items_dict.keys()
date = "2025-06-21"This time, we will look at the rescale values of dNBR to adjust our colormap. dNBR ranges from values of -1 to 1, with more positive values indicating more severe burns.
# Extract collection name and item ID for the first date
observation_date = items_dict[date]
collection_id = observation_date.collection_id
item_id = observation_date.id
# Select relevant asset (microbial CH4 emissions)
object = observation_date.assets[asset_name]
raster_bands = object.extra_fields.get("raster:bands", [{}])
# Print the raster bands' information
raster_bands[{'scale': 1.0,
'nodata': -9999.0,
'offset': 0.0,
'sampling': 'area',
'data_type': 'float64',
'histogram': {'max': 0.9816958355058067,
'min': -0.1365383543458513,
'count': 11,
'buckets': [8923, 258816, 109761, 30963, 7436, 1582, 328, 53, 50, 19]},
'statistics': {'mean': 0.08097275888625194,
'stddev': 0.0855684493119729,
'maximum': 0.9816958355058067,
'minimum': -0.1365383543458513,
'valid_percent': 99.24179101642272}}]
#Generate an appropriate color bar range.
rescale_values = {
"max": raster_bands[0]['statistics']['maximum'],
"min": raster_bands[0]['statistics']['minimum'],
}
print(rescale_values){'max': 0.9816958355058067, 'min': -0.1365383543458513}
# Choose a colormap for displaying the data
# Make sure to capitalize per Matplotlib standard colormap names
# For more information on Colormaps in Matplotlib, please visit https://matplotlib.org/stable/users/explain/colors/colormaps.html
color_map = "inferno"# Make a GET request to retrieve information for your first date/time
observation_tile = requests.get(
f"{RASTER_API_URL}/collections/{collection_id}/items/{item_id}/WebMercatorQuad/tilejson.json?"
f"&assets={asset_name}"
f"&color_formula=gamma+r+1.05&colormap_name={color_map.lower()}"
f"&rescale={rescale_values['min']},{rescale_values['max']}"
).json()
# Print the properties of the retrieved granule to the console
observation_tile{'tilejson': '2.2.0',
'version': '1.0.0',
'scheme': 'xyz',
'tiles': ['https://dev.openveda.cloud/api/raster/collections/opera-all-vars-subdaily/items/opera-2025-06-21T18:05:00/tiles/WebMercatorQuad/{z}/{x}/{y}@1x?assets=dnbr&color_formula=gamma+r+1.05&colormap_name=inferno&rescale=-0.1365383543458513%2C0.9816958355058067'],
'minzoom': 0,
'maxzoom': 24,
'bounds': [-108.23813471556193,
32.87367428279499,
-108.01506342300563,
33.04157419951851],
'center': [-108.12659906928377, 32.95762424115675, 0]}
Generate Map for dNBR
We will use the folium package once again, but this time we also add code to generate a colorbar.
# --- Create the DualMap ---
m = Map(
tiles="OpenStreetMap",
location=[
32.97,
-108.15,
],
zoom_start=12,
)
map_layer = TileLayer(
tiles=observation_tile["tiles"][0],
attr="VEDA",
opacity=0.6,
)
map_layer.add_to(m)
# --- Add title ---
title_html = f'''
<div style="
position: fixed;
top: 75px; left: 0; width: 100%;
text-align: center;
font-size: 20px;
font-weight: bold;
background-color: rgba(255, 255, 255, 0.7);
padding: 5px;
z-index: 9999;
">
Burn Severity Map (dNBR) on {date}
</div>
'''
m.get_root().html.add_child(folium.Element(title_html))
# Get the matplotlib colormap (same as your API color_map)
mpl_colormap = mpl_cm.get_cmap(color_map.lower())
# Create a Branca LinearColormap using the same range
colormap = cm.LinearColormap(
colors=[mpl_colormap(i) for i in range(mpl_colormap.N)],
vmin=rescale_values['min'],
vmax=rescale_values['max']
)
colormap.caption = "dNBR"
# --- Use to_step() to get stable HTML ---
colormap_step = colormap.to_step(n=50)
colorbar_html = colormap_step._repr_html_()
# --- Wrap and fix position (bottom-left) ---
fixed_colorbar = f'''
<div style="
position: fixed;
bottom: 30px;
left: 30px;
width: 220px;
z-index: 9999;
">
{colorbar_html}
</div>
'''
m.get_root().html.add_child(folium.Element(fixed_colorbar))
m/var/folders/jq/m05tkv2d1llbn0w9wc6cltyr0000gn/T/ipykernel_90434/1287173695.py:37: MatplotlibDeprecationWarning: The get_cmap function was deprecated in Matplotlib 3.7 and will be removed in 3.11. Use ``matplotlib.colormaps[name]`` or ``matplotlib.colormaps.get_cmap()`` or ``pyplot.get_cmap()`` instead.
mpl_colormap = mpl_cm.get_cmap(color_map.lower())
About the Data: VEG-ANOM-MAX
Finally, we will explore changes in vegetation cover.
VEG-ANOM-MAX is derived from OPERA Disturbance Alert - Harmonized Landsat Sentinel-2 data. It measures the difference between historical and current year observed vegetation cover at the date of maximum decrease (vegetation loss of 0-100%). This layer can be used to threshold vegetation disturbance per a given sensitivity (e.g. disturbance of >=20% vegetation cover loss). The sum of the historical percent vegetation and the anomaly value will be the vegetation cover estimate for the current year.
The process for visualization will exactly follow prior examples.
Query the STAC API for Max Vegetation Anomaly
# Fetch the collection from the STAC API
collection_name_opera_daily = "opera-all-vars-daily"
catalog = Client.open(STAC_API_URL)
collection = catalog.get_collection(collection_name_opera_daily)
# Print the properties of the collection to the console
collection- type "Collection"
- id "opera-all-vars-daily"
- stac_version "1.1.0"
- description "OPERA (Observational Products for End-Users from Remote Sensing Analysis) provides multiple Earth observation products including InSAR displacement measurements, vegetation disturbance alerts, and surface water extent mapping. This collection combines OPERA-InSAR displacement products (unwrapped/wrapped phase, azimuth and range offsets), ARIA OPERA vegetation disturbance status and anomaly products derived from Sentinel-2, and HLS-derived dynamic surface water extent products for comprehensive land surface monitoring."
links[] 5 items
0
- rel "items"
- href "https://dev.openveda.cloud/api/stac/collections/opera-all-vars-daily/items"
- type "application/geo+json"
1
- rel "parent"
- href "https://dev.openveda.cloud/api/stac/"
- type "application/json"
2
- rel "root"
- href "https://dev.openveda.cloud/api/stac"
- type "application/json"
- title "VEDA (Visualization, Exploration, and Data Analysis) STAC API"
3
- rel "self"
- href "https://dev.openveda.cloud/api/stac/collections/opera-all-vars-daily"
- type "application/json"
4
- rel "http://www.opengis.net/def/rel/ogc/1.0/queryables"
- href "https://dev.openveda.cloud/api/stac/collections/opera-all-vars-daily/queryables"
- type "application/schema+json"
- title "Queryables"
stac_extensions[] 3 items
- 0 "https://stac-extensions.github.io/render/v1.0.0/schema.json"
- 1 "https://stac-extensions.github.io/item-assets/v1.0.0/schema.json"
- 2 "https://stac-extensions.github.io/authentication/v1.1.0/schema.json"
tenant[] 1 items
- 0 "nasa-disasters"
renders
AZ
assets[] 1 items
- 0 "AZ"
rescale[] 1 items
0[] 2 items
- 0 -10
- 1 72
RNG
assets[] 1 items
- 0 "RNG"
rescale[] 1 items
0[] 2 items
- 0 -15
- 1 15
UNW
assets[] 1 items
- 0 "UNW"
rescale[] 1 items
0[] 2 items
- 0 -1
- 1 1
WRP
assets[] 1 items
- 0 "WRP"
rescale[] 1 items
0[] 2 items
- 0 -3.14
- 1 3.14
hls-WTR
assets[] 1 items
- 0 "hls-WTR"
rescale[] 1 items
0[] 2 items
- 0 0
- 1 255
hls-BWTR
assets[] 1 items
- 0 "hls-BWTR"
rescale[] 1 items
0[] 2 items
- 0 0
- 1 255
hls-DWSx
assets[] 1 items
- 0 "hls-DWSx"
rescale[] 1 items
0[] 2 items
- 0 0
- 1 255
GEN-ANOM-MAX
assets[] 1 items
- 0 "GEN-ANOM-MAX"
rescale[] 1 items
0[] 2 items
- 0 0
- 1 100
- colormap_name "ylorrd"
VEG-ANOM-MAX
assets[] 1 items
- 0 "VEG-ANOM-MAX"
rescale[] 1 items
0[] 2 items
- 0 0
- 1 100
- colormap_name "ylorrd"
hls-FloodMap
assets[] 1 items
- 0 "hls-FloodMap"
rescale[] 1 items
0[] 2 items
- 0 0
- 1 1
hls-NoSnowIce
assets[] 1 items
- 0 "hls-NoSnowIce"
rescale[] 1 items
0[] 2 items
- 0 0
- 1 255
GEN-DIST-STATUS
assets[] 1 items
- 0 "GEN-DIST-STATUS"
rescale[] 1 items
0[] 2 items
- 0 0
- 1 255
- colormap_name "ylorrd"
VEG-DIST-STATUS
assets[] 1 items
- 0 "VEG-DIST-STATUS"
rescale[] 1 items
0[] 2 items
- 0 0
- 1 255
- colormap_name "ylorrd"
hls-changeMap-date1
assets[] 1 items
- 0 "hls-changeMap-date1"
rescale[] 1 items
0[] 2 items
- 0 0
- 1 1
hls-changeMap-date2
assets[] 1 items
- 0 "hls-changeMap-date2"
rescale[] 1 items
0[] 2 items
- 0 0
- 1 1
item_assets
AZ
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Azimuth Offset"
- description "Azimuth offset measurements detecting horizontal ground displacement perpendicular to satellite flight direction from InSAR processing."
RNG
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Range Offset"
- description "Range offset measurements detecting ground displacement parallel to satellite line-of-sight from InSAR processing."
UNW
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Unwrapped Phase"
- description "Unwrapped interferometric phase representing ground displacement measurements after phase ambiguity resolution."
WRP
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Wrapped Phase"
- description "Wrapped interferometric phase showing ground displacement within 2π phase cycles before unwrapping."
hls-WTR
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "HLS Water"
- description "HLS water classification layer."
hls-BWTR
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "HLS Binary Water"
- description "HLS binary water classification."
hls-DWSx
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "HLS Dynamic Surface Water Extent"
- description "HLS Dynamic Surface Water Extent product showing water presence and classification."
GEN-ANOM-MAX
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Sentinel-2 ARIA OPERA Disturbance Alert (GEN ANOM MAX)"
- description "Generic disturbance maximum anomaly value."
VEG-ANOM-MAX
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Sentinel-2 ARIA OPERA Disturbance Alert (VEG ANOM MAX)"
- description "S2 ARIA OPERA disturbance alert maximum vegetation anomaly value."
hls-FloodMap
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "HLS Flood Map"
- description "HLS-derived flood extent map identifying inundated areas."
hls-NoSnowIce
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "HLS Water No Snow/Ice"
- description "HLS water extent excluding snow and ice coverage."
GEN-DIST-STATUS
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Sentinel-2 ARIA OPERA Disturbance Alert (GEN DIST STATUS)"
- description "Generic disturbance status."
VEG-DIST-STATUS
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "Sentinel-2 ARIA OPERA Disturbance Alert (VEG DIST STATUS)"
- description "S2 ARIA OPERA vegetation disturbance status."
hls-changeMap-date1
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "HLS Change Map"
- description "HLS surface water change detection map showing changes in water extent between time periods."
hls-changeMap-date2
- type "image/tiff; application=geotiff; profile=cloud-optimized"
roles[] 2 items
- 0 "data"
- 1 "layer"
- title "HLS Change Map"
- description "HLS surface water change detection map showing changes in water extent between time periods."
- dashboard:is_periodic False
- dashboard:time_interval "P1D"
auth:schemes
oidc
- type "openIdConnect"
- openIdConnectUrl "https://keycloak.delta-backend.xyz/realms/veda/.well-known/openid-configuration"
- title "OPERA All Variables Daily"
extent
spatial
bbox[] 1 items
0[] 4 items
- 0 -127.047501638
- 1 -32.66765099343841
- 2 92.1772250157607
- 3 42.5476
temporal
interval[] 1 items
0[] 2 items
- 0 "2023-06-12T00:00:00Z"
- 1 "2025-07-11T00:00:00Z"
- license "CC0-1.0"
providers[] 1 items
0
- name "NASA Disasters Program"
roles[] 1 items
- 0 "host"
- url "https://disasters.openveda.cloud"
summaries
# The search function lets you search for items within a specific date/time range
search = catalog.search(
collections=collection_name_opera_daily,
datetime=['2025-06-09T00:00:00Z','2025-06-29T00:00:00Z']
)
items = search.item_collection()
# Print how many items we found in our search
print(f"# items found: {len(items)}")# items found: 4
# Examine the first item in the collection
# Keep in mind that a list starts from 0, 1, 2... therefore items[0] is referring to the first item in the list/collection
items = search.item_collection()
items[0]- type "Feature"
- stac_version "1.1.0"
stac_extensions[] 2 items
- 0 "https://stac-extensions.github.io/raster/v1.1.0/schema.json"
- 1 "https://stac-extensions.github.io/projection/v2.0.0/schema.json"
- id "opera-2025-06-28"
geometry
- type "Polygon"
coordinates[] 1 items
0[] 5 items
0[] 2 items
- 0 -107.17378006048253
- 1 31.529271747816416
1[] 2 items
- 0 -103.80645672940621
- 1 31.529271747816416
2[] 2 items
- 0 -103.80645672940621
- 1 34.34166257051598
3[] 2 items
- 0 -107.17378006048253
- 1 34.34166257051598
4[] 2 items
- 0 -107.17378006048253
- 1 31.529271747816416
bbox[] 4 items
- 0 -107.17378006048253
- 1 31.529271747816416
- 2 -103.80645672940621
- 3 34.34166257051598
properties
- datetime "2025-06-28T00:00:00Z"
links[] 4 items
0
- rel "collection"
- href "https://dev.openveda.cloud/api/stac/collections/opera-all-vars-daily"
- type "application/json"
1
- rel "parent"
- href "https://dev.openveda.cloud/api/stac/collections/opera-all-vars-daily"
- type "application/json"
2
- rel "root"
- href "https://dev.openveda.cloud/api/stac"
- type "application/json"
- title "VEDA (Visualization, Exploration, and Data Analysis) STAC API"
3
- rel "self"
- href "https://dev.openveda.cloud/api/stac/collections/opera-all-vars-daily/items/opera-2025-06-28"
- type "application/geo+json"
assets
hls-BWTR
- href "s3://nasa-disasters/drcs_activations_new/OPERA/dswx/202506_Fire_NM_OPERA_DSWx-HLS_V1_BWTR_2025-06-28_day.tif"
- type "image/tiff; application=geotiff"
- title "HLS Binary Water"
- description "HLS binary water classification."
proj:bbox[] 4 items
- 0 -107.17378006048253
- 1 31.529271747816416
- 2 -103.80645672940621
- 3 34.34166257051598
- proj:epsg 4326
proj:shape[] 2 items
- 0 9467
- 1 11335
raster:bands[] 1 items
0
- scale 1.0
- nodata 0.0
- offset 0.0
- sampling "area"
- data_type "uint8"
histogram
- max 255.0
- min 1.0
- count 11
buckets[] 10 items
- 0 988
- 1 0
- 2 0
- 3 0
- 4 0
- 5 0
- 6 0
- 7 0
- 8 0
- 9 380803
statistics
- mean 252.5171599120985
- stddev 12.82359227869026
- maximum 255
- minimum 1
- valid_percent 43.55639876606308
proj:geometry
- type "Polygon"
coordinates[] 1 items
0[] 5 items
0[] 2 items
- 0 -107.17378006048253
- 1 31.529271747816416
1[] 2 items
- 0 -103.80645672940621
- 1 31.529271747816416
2[] 2 items
- 0 -103.80645672940621
- 1 34.34166257051598
3[] 2 items
- 0 -107.17378006048253
- 1 34.34166257051598
4[] 2 items
- 0 -107.17378006048253
- 1 31.529271747816416
proj:transform[] 9 items
- 0 0.0002970730772894858
- 1 0.0
- 2 -107.17378006048253
- 3 0.0
- 4 -0.0002970730772894858
- 5 34.34166257051598
- 6 0.0
- 7 0.0
- 8 1.0
roles[] 2 items
- 0 "data"
- 1 "layer"
- collection "opera-all-vars-daily"
# Restructure our items into a dictionary where keys are the datetime items
# Then we can query more easily by date/time, e.g. "2020"
items_dict = {item.properties["datetime"][:10]: item for item in items}I will use the colormap under VEG-ANOM-MAX.
asset_name = "VEG-ANOM-MAX"Fetch Imagery from Raster API for Max Vegetation Anomoly
date = "2025-06-24"# Extract collection name and item ID for the first date
observation_date= items_dict[date]
collection_id = observation_date.collection_id
item_id = observation_date.id
# Select relevant asset (microbial CH4 emissions)
object = observation_date.assets[asset_name]
raster_bands = object.extra_fields.get("raster:bands", [{}])
# Print the raster bands' information
raster_bands[{'scale': 1.0,
'nodata': 0.0,
'offset': 0.0,
'sampling': 'area',
'data_type': 'uint8',
'histogram': {'max': 255.0,
'min': 10.0,
'count': 11,
'buckets': [3681, 653, 63, 3, 0, 0, 0, 0, 0, 55864]},
'statistics': {'mean': 238.1747477764503,
'stddev': 60.02815956239316,
'maximum': 255,
'minimum': 10,
'valid_percent': 5.792476624015748}}]
#Generate an appropriate color bar range.
rescale_values = {
"max": raster_bands[0]['statistics']['maximum'],
"min": raster_bands[0]['statistics']['minimum'],
}
print(rescale_values){'max': 255, 'min': 10}
# Choose a colormap for displaying the data
# Make sure to capitalize per Matplotlib standard colormap names
# For more information on Colormaps in Matplotlib, please visit https://matplotlib.org/stable/users/explain/colors/colormaps.html
color_map = "magma"For this situation, I am hard-coding the maximum rescale to 100, because the values above 100 (255) are erroneous (no data) values.
# Make a GET request to retrieve information for your first date/time
observation_tile = requests.get(
f"{RASTER_API_URL}/collections/{collection_id}/items/{item_id}/WebMercatorQuad/tilejson.json?"
f"&assets={asset_name}"
f"&color_formula=gamma+r+1.05&colormap_name={color_map.lower()}"
f"&rescale={rescale_values['min']},100"
).json()
# Print the properties of the retrieved granule to the console
observation_tile{'tilejson': '2.2.0',
'version': '1.0.0',
'scheme': 'xyz',
'tiles': ['https://dev.openveda.cloud/api/raster/collections/opera-all-vars-daily/items/opera-2025-06-24/tiles/WebMercatorQuad/{z}/{x}/{y}@1x?assets=VEG-ANOM-MAX&color_formula=gamma+r+1.05&colormap_name=magma&rescale=10%2C100'],
'minzoom': 0,
'maxzoom': 24,
'bounds': [-108.97616786143968,
31.475062809632632,
-107.00321128236091,
33.46381942656848],
'center': [-107.9896895719003, 32.469441118100555, 0]}
params = {
"assets": "VEG-ANOM-MAX",
"rescale": "0,100",
"colormap_name": "ylorrd"
}tile = requests.get(
f"{RASTER_API_URL}/collections/{collection_name_opera_daily}/items/{f'opera-{date}'}/WebMercatorQuad/tilejson.json?",
params=params,
).json()
tile{'tilejson': '2.2.0',
'version': '1.0.0',
'scheme': 'xyz',
'tiles': ['https://dev.openveda.cloud/api/raster/collections/opera-all-vars-daily/items/opera-2025-06-24/tiles/WebMercatorQuad/{z}/{x}/{y}@1x?assets=VEG-ANOM-MAX&rescale=0%2C100&colormap_name=ylorrd'],
'minzoom': 0,
'maxzoom': 24,
'bounds': [-108.97616786143968,
31.475062809632632,
-107.00321128236091,
33.46381942656848],
'center': [-107.9896895719003, 32.469441118100555, 0]}
Generate Map for Max Vegetation Anomoly
We will use the folium package once again with the same format at the dNBR visualization.
m = Map(
tiles="OpenStreetMap",
location=[
32.97,
-108.15,
],
zoom_start=12,
)
map_layer = TileLayer(
tiles=observation_tile["tiles"][0],
attr="VEDA",
opacity=0.6,
)
map_layer.add_to(m)
# --- Add title ---
title_html = f'''
<div style="
position: fixed;
top: 75px; left: 0; width: 100%;
text-align: center;
font-size: 20px;
font-weight: bold;
background-color: rgba(255, 255, 255, 0.7);
padding: 5px;
z-index: 9999;
">
Maximum Loss of Vegetation {date}
</div>
'''
m.get_root().html.add_child(folium.Element(title_html))
# Get the matplotlib colormap (same as your API color_map)
mpl_colormap = mpl_cm.get_cmap(color_map.lower())
# Create a Branca LinearColormap using the same range
colormap = cm.LinearColormap(
colors=[mpl_colormap(i) for i in range(mpl_colormap.N)],
vmin=rescale_values['min'],
vmax=100
)
colormap.caption = "Vegetation Loss (%)"
# --- Use to_step() to get stable HTML ---
colormap_step = colormap.to_step(n=50)
colorbar_html = colormap_step._repr_html_()
# --- Wrap and fix position (bottom-left) ---
fixed_colorbar = f'''
<div style="
position: fixed;
bottom: 30px;
left: 30px;
width: 220px;
z-index: 9999;
">
{colorbar_html}
</div>
'''
m.get_root().html.add_child(folium.Element(fixed_colorbar))
m/var/folders/jq/m05tkv2d1llbn0w9wc6cltyr0000gn/T/ipykernel_90434/3402120942.py:36: MatplotlibDeprecationWarning: The get_cmap function was deprecated in Matplotlib 3.7 and will be removed in 3.11. Use ``matplotlib.colormaps[name]`` or ``matplotlib.colormaps.get_cmap()`` or ``pyplot.get_cmap()`` instead.
mpl_colormap = mpl_cm.get_cmap(color_map.lower())
Some of the areas with the most significant vegetation loss line up with the areas of peak dNBR. This is one of the utilties of these data: you can draw connections between different datasets.
Summary
In this case study we have successfully visualized how NASA monitors wildfires with several satellite products observing the June 2025 Trout Fire in New Mexico. We demonstrated how to query the STAC collections and raster API to gather satellite imagery of a disaster. Using three satellite products, we could see areas that were ongoing signifcant burning (dNBR), areas that lost significant vegetation (VEG-ANOM-MAX), and what the burn scar looked like (Sentinel-2 color IR and true color). Using the various products, we can analyze how the fire was evolving and how it impacted the area, and how things like the areas of burning related to the lost vegetation spatially.