Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -678,15 +678,11 @@ class TyphoonAnalyzer:
|
|
678 |
|
679 |
def create_typhoon_animation(self, year, typhoon_id):
|
680 |
"""Create animated visualization of typhoon path"""
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
# Base map settings
|
687 |
-
fig.update_layout(
|
688 |
-
title=f"Typhoon Path Animation - {storm_data['NAME'].iloc[0]} ({year})",
|
689 |
-
showlegend=True,
|
690 |
geo=dict(
|
691 |
projection_type='mercator',
|
692 |
showland=True,
|
@@ -702,61 +698,127 @@ class TyphoonAnalyzer:
|
|
702 |
)
|
703 |
)
|
704 |
|
705 |
-
#
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
720 |
)
|
721 |
-
frames.append(frame)
|
722 |
|
723 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
724 |
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
'args': [[None], {'frame': {'duration': 0, 'redraw': True},
|
737 |
-
'mode': 'immediate',
|
738 |
-
'transition': {'duration': 0}}],
|
739 |
-
'label': 'Pause',
|
740 |
-
'method': 'animate'
|
741 |
-
}
|
742 |
-
],
|
743 |
-
'type': 'buttons',
|
744 |
-
'showactive': False,
|
745 |
-
'x': 0.1,
|
746 |
-
'y': 0,
|
747 |
-
'xanchor': 'right',
|
748 |
-
'yanchor': 'top'
|
749 |
-
}]
|
750 |
-
)
|
751 |
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
760 |
|
761 |
return fig, info_text
|
762 |
def create_pressure_analysis(self, data):
|
|
|
678 |
|
679 |
def create_typhoon_animation(self, year, typhoon_id):
|
680 |
"""Create animated visualization of typhoon path"""
|
681 |
+
# Create default empty figure
|
682 |
+
empty_fig = go.Figure()
|
683 |
+
empty_fig.update_layout(
|
684 |
+
title="No Data Available",
|
685 |
+
showlegend=False,
|
|
|
|
|
|
|
|
|
686 |
geo=dict(
|
687 |
projection_type='mercator',
|
688 |
showland=True,
|
|
|
698 |
)
|
699 |
)
|
700 |
|
701 |
+
# Input validation
|
702 |
+
if not typhoon_id:
|
703 |
+
return empty_fig, "Please select a typhoon"
|
704 |
+
|
705 |
+
# Get storm data
|
706 |
+
try:
|
707 |
+
storm_data = self.typhoon_data[self.typhoon_data['SID'] == typhoon_id]
|
708 |
+
if len(storm_data) == 0:
|
709 |
+
return empty_fig, "No data available for selected typhoon"
|
710 |
+
|
711 |
+
storm_data = storm_data.sort_values('ISO_TIME')
|
712 |
+
|
713 |
+
# Get the name safely
|
714 |
+
storm_name = storm_data['NAME'].values[0] if len(storm_data) > 0 else "Unknown"
|
715 |
+
|
716 |
+
fig = go.Figure()
|
717 |
+
|
718 |
+
# Base map settings
|
719 |
+
fig.update_layout(
|
720 |
+
title=f"Typhoon Path Animation - {storm_name}",
|
721 |
+
showlegend=True,
|
722 |
+
geo=dict(
|
723 |
+
projection_type='mercator',
|
724 |
+
showland=True,
|
725 |
+
showcoastlines=True,
|
726 |
+
landcolor='rgb(243, 243, 243)',
|
727 |
+
countrycolor='rgb(204, 204, 204)',
|
728 |
+
coastlinecolor='rgb(214, 214, 214)',
|
729 |
+
showocean=True,
|
730 |
+
oceancolor='rgb(230, 250, 255)',
|
731 |
+
lataxis=dict(range=[0, 50]),
|
732 |
+
lonaxis=dict(range=[100, 180]),
|
733 |
+
center=dict(lat=20, lon=140)
|
734 |
+
)
|
735 |
)
|
|
|
736 |
|
737 |
+
# Create animation frames
|
738 |
+
frames = []
|
739 |
+
for i in range(len(storm_data)):
|
740 |
+
frame = go.Frame(
|
741 |
+
data=[
|
742 |
+
go.Scattergeo(
|
743 |
+
lon=storm_data['LON'].values[:i+1],
|
744 |
+
lat=storm_data['LAT'].values[:i+1],
|
745 |
+
mode='lines+markers',
|
746 |
+
line=dict(width=2, color='red'),
|
747 |
+
marker=dict(size=8, color='red'),
|
748 |
+
name='Path',
|
749 |
+
hovertemplate=(
|
750 |
+
f"Time: {pd.to_datetime(storm_data['ISO_TIME'].values[i]).strftime('%Y-%m-%d %H:%M')}<br>" +
|
751 |
+
f"Wind: {storm_data['USA_WIND'].values[i]:.1f} kt<br>" +
|
752 |
+
f"Pressure: {storm_data['WMO_PRES'].values[i]:.1f} hPa<br>" +
|
753 |
+
f"Lat: {storm_data['LAT'].values[i]:.2f}°N<br>" +
|
754 |
+
f"Lon: {storm_data['LON'].values[i]:.2f}°E"
|
755 |
+
)
|
756 |
+
)
|
757 |
+
],
|
758 |
+
name=f'frame{i}'
|
759 |
+
)
|
760 |
+
frames.append(frame)
|
761 |
+
|
762 |
+
fig.frames = frames
|
763 |
+
|
764 |
+
# Add animation controls
|
765 |
+
fig.update_layout(
|
766 |
+
updatemenus=[{
|
767 |
+
'buttons': [
|
768 |
+
{
|
769 |
+
'args': [None, {'frame': {'duration': 100, 'redraw': True},
|
770 |
+
'fromcurrent': True}],
|
771 |
+
'label': 'Play',
|
772 |
+
'method': 'animate'
|
773 |
+
},
|
774 |
+
{
|
775 |
+
'args': [[None], {'frame': {'duration': 0, 'redraw': True},
|
776 |
+
'mode': 'immediate',
|
777 |
+
'transition': {'duration': 0}}],
|
778 |
+
'label': 'Pause',
|
779 |
+
'method': 'animate'
|
780 |
+
}
|
781 |
+
],
|
782 |
+
'type': 'buttons',
|
783 |
+
'showactive': False,
|
784 |
+
'x': 0.1,
|
785 |
+
'y': 0,
|
786 |
+
'xanchor': 'right',
|
787 |
+
'yanchor': 'top'
|
788 |
+
}]
|
789 |
+
)
|
790 |
|
791 |
+
# Add initial data
|
792 |
+
fig.add_trace(
|
793 |
+
go.Scattergeo(
|
794 |
+
lon=[storm_data['LON'].values[0]],
|
795 |
+
lat=[storm_data['LAT'].values[0]],
|
796 |
+
mode='markers',
|
797 |
+
marker=dict(size=8, color='red'),
|
798 |
+
name='Start Point',
|
799 |
+
showlegend=True
|
800 |
+
)
|
801 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
802 |
|
803 |
+
start_time = pd.to_datetime(storm_data['ISO_TIME'].values[0])
|
804 |
+
end_time = pd.to_datetime(storm_data['ISO_TIME'].values[-1])
|
805 |
+
duration = (end_time - start_time).total_seconds() / 3600
|
806 |
+
|
807 |
+
info_text = f"""
|
808 |
+
### Typhoon Information
|
809 |
+
- Name: {storm_name}
|
810 |
+
- Start Date: {start_time.strftime('%Y-%m-%d %H:%M')}
|
811 |
+
- End Date: {end_time.strftime('%Y-%m-%d %H:%M')}
|
812 |
+
- Maximum Wind Speed: {storm_data['USA_WIND'].max():.1f} kt
|
813 |
+
- Minimum Pressure: {storm_data['WMO_PRES'].min():.1f} hPa
|
814 |
+
- Duration: {duration:.1f} hours
|
815 |
+
"""
|
816 |
+
|
817 |
+
return fig, info_text
|
818 |
+
|
819 |
+
except Exception as e:
|
820 |
+
print(f"Error in create_typhoon_animation: {str(e)}")
|
821 |
+
return empty_fig, f"Error processing typhoon data: {str(e)}"
|
822 |
|
823 |
return fig, info_text
|
824 |
def create_pressure_analysis(self, data):
|