Skip to content
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
fc9aed8
added requirements file and made minor changes to aorc v1.1 notebook
Castronova Jun 3, 2025
d45f57d
adding sample data for the aorc notebook
Castronova Jun 3, 2025
ee5ca79
updates to ensure the aorc notebook is working on 2i2c
Castronova Jun 3, 2025
5117209
adding NGEN Hydrofabric tutorial
Castronova Jun 4, 2025
7e57ea3
adding initial version of NOAA NGEN notebook
Castronova Jun 4, 2025
bb2e23b
added initial version of bigquery NWM notebook
Castronova Jun 4, 2025
ceb0a5e
added ngen execution
Castronova Jun 5, 2025
014be5a
added forcing visualzation to ngiab notebook
Castronova Jun 5, 2025
de4d4fa
added results visualization
Castronova Jun 5, 2025
7946c8b
updated the image of usgs gage location
igarousi Jun 6, 2025
b8d067e
added an image of the models schematic
igarousi Jun 9, 2025
eb4f5f4
updated the descriptions and visualization
igarousi Jun 9, 2025
80aa1e4
updates to the big query notebook
Castronova Jun 9, 2025
f1fe1ee
Removed the FDC calculation and added BDC calculation
igarousi Jun 9, 2025
aabab8f
added images for the NWM analysis and forecast configurations
igarousi Jun 9, 2025
77f1977
revised the descriptive content, added new images, changed formats to…
igarousi Jun 9, 2025
68f8b8c
cleared outputs of all cells
igarousi Jun 9, 2025
a707044
modifications to bigquery and aorc notebooks for si
Castronova Jun 11, 2025
fea7626
removed debug statement from hydrofabric sidecar app
Castronova Jun 11, 2025
1737712
added the jupyter notebook and supporting files for comparing AORC an…
igarousi Jul 1, 2025
76fa8a5
Include all dask dependencies
abnerbog Jul 8, 2025
8c1d2d2
Generate notebook outputs and small text changes
abnerbog Jul 8, 2025
b5b0af2
Minor plotting edits, watershed / rainfall map
abnerbog Jul 8, 2025
da23639
small test commit
abnerbog Jul 11, 2025
4b8d087
Add package versions and local env folder
abnerbog Jul 14, 2025
32c6624
Add utility fxns to clip and plot data from single watershed
abnerbog Jul 14, 2025
253b0e4
Remove comment in conda install
abnerbog Jul 14, 2025
995eebc
Add environment.yml with snapshot of pkg versions
abnerbog Jul 23, 2025
8d4a210
Update shell script to reference environment.yml
abnerbog Jul 23, 2025
61ce1f2
Update watershed plotting utility fxn
abnerbog Jul 23, 2025
a4dbce7
Seperate precipitation plots by watershed
abnerbog Jul 23, 2025
893b503
added zarr creation and storing in HydroShare
Castronova Jul 28, 2025
6d8a3da
Adding conda environment files
homa-slh Jul 31, 2025
e10ed16
Updating Readme file
homa-slh Jul 31, 2025
ea1edb8
Updating for conda env setup
homa-slh Jul 31, 2025
ea084e8
Updating for conda env setup
homa-slh Jul 31, 2025
4151edd
Update environment.yml
abnerbog Aug 1, 2025
f0b1a02
Use libmamba for package loading
abnerbog Aug 1, 2025
912b4c4
Make readability updates
abnerbog Aug 1, 2025
be8dd94
Make readability updates
abnerbog Aug 1, 2025
9d4a4bc
Adding changes from Homa
abnerbog Aug 1, 2025
a650829
Adding a couple missing pkgs
abnerbog Aug 1, 2025
3c6b96a
Small edits in saving to HS section
abnerbog Aug 1, 2025
a471ff8
Remove extra comments from notebook
abnerbog Aug 1, 2025
2ad8f12
Remove username from S3 authentication
abnerbog Aug 1, 2025
14a2982
Updated readme
homa-slh Aug 6, 2025
14f547a
Updated description for Conda env setup
homa-slh Aug 6, 2025
11128d3
adding kerchunk notebook
Castronova Aug 6, 2025
ed12cb4
Merge branch 'develop' of https://github.com/CUAHSI/notebooks into de…
Castronova Aug 6, 2025
90cee1a
Adding kerchunk example
Castronova Aug 15, 2025
b71955d
removing duplicate kerchunk directory
Castronova Aug 15, 2025
9d70d05
adding notebook for building the ivt zarr from CESM2 netcdf
Castronova Aug 15, 2025
fbfd477
Added the updated science use case that computes atmospheric rivers c…
igarousi Aug 25, 2025
3e97fd1
cleared all outputs
igarousi Aug 25, 2025
c396d4e
added datatree to the notebook to make working with zarr groups easier
Castronova Aug 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fsspec >= 2024.6.0
geopandas >= 0.14.4
numpy >= 1.26.4
matplotlib >= 3.9.0
dask[complete] >= 2024.5.2
xarray >= 2024.5.0
rioxarray >= 0.15.5
geocube >= 0.5.2
s3fs >= 2024.6.0
zarr >= 2.18.2
jupyterlab >= 4.4.3
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ISO-8859-1
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROJCS["NAD_1983_Contiguous_USA_Albers",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Albers"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-96.0],PARAMETER["Standard_Parallel_1",29.5],PARAMETER["Standard_Parallel_2",45.5],PARAMETER["Latitude_Of_Origin",23.0],UNIT["Meter",1.0]]
Binary file not shown.
Binary file not shown.
1,023 changes: 1,023 additions & 0 deletions Data Access Examples/BigQuery/access-nwm-forecasts-using-bigquery.ipynb

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Data Access Examples/BigQuery/img/vt-storm.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions Data Access Examples/BigQuery/requirements2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
jupyterlab >= 4.4
pandas >= 2.2
matplotlib >= 3.10
tqdm >- 4.67
ipywidgets >= 8.1
shapely >= 2.1
ipyleaflet >= 0.19
sidecar >= 0.7
geopandas >= 1.1
Empty file.
301 changes: 301 additions & 0 deletions Data Access Examples/BigQuery/utils/forecast.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
#!/usr/bin/env python3

"""
Description: This script contains helper functions for collecting National
Water Model timeseries data using the BiGQuery API, located
at https://nwm-api.ciroh.org/

Author(s): Tony Castronova <[email protected]>
"""

import io
import pandas
import requests
import concurrent.futures
import matplotlib.pyplot as plt
from concurrent.futures import ThreadPoolExecutor


from utils import nwmmap
from datetime import datetime, timedelta
from ipyleaflet import Map, basemaps, WidgetControl
from ipywidgets import IntSlider, ColorPicker, jslink, Label, Text
import ipywidgets as widgets
from datetime import datetime, timedelta


# use the notebook version of TQDM if running in Jupyter
# otherwise use the regular version.
try:
from tqdm.notebook import tqdm as tqdm
except ImportError:
from tqdm import tqdm


class Forecasts():
def __init__(self, api_key, api_url='https://nwm-api.ciroh.org'):
self.url = f'{api_url}/forecast'
self.header = {
'x-api-key': api_key
}
self.df = None


def fetch_url(self, params):
try:
response = requests.get(self.url,
params=params,
headers=self.header)

# Raise an exception for HTTP errors
response.raise_for_status()
return response

except requests.exceptions.RequestException as e:
return f"Error fetching {self.url}: {e}"

def fetch_async(self, params_list):

results = []
errors = []

# Use ThreadPoolExecutor to make concurrent GET requests
# TQDM is used to provide a nice looking progress bar
with ThreadPoolExecutor(max_workers=5) as executor:

# Submit all URLs to the executor
future_to_url = {executor.submit(self.fetch_url, param): param for param in params_list}

# Process the results as they complete
for future in tqdm(concurrent.futures.as_completed(future_to_url),
total=len(future_to_url),
desc="Fetching Forecast Data",
unit="url",
colour="green",
dynamic_ncols=True):
url = future_to_url[future]
try:
res = future.result()

# attempt to get the status code.
# if one is not returned, we should log
# it as an error.
status_code = res.status_code

# otherwise, the
results.append(res)

except Exception as e:
errors.append(f"Exception for {url}: {e}")

return results, errors



def collect_forecasts(self,
comids,
forecast_type,
ensemble,
reference_times):

# build a parameters to query
params = [
{'comids': ','.join(map(str, comids)),
'forecast_type': forecast_type,
'reference_time': reftime,
'ensemble': ','.join(map(str, ensemble)),
'output_format': 'csv'}
for reftime in reference_times
]

# query the api asynchronously with the parameters defined above
responses, errors = self.fetch_async(params)

# filter out only the successful responses and
# convert them into a single pandas dataframe
successful_responses = [resp for resp in responses if resp.status_code == 200]
dfs = [pandas.read_csv(io.StringIO(res.text), sep=',') for res in successful_responses]
df = pandas.concat(dfs, ignore_index=True)

# clean datetime columns and return
df.time = pandas.to_datetime(df.time)
df.reference_time = pandas.to_datetime(df.reference_time)

self.df = df

def plot(self, comid, plot_type='series', xlabel='Time', ylabel='Streamflow'):

if self.df is None:
print('No forecast data to plot. Run "collect_forecasts" to collect data')
return None

df = self.df[self.df.feature_id == int(comid)]

fig, ax = plt.subplots(figsize=(10, 5))

if plot_type == 'series':
self.__plot_series(df, ax)
elif plot_type == 'iqr':
self.__plot_iqr(df, ax)
else:
print(f'Unrecognized plot type: {plot_type}')
return None

ax.grid(True)
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
plt.xticks(rotation=25)

return plt, ax

def __plot_series(self, df, ax):


# Group by 'reference_time' and plot each group on the same axis
for reference_time, group in df.groupby('reference_time'):
group.plot(x='time', y='streamflow', ax=ax, label=str(reference_time), legend=False)

def __plot_iqr(self, df, ax):

iqr = df.groupby(df.time)['streamflow'].quantile([0.25, 0.75])
iqr = iqr.reset_index()
iqr = iqr.rename(columns={'level_1': 'quantile'})

df_pivot = iqr.pivot(index='time', columns='quantile', values='streamflow')
df_pivot.index = pandas.to_datetime(df_pivot.index)

ax.fill_between(df_pivot.index, df_pivot[0.25], df_pivot[0.75], color='blue', alpha=0.3);


class ForecastMap(nwmmap.Map):
def __init__(self, api_key):
# initialize the parent class
super().__init__()

self.reach_label = None
self.forecasts = Forecasts(api_key)


# Plot Widget
self.output = widgets.Output()
self.output_close = widgets.Button(description="Close")
self.output_close.on_click(self.on_plot_close)
self.plot_container = widgets.VBox([self.output, self.output_close])
self.plot_container.layout.display = 'none'
self.plot_control = WidgetControl(widget=self.plot_container, position='bottomleft')
self.map.add(self.plot_control)


self.styled_container = widgets.HTML("<style>.floating-widget { z-index: 9999 !important; position: relative; }</style>")
self.map.add(WidgetControl(widget=self.styled_container, position="bottomleft"))



# Forecast Submit Widget
self.submit = widgets.Button(description='Submit',
disabled=True,
button_style='',
icon=' ')
self.submit.on_click(self.collect_forecasts)

self.map.add(WidgetControl(widget=self.submit,
position='bottomleft'))

# Forecast Option Widgets
self.reach_label = widgets.Text(value='No Reach Selected',
disabled=True,
layout=widgets.Layout(width='200px'))

self.start_date = widgets.DatePicker(disabled = False,
value = datetime.today() - timedelta(days=10),
layout=widgets.Layout(width='200px'))

self.forecast_type = widgets.Dropdown(options=['short_range', 'medium_range', 'long_range'],
value='medium_range',
disabled=False,
layout=widgets.Layout(width='200px'))

self.num_days = widgets.IntText(value=10,
disabled=False,
layout=widgets.Layout(width='200px'))

label_layout = widgets.Layout(width='100px', justify_content='flex-end')

self.options_widgets = widgets.VBox([
widgets.HBox([Label(value='Reach', layout=label_layout), self.reach_label]),
widgets.HBox([Label(value='Start Date', layout=label_layout), self.start_date]),
widgets.HBox([Label(value='Forecast Type', layout=label_layout), self.forecast_type]),
widgets.HBox([Label(value='Number of Days', layout=label_layout), self.num_days])
])

self.map.add(WidgetControl(widget=self.options_widgets,
position='bottomleft'))


# debugging - REMOVE ME
self.reach_label.value = '4965151'
self.submit.disabled = False



def on_plot_close(self, callback):
print('on_plot_close')
self.output.clear_output(wait=True)

# toggle the visibility of map widgets
self.toggle_widget_visibility([self.plot_container, self.options_widgets, self.submit])


def action_after_map_click(self):

if self.selected() is not None:
# set the value of the label
self.reach_label.value = self.selected()

# activate the submission button
self.submit.disabled = False
self.submit.button_style = 'info'

else:
# set the value of the label
self.reach_label.value = 'No Reach Selected'

# deactivate the submission button
self.submit.disabled = True
self.submit.button_style = ''

def collect_forecasts(self, callback):

reference_times = [(self.start_date.value + timedelta(days=i)).strftime('%Y-%m-%d')
for i in range(self.num_days.value)]

self.forecasts.collect_forecasts([self.reach_label.value],
self.forecast_type.value,
[0],
reference_times)
self.show_plot()

def show_plot(self):

# toggle the visibility of map widgets
self.toggle_widget_visibility([self.plot_container, self.options_widgets, self.submit])

with self.output:

# create and show a plot of the data
plt, ax = self.forecasts.plot(self.reach_label.value, plot_type='iqr', ylabel='Streamflow (cms)')
ax.set_title(f'IQR of Forecasted Streamflow for NWM {self.reach_label.value}')
plt.show()

def toggle_widget_visibility(self, widgets=[]):

for widget in widgets:
visibility = widget.layout.display

if visibility == 'none':
widget.layout.display = 'flex'
else:
widget.layout.display = 'none'



Loading