Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -20,6 +20,8 @@ import tempfile
|
|
| 20 |
import csv
|
| 21 |
from collections import defaultdict
|
| 22 |
import filecmp
|
|
|
|
|
|
|
| 23 |
|
| 24 |
# Command-line argument parsing
|
| 25 |
parser = argparse.ArgumentParser(description='Typhoon Analysis Dashboard')
|
|
@@ -47,20 +49,20 @@ color_map = {
|
|
| 47 |
|
| 48 |
# Classification standards with distinct colors for Matplotlib
|
| 49 |
atlantic_standard = {
|
| 50 |
-
'C5 Super Typhoon': {'wind_speed': 137, 'color': 'Red', 'hex': '#FF0000'},
|
| 51 |
-
'C4 Very Strong Typhoon': {'wind_speed': 113, 'color': 'Orange', 'hex': '#FFA500'},
|
| 52 |
-
'C3 Strong Typhoon': {'wind_speed': 96, 'color': 'Yellow', 'hex': '#FFFF00'},
|
| 53 |
-
'C2 Typhoon': {'wind_speed': 83, 'color': 'Green', 'hex': '#00FF00'},
|
| 54 |
-
'C1 Typhoon': {'wind_speed': 64, 'color': 'Cyan', 'hex': '#00FFFF'},
|
| 55 |
-
'Tropical Storm': {'wind_speed': 34, 'color': 'Blue', 'hex': '#0000FF'},
|
| 56 |
-
'Tropical Depression': {'wind_speed': 0, 'color': 'Gray', 'hex': '#808080'}
|
| 57 |
}
|
| 58 |
|
| 59 |
taiwan_standard = {
|
| 60 |
-
'Strong Typhoon': {'wind_speed': 51.0, 'color': 'Red', 'hex': '#FF0000'},
|
| 61 |
-
'Medium Typhoon': {'wind_speed': 33.7, 'color': 'Orange', 'hex': '#FFA500'},
|
| 62 |
-
'Mild Typhoon': {'wind_speed': 17.2, 'color': 'Yellow', 'hex': '#FFFF00'},
|
| 63 |
-
'Tropical Depression': {'wind_speed': 0, 'color': 'Gray', 'hex': '#808080'}
|
| 64 |
}
|
| 65 |
|
| 66 |
# Data loading and preprocessing functions
|
|
@@ -302,7 +304,7 @@ def generate_main_analysis(start_year, start_month, end_year, end_month, enso_ph
|
|
| 302 |
|
| 303 |
return tracks_fig, wind_scatter, pressure_scatter, regression_fig, slopes_text
|
| 304 |
|
| 305 |
-
# Video animation function with
|
| 306 |
def categorize_typhoon_by_standard(wind_speed, standard):
|
| 307 |
if standard == 'taiwan':
|
| 308 |
wind_speed_ms = wind_speed * 0.514444
|
|
@@ -341,9 +343,9 @@ def generate_track_video(year, typhoon, standard):
|
|
| 341 |
lat_padding = max((max_lat - min_lat) * 0.3, 5)
|
| 342 |
lon_padding = max((max_lon - min_lon) * 0.3, 5)
|
| 343 |
|
| 344 |
-
# Set up the figure
|
| 345 |
-
fig = plt.figure(figsize=(9, 7), dpi=100)
|
| 346 |
-
ax = plt.axes([0.05, 0.
|
| 347 |
ax.set_extent([min_lon - lon_padding, max_lon + lon_padding, min_lat - lat_padding, max_lat + lat_padding], crs=ccrs.PlateCarree())
|
| 348 |
|
| 349 |
# Add world map features
|
|
@@ -360,18 +362,18 @@ def generate_track_video(year, typhoon, standard):
|
|
| 360 |
point, = ax.plot([], [], 'o', markersize=8, transform=ccrs.PlateCarree())
|
| 361 |
date_text = ax.text(0.02, 0.02, '', transform=ax.transAxes, fontsize=10, bbox=dict(facecolor='white', alpha=0.8))
|
| 362 |
|
| 363 |
-
# Add sidebar
|
| 364 |
-
details_title = fig.text(0.
|
| 365 |
-
details_text = fig.text(0.
|
| 366 |
bbox=dict(facecolor='white', alpha=0.8, boxstyle='round,pad=0.5'))
|
| 367 |
|
| 368 |
-
# Add color legend
|
| 369 |
standard_dict = atlantic_standard if standard == 'atlantic' else taiwan_standard
|
| 370 |
legend_elements = [plt.Line2D([0], [0], marker='o', color='w', label=f"{cat}",
|
| 371 |
-
|
| 372 |
for cat, details in standard_dict.items()]
|
| 373 |
-
fig.legend(handles=legend_elements, title="Color Legend", loc='
|
| 374 |
-
bbox_to_anchor=(0.95, 0.
|
| 375 |
|
| 376 |
def init():
|
| 377 |
line.set_data([], [])
|
|
@@ -393,7 +395,6 @@ def generate_track_video(year, typhoon, standard):
|
|
| 393 |
details_text.set_text(details)
|
| 394 |
return line, point, date_text, details_text
|
| 395 |
|
| 396 |
-
# Create animation
|
| 397 |
ani = animation.FuncAnimation(fig, update, init_func=init, frames=len(storm.time),
|
| 398 |
interval=200, blit=True, repeat=True)
|
| 399 |
|
|
@@ -439,7 +440,118 @@ def perform_longitude_regression(start_year, start_month, end_year, end_month):
|
|
| 439 |
beta_1, exp_beta_1, p_value = model.params['ONI'], np.exp(model.params['ONI']), model.pvalues['ONI']
|
| 440 |
return f"Longitude Regression: β1={beta_1:.4f}, Odds Ratio={exp_beta_1:.4f}, P-value={p_value:.4f}"
|
| 441 |
|
| 442 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 443 |
with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
|
| 444 |
gr.Markdown("# Typhoon Analysis Dashboard")
|
| 445 |
|
|
@@ -451,9 +563,11 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
|
|
| 451 |
|
| 452 |
### Features:
|
| 453 |
- **Track Visualization**: View typhoon tracks by time period and ENSO phase
|
| 454 |
-
- **
|
| 455 |
-
- **
|
| 456 |
-
- **
|
|
|
|
|
|
|
| 457 |
|
| 458 |
Select a tab above to begin your analysis.
|
| 459 |
""")
|
|
@@ -623,7 +737,7 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
|
|
| 623 |
5. The animation shows the typhoon track growing over a world map, with:
|
| 624 |
- Date on the bottom left
|
| 625 |
- Sidebar on the right showing typhoon details (name, date, wind speed, category) as it moves
|
| 626 |
-
- Color legend with colored markers
|
| 627 |
""")
|
| 628 |
|
| 629 |
def update_typhoon_options(year):
|
|
@@ -638,6 +752,26 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
|
|
| 638 |
inputs=[year_dropdown, typhoon_dropdown, standard_dropdown],
|
| 639 |
outputs=path_video
|
| 640 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 641 |
|
| 642 |
# Custom CSS for better visibility
|
| 643 |
gr.HTML("""
|
|
|
|
| 20 |
import csv
|
| 21 |
from collections import defaultdict
|
| 22 |
import filecmp
|
| 23 |
+
from sklearn.manifold import TSNE
|
| 24 |
+
from sklearn.cluster import DBSCAN
|
| 25 |
|
| 26 |
# Command-line argument parsing
|
| 27 |
parser = argparse.ArgumentParser(description='Typhoon Analysis Dashboard')
|
|
|
|
| 49 |
|
| 50 |
# Classification standards with distinct colors for Matplotlib
|
| 51 |
atlantic_standard = {
|
| 52 |
+
'C5 Super Typhoon': {'wind_speed': 137, 'color': 'Red', 'hex': '#FF0000'},
|
| 53 |
+
'C4 Very Strong Typhoon': {'wind_speed': 113, 'color': 'Orange', 'hex': '#FFA500'},
|
| 54 |
+
'C3 Strong Typhoon': {'wind_speed': 96, 'color': 'Yellow', 'hex': '#FFFF00'},
|
| 55 |
+
'C2 Typhoon': {'wind_speed': 83, 'color': 'Green', 'hex': '#00FF00'},
|
| 56 |
+
'C1 Typhoon': {'wind_speed': 64, 'color': 'Cyan', 'hex': '#00FFFF'},
|
| 57 |
+
'Tropical Storm': {'wind_speed': 34, 'color': 'Blue', 'hex': '#0000FF'},
|
| 58 |
+
'Tropical Depression': {'wind_speed': 0, 'color': 'Gray', 'hex': '#808080'}
|
| 59 |
}
|
| 60 |
|
| 61 |
taiwan_standard = {
|
| 62 |
+
'Strong Typhoon': {'wind_speed': 51.0, 'color': 'Red', 'hex': '#FF0000'},
|
| 63 |
+
'Medium Typhoon': {'wind_speed': 33.7, 'color': 'Orange', 'hex': '#FFA500'},
|
| 64 |
+
'Mild Typhoon': {'wind_speed': 17.2, 'color': 'Yellow', 'hex': '#FFFF00'},
|
| 65 |
+
'Tropical Depression': {'wind_speed': 0, 'color': 'Gray', 'hex': '#808080'}
|
| 66 |
}
|
| 67 |
|
| 68 |
# Data loading and preprocessing functions
|
|
|
|
| 304 |
|
| 305 |
return tracks_fig, wind_scatter, pressure_scatter, regression_fig, slopes_text
|
| 306 |
|
| 307 |
+
# Video animation function with fixed sidebar
|
| 308 |
def categorize_typhoon_by_standard(wind_speed, standard):
|
| 309 |
if standard == 'taiwan':
|
| 310 |
wind_speed_ms = wind_speed * 0.514444
|
|
|
|
| 343 |
lat_padding = max((max_lat - min_lat) * 0.3, 5)
|
| 344 |
lon_padding = max((max_lon - min_lon) * 0.3, 5)
|
| 345 |
|
| 346 |
+
# Set up the figure (900x700 pixels at 100 DPI)
|
| 347 |
+
fig = plt.figure(figsize=(9, 7), dpi=100)
|
| 348 |
+
ax = plt.axes([0.05, 0.05, 0.65, 0.90], projection=ccrs.PlateCarree()) # Adjusted to leave space for sidebar
|
| 349 |
ax.set_extent([min_lon - lon_padding, max_lon + lon_padding, min_lat - lat_padding, max_lat + lat_padding], crs=ccrs.PlateCarree())
|
| 350 |
|
| 351 |
# Add world map features
|
|
|
|
| 362 |
point, = ax.plot([], [], 'o', markersize=8, transform=ccrs.PlateCarree())
|
| 363 |
date_text = ax.text(0.02, 0.02, '', transform=ax.transAxes, fontsize=10, bbox=dict(facecolor='white', alpha=0.8))
|
| 364 |
|
| 365 |
+
# Add sidebar on the right
|
| 366 |
+
details_title = fig.text(0.75, 0.95, "Typhoon Details", fontsize=12, fontweight='bold', verticalalignment='top')
|
| 367 |
+
details_text = fig.text(0.75, 0.85, '', fontsize=10, verticalalignment='top',
|
| 368 |
bbox=dict(facecolor='white', alpha=0.8, boxstyle='round,pad=0.5'))
|
| 369 |
|
| 370 |
+
# Add color legend
|
| 371 |
standard_dict = atlantic_standard if standard == 'atlantic' else taiwan_standard
|
| 372 |
legend_elements = [plt.Line2D([0], [0], marker='o', color='w', label=f"{cat}",
|
| 373 |
+
markerfacecolor=details['hex'], markersize=10)
|
| 374 |
for cat, details in standard_dict.items()]
|
| 375 |
+
fig.legend(handles=legend_elements, title="Color Legend", loc='lower right',
|
| 376 |
+
bbox_to_anchor=(0.95, 0.05), fontsize=10)
|
| 377 |
|
| 378 |
def init():
|
| 379 |
line.set_data([], [])
|
|
|
|
| 395 |
details_text.set_text(details)
|
| 396 |
return line, point, date_text, details_text
|
| 397 |
|
|
|
|
| 398 |
ani = animation.FuncAnimation(fig, update, init_func=init, frames=len(storm.time),
|
| 399 |
interval=200, blit=True, repeat=True)
|
| 400 |
|
|
|
|
| 440 |
beta_1, exp_beta_1, p_value = model.params['ONI'], np.exp(model.params['ONI']), model.pvalues['ONI']
|
| 441 |
return f"Longitude Regression: β1={beta_1:.4f}, Odds Ratio={exp_beta_1:.4f}, P-value={p_value:.4f}"
|
| 442 |
|
| 443 |
+
# t-SNE clustering functions
|
| 444 |
+
def filter_west_pacific_coordinates(lons, lats):
|
| 445 |
+
mask = (lons >= 100) & (lons <= 180) & (lats >= 0) & (lats <= 50)
|
| 446 |
+
return lons[mask], lats[mask]
|
| 447 |
+
|
| 448 |
+
def update_route_clusters(start_year, start_month, end_year, end_month, enso_value, season):
|
| 449 |
+
start_date = datetime(int(start_year), int(start_month), 1)
|
| 450 |
+
end_date = datetime(int(end_year), int(end_month), 28)
|
| 451 |
+
|
| 452 |
+
all_storms_data = []
|
| 453 |
+
for year in range(int(start_year), int(end_year) + 1):
|
| 454 |
+
season_data = ibtracs.get_season(year)
|
| 455 |
+
for storm_id in season_data.summary()['id']:
|
| 456 |
+
storm = ibtracs.get_storm(storm_id)
|
| 457 |
+
if storm.time[0] >= start_date and storm.time[-1] <= end_date:
|
| 458 |
+
lons, lats = filter_west_pacific_coordinates(np.array(storm.lon), np.array(storm.lat))
|
| 459 |
+
if len(lons) > 1:
|
| 460 |
+
all_storms_data.append((lons, lats, np.array(storm.vmax), np.array(storm.mslp), np.array(storm.time), storm.name))
|
| 461 |
+
|
| 462 |
+
if not all_storms_data:
|
| 463 |
+
return go.Figure(), go.Figure(), go.Figure(), "No storms found in the selected period."
|
| 464 |
+
|
| 465 |
+
# Prepare route vectors for t-SNE
|
| 466 |
+
max_length = max(len(st[0]) for st in all_storms_data)
|
| 467 |
+
route_vectors = []
|
| 468 |
+
for lons, lats, _, _, _, _ in all_storms_data:
|
| 469 |
+
interp_lons = np.interp(np.linspace(0, 1, max_length), np.linspace(0, 1, len(lons)), lons)
|
| 470 |
+
interp_lats = np.interp(np.linspace(0, 1, max_length), np.linspace(0, 1, len(lats)), lats)
|
| 471 |
+
route_vectors.append(np.column_stack((interp_lons, interp_lats)).flatten())
|
| 472 |
+
route_vectors = np.array(route_vectors)
|
| 473 |
+
|
| 474 |
+
# Perform t-SNE
|
| 475 |
+
tsne_results = TSNE(n_components=2, random_state=42, perplexity=min(30, len(route_vectors)-1)).fit_transform(route_vectors)
|
| 476 |
+
|
| 477 |
+
# Dynamic DBSCAN clustering
|
| 478 |
+
target_clusters = min(5, len(all_storms_data) // 3)
|
| 479 |
+
eps_range = np.arange(5.0, 50.0, 5.0)
|
| 480 |
+
min_samples = max(3, len(all_storms_data) // 20)
|
| 481 |
+
best_labels = None
|
| 482 |
+
best_eps = None
|
| 483 |
+
best_n_clusters = 0
|
| 484 |
+
best_noise_ratio = 1.0
|
| 485 |
+
|
| 486 |
+
for eps in eps_range:
|
| 487 |
+
dbscan = DBSCAN(eps=eps, min_samples=min_samples)
|
| 488 |
+
labels = dbscan.fit_predict(tsne_results)
|
| 489 |
+
n_clusters = len(set(labels)) - (1 if -1 in labels else 0)
|
| 490 |
+
noise_points = np.sum(labels == -1)
|
| 491 |
+
noise_ratio = noise_points / len(labels)
|
| 492 |
+
|
| 493 |
+
if n_clusters >= target_clusters and noise_ratio < 0.3 and (n_clusters > best_n_clusters or (n_clusters == best_n_clusters and noise_ratio < best_noise_ratio)):
|
| 494 |
+
best_labels = labels
|
| 495 |
+
best_eps = eps
|
| 496 |
+
best_n_clusters = n_clusters
|
| 497 |
+
best_noise_ratio = noise_ratio
|
| 498 |
+
|
| 499 |
+
if best_labels is None:
|
| 500 |
+
dbscan = DBSCAN(eps=5.0, min_samples=min_samples)
|
| 501 |
+
best_labels = dbscan.fit_predict(tsne_results)
|
| 502 |
+
best_eps = 5.0
|
| 503 |
+
best_n_clusters = len(set(best_labels)) - (1 if -1 in best_labels else 0)
|
| 504 |
+
|
| 505 |
+
# t-SNE Scatter Plot
|
| 506 |
+
fig_tsne = go.Figure()
|
| 507 |
+
for cluster in set(best_labels):
|
| 508 |
+
mask = best_labels == cluster
|
| 509 |
+
name = "Noise" if cluster == -1 else f"Cluster {cluster}"
|
| 510 |
+
fig_tsne.add_trace(go.Scatter(
|
| 511 |
+
x=tsne_results[mask, 0], y=tsne_results[mask, 1], mode='markers',
|
| 512 |
+
name=name, text=[all_storms_data[i][5] for i in range(len(all_storms_data)) if mask[i]],
|
| 513 |
+
hoverinfo='text'
|
| 514 |
+
))
|
| 515 |
+
fig_tsne.update_layout(title="t-SNE Clustering of Typhoon Routes", xaxis_title="t-SNE 1", yaxis_title="t-SNE 2")
|
| 516 |
+
|
| 517 |
+
# Typhoon Routes Plot
|
| 518 |
+
fig_routes = go.Figure()
|
| 519 |
+
for i, (lons, lats, _, _, _, name) in enumerate(all_storms_data):
|
| 520 |
+
cluster = best_labels[i]
|
| 521 |
+
color = 'gray' if cluster == -1 else px.colors.qualitative.Plotly[cluster % len(px.colors.qualitative.Plotly)]
|
| 522 |
+
fig_routes.add_trace(go.Scattergeo(
|
| 523 |
+
lon=lons, lat=lats, mode='lines+markers', name=name,
|
| 524 |
+
line=dict(color=color), marker=dict(size=4), hoverinfo='text', text=name
|
| 525 |
+
))
|
| 526 |
+
fig_routes.update_layout(
|
| 527 |
+
title="Typhoon Routes by Cluster",
|
| 528 |
+
geo=dict(scope='asia', projection_type='mercator', showland=True, landcolor='lightgray')
|
| 529 |
+
)
|
| 530 |
+
|
| 531 |
+
# Cluster Statistics Plot
|
| 532 |
+
cluster_stats = []
|
| 533 |
+
for cluster in set(best_labels) - {-1}:
|
| 534 |
+
mask = best_labels == cluster
|
| 535 |
+
winds = [all_storms_data[i][2].max() for i in range(len(all_storms_data)) if mask[i]]
|
| 536 |
+
pressures = [all_storms_data[i][3].min() for i in range(len(all_storms_data)) if mask[i]]
|
| 537 |
+
cluster_stats.append({
|
| 538 |
+
'Cluster': cluster,
|
| 539 |
+
'Count': np.sum(mask),
|
| 540 |
+
'Mean Wind': np.mean(winds),
|
| 541 |
+
'Mean Pressure': np.mean(pressures)
|
| 542 |
+
})
|
| 543 |
+
stats_df = pd.DataFrame(cluster_stats)
|
| 544 |
+
fig_stats = px.bar(stats_df, x='Cluster', y=['Mean Wind', 'Mean Pressure'], barmode='group',
|
| 545 |
+
title="Cluster Statistics (Mean Wind Speed and Pressure)")
|
| 546 |
+
|
| 547 |
+
# Cluster Information
|
| 548 |
+
cluster_info = f"Number of Clusters: {best_n_clusters}\nBest EPS: {best_eps}\nNoise Points: {best_noise_ratio*100:.1f}%"
|
| 549 |
+
for stat in cluster_stats:
|
| 550 |
+
cluster_info += f"\nCluster {stat['Cluster']}: {stat['Count']} storms, Mean Wind: {stat['Mean Wind']:.1f} kt, Mean Pressure: {stat['Mean Pressure']:.1f} hPa"
|
| 551 |
+
|
| 552 |
+
return fig_tsne, fig_routes, fig_stats, cluster_info
|
| 553 |
+
|
| 554 |
+
# Gradio Interface
|
| 555 |
with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
|
| 556 |
gr.Markdown("# Typhoon Analysis Dashboard")
|
| 557 |
|
|
|
|
| 563 |
|
| 564 |
### Features:
|
| 565 |
- **Track Visualization**: View typhoon tracks by time period and ENSO phase
|
| 566 |
+
- **Wind Analysis**: Examine wind speed vs ONI relationships
|
| 567 |
+
- **Pressure Analysis**: Analyze pressure vs ONI relationships
|
| 568 |
+
- **Longitude Analysis**: Study typhoon generation longitude vs ONI
|
| 569 |
+
- **Path Animation**: Watch animated typhoon paths with a sidebar
|
| 570 |
+
- **TSNE Cluster**: Perform t-SNE clustering on typhoon routes
|
| 571 |
|
| 572 |
Select a tab above to begin your analysis.
|
| 573 |
""")
|
|
|
|
| 737 |
5. The animation shows the typhoon track growing over a world map, with:
|
| 738 |
- Date on the bottom left
|
| 739 |
- Sidebar on the right showing typhoon details (name, date, wind speed, category) as it moves
|
| 740 |
+
- Color legend with colored markers on the bottom right
|
| 741 |
""")
|
| 742 |
|
| 743 |
def update_typhoon_options(year):
|
|
|
|
| 752 |
inputs=[year_dropdown, typhoon_dropdown, standard_dropdown],
|
| 753 |
outputs=path_video
|
| 754 |
)
|
| 755 |
+
|
| 756 |
+
with gr.Tab("TSNE Cluster"):
|
| 757 |
+
with gr.Row():
|
| 758 |
+
tsne_start_year = gr.Number(label="Start Year", value=2000, minimum=1900, maximum=2024, step=1)
|
| 759 |
+
tsne_start_month = gr.Dropdown(label="Start Month", choices=list(range(1, 13)), value=1)
|
| 760 |
+
tsne_end_year = gr.Number(label="End Year", value=2024, minimum=1900, maximum=2024, step=1)
|
| 761 |
+
tsne_end_month = gr.Dropdown(label="End Month", choices=list(range(1, 13)), value=12)
|
| 762 |
+
tsne_enso_phase = gr.Dropdown(label="ENSO Phase", choices=['all', 'El Nino', 'La Nina', 'Neutral'], value='all')
|
| 763 |
+
tsne_season = gr.Dropdown(label="Season", choices=['all', 'summer', 'winter'], value='all')
|
| 764 |
+
tsne_analyze_btn = gr.Button("Analyze")
|
| 765 |
+
tsne_plot = gr.Plot(label="t-SNE Clusters")
|
| 766 |
+
routes_plot = gr.Plot(label="Typhoon Routes")
|
| 767 |
+
stats_plot = gr.Plot(label="Cluster Statistics")
|
| 768 |
+
cluster_info = gr.Textbox(label="Cluster Information", lines=10)
|
| 769 |
+
|
| 770 |
+
tsne_analyze_btn.click(
|
| 771 |
+
fn=update_route_clusters,
|
| 772 |
+
inputs=[tsne_start_year, tsne_start_month, tsne_end_year, tsne_end_month, tsne_enso_phase, tsne_season],
|
| 773 |
+
outputs=[tsne_plot, routes_plot, stats_plot, cluster_info]
|
| 774 |
+
)
|
| 775 |
|
| 776 |
# Custom CSS for better visibility
|
| 777 |
gr.HTML("""
|