import altair as alt
import pandas as pd
import plotly.graph_objects as go
import streamlit as st
from src.helper_functions import custom_metric_box, pollution_box
from src.predict import get_data_and_predictions, update_data_and_predictions
st.set_page_config(
page_title="Utrecht Pollution Dashboard ",
page_icon="🌱",
layout="wide",
initial_sidebar_state="expanded",
)
alt.themes.enable("dark")
update_data_and_predictions()
week_data, predictions_O3, predictions_NO2 = get_data_and_predictions()
today = week_data.iloc[-1]
previous_day = week_data.iloc[-2]
dates_past = pd.date_range(end=pd.Timestamp.today(), periods=8).to_list()
dates_future = pd.date_range(
start=pd.Timestamp.today() + pd.Timedelta(days=1), periods=3
).to_list()
# O3 and NO2 values for the past 7 days
o3_past_values = week_data["O3"]
no2_past_values = week_data["NO2"]
o3_future_values = pd.Series(predictions_O3[0].flatten())
no2_future_values = pd.Series(predictions_NO2[0].flatten())
o3_values = pd.concat([o3_past_values, o3_future_values], ignore_index=True)
no2_values = pd.concat([no2_past_values, no2_future_values], ignore_index=True)
dates = dates_past + dates_future
df = pd.DataFrame({"Date": dates, "O3": o3_values, "NO2": no2_values})
# App Title
st.title("Utrecht Pollution Dashboard 🌱")
col1, col2 = st.columns((1, 3))
# Create a 3-column layout
with col1:
st.subheader("Current Weather")
custom_metric_box(
label="🥵 Temperature",
value=f"{round(today['mean_temp'] * 0.1)} °C",
)
custom_metric_box(
label="💧 Humidity",
value=f"{round(today['humidity'])} %",
)
custom_metric_box(
label="🪨 Pressure",
value=f"{round(today['pressure'] * 0.1)} hPa",
)
custom_metric_box(
label="🌧️ Precipitation",
value=f"{round(today['percipitation'] * 0.1)} mm",
)
custom_metric_box(
label="🌤️ Solar Radiation",
value=f"{round(today['global_radiation'])} J/m²",
)
custom_metric_box(
label="🌪️ Wind Speed",
value=f"{round(today['wind_speed'] * 0.1, 1)} m/s",
)
with col2:
st.subheader("Current Pollution Levels")
sub1, sub2 = st.columns((1, 1))
# Ozone (O₃) Pollution Box
with sub1:
pollution_box(
label="O3",
value=f"{round(today['O3'])} µg/m³",
delta=f"{round(int(today['O3']) - int(previous_day['O3']))} µg/m³",
threshold=120,
)
with st.expander("Learn more about O3", expanded=False):
st.markdown(
"""
*Ozone (O3)*: A harmful gas at ground level that can irritate the respiratory system and aggravate asthma.
**Good/Bad**: "Good" means safe levels for most people, while "Bad" suggests harmful levels, especially for sensitive groups.
""",
unsafe_allow_html=True,
)
# Nitrogen Dioxide (NO₂) Pollution Box
with sub2:
pollution_box(
label="NO2",
value=f"{round(today['NO2'])} µg/m³",
delta=f"{round(int(today['NO2']) - int(previous_day['NO2']))} µg/m³",
threshold=40,
)
with st.expander("Learn more about NO2", expanded=False):
st.markdown(
"""
*Nitrogen Dioxide (NO2)*: A toxic gas that contributes to lung irritation and worsens asthma and other respiratory issues.
**Good/Bad**: "Good" means safe air quality, while "Bad" indicates levels that could cause respiratory problems, especially for vulnerable individuals.
""",
unsafe_allow_html=True,
)
# Create two columns for two separate graphs
st.subheader("O3 Forecast")
# Define the new color logic: green, orange, and red based on the threshold
def get_simple_color_scale(values, threshold):
"""Returns green for values below the threshold, orange for values between the threshold and 2x the threshold, and red for values above 2x the threshold."""
return [
"#77C124"
if v < threshold
else "#E68B0A"
if v < 2 * threshold
else "#E63946"
for v in values
]
# O3 Bar Plot (threshold: 40)
o3_past_values = o3_values[:-3] # Last 3 values are predictions
o3_future_values = o3_values[-3:] # Last 3 values are predictions
o3_colors = get_simple_color_scale(o3_past_values, 40) # Color for past values
fig_o3 = go.Figure()
# Add past values
fig_o3.add_trace(
go.Bar(
x=df["Date"][:-3], # Dates for past values
y=o3_past_values,
name="O3 Past",
marker=dict(color=o3_colors), # Apply the color scale
hovertemplate="%{x|%d-%b-%Y}
%{y} µg/m³",
)
)
# Add predicted values with reduced opacity
predicted_o3_colors = get_simple_color_scale(
o3_future_values, 40
) # Color for future values
fig_o3.add_trace(
go.Bar(
x=df["Date"][-3:], # Dates for predicted values
y=o3_future_values,
name="O3 Predicted",
marker=dict(
color=predicted_o3_colors, opacity=0.5
), # Set opacity to 0.5 for predictions
hovertemplate="%{x|%d-%b-%Y}
%{y} µg/m³",
)
)
fig_o3.add_shape(
dict(
type="line",
x0=pd.Timestamp.today(),
x1=pd.Timestamp.today(),
y0=min(o3_values),
y1=max(o3_values),
line=dict(color="White", width=3, dash="dash"),
)
)
fig_o3.update_layout(
plot_bgcolor="rgba(0, 0, 0, 0)",
paper_bgcolor="rgba(0, 0, 0, 0)",
yaxis_title="O3 Concentration (µg/m³)",
font=dict(size=14),
hovermode="x",
xaxis=dict(
title="Date",
type="date",
tickmode="array",
tickvals=df["Date"],
tickformat="%d-%b",
tickangle=-45,
tickcolor="gray",
),
showlegend=False, # Disable legend
)
st.plotly_chart(fig_o3, key="fig_o3")
# NO2 Bar Plot (threshold: 120)
st.subheader("NO2 Forecast")
no2_past_values = no2_values[:-3] # Last 3 values are predictions
no2_future_values = no2_values[-3:] # Last 3 values are predictions
no2_colors = get_simple_color_scale(no2_past_values, 120) # Color for past values
fig_no2 = go.Figure()
# Add past values
fig_no2.add_trace(
go.Bar(
x=df["Date"][:-3], # Dates for past values
y=no2_past_values,
name="NO2 Past",
marker=dict(color=no2_colors), # Apply the color scale
hovertemplate="%{x|%d-%b-%Y}
%{y} µg/m³",
)
)
# Add predicted values with reduced opacity
predicted_no2_colors = get_simple_color_scale(
no2_future_values, 120
) # Color for future values
fig_no2.add_trace(
go.Bar(
x=df["Date"][-3:], # Dates for predicted values
y=no2_future_values,
name="NO2 Predicted",
marker=dict(
color=predicted_no2_colors, opacity=0.5
), # Set opacity to 0.5 for predictions
hovertemplate="%{x|%d-%b-%Y}
%{y} µg/m³",
)
)
fig_no2.add_shape(
dict(
type="line",
x0=pd.Timestamp.today(),
x1=pd.Timestamp.today(),
y0=min(no2_values),
y1=max(no2_values),
line=dict(color="White", width=3, dash="dash"),
)
)
fig_no2.update_layout(
plot_bgcolor="rgba(0, 0, 0, 0)",
paper_bgcolor="rgba(0, 0, 0, 0)",
yaxis_title="NO2 Concentration (µg/m³)",
font=dict(size=14),
hovermode="x",
xaxis=dict(
title="Date",
type="date",
tickmode="array",
tickvals=df["Date"],
tickformat="%d-%b",
tickangle=-45,
tickcolor="gray",
),
showlegend=False, # Disable legend
)
st.plotly_chart(fig_no2, key="fig_no2")