%pip install OWSLib==0.28.1 --quiet
Note: you may need to restart the kernel to use updated packages.
You can launch this notbook using mybinder, by clicking the button below.
OWSLib
to determine what data is available and inspect the metadataOWSLib
to filter and read the datageopandas
and folium
to analyze and plot the dataNote that the default examples environment is missing one requirement: oswlib
. We can pip install
that before we move on.
%pip install OWSLib==0.28.1 --quiet
Note: you may need to restart the kernel to use updated packages.
import datetime as dt
import geopandas as gpd
from owslib.ogcapi.features import Features
import requests
The fire data shown is generated by the FEDs algorithm. The FEDs algorithm tracks fire movement and severity by ingesting observations from the VIIRS thermal sensors on the Suomi NPP and NOAA-20 satellites. This algorithm uses raw VIIRS observations to generate a polygon of the fire, locations of the active fire line, and estimates of fire mean Fire Radiative Power (FRP). The VIIRS sensors overpass at ~1:30 AM and PM local time, and provide estimates of fire evolution ~ every 12 hours. The data produced by this algorithm describe where fires are in space and how fires evolve through time. This CONUS-wide implementation of the FEDs algorithm is based on Chen et al 2020’s algorithm for California.
The data produced by this algorithm is considered experimental.
The datasets that are distributed throught the OGC API are organized into collections. We can display the collections with the command:
= "https://openveda.cloud/api/features"
OGC_URL
= Features(url=OGC_URL)
w w.feature_collections()
['public.eis_fire_lf_perimeter_archive',
'public.eis_fire_lf_newfirepix_archive',
'public.eis_fire_lf_fireline_archive',
'public.eis_fire_lf_fireline_nrt',
'public.eis_fire_snapshot_fireline_nrt',
'public.eis_fire_snapshot_newfirepix_nrt',
'public.eis_fire_lf_newfirepix_nrt',
'public.eis_fire_lf_perimeter_nrt',
'public.eis_fire_snapshot_perimeter_nrt',
'public.st_subdivide',
'public.st_hexagongrid',
'public.st_squaregrid']
We will focus on the public.eis_fire_snapshot_fireline_nrt
collection, the public.eis_fire_snapshot_perimeter_nrt
collection, and the public.eis_fire_lf_perimeter_archive
collection here.
We can access information that describes the public.eis_fire_snapshot_perimeter_nrt
table.
= w.collection("public.eis_fire_snapshot_perimeter_nrt") perm
We are interested in accessing the queryable fields. Each of these fields will represent a column in our dataframe.
= w.collection_queryables("public.eis_fire_snapshot_perimeter_nrt")
perm_q "properties"] perm_q[
{'geometry': {'$ref': 'https://geojson.org/schema/Geometry.json'},
'duration': {'name': 'duration', 'type': 'number'},
'farea': {'name': 'farea', 'type': 'number'},
'fireid': {'name': 'fireid', 'type': 'number'},
'flinelen': {'name': 'flinelen', 'type': 'number'},
'fperim': {'name': 'fperim', 'type': 'number'},
'geom_counts': {'name': 'geom_counts', 'type': 'string'},
'isactive': {'name': 'isactive', 'type': 'number'},
'low_confidence_grouping': {'name': 'low_confidence_grouping',
'type': 'number'},
'meanfrp': {'name': 'meanfrp', 'type': 'number'},
'n_newpixels': {'name': 'n_newpixels', 'type': 'number'},
'n_pixels': {'name': 'n_pixels', 'type': 'number'},
'pixden': {'name': 'pixden', 'type': 'number'},
'primarykey': {'name': 'primarykey', 'type': 'string'},
'region': {'name': 'region', 'type': 'string'},
't': {'name': 't', 'type': 'string'}}
We also want to know the most recent data from the dataset. OWSLib doesn’t enable all of the same fields as the API does, so to find the time of the most recent data, we will parse some json from the API.
This is a URL call to the API: https://openveda.cloud/api/features/collections/public.eis_fire_lf_fireline_nrt/items?f=geojson&sortby=-t"
= "https://openveda.cloud/api/features/collections/public.eis_fire_lf_fireline_nrt/items?f=geojson&sortby=-t"
url_example = requests.get(url_example)
response = response.json()['features'][0]['properties']['t'] # Extracting the most recent time from the json dictionary.
most_recent_time most_recent_time
It is always a good idea to do any data filtering as early as possible. In this example we know that we want the data for particular spatial and temporal extents. We can apply those and other filters using the OWSLib
package.
In the below example we are:
public.eis_fire_snapshot_perimeter_nrt
collectionbbox
parameterdatetime
parameterfilter
parameter. The filter
parameter lets us filter by the columns in ‘public.eis_fire_snapshot_perimeter_nrt’ using SQL-style queries.NOTE: The limit
parameter desginates the maximum number of objects the query will return. The default limit is 10, so if we want to all of the fire perimeters within certain conditions, we need to make sure that the limit is large.
## Get 7 days before most recent fire perimeter
= most_recent_time + "+00:00"
most_recent_time = dt.datetime.strptime(most_recent_time, "%Y-%m-%dT%H:%M:%S+00:00")
now = now - dt.timedelta(weeks=1)
last_week = dt.datetime.strftime(last_week, "%Y-%m-%dT%H:%M:%S+00:00")
last_week print("Most Recent Time =", most_recent_time)
print("Last week =", last_week)
Most Recent Time = 2025-05-21T12:00:00+00:00
Last week = 2025-05-14T12:00:00+00:00
= w.collection_items(
perm_results "public.eis_fire_snapshot_perimeter_nrt", # name of the dataset we want
=["-106.8", "24.5", "-72.9", "37.3"], # coodrinates of bounding box,
bbox=[last_week + "/" + most_recent_time], # date range
datetime=1000, # max number of items returned
limitfilter="farea>5 AND duration>2", # additional filters based on queryable fields
)
The result is a dictionary containing all of the data and some summary fields. We can look at the keys to see what all is in there.
perm_results.keys()
dict_keys(['type', 'id', 'title', 'description', 'numberMatched', 'numberReturned', 'links', 'features'])
For instance you can check the total number of matched items and make sure that it is equal to the number of returned items. This is how you know that the limit
you defined above is high enough.
"numberMatched"] == perm_results["numberReturned"] perm_results[
True
You can also access the data directly in the browser or in an HTTP GET call using the constructed link.
"links"][1]["href"] perm_results[
'https://openveda.cloud/api/features/collections/public.eis_fire_snapshot_perimeter_nrt/items?bbox=-106.8%2C24.5%2C-72.9%2C37.3&datetime=2025-05-14T12%3A00%3A00%2B00%3A00%2F2025-05-21T12%3A00%3A00%2B00%3A00&limit=1000&filter=farea%3E5+AND+duration%3E2'
If we wanted to combine collections to make more informative analyses, we can use some of the same principles.
First we’ll get the queryable fields, and the extents:
= w.collection_queryables("public.eis_fire_snapshot_fireline_nrt")
fline_q = w.collection("public.eis_fire_snapshot_fireline_nrt")
fline_collection "properties"] fline_q[
{'geometry': {'$ref': 'https://geojson.org/schema/Geometry.json'},
'fireid': {'name': 'fireid', 'type': 'number'},
'mergeid': {'name': 'mergeid', 'type': 'number'},
'primarykey': {'name': 'primarykey', 'type': 'string'},
'region': {'name': 'region', 'type': 'string'},
't': {'name': 't', 'type': 'string'}}
Then we’ll use those fields to get most recent fire perimeters and fire lines.
= w.collection_items(
perm_results "public.eis_fire_snapshot_perimeter_nrt",
=most_recent_time,
datetime=1000,
limit
)= gpd.GeoDataFrame.from_features(perm_results["features"])
perimeters
## Get the most recent fire lines
= perimeters.fireid.unique()
perimeter_ids = ",".join(map(str, perimeter_ids))
perimeter_ids
= w.collection_items(
fline_results "public.eis_fire_snapshot_fireline_nrt",
=1000,
limitfilter="fireid IN ("
+ perimeter_ids
+ ")", # only the fires from the fire perimeter query above
)= gpd.GeoDataFrame.from_features(fline_results["features"]) fline
= perimeters.set_crs("epsg:4326")
perimeters = fline.set_crs("epsg:4326")
fline
= perimeters.explore(zoom_start=5, location=(41, -122))
m = fline.explore(m=m, color="orange")
m m
We may be interested in understanding how a fire evolved through time. To do this, we can work with the “Large fire” or “lf” perimeter collections. The public.eis_fire_lf_perimeter_nrt
collection has the full spread history of fires from this year. public.eis_fire_lf_perimeter_archive
has the full spread history of fires from 2018-2021 that were in the Western United States. The Camp Fire was in 2018, so we will work with the public.eis_fire_lf_perimeter_archive
collection.
We can start by querying with information specific to the Camp Fire, like it’s genreal region (Northern California), and when it was active (November 2018). With that information, we can get the fireID associated with the Camp Fire.
= w.collection_items(
perimeters_archive_results "public.eis_fire_lf_perimeter_archive",
=["-124.52", "39.2", "-120", "42"], # North California bounding box,
bbox=["2018-11-01T00:00:00+00:00/2018-11-30T12:00:00+00:00"],
datetime=3000,
limit
)
perimeters_archive_results
= gpd.GeoDataFrame.from_features(perimeters_archive_results["features"])
perimeters = perimeters.sort_values(by="t", ascending=False)
perimeters = perimeters.set_crs("epsg:4326")
perimeters
print(perimeters.fireid.unique())
= perimeters.explore(
m ={"fillOpacity": 0}, zoom_start=9, location=(39.7, -121.4)
style_kwds
) m
['F17028' 'F18493']
Based on the map, we know that the fireID for the Camp Fire is “F17028”. We can use that to directly query for that particular fire.
= w.collection_items(
perimeters_archive_results "public.eis_fire_lf_perimeter_archive",
filter="fireid = 'F17028'",
=["2018-01-01T00:00:00+00:00/2018-12-31T12:00:00+00:00"],
datetime=3000,
limit
)
perimeters_archive_results
= gpd.GeoDataFrame.from_features(perimeters_archive_results["features"])
perimeters = perimeters.sort_values(by="t", ascending=False)
perimeters = perimeters.set_crs("epsg:4326")
perimeters
= perimeters.explore(
m ={"fillOpacity": 0}, zoom_start=12, location=(39.7, -121.4)
style_kwds
) m