CSDI-Weather / pages /7_CoWIN.py
OttoYu's picture
Upload 9 files
d4bea00 verified
import streamlit as st
import requests
import json
import pandas as pd
import folium
from streamlit_folium import st_folium
import plotly.graph_objects as go
import numpy as np
from datetime import datetime
from branca.colormap import LinearColormap
import pytz
st.set_page_config(layout="wide", page_title="Real-Time CoWIN Weather Data Dashboard")
@st.cache_data(ttl=300) # Cache data for 5 minutes (300 seconds)
def fetch_data():
hk_tz = pytz.timezone('Asia/Hong_Kong')
current_time = datetime.now(hk_tz).strftime('%Y-%m-%dT%H:%M:%S')
url = f'https://cowin.hku.hk/API/data/CoWIN/map?time={current_time}'
response = requests.get(url)
return json.loads(response.text), current_time
data, fetched_time = fetch_data()
features = data
df = pd.json_normalize(features)
df.rename(columns={
'station': 'Station',
'temp': 'Temperature',
'lat': 'Latitude',
'lon': 'Longitude',
'wd': 'Wind Direction',
'ws': 'Wind Speed',
'rh': 'Relative Humidity',
'uv': 'UV Radiation',
'me_name': 'Name'
}, inplace=True)
attribute = st.selectbox(
'Select Weather Attributes to Plot and Map (Data from HKO-HKU CoWIN)',
['Temperature', 'Wind Speed', 'Relative Humidity', 'UV Radiation']
)
col1, col2, col3 = st.columns([1.65, 2, 1.2])
with col1:
attr_series = pd.Series(df[attribute].dropna())
hist_data = np.histogram(attr_series, bins=10)
bin_edges = hist_data[1]
counts = hist_data[0]
def get_color(value, min_value, max_value):
ratio = (value - min_value) / (max_value - min_value)
r = int(255 * ratio)
b = int(255 * (1 - ratio))
return f'rgb({r}, 0, {b})'
fig = go.Figure()
for i in range(len(bin_edges) - 1):
bin_center = (bin_edges[i] + bin_edges[i + 1]) / 2
color = get_color(bin_center, bin_edges.min(), bin_edges.max())
fig.add_trace(go.Bar(
x=[f'{bin_edges[i]:.1f} - {bin_edges[i + 1]:.1f}'],
y=[counts[i]],
marker_color=color,
name=f'{bin_edges[i]:.1f} - {bin_edges[i + 1]:.1f}'
))
fig.update_layout(
xaxis_title=f'{attribute}',
yaxis_title='Count',
title=f'{attribute} Distribution',
bargap=0.2,
title_font_size=20,
xaxis_title_font_size=14,
yaxis_title_font_size=14,
height=350,
xaxis=dict(title_font_size=14),
yaxis=dict(title_font_size=14)
)
st.plotly_chart(fig, use_container_width=True)
st.caption(f"Data fetched at: {fetched_time}")
with st.container():
col_1, col_2 = st.columns([1, 1])
with col_1:
if attr_series.size > 0:
avg_attr = np.mean(attr_series)
std_attr = np.std(attr_series)
max_attr = np.max(attr_series)
min_attr = np.min(attr_series)
st.metric(label=f"Average {attribute}", value=f"{avg_attr:.2f}")
st.metric(label=f"Minimum {attribute}", value=f"{min_attr:.2f}")
with col_2:
st.metric(label=f"Maximum {attribute}", value=f"{max_attr:.2f}")
st.metric(label=f"Std. Dev {attribute}", value=f"{std_attr:.2f}")
def attribute_to_color(value, min_value, max_value):
"""Convert a value to a color based on the gradient."""
ratio = (value - min_value) / (max_value - min_value)
return LinearColormap(['blue', 'purple', 'red']).rgb_hex_str(ratio)
with col2:
m = folium.Map(location=[22.3547, 114.1483], zoom_start=11, tiles='https://landsd.azure-api.net/dev/osm/xyz/basemap/gs/WGS84/tile/{z}/{x}/{y}.png?key=f4d3e21d4fc14954a1d5930d4dde3809',attr="Map infortmation from Lands Department")
folium.TileLayer(
tiles='https://mapapi.geodata.gov.hk/gs/api/v1.0.0/xyz/label/hk/en/wgs84/{z}/{x}/{y}.png',
attr="Map infortmation from Lands Department").add_to(m)
min_value = df[attribute].min()
max_value = df[attribute].max()
for _, row in df.iterrows():
lat = row['Latitude']
lon = row['Longitude']
station = row['Station']
name = row['Name']
value = row[attribute]
color = attribute_to_color(value, min_value, max_value) if pd.notna(value) else 'gray'
folium.Marker(
location=[lat, lon],
popup=(
f"<p style='font-size: 12px; background-color: white; padding: 5px; border-radius: 5px;'>"
f"Station: {station}<br>"
f"Name: {name}<br>"
f"{attribute}: {value}<br>"
f"</p>"
),
icon=folium.DivIcon(
html=f'<div style="font-size: 10pt; color: {color}; padding: 2px; border-radius: 5px;">'
f'<strong>{value}</strong></div>'
)
).add_to(m)
# Create a color scale legend
colormap = folium.LinearColormap(
colors=['blue', 'purple', 'red'],
index=[min_value, (min_value + max_value) / 2, max_value],
vmin=min_value,
vmax=max_value,
caption=f'{attribute}'
)
colormap.add_to(m)
st_folium(m, use_container_width=True , height=650)
with col3:
st.markdown(
"""
<style>
.dataframe-container {
height: 600px;
overflow-y: auto;
}
</style>
""",
unsafe_allow_html=True
)
st.dataframe(df[['Station', 'Name', 'Temperature', 'Wind Speed', 'Relative Humidity', 'UV Radiation', 'Latitude', 'Longitude']], height=600)
if st.button("Refresh Data"):
st.experimental_rerun()
hk_tz = pytz.timezone('Asia/Hong_Kong')
current_time = datetime.now(hk_tz)
if 'last_ran' not in st.session_state or (current_time - st.session_state.last_ran.replace(tzinfo=hk_tz)).total_seconds() > 300:
st.session_state.last_ran = current_time
st.experimental_rerun()