NovatasticRoScript commited on
Commit
76ada68
·
verified ·
1 Parent(s): 973ea51

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +74 -17
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 (for cropping to DISPLAY_BBOX) ---
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 _lonlat_to_fulldisk_px(lon, lat, img_w, img_h):
31
  x, y = _HIMA_PROJ(lon, lat)
32
- px = (x + _FULL_DISK_EXTENT) / (2*_FULL_DISK_EXTENT) * img_w
33
- py = (_FULL_DISK_EXTENT - y) / (2*_FULL_DISK_EXTENT) * img_h
34
- return px, py
35
 
36
- def _crop_resize_to_region(full_disk_arr):
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
- xs, ys = [], []
41
  for lon, lat in corners:
42
- px, py = _lonlat_to_fulldisk_px(lon, lat, w, h)
43
- xs.append(px); ys.append(py)
44
- x0, x1 = max(0,int(min(xs))), min(w,int(max(xs)))
45
- y0, y1 = max(0,int(min(ys))), min(h,int(max(ys)))
 
 
 
 
 
 
 
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
- # --- Country borders/coastlines (drawn at render time, same approach as Colab script) ---
 
 
 
 
 
 
 
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
- raw = fetch_slider_raw(ts, product)
131
- return _crop_resize_to_region(raw)
 
 
 
 
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()