Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1236,9 +1236,64 @@ def create_genesis_animation(prediction_data, enable_animation=True):
|
|
1236 |
intensities = [pt['intensity'] for pt in current_track]
|
1237 |
categories = [pt['category'] for pt in current_track]
|
1238 |
|
1239 |
-
#
|
1240 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1241 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1242 |
# Create frame
|
1243 |
frames.append(go.Frame(
|
1244 |
data=frame_data,
|
@@ -1266,8 +1321,50 @@ def create_genesis_animation(prediction_data, enable_animation=True):
|
|
1266 |
|
1267 |
fig.frames = frames
|
1268 |
|
1269 |
-
# Set up initial frame, add
|
1270 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1271 |
|
1272 |
else:
|
1273 |
# STATIC VERSION (Final state overview)
|
@@ -1295,8 +1392,26 @@ def create_genesis_animation(prediction_data, enable_animation=True):
|
|
1295 |
)
|
1296 |
)
|
1297 |
|
1298 |
-
# Add complete storm tracks
|
1299 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1300 |
|
1301 |
# FINAL LAYOUT (Professional meteorological style)
|
1302 |
fig.update_layout(
|
@@ -1336,7 +1451,7 @@ def create_genesis_animation(prediction_data, enable_animation=True):
|
|
1336 |
)
|
1337 |
|
1338 |
return fig
|
1339 |
-
|
1340 |
except Exception as e:
|
1341 |
logging.error(f"Error creating professional genesis animation: {e}")
|
1342 |
import traceback
|
|
|
1236 |
intensities = [pt['intensity'] for pt in current_track]
|
1237 |
categories = [pt['category'] for pt in current_track]
|
1238 |
|
1239 |
+
# Get colors based on intensity (Saffir-Simpson-like scale)
|
1240 |
+
colors = []
|
1241 |
+
for intensity in intensities:
|
1242 |
+
if intensity < 34:
|
1243 |
+
colors.append('#80808080') # Gray - TD
|
1244 |
+
elif intensity < 64:
|
1245 |
+
colors.append('#0000FF80') # Blue - TS
|
1246 |
+
elif intensity < 83:
|
1247 |
+
colors.append('#00FF0080') # Green - Cat 1
|
1248 |
+
elif intensity < 96:
|
1249 |
+
colors.append('#FFFF0080') # Yellow - Cat 2
|
1250 |
+
elif intensity < 113:
|
1251 |
+
colors.append('#FFA50080') # Orange - Cat 3
|
1252 |
+
elif intensity < 137:
|
1253 |
+
colors.append('#FF000080') # Red - Cat 4
|
1254 |
+
else:
|
1255 |
+
colors.append('#8B000080') # DarkRed - Cat 5
|
1256 |
|
1257 |
+
# Historical track line
|
1258 |
+
frame_data.append(
|
1259 |
+
go.Scattergeo(
|
1260 |
+
lat=lats,
|
1261 |
+
lon=lons,
|
1262 |
+
mode='lines',
|
1263 |
+
line=dict(
|
1264 |
+
width=2,
|
1265 |
+
color='gray'
|
1266 |
+
),
|
1267 |
+
name=f"{storm_id} Track",
|
1268 |
+
showlegend=(day_idx == 0),
|
1269 |
+
hoverinfo='skip'
|
1270 |
+
)
|
1271 |
+
)
|
1272 |
+
|
1273 |
+
# Current position marker
|
1274 |
+
frame_data.append(
|
1275 |
+
go.Scattergeo(
|
1276 |
+
lat=[lats[-1]],
|
1277 |
+
lon=[lons[-1]],
|
1278 |
+
mode='markers',
|
1279 |
+
marker=dict(
|
1280 |
+
size=10,
|
1281 |
+
symbol='circle',
|
1282 |
+
color=colors[-1]
|
1283 |
+
),
|
1284 |
+
name=f"{storm_id} Position",
|
1285 |
+
showlegend=(day_idx == 0),
|
1286 |
+
hovertemplate=(
|
1287 |
+
f"{storm_id}<br>"
|
1288 |
+
f"Lat: {lats[-1]:.1f}°N<br>"
|
1289 |
+
f"Lon: {lons[-1]:.1f}°E<br>"
|
1290 |
+
f"Intensity: {intensities[-1]} kt<br>"
|
1291 |
+
f"Category: {categories[-1]}<br>"
|
1292 |
+
'<extra></extra>'
|
1293 |
+
)
|
1294 |
+
)
|
1295 |
+
)
|
1296 |
+
|
1297 |
# Create frame
|
1298 |
frames.append(go.Frame(
|
1299 |
data=frame_data,
|
|
|
1321 |
|
1322 |
fig.frames = frames
|
1323 |
|
1324 |
+
# Set up initial frame, add play/pause controls, sliders, etc.
|
1325 |
+
slider_steps = [
|
1326 |
+
dict(method="animate",
|
1327 |
+
args=[[frame.name], {
|
1328 |
+
"mode": "immediate",
|
1329 |
+
"frame": {"duration": 500, "redraw": True},
|
1330 |
+
"transition": {"duration": 0}
|
1331 |
+
}],
|
1332 |
+
label=frame.name)
|
1333 |
+
for frame in frames
|
1334 |
+
]
|
1335 |
+
|
1336 |
+
fig.update_layout(
|
1337 |
+
updatemenus=[dict(
|
1338 |
+
type="buttons",
|
1339 |
+
showactive=False,
|
1340 |
+
y=0,
|
1341 |
+
x=1.05,
|
1342 |
+
xanchor="right",
|
1343 |
+
yanchor="top",
|
1344 |
+
pad=dict(t=0, r=10),
|
1345 |
+
buttons=[
|
1346 |
+
dict(label="▶️ Play",
|
1347 |
+
method="animate",
|
1348 |
+
args=[None, {
|
1349 |
+
"frame": {"duration": 500, "redraw": True},
|
1350 |
+
"fromcurrent": True,
|
1351 |
+
"transition": {"duration": 0}
|
1352 |
+
}]),
|
1353 |
+
dict(label="⏸️ Pause",
|
1354 |
+
method="animate",
|
1355 |
+
args=[[None], {
|
1356 |
+
"frame": {"duration": 0, "redraw": False},
|
1357 |
+
"mode": "immediate",
|
1358 |
+
"transition": {"duration": 0}
|
1359 |
+
}])
|
1360 |
+
]
|
1361 |
+
)],
|
1362 |
+
sliders=[dict(
|
1363 |
+
active=0,
|
1364 |
+
pad=dict(t=50),
|
1365 |
+
steps=slider_steps
|
1366 |
+
)]
|
1367 |
+
)
|
1368 |
|
1369 |
else:
|
1370 |
# STATIC VERSION (Final state overview)
|
|
|
1392 |
)
|
1393 |
)
|
1394 |
|
1395 |
+
# Add complete storm tracks
|
1396 |
+
for storm in storm_predictions:
|
1397 |
+
track = storm['track']
|
1398 |
+
if track:
|
1399 |
+
lats = [pt['lat'] for pt in track]
|
1400 |
+
lons = [pt['lon'] for pt in track]
|
1401 |
+
fig.add_trace(
|
1402 |
+
go.Scattergeo(
|
1403 |
+
lat=lats,
|
1404 |
+
lon=lons,
|
1405 |
+
mode='lines+markers',
|
1406 |
+
line=dict(width=2, color='gray'),
|
1407 |
+
marker=dict(
|
1408 |
+
size=6,
|
1409 |
+
symbol='circle',
|
1410 |
+
color='red'
|
1411 |
+
),
|
1412 |
+
name=f"{storm['storm_id']} Full Track"
|
1413 |
+
)
|
1414 |
+
)
|
1415 |
|
1416 |
# FINAL LAYOUT (Professional meteorological style)
|
1417 |
fig.update_layout(
|
|
|
1451 |
)
|
1452 |
|
1453 |
return fig
|
1454 |
+
|
1455 |
except Exception as e:
|
1456 |
logging.error(f"Error creating professional genesis animation: {e}")
|
1457 |
import traceback
|