CSDI-Weather / pages /9_Air Quality.py
OttoYu's picture
Upload 9 files
d4bea00 verified
import pandas as pd
import plotly.graph_objects as go
import streamlit as st
import requests
from streamlit_folium import st_folium
import folium
import branca.colormap as cm
st.set_page_config(layout="wide", page_title="Air Quality Health Index Dashboard")
@st.cache_data(ttl=600)
def fetch_data(url):
response = requests.get(url)
return response.json()
def get_color(aqhi):
if aqhi <= 3:
return '#006400' # Dark Green
elif aqhi <= 6:
return '#DAA520' # Goldenrod
elif aqhi <= 7:
return '#FF4500' # OrangeRed
elif aqhi <= 10:
return '#8B0000' # Dark Red
else:
return '#4B0082' # Indigo
current_url = "https://csdi.vercel.app/aqhi/data/?last=true"
hourly_url = "https://csdi.vercel.app/aqhi/data/"
messages_url = "https://csdi.vercel.app/aqhi/repo/"
current_data = fetch_data(current_url)
hourly_data = fetch_data(hourly_url)
messages_data = fetch_data(messages_url)
features_current = current_data['features']
features_hourly = hourly_data['features']
messages = messages_data['aqhi_report']
forecasts = messages_data['aqhi_forecast']
col1, col2, col3 = st.columns([1, 1.5, 1.3])
with col1:
st.caption('The following is the past hour Air Quality Health Index from EDP, updated hourly.')
for message in messages:
if message['StationTypeEN'] == 'General Stations':
with st.expander(f"General Stations - {message['AQHIRiskEN']}", expanded=True):
st.metric("AQHI Range", message['AQHIRange'])
st.markdown(f"**Risk Level:** {message['AQHIRiskEN']}")
stations = [feature['properties']['name'] for feature in features_hourly]
selected_station = st.selectbox("Select Station", stations, key="station1")
preset_station = "Tai Po"
tai_po_index = stations.index(preset_station) if preset_station in stations else 0
selected_station2 = st.selectbox("Select another station for comparison", stations, index=tai_po_index,
key="station2")
station_data2 = next(
feature for feature in features_hourly if feature['properties']['name'] == selected_station2)
station_df2 = pd.DataFrame(station_data2['properties']['feature'])
station_data = next(feature for feature in features_hourly if feature['properties']['name'] == selected_station)
station_df = pd.DataFrame(station_data['properties']['feature'])
station_df['aqhi'] = pd.to_numeric(station_df['aqhi'], errors='coerce')
fig_hist = go.Figure(data=[
go.Bar(
x=station_df['DateTime'],
y=station_df['aqhi'],
marker=dict(
color=station_df['aqhi'].apply(get_color),
),
)
])
fig_hist.update_layout(
title='AQHI Histogram',
xaxis_title='Time',
yaxis_title='AQHI',
bargap=0.2,
height=350
)
st.plotly_chart(fig_hist)
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)
colormap = cm.LinearColormap(colors=['#006400', '#DAA520', '#FF4500', '#8B0000', '#4B0082'],
vmin=1, vmax=10)
m.add_child(colormap)
for feature in features_current:
coords = feature['geometry']['coordinates']
name = feature['properties']['name']
aqhi = int(feature['properties']['feature'][0]['aqhi'])
folium.CircleMarker(
location=[coords[1], coords[0]],
radius=10,
popup=f"{name}: AQHI {aqhi}",
color=get_color(aqhi),
fill=True,
fillColor=get_color(aqhi)
).add_to(m)
st_folium(m, use_container_width=True , height=750)
with col3:
for df in [station_df, station_df2]:
for col in ['aqhi', 'PM25', 'PM10', 'NO2']:
df[col] = pd.to_numeric(df[col], errors='coerce')
tab1, tab2, tab3, tab4 = st.tabs(["AQHI", "PM2.5", "PM10", "NO2"])
with tab1:
fig_aqhi = go.Figure()
fig_aqhi.add_trace(go.Scatter(x=station_df['DateTime'], y=station_df['aqhi'], mode='lines+markers', name=selected_station))
fig_aqhi.add_trace(go.Scatter(x=station_df2['DateTime'], y=station_df2['aqhi'], mode='lines+markers', name=selected_station2))
fig_aqhi.update_layout(title='AQHI Comparison', xaxis_title='Time', yaxis_title='AQHI', legend_title='Stations', height =380)
st.plotly_chart(fig_aqhi)
with tab2:
fig_pm25 = go.Figure()
fig_pm25.add_trace(go.Scatter(x=station_df['DateTime'], y=station_df['PM25'], mode='lines+markers', name=selected_station))
fig_pm25.add_trace(go.Scatter(x=station_df2['DateTime'], y=station_df2['PM25'], mode='lines+markers', name=selected_station2))
fig_pm25.update_layout(title='PM2.5 Comparison', xaxis_title='Time', yaxis_title='PM2.5 (µg/m³)', legend_title='Stations', height =380)
st.plotly_chart(fig_pm25)
with tab3:
fig_pm10 = go.Figure()
fig_pm10.add_trace(go.Scatter(x=station_df['DateTime'], y=station_df['PM10'], mode='lines+markers', name=selected_station))
fig_pm10.add_trace(go.Scatter(x=station_df2['DateTime'], y=station_df2['PM10'], mode='lines+markers', name=selected_station2))
fig_pm10.update_layout(title='PM10 Comparison', xaxis_title='Time', yaxis_title='PM10 (µg/m³)', legend_title='Stations', height =380)
st.plotly_chart(fig_pm10)
with tab4:
fig_no2 = go.Figure()
fig_no2.add_trace(go.Scatter(x=station_df['DateTime'], y=station_df['NO2'], mode='lines+markers', name=selected_station))
fig_no2.add_trace(go.Scatter(x=station_df2['DateTime'], y=station_df2['NO2'], mode='lines+markers', name=selected_station2))
fig_no2.update_layout(title='NO2 Comparison', xaxis_title='Time', yaxis_title='NO2 (µg/m³)', legend_title='Stations', height =380)
st.plotly_chart(fig_no2)
st.caption('Current Difference')
col3_1, col3_2 = st.columns(2)
with col3_1:
st.metric(f"{selected_station} PM2.5", f"{station_df['PM25'].iloc[-1]:.1f}")
st.metric(f"{selected_station} PM10", f"{station_df['PM10'].iloc[-1]:.1f}")
st.metric(f"{selected_station} NO2", f"{station_df['NO2'].iloc[-1]:.1f}")
with col3_2:
st.metric(f"{selected_station2} PM2.5", f"{station_df2['PM25'].iloc[-1]:.1f}")
st.metric(f"{selected_station2} PM10", f"{station_df2['PM10'].iloc[-1]:.1f}")
st.metric(f"{selected_station2} NO2", f"{station_df2['NO2'].iloc[-1]:.1f}")
st.caption('The PM2.5, PM10 and NO2 are in µg/m³ unit.')