Py.Cafe

Sindhup24/

altair-riverflow-forecast

Altair Time Series Visualization

DocsPricing
  • app.py
  • merged_river_data.csv
  • 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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import pandas as pd
import altair as alt
import solara
from solara.website.utils import apidoc

# Load the river data
file_path = 'merged_river_data.csv'  # Ensure the path is correct on py.cafe
river_data = pd.read_csv(file_path)

# Convert ForecastData from string representation of lists to actual lists
river_data['ForecastData'] = river_data['ForecastData'].apply(eval)

# Convert RiverNumber to string for compatibility
river_data['RiverNumber'] = river_data['RiverNumber'].astype(str)

# Determine the bounds of the map based on the river points
min_lat, max_lat = river_data['XCoordinate'].min(), river_data['XCoordinate'].max()
min_lon, max_lon = river_data['YCoordinate'].min(), river_data['YCoordinate'].max()
center_lat = (min_lat + max_lat) / 2
center_lon = (min_lon + max_lon) / 2

# Create a map chart using Altair
background = alt.Chart(alt.topo_feature('https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json', 'countries')).mark_geoshape(
    fill='lightgray',
    stroke='white'
).properties(
    width=800,
    height=400
).project(
    'mercator',
    center=[center_lon, center_lat],
    scale=500  # Adjust the scale for zoom level
)

points = alt.Chart(river_data).mark_circle(
    size=10,
    color='steelblue',
    opacity=0.8
).encode(
    longitude='YCoordinate:Q',
    latitude='XCoordinate:Q',
    tooltip=['RiverNumber:N', 'Name:N', 'Description:N']
)

map_chart = background + points

@solara.component
def Page():
    click_data, set_click_data = solara.use_state({})
    hover_data, set_hover_data = solara.use_state({})

    def convert_to_dataframe(data):
        if data:
            relevant_keys = ["RiverNumber", "Name", "XCoordinate", "YCoordinate"]
            filtered_data = {key: [data.get(key, '')] for key in relevant_keys}
            return pd.DataFrame(filtered_data)
        return pd.DataFrame()

    click_df = convert_to_dataframe(click_data)
    hover_df = convert_to_dataframe(hover_data)

    def create_forecast_chart(river_number):
        forecast_data = river_data[river_data['RiverNumber'] == river_number]['ForecastData'].values[0]
        forecast_df = pd.DataFrame({
            'Date': pd.date_range(start='2021-01-01', periods=len(forecast_data), freq='D'),
            'Flow': forecast_data
        })
        forecast_df['RiverWatch'] = forecast_df['Flow'].rolling(window=7, min_periods=1).mean()

        base = alt.Chart(forecast_df).encode(
            x='Date:T'
        )

        area = base.mark_area(
            color='black',
            opacity=1.0
        ).encode(
            y='Flow:Q'
        )

        line_river_watch = base.mark_line(
            color='green'
        ).encode(
            y='RiverWatch:Q'
        )

        line_1_5yr = alt.Chart(pd.DataFrame({
            'Date': forecast_df['Date'],
            'Threshold': [1500] * len(forecast_df)
        })).mark_rule(
            color='magenta'
        ).encode(
            y='Threshold:Q'
        )

        line_10yr = alt.Chart(pd.DataFrame({
            'Date': forecast_df['Date'],
            'Threshold': [2000] * len(forecast_df)
        })).mark_rule(
            color='red',
            strokeDash=[5, 5]
        ).encode(
            y='Threshold:Q'
        )

        line_25yr = alt.Chart(pd.DataFrame({
            'Date': forecast_df['Date'],
            'Threshold': [2500] * len(forecast_df)
        })).mark_rule(
            color='red'
        ).encode(
            y='Threshold:Q'
        )

        chart = alt.layer(area, line_river_watch, line_1_5yr, line_10yr, line_25yr).properties(
            width=800,
            height=400,
            title=f"Forecast Data for River {river_number}"
        ).interactive()

        return chart

    with solara.Div() as main:
        solara.AltairChart(map_chart, on_click=set_click_data, on_hover=set_hover_data)

        solara.Markdown("### Click data:")
        if not click_df.empty:
            solara.DataFrame(click_df)
        else:
            solara.Markdown("No data")

        if 'RiverNumber' in click_data:
            solara.Markdown("### Forecast Data:")
            forecast_chart = create_forecast_chart(click_data['RiverNumber'])
            solara.AltairChart(forecast_chart)
        else:
            solara.Markdown("### Forecast Data: Click on a river point to view forecast data")

    return main

# This is for documentation purposes
doc = apidoc(solara.AltairChart)  # type: ignore