NovatasticRoScript commited on
Commit
380f290
Β·
verified Β·
1 Parent(s): 1ec6543

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -29
app.py CHANGED
@@ -11,25 +11,24 @@ from pysteps.extrapolation.semilagrangian import extrapolate
11
  from pyproj import Proj
12
  from datetime import datetime, timedelta
13
  import gradio as gr
14
- from fastapi import FastAPI
15
  from fastapi.staticfiles import StaticFiles
16
- import uvicorn
17
 
18
  HF_TOKEN=os.environ.get("HF_TOKEN","")
19
  MODEL_REPO="NovatasticRoScript/himawari-nowcast"
20
  DATASET_REPO="NovatasticRoScript/himawari-live-cache"
21
  SEQ=9; PRED=18; LK=4; W=H=640; IN_CH=6
22
- DISPLAY_BBOX=(103.,-3.,139.,35.) # lon_min, lat_min, lon_max, lat_max
23
  PAR_POLY=[(115.,5.),(115.,15.),(120.,21.),(120.,25.),(135.,25.),(135.,5.)]
24
  SLIDER="https://slider.cira.colostate.edu"
25
  ZOOM=3
26
  FRAMES_DIR="/tmp/frames"
27
- UPDATE_INTERVAL_SEC=600 # 10 minutes
28
 
29
  os.makedirs(FRAMES_DIR, exist_ok=True)
30
 
31
  # =============================================================================
32
- # Himawari full-disk geostationary projection (for cropping to DISPLAY_BBOX)
33
  # =============================================================================
34
  _HIMA_PROJ = Proj(proj='geos', h=35785863.0, lon_0=140.7, a=6378137.0, b=6356752.3, sweep='x')
35
  _FULL_DISK_EXTENT = 5500000.035308
@@ -99,7 +98,7 @@ model.eval()
99
  zeros_t=torch.zeros(1,1,H,W); coast_t=torch.zeros(1,1,H,W)
100
 
101
  # =============================================================================
102
- # SLIDER fetch (zoom-tile mosaic, falls back to full-disk crop)
103
  # =============================================================================
104
  def slider_ts(product="band_13"):
105
  url=f"{SLIDER}/data/json/himawari/full_disk/{product}/latest_times.json"
@@ -188,7 +187,7 @@ def save_frame_png(data, path):
188
  Image.fromarray(rgb).save(path)
189
 
190
  # =============================================================================
191
- # Background update loop: runs forever, refreshes frames every 10 minutes
192
  # =============================================================================
193
  def update_loop():
194
  while True:
@@ -246,21 +245,25 @@ if not os.path.exists(os.path.join(FRAMES_DIR,"manifest.json")):
246
  threading.Thread(target=update_loop, daemon=True).start()
247
 
248
  # =============================================================================
249
- # Map UI (Leaflet, served as static HTML/JS inside a Gradio Blocks page)
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  # =============================================================================
251
  _bounds_js = json.dumps([[DISPLAY_BBOX[1],DISPLAY_BBOX[0]],[DISPLAY_BBOX[3],DISPLAY_BBOX[2]]])
252
  _par_js = json.dumps([[lat,lon] for lon,lat in PAR_POLY])
253
 
254
- _MAP_HTML_TEMPLATE = r"""
255
- <div id="liveMap" style="width:100%;height:560px;border-radius:8px;"></div>
256
- <div style="margin-top:8px;display:flex;align-items:center;gap:10px;font-family:monospace;">
257
- <button id="liveBtn" style="padding:6px 14px;border-radius:6px;border:none;background:#e63946;color:white;font-weight:bold;cursor:pointer;">πŸ”΄ LIVE</button>
258
- <span id="frameLabel" style="min-width:260px;">Loading...</span>
259
- <input id="frameSlider" type="range" min="0" max="1" value="0" style="flex:1;">
260
- </div>
261
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css"/>
262
- <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js"></script>
263
- <script>
264
  (function(){
265
  const BOUNDS = __BOUNDS__;
266
  const PAR = __PAR__;
@@ -338,18 +341,34 @@ _MAP_HTML_TEMPLATE = r"""
338
  poll();
339
  setInterval(poll, 20000);
340
  })();
