Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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
|
| 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.)
|
| 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
|
| 28 |
|
| 29 |
os.makedirs(FRAMES_DIR, exist_ok=True)
|
| 30 |
|
| 31 |
# =============================================================================
|
| 32 |
-
# Himawari full-disk geostationary projection
|
| 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
|
| 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
|
| 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 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 349 |
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
|
| 354 |
-
|
| 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()
|
|
|