Mihkelmj commited on
Commit
23da2ff
1 Parent(s): 94c13a3

made the user app presentable for the demo

Browse files
Files changed (3) hide show
  1. app.py +91 -50
  2. predictions_history.csv +6 -6
  3. src/helper_functions.py +11 -8
app.py CHANGED
@@ -7,7 +7,7 @@ from src.helper_functions import custom_metric_box, pollution_box
7
  from src.predict import get_data_and_predictions
8
 
9
  st.set_page_config(
10
- page_title="Utrecht Pollution Dashboard",
11
  page_icon="🌱",
12
  layout="wide",
13
  initial_sidebar_state="expanded",
@@ -37,87 +37,102 @@ dates = dates_past + dates_future
37
  df = pd.DataFrame({"Date": dates, "O3": o3_values, "NO2": no2_values})
38
 
39
  # App Title
40
- st.title("Utrecht Pollution Dashboard🌱")
41
 
42
- col1, col2 = st.columns((2, 3))
43
  # Create a 3-column layout
44
  with col1:
45
  st.subheader("Current Weather")
46
- subcol1, subcol2 = st.columns((1, 1))
47
- with subcol1:
48
- custom_metric_box(
49
- label="Temperature",
50
- value=f"{round(today['mean_temp'] * 0.1)} °C",
51
- delta=f"{round(today['mean_temp'] * 0.1) - round(previous_day['mean_temp'] * 0.1)} °C",
52
- )
53
- custom_metric_box(
54
- label="Humidity",
55
- value=f"{round(today['humidity'])} %",
56
- delta=f"{round(today['humidity']) - round(previous_day['humidity'])} %",
57
- )
58
- custom_metric_box(
59
- label="Pressure",
60
- value=f"{round(today['pressure'] * 0.1)} hPa",
61
- delta=f"{round(today['pressure'] * 0.1) - round(previous_day['pressure'] * 0.1)} hPa",
62
- )
63
- with subcol2:
64
- custom_metric_box(
65
- label="Precipitation",
66
- value=f"{round(today['percipitation'] * 0.1)} mm",
67
- delta=f"{round(today['percipitation'] * 0.1) - round(previous_day['percipitation'] * 0.1)} mm",
68
- )
69
- custom_metric_box(
70
- label="Solar Radiation",
71
- value=f"{round(today['global_radiation'])} J/",
72
- delta=f"{round(today['global_radiation']) - round(previous_day['global_radiation'])} J/m²",
73
- )
74
- custom_metric_box(
75
- label="Wind Speed",
76
- value=f"{round(today['wind_speed'] * 0.1, 1)} m/s",
77
- delta=f"{round(today['wind_speed'] * 0.1, 1) - round(previous_day['wind_speed'] * 0.1, 1)} m/s",
78
- )
79
 
80
  with col2:
81
  st.subheader("Current Pollution Levels")
82
  sub1, sub2 = st.columns((1, 1))
83
- # Display the prediction
84
- # st.write(f'Predicted Pollution Level: {prediction[0]:.2f}')
85
  with sub1:
86
  pollution_box(
87
  label="O<sub>3</sub>",
88
  value=f"{round(today['O3'])} µg/m³",
89
  delta=f"{round(int(today['O3']) - int(previous_day['O3']))} µg/m³",
 
90
  )
91
  with st.expander("Learn more about O3", expanded=False):
92
  st.markdown(
93
- "*Ozone (O<sub>3</sub>)*: A harmful gas at ground level, contributing to respiratory issues and aggravating asthma.",
 
 
 
94
  unsafe_allow_html=True,
95
  )
 
 
96
  with sub2:
97
  pollution_box(
98
  label="NO<sub>2</sub>",
99
  value=f"{round(today['NO2'])} µg/m³",
100
  delta=f"{round(int(today['NO2']) - int(previous_day['NO2']))} µg/m³",
 
101
  )
102
- with st.expander("Learn more about O3", expanded=False):
103
  st.markdown(
104
- "*Wadeva particle (NO<sub>2</sub>)*: A harmful gas at ground level, contributing to respiratory issues and aggravating asthma.",
 
 
 
105
  unsafe_allow_html=True,
106
  )
107
 
108
  # Create two columns for two separate graphs
109
  # Plot O3 in the first subcolumn
110
- st.subheader("O3 and NO2 Prediction")
111
  # Plot NO2 in the second subcolumn
 
 
 
 
 
 
 
 
 
 
 
 
112
  fig_o3 = go.Figure()
113
  fig_o3.add_trace(
114
- go.Scatter(
115
  x=df["Date"],
116
  y=df["O3"],
117
- mode="lines+markers",
118
  name="O3",
119
- line=dict(color="rgb(0, 191, 255)", width=4),
120
- hovertemplate="%{x|%d-%b-%Y}<br> %{y} µg/m³<extra></extra>",
121
  )
122
  )
123
  fig_o3.add_shape(
@@ -148,14 +163,17 @@ with col2:
148
  )
149
  st.plotly_chart(fig_o3, key="fig_o3")
150
 
 
 
 
151
  fig_no2 = go.Figure()
152
  fig_no2.add_trace(
153
- go.Scatter(
154
  x=df["Date"],
155
  y=df["NO2"],
156
- mode="lines+markers",
157
  name="NO2",
158
- line=dict(color="rgb(255, 20, 147)", width=4),
 
159
  )
160
  )
161
  fig_no2.add_shape(
@@ -165,7 +183,7 @@ with col2:
165
  x1=pd.Timestamp.today(),
166
  y0=min(no2_values),
167
  y1=max(no2_values),
168
- line=dict(color="gray", width=3, dash="dash"),
169
  )
170
  )