341
- </script>
342
  """
 
343
 
344
- MAP_HTML = _MAP_HTML_TEMPLATE.replace("__BOUNDS__", _bounds_js).replace("__PAR__", _par_js)
345
-
346
- with gr.Blocks(title="Himawari PAR Nowcast") as demo:
347
- gr.Markdown("### πŸŒ€ Himawari-9 Live Nowcast β€” Philippine Area of Responsibility")
348
- gr.HTML(MAP_HTML)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
350
- fastapi_app = FastAPI()
351
- fastapi_app.mount("/frames", StaticFiles(directory=FRAMES_DIR), name="frames")
352
- fastapi_app = gr.mount_gradio_app(fastapi_app, demo, path="/")
353
 
354
- if __name__ == "__main__":
355
- uvicorn.run(fastapi_app, host="0.0.0.0", port=7860)
 
11
  from pyproj import Proj
12
  from datetime import datetime, timedelta
13
  import gradio as gr
 
14
  from fastapi.staticfiles import StaticFiles
15
+ from fastapi.responses import HTMLResponse
16
 
17
  HF_TOKEN=os.environ.get("HF_TOKEN","")
18
  MODEL_REPO="NovatasticRoScript/himawari-nowcast"
19
  DATASET_REPO="NovatasticRoScript/himawari-live-cache"
20
  SEQ=9; PRED=18; LK=4; W=H=640; IN_CH=6
21
+ DISPLAY_BBOX=(103.,-3.,139.,35.)
22
  PAR_POLY=[(115.,5.),(115.,15.),(120.,21.),(120.,25.),(135.,25.),(135.,5.)]
23
  SLIDER="https://slider.cira.colostate.edu"
24
  ZOOM=3
25
  FRAMES_DIR="/tmp/frames"
26
+ UPDATE_INTERVAL_SEC=600
27
 
28
  os.makedirs(FRAMES_DIR, exist_ok=True)
29
 
30
  # =============================================================================
31
+ # Himawari full-disk geostationary projection
32
  # =============================================================================
33
  _HIMA_PROJ = Proj(proj='geos', h=35785863.0, lon_0=140.7, a=6378137.0, b=6356752.3, sweep='x')
34
  _FULL_DISK_EXTENT = 5500000.035308
 
98
  zeros_t=torch.zeros(1,1,H,W); coast_t=torch.zeros(1,1,H,W)
99
 
100
  # =============================================================================
101
+ # SLIDER fetch
102
  # =============================================================================
103
  def slider_ts(product="band_13"):
104
  url=f"{SLIDER}/data/json/himawari/full_disk/{product}/latest_times.json"
 
187
  Image.fromarray(rgb).save(path)
188
 
189
  # =============================================================================
190
+ # Background update loop
191
  # =============================================================================
192
  def update_loop():
193
  while True:
 
245
  threading.Thread(target=update_loop, daemon=True).start()
246
 
247
  # =============================================================================
248
+ # Gradio landing page (just a link β€” keeps Spaces' gradio SDK happy)
249
+ # =============================================================================
250
+ with gr.Blocks(title="Himawari PAR Nowcast") as demo:
251
+ gr.Markdown("### πŸŒ€ Himawari-9 Live Nowcast β€” Philippine Area of Responsibility")
252
+ gr.HTML('<a href="/live" target="_self" style="display:inline-block;padding:10px 20px;'
253
+ 'background:#e63946;color:white;border-radius:6px;text-decoration:none;'
254
+ 'font-weight:bold;">πŸ—ΊοΈ Open Live Map</a>')
255
+
256
+ demo.launch(server_name="0.0.0.0", server_port=7860, prevent_thread_lock=True)
257
+
258
+ demo.app.mount("/frames", StaticFiles(directory=FRAMES_DIR), name="frames")
259
+
260
+ # =============================================================================
261
+ # Standalone map page (real HTML response -- scripts execute normally here)
262
  # =============================================================================
263
  _bounds_js = json.dumps([[DISPLAY_BBOX[1],DISPLAY_BBOX[0]],[DISPLAY_BBOX[3],DISPLAY_BBOX[2]]])
264
  _par_js = json.dumps([[lat,lon] for lon,lat in PAR_POLY])
265
 
266
+ _JS_LOGIC = """
 
 
 
 
 
 
 
 
 
267
  (function(){
268
  const BOUNDS = __BOUNDS__;
269
  const PAR = __PAR__;
 
341
  poll();
342
  setInterval(poll, 20000);
343
  })();
 
344
  """
345
+ _JS_LOGIC = _JS_LOGIC.replace("__BOUNDS__", _bounds_js).replace("__PAR__", _par_js)
346
 
347
+ FULL_PAGE_HTML = f"""<!DOCTYPE html>
348
+ <html>
349
+ <head>
350
+ <meta charset="utf-8"/>
351
+ <title>Himawari PAR Nowcast</title>
352
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css"/>
353
+ <style>body{{margin:0;font-family:monospace;background:#111;color:#eee;}}
354
+ #controls{{padding:10px;display:flex;align-items:center;gap:10px;}}</style>
355
+ </head>
356
+ <body>
357
+ <div id="liveMap" style="width:100%;height:90vh;"></div>
358
+ <div id="controls">
359
+ <button id="liveBtn" style="padding:6px 14px;border-radius:6px;border:none;background:#e63946;color:white;font-weight:bold;cursor:pointer;">πŸ”΄ LIVE</button>
360
+ <span id="frameLabel">Loading...</span>
361
+ <input id="frameSlider" type="range" min="0" max="1" value="0" style="flex:1;">
362
+ </div>
363
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js"></script>
364
+ <script>
365
+ {_JS_LOGIC}
366
+ </script>
367
+ </body>
368
+ </html>"""
369
 
370
+ @demo.app.get("/live", response_class=HTMLResponse)
371
+ def live_map():
372
+ return FULL_PAGE_HTML
373
 
374
+ demo.block_thread()