Spaces:
Runtime error
Runtime error
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") | |
# 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() |