171
  fig_no2.update_layout(
@@ -185,3 +203,26 @@ with col2:
185
  ),
186
  )
187
  st.plotly_chart(fig_no2, key="fig_no2")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  from src.predict import get_data_and_predictions
8
 
9
  st.set_page_config(
10
+ page_title="Utrecht Pollution Dashboard ",
11
  page_icon="🌱",
12
  layout="wide",
13
  initial_sidebar_state="expanded",
 
37
  df = pd.DataFrame({"Date": dates, "O3": o3_values, "NO2": no2_values})
38
 
39
  # App Title
40
+ st.title("Utrecht Pollution Dashboard 🌱")
41
 
42
+ col1, col2 = st.columns((1, 3))
43
  # Create a 3-column layout
44
  with col1:
45
  st.subheader("Current Weather")
46
+
47
+
48
+ custom_metric_box(
49
+ label="Temperature",
50
+ value=f"{round(today['mean_temp'] * 0.1)} °C",
51
+ )
52
+ custom_metric_box(
53
+ label="Humidity",
54
+ value=f"{round(today['humidity'])} %",
55
+ )
56
+ custom_metric_box(
57
+ label="Pressure",
58
+ value=f"{round(today['pressure'] * 0.1)} hPa",
59
+ )
60
+
61
+ custom_metric_box(
62
+ label="Precipitation",
63
+ value=f"{round(today['percipitation'] * 0.1)} mm",
64
+ )
65
+ custom_metric_box(
66
+ label="Solar Radiation",
67
+ value=f"{round(today['global_radiation'])} J/m²",
68
+ )
69
+ custom_metric_box(
70
+ label="Wind Speed",
71
+ value=f"{round(today['wind_speed'] * 0.1, 1)} m/s",
72
+ )
 
 
 
 
 
 
73
 
74
  with col2:
75
  st.subheader("Current Pollution Levels")
76
  sub1, sub2 = st.columns((1, 1))
77
+
78
+ # Ozone (O₃) Pollution Box
79
  with sub1:
80
  pollution_box(
81
  label="O<sub>3</sub>",
82
  value=f"{round(today['O3'])} µg/m³",
83
  delta=f"{round(int(today['O3']) - int(previous_day['O3']))} µg/m³",
84
+ threshold=120
85
  )
86
  with st.expander("Learn more about O3", expanded=False):
87
  st.markdown(
88
+ """
89
+ *Ozone (O<sub>3</sub>)*: A harmful gas at ground level that can irritate the respiratory system and aggravate asthma.<br>
90
+ **Good/Bad**: "Good" means safe levels for most people, while "Bad" suggests harmful levels, especially for sensitive groups.
91
+ """,
92
  unsafe_allow_html=True,
93
  )
94
+
95
+ # Nitrogen Dioxide (NO₂) Pollution Box
96
  with sub2:
97
  pollution_box(
98
  label="NO<sub>2</sub>",
99
  value=f"{round(today['NO2'])} µg/m³",
100
  delta=f"{round(int(today['NO2']) - int(previous_day['NO2']))} µg/m³",
101
+ threshold=40
102
  )
103
+ with st.expander("Learn more about NO2", expanded=False):
104
  st.markdown(
105
+ """
106
+ *Nitrogen Dioxide (NO<sub>2</sub>)*: A toxic gas that contributes to lung irritation and worsens asthma and other respiratory issues.<br>
107
+ **Good/Bad**: "Good" means safe air quality, while "Bad" indicates levels that could cause respiratory problems, especially for vulnerable individuals.
108
+ """,
109
  unsafe_allow_html=True,
110
  )
111
 
112
  # Create two columns for two separate graphs
113
  # Plot O3 in the first subcolumn
114
+ st.subheader("O3 Forecast")
115
  # Plot NO2 in the second subcolumn
116
+ # Define the new color logic: green, orange, and red based on the threshold
117
+ def get_simple_color_scale(values, threshold):
118
+ """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."""
119
+ return [
120
+ "#77C124" if v < threshold else
121
+ "#E68B0A" if v < 2 * threshold else
122
+ "#E63946" for v in values
123
+ ]
124
+
125
+ # O3 Bar Plot (threshold: 40)
126
+ o3_colors = get_simple_color_scale(o3_values, 40) # Green below 40, Orange above 40, Red above 80
127
+
128
  fig_o3 = go.Figure()
129
  fig_o3.add_trace(
130
+ go.Bar(
131
  x=df["Date"],
132
  y=df["O3"],
 
133
  name="O3",
134
+ marker=dict(color=o3_colors), # Apply the color scale
135
+ hovertemplate="%{x|%d-%b-%Y}<br>%{y} µg/m³<extra></extra>",
136
  )
137
  )
138
  fig_o3.add_shape(
 
163
  )
164
  st.plotly_chart(fig_o3, key="fig_o3")
165
 
166
+ # NO2 Bar Plot (threshold: 120)
167
+ no2_colors = get_simple_color_scale(no2_values, 120) # Green below 120, Orange above 120, Red above 240
168
+ st.subheader("NO2 Forecast")
169
  fig_no2 = go.Figure()
170
  fig_no2.add_trace(
171
+ go.Bar(
172
  x=df["Date"],
173
  y=df["NO2"],
 
174
  name="NO2",
175
+ marker=dict(color=no2_colors), # Apply the color scale
176
+ hovertemplate="%{x|%d-%b-%Y}<br>%{y} µg/m³<extra></extra>",
177
  )
178
  )
179
  fig_no2.add_shape(
 
183
  x1=pd.Timestamp.today(),
184
  y0=min(no2_values),
185
  y1=max(no2_values),
186
+ line=dict(color="White", width=3, dash="dash"),
187
  )
188
  )
