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"

" f"Station: {station}
" f"Name: {name}
" f"{attribute}: {value}
" f"

" ), icon=folium.DivIcon( html=f'
' f'{value}
' ) ).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( """ """, 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()