Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -25,7 +25,6 @@ import cartopy.crs as ccrs
|
|
25 |
import cartopy.feature as cfeature
|
26 |
import plotly.graph_objects as go
|
27 |
import plotly.express as px
|
28 |
-
import plotly.io as pio
|
29 |
from plotly.subplots import make_subplots
|
30 |
|
31 |
from sklearn.manifold import TSNE
|
@@ -977,17 +976,22 @@ def generate_genesis_prediction_monthly(month, oni_value, year=2025):
|
|
977 |
genesis_gpi = gpi_field[max_i, max_j]
|
978 |
|
979 |
# Determine probability of actual genesis
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
|
|
|
|
|
|
|
|
|
|
|
991 |
|
992 |
# Generate storm tracks for genesis events
|
993 |
storm_predictions = []
|
@@ -1683,6 +1687,66 @@ Storm {storm['storm_id']}:
|
|
1683 |
logging.error(f"Error creating prediction summary: {e}")
|
1684 |
return f"Error generating summary: {str(e)}"
|
1685 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1686 |
# -----------------------------
|
1687 |
# FIXED: ADVANCED ML FEATURES WITH ROBUST ERROR HANDLING
|
1688 |
# -----------------------------
|
@@ -3009,7 +3073,7 @@ def create_interface():
|
|
3009 |
""")
|
3010 |
|
3011 |
with gr.Row():
|
3012 |
-
genesis_animation = gr.
|
3013 |
|
3014 |
with gr.Row():
|
3015 |
genesis_summary = gr.Textbox(label="📋 Monthly Genesis Analysis Summary", lines=25)
|
@@ -3022,28 +3086,18 @@ def create_interface():
|
|
3022 |
f"Genesis prediction run for month={month}, oni={oni}: {len(prediction_data.get('genesis_events', []))} events"
|
3023 |
)
|
3024 |
|
3025 |
-
|
3026 |
-
genesis_fig = create_genesis_animation(prediction_data, animation)
|
3027 |
-
|
3028 |
-
# Convert to HTML with inline Plotly JS for HF Spaces without CDN access
|
3029 |
-
genesis_html = pio.to_html(
|
3030 |
-
genesis_fig,
|
3031 |
-
include_plotlyjs='inline',
|
3032 |
-
full_html=False
|
3033 |
-
)
|
3034 |
|
3035 |
# Generate summary
|
3036 |
summary_text = create_prediction_summary(prediction_data)
|
3037 |
|
3038 |
-
return
|
3039 |
|
3040 |
except Exception as e:
|
3041 |
import traceback
|
3042 |
error_msg = f"Genesis prediction failed: {str(e)}\n\nDetails:\n{traceback.format_exc()}"
|
3043 |
logging.error(error_msg)
|
3044 |
-
|
3045 |
-
err_html = pio.to_html(err_fig, include_plotlyjs='inline', full_html=False)
|
3046 |
-
return err_html, error_msg
|
3047 |
|
3048 |
generate_genesis_btn.click(
|
3049 |
fn=run_genesis_prediction,
|
|
|
25 |
import cartopy.feature as cfeature
|
26 |
import plotly.graph_objects as go
|
27 |
import plotly.express as px
|
|
|
28 |
from plotly.subplots import make_subplots
|
29 |
|
30 |
from sklearn.manifold import TSNE
|
|
|
976 |
genesis_gpi = gpi_field[max_i, max_j]
|
977 |
|
978 |
# Determine probability of actual genesis
|
979 |
+
# Probability influenced by ONI to mimic El Niño/La Niña effects
|
980 |
+
genesis_prob = np.clip(0.3 + genesis_gpi / 4.0 + 0.2 * np.tanh(oni_value), 0, 0.9)
|
981 |
+
|
982 |
+
if np.random.random() < genesis_prob:
|
983 |
+
event = {
|
984 |
+
'day': day,
|
985 |
+
'lat': genesis_lat,
|
986 |
+
'lon': genesis_lon,
|
987 |
+
'gpi': genesis_gpi,
|
988 |
+
'probability': genesis_prob,
|
989 |
+
'date': f"{year}-{month:02d}-{day:02d}"
|
990 |
+
}
|
991 |
+
logging.info(
|
992 |
+
f"Genesis on day {day}: lat={genesis_lat:.1f} lon={genesis_lon:.1f} GPI={genesis_gpi:.2f} prob={genesis_prob:.2f}"
|
993 |
+
)
|
994 |
+
genesis_events.append(event)
|
995 |
|
996 |
# Generate storm tracks for genesis events
|
997 |
storm_predictions = []
|
|
|
1687 |
logging.error(f"Error creating prediction summary: {e}")
|
1688 |
return f"Error generating summary: {str(e)}"
|
1689 |
|
1690 |
+
def generate_genesis_video(prediction_data):
|
1691 |
+
"""Create a simple MP4/GIF animation for the genesis prediction"""
|
1692 |
+
try:
|
1693 |
+
daily_maps = prediction_data.get('daily_gpi_maps', [])
|
1694 |
+
if not daily_maps:
|
1695 |
+
return None
|
1696 |
+
|
1697 |
+
storms = prediction_data.get('storm_predictions', [])
|
1698 |
+
lat_range = daily_maps[0]['lat_range']
|
1699 |
+
lon_range = daily_maps[0]['lon_range']
|
1700 |
+
|
1701 |
+
fig, ax = plt.subplots(figsize=(8, 6), subplot_kw={'projection': ccrs.PlateCarree()})
|
1702 |
+
ax.set_extent([lon_range.min()-5, lon_range.max()+5,
|
1703 |
+
lat_range.min()-5, lat_range.max()+5])
|
1704 |
+
ax.coastlines()
|
1705 |
+
ax.add_feature(cfeature.BORDERS, linewidth=0.5)
|
1706 |
+
|
1707 |
+
img = ax.imshow(
|
1708 |
+
daily_maps[0]['gpi_field'], origin='lower',
|
1709 |
+
extent=[lon_range.min(), lon_range.max(), lat_range.min(), lat_range.max()],
|
1710 |
+
vmin=0, vmax=3, cmap='viridis', alpha=0.8
|
1711 |
+
)
|
1712 |
+
cbar = plt.colorbar(img, ax=ax, orientation='vertical', pad=0.02, label='GPI')
|
1713 |
+
|
1714 |
+
lines = [ax.plot([], [], 'k-', lw=2)[0] for _ in storms]
|
1715 |
+
points = [ax.plot([], [], 'ro')[0] for _ in storms]
|
1716 |
+
|
1717 |
+
title = ax.set_title('')
|
1718 |
+
|
1719 |
+
def animate(i):
|
1720 |
+
day = daily_maps[i]['day']
|
1721 |
+
img.set_data(daily_maps[i]['gpi_field'])
|
1722 |
+
for line, point, storm in zip(lines, points, storms):
|
1723 |
+
past = [p for p in storm.get('track', []) if p['day'] <= day]
|
1724 |
+
if not past:
|
1725 |
+
continue
|
1726 |
+
lats = [p['lat'] for p in past]
|
1727 |
+
lons = [p['lon'] for p in past]
|
1728 |
+
line.set_data(lons, lats)
|
1729 |
+
point.set_data(lons[-1], lats[-1])
|
1730 |
+
title.set_text(f"Day {day} of {prediction_data['month']:02d}/{prediction_data.get('year', 2025)}")
|
1731 |
+
return [img, *lines, *points, title]
|
1732 |
+
|
1733 |
+
anim = animation.FuncAnimation(fig, animate, frames=len(daily_maps), interval=600, blit=False)
|
1734 |
+
|
1735 |
+
if shutil.which('ffmpeg'):
|
1736 |
+
writer = animation.FFMpegWriter(fps=2)
|
1737 |
+
suffix = '.mp4'
|
1738 |
+
else:
|
1739 |
+
writer = animation.PillowWriter(fps=2)
|
1740 |
+
suffix = '.gif'
|
1741 |
+
|
1742 |
+
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=suffix, dir=tempfile.gettempdir())
|
1743 |
+
anim.save(temp_file.name, writer=writer)
|
1744 |
+
plt.close(fig)
|
1745 |
+
return temp_file.name
|
1746 |
+
except Exception as e:
|
1747 |
+
logging.error(f"Error generating genesis video: {e}")
|
1748 |
+
return None
|
1749 |
+
|
1750 |
# -----------------------------
|
1751 |
# FIXED: ADVANCED ML FEATURES WITH ROBUST ERROR HANDLING
|
1752 |
# -----------------------------
|
|
|
3073 |
""")
|
3074 |
|
3075 |
with gr.Row():
|
3076 |
+
genesis_animation = gr.Video(label="🗺️ Daily Genesis Potential & Storm Development")
|
3077 |
|
3078 |
with gr.Row():
|
3079 |
genesis_summary = gr.Textbox(label="📋 Monthly Genesis Analysis Summary", lines=25)
|
|
|
3086 |
f"Genesis prediction run for month={month}, oni={oni}: {len(prediction_data.get('genesis_events', []))} events"
|
3087 |
)
|
3088 |
|
3089 |
+
video_path = generate_genesis_video(prediction_data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3090 |
|
3091 |
# Generate summary
|
3092 |
summary_text = create_prediction_summary(prediction_data)
|
3093 |
|
3094 |
+
return video_path, summary_text
|
3095 |
|
3096 |
except Exception as e:
|
3097 |
import traceback
|
3098 |
error_msg = f"Genesis prediction failed: {str(e)}\n\nDetails:\n{traceback.format_exc()}"
|
3099 |
logging.error(error_msg)
|
3100 |
+
return None, error_msg
|
|
|
|
|
3101 |
|
3102 |
generate_genesis_btn.click(
|
3103 |
fn=run_genesis_prediction,
|