Py.Cafe

Sindhup24/

geoglows-forecast-analysis

Forecast Analysis for Geoglows River Data

DocsPricing
  • app.py
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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))
    )