Spaces:
Running
Running
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 |
|