OttoYu commited on
Commit
e009d70
·
verified ·
1 Parent(s): e78dc0d

CoWIN added.

Browse files
Files changed (1) hide show
  1. pages/7_CoWIN.py +167 -0
pages/7_CoWIN.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ import json
4
+ import pandas as pd
5
+ import folium
6
+ from streamlit_folium import st_folium
7
+ import plotly.graph_objects as go
8
+ import numpy as np
9
+ from datetime import datetime
10
+ from branca.colormap import LinearColormap
11
+
12
+ st.set_page_config(layout="wide", page_title="Real-Time CoWIN Weather Data Dashboard")
13
+
14
+ @st.cache_data(ttl=300) # Cache data for 5 minutes (300 seconds)
15
+ def fetch_data():
16
+ current_time = datetime.now().strftime('%Y-%m-%dT%H:%M:%S')
17
+ url = f'https://cowin.hku.hk/API/data/CoWIN/map?time={current_time}'
18
+ response = requests.get(url)
19
+ return json.loads(response.text)
20
+
21
+ data = fetch_data()
22
+
23
+ features = data
24
+ df = pd.json_normalize(features)
25
+
26
+ df.rename(columns={
27
+ 'station': 'Station',
28
+ 'temp': 'Temperature',
29
+ 'lat': 'Latitude',
30
+ 'lon': 'Longitude',
31
+ 'wd': 'Wind Direction',
32
+ 'ws': 'Wind Speed',
33
+ 'rh': 'Relative Humidity',
34
+ 'uv': 'UV Radiation',
35
+ 'me_name': 'Name'
36
+ }, inplace=True)
37
+
38
+ attribute = st.selectbox(
39
+ 'Select Weather Attributes to Plot and Map (Data from HKO-HKU CoWIN)',
40
+ ['Temperature', 'Wind Speed', 'Relative Humidity', 'UV Radiation']
41
+ )
42
+
43
+ col1, col2, col3 = st.columns([1.65, 2, 1.15])
44
+
45
+ with col1:
46
+ attr_series = pd.Series(df[attribute].dropna())
47
+
48
+ hist_data = np.histogram(attr_series, bins=10)
49
+ bin_edges = hist_data[1]
50
+ counts = hist_data[0]
51
+
52
+ def get_color(value, min_value, max_value):
53
+ ratio = (value - min_value) / (max_value - min_value)
54
+ r = int(255 * ratio)
55
+ b = int(255 * (1 - ratio))
56
+ return f'rgb({r}, 0, {b})'
57
+
58
+ fig = go.Figure()
59
+
60
+ for i in range(len(bin_edges) - 1):
61
+ bin_center = (bin_edges[i] + bin_edges[i + 1]) / 2
62
+ color = get_color(bin_center, bin_edges.min(), bin_edges.max())
63
+ fig.add_trace(go.Bar(
64
+ x=[f'{bin_edges[i]:.1f} - {bin_edges[i + 1]:.1f}'],
65
+ y=[counts[i]],
66
+ marker_color=color,
67
+ name=f'{bin_edges[i]:.1f} - {bin_edges[i + 1]:.1f}'
68
+ ))
69
+
70
+ fig.update_layout(
71
+ xaxis_title=f'{attribute}',
72
+ yaxis_title='Count',
73
+ title=f'{attribute} Distribution',
74
+ bargap=0.2,
75
+ title_font_size=20,
76
+ xaxis_title_font_size=14,
77
+ yaxis_title_font_size=14,
78
+ height=350,
79
+ xaxis=dict(title_font_size=14),
80
+ yaxis=dict(title_font_size=14)
81
+ )
82
+
83
+ st.plotly_chart(fig, use_container_width=True)
84
+
85
+ with st.container():
86
+ col_1, col_2 = st.columns([1, 1])
87
+ with col_1:
88
+ if attr_series.size > 0:
89
+ avg_attr = np.mean(attr_series)
90
+ std_attr = np.std(attr_series)
91
+ max_attr = np.max(attr_series)
92
+ min_attr = np.min(attr_series)
93
+
94
+ st.metric(label=f"Average {attribute}", value=f"{avg_attr:.2f}")
95
+ st.metric(label=f"Minimum {attribute}", value=f"{min_attr:.2f}")
96
+ with col_2:
97
+ st.metric(label=f"Maximum {attribute}", value=f"{max_attr:.2f}")
98
+ st.metric(label=f"Std. Dev {attribute}", value=f"{std_attr:.2f}")
99
+
100
+ def attribute_to_color(value, min_value, max_value):
101
+ """Convert a value to a color based on the gradient."""
102
+ ratio = (value - min_value) / (max_value - min_value)
103
+ return LinearColormap(['blue', 'purple', 'red']).rgb_hex_str(ratio)
104
+
105
+ with col2:
106
+ m = folium.Map(location=[22.3547, 114.1483], zoom_start=11, tiles='CartoDB positron')
107
+
108
+ min_value = df[attribute].min()
109
+ max_value = df[attribute].max()
110
+
111
+ for _, row in df.iterrows():
112
+ lat = row['Latitude']
113
+ lon = row['Longitude']
114
+ station = row['Station']
115
+ name = row['Name']
116
+ value = row[attribute]
117
+
118
+ color = attribute_to_color(value, min_value, max_value) if pd.notna(value) else 'gray'
119
+
120
+ folium.Marker(
121
+ location=[lat, lon],
122
+ popup=(
123
+ f"<p style='font-size: 12px; background-color: white; padding: 5px; border-radius: 5px;'>"
124
+ f"Station: {station}<br>"
125
+ f"Name: {name}<br>"
126
+ f"{attribute}: {value}<br>"
127
+ f"</p>"
128
+ ),
129
+ icon=folium.DivIcon(
130
+ html=f'<div style="font-size: 10pt; color: {color}; padding: 2px; border-radius: 5px;">'
131
+ f'<strong>{value}</strong></div>'
132
+ )
133
+ ).add_to(m)
134
+
135
+ # Create a color scale legend
136
+ colormap = folium.LinearColormap(
137
+ colors=['blue', 'purple', 'red'],
138
+ index=[min_value, (min_value + max_value) / 2, max_value],
139
+ vmin=min_value,
140
+ vmax=max_value,
141
+ caption=f'{attribute}'
142
+ )
143
+ colormap.add_to(m)
144
+
145
+ st_folium(m, width=500, height=600)
146
+
147
+ with col3:
148
+ st.markdown(
149
+ """
150
+ <style>
151
+ .dataframe-container {
152
+ height: 600px;
153
+ overflow-y: auto;
154
+ }
155
+ </style>
156
+ """,
157
+ unsafe_allow_html=True
158
+ )
159
+
160
+ st.dataframe(df[['Station', 'Name', 'Temperature', 'Wind Speed', 'Relative Humidity', 'UV Radiation', 'Latitude', 'Longitude']], height=600)
161
+
162
+ if st.button("Refresh Data"):
163
+ st.experimental_rerun()
164
+
165
+ if 'last_ran' not in st.session_state or (datetime.now() - st.session_state.last_ran).seconds > 300:
166
+ st.session_state.last_ran = datetime.now()
167
+ st.experimental_rerun()