Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -22,33 +22,46 @@ PAR_POLY=[(115.,5.),(115.,15.),(120.,21.),(120.,25.),(135.,25.),(135.,5.)]
|
|
| 22 |
NEIGHBOR_COUNTRIES=["Philippines","Taiwan","Vietnam","Malaysia","Indonesia",
|
| 23 |
"China","Japan","Brunei","Palau"]
|
| 24 |
SLIDER="https://slider.cira.colostate.edu"
|
|
|
|
| 25 |
|
| 26 |
-
# --- Himawari full-disk geostationary projection
|
| 27 |
_HIMA_PROJ = Proj(proj='geos', h=35785863.0, lon_0=140.7, a=6378137.0, b=6356752.3, sweep='x')
|
| 28 |
_FULL_DISK_EXTENT = 5500000.035308 # meters, half-width/height of full-disk image
|
| 29 |
|
| 30 |
-
def
|
| 31 |
x, y = _HIMA_PROJ(lon, lat)
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
return
|
| 35 |
|
| 36 |
-
def
|
| 37 |
-
h, w = full_disk_arr.shape
|
| 38 |
lon_min, lat_min, lon_max, lat_max = DISPLAY_BBOX
|
| 39 |
corners = [(lon_min,lat_min),(lon_min,lat_max),(lon_max,lat_min),(lon_max,lat_max)]
|
| 40 |
-
|
| 41 |
for lon, lat in corners:
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
crop = full_disk_arr[y0:y1, x0:x1] if (x1>x0 and y1>y0) else full_disk_arr
|
| 47 |
-
img = Image.fromarray((np.clip(crop,0,1)*255).astype(np.uint8))
|
| 48 |
-
img = img.resize((W,H), Image.Resampling.BILINEAR)
|
| 49 |
return np.array(img, dtype=np.float32)/255.
|
| 50 |
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
def _fetch_geojson():
|
| 53 |
try:
|
| 54 |
return requests.get(
|
|
@@ -126,9 +139,53 @@ def fetch_slider_raw(ts, product="band_13"):
|
|
| 126 |
img=Image.open(io.BytesIO(r.content)).convert("L")
|
| 127 |
return np.array(img,dtype=np.float32)/255.
|
| 128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
def fetch_slider(ts, product="band_13"):
|
| 130 |
-
|
| 131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
|
| 133 |
def build_live_seq():
|
| 134 |
ts=slider_ts()
|
|
|
|
| 22 |
NEIGHBOR_COUNTRIES=["Philippines","Taiwan","Vietnam","Malaysia","Indonesia",
|
| 23 |
"China","Japan","Brunei","Palau"]
|
| 24 |
SLIDER="https://slider.cira.colostate.edu"
|
| 25 |
+
ZOOM=3 # tile zoom level for regional crop (0=full disk thumbnail, higher=sharper, more tiles)
|
| 26 |
|
| 27 |
+
# --- Himawari full-disk geostationary projection ---
|
| 28 |
_HIMA_PROJ = Proj(proj='geos', h=35785863.0, lon_0=140.7, a=6378137.0, b=6356752.3, sweep='x')
|
| 29 |
_FULL_DISK_EXTENT = 5500000.035308 # meters, half-width/height of full-disk image
|
| 30 |
|
| 31 |
+
def _lonlat_to_frac(lon, lat):
|
| 32 |
x, y = _HIMA_PROJ(lon, lat)
|
| 33 |
+
fx = (x + _FULL_DISK_EXTENT) / (2*_FULL_DISK_EXTENT)
|
| 34 |
+
fy = (_FULL_DISK_EXTENT - y) / (2*_FULL_DISK_EXTENT)
|
| 35 |
+
return fx, fy
|
| 36 |
|
| 37 |
+
def _bbox_frac_range():
|
|
|
|
| 38 |
lon_min, lat_min, lon_max, lat_max = DISPLAY_BBOX
|
| 39 |
corners = [(lon_min,lat_min),(lon_min,lat_max),(lon_max,lat_min),(lon_max,lat_max)]
|
| 40 |
+
fxs, fys = [], []
|
| 41 |
for lon, lat in corners:
|
| 42 |
+
fx, fy = _lonlat_to_frac(lon, lat)
|
| 43 |
+
fxs.append(fx); fys.append(fy)
|
| 44 |
+
return min(fxs), max(fxs), min(fys), max(fys)
|
| 45 |
+
|
| 46 |
+
def _crop_resize_to_region(full_disk_arr):
|
| 47 |
+
"""Fallback: crop the single full-disk zoom-0 image (low-res)."""
|
| 48 |
+
h, w = full_disk_arr.shape
|
| 49 |
+
fx0, fx1, fy0, fy1 = _bbox_frac_range()
|
| 50 |
+
x0, x1 = int(fx0*w), int(fx1*w)
|
| 51 |
+
y0, y1 = int(fy0*h), int(fy1*h)
|
| 52 |
+
x0,x1 = max(0,x0), min(w,x1); y0,y1 = max(0,y0), min(h,y1)
|
| 53 |
crop = full_disk_arr[y0:y1, x0:x1] if (x1>x0 and y1>y0) else full_disk_arr
|
| 54 |
+
img = Image.fromarray((np.clip(crop,0,1)*255).astype(np.uint8)).resize((W,H), Image.Resampling.BILINEAR)
|
|
|
|
| 55 |
return np.array(img, dtype=np.float32)/255.
|
| 56 |
|
| 57 |
+
def _bbox_tile_range(zoom):
|
| 58 |
+
n = 2**zoom
|
| 59 |
+
fx0, fx1, fy0, fy1 = _bbox_frac_range()
|
| 60 |
+
col0,col1 = max(0,int(fx0*n)), min(n-1,int(fx1*n))
|
| 61 |
+
row0,row1 = max(0,int(fy0*n)), min(n-1,int(fy1*n))
|
| 62 |
+
return row0,row1,col0,col1,n
|
| 63 |
+
|
| 64 |
+
# --- Country borders/coastlines ---
|
| 65 |
def _fetch_geojson():
|
| 66 |
try:
|
| 67 |
return requests.get(
|
|
|
|
| 139 |
img=Image.open(io.BytesIO(r.content)).convert("L")
|
| 140 |
return np.array(img,dtype=np.float32)/255.
|
| 141 |
|
| 142 |
+
def fetch_slider_region(ts, zoom=ZOOM, product="band_13"):
|
| 143 |
+
row0,row1,col0,col1,n = _bbox_tile_range(zoom)
|
| 144 |
+
date_path=f"{ts[:4]}/{ts[4:6]}/{ts[6:8]}"
|
| 145 |
+
tiles={}
|
| 146 |
+
tile_h=tile_w=None
|
| 147 |
+
for row in range(row0,row1+1):
|
| 148 |
+
for col in range(col0,col1+1):
|
| 149 |
+
url=f"{SLIDER}/data/imagery/{date_path}/himawari---full_disk/{product}/{ts}/{zoom:02d}/{row:03d}_{col:03d}.png"
|
| 150 |
+
try:
|
| 151 |
+
r=requests.get(url,timeout=10)
|
| 152 |
+
r.raise_for_status()
|
| 153 |
+
img=Image.open(io.BytesIO(r.content)).convert("L")
|
| 154 |
+
arr=np.array(img,dtype=np.float32)/255.
|
| 155 |
+
tiles[(row,col)]=arr
|
| 156 |
+
if tile_h is None: tile_h,tile_w=arr.shape
|
| 157 |
+
print(f"TILE z{zoom} {row:03d}_{col:03d} OK {arr.shape}")
|
| 158 |
+
except Exception as e:
|
| 159 |
+
print(f"TILE z{zoom} {row:03d}_{col:03d} FAILED: {e}")
|
| 160 |
+
if not tiles:
|
| 161 |
+
raise RuntimeError(f"No tiles fetched at zoom {zoom}")
|
| 162 |
+
|
| 163 |
+
n_rows=row1-row0+1; n_cols=col1-col0+1
|
| 164 |
+
mosaic=np.zeros((tile_h*n_rows,tile_w*n_cols),dtype=np.float32)
|
| 165 |
+
for (row,col),arr in tiles.items():
|
| 166 |
+
r_off=(row-row0)*tile_h; c_off=(col-col0)*tile_w
|
| 167 |
+
mosaic[r_off:r_off+tile_h,c_off:c_off+tile_w]=arr
|
| 168 |
+
|
| 169 |
+
fx0,fx1,fy0,fy1 = _bbox_frac_range()
|
| 170 |
+
mosaic_fx0,mosaic_fx1 = col0/n,(col1+1)/n
|
| 171 |
+
mosaic_fy0,mosaic_fy1 = row0/n,(row1+1)/n
|
| 172 |
+
mh,mw = mosaic.shape
|
| 173 |
+
px0=int((fx0-mosaic_fx0)/(mosaic_fx1-mosaic_fx0)*mw)
|
| 174 |
+
px1=int((fx1-mosaic_fx0)/(mosaic_fx1-mosaic_fx0)*mw)
|
| 175 |
+
py0=int((fy0-mosaic_fy0)/(mosaic_fy1-mosaic_fy0)*mh)
|
| 176 |
+
py1=int((fy1-mosaic_fy0)/(mosaic_fy1-mosaic_fy0)*mh)
|
| 177 |
+
px0,px1=max(0,px0),min(mw,px1); py0,py1=max(0,py0),min(mh,py1)
|
| 178 |
+
crop = mosaic[py0:py1,px0:px1] if (px1>px0 and py1>py0) else mosaic
|
| 179 |
+
img = Image.fromarray((np.clip(crop,0,1)*255).astype(np.uint8)).resize((W,H), Image.Resampling.BILINEAR)
|
| 180 |
+
return np.array(img, dtype=np.float32)/255.
|
| 181 |
+
|
| 182 |
def fetch_slider(ts, product="band_13"):
|
| 183 |
+
try:
|
| 184 |
+
return fetch_slider_region(ts, zoom=ZOOM, product=product)
|
| 185 |
+
except Exception as e:
|
| 186 |
+
print(f"Zoom {ZOOM} tile mosaic failed ({e}) -- falling back to full-disk zoom00")
|
| 187 |
+
raw = fetch_slider_raw(ts, product)
|
| 188 |
+
return _crop_resize_to_region(raw)
|
| 189 |
|
| 190 |
def build_live_seq():
|
| 191 |
ts=slider_ts()
|