189
  fig_no2.update_layout(
 
203
  ),
204
  )
205
  st.plotly_chart(fig_no2, key="fig_no2")
206
+
207
+ import matplotlib.pyplot as plt
208
+ import numpy as np
209
+
210
+ fig, ax = plt.subplots()
211
+
212
+ bar = ax.bar([1,2,3,4,5,6],[4,5,6,3,7,5])
213
+
214
+ def gradientbars(bars):
215
+ grad = np.atleast_2d(np.linspace(0,1,256)).T
216
+ ax = bars[0].axes
217
+ lim = ax.get_xlim()+ax.get_ylim()
218
+ for bar in bars:
219
+ bar.set_zorder(1)
220
+ bar.set_facecolor("none")
221
+ x,y = bar.get_xy()
222
+ w, h = bar.get_width(), bar.get_height()
223
+ ax.imshow(grad, extent=[x,x+w,y,y+h], aspect="auto", zorder=0)
224
+ ax.axis(lim)
225
+
226
+ gradientbars(bar)
227
+
228
+ plt.show()
predictions_history.csv CHANGED
@@ -5,9 +5,9 @@ O3,2024-10-24,2024-10-26,16.000984317626852
5
  NO2,2024-10-24,2024-10-26,25.760307451092384
6
  O3,2024-10-24,2024-10-27,19.64377495640328
7
  NO2,2024-10-24,2024-10-27,31.210576791105115
8
- O3,2024-10-24,2024-10-25,10.33808859423279
9
- NO2,2024-10-24,2024-10-25,25.68519991558237
10
- O3,2024-10-24,2024-10-26,16.000984317626852
11
- NO2,2024-10-24,2024-10-26,25.760307451092384
12
- O3,2024-10-24,2024-10-27,19.64377495640328
13
- NO2,2024-10-24,2024-10-27,31.210576791105115
 
5
  NO2,2024-10-24,2024-10-26,25.760307451092384
6
  O3,2024-10-24,2024-10-27,19.64377495640328
7
  NO2,2024-10-24,2024-10-27,31.210576791105115
8
+ O3,2024-10-24,2024-10-25,10.338088594245967
9
+ NO2,2024-10-24,2024-10-25,25.685199915579293
10
+ O3,2024-10-24,2024-10-26,16.00098431760588
11
+ NO2,2024-10-24,2024-10-26,25.7603074510804
12
+ O3,2024-10-24,2024-10-27,19.643774956417968
13
+ NO2,2024-10-24,2024-10-27,31.21057679109889
src/helper_functions.py CHANGED
@@ -2,7 +2,7 @@ import streamlit as st
2
 
3
 
4
  # Custom function to create styled metric boxes with compact layout
5
- def custom_metric_box(label, value, delta):
6
  st.markdown(f"""
7
  <div style="
8
  padding: 5px;
@@ -17,14 +17,18 @@ def custom_metric_box(label, value, delta):
17
  </div>
18
  <div>
19
  <p style="font-size: 18px; font-weight: bold; margin: 0;">{value}</p> <!-- Smaller metric -->
20
- <p style="color: {'green' if '+' in delta else 'orange'}; font-size: 12px; margin: 0;">{delta}</p> <!-- Smaller delta text -->
21
  </div>
22
  </div>
23
  """, unsafe_allow_html=True)
24
 
25
  # Custom function to create pollution metric boxes with side-by-side layout for label and value
26
  # Custom function to create pollution metric boxes with side-by-side layout and fixed width
27
- def pollution_box(label, value, delta):
 
 
 
 
 
28
  st.markdown(f"""
29
  <div style="
30
  background: rgba(255, 255, 255, 0.05);
@@ -35,10 +39,9 @@ def pollution_box(label, value, delta):
35
  border: 1px solid rgba(255, 255, 255, 0.15);
36
  padding: 15px;
37
  margin-bottom: 10px;
38
- width: 300px; /* Fixed width */
39
  ">
40
- <h4 style="font-size: 18px; font-weight: normal; margin: 0;">{label}</h4> <!-- Smaller label -->
41
- <p style="font-size: 36px; font-weight: bold; margin: 0;">{value}</p> <!-- Larger metric -->
42
- <p style="color: {'green' if '+' in delta else 'orange'}; margin: 0;">{delta}</p>
43
  </div>
44
- """, unsafe_allow_html=True)
 
2
 
3
 
4
  # Custom function to create styled metric boxes with compact layout
5
+ def custom_metric_box(label, value):
6
  st.markdown(f"""
7
  <div style="
8
  padding: 5px;
 
17
  </div>
18
  <div>
19
  <p style="font-size: 18px; font-weight: bold; margin: 0;">{value}</p> <!-- Smaller metric -->
 
20
  </div>
21
  </div>
22
  """, unsafe_allow_html=True)
23
 
24
  # Custom function to create pollution metric boxes with side-by-side layout for label and value
25
  # Custom function to create pollution metric boxes with side-by-side layout and fixed width
26
+ def pollution_box(label, value, delta, threshold):
27
+ # Determine if the pollution level is "Good" or "Bad"
28
+ status = "Good" if float(value.split()[0]) < threshold else "Bad"
29
+ status_color = "#77C124" if status == "Good" else "#E68B0A"
30
+
31
+ # Render the pollution box
32
  st.markdown(f"""
33
  <div style="
34
  background: rgba(255, 255, 255, 0.05);
 
39
  border: 1px solid rgba(255, 255, 255, 0.15);
40
  padding: 15px;
41
  margin-bottom: 10px;
 
42
  ">
43
+ <h4 style="font-size: 24px; font-weight: bold; margin: 0;">{label}</h4> <!-- Bigger label -->
44
+ <p style="font-size: 36px; font-weight: bold; color: {status_color}; margin: 0;">{status}</p> <!-- Good/Bad with color -->
45
+ <p style="font-size: 18px; margin: 0;">{value}</p> <!-- Smaller value where delta used to be -->
46
  </div>
47
+ """, unsafe_allow_html=True)