Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -33,7 +33,7 @@ DATA_PATH = args.data_path
|
|
| 33 |
|
| 34 |
ONI_DATA_PATH = os.path.join(DATA_PATH, 'oni_data.csv')
|
| 35 |
TYPHOON_DATA_PATH = os.path.join(DATA_PATH, 'processed_typhoon_data.csv')
|
| 36 |
-
LOCAL_iBtrace_PATH = os.path.join(DATA_PATH, 'ibtracs.
|
| 37 |
iBtrace_uri = 'https://www.ncei.noaa.gov/data/international-best-track-archive-for-climate-stewardship-ibtracs/v04r01/access/csv/ibtracs.ALL.list.v04r01.csv'
|
| 38 |
CACHE_FILE = 'ibtracs_cache.pkl'
|
| 39 |
CACHE_EXPIRY_DAYS = 1
|
|
@@ -325,6 +325,83 @@ def generate_main_analysis(start_year, start_month, end_year, end_month, enso_ph
|
|
| 325 |
|
| 326 |
return tracks_fig, wind_scatter, pressure_scatter, regression_fig, slopes_text
|
| 327 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 328 |
# Video animation function with fixed sidebar and wind radius visualization
|
| 329 |
def categorize_typhoon_by_standard(wind_speed, standard):
|
| 330 |
if standard == 'taiwan':
|
|
@@ -523,6 +600,21 @@ def generate_track_video(year, basin, typhoon, standard):
|
|
| 523 |
|
| 524 |
return temp_file.name
|
| 525 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 526 |
# Logistic regression functions
|
| 527 |
def perform_wind_regression(start_year, start_month, end_year, end_month):
|
| 528 |
start_date = datetime(start_year, start_month, 1)
|
|
@@ -916,6 +1008,45 @@ def update_route_clusters(start_year, start_month, end_year, end_month, enso_val
|
|
| 916 |
cluster_info_text = "\n".join(cluster_info_lines)
|
| 917 |
return fig_tsne, fig_routes, fig_stats, cluster_info_text
|
| 918 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 919 |
# Gradio Interface
|
| 920 |
with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
|
| 921 |
gr.Markdown("# Typhoon Analysis Dashboard")
|
|
@@ -949,7 +1080,6 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
|
|
| 949 |
tracks_plot = gr.Plot(label="Typhoon Tracks", elem_id="tracks_plot")
|
| 950 |
typhoon_count = gr.Textbox(label="Number of Typhoons Displayed")
|
| 951 |
|
| 952 |
-
# Function definitions remain the same
|
| 953 |
analyze_btn.click(
|
| 954 |
fn=get_full_tracks,
|
| 955 |
inputs=[start_year, start_month, end_year, end_month, enso_phase, typhoon_search],
|
|
@@ -968,7 +1098,6 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
|
|
| 968 |
wind_scatter = gr.Plot(label="Wind Speed vs ONI")
|
| 969 |
wind_regression_results = gr.Textbox(label="Wind Regression Results")
|
| 970 |
|
| 971 |
-
# Function definitions remain the same
|
| 972 |
wind_analyze_btn.click(
|
| 973 |
fn=get_wind_analysis,
|
| 974 |
inputs=[wind_start_year, wind_start_month, wind_end_year, wind_end_month, wind_enso_phase, wind_typhoon_search],
|
|
@@ -987,7 +1116,6 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
|
|
| 987 |
pressure_scatter = gr.Plot(label="Pressure vs ONI")
|
| 988 |
pressure_regression_results = gr.Textbox(label="Pressure Regression Results")
|
| 989 |
|
| 990 |
-
# Function definitions remain the same
|
| 991 |
pressure_analyze_btn.click(
|
| 992 |
fn=get_pressure_analysis,
|
| 993 |
inputs=[pressure_start_year, pressure_start_month, pressure_end_year, pressure_end_month, pressure_enso_phase, pressure_typhoon_search],
|
|
@@ -1007,7 +1135,6 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
|
|
| 1007 |
slopes_text = gr.Textbox(label="Regression Slopes")
|
| 1008 |
lon_regression_results = gr.Textbox(label="Longitude Regression Results")
|
| 1009 |
|
| 1010 |
-
# Function definitions remain the same
|
| 1011 |
lon_analyze_btn.click(
|
| 1012 |
fn=get_longitude_analysis,
|
| 1013 |
inputs=[lon_start_year, lon_start_month, lon_end_year, lon_end_month, lon_enso_phase, lon_typhoon_search],
|
|
@@ -1058,8 +1185,6 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
|
|
| 1058 |
Different agencies use different wind speed averaging periods: USA (1-min), JTWC (1-min), JMA (10-min), IMD (3-min).
|
| 1059 |
""")
|
| 1060 |
|
| 1061 |
-
# Basin to prefix mapping and functions remain the same
|
| 1062 |
-
|
| 1063 |
year_dropdown.change(fn=update_typhoon_options, inputs=[year_dropdown, basin_dropdown], outputs=typhoon_dropdown)
|
| 1064 |
basin_dropdown.change(fn=update_typhoon_options, inputs=[year_dropdown, basin_dropdown], outputs=typhoon_dropdown)
|
| 1065 |
|
|
|
|
| 33 |
|
| 34 |
ONI_DATA_PATH = os.path.join(DATA_PATH, 'oni_data.csv')
|
| 35 |
TYPHOON_DATA_PATH = os.path.join(DATA_PATH, 'processed_typhoon_data.csv')
|
| 36 |
+
LOCAL_iBtrace_PATH = os.path.join(DATA_PATH, 'ibtracs.WP.list.v04r01.csv')
|
| 37 |
iBtrace_uri = 'https://www.ncei.noaa.gov/data/international-best-track-archive-for-climate-stewardship-ibtracs/v04r01/access/csv/ibtracs.ALL.list.v04r01.csv'
|
| 38 |
CACHE_FILE = 'ibtracs_cache.pkl'
|
| 39 |
CACHE_EXPIRY_DAYS = 1
|
|
|
|
| 325 |
|
| 326 |
return tracks_fig, wind_scatter, pressure_scatter, regression_fig, slopes_text
|
| 327 |
|
| 328 |
+
# Get full tracks function for Track Visualization tab
|
| 329 |
+
def get_full_tracks(start_year, start_month, end_year, end_month, enso_phase, typhoon_search):
|
| 330 |
+
start_date = datetime(start_year, start_month, 1)
|
| 331 |
+
end_date = datetime(end_year, end_month, 28)
|
| 332 |
+
filtered_data = merged_data[
|
| 333 |
+
(merged_data['ISO_TIME'] >= start_date) &
|
| 334 |
+
(merged_data['ISO_TIME'] <= end_date)
|
| 335 |
+
]
|
| 336 |
+
filtered_data['ENSO_Phase'] = filtered_data['ONI'].apply(classify_enso_phases)
|
| 337 |
+
if enso_phase != 'all':
|
| 338 |
+
filtered_data = filtered_data[filtered_data['ENSO_Phase'] == enso_phase.capitalize()]
|
| 339 |
+
unique_storms = filtered_data['SID'].unique()
|
| 340 |
+
count = len(unique_storms)
|
| 341 |
+
fig = go.Figure()
|
| 342 |
+
for sid in unique_storms:
|
| 343 |
+
storm_data = typhoon_data[typhoon_data['SID'] == sid]
|
| 344 |
+
name = storm_data['NAME'].iloc[0] if not pd.isna(storm_data['NAME'].iloc[0]) else "Unnamed"
|
| 345 |
+
storm_oni = filtered_data[filtered_data['SID'] == sid]['ONI'].iloc[0]
|
| 346 |
+
color = 'red' if storm_oni >= 0.5 else ('blue' if storm_oni <= -0.5 else 'green')
|
| 347 |
+
fig.add_trace(go.Scattergeo(
|
| 348 |
+
lon=storm_data['LON'], lat=storm_data['LAT'], mode='lines',
|
| 349 |
+
name=f"{name} ({storm_data['SEASON'].iloc[0]})",
|
| 350 |
+
line=dict(width=1.5, color=color),
|
| 351 |
+
hoverinfo="name"
|
| 352 |
+
))
|
| 353 |
+
if typhoon_search:
|
| 354 |
+
search_mask = typhoon_data['NAME'].str.contains(typhoon_search, case=False, na=False)
|
| 355 |
+
if search_mask.any():
|
| 356 |
+
for sid in typhoon_data[search_mask]['SID'].unique():
|
| 357 |
+
storm_data = typhoon_data[typhoon_data['SID'] == sid]
|
| 358 |
+
fig.add_trace(go.Scattergeo(
|
| 359 |
+
lon=storm_data['LON'], lat=storm_data['LAT'], mode='lines+markers',
|
| 360 |
+
name=f"MATCHED: {storm_data['NAME'].iloc[0]} ({storm_data['SEASON'].iloc[0]})",
|
| 361 |
+
line=dict(width=3, color='yellow'),
|
| 362 |
+
marker=dict(size=5),
|
| 363 |
+
hoverinfo="name"
|
| 364 |
+
))
|
| 365 |
+
fig.update_layout(
|
| 366 |
+
title=f"Typhoon Tracks ({start_year}-{start_month} to {end_year}-{end_month})",
|
| 367 |
+
geo=dict(
|
| 368 |
+
projection_type='natural earth',
|
| 369 |
+
showland=True,
|
| 370 |
+
showcoastlines=True,
|
| 371 |
+
landcolor='rgb(243, 243, 243)',
|
| 372 |
+
countrycolor='rgb(204, 204, 204)',
|
| 373 |
+
coastlinecolor='rgb(204, 204, 204)',
|
| 374 |
+
center=dict(lon=140, lat=20),
|
| 375 |
+
projection_scale=3
|
| 376 |
+
),
|
| 377 |
+
legend_title="Typhoons by ENSO Phase",
|
| 378 |
+
showlegend=True,
|
| 379 |
+
height=700
|
| 380 |
+
)
|
| 381 |
+
fig.add_annotation(
|
| 382 |
+
x=0.02, y=0.98, xref="paper", yref="paper",
|
| 383 |
+
text="Red: El Niño, Blue: La Niña, Green: Neutral",
|
| 384 |
+
showarrow=False, align="left",
|
| 385 |
+
bgcolor="rgba(255,255,255,0.8)"
|
| 386 |
+
)
|
| 387 |
+
return fig, f"Total typhoons displayed: {count}"
|
| 388 |
+
|
| 389 |
+
# Analysis functions for Wind, Pressure, and Longitude tabs
|
| 390 |
+
def get_wind_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search):
|
| 391 |
+
results = generate_main_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search)
|
| 392 |
+
regression = perform_wind_regression(start_year, start_month, end_year, end_month)
|
| 393 |
+
return results[1], regression
|
| 394 |
+
|
| 395 |
+
def get_pressure_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search):
|
| 396 |
+
results = generate_main_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search)
|
| 397 |
+
regression = perform_pressure_regression(start_year, start_month, end_year, end_month)
|
| 398 |
+
return results[2], regression
|
| 399 |
+
|
| 400 |
+
def get_longitude_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search):
|
| 401 |
+
results = generate_main_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search)
|
| 402 |
+
regression = perform_longitude_regression(start_year, start_month, end_year, end_month)
|
| 403 |
+
return results[3], results[4], regression
|
| 404 |
+
|
| 405 |
# Video animation function with fixed sidebar and wind radius visualization
|
| 406 |
def categorize_typhoon_by_standard(wind_speed, standard):
|
| 407 |
if standard == 'taiwan':
|
|
|
|
| 600 |
|
| 601 |
return temp_file.name
|
| 602 |
|
| 603 |
+
def simplified_track_video(year, basin, typhoon, standard):
|
| 604 |
+
if not typhoon:
|
| 605 |
+
return None
|
| 606 |
+
|
| 607 |
+
# Extract storm ID from the dropdown selection
|
| 608 |
+
typhoon_id = typhoon.split('(')[-1].strip(')')
|
| 609 |
+
|
| 610 |
+
# Extract basin code from the basin selection
|
| 611 |
+
basin_code = "All"
|
| 612 |
+
if basin != "All Basins":
|
| 613 |
+
basin_code = basin.split(' - ')[0]
|
| 614 |
+
|
| 615 |
+
# Generate the animation
|
| 616 |
+
return generate_track_video(year, basin_code, typhoon, standard)
|
| 617 |
+
|
| 618 |
# Logistic regression functions
|
| 619 |
def perform_wind_regression(start_year, start_month, end_year, end_month):
|
| 620 |
start_date = datetime(start_year, start_month, 1)
|
|
|
|
| 1008 |
cluster_info_text = "\n".join(cluster_info_lines)
|
| 1009 |
return fig_tsne, fig_routes, fig_stats, cluster_info_text
|
| 1010 |
|
| 1011 |
+
# Define the basin to prefix mapping
|
| 1012 |
+
basin_to_prefix = {
|
| 1013 |
+
"All Basins": None,
|
| 1014 |
+
"NA - North Atlantic": "AL",
|
| 1015 |
+
"EP - Eastern North Pacific": "EP",
|
| 1016 |
+
"WP - Western North Pacific": "WP",
|
| 1017 |
+
"NI - North Indian": ["IO", "BB", "AS"], # Multiple prefixes for North Indian
|
| 1018 |
+
"SI - South Indian": "SI",
|
| 1019 |
+
"SP - Southern Pacific": "SP",
|
| 1020 |
+
"SA - South Atlantic": "SL"
|
| 1021 |
+
}
|
| 1022 |
+
|
| 1023 |
+
# Update typhoon options function for animation tab
|
| 1024 |
+
def update_typhoon_options(year, basin):
|
| 1025 |
+
try:
|
| 1026 |
+
season = ibtracs.get_season(int(year))
|
| 1027 |
+
storm_summary = season.summary()
|
| 1028 |
+
|
| 1029 |
+
# Get the prefix for filtering
|
| 1030 |
+
prefix = basin_to_prefix.get(basin)
|
| 1031 |
+
|
| 1032 |
+
# Get all storms for the year
|
| 1033 |
+
options = []
|
| 1034 |
+
for i in range(len(storm_summary)):
|
| 1035 |
+
try:
|
| 1036 |
+
name = storm_summary['name'][i] if not pd.isna(storm_summary['name'][i]) else "Unnamed"
|
| 1037 |
+
storm_id = storm_summary['id'][i]
|
| 1038 |
+
|
| 1039 |
+
# Filter by basin if a specific basin is selected
|
| 1040 |
+
if prefix is None or (isinstance(prefix, list) and any(storm_id.startswith(p) for p in prefix)) or (not isinstance(prefix, list) and storm_id.startswith(prefix)):
|
| 1041 |
+
options.append(f"{name} ({storm_id})")
|
| 1042 |
+
except Exception:
|
| 1043 |
+
continue
|
| 1044 |
+
|
| 1045 |
+
return gr.update(choices=options, value=options[0] if options else None)
|
| 1046 |
+
except Exception as e:
|
| 1047 |
+
print(f"Error updating typhoon options: {e}")
|
| 1048 |
+
return gr.update(choices=[], value=None)
|
| 1049 |
+
|
| 1050 |
# Gradio Interface
|
| 1051 |
with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
|
| 1052 |
gr.Markdown("# Typhoon Analysis Dashboard")
|
|
|
|
| 1080 |
tracks_plot = gr.Plot(label="Typhoon Tracks", elem_id="tracks_plot")
|
| 1081 |
typhoon_count = gr.Textbox(label="Number of Typhoons Displayed")
|
| 1082 |
|
|
|
|
| 1083 |
analyze_btn.click(
|
| 1084 |
fn=get_full_tracks,
|
| 1085 |
inputs=[start_year, start_month, end_year, end_month, enso_phase, typhoon_search],
|
|
|
|
| 1098 |
wind_scatter = gr.Plot(label="Wind Speed vs ONI")
|
| 1099 |
wind_regression_results = gr.Textbox(label="Wind Regression Results")
|
| 1100 |
|
|
|
|
| 1101 |
wind_analyze_btn.click(
|
| 1102 |
fn=get_wind_analysis,
|
| 1103 |
inputs=[wind_start_year, wind_start_month, wind_end_year, wind_end_month, wind_enso_phase, wind_typhoon_search],
|
|
|
|
| 1116 |
pressure_scatter = gr.Plot(label="Pressure vs ONI")
|
| 1117 |
pressure_regression_results = gr.Textbox(label="Pressure Regression Results")
|
| 1118 |
|
|
|
|
| 1119 |
pressure_analyze_btn.click(
|
| 1120 |
fn=get_pressure_analysis,
|
| 1121 |
inputs=[pressure_start_year, pressure_start_month, pressure_end_year, pressure_end_month, pressure_enso_phase, pressure_typhoon_search],
|
|
|
|
| 1135 |
slopes_text = gr.Textbox(label="Regression Slopes")
|
| 1136 |
lon_regression_results = gr.Textbox(label="Longitude Regression Results")
|
| 1137 |
|
|
|
|
| 1138 |
lon_analyze_btn.click(
|
| 1139 |
fn=get_longitude_analysis,
|
| 1140 |
inputs=[lon_start_year, lon_start_month, lon_end_year, lon_end_month, lon_enso_phase, lon_typhoon_search],
|
|
|
|
| 1185 |
Different agencies use different wind speed averaging periods: USA (1-min), JTWC (1-min), JMA (10-min), IMD (3-min).
|
| 1186 |
""")
|
| 1187 |
|
|
|
|
|
|
|
| 1188 |
year_dropdown.change(fn=update_typhoon_options, inputs=[year_dropdown, basin_dropdown], outputs=typhoon_dropdown)
|
| 1189 |
basin_dropdown.change(fn=update_typhoon_options, inputs=[year_dropdown, basin_dropdown], outputs=typhoon_dropdown)
|
| 1190 |
|