nakas commited on
Commit
2bde2be
·
1 Parent(s): 637fa34

fix(leaflet): ensure GIF loops + render via Folium or Leaflet; disable SSR so JS runs; fix(raw-grib): force Herbie download and copy into exports

Browse files
Files changed (2) hide show
  1. app.py +93 -57
  2. requirements.txt +1 -0
app.py CHANGED
@@ -467,6 +467,42 @@ def add_radar_image_layer(fig: go.Figure, lat2d: np.ndarray, lon2d: np.ndarray,
467
  print(f"Image layer error: {e}")
468
  return False
469
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
470
  def export_radar_grib(forecast_hour: int, min_dbz: float):
471
  """Export the HRRR radar (REFC) field to a GRIB2 file with values below min_dbz set to missing.
472
 
@@ -494,24 +530,14 @@ def export_radar_grib(forecast_hour: int, min_dbz: float):
494
  thr = float(min_dbz) if min_dbz is not None else 1.0
495
  z = np.where(z >= thr, z.astype(float), np.nan)
496
 
497
- # Determine source GRIB path
498
  src = None
499
- if isinstance(info, dict):
500
- src = info.get('file')
501
  if not src:
502
- # Try to pull from encodings
503
- try:
504
- src = ds.encoding.get('source', None)
505
- except Exception:
506
- pass
507
  if not src:
508
- for vn in var_names:
509
- enc = getattr(ds[vn], 'encoding', {})
510
- src = enc.get('source', None)
511
- if src:
512
- break
513
- if not src:
514
- return None, "Could not locate the source GRIB file path."
515
 
516
  import os
517
  from eccodes import codes_grib_new_from_file, codes_get, codes_set, codes_set_values, codes_write, codes_release
@@ -588,18 +614,19 @@ def download_raw_grib(forecast_hour: int):
588
  try:
589
  if not HERBIE_AVAILABLE:
590
  return None, "Herbie is not available"
591
- ds, info = fetch_real_hrrr_data('REFC:entire atmosphere', int(forecast_hour), return_src=True)
592
- if isinstance(info, dict) and info.get('file'):
593
- src_file = info['file']
594
- try:
595
- import shutil
596
- os.makedirs('exports', exist_ok=True)
597
- base = os.path.basename(str(src_file))
598
- dest = os.path.join('exports', f"raw_{base}")
599
- shutil.copy2(src_file, dest)
600
- return dest, None
601
- except Exception as e:
602
- return None, f"Copy error: {e}"
 
603
  # Fallback: attempt direct Herbie path
604
  current_time = datetime.utcnow().replace(minute=0, second=0, microsecond=0)
605
  for hours_back in [2, 3, 6, 12, 18]:
@@ -615,16 +642,7 @@ def download_raw_grib(forecast_hour: int):
615
  local = files[0]
616
  if not local and hasattr(H, 'fpath'):
617
  local = H.fpath
618
- if local:
619
- try:
620
- import shutil
621
- os.makedirs('exports', exist_ok=True)
622
- base = os.path.basename(str(local))
623
- dest = os.path.join('exports', f"raw_{base}")
624
- shutil.copy2(local, dest)
625
- return dest, None
626
- except Exception as e:
627
- return None, f"Copy error: {e}"
628
  except Exception:
629
  continue
630
  return None, "Unable to locate/download raw GRIB file"
@@ -713,25 +731,42 @@ def build_leaflet_overlay_html(gif_path: Optional[str], grid: Optional[Dict[str,
713
  gif_b64 = base64.b64encode(f.read()).decode('ascii')
714
  data_url = f"data:image/gif;base64,{gif_b64}"
715
 
716
- html = f"""
717
- <link rel=\"stylesheet\" href=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css\"/>
718
- <div id=\"leaflet-map\" style=\"height:500px; border-radius:8px; overflow:hidden;\"></div>
719
- <script src=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.js\"></script>
720
- <script>
721
- (function() {{
722
- var map = L.map('leaflet-map', {{center: [{c_lat:.5f}, {c_lon:.5f}], zoom: 5, zoomControl: true}});
723
- L.tileLayer('https://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png', {{
724
- maxZoom: 18,
725
- attribution: '&copy; OpenStreetMap contributors'
726
- }}).addTo(map);
727
-
728
- var bounds = [[{min_lat:.6f}, {min_lon:.6f}], [{max_lat:.6f}, {max_lon:.6f}]];
729
- var overlay = L.imageOverlay('{data_url}', bounds, {{opacity: 0.95, interactive: false}}).addTo(map);
730
- map.fitBounds(bounds);
731
- }})();
732
- </script>
733
- """
734
- return html
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
735
  except Exception as e:
736
  return f"<div style='padding:8px;color:#900'>Leaflet overlay error: {str(e)}</div>"
737
 
@@ -1163,4 +1198,5 @@ with gr.Blocks(title="HRRR Weather + Radar") as app:
1163
  )
1164
 
1165
  if __name__ == "__main__":
1166
- app.launch(server_name="0.0.0.0", server_port=7860)
 
 
467
  print(f"Image layer error: {e}")
468
  return False
469
 
470
+ def _locate_or_download_grib(forecast_hour: int):
471
+ """Return local GRIB2 path for HRRR REFC at fxx, downloading if needed."""
472
+ if not HERBIE_AVAILABLE:
473
+ return None, "Herbie is not available"
474
+ try:
475
+ current_time = datetime.utcnow().replace(minute=0, second=0, microsecond=0)
476
+ for hours_back in [2, 3, 6, 12, 18]:
477
+ try:
478
+ target_time = current_time - timedelta(hours=hours_back)
479
+ date_str = target_time.strftime('%Y-%m-%d %H:00')
480
+ H = Herbie(date_str, model='hrrr', product='sfc', fxx=int(forecast_hour))
481
+ # Ensure local file
482
+ local = None
483
+ try:
484
+ local = H.get_localFilePath()
485
+ except Exception:
486
+ local = None
487
+ if not local:
488
+ files = None
489
+ try:
490
+ files = H.download()
491
+ except Exception:
492
+ files = None
493
+ if isinstance(files, (list, tuple)) and files:
494
+ local = files[0]
495
+ if not local and hasattr(H, 'fpath'):
496
+ local = H.fpath
497
+ if local:
498
+ return local, None
499
+ except Exception as e:
500
+ print(f"locate/download attempt failed: {e}")
501
+ continue
502
+ return None, "Unable to locate/download GRIB file"
503
+ except Exception as e:
504
+ return None, f"Locate/download error: {e}"
505
+
506
  def export_radar_grib(forecast_hour: int, min_dbz: float):
507
  """Export the HRRR radar (REFC) field to a GRIB2 file with values below min_dbz set to missing.
508
 
 
530
  thr = float(min_dbz) if min_dbz is not None else 1.0
531
  z = np.where(z >= thr, z.astype(float), np.nan)
532
 
533
+ # Determine or download source GRIB path
534
  src = None
535
+ if isinstance(info, dict) and info.get('file') and os.path.exists(info['file']):
536
+ src = info['file']
537
  if not src:
538
+ src, err = _locate_or_download_grib(int(forecast_hour))
 
 
 
 
539
  if not src:
540
+ return None, err or "Could not obtain source GRIB file"
 
 
 
 
 
 
541
 
542
  import os
543
  from eccodes import codes_grib_new_from_file, codes_get, codes_set, codes_set_values, codes_write, codes_release
 
614
  try:
615
  if not HERBIE_AVAILABLE:
616
  return None, "Herbie is not available"
617
+ # Try immediate locate/download via Herbie
618
+ src_file, err = _locate_or_download_grib(int(forecast_hour))
619
+ if not src_file:
620
+ return None, err
621
+ try:
622
+ import shutil
623
+ os.makedirs('exports', exist_ok=True)
624
+ base = os.path.basename(str(src_file))
625
+ dest = os.path.join('exports', f"raw_{base}")
626
+ shutil.copy2(src_file, dest)
627
+ return dest, None
628
+ except Exception as e:
629
+ return None, f"Copy error: {e}"
630
  # Fallback: attempt direct Herbie path
631
  current_time = datetime.utcnow().replace(minute=0, second=0, microsecond=0)
632
  for hours_back in [2, 3, 6, 12, 18]:
 
642
  local = files[0]
643
  if not local and hasattr(H, 'fpath'):
644
  local = H.fpath
645
+ # Fallback handled above
 
 
 
 
 
 
 
 
 
646
  except Exception:
647
  continue
648
  return None, "Unable to locate/download raw GRIB file"
 
731
  gif_b64 = base64.b64encode(f.read()).decode('ascii')
732
  data_url = f"data:image/gif;base64,{gif_b64}"
733
 
734
+ # Prefer Folium (self-contained HTML) and fallback to raw Leaflet
735
+ try:
736
+ import folium
737
+ m = folium.Map(location=[c_lat, c_lon], zoom_start=5, control_scale=True)
738
+ folium.TileLayer('OpenStreetMap').add_to(m)
739
+ folium.raster_layers.ImageOverlay(
740
+ image=data_url,
741
+ bounds=[[min_lat, min_lon], [max_lat, max_lon]],
742
+ opacity=0.95,
743
+ interactive=False,
744
+ cross_origin=False,
745
+ zindex=2,
746
+ ).add_to(m)
747
+ folium.LayerControl().add_to(m)
748
+ html = m.get_root().render()
749
+ return html
750
+ except Exception as fe:
751
+ html = f"""
752
+ <link rel=\"stylesheet\" href=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css\"/>
753
+ <div id=\"leaflet-map\" style=\"height:500px; border-radius:8px; overflow:hidden;\"></div>
754
+ <script src=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.js\"></script>
755
+ <script>
756
+ (function() {{
757
+ var map = L.map('leaflet-map', {{center: [{c_lat:.5f}, {c_lon:.5f}], zoom: 5, zoomControl: true}});
758
+ L.tileLayer('https://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png', {{
759
+ maxZoom: 18,
760
+ attribution: '&copy; OpenStreetMap contributors'
761
+ }}).addTo(map);
762
+
763
+ var bounds = [[{min_lat:.6f}, {min_lon:.6f}], [{max_lat:.6f}, {max_lon:.6f}]];
764
+ var overlay = L.imageOverlay('{data_url}', bounds, {{opacity: 0.95, interactive: false}}).addTo(map);
765
+ map.fitBounds(bounds);
766
+ }})();
767
+ </script>
768
+ """
769
+ return html
770
  except Exception as e:
771
  return f"<div style='padding:8px;color:#900'>Leaflet overlay error: {str(e)}</div>"
772
 
 
1198
  )
1199
 
1200
  if __name__ == "__main__":
1201
+ # Disable SSR to allow custom JS (Leaflet/Folium) to run in gr.HTML blocks
1202
+ app.launch(server_name="0.0.0.0", server_port=7860, ssr_mode=False)
requirements.txt CHANGED
@@ -8,6 +8,7 @@ metpy>=1.5.0
8
  cartopy>=0.22.0
9
  matplotlib>=3.7.0
10
  imageio>=2.16.0
 
11
  requests>=2.31.0
12
  aiohttp>=3.8.0
13
  fsspec>=2023.1.0
 
8
  cartopy>=0.22.0
9
  matplotlib>=3.7.0
10
  imageio>=2.16.0
11
+ folium>=0.14.0
12
  requests>=2.31.0
13
  aiohttp>=3.8.0
14
  fsspec>=2023.1.0