jeffliulab commited on
Commit
50e15a2
Β·
1 Parent(s): 4692887

Show current values and 24h delta below each forecast metric

Browse files
Files changed (1) hide show
  1. app.py +89 -7
app.py CHANGED
@@ -6,12 +6,14 @@ Usage:
6
  """
7
 
8
  import logging
 
9
  from datetime import timedelta
10
 
11
  import gradio as gr
12
 
13
  from hrrr_fetch import fetch_hrrr_input
14
  from model_utils import run_forecast, load_model, AVAILABLE_MODELS
 
15
  from visualization import (
16
  get_static_maps,
17
  plot_temperature,
@@ -185,6 +187,21 @@ button.primary {
185
  padding: 10px 28px !important;
186
  }
187
  button.primary:hover { background: #0A74E0 !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  """
189
 
190
  # ── Helpers ────────────────────────────────────────────────────────────
@@ -199,6 +216,41 @@ def _resolve_model(display: str) -> str:
199
  return model_keys[model_choices.index(display)]
200
 
201
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  def _hero_placeholder() -> str:
203
  return (
204
  '<div class="hero-card">'
@@ -208,7 +260,30 @@ def _hero_placeholder() -> str:
208
  )
209
 
210
 
211
- def _hero_html(r: dict, cycle_str: str, forecast_str: str, model_label: str) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  return (
213
  '<div class="hero-card">'
214
  # temperature + status
@@ -217,18 +292,24 @@ def _hero_html(r: dict, cycle_str: str, forecast_str: str, model_label: str) ->
217
  f'<span class="hero-temp-unit">Β°C</span></div>'
218
  f'<div class="hero-status">{r["rain_status"]}</div>'
219
  "</div>"
 
220
  # metric tiles
221
  '<div class="hero-metrics">'
222
  f'<div class="metric-tile"><div class="metric-value">{r["temperature_f"]:.0f}Β°F</div>'
223
- '<div class="metric-label">Temperature</div></div>'
 
224
  f'<div class="metric-tile"><div class="metric-value">{r["humidity_pct"]:.0f}%</div>'
225
- '<div class="metric-label">Humidity</div></div>'
 
226
  f'<div class="metric-tile"><div class="metric-value">{r["wind_speed_ms"]:.1f}</div>'
227
- f'<div class="metric-label">Wind m/s {r["wind_dir_str"]}</div></div>'
 
228
  f'<div class="metric-tile"><div class="metric-value">{r["gust_ms"]:.1f}</div>'
229
- '<div class="metric-label">Gust m/s</div></div>'
 
230
  f'<div class="metric-tile"><div class="metric-value">{r["precipitation_mm"]:.2f}</div>'
231
- '<div class="metric-label">Precip mm</div></div>'
 
232
  "</div>"
233
  # meta line
234
  '<div class="hero-meta">'
@@ -267,8 +348,9 @@ def do_forecast(model_display: str, progress=gr.Progress()):
267
  except Exception as e:
268
  raise gr.Error(f"Inference failed: {e}")
269
 
 
270
  model_label = model_display.split("(")[0].strip()
271
- hero = _hero_html(r, cycle_str, forecast_str, model_label)
272
  temp_fig = plot_temperature(input_array, r, cycle_str, forecast_str)
273
  precip_fig = plot_precipitation(input_array, r, cycle_str, forecast_str)
274
  wind_fig = plot_wind_speed(input_array, r, cycle_str, forecast_str)
 
6
  """
7
 
8
  import logging
9
+ import math
10
  from datetime import timedelta
11
 
12
  import gradio as gr
13
 
14
  from hrrr_fetch import fetch_hrrr_input
15
  from model_utils import run_forecast, load_model, AVAILABLE_MODELS
16
+ from var_mapping import JUMBO_ROW, JUMBO_COL
17
  from visualization import (
18
  get_static_maps,
19
  plot_temperature,
 
187
  padding: 10px 28px !important;
188
  }
189
  button.primary:hover { background: #0A74E0 !important; }
190
+
191
+ /* ── Current / delta annotations ── */
192
+ .metric-current {
193
+ font-size: 11px; font-weight: 500;
194
+ color: var(--muted);
195
+ margin-top: 6px;
196
+ line-height: 1.4;
197
+ }
198
+ .delta-up { color: #FF3B30; font-weight: 600; }
199
+ .delta-down { color: #0A84FF; font-weight: 600; }
200
+ .delta-neutral { color: #86868B; font-weight: 600; }
201
+ .hero-current-note {
202
+ font-size: 13px; color: var(--muted);
203
+ margin-top: 2px; margin-bottom: 0;
204
+ }
205
  """
206
 
207
  # ── Helpers ────────────────────────────────────────────────────────────
 
216
  return model_keys[model_choices.index(display)]
217
 
218
 
219
+ def _extract_current(input_array) -> dict:
220
+ """Extract current observed values at Jumbo from the input array."""
221
+ tmp_k = float(input_array[JUMBO_ROW, JUMBO_COL, 0])
222
+ rh = float(input_array[JUMBO_ROW, JUMBO_COL, 1])
223
+ u = float(input_array[JUMBO_ROW, JUMBO_COL, 2])
224
+ v = float(input_array[JUMBO_ROW, JUMBO_COL, 3])
225
+ gust = float(input_array[JUMBO_ROW, JUMBO_COL, 4])
226
+ apcp = float(input_array[JUMBO_ROW, JUMBO_COL, 6])
227
+ tmp_c = tmp_k - 273.15
228
+ return {
229
+ "temperature_c": tmp_c,
230
+ "temperature_f": tmp_c * 9 / 5 + 32,
231
+ "humidity_pct": max(0.0, min(100.0, rh)),
232
+ "wind_speed_ms": math.sqrt(u**2 + v**2),
233
+ "gust_ms": max(gust, 0.0),
234
+ "precipitation_mm": max(apcp, 0.0),
235
+ }
236
+
237
+
238
+ def _delta_html(now_val: float, fcst_val: float, fmt: str = ".1f", unit: str = "") -> str:
239
+ """Render 'Now X Β· +/-delta' with colored arrow."""
240
+ diff = fcst_val - now_val
241
+ if abs(diff) < 0.05:
242
+ arrow, cls = "", "delta-neutral"
243
+ elif diff > 0:
244
+ arrow, cls = " ↑", "delta-up"
245
+ else:
246
+ arrow, cls = " ↓", "delta-down"
247
+ sign = "+" if diff >= 0 else ""
248
+ return (
249
+ f'Now {now_val:{fmt}}{unit} Β· '
250
+ f'<span class="{cls}">{sign}{diff:{fmt}}{unit}{arrow}</span>'
251
+ )
252
+
253
+
254
  def _hero_placeholder() -> str:
255
  return (
256
  '<div class="hero-card">'
 
260
  )
261
 
262
 
263
+ def _hero_html(r: dict, cur: dict, cycle_str: str, forecast_str: str, model_label: str) -> str:
264
+ # delta strings for each metric
265
+ d_temp = _delta_html(cur["temperature_f"], r["temperature_f"], ".0f", "Β°F")
266
+ d_hum = _delta_html(cur["humidity_pct"], r["humidity_pct"], ".0f", "%")
267
+ d_wind = _delta_html(cur["wind_speed_ms"], r["wind_speed_ms"], ".1f", "")
268
+ d_gust = _delta_html(cur["gust_ms"], r["gust_ms"], ".1f", "")
269
+ d_prec = _delta_html(cur["precipitation_mm"], r["precipitation_mm"], ".2f", "")
270
+
271
+ # main temperature delta
272
+ temp_diff = r["temperature_c"] - cur["temperature_c"]
273
+ sign = "+" if temp_diff >= 0 else ""
274
+ if abs(temp_diff) < 0.05:
275
+ tcls = "delta-neutral"
276
+ elif temp_diff > 0:
277
+ tcls = "delta-up"
278
+ else:
279
+ tcls = "delta-down"
280
+ temp_note = (
281
+ f'<p class="hero-current-note">'
282
+ f'Now {cur["temperature_c"]:.1f}Β°C Β· '
283
+ f'<span class="{tcls}">{sign}{temp_diff:.1f}Β°C in 24h</span>'
284
+ f'</p>'
285
+ )
286
+
287
  return (
288
  '<div class="hero-card">'
289
  # temperature + status
 
292
  f'<span class="hero-temp-unit">Β°C</span></div>'
293
  f'<div class="hero-status">{r["rain_status"]}</div>'
294
  "</div>"
295
+ + temp_note +
296
  # metric tiles
297
  '<div class="hero-metrics">'
298
  f'<div class="metric-tile"><div class="metric-value">{r["temperature_f"]:.0f}Β°F</div>'
299
+ f'<div class="metric-label">Temperature</div>'
300
+ f'<div class="metric-current">{d_temp}</div></div>'
301
  f'<div class="metric-tile"><div class="metric-value">{r["humidity_pct"]:.0f}%</div>'
302
+ f'<div class="metric-label">Humidity</div>'
303
+ f'<div class="metric-current">{d_hum}</div></div>'
304
  f'<div class="metric-tile"><div class="metric-value">{r["wind_speed_ms"]:.1f}</div>'
305
+ f'<div class="metric-label">Wind m/s {r["wind_dir_str"]}</div>'
306
+ f'<div class="metric-current">{d_wind}</div></div>'
307
  f'<div class="metric-tile"><div class="metric-value">{r["gust_ms"]:.1f}</div>'
308
+ f'<div class="metric-label">Gust m/s</div>'
309
+ f'<div class="metric-current">{d_gust}</div></div>'
310
  f'<div class="metric-tile"><div class="metric-value">{r["precipitation_mm"]:.2f}</div>'
311
+ f'<div class="metric-label">Precip mm</div>'
312
+ f'<div class="metric-current">{d_prec}</div></div>'
313
  "</div>"
314
  # meta line
315
  '<div class="hero-meta">'
 
348
  except Exception as e:
349
  raise gr.Error(f"Inference failed: {e}")
350
 
351
+ cur = _extract_current(input_array)
352
  model_label = model_display.split("(")[0].strip()
353
+ hero = _hero_html(r, cur, cycle_str, forecast_str, model_label)
354
  temp_fig = plot_temperature(input_array, r, cycle_str, forecast_str)
355
  precip_fig = plot_precipitation(input_array, r, cycle_str, forecast_str)
356
  wind_fig = plot_wind_speed(input_array, r, cycle_str, forecast_str)