nakas commited on
Commit
fa540b8
·
1 Parent(s): 97f5d35

Add global heatmap preview and full-wave defaults

Browse files
Files changed (1) hide show
  1. app.py +110 -12
app.py CHANGED
@@ -274,6 +274,69 @@ def prepare_results(
274
  return display_df, fig, status
275
 
276
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  def run_query(
278
  model: str,
279
  variable: str,
@@ -342,12 +405,13 @@ def run_global_download(
342
  max_horizon: int,
343
  grid_step: float,
344
  raw_members: str,
345
- ) -> Tuple[str, Optional[pd.DataFrame], Optional[str]]:
 
346
  try:
347
  token = get_token()
348
 
349
  selected_from_dropdown = [name.lower() for name in (variables or [])]
350
- custom_list = parse_variable_list(custom_variables)
351
  variable_names = list(dict.fromkeys([*selected_from_dropdown, *custom_list]))
352
  if not variable_names:
353
  raise ValueError("Select at least one variable or enter custom variable names.")
@@ -361,10 +425,12 @@ def run_global_download(
361
 
362
  members = build_members_list(raw_members, model)
363
 
364
- variables_payload = [
365
- {"name": name, "level": DEFAULT_LEVEL, "alias": make_alias(name)}
366
- for name in variable_names
367
- ]
 
 
368
 
369
  df = fetch_wave_history(
370
  token=token,
@@ -392,6 +458,10 @@ def run_global_download(
392
  for col in ("forecasted_time", "forecasted_at"):
393
  if col in df.columns:
394
  df[col] = pd.to_datetime(df[col], utc=True, errors="coerce")
 
 
 
 
395
 
396
  preview = df.head(500).copy()
397
  preview_rows = len(preview)
@@ -402,17 +472,37 @@ def run_global_download(
402
  finally:
403
  tmp.close()
404
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
  status = (
406
  f"Fetched {len(df)} rows across {len(variable_names)} variable(s) "
407
- f"for {valid_iso}. Showing the first {preview_rows} rows."
408
  )
409
 
410
- return status, preview, tmp.name
411
 
412
  except ConfigurationError as exc:
413
- return f"⚠️ {exc}", None, None
414
  except Exception as exc: # noqa: BLE001
415
- return f"❌ {exc}", None, None
416
 
417
 
418
  def default_time_window(hours_back: int = 6, hours_forward: int = 24) -> Tuple[str, str]:
@@ -543,7 +633,7 @@ def build_interface() -> gr.Blocks:
543
  global_variables_input = gr.CheckboxGroup(
544
  label="Variables",
545
  choices=[code.upper() for code in WAVE_VARIABLES.keys()],
546
- value=["SWH", "MWD", "MWP"],
547
  info="Select one or more parameters to include in the download.",
548
  )
549
  global_custom_variables_input = gr.Textbox(
@@ -582,6 +672,12 @@ def build_interface() -> gr.Blocks:
582
  placeholder="e.g. 0,1,2,3",
583
  info="Leave blank for default control member. Ignored for deterministic model.",
584
  )
 
 
 
 
 
 
585
  global_submit = gr.Button("Download global snapshot", variant="primary")
586
 
587
  with gr.Column(scale=2):
@@ -592,6 +688,7 @@ def build_interface() -> gr.Blocks:
592
  interactive=False,
593
  wrap=False,
594
  )
 
595
  global_file_output = gr.File(label="Download CSV")
596
 
597
  global_submit.click(
@@ -605,8 +702,9 @@ def build_interface() -> gr.Blocks:
605
  global_max_horizon_input,
606
  global_grid_step_input,
607
  global_members_input,
 
608
  ],
609
- outputs=[global_status_output, global_preview_output, global_file_output],
610
  )
611
 
612
  demo.queue()
 
274
  return display_df, fig, status
275
 
276
 
277
+ def make_global_heatmap(
278
+ df: pd.DataFrame,
279
+ alias: str,
280
+ variable_label: str,
281
+ valid_time_iso: str,
282
+ ) -> Optional[go.Figure]:
283
+ """Generate a Plotly heatmap from a gridded dataframe."""
284
+ if alias not in df.columns:
285
+ return None
286
+
287
+ subset = df.dropna(subset=[alias, "lat", "lon"]).copy()
288
+ if subset.empty:
289
+ return None
290
+
291
+ subset["lat"] = subset["lat"].astype(float)
292
+ subset["lon"] = subset["lon"].astype(float)
293
+
294
+ if "forecasted_time" in subset.columns:
295
+ subset["forecasted_time"] = pd.to_datetime(
296
+ subset["forecasted_time"], utc=True, errors="coerce"
297
+ )
298
+ subset = subset.dropna(subset=["forecasted_time"])
299
+ if not subset.empty:
300
+ target_time = subset["forecasted_time"].min()
301
+ subset = subset[subset["forecasted_time"] == target_time]
302
+
303
+ if "member" in subset.columns:
304
+ subset = subset.sort_values("member").groupby(["lat", "lon"], as_index=False).first()
305
+
306
+ subset = subset.sort_values(["lat", "lon"])
307
+ subset = subset.drop_duplicates(subset=["lat", "lon"], keep="first")
308
+
309
+ if subset.empty:
310
+ return None
311
+
312
+ pivot = subset.pivot(index="lat", columns="lon", values=alias)
313
+ if pivot.empty:
314
+ return None
315
+
316
+ pivot = pivot.sort_index(ascending=False)
317
+ lats = pivot.index.to_list()
318
+ lons = pivot.columns.to_list()
319
+ values = pivot.values
320
+
321
+ fig = go.Figure(
322
+ data=go.Heatmap(
323
+ x=lons,
324
+ y=lats,
325
+ z=values,
326
+ colorscale="Viridis",
327
+ colorbar=dict(title=variable_label),
328
+ )
329
+ )
330
+ fig.update_layout(
331
+ title=f"{variable_label} at {valid_time_iso}",
332
+ xaxis_title="Longitude",
333
+ yaxis_title="Latitude",
334
+ template="plotly_white",
335
+ margin=dict(l=60, r=20, t=60, b=40),
336
+ )
337
+ return fig
338
+
339
+
340
  def run_query(
341
  model: str,
342
  variable: str,
 
405
  max_horizon: int,
406
  grid_step: float,
407
  raw_members: str,
408
+ preview_variable: str,
409
+ ) -> Tuple[str, Optional[pd.DataFrame], Optional[go.Figure], Optional[str]]:
410
  try:
411
  token = get_token()
412
 
413
  selected_from_dropdown = [name.lower() for name in (variables or [])]
414
+ custom_list = [name.lower() for name in parse_variable_list(custom_variables)]
415
  variable_names = list(dict.fromkeys([*selected_from_dropdown, *custom_list]))
416
  if not variable_names:
417
  raise ValueError("Select at least one variable or enter custom variable names.")
 
425
 
426
  members = build_members_list(raw_members, model)
427
 
428
+ alias_map = {}
429
+ variables_payload = []
430
+ for name in variable_names:
431
+ alias = make_alias(name)
432
+ alias_map[name] = alias
433
+ variables_payload.append({"name": name, "level": DEFAULT_LEVEL, "alias": alias})
434
 
435
  df = fetch_wave_history(
436
  token=token,
 
458
  for col in ("forecasted_time", "forecasted_at"):
459
  if col in df.columns:
460
  df[col] = pd.to_datetime(df[col], utc=True, errors="coerce")
461
+ if {"forecasted_time", "forecasted_at"}.issubset(df.columns):
462
+ df["lead_time_hours"] = (
463
+ (df["forecasted_time"] - df["forecasted_at"]).dt.total_seconds() / 3600.0
464
+ )
465
 
466
  preview = df.head(500).copy()
467
  preview_rows = len(preview)
 
472
  finally:
473
  tmp.close()
474
 
475
+ preview_choice = (preview_variable or variable_names[0]).strip().lower()
476
+ if preview_choice not in alias_map:
477
+ preview_choice = variable_names[0]
478
+ alias_for_map = alias_map[preview_choice]
479
+ variable_label, unit = WAVE_VARIABLES.get(
480
+ preview_choice,
481
+ (preview_choice.upper(), ""),
482
+ )
483
+ display_label = f"{variable_label} ({unit})" if unit else variable_label
484
+
485
+ map_fig = make_global_heatmap(df, alias_for_map, display_label, valid_iso)
486
+
487
+ lead_info = ""
488
+ if "lead_time_hours" in df.columns:
489
+ lead_series = df["lead_time_hours"].dropna()
490
+ if not lead_series.empty:
491
+ lead_info = (
492
+ f" Lead times {lead_series.min():.0f}–{lead_series.max():.0f} h."
493
+ )
494
+
495
  status = (
496
  f"Fetched {len(df)} rows across {len(variable_names)} variable(s) "
497
+ f"for {valid_iso}.{lead_info} Showing the first {preview_rows} rows."
498
  )
499
 
500
+ return status, preview, map_fig, tmp.name
501
 
502
  except ConfigurationError as exc:
503
+ return f"⚠️ {exc}", None, None, None
504
  except Exception as exc: # noqa: BLE001
505
+ return f"❌ {exc}", None, None, None
506
 
507
 
508
  def default_time_window(hours_back: int = 6, hours_forward: int = 24) -> Tuple[str, str]:
 
633
  global_variables_input = gr.CheckboxGroup(
634
  label="Variables",
635
  choices=[code.upper() for code in WAVE_VARIABLES.keys()],
636
+ value=[code.upper() for code in WAVE_VARIABLES.keys()],
637
  info="Select one or more parameters to include in the download.",
638
  )
639
  global_custom_variables_input = gr.Textbox(
 
672
  placeholder="e.g. 0,1,2,3",
673
  info="Leave blank for default control member. Ignored for deterministic model.",
674
  )
675
+ global_preview_variable_input = gr.Dropdown(
676
+ label="Preview variable for map",
677
+ choices=[code.upper() for code in WAVE_VARIABLES.keys()],
678
+ value="SWH",
679
+ info="Used for the heatmap preview below.",
680
+ )
681
  global_submit = gr.Button("Download global snapshot", variant="primary")
682
 
683
  with gr.Column(scale=2):
 
688
  interactive=False,
689
  wrap=False,
690
  )
691
+ global_map_output = gr.Plot(label="Global map preview", show_label=True)
692
  global_file_output = gr.File(label="Download CSV")
693
 
694
  global_submit.click(
 
702
  global_max_horizon_input,
703
  global_grid_step_input,
704
  global_members_input,
705
+ global_preview_variable_input,
706
  ],
707
+ outputs=[global_status_output, global_preview_output, global_map_output, global_file_output],
708
  )
709
 
710
  demo.queue()