The National Weather Service (NWS) maintains flood stage thresholds for thousands of stream gages across the United States. These thresholds define when water levels become hazardous.
text
https://water.noaa.gov/resources/downloads/reports/nwps_all_gauges_report.csv
text
https://water.noaa.gov/gauges/<station_id>
Example: https://water.noaa.gov/gauges/04118105
CategoryCSV ColumnDescriptionAction Stageaction stageWater level requiring monitoring, preparation may be needed**Flood Stage (Minor)****flood stage****Minimal property damage, some public threat. Use this to determine if flooding occurred.**Moderate Flood Stagemoderate flood stageStructure inundation, evacuations may be neededMajor Flood Stagemajor flood stageExtensive damage, significant evacuations required
For general flood detection, use the flood stage column as the threshold.
python
import pandas as pd
import csv
import urllib.request
import io
nws_url = "https://water.noaa.gov/resources/downloads/reports/nwps_all_gauges_report.csv"
response = urllib.request.urlopen(nws_url)
content = response.read().decode('utf-8')
reader = csv.reader(io.StringIO(content))
headers = next(reader)
data = [row[:43] for row in reader] # Truncate to 43 columns
nws_df = pd.DataFrame(data, columns=headers)
The NWS CSV has a known issue: header row has 43 columns but data rows have 44 columns. Always truncate data rows to match header count:
python
data = [row[:43] for row in reader]
Column NameDescriptionusgs idUSGS station ID (8-digit string)location nameStation name/locationstateTwo-letter state codeaction stageAction threshold (feet)flood stageMinor flood threshold (feet)moderate flood stageModerate flood threshold (feet)major flood stageMajor flood threshold (feet)
Threshold columns need conversion from strings:
python
nws_df['flood stage'] = pd.to_numeric(nws_df['flood stage'], errors='coerce')
python
# Get stations for a specific state
state_stations = nws_df[
(nws_df['state'] == '<STATE_CODE>') &
(nws_df['usgs id'].notna()) &
(nws_df['usgs id'] != '') &
(nws_df['flood stage'].notna()) &
(nws_df['flood stage'] != -9999)
]
python
# Build a dictionary of station thresholds
station_ids = ['<id_1>', '<id_2>', '<id_3>']
thresholds = {}
for _, row in nws_df.iterrows():
usgs_id = str(row['usgs id']).strip()
if usgs_id in station_ids:
thresholds[usgs_id] = {
'name': row['location name'],
'flood': row['flood stage']
}
IssueCauseSolutionColumn mismatch errorCSV has 44 data columns but 43 headersTruncate rows to 43 columnsMissing thresholdsStation not in NWS databaseSkip station or use alternative sourceValue is -9999No threshold definedFilter out these valuesEmpty usgs idNWS-only stationFilter by usgs id != ''