Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -631,6 +631,7 @@ def get_available_years():
|
|
631 |
years = sorted(years)
|
632 |
return years
|
633 |
|
|
|
634 |
# Function to get available typhoons for a selected year
|
635 |
def get_typhoons_for_year(year):
|
636 |
if not year or ibtracs is None:
|
@@ -638,20 +639,77 @@ def get_typhoons_for_year(year):
|
|
638 |
|
639 |
try:
|
640 |
year = int(year)
|
|
|
|
|
|
|
641 |
season = ibtracs.get_season(year)
|
642 |
storm_summary = season.summary()
|
643 |
|
644 |
typhoon_options = []
|
645 |
for i in range(storm_summary['season_storms']):
|
646 |
-
|
647 |
-
|
648 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
649 |
|
650 |
return typhoon_options
|
651 |
except Exception as e:
|
652 |
print(f"Error getting typhoons for year {year}: {e}")
|
653 |
return []
|
654 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
655 |
# Create animation for typhoon path
|
656 |
def create_typhoon_path_animation(year, typhoon_id, standard):
|
657 |
if not year or not typhoon_id:
|
@@ -802,6 +860,7 @@ def create_typhoon_path_animation(year, typhoon_id, standard):
|
|
802 |
print(f"Error creating typhoon path animation: {e}")
|
803 |
return None
|
804 |
|
|
|
805 |
# Function to analyze typhoon tracks
|
806 |
def analyze_typhoon_tracks(start_year, start_month, end_year, end_month, enso_selection, typhoon_search=""):
|
807 |
start_date = datetime(int(start_year), int(start_month), 1)
|
@@ -820,48 +879,68 @@ def analyze_typhoon_tracks(start_year, start_month, end_year, end_month, enso_se
|
|
820 |
enso_value = enso_map[enso_selection]
|
821 |
|
822 |
try:
|
|
|
823 |
for year in range(int(start_year), int(end_year) + 1):
|
824 |
if year not in ibtracs.data.keys():
|
825 |
continue
|
826 |
|
827 |
season = ibtracs.get_season(year)
|
828 |
for storm_id in season.summary()['id']:
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
storm_oni =
|
836 |
-
if isinstance(storm_oni, pd.Series):
|
837 |
-
storm_oni = storm_oni.iloc[0]
|
838 |
|
839 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
840 |
|
841 |
-
if
|
842 |
-
|
843 |
-
(enso_value == 'la_nina' and phase == 'La Nina') or
|
844 |
-
(enso_value == 'neutral' and phase == 'Neutral')):
|
845 |
-
|
846 |
-
color = {'El Nino': 'red', 'La Nina': 'blue', 'Neutral': 'green'}[phase]
|
847 |
-
|
848 |
-
# Highlight searched typhoon
|
849 |
-
if typhoon_search and typhoon_search.lower() in storm.name.lower():
|
850 |
-
line_width = 5
|
851 |
-
line_color = 'yellow'
|
852 |
-
else:
|
853 |
-
line_width = 2
|
854 |
-
line_color = color
|
855 |
|
856 |
-
|
857 |
-
|
858 |
-
|
859 |
-
|
860 |
-
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
865 |
|
866 |
fig_tracks.update_layout(
|
867 |
title=f'Typhoon Tracks from {start_year}-{start_month} to {end_year}-{end_month}',
|
@@ -870,6 +949,8 @@ def analyze_typhoon_tracks(start_year, start_month, end_year, end_month, enso_se
|
|
870 |
showland=True,
|
871 |
coastlinecolor='rgb(100, 100, 100)',
|
872 |
countrycolor='rgb(204, 204, 204)',
|
|
|
|
|
873 |
)
|
874 |
)
|
875 |
|
@@ -884,7 +965,7 @@ def analyze_typhoon_tracks(start_year, start_month, end_year, end_month, enso_se
|
|
884 |
max_wind = filtered_data['USA_WIND'].max() if not filtered_data.empty else 0
|
885 |
min_press = filtered_data['USA_PRES'].min() if not filtered_data.empty else 0
|
886 |
|
887 |
-
stats_text = f"Maximum Wind Speed: {max_wind:.2f} knots\nMinimum Pressure: {min_press:.2f} hPa"
|
888 |
|
889 |
# Create wind scatter plot
|
890 |
wind_oni_scatter = px.scatter(filtered_data, x='ONI', y='USA_WIND', color='Category',
|
|
|
631 |
years = sorted(years)
|
632 |
return years
|
633 |
|
634 |
+
# Function to get available typhoons for a selected year
|
635 |
# Function to get available typhoons for a selected year
|
636 |
def get_typhoons_for_year(year):
|
637 |
if not year or ibtracs is None:
|
|
|
639 |
|
640 |
try:
|
641 |
year = int(year)
|
642 |
+
if year not in ibtracs.data:
|
643 |
+
return []
|
644 |
+
|
645 |
season = ibtracs.get_season(year)
|
646 |
storm_summary = season.summary()
|
647 |
|
648 |
typhoon_options = []
|
649 |
for i in range(storm_summary['season_storms']):
|
650 |
+
try:
|
651 |
+
storm_id = storm_summary['id'][i]
|
652 |
+
storm_name = storm_summary['name'][i]
|
653 |
+
# Use storm name as the display name, but return the ID as the value
|
654 |
+
display_name = f"{storm_name} ({storm_id})"
|
655 |
+
typhoon_options.append((display_name, storm_id))
|
656 |
+
except Exception as e:
|
657 |
+
print(f"Error retrieving typhoon info: {e}")
|
658 |
+
continue
|
659 |
|
660 |
return typhoon_options
|
661 |
except Exception as e:
|
662 |
print(f"Error getting typhoons for year {year}: {e}")
|
663 |
return []
|
664 |
|
665 |
+
# In the Gradio interface for the Typhoon Path Animation tab:
|
666 |
+
with gr.Tab("Typhoon Path Animation"):
|
667 |
+
with gr.Row():
|
668 |
+
available_years = [year for year in range(1950, 2025) if year in ibtracs.data.keys()] if ibtracs else []
|
669 |
+
year_dropdown = gr.Dropdown(
|
670 |
+
choices=available_years,
|
671 |
+
value=available_years[-1] if available_years else None,
|
672 |
+
label="Year"
|
673 |
+
)
|
674 |
+
|
675 |
+
typhoon_dropdown = gr.Dropdown(
|
676 |
+
label="Typhoon",
|
677 |
+
interactive=True
|
678 |
+
)
|
679 |
+
|
680 |
+
standard_dropdown = gr.Dropdown(
|
681 |
+
choices=["atlantic", "taiwan"],
|
682 |
+
value="atlantic",
|
683 |
+
label="Classification Standard"
|
684 |
+
)
|
685 |
+
|
686 |
+
# Update typhoon dropdown when year changes
|
687 |
+
def update_typhoon_dropdown(year):
|
688 |
+
if not year:
|
689 |
+
return [], None
|
690 |
+
|
691 |
+
typhoons = get_typhoons_for_year(year)
|
692 |
+
if not typhoons:
|
693 |
+
return [], None
|
694 |
+
|
695 |
+
return [{"label": name, "value": id} for name, id in typhoons], typhoons[0][1]
|
696 |
+
|
697 |
+
year_dropdown.change(
|
698 |
+
update_typhoon_dropdown,
|
699 |
+
inputs=year_dropdown,
|
700 |
+
outputs=[typhoon_dropdown, typhoon_dropdown]
|
701 |
+
)
|
702 |
+
|
703 |
+
animation_button = gr.Button("Generate Animation")
|
704 |
+
|
705 |
+
typhoon_animation = gr.Plot(label="Typhoon Path Animation")
|
706 |
+
|
707 |
+
animation_button.click(
|
708 |
+
create_typhoon_path_animation,
|
709 |
+
inputs=[year_dropdown, typhoon_dropdown, standard_dropdown],
|
710 |
+
outputs=typhoon_animation
|
711 |
+
)
|
712 |
+
|
713 |
# Create animation for typhoon path
|
714 |
def create_typhoon_path_animation(year, typhoon_id, standard):
|
715 |
if not year or not typhoon_id:
|
|
|
860 |
print(f"Error creating typhoon path animation: {e}")
|
861 |
return None
|
862 |
|
863 |
+
# Function to analyze typhoon tracks
|
864 |
# Function to analyze typhoon tracks
|
865 |
def analyze_typhoon_tracks(start_year, start_month, end_year, end_month, enso_selection, typhoon_search=""):
|
866 |
start_date = datetime(int(start_year), int(start_month), 1)
|
|
|
879 |
enso_value = enso_map[enso_selection]
|
880 |
|
881 |
try:
|
882 |
+
processed_storms = 0
|
883 |
for year in range(int(start_year), int(end_year) + 1):
|
884 |
if year not in ibtracs.data.keys():
|
885 |
continue
|
886 |
|
887 |
season = ibtracs.get_season(year)
|
888 |
for storm_id in season.summary()['id']:
|
889 |
+
try:
|
890 |
+
storm = get_storm_data(storm_id)
|
891 |
+
storm_dates = storm.time
|
892 |
+
|
893 |
+
if any(start_date <= date <= end_date for date in storm_dates):
|
894 |
+
storm_date_str = storm_dates[0].strftime('%Y-%b')
|
895 |
+
storm_oni = None
|
|
|
|
|
896 |
|
897 |
+
# Find the ONI value - handle case where date might not be in index
|
898 |
+
if storm_date_str in oni_df.index:
|
899 |
+
storm_oni = oni_df.loc[storm_date_str]['ONI']
|
900 |
+
if isinstance(storm_oni, pd.Series):
|
901 |
+
storm_oni = storm_oni.iloc[0]
|
902 |
+
else:
|
903 |
+
# Try to find closest date
|
904 |
+
closest_dates = oni_df.index[oni_df.index.year == storm_dates[0].year]
|
905 |
+
if len(closest_dates) > 0:
|
906 |
+
closest_date = min(closest_dates, key=lambda x: abs((x - storm_dates[0].to_pydatetime()).total_seconds()))
|
907 |
+
storm_oni = oni_df.loc[closest_date]['ONI']
|
908 |
+
if isinstance(storm_oni, pd.Series):
|
909 |
+
storm_oni = storm_oni.iloc[0]
|
910 |
|
911 |
+
if storm_oni is not None:
|
912 |
+
phase = classify_enso_phases(storm_oni)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
913 |
|
914 |
+
if (enso_value == 'all' or
|
915 |
+
(enso_value == 'el_nino' and phase == 'El Nino') or
|
916 |
+
(enso_value == 'la_nina' and phase == 'La Nina') or
|
917 |
+
(enso_value == 'neutral' and phase == 'Neutral')):
|
918 |
+
|
919 |
+
color = {'El Nino': 'red', 'La Nina': 'blue', 'Neutral': 'green'}[phase]
|
920 |
+
|
921 |
+
# Highlight searched typhoon
|
922 |
+
if typhoon_search and typhoon_search.lower() in storm.name.lower():
|
923 |
+
line_width = 5
|
924 |
+
line_color = 'yellow'
|
925 |
+
else:
|
926 |
+
line_width = 2
|
927 |
+
line_color = color
|
928 |
+
|
929 |
+
fig_tracks.add_trace(go.Scattergeo(
|
930 |
+
lon=storm.lon,
|
931 |
+
lat=storm.lat,
|
932 |
+
mode='lines',
|
933 |
+
name=storm.name,
|
934 |
+
text=f'{storm.name} ({year})',
|
935 |
+
hoverinfo='text',
|
936 |
+
line=dict(width=line_width, color=line_color)
|
937 |
+
))
|
938 |
+
processed_storms += 1
|
939 |
+
except Exception as e:
|
940 |
+
print(f"Error processing storm {storm_id}: {e}")
|
941 |
+
continue
|
942 |
+
|
943 |
+
print(f"Processed {processed_storms} storms for track display.")
|
944 |
|
945 |
fig_tracks.update_layout(
|
946 |
title=f'Typhoon Tracks from {start_year}-{start_month} to {end_year}-{end_month}',
|
|
|
949 |
showland=True,
|
950 |
coastlinecolor='rgb(100, 100, 100)',
|
951 |
countrycolor='rgb(204, 204, 204)',
|
952 |
+
showocean=True,
|
953 |
+
oceancolor='rgb(230, 250, 255)',
|
954 |
)
|
955 |
)
|
956 |
|
|
|
965 |
max_wind = filtered_data['USA_WIND'].max() if not filtered_data.empty else 0
|
966 |
min_press = filtered_data['USA_PRES'].min() if not filtered_data.empty else 0
|
967 |
|
968 |
+
stats_text = f"Maximum Wind Speed: {max_wind:.2f} knots\nMinimum Pressure: {min_press:.2f} hPa\nTotal Storms: {processed_storms}"
|
969 |
|
970 |
# Create wind scatter plot
|
971 |
wind_oni_scatter = px.scatter(filtered_data, x='ONI', y='USA_WIND', color='Category',
|