Spaces:
Sleeping
Sleeping
ok7
Browse files- .DS_Store +0 -0
- __pycache__/app.cpython-313.pyc +0 -0
- app.py +241 -88
.DS_Store
CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
|
|
__pycache__/app.cpython-313.pyc
CHANGED
Binary files a/__pycache__/app.cpython-313.pyc and b/__pycache__/app.cpython-313.pyc differ
|
|
app.py
CHANGED
@@ -187,86 +187,195 @@ class RainViewerMap:
|
|
187 |
for storm_info in hurricane_data:
|
188 |
storm_type = storm_info.get('type', 'unknown')
|
189 |
|
190 |
-
if storm_type == '
|
191 |
-
# Handle
|
192 |
-
|
193 |
-
|
|
|
|
|
194 |
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
199 |
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
270 |
|
271 |
def add_current_storms_markers(self, folium_map, storm_data):
|
272 |
"""Add markers for storms from CurrentStorms.json format"""
|
@@ -382,7 +491,7 @@ def create_gradio_app():
|
|
382 |
info="Select radar timestamp"
|
383 |
)
|
384 |
|
385 |
-
gr.Markdown("### Hurricane Information")
|
386 |
storm_list = gr.HTML(
|
387 |
value="Loading storm data...",
|
388 |
label="Active Storms"
|
@@ -466,15 +575,59 @@ def create_gradio_app():
|
|
466 |
lat = display_props.get('lat', 'N/A')
|
467 |
lon = display_props.get('lon', 'N/A')
|
468 |
|
469 |
-
|
470 |
-
html_content += f"
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
478 |
|
479 |
elif storm_type == 'sample':
|
480 |
html_content += "<em>Sample data for demonstration</em><br>"
|
|
|
187 |
for storm_info in hurricane_data:
|
188 |
storm_type = storm_info.get('type', 'unknown')
|
189 |
|
190 |
+
if storm_type == 'nhc_storm':
|
191 |
+
# Handle real NHC storm data
|
192 |
+
storm_id = storm_info.get('storm_id', 'Unknown')
|
193 |
+
forecast_points = storm_info.get('forecast_points', {})
|
194 |
+
forecast_track = storm_info.get('forecast_track', {})
|
195 |
+
forecast_cone = storm_info.get('forecast_cone', {})
|
196 |
|
197 |
+
# Add forecast track line
|
198 |
+
if forecast_track and forecast_track.get('features'):
|
199 |
+
for track_feature in forecast_track['features']:
|
200 |
+
if track_feature['geometry']['type'] == 'LineString':
|
201 |
+
coords = track_feature['geometry']['coordinates']
|
202 |
+
# Convert from [lon, lat] to [lat, lon] for Folium
|
203 |
+
track_coords = [[coord[1], coord[0]] for coord in coords]
|
204 |
+
|
205 |
+
track_props = track_feature.get('properties', {})
|
206 |
+
storm_name = track_props.get('stormname', storm_id)
|
207 |
+
|
208 |
+
folium.PolyLine(
|
209 |
+
locations=track_coords,
|
210 |
+
color='red',
|
211 |
+
weight=4,
|
212 |
+
opacity=0.8,
|
213 |
+
popup=f"Forecast Track: {storm_name}"
|
214 |
+
).add_to(folium_map)
|
215 |
|
216 |
+
# Add forecast cone if available
|
217 |
+
if forecast_cone and forecast_cone.get('features'):
|
218 |
+
for cone_feature in forecast_cone['features']:
|
219 |
+
if cone_feature['geometry']['type'] == 'Polygon':
|
220 |
+
coords = cone_feature['geometry']['coordinates'][0] # Outer ring
|
221 |
+
# Convert from [lon, lat] to [lat, lon] for Folium
|
222 |
+
cone_coords = [[coord[1], coord[0]] for coord in coords]
|
223 |
+
|
224 |
+
cone_props = cone_feature.get('properties', {})
|
225 |
+
storm_name = cone_props.get('stormname', storm_id)
|
226 |
+
|
227 |
+
folium.Polygon(
|
228 |
+
locations=cone_coords,
|
229 |
+
color='orange',
|
230 |
+
weight=2,
|
231 |
+
opacity=0.6,
|
232 |
+
fillColor='yellow',
|
233 |
+
fillOpacity=0.2,
|
234 |
+
popup=f"Forecast Cone: {storm_name}"
|
235 |
+
).add_to(folium_map)
|
236 |
+
|
237 |
+
# Add forecast points with spinning hurricane markers
|
238 |
+
if forecast_points and forecast_points.get('features'):
|
239 |
+
for feature in forecast_points['features']:
|
240 |
+
if feature['geometry']['type'] == 'Point':
|
241 |
+
coords = feature['geometry']['coordinates']
|
242 |
+
props = feature.get('properties', {})
|
243 |
+
|
244 |
+
# Extract detailed storm information
|
245 |
+
storm_name = props.get('stormname', 'Unknown Storm')
|
246 |
+
storm_type_detail = props.get('stormtype', 'Unknown')
|
247 |
+
max_wind = props.get('maxwind', 'N/A')
|
248 |
+
pressure = props.get('mslp', 'N/A')
|
249 |
+
gust = props.get('gust', 'N/A')
|
250 |
+
tau = props.get('tau', 0) # Forecast hour
|
251 |
+
date_label = props.get('datelbl', 'Unknown Time')
|
252 |
+
ss_num = props.get('ssnum', 0) # Saffir-Simpson number
|
253 |
+
advisory_num = props.get('advisnum', 'N/A')
|
254 |
+
advisory_date = props.get('advdate', 'N/A')
|
255 |
+
|
256 |
+
# Determine marker properties based on storm intensity
|
257 |
+
color = self.get_storm_color(max_wind)
|
258 |
+
radius = 8 + (ss_num * 2) # Larger markers for stronger storms
|
259 |
+
|
260 |
+
# Create detailed popup
|
261 |
+
popup_text = f"""
|
262 |
+
<div style='font-family: Arial; font-size: 12px; line-height: 1.4; min-width: 250px;'>
|
263 |
+
<div style='text-align: center; background: {color}; color: white; padding: 8px; margin: -8px -8px 8px -8px; border-radius: 5px 5px 0 0;'>
|
264 |
+
<span style='font-size: 20px;'>🌀</span>
|
265 |
+
<strong style='font-size: 16px;'>{storm_name}</strong>
|
266 |
+
</div>
|
267 |
+
<table style='width: 100%; border-collapse: collapse;'>
|
268 |
+
<tr><td style='padding: 3px; font-weight: bold;'>Type:</td><td style='padding: 3px;'>{storm_type_detail}</td></tr>
|
269 |
+
<tr><td style='padding: 3px; font-weight: bold;'>Max Winds:</td><td style='padding: 3px; color: {color}; font-weight: bold;'>{max_wind} kt</td></tr>
|
270 |
+
<tr><td style='padding: 3px; font-weight: bold;'>Gusts:</td><td style='padding: 3px;'>{gust} kt</td></tr>
|
271 |
+
<tr><td style='padding: 3px; font-weight: bold;'>Pressure:</td><td style='padding: 3px;'>{pressure} mb</td></tr>
|
272 |
+
<tr><td style='padding: 3px; font-weight: bold;'>Category:</td><td style='padding: 3px;'>{ss_num if ss_num > 0 else 'N/A'}</td></tr>
|
273 |
+
<tr><td style='padding: 3px; font-weight: bold;'>Time:</td><td style='padding: 3px;'>{date_label}</td></tr>
|
274 |
+
<tr><td style='padding: 3px; font-weight: bold;'>Forecast:</td><td style='padding: 3px;'>+{tau}h</td></tr>
|
275 |
+
<tr><td style='padding: 3px; font-weight: bold;'>Advisory:</td><td style='padding: 3px;'>#{advisory_num}</td></tr>
|
276 |
+
<tr><td style='padding: 3px; font-weight: bold;'>Position:</td><td style='padding: 3px;'>{coords[1]:.1f}°N, {abs(coords[0]):.1f}°W</td></tr>
|
277 |
+
</table>
|
278 |
+
<div style='text-align: center; margin-top: 8px; font-size: 10px; color: #666;'>
|
279 |
+
Updated: {advisory_date}
|
280 |
+
</div>
|
281 |
+
</div>
|
282 |
+
"""
|
283 |
+
|
284 |
+
# Different marker styles for current vs forecast positions
|
285 |
+
if tau == 0: # Current position - SPINNING HURRICANE!
|
286 |
+
# Create spinning hurricane icon for current position
|
287 |
+
spinning_hurricane_html = f"""
|
288 |
+
<div style="
|
289 |
+
font-size: {radius * 2.5}px;
|
290 |
+
color: {color};
|
291 |
+
text-shadow: 3px 3px 6px rgba(0,0,0,0.7);
|
292 |
+
animation: spin 2s linear infinite;
|
293 |
+
display: flex;
|
294 |
+
align-items: center;
|
295 |
+
justify-content: center;
|
296 |
+
width: {radius * 4}px;
|
297 |
+
height: {radius * 4}px;
|
298 |
+
background: radial-gradient(circle, rgba(255,255,255,0.9) 0%, rgba(255,255,255,0.4) 70%, transparent 100%);
|
299 |
+
border-radius: 50%;
|
300 |
+
border: 3px solid {color};
|
301 |
+
box-shadow: 0 0 20px rgba(255,255,255,0.8), 0 0 40px {color};
|
302 |
+
">
|
303 |
+
🌀
|
304 |
+
</div>
|
305 |
+
<style>
|
306 |
+
@keyframes spin {{
|
307 |
+
from {{ transform: rotate(0deg); }}
|
308 |
+
to {{ transform: rotate(360deg); }}
|
309 |
+
}}
|
310 |
+
</style>
|
311 |
+
"""
|
312 |
+
|
313 |
+
# Add spinning hurricane marker for current position
|
314 |
+
folium.Marker(
|
315 |
+
location=[coords[1], coords[0]],
|
316 |
+
popup=folium.Popup(popup_text, max_width=300),
|
317 |
+
icon=folium.DivIcon(
|
318 |
+
html=spinning_hurricane_html,
|
319 |
+
class_name="spinning-hurricane",
|
320 |
+
icon_size=(radius * 4, radius * 4),
|
321 |
+
icon_anchor=(radius * 2, radius * 2)
|
322 |
+
)
|
323 |
+
).add_to(folium_map)
|
324 |
+
|
325 |
+
# Add multiple pulsing circles around current position
|
326 |
+
for i, pulse_radius in enumerate([radius + 10, radius + 20, radius + 30]):
|
327 |
+
folium.CircleMarker(
|
328 |
+
location=[coords[1], coords[0]],
|
329 |
+
radius=pulse_radius,
|
330 |
+
color=color,
|
331 |
+
fill=False,
|
332 |
+
weight=3 - i,
|
333 |
+
opacity=0.7 - (i * 0.2),
|
334 |
+
popup=f"Current Position: {storm_name}"
|
335 |
+
).add_to(folium_map)
|
336 |
+
|
337 |
+
else: # Forecast position
|
338 |
+
# For forecast positions, use smaller hurricane icon
|
339 |
+
forecast_icon_html = f"""
|
340 |
+
<div style="
|
341 |
+
font-size: {radius + 4}px;
|
342 |
+
color: {color};
|
343 |
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
|
344 |
+
display: flex;
|
345 |
+
align-items: center;
|
346 |
+
justify-content: center;
|
347 |
+
width: {radius * 2.5}px;
|
348 |
+
height: {radius * 2.5}px;
|
349 |
+
background: rgba(255,255,255,0.9);
|
350 |
+
border-radius: 50%;
|
351 |
+
border: 2px solid {color};
|
352 |
+
box-shadow: 0 0 10px rgba(0,0,0,0.3);
|
353 |
+
">
|
354 |
+
🌀
|
355 |
+
</div>
|
356 |
+
"""
|
357 |
+
|
358 |
+
folium.Marker(
|
359 |
+
location=[coords[1], coords[0]],
|
360 |
+
popup=folium.Popup(popup_text, max_width=300),
|
361 |
+
icon=folium.DivIcon(
|
362 |
+
html=forecast_icon_html,
|
363 |
+
class_name="forecast-hurricane",
|
364 |
+
icon_size=(radius * 2.5, radius * 2.5),
|
365 |
+
icon_anchor=(radius * 1.25, radius * 1.25)
|
366 |
+
)
|
367 |
+
).add_to(folium_map)
|
368 |
+
|
369 |
+
# Add forecast time label
|
370 |
+
folium.Marker(
|
371 |
+
location=[coords[1] - 0.15, coords[0]],
|
372 |
+
icon=folium.DivIcon(
|
373 |
+
html=f'<div style="font-size: 9px; background: white; padding: 2px 4px; border-radius: 3px; border: 1px solid {color}; box-shadow: 0 1px 3px rgba(0,0,0,0.3);">{date_label}</div>',
|
374 |
+
class_name="forecast-label",
|
375 |
+
icon_size=(70, 20),
|
376 |
+
icon_anchor=(35, 10)
|
377 |
+
)
|
378 |
+
).add_to(folium_map)
|
379 |
|
380 |
def add_current_storms_markers(self, folium_map, storm_data):
|
381 |
"""Add markers for storms from CurrentStorms.json format"""
|
|
|
491 |
info="Select radar timestamp"
|
492 |
)
|
493 |
|
494 |
+
gr.Markdown("### 🌀 Active Hurricane Information")
|
495 |
storm_list = gr.HTML(
|
496 |
value="Loading storm data...",
|
497 |
label="Active Storms"
|
|
|
575 |
lat = display_props.get('lat', 'N/A')
|
576 |
lon = display_props.get('lon', 'N/A')
|
577 |
|
578 |
+
# Create detailed storm information table with spinning icon
|
579 |
+
html_content += f"""
|
580 |
+
<div style='background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 15px; border-radius: 10px; margin: 5px 0;'>
|
581 |
+
<div style='text-align: center; margin-bottom: 10px;'>
|
582 |
+
<span style='font-size: 24px; animation: spin 3s linear infinite;'>🌀</span>
|
583 |
+
<h3 style='margin: 5px 0; color: {self.get_storm_color(max_wind)};'>{storm_name}</h3>
|
584 |
+
</div>
|
585 |
+
<table style='width: 100%; border-collapse: collapse; font-size: 12px;'>
|
586 |
+
<tr style='background: rgba(255,255,255,0.7);'>
|
587 |
+
<td style='padding: 5px; border: 1px solid #ddd; font-weight: bold;'>Type:</td>
|
588 |
+
<td style='padding: 5px; border: 1px solid #ddd;'>{storm_type_detail}</td>
|
589 |
+
</tr>
|
590 |
+
<tr>
|
591 |
+
<td style='padding: 5px; border: 1px solid #ddd; font-weight: bold;'>Max Winds:</td>
|
592 |
+
<td style='padding: 5px; border: 1px solid #ddd; color: {self.get_storm_color(max_wind)};'><strong>{max_wind} kt</strong></td>
|
593 |
+
</tr>
|
594 |
+
<tr style='background: rgba(255,255,255,0.7);'>
|
595 |
+
<td style='padding: 5px; border: 1px solid #ddd; font-weight: bold;'>Category:</td>
|
596 |
+
<td style='padding: 5px; border: 1px solid #ddd;'>{f'Category {ss_num}' if ss_num > 0 else 'Tropical Storm/Depression'}</td>
|
597 |
+
</tr>
|
598 |
+
<tr>
|
599 |
+
<td style='padding: 5px; border: 1px solid #ddd; font-weight: bold;'>Pressure:</td>
|
600 |
+
<td style='padding: 5px; border: 1px solid #ddd;'>{pressure} mb</td>
|
601 |
+
</tr>
|
602 |
+
<tr style='background: rgba(255,255,255,0.7);'>
|
603 |
+
<td style='padding: 5px; border: 1px solid #ddd; font-weight: bold;'>Position:</td>
|
604 |
+
<td style='padding: 5px; border: 1px solid #ddd;'>{lat}°N, {abs(float(lon)) if lon != 'N/A' else 'N/A'}°W</td>
|
605 |
+
</tr>
|
606 |
+
<tr>
|
607 |
+
<td style='padding: 5px; border: 1px solid #ddd; font-weight: bold;'>Advisory:</td>
|
608 |
+
<td style='padding: 5px; border: 1px solid #ddd;'>#{advisory_num}</td>
|
609 |
+
</tr>
|
610 |
+
<tr style='background: rgba(255,255,255,0.7);'>
|
611 |
+
<td style='padding: 5px; border: 1px solid #ddd; font-weight: bold;'>Updated:</td>
|
612 |
+
<td style='padding: 5px; border: 1px solid #ddd;'>{advisory_date}</td>
|
613 |
+
</tr>
|
614 |
+
<tr>
|
615 |
+
<td style='padding: 5px; border: 1px solid #ddd; font-weight: bold;'>Forecast Points:</td>
|
616 |
+
<td style='padding: 5px; border: 1px solid #ddd;'>{len(features)} positions</td>
|
617 |
+
</tr>
|
618 |
+
</table>
|
619 |
+
|
620 |
+
<div style='margin-top: 10px; font-size: 11px; color: #666;'>
|
621 |
+
<strong>🌀 Forecast Track:</strong> Click hurricane markers on map for detailed timing
|
622 |
+
</div>
|
623 |
+
</div>
|
624 |
+
<style>
|
625 |
+
@keyframes spin {{
|
626 |
+
from {{ transform: rotate(0deg); }}
|
627 |
+
to {{ transform: rotate(360deg); }}
|
628 |
+
}}
|
629 |
+
</style>
|
630 |
+
"""
|
631 |
|
632 |
elif storm_type == 'sample':
|
633 |
html_content += "<em>Sample data for demonstration</em><br>"
|