US flood exposure & loss — NSI + FEMA — quickstart¶
This notebook shows the nsi backend end-to-end on its two fully-open sources:
structures— the USACE National Structure Inventory: building points with replacement values.nfip— the FEMA NFIP redacted claims (v3): observed flood-insurance losses.
A third source, nfhl (FEMA National Flood Hazard Layer), is shown as a snippet at the end — it needs a
reachable hazards.fema.gov and so is not executed here.
All three are keyless and in the US public domain. Every source requires a spatial or attribute bound — there is no unbounded national pull.
The three sources at a glance¶
Each source is selected with source= (or the nfip / nfhl facade aliases) and needs a bound — an
unbounded national pull is refused. The output shape is per source:
source |
Required bound | Output | Facade key |
|---|---|---|---|
structures |
fips= (2/5/11/15-digit) or lat_lim/lon_lim box |
vector FeatureCollection |
"nsi" (default) |
nfhl |
lat_lim/lon_lim box |
vector FeatureCollection |
"nfhl" |
nfip |
a filters= mapping (state / county / year / flood_event; max_records= caps it) |
tabular DataFrame |
"nfip" |
All three are keyless, US public domain, and US-only (a non-US request returns an empty result).
Setup¶
download() returns the result in the shape the source declares (OUTPUT_KIND per instance): a pyramids
FeatureCollection for structures, a pandas.DataFrame for nfip. Tabular results are also written to the
output directory.
import tempfile
import matplotlib.pyplot as plt
from earthlens.core import EarthLens
OUT = tempfile.mkdtemp() # nfip writes a CSV here
COUNTY = "22071" # Orleans Parish, Louisiana
1. Structures — the National Structure Inventory¶
We pull every structure in a single census tract (an 11-digit FIPS code). The result is a vector
FeatureCollection of building points, each carrying occupancy type, damage category, and replacement values.
structures = EarthLens(
"nsi", source="structures", fips="22071012700", path=OUT
).download()
print(type(structures).__name__, "with", len(structures), "buildings")
structures[
["occtype", "st_damcat", "val_struct", "val_cont", "found_type", "sqft"]
].head()
Each building has a point geometry and a replacement value. We colour the points by structure value to see how exposure is distributed across the tract.
gdf = structures
ax = gdf.plot(
column="val_struct", cmap="viridis", legend=True, markersize=8, figsize=(7, 6)
)
ax.set_title("NSI structure replacement value ($) — tract 22071012700")
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
plt.tight_layout()
plt.show()
The dollar exposure of the tract is just the sum of the structure (and contents) values — the quantity a flood-damage model multiplies by a depth-damage curve.
total_structure = gdf["val_struct"].sum()
total_contents = gdf["val_cont"].sum()
print(f"Total structure value: ${total_structure:,.0f}")
print(f"Total contents value: ${total_contents:,.0f}")
2. NFIP claims — observed flood losses¶
The FEMA NFIP redacted-claims v3 endpoint holds millions of flood-insurance claims. We bound the pull to one
county and loss year (and cap it), and get back a tabular DataFrame with friendly column names.
claims = EarthLens(
"nfip", filters={"county": COUNTY, "year": 2005}, max_records=50, path=OUT
).download()
print(type(claims).__name__, "with shape", claims.shape)
claims[
[
"date_of_loss",
"rated_flood_zone",
"building_paid",
"contents_paid",
"cause_of_damage",
]
].head()
How much was actually paid out? We sum the building and contents claim payments across the sampled claims.
paid = claims[["building_paid", "contents_paid"]].fillna(0).sum()
print(f"Building claims paid: ${paid['building_paid']:,.0f}")
print(f"Contents claims paid: ${paid['contents_paid']:,.0f}")
ax = claims["rated_flood_zone"].value_counts().head(10).plot.bar(figsize=(7, 4))
ax.set_title("NFIP 2005 claims by rated flood zone — Orleans Parish (sample)")
ax.set_xlabel("Rated flood zone")
ax.set_ylabel("Number of claims")
plt.tight_layout()
plt.show()
3. Flood zones — the FEMA National Flood Hazard Layer¶
The nfhl source returns the regulatory flood zones (FLD_ZONE, SFHA_TF) for a bounding box, as a vector
FeatureCollection. It is served from hazards.fema.gov, which is not reachable from every network, so it is
shown here as a snippet rather than executed:
zones = EarthLens(
"nfhl",
lat_lim=[29.95, 29.96],
lon_lim=[-90.07, -90.06],
).download()
zones[["FLD_ZONE", "SFHA_TF", "ZONE_SUBTY"]].head()
Overlaying the NSI structures on the NFHL zones (spatial join on FLD_ZONE) gives, per building, whether it sits
in a Special Flood Hazard Area — the exposure-in-hazard intersection that drives flood risk.
Summary¶
structures→ a vectorFeatureCollectionof NSI buildings with replacement values, bounded byfips=or a box.nfip→ a tabularDataFrameof FEMA flood-insurance claims, bounded bystate/county/year/flood_event.nfhl→ a vectorFeatureCollectionof FEMA flood zones, bounded by a box (needs a reachable network).
All three are US-only, keyless, public domain, and refuse aggregate= and unbounded pulls.