import zarr
import s3fs
import pandas as pd
import altair as alt
import solara
from ipyleaflet import Map, Marker
from ipywidgets import HTML
# Function to fetch forecast data
def get_forecast_data(river_number, date):
s3_bucket_url = f's3://geoglows-v2-forecasts/{date}.zarr/'
s3 = s3fs.S3FileSystem(anon=True)
try:
mapper = s3fs.S3Map(root=s3_bucket_url, s3=s3, check=False)
zarr_group = zarr.open_group(mapper, mode='r')
rivid_array = zarr_group['rivid'][:]
if river_number in rivid_array:
river_index = list(rivid_array).index(river_number)
qout_array = zarr_group['Qout'][:, :, river_index]
time_array = zarr_group['time'][:]
ensemble_array = zarr_group['ensemble'][:]
forecast_df = pd.DataFrame(qout_array, columns=time_array)
forecast_df.index = [f"ensemble_{i}" for i in ensemble_array]
forecast_df.columns = time_array
forecast_df = forecast_df.transpose()
forecast_df.reset_index(inplace=True)
forecast_df = forecast_df.melt(id_vars=["index"], var_name="ensemble", value_name="flow")
forecast_df.rename(columns={"index": "date"}, inplace=True)
return forecast_df
else:
return pd.DataFrame()
except Exception as e:
print(f"Error accessing data for RiverNumber {river_number} on {date}: {e}")
return pd.DataFrame()
# Function to create Altair plot
def create_altair_plot(forecast_data):
return_periods = {
"period": ["2-year", "5-year", "10-year"],
"flow": [20, 25, 30]
}
return_period_df = pd.DataFrame(return_periods)
base = alt.Chart(forecast_data).mark_line().encode(
x='date:T',
y='flow:Q',
color='ensemble:N'
).properties(
title='Forecast Data with Return Period Overlay'
)
return_period_chart = alt.Chart(return_period_df).mark_rule(color='red').encode(
y='flow:Q',
size=alt.value(2),
tooltip=['period:N', 'flow:Q']
).properties(
title='Return Periods'
)
combined_chart = alt.layer(base, return_period_chart).resolve_scale(
y='independent'
)
return combined_chart
# Sample river data with lat, lon, and river numbers
river_data = pd.DataFrame({
'RiverNumber': [110123714, 110123715, 110123716],
'Latitude': [40.7128, 34.0522, 37.7749],
'Longitude': [-74.0060, -118.2437, -122.4194]
})
# Define the river number and date for fetching data
date = '2024040100' # Replace with the desired date
# Solara components
@solara.component
def RiverForecastApp():
# HTML widget to display the chart
display_html = solara.use_memo(lambda: HTML("<p>Click on a river point to see the forecast data.</p>"), [])
# Click handler to fetch data and update the plot
def handle_click(event=None, river_number=None):
forecast_data = get_forecast_data(river_number, date)
if not forecast_data.empty:
chart = create_altair_plot(forecast_data)
chart_html = chart.to_html()
display_html.value = chart_html
else:
display_html.value = f"<p>No forecast data found for RiverNumber {river_number} on {date}.</p>"
# Create map with river points
def create_map(click_handler):
map_center = (39.8283, -98.5795) # Center of the US
m = Map(center=map_center, zoom=4)
for index, row in river_data.iterrows():
marker = Marker(location=(row['Latitude'], row['Longitude']), draggable=False)
river_number = row['RiverNumber']
marker.on_click(lambda event, river_number=river_number: click_handler(event, river_number))
m.add_layer(marker)
return m
# Create the map with click handler
river_map = create_map(handle_click)
# Layout
solara.Column(
solara.Card(solara.Figure(river_map)),
solara.Card(solara.Figure(display_html))
)