Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1587,6 +1587,20 @@ def create_advanced_prediction_model(typhoon_data):
|
|
| 1587 |
except Exception as e:
|
| 1588 |
return None, f"Error creating prediction model: {str(e)}"
|
| 1589 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1590 |
def get_realistic_genesis_locations():
|
| 1591 |
"""Get realistic typhoon genesis regions based on climatology"""
|
| 1592 |
return {
|
|
@@ -1603,7 +1617,7 @@ def get_realistic_genesis_locations():
|
|
| 1603 |
}
|
| 1604 |
|
| 1605 |
def predict_storm_route_and_intensity_realistic(genesis_region, month, oni_value, models=None, forecast_hours=72, use_advanced_physics=True):
|
| 1606 |
-
"""Realistic prediction
|
| 1607 |
try:
|
| 1608 |
genesis_locations = get_realistic_genesis_locations()
|
| 1609 |
|
|
@@ -1623,11 +1637,11 @@ def predict_storm_route_and_intensity_realistic(genesis_region, month, oni_value
|
|
| 1623 |
}
|
| 1624 |
|
| 1625 |
# REALISTIC starting intensity - Tropical Depression level
|
| 1626 |
-
base_intensity =
|
| 1627 |
|
| 1628 |
-
# Environmental factors for genesis
|
| 1629 |
if oni_value > 1.0: # Strong El Niño - suppressed development
|
| 1630 |
-
intensity_modifier = -
|
| 1631 |
elif oni_value > 0.5: # Moderate El Niño
|
| 1632 |
intensity_modifier = -3
|
| 1633 |
elif oni_value < -1.0: # Strong La Niña - enhanced development
|
|
@@ -1637,142 +1651,193 @@ def predict_storm_route_and_intensity_realistic(genesis_region, month, oni_value
|
|
| 1637 |
else: # Neutral
|
| 1638 |
intensity_modifier = oni_value * 2
|
| 1639 |
|
| 1640 |
-
# Seasonal genesis effects
|
| 1641 |
seasonal_factors = {
|
| 1642 |
-
1: -8, 2: -6, 3: -4, 4: -2, 5: 2, 6:
|
| 1643 |
-
7:
|
| 1644 |
}
|
| 1645 |
seasonal_modifier = seasonal_factors.get(month, 0)
|
| 1646 |
|
| 1647 |
# Genesis region favorability
|
| 1648 |
region_factors = {
|
| 1649 |
-
"Western Pacific Main Development Region":
|
| 1650 |
-
"South China Sea":
|
| 1651 |
-
"Philippine Sea":
|
| 1652 |
-
"Marshall Islands":
|
| 1653 |
-
"Monsoon Trough":
|
| 1654 |
-
"ITCZ Region":
|
| 1655 |
-
"Subtropical Region":
|
| 1656 |
-
"Bay of Bengal":
|
| 1657 |
-
"Eastern Pacific":
|
| 1658 |
-
"Atlantic MDR":
|
| 1659 |
}
|
| 1660 |
region_modifier = region_factors.get(genesis_region, 0)
|
| 1661 |
|
| 1662 |
# Calculate realistic starting intensity (TD level)
|
| 1663 |
predicted_intensity = base_intensity + intensity_modifier + seasonal_modifier + region_modifier
|
| 1664 |
-
predicted_intensity = max(25, min(40, predicted_intensity)) #
|
| 1665 |
|
| 1666 |
# Add realistic uncertainty for genesis
|
| 1667 |
intensity_uncertainty = np.random.normal(0, 2)
|
| 1668 |
predicted_intensity += intensity_uncertainty
|
| 1669 |
-
predicted_intensity = max(25, min(38, predicted_intensity)) #
|
| 1670 |
|
| 1671 |
results['current_prediction'] = {
|
| 1672 |
'intensity_kt': predicted_intensity,
|
| 1673 |
-
'pressure_hpa': 1008 - (predicted_intensity - 25) * 0.
|
| 1674 |
'category': categorize_typhoon_enhanced(predicted_intensity),
|
| 1675 |
'genesis_region': genesis_region
|
| 1676 |
}
|
| 1677 |
|
| 1678 |
-
#
|
| 1679 |
current_lat = lat
|
| 1680 |
current_lon = lon
|
| 1681 |
current_intensity = predicted_intensity
|
| 1682 |
|
| 1683 |
route_points = []
|
| 1684 |
|
| 1685 |
-
# Track storm development over time
|
| 1686 |
for hour in range(0, forecast_hours + 6, 6):
|
| 1687 |
|
| 1688 |
-
#
|
| 1689 |
-
#
|
| 1690 |
-
beta_drift_lat = 0.03 * np.sin(np.radians(current_lat))
|
| 1691 |
-
beta_drift_lon = -0.08 * np.cos(np.radians(current_lat))
|
| 1692 |
|
| 1693 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1694 |
if month in [6, 7, 8, 9]: # Peak season
|
| 1695 |
-
ridge_strength = 1.
|
| 1696 |
-
ridge_position =
|
| 1697 |
else: # Off season
|
| 1698 |
-
ridge_strength = 0.
|
| 1699 |
-
ridge_position =
|
| 1700 |
|
| 1701 |
-
#
|
| 1702 |
-
if current_lat < ridge_position -
|
| 1703 |
-
lat_tendency = 0.
|
| 1704 |
-
lon_tendency = -0.
|
| 1705 |
elif current_lat > ridge_position - 3: # Near ridge - recurvature
|
| 1706 |
-
lat_tendency = 0.
|
| 1707 |
-
lon_tendency = 0.
|
| 1708 |
-
else: # In between
|
| 1709 |
-
lat_tendency = 0.
|
| 1710 |
-
lon_tendency = -0.
|
| 1711 |
-
|
| 1712 |
-
# ENSO steering modulation
|
| 1713 |
-
if oni_value > 0.5: # El Niño - more eastward motion
|
| 1714 |
-
lon_tendency += 0.
|
| 1715 |
-
|
|
|
|
| 1716 |
lon_tendency -= 0.08
|
|
|
|
| 1717 |
|
| 1718 |
-
# Add motion uncertainty that grows with time
|
| 1719 |
-
motion_uncertainty = 0.02 + (hour /
|
| 1720 |
lat_noise = np.random.normal(0, motion_uncertainty)
|
| 1721 |
lon_noise = np.random.normal(0, motion_uncertainty)
|
| 1722 |
|
| 1723 |
-
# Update position
|
| 1724 |
current_lat += lat_tendency + lat_noise
|
| 1725 |
current_lon += lon_tendency + lon_noise
|
| 1726 |
|
| 1727 |
-
# REALISTIC intensity evolution
|
| 1728 |
|
| 1729 |
-
# Development phase (first 48-72 hours)
|
| 1730 |
if hour <= 48:
|
| 1731 |
-
if current_intensity <
|
| 1732 |
-
if
|
| 1733 |
-
intensity_tendency =
|
|
|
|
|
|
|
| 1734 |
else:
|
| 1735 |
-
intensity_tendency =
|
|
|
|
|
|
|
| 1736 |
else: # Already strong
|
| 1737 |
-
intensity_tendency =
|
| 1738 |
-
|
| 1739 |
-
|
| 1740 |
-
|
| 1741 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1742 |
else:
|
| 1743 |
-
intensity_tendency = -1.
|
| 1744 |
-
|
|
|
|
| 1745 |
else:
|
| 1746 |
-
|
|
|
|
|
|
|
|
|
|
| 1747 |
|
| 1748 |
-
# Environmental modulation
|
| 1749 |
if current_lat > 35: # High latitude - rapid weakening
|
| 1750 |
-
intensity_tendency -=
|
| 1751 |
elif current_lat > 30: # Moderate latitude
|
| 1752 |
-
intensity_tendency -=
|
| 1753 |
-
elif current_lon <
|
| 1754 |
-
intensity_tendency -=
|
| 1755 |
elif 125 <= current_lon <= 155 and 10 <= current_lat <= 25: # Warm pool
|
|
|
|
|
|
|
| 1756 |
intensity_tendency += 1
|
| 1757 |
|
| 1758 |
-
# SST effects (realistic)
|
| 1759 |
-
if current_lat < 8: # Very warm but
|
| 1760 |
intensity_tendency += 0.5
|
| 1761 |
-
elif 8 <= current_lat <= 20: # Sweet spot
|
| 1762 |
-
intensity_tendency +=
|
| 1763 |
-
elif current_lat
|
| 1764 |
-
intensity_tendency -=
|
| 1765 |
-
|
| 1766 |
-
|
| 1767 |
-
|
| 1768 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1769 |
|
| 1770 |
# Calculate confidence based on forecast time and environment
|
| 1771 |
-
base_confidence = 0.
|
| 1772 |
-
time_penalty = (hour /
|
| 1773 |
-
environment_penalty = 0.
|
| 1774 |
confidence = max(0.25, base_confidence - time_penalty - environment_penalty)
|
| 1775 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1776 |
route_points.append({
|
| 1777 |
'hour': hour,
|
| 1778 |
'lat': current_lat,
|
|
@@ -1780,26 +1845,28 @@ def predict_storm_route_and_intensity_realistic(genesis_region, month, oni_value
|
|
| 1780 |
'intensity_kt': current_intensity,
|
| 1781 |
'category': categorize_typhoon_enhanced(current_intensity),
|
| 1782 |
'confidence': confidence,
|
| 1783 |
-
'development_stage':
|
|
|
|
|
|
|
| 1784 |
})
|
| 1785 |
|
| 1786 |
results['route_forecast'] = route_points
|
| 1787 |
|
| 1788 |
# Realistic confidence scores
|
| 1789 |
results['confidence_scores'] = {
|
| 1790 |
-
'genesis': 0.
|
| 1791 |
-
'early_development': 0.
|
| 1792 |
'position_24h': 0.85,
|
| 1793 |
-
'position_48h': 0.
|
| 1794 |
-
'position_72h': 0.
|
| 1795 |
-
'intensity_24h': 0.
|
| 1796 |
-
'intensity_48h': 0.
|
| 1797 |
-
'intensity_72h': 0.
|
| 1798 |
-
'long_term': max(0.3, 0.8 - (forecast_hours /
|
| 1799 |
}
|
| 1800 |
|
| 1801 |
# Model information
|
| 1802 |
-
results['model_info'] = f"Realistic
|
| 1803 |
|
| 1804 |
return results
|
| 1805 |
|
|
@@ -1814,7 +1881,7 @@ def predict_storm_route_and_intensity_realistic(genesis_region, month, oni_value
|
|
| 1814 |
}
|
| 1815 |
|
| 1816 |
def create_animated_route_visualization(prediction_results, show_uncertainty=True, enable_animation=True):
|
| 1817 |
-
"""Create animated
|
| 1818 |
try:
|
| 1819 |
if 'route_forecast' not in prediction_results or not prediction_results['route_forecast']:
|
| 1820 |
return None, "No route forecast data available"
|
|
@@ -1829,11 +1896,24 @@ def create_animated_route_visualization(prediction_results, show_uncertainty=Tru
|
|
| 1829 |
categories = [point['category'] for point in route_data]
|
| 1830 |
confidences = [point.get('confidence', 0.8) for point in route_data]
|
| 1831 |
stages = [point.get('development_stage', 'Unknown') for point in route_data]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1832 |
|
| 1833 |
if enable_animation:
|
| 1834 |
-
#
|
| 1835 |
-
|
| 1836 |
|
|
|
|
| 1837 |
# Add complete track as background
|
| 1838 |
fig.add_trace(
|
| 1839 |
go.Scattergeo(
|
|
@@ -1843,35 +1923,62 @@ def create_animated_route_visualization(prediction_results, show_uncertainty=Tru
|
|
| 1843 |
line=dict(color='lightgray', width=2, dash='dot'),
|
| 1844 |
name='Complete Track',
|
| 1845 |
showlegend=True,
|
| 1846 |
-
opacity=0.
|
| 1847 |
-
)
|
|
|
|
| 1848 |
)
|
| 1849 |
|
| 1850 |
-
#
|
| 1851 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1852 |
for i in range(len(route_data)):
|
| 1853 |
frame_lons = lons[:i+1]
|
| 1854 |
frame_lats = lats[:i+1]
|
| 1855 |
frame_intensities = intensities[:i+1]
|
| 1856 |
frame_categories = categories[:i+1]
|
|
|
|
| 1857 |
|
| 1858 |
# Current position marker
|
| 1859 |
current_color = enhanced_color_map.get(frame_categories[-1], 'rgb(128,128,128)')
|
| 1860 |
current_size = 15 + (frame_intensities[-1] / 10)
|
| 1861 |
|
| 1862 |
frame_data = [
|
| 1863 |
-
#
|
| 1864 |
go.Scattergeo(
|
| 1865 |
lon=frame_lons,
|
| 1866 |
lat=frame_lats,
|
| 1867 |
mode='lines+markers',
|
| 1868 |
-
line=dict(color='blue', width=
|
| 1869 |
marker=dict(
|
| 1870 |
size=[8 + (intensity/15) for intensity in frame_intensities],
|
| 1871 |
color=[enhanced_color_map.get(cat, 'rgb(128,128,128)') for cat in frame_categories],
|
| 1872 |
-
opacity=0.8
|
|
|
|
| 1873 |
),
|
| 1874 |
-
name='Track',
|
| 1875 |
showlegend=False
|
| 1876 |
),
|
| 1877 |
# Current position highlight
|
|
@@ -1882,8 +1989,8 @@ def create_animated_route_visualization(prediction_results, show_uncertainty=Tru
|
|
| 1882 |
marker=dict(
|
| 1883 |
size=current_size,
|
| 1884 |
color=current_color,
|
| 1885 |
-
symbol='
|
| 1886 |
-
line=dict(width=
|
| 1887 |
),
|
| 1888 |
name='Current Position',
|
| 1889 |
showlegend=False,
|
|
@@ -1893,9 +2000,43 @@ def create_animated_route_visualization(prediction_results, show_uncertainty=Tru
|
|
| 1893 |
f"Intensity: {intensities[i]:.0f} kt<br>"
|
| 1894 |
f"Category: {categories[i]}<br>"
|
| 1895 |
f"Stage: {stages[i]}<br>"
|
|
|
|
| 1896 |
f"Confidence: {confidences[i]*100:.0f}%<br>"
|
| 1897 |
"<extra></extra>"
|
| 1898 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1899 |
)
|
| 1900 |
]
|
| 1901 |
|
|
@@ -1903,20 +2044,20 @@ def create_animated_route_visualization(prediction_results, show_uncertainty=Tru
|
|
| 1903 |
data=frame_data,
|
| 1904 |
name=str(i),
|
| 1905 |
layout=go.Layout(
|
| 1906 |
-
title=f"Storm
|
| 1907 |
-
f"Intensity: {intensities[i]:.0f} kt | Category: {categories[i]} | Stage: {stages[i]}"
|
| 1908 |
)
|
| 1909 |
))
|
| 1910 |
|
| 1911 |
fig.frames = frames
|
| 1912 |
|
| 1913 |
-
# Add play/pause
|
| 1914 |
fig.update_layout(
|
| 1915 |
updatemenus=[
|
| 1916 |
{
|
| 1917 |
"buttons": [
|
| 1918 |
{
|
| 1919 |
-
"args": [None, {"frame": {"duration":
|
| 1920 |
"fromcurrent": True, "transition": {"duration": 300}}],
|
| 1921 |
"label": "▶️ Play",
|
| 1922 |
"method": "animate"
|
|
@@ -1926,6 +2067,12 @@ def create_animated_route_visualization(prediction_results, show_uncertainty=Tru
|
|
| 1926 |
"mode": "immediate", "transition": {"duration": 0}}],
|
| 1927 |
"label": "⏸️ Pause",
|
| 1928 |
"method": "animate"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1929 |
}
|
| 1930 |
],
|
| 1931 |
"direction": "left",
|
|
@@ -1943,8 +2090,8 @@ def create_animated_route_visualization(prediction_results, show_uncertainty=Tru
|
|
| 1943 |
"yanchor": "top",
|
| 1944 |
"xanchor": "left",
|
| 1945 |
"currentvalue": {
|
| 1946 |
-
"font": {"size":
|
| 1947 |
-
"prefix": "Hour:",
|
| 1948 |
"visible": True,
|
| 1949 |
"xanchor": "right"
|
| 1950 |
},
|
|
@@ -1957,50 +2104,46 @@ def create_animated_route_visualization(prediction_results, show_uncertainty=Tru
|
|
| 1957 |
{
|
| 1958 |
"args": [[str(i)], {"frame": {"duration": 300, "redraw": True},
|
| 1959 |
"mode": "immediate", "transition": {"duration": 300}}],
|
| 1960 |
-
"label": f"
|
| 1961 |
"method": "animate"
|
| 1962 |
}
|
| 1963 |
-
for i in range(len(route_data))
|
| 1964 |
]
|
| 1965 |
}]
|
| 1966 |
)
|
| 1967 |
|
| 1968 |
else:
|
| 1969 |
-
#
|
| 1970 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1971 |
|
| 1972 |
# Add full track with intensity coloring
|
| 1973 |
-
for i in range(len(route_data)):
|
| 1974 |
point = route_data[i]
|
| 1975 |
color = enhanced_color_map.get(point['category'], 'rgb(128,128,128)')
|
| 1976 |
size = 8 + (point['intensity_kt'] / 12)
|
| 1977 |
|
| 1978 |
-
# Genesis point special marker
|
| 1979 |
-
if i == 0:
|
| 1980 |
-
fig.add_trace(
|
| 1981 |
-
go.Scattergeo(
|
| 1982 |
-
lon=[point['lon']],
|
| 1983 |
-
lat=[point['lat']],
|
| 1984 |
-
mode='markers',
|
| 1985 |
-
marker=dict(
|
| 1986 |
-
size=20,
|
| 1987 |
-
color='yellow',
|
| 1988 |
-
symbol='star',
|
| 1989 |
-
line=dict(width=3, color='black')
|
| 1990 |
-
),
|
| 1991 |
-
name='Genesis',
|
| 1992 |
-
showlegend=True,
|
| 1993 |
-
hovertemplate=(
|
| 1994 |
-
f"<b>GENESIS - {prediction_results['genesis_info']['description']}</b><br>"
|
| 1995 |
-
f"Position: {point['lat']:.1f}°N, {point['lon']:.1f}°E<br>"
|
| 1996 |
-
f"Initial Intensity: {point['intensity_kt']:.0f} kt<br>"
|
| 1997 |
-
f"Category: {point['category']}<br>"
|
| 1998 |
-
"<extra></extra>"
|
| 1999 |
-
)
|
| 2000 |
-
)
|
| 2001 |
-
)
|
| 2002 |
-
|
| 2003 |
-
# Regular track points
|
| 2004 |
fig.add_trace(
|
| 2005 |
go.Scattergeo(
|
| 2006 |
lon=[point['lon']],
|
|
@@ -2012,18 +2155,19 @@ def create_animated_route_visualization(prediction_results, show_uncertainty=Tru
|
|
| 2012 |
opacity=point.get('confidence', 0.8),
|
| 2013 |
line=dict(width=1, color='white')
|
| 2014 |
),
|
| 2015 |
-
name=f"Hour {point['hour']}" if i %
|
| 2016 |
-
showlegend=(i %
|
| 2017 |
hovertemplate=(
|
| 2018 |
f"<b>Hour {point['hour']}</b><br>"
|
| 2019 |
f"Position: {point['lat']:.1f}°N, {point['lon']:.1f}°E<br>"
|
| 2020 |
f"Intensity: {point['intensity_kt']:.0f} kt<br>"
|
| 2021 |
f"Category: {point['category']}<br>"
|
| 2022 |
f"Stage: {point.get('development_stage', 'Unknown')}<br>"
|
| 2023 |
-
f"
|
| 2024 |
"<extra></extra>"
|
| 2025 |
)
|
| 2026 |
-
)
|
|
|
|
| 2027 |
)
|
| 2028 |
|
| 2029 |
# Connect points with track line
|
|
@@ -2035,9 +2179,57 @@ def create_animated_route_visualization(prediction_results, show_uncertainty=Tru
|
|
| 2035 |
line=dict(color='black', width=3),
|
| 2036 |
name='Forecast Track',
|
| 2037 |
showlegend=True
|
| 2038 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2039 |
)
|
| 2040 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2041 |
# Add uncertainty cone if requested
|
| 2042 |
if show_uncertainty and len(route_data) > 1:
|
| 2043 |
uncertainty_lats_upper = []
|
|
@@ -2047,7 +2239,7 @@ def create_animated_route_visualization(prediction_results, show_uncertainty=Tru
|
|
| 2047 |
|
| 2048 |
for i, point in enumerate(route_data):
|
| 2049 |
# Uncertainty grows with time and decreases with confidence
|
| 2050 |
-
base_uncertainty = 0.
|
| 2051 |
confidence_factor = point.get('confidence', 0.8)
|
| 2052 |
uncertainty = base_uncertainty / confidence_factor
|
| 2053 |
|
|
@@ -2069,37 +2261,52 @@ def create_animated_route_visualization(prediction_results, show_uncertainty=Tru
|
|
| 2069 |
line=dict(color='rgba(128,128,128,0.4)', width=1),
|
| 2070 |
name='Uncertainty Cone',
|
| 2071 |
showlegend=True
|
| 2072 |
-
)
|
|
|
|
| 2073 |
)
|
| 2074 |
|
| 2075 |
-
# Enhanced layout
|
| 2076 |
fig.update_layout(
|
| 2077 |
-
title=f"
|
| 2078 |
-
|
| 2079 |
-
|
| 2080 |
-
showland=True,
|
| 2081 |
-
landcolor="LightGray",
|
| 2082 |
-
showocean=True,
|
| 2083 |
-
oceancolor="LightBlue",
|
| 2084 |
-
showcoastlines=True,
|
| 2085 |
-
coastlinecolor="DarkGray",
|
| 2086 |
-
showlakes=True,
|
| 2087 |
-
lakecolor="LightBlue",
|
| 2088 |
-
center=dict(lat=np.mean(lats), lon=np.mean(lons)),
|
| 2089 |
-
projection_scale=2.5
|
| 2090 |
-
),
|
| 2091 |
-
height=900, # Much larger
|
| 2092 |
-
width=1400, # Much wider
|
| 2093 |
showlegend=True
|
| 2094 |
)
|
| 2095 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2096 |
# Generate enhanced forecast text
|
| 2097 |
current = prediction_results['current_prediction']
|
| 2098 |
genesis_info = prediction_results['genesis_info']
|
| 2099 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2100 |
forecast_text = f"""
|
| 2101 |
-
|
| 2102 |
-
{'='*
|
| 2103 |
|
| 2104 |
GENESIS CONDITIONS:
|
| 2105 |
• Region: {current.get('genesis_region', 'Unknown')}
|
|
@@ -2107,42 +2314,47 @@ GENESIS CONDITIONS:
|
|
| 2107 |
• Starting Position: {lats[0]:.1f}°N, {lons[0]:.1f}°E
|
| 2108 |
• Initial Intensity: {current['intensity_kt']:.0f} kt (Tropical Depression)
|
| 2109 |
• Genesis Pressure: {current.get('pressure_hpa', 1008):.0f} hPa
|
| 2110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2111 |
|
| 2112 |
DEVELOPMENT TIMELINE:
|
| 2113 |
• Hour 0 (Genesis): {intensities[0]:.0f} kt - {categories[0]}
|
| 2114 |
-
• Hour 24
|
| 2115 |
-
• Hour 48
|
| 2116 |
-
• Hour 72
|
| 2117 |
-
|
| 2118 |
-
|
| 2119 |
-
|
| 2120 |
-
•
|
| 2121 |
-
•
|
| 2122 |
-
•
|
| 2123 |
-
• Final: {lats[-1]:.1f}°N, {lons[-1]:.1f}°E
|
| 2124 |
-
|
| 2125 |
-
DEVELOPMENT STAGES:
|
| 2126 |
-
• Genesis (0-24h): Initial cyclogenesis and organization
|
| 2127 |
-
• Development (24-72h): Intensification in favorable environment
|
| 2128 |
-
• Mature (72-120h): Peak intensity and steady motion
|
| 2129 |
-
• Decay (120h+): Gradual weakening due to environmental factors
|
| 2130 |
|
| 2131 |
CONFIDENCE ASSESSMENT:
|
| 2132 |
• Genesis Likelihood: {prediction_results['confidence_scores'].get('genesis', 0.85)*100:.0f}%
|
| 2133 |
-
• Early Development: {prediction_results['confidence_scores'].get('early_development', 0.80)*100:.0f}%
|
| 2134 |
• 24-hour Track: {prediction_results['confidence_scores'].get('position_24h', 0.85)*100:.0f}%
|
| 2135 |
• 48-hour Track: {prediction_results['confidence_scores'].get('position_48h', 0.75)*100:.0f}%
|
| 2136 |
• 72-hour Track: {prediction_results['confidence_scores'].get('position_72h', 0.65)*100:.0f}%
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2137 |
|
| 2138 |
MODEL: {prediction_results['model_info']}
|
| 2139 |
-
ANIMATION: {"Enabled - Use controls below map" if enable_animation else "Static view - All time steps shown"}
|
| 2140 |
"""
|
| 2141 |
|
| 2142 |
return fig, forecast_text.strip()
|
| 2143 |
|
| 2144 |
except Exception as e:
|
| 2145 |
-
error_msg = f"Error creating
|
| 2146 |
logging.error(error_msg)
|
| 2147 |
import traceback
|
| 2148 |
traceback.print_exc()
|
|
@@ -2882,12 +3094,13 @@ def create_interface():
|
|
| 2882 |
pred_month = gr.Slider(1, 12, label="Month", value=9, info="Peak season: Jul-Oct")
|
| 2883 |
pred_oni = gr.Number(label="ONI Value", value=0.0, info="ENSO index (-3 to 3)")
|
| 2884 |
with gr.Row():
|
| 2885 |
-
forecast_hours = gr.
|
| 2886 |
-
6, 240,
|
| 2887 |
label="Forecast Length (hours)",
|
| 2888 |
value=72,
|
|
|
|
|
|
|
| 2889 |
step=6,
|
| 2890 |
-
info="Extended forecasting
|
| 2891 |
)
|
| 2892 |
advanced_physics = gr.Checkbox(
|
| 2893 |
label="Advanced Physics",
|
|
@@ -2965,17 +3178,26 @@ def create_interface():
|
|
| 2965 |
prediction_info_text = """
|
| 2966 |
### 🌊 Realistic Storm Genesis Features:
|
| 2967 |
- **Climatological Genesis Regions**: 10 realistic development zones based on historical data
|
| 2968 |
-
- **Tropical Depression Starting Point**: Storms begin at realistic 25-
|
| 2969 |
-
- **
|
| 2970 |
-
- **Extended
|
|
|
|
| 2971 |
- **Environmental Coupling**: Advanced physics with ENSO, SST, shear, β-drift, ridge patterns
|
| 2972 |
-
- **Realistic Intensity Evolution**: Natural intensification/weakening cycles, not just decay
|
| 2973 |
|
| 2974 |
### 📊 Enhanced Development Stages:
|
| 2975 |
- **Genesis (0-24h)**: Initial cyclogenesis at Tropical Depression level
|
| 2976 |
-
- **Development (24-72h)**:
|
| 2977 |
- **Mature (72-120h)**: Peak intensity phase with environmental modulation
|
| 2978 |
-
- **
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2979 |
|
| 2980 |
### 🌍 Genesis Region Selection:
|
| 2981 |
- **Western Pacific MDR**: Peak activity zone near Guam (12.5°N, 145°E)
|
|
@@ -2985,20 +3207,21 @@ def create_interface():
|
|
| 2985 |
- **Monsoon Trough**: Monsoon-driven genesis (10°N, 130°E)
|
| 2986 |
- **Other Basins**: Bay of Bengal, Eastern Pacific, Atlantic options
|
| 2987 |
|
| 2988 |
-
###
|
| 2989 |
-
- **Beta Drift**: Coriolis-induced storm motion (latitude-dependent)
|
| 2990 |
-
- **Ridge Interaction**: Seasonal subtropical ridge position effects
|
| 2991 |
-
- **ENSO Modulation**: Non-linear intensity and steering effects
|
| 2992 |
-
- **SST Coupling**: Ocean temperature impact on intensification
|
| 2993 |
-
- **Shear Analysis**: Environmental wind shear impact assessment
|
| 2994 |
-
- **Recurvature Physics**: Advanced extratropical transition modeling
|
| 2995 |
-
|
| 2996 |
-
### 🎬 Animation Features:
|
| 2997 |
- **Real-time Development**: Watch TD evolve to typhoon intensity
|
| 2998 |
-
- **
|
|
|
|
| 2999 |
- **Stage Tracking**: Visual indicators for development phases
|
| 3000 |
-
- **
|
| 3001 |
-
- **
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3002 |
"""
|
| 3003 |
gr.Markdown(prediction_info_text)
|
| 3004 |
|
|
|
|
| 1587 |
except Exception as e:
|
| 1588 |
return None, f"Error creating prediction model: {str(e)}"
|
| 1589 |
|
| 1590 |
+
def rgb_string_to_hex(rgb_string):
|
| 1591 |
+
"""Convert RGB string like 'rgb(255,0,0)' to hex format '#FF0000'"""
|
| 1592 |
+
try:
|
| 1593 |
+
if rgb_string.startswith('rgb(') and rgb_string.endswith(')'):
|
| 1594 |
+
# Extract numbers from rgb(r,g,b)
|
| 1595 |
+
rgb_values = rgb_string[4:-1].split(',')
|
| 1596 |
+
r, g, b = [int(x.strip()) for x in rgb_values]
|
| 1597 |
+
return f'#{r:02x}{g:02x}{b:02x}'
|
| 1598 |
+
else:
|
| 1599 |
+
# Already in hex or other format
|
| 1600 |
+
return rgb_string
|
| 1601 |
+
except:
|
| 1602 |
+
return '#808080' # Default gray
|
| 1603 |
+
|
| 1604 |
def get_realistic_genesis_locations():
|
| 1605 |
"""Get realistic typhoon genesis regions based on climatology"""
|
| 1606 |
return {
|
|
|
|
| 1617 |
}
|
| 1618 |
|
| 1619 |
def predict_storm_route_and_intensity_realistic(genesis_region, month, oni_value, models=None, forecast_hours=72, use_advanced_physics=True):
|
| 1620 |
+
"""Realistic prediction with proper typhoon speeds and development"""
|
| 1621 |
try:
|
| 1622 |
genesis_locations = get_realistic_genesis_locations()
|
| 1623 |
|
|
|
|
| 1637 |
}
|
| 1638 |
|
| 1639 |
# REALISTIC starting intensity - Tropical Depression level
|
| 1640 |
+
base_intensity = 30 # Start at TD level (25-35 kt)
|
| 1641 |
|
| 1642 |
+
# Environmental factors for genesis
|
| 1643 |
if oni_value > 1.0: # Strong El Niño - suppressed development
|
| 1644 |
+
intensity_modifier = -6
|
| 1645 |
elif oni_value > 0.5: # Moderate El Niño
|
| 1646 |
intensity_modifier = -3
|
| 1647 |
elif oni_value < -1.0: # Strong La Niña - enhanced development
|
|
|
|
| 1651 |
else: # Neutral
|
| 1652 |
intensity_modifier = oni_value * 2
|
| 1653 |
|
| 1654 |
+
# Seasonal genesis effects
|
| 1655 |
seasonal_factors = {
|
| 1656 |
+
1: -8, 2: -6, 3: -4, 4: -2, 5: 2, 6: 6,
|
| 1657 |
+
7: 10, 8: 12, 9: 15, 10: 10, 11: 4, 12: -5
|
| 1658 |
}
|
| 1659 |
seasonal_modifier = seasonal_factors.get(month, 0)
|
| 1660 |
|
| 1661 |
# Genesis region favorability
|
| 1662 |
region_factors = {
|
| 1663 |
+
"Western Pacific Main Development Region": 8,
|
| 1664 |
+
"South China Sea": 4,
|
| 1665 |
+
"Philippine Sea": 5,
|
| 1666 |
+
"Marshall Islands": 7,
|
| 1667 |
+
"Monsoon Trough": 6,
|
| 1668 |
+
"ITCZ Region": 3,
|
| 1669 |
+
"Subtropical Region": 2,
|
| 1670 |
+
"Bay of Bengal": 4,
|
| 1671 |
+
"Eastern Pacific": 6,
|
| 1672 |
+
"Atlantic MDR": 5
|
| 1673 |
}
|
| 1674 |
region_modifier = region_factors.get(genesis_region, 0)
|
| 1675 |
|
| 1676 |
# Calculate realistic starting intensity (TD level)
|
| 1677 |
predicted_intensity = base_intensity + intensity_modifier + seasonal_modifier + region_modifier
|
| 1678 |
+
predicted_intensity = max(25, min(40, predicted_intensity)) # Keep in TD-weak TS range
|
| 1679 |
|
| 1680 |
# Add realistic uncertainty for genesis
|
| 1681 |
intensity_uncertainty = np.random.normal(0, 2)
|
| 1682 |
predicted_intensity += intensity_uncertainty
|
| 1683 |
+
predicted_intensity = max(25, min(38, predicted_intensity)) # TD range
|
| 1684 |
|
| 1685 |
results['current_prediction'] = {
|
| 1686 |
'intensity_kt': predicted_intensity,
|
| 1687 |
+
'pressure_hpa': 1008 - (predicted_intensity - 25) * 0.6, # Realistic TD pressure
|
| 1688 |
'category': categorize_typhoon_enhanced(predicted_intensity),
|
| 1689 |
'genesis_region': genesis_region
|
| 1690 |
}
|
| 1691 |
|
| 1692 |
+
# REALISTIC route prediction with proper typhoon speeds
|
| 1693 |
current_lat = lat
|
| 1694 |
current_lon = lon
|
| 1695 |
current_intensity = predicted_intensity
|
| 1696 |
|
| 1697 |
route_points = []
|
| 1698 |
|
| 1699 |
+
# Track storm development over time with REALISTIC SPEEDS
|
| 1700 |
for hour in range(0, forecast_hours + 6, 6):
|
| 1701 |
|
| 1702 |
+
# REALISTIC typhoon motion - much faster speeds
|
| 1703 |
+
# Typical typhoon forward speed: 15-25 km/h (0.14-0.23°/hour)
|
|
|
|
|
|
|
| 1704 |
|
| 1705 |
+
# Base forward speed depends on latitude and storm intensity
|
| 1706 |
+
if current_lat < 20: # Low latitude - slower
|
| 1707 |
+
base_speed = 0.12 # ~13 km/h
|
| 1708 |
+
elif current_lat < 30: # Mid latitude - moderate
|
| 1709 |
+
base_speed = 0.18 # ~20 km/h
|
| 1710 |
+
else: # High latitude - faster
|
| 1711 |
+
base_speed = 0.25 # ~28 km/h
|
| 1712 |
+
|
| 1713 |
+
# Intensity affects speed (stronger storms can move faster)
|
| 1714 |
+
intensity_speed_factor = 1.0 + (current_intensity - 50) / 200
|
| 1715 |
+
base_speed *= max(0.8, min(1.4, intensity_speed_factor))
|
| 1716 |
+
|
| 1717 |
+
# Beta drift (Coriolis effect) - realistic values
|
| 1718 |
+
beta_drift_lat = 0.02 * np.sin(np.radians(current_lat))
|
| 1719 |
+
beta_drift_lon = -0.05 * np.cos(np.radians(current_lat))
|
| 1720 |
+
|
| 1721 |
+
# Seasonal steering patterns with realistic speeds
|
| 1722 |
if month in [6, 7, 8, 9]: # Peak season
|
| 1723 |
+
ridge_strength = 1.2
|
| 1724 |
+
ridge_position = 32 + 4 * np.sin(2 * np.pi * (month - 6) / 4)
|
| 1725 |
else: # Off season
|
| 1726 |
+
ridge_strength = 0.9
|
| 1727 |
+
ridge_position = 28
|
| 1728 |
|
| 1729 |
+
# REALISTIC motion based on position relative to subtropical ridge
|
| 1730 |
+
if current_lat < ridge_position - 10: # Well south of ridge - westward movement
|
| 1731 |
+
lat_tendency = base_speed * 0.3 + beta_drift_lat # Slight poleward
|
| 1732 |
+
lon_tendency = -base_speed * 0.9 + beta_drift_lon # Strong westward
|
| 1733 |
elif current_lat > ridge_position - 3: # Near ridge - recurvature
|
| 1734 |
+
lat_tendency = base_speed * 0.8 + beta_drift_lat # Strong poleward
|
| 1735 |
+
lon_tendency = base_speed * 0.4 + beta_drift_lon # Eastward
|
| 1736 |
+
else: # In between - normal WNW motion
|
| 1737 |
+
lat_tendency = base_speed * 0.4 + beta_drift_lat # Moderate poleward
|
| 1738 |
+
lon_tendency = -base_speed * 0.7 + beta_drift_lon # Moderate westward
|
| 1739 |
+
|
| 1740 |
+
# ENSO steering modulation (realistic effects)
|
| 1741 |
+
if oni_value > 0.5: # El Niño - more eastward/poleward motion
|
| 1742 |
+
lon_tendency += 0.05
|
| 1743 |
+
lat_tendency += 0.02
|
| 1744 |
+
elif oni_value < -0.5: # La Niña - more westward motion
|
| 1745 |
lon_tendency -= 0.08
|
| 1746 |
+
lat_tendency -= 0.01
|
| 1747 |
|
| 1748 |
+
# Add motion uncertainty that grows with time (realistic error growth)
|
| 1749 |
+
motion_uncertainty = 0.02 + (hour / 120) * 0.04
|
| 1750 |
lat_noise = np.random.normal(0, motion_uncertainty)
|
| 1751 |
lon_noise = np.random.normal(0, motion_uncertainty)
|
| 1752 |
|
| 1753 |
+
# Update position with realistic speeds
|
| 1754 |
current_lat += lat_tendency + lat_noise
|
| 1755 |
current_lon += lon_tendency + lon_noise
|
| 1756 |
|
| 1757 |
+
# REALISTIC intensity evolution with proper development cycles
|
| 1758 |
|
| 1759 |
+
# Development phase (first 48-72 hours) - realistic intensification
|
| 1760 |
if hour <= 48:
|
| 1761 |
+
if current_intensity < 50: # Still weak - rapid development possible
|
| 1762 |
+
if 10 <= current_lat <= 25 and 115 <= current_lon <= 165: # Favorable environment
|
| 1763 |
+
intensity_tendency = 4.5 if current_intensity < 35 else 3.0
|
| 1764 |
+
elif 120 <= current_lon <= 155 and 15 <= current_lat <= 20: # Best environment
|
| 1765 |
+
intensity_tendency = 6.0 if current_intensity < 40 else 4.0
|
| 1766 |
else:
|
| 1767 |
+
intensity_tendency = 2.0
|
| 1768 |
+
elif current_intensity < 80: # Moderate intensity
|
| 1769 |
+
intensity_tendency = 2.5 if (120 <= current_lon <= 155 and 10 <= current_lat <= 25) else 1.0
|
| 1770 |
else: # Already strong
|
| 1771 |
+
intensity_tendency = 1.0
|
| 1772 |
+
|
| 1773 |
+
# Mature phase (48-120 hours) - peak intensity maintenance
|
| 1774 |
+
elif hour <= 120:
|
| 1775 |
+
if current_lat < 25 and current_lon > 120: # Still in favorable waters
|
| 1776 |
+
if current_intensity < 120:
|
| 1777 |
+
intensity_tendency = 1.5
|
| 1778 |
+
else:
|
| 1779 |
+
intensity_tendency = 0.0 # Maintain intensity
|
| 1780 |
else:
|
| 1781 |
+
intensity_tendency = -1.5
|
| 1782 |
+
|
| 1783 |
+
# Extended phase (120+ hours) - gradual weakening
|
| 1784 |
else:
|
| 1785 |
+
if current_lat < 30 and current_lon > 115:
|
| 1786 |
+
intensity_tendency = -2.0 # Slow weakening
|
| 1787 |
+
else:
|
| 1788 |
+
intensity_tendency = -3.5 # Faster weakening
|
| 1789 |
|
| 1790 |
+
# Environmental modulation (realistic effects)
|
| 1791 |
if current_lat > 35: # High latitude - rapid weakening
|
| 1792 |
+
intensity_tendency -= 12
|
| 1793 |
elif current_lat > 30: # Moderate latitude
|
| 1794 |
+
intensity_tendency -= 5
|
| 1795 |
+
elif current_lon < 110: # Land interaction
|
| 1796 |
+
intensity_tendency -= 15
|
| 1797 |
elif 125 <= current_lon <= 155 and 10 <= current_lat <= 25: # Warm pool
|
| 1798 |
+
intensity_tendency += 2
|
| 1799 |
+
elif 160 <= current_lon <= 180 and 15 <= current_lat <= 30: # Still warm
|
| 1800 |
intensity_tendency += 1
|
| 1801 |
|
| 1802 |
+
# SST effects (realistic temperature impact)
|
| 1803 |
+
if current_lat < 8: # Very warm but weak Coriolis
|
| 1804 |
intensity_tendency += 0.5
|
| 1805 |
+
elif 8 <= current_lat <= 20: # Sweet spot for development
|
| 1806 |
+
intensity_tendency += 2.0
|
| 1807 |
+
elif 20 < current_lat <= 30: # Marginal
|
| 1808 |
+
intensity_tendency -= 1.0
|
| 1809 |
+
elif current_lat > 30: # Cool waters
|
| 1810 |
+
intensity_tendency -= 4.0
|
| 1811 |
+
|
| 1812 |
+
# Shear effects (simplified but realistic)
|
| 1813 |
+
if month in [12, 1, 2, 3]: # High shear season
|
| 1814 |
+
intensity_tendency -= 2.0
|
| 1815 |
+
elif month in [7, 8, 9]: # Low shear season
|
| 1816 |
+
intensity_tendency += 1.0
|
| 1817 |
+
|
| 1818 |
+
# Update intensity with realistic bounds and variability
|
| 1819 |
+
intensity_noise = np.random.normal(0, 1.5) # Small random fluctuations
|
| 1820 |
+
current_intensity += intensity_tendency + intensity_noise
|
| 1821 |
+
current_intensity = max(20, min(185, current_intensity)) # Realistic range
|
| 1822 |
|
| 1823 |
# Calculate confidence based on forecast time and environment
|
| 1824 |
+
base_confidence = 0.92
|
| 1825 |
+
time_penalty = (hour / 120) * 0.45
|
| 1826 |
+
environment_penalty = 0.15 if current_lat > 30 or current_lon < 115 else 0
|
| 1827 |
confidence = max(0.25, base_confidence - time_penalty - environment_penalty)
|
| 1828 |
|
| 1829 |
+
# Determine development stage
|
| 1830 |
+
if hour <= 24:
|
| 1831 |
+
stage = 'Genesis'
|
| 1832 |
+
elif hour <= 72:
|
| 1833 |
+
stage = 'Development'
|
| 1834 |
+
elif hour <= 120:
|
| 1835 |
+
stage = 'Mature'
|
| 1836 |
+
elif hour <= 240:
|
| 1837 |
+
stage = 'Extended'
|
| 1838 |
+
else:
|
| 1839 |
+
stage = 'Long-term'
|
| 1840 |
+
|
| 1841 |
route_points.append({
|
| 1842 |
'hour': hour,
|
| 1843 |
'lat': current_lat,
|
|
|
|
| 1845 |
'intensity_kt': current_intensity,
|
| 1846 |
'category': categorize_typhoon_enhanced(current_intensity),
|
| 1847 |
'confidence': confidence,
|
| 1848 |
+
'development_stage': stage,
|
| 1849 |
+
'forward_speed_kmh': base_speed * 111, # Convert to km/h
|
| 1850 |
+
'pressure_hpa': max(900, 1013 - (current_intensity - 25) * 0.9)
|
| 1851 |
})
|
| 1852 |
|
| 1853 |
results['route_forecast'] = route_points
|
| 1854 |
|
| 1855 |
# Realistic confidence scores
|
| 1856 |
results['confidence_scores'] = {
|
| 1857 |
+
'genesis': 0.88,
|
| 1858 |
+
'early_development': 0.82,
|
| 1859 |
'position_24h': 0.85,
|
| 1860 |
+
'position_48h': 0.78,
|
| 1861 |
+
'position_72h': 0.68,
|
| 1862 |
+
'intensity_24h': 0.75,
|
| 1863 |
+
'intensity_48h': 0.65,
|
| 1864 |
+
'intensity_72h': 0.55,
|
| 1865 |
+
'long_term': max(0.3, 0.8 - (forecast_hours / 240) * 0.5)
|
| 1866 |
}
|
| 1867 |
|
| 1868 |
# Model information
|
| 1869 |
+
results['model_info'] = f"Enhanced Realistic Model - {genesis_region}"
|
| 1870 |
|
| 1871 |
return results
|
| 1872 |
|
|
|
|
| 1881 |
}
|
| 1882 |
|
| 1883 |
def create_animated_route_visualization(prediction_results, show_uncertainty=True, enable_animation=True):
|
| 1884 |
+
"""Create comprehensive animated route visualization with intensity plots"""
|
| 1885 |
try:
|
| 1886 |
if 'route_forecast' not in prediction_results or not prediction_results['route_forecast']:
|
| 1887 |
return None, "No route forecast data available"
|
|
|
|
| 1896 |
categories = [point['category'] for point in route_data]
|
| 1897 |
confidences = [point.get('confidence', 0.8) for point in route_data]
|
| 1898 |
stages = [point.get('development_stage', 'Unknown') for point in route_data]
|
| 1899 |
+
speeds = [point.get('forward_speed_kmh', 15) for point in route_data]
|
| 1900 |
+
pressures = [point.get('pressure_hpa', 1013) for point in route_data]
|
| 1901 |
+
|
| 1902 |
+
# Create subplot layout with map and intensity plot
|
| 1903 |
+
fig = make_subplots(
|
| 1904 |
+
rows=2, cols=2,
|
| 1905 |
+
subplot_titles=('Storm Track Animation', 'Wind Speed vs Time', 'Forward Speed vs Time', 'Pressure vs Time'),
|
| 1906 |
+
specs=[[{"type": "geo", "colspan": 2}, None],
|
| 1907 |
+
[{"type": "xy"}, {"type": "xy"}]],
|
| 1908 |
+
vertical_spacing=0.15,
|
| 1909 |
+
row_heights=[0.7, 0.3]
|
| 1910 |
+
)
|
| 1911 |
|
| 1912 |
if enable_animation:
|
| 1913 |
+
# Add frames for animation
|
| 1914 |
+
frames = []
|
| 1915 |
|
| 1916 |
+
# Static background elements first
|
| 1917 |
# Add complete track as background
|
| 1918 |
fig.add_trace(
|
| 1919 |
go.Scattergeo(
|
|
|
|
| 1923 |
line=dict(color='lightgray', width=2, dash='dot'),
|
| 1924 |
name='Complete Track',
|
| 1925 |
showlegend=True,
|
| 1926 |
+
opacity=0.4
|
| 1927 |
+
),
|
| 1928 |
+
row=1, col=1
|
| 1929 |
)
|
| 1930 |
|
| 1931 |
+
# Genesis marker (always visible)
|
| 1932 |
+
fig.add_trace(
|
| 1933 |
+
go.Scattergeo(
|
| 1934 |
+
lon=[lons[0]],
|
| 1935 |
+
lat=[lats[0]],
|
| 1936 |
+
mode='markers',
|
| 1937 |
+
marker=dict(
|
| 1938 |
+
size=25,
|
| 1939 |
+
color='gold',
|
| 1940 |
+
symbol='star',
|
| 1941 |
+
line=dict(width=3, color='black')
|
| 1942 |
+
),
|
| 1943 |
+
name='Genesis',
|
| 1944 |
+
showlegend=True,
|
| 1945 |
+
hovertemplate=(
|
| 1946 |
+
f"<b>GENESIS</b><br>"
|
| 1947 |
+
f"Position: {lats[0]:.1f}°N, {lons[0]:.1f}°E<br>"
|
| 1948 |
+
f"Initial: {intensities[0]:.0f} kt<br>"
|
| 1949 |
+
f"Region: {prediction_results['genesis_info']['description']}<br>"
|
| 1950 |
+
"<extra></extra>"
|
| 1951 |
+
)
|
| 1952 |
+
),
|
| 1953 |
+
row=1, col=1
|
| 1954 |
+
)
|
| 1955 |
+
|
| 1956 |
+
# Create animation frames
|
| 1957 |
for i in range(len(route_data)):
|
| 1958 |
frame_lons = lons[:i+1]
|
| 1959 |
frame_lats = lats[:i+1]
|
| 1960 |
frame_intensities = intensities[:i+1]
|
| 1961 |
frame_categories = categories[:i+1]
|
| 1962 |
+
frame_hours = hours[:i+1]
|
| 1963 |
|
| 1964 |
# Current position marker
|
| 1965 |
current_color = enhanced_color_map.get(frame_categories[-1], 'rgb(128,128,128)')
|
| 1966 |
current_size = 15 + (frame_intensities[-1] / 10)
|
| 1967 |
|
| 1968 |
frame_data = [
|
| 1969 |
+
# Animated track up to current point
|
| 1970 |
go.Scattergeo(
|
| 1971 |
lon=frame_lons,
|
| 1972 |
lat=frame_lats,
|
| 1973 |
mode='lines+markers',
|
| 1974 |
+
line=dict(color='blue', width=4),
|
| 1975 |
marker=dict(
|
| 1976 |
size=[8 + (intensity/15) for intensity in frame_intensities],
|
| 1977 |
color=[enhanced_color_map.get(cat, 'rgb(128,128,128)') for cat in frame_categories],
|
| 1978 |
+
opacity=0.8,
|
| 1979 |
+
line=dict(width=1, color='white')
|
| 1980 |
),
|
| 1981 |
+
name='Current Track',
|
| 1982 |
showlegend=False
|
| 1983 |
),
|
| 1984 |
# Current position highlight
|
|
|
|
| 1989 |
marker=dict(
|
| 1990 |
size=current_size,
|
| 1991 |
color=current_color,
|
| 1992 |
+
symbol='circle',
|
| 1993 |
+
line=dict(width=3, color='white')
|
| 1994 |
),
|
| 1995 |
name='Current Position',
|
| 1996 |
showlegend=False,
|
|
|
|
| 2000 |
f"Intensity: {intensities[i]:.0f} kt<br>"
|
| 2001 |
f"Category: {categories[i]}<br>"
|
| 2002 |
f"Stage: {stages[i]}<br>"
|
| 2003 |
+
f"Speed: {speeds[i]:.1f} km/h<br>"
|
| 2004 |
f"Confidence: {confidences[i]*100:.0f}%<br>"
|
| 2005 |
"<extra></extra>"
|
| 2006 |
)
|
| 2007 |
+
),
|
| 2008 |
+
# Animated wind plot
|
| 2009 |
+
go.Scatter(
|
| 2010 |
+
x=frame_hours,
|
| 2011 |
+
y=frame_intensities,
|
| 2012 |
+
mode='lines+markers',
|
| 2013 |
+
line=dict(color='red', width=3),
|
| 2014 |
+
marker=dict(size=6, color='red'),
|
| 2015 |
+
name='Wind Speed',
|
| 2016 |
+
showlegend=False,
|
| 2017 |
+
yaxis='y2'
|
| 2018 |
+
),
|
| 2019 |
+
# Animated speed plot
|
| 2020 |
+
go.Scatter(
|
| 2021 |
+
x=frame_hours,
|
| 2022 |
+
y=speeds[:i+1],
|
| 2023 |
+
mode='lines+markers',
|
| 2024 |
+
line=dict(color='green', width=2),
|
| 2025 |
+
marker=dict(size=4, color='green'),
|
| 2026 |
+
name='Forward Speed',
|
| 2027 |
+
showlegend=False,
|
| 2028 |
+
yaxis='y3'
|
| 2029 |
+
),
|
| 2030 |
+
# Animated pressure plot
|
| 2031 |
+
go.Scatter(
|
| 2032 |
+
x=frame_hours,
|
| 2033 |
+
y=pressures[:i+1],
|
| 2034 |
+
mode='lines+markers',
|
| 2035 |
+
line=dict(color='purple', width=2),
|
| 2036 |
+
marker=dict(size=4, color='purple'),
|
| 2037 |
+
name='Pressure',
|
| 2038 |
+
showlegend=False,
|
| 2039 |
+
yaxis='y4'
|
| 2040 |
)
|
| 2041 |
]
|
| 2042 |
|
|
|
|
| 2044 |
data=frame_data,
|
| 2045 |
name=str(i),
|
| 2046 |
layout=go.Layout(
|
| 2047 |
+
title=f"Storm Development Animation - Hour {route_data[i]['hour']}<br>"
|
| 2048 |
+
f"Intensity: {intensities[i]:.0f} kt | Category: {categories[i]} | Stage: {stages[i]} | Speed: {speeds[i]:.1f} km/h"
|
| 2049 |
)
|
| 2050 |
))
|
| 2051 |
|
| 2052 |
fig.frames = frames
|
| 2053 |
|
| 2054 |
+
# Add play/pause controls
|
| 2055 |
fig.update_layout(
|
| 2056 |
updatemenus=[
|
| 2057 |
{
|
| 2058 |
"buttons": [
|
| 2059 |
{
|
| 2060 |
+
"args": [None, {"frame": {"duration": 1000, "redraw": True},
|
| 2061 |
"fromcurrent": True, "transition": {"duration": 300}}],
|
| 2062 |
"label": "▶️ Play",
|
| 2063 |
"method": "animate"
|
|
|
|
| 2067 |
"mode": "immediate", "transition": {"duration": 0}}],
|
| 2068 |
"label": "⏸️ Pause",
|
| 2069 |
"method": "animate"
|
| 2070 |
+
},
|
| 2071 |
+
{
|
| 2072 |
+
"args": [None, {"frame": {"duration": 500, "redraw": True},
|
| 2073 |
+
"fromcurrent": True, "transition": {"duration": 300}}],
|
| 2074 |
+
"label": "⏩ Fast",
|
| 2075 |
+
"method": "animate"
|
| 2076 |
}
|
| 2077 |
],
|
| 2078 |
"direction": "left",
|
|
|
|
| 2090 |
"yanchor": "top",
|
| 2091 |
"xanchor": "left",
|
| 2092 |
"currentvalue": {
|
| 2093 |
+
"font": {"size": 16},
|
| 2094 |
+
"prefix": "Hour: ",
|
| 2095 |
"visible": True,
|
| 2096 |
"xanchor": "right"
|
| 2097 |
},
|
|
|
|
| 2104 |
{
|
| 2105 |
"args": [[str(i)], {"frame": {"duration": 300, "redraw": True},
|
| 2106 |
"mode": "immediate", "transition": {"duration": 300}}],
|
| 2107 |
+
"label": f"H{route_data[i]['hour']}",
|
| 2108 |
"method": "animate"
|
| 2109 |
}
|
| 2110 |
+
for i in range(0, len(route_data), max(1, len(route_data)//20)) # Limit slider steps
|
| 2111 |
]
|
| 2112 |
}]
|
| 2113 |
)
|
| 2114 |
|
| 2115 |
else:
|
| 2116 |
+
# Static view with all points
|
| 2117 |
+
# Add genesis marker
|
| 2118 |
+
fig.add_trace(
|
| 2119 |
+
go.Scattergeo(
|
| 2120 |
+
lon=[lons[0]],
|
| 2121 |
+
lat=[lats[0]],
|
| 2122 |
+
mode='markers',
|
| 2123 |
+
marker=dict(
|
| 2124 |
+
size=25,
|
| 2125 |
+
color='gold',
|
| 2126 |
+
symbol='star',
|
| 2127 |
+
line=dict(width=3, color='black')
|
| 2128 |
+
),
|
| 2129 |
+
name='Genesis',
|
| 2130 |
+
showlegend=True,
|
| 2131 |
+
hovertemplate=(
|
| 2132 |
+
f"<b>GENESIS</b><br>"
|
| 2133 |
+
f"Position: {lats[0]:.1f}°N, {lons[0]:.1f}°E<br>"
|
| 2134 |
+
f"Initial: {intensities[0]:.0f} kt<br>"
|
| 2135 |
+
"<extra></extra>"
|
| 2136 |
+
)
|
| 2137 |
+
),
|
| 2138 |
+
row=1, col=1
|
| 2139 |
+
)
|
| 2140 |
|
| 2141 |
# Add full track with intensity coloring
|
| 2142 |
+
for i in range(0, len(route_data), max(1, len(route_data)//50)): # Sample points for performance
|
| 2143 |
point = route_data[i]
|
| 2144 |
color = enhanced_color_map.get(point['category'], 'rgb(128,128,128)')
|
| 2145 |
size = 8 + (point['intensity_kt'] / 12)
|
| 2146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2147 |
fig.add_trace(
|
| 2148 |
go.Scattergeo(
|
| 2149 |
lon=[point['lon']],
|
|
|
|
| 2155 |
opacity=point.get('confidence', 0.8),
|
| 2156 |
line=dict(width=1, color='white')
|
| 2157 |
),
|
| 2158 |
+
name=f"Hour {point['hour']}" if i % 10 == 0 else None,
|
| 2159 |
+
showlegend=(i % 10 == 0),
|
| 2160 |
hovertemplate=(
|
| 2161 |
f"<b>Hour {point['hour']}</b><br>"
|
| 2162 |
f"Position: {point['lat']:.1f}°N, {point['lon']:.1f}°E<br>"
|
| 2163 |
f"Intensity: {point['intensity_kt']:.0f} kt<br>"
|
| 2164 |
f"Category: {point['category']}<br>"
|
| 2165 |
f"Stage: {point.get('development_stage', 'Unknown')}<br>"
|
| 2166 |
+
f"Speed: {point.get('forward_speed_kmh', 15):.1f} km/h<br>"
|
| 2167 |
"<extra></extra>"
|
| 2168 |
)
|
| 2169 |
+
),
|
| 2170 |
+
row=1, col=1
|
| 2171 |
)
|
| 2172 |
|
| 2173 |
# Connect points with track line
|
|
|
|
| 2179 |
line=dict(color='black', width=3),
|
| 2180 |
name='Forecast Track',
|
| 2181 |
showlegend=True
|
| 2182 |
+
),
|
| 2183 |
+
row=1, col=1
|
| 2184 |
+
)
|
| 2185 |
+
|
| 2186 |
+
# Add static intensity, speed, and pressure plots
|
| 2187 |
+
# Wind speed plot
|
| 2188 |
+
fig.add_trace(
|
| 2189 |
+
go.Scatter(
|
| 2190 |
+
x=hours,
|
| 2191 |
+
y=intensities,
|
| 2192 |
+
mode='lines+markers',
|
| 2193 |
+
line=dict(color='red', width=3),
|
| 2194 |
+
marker=dict(size=6, color='red'),
|
| 2195 |
+
name='Wind Speed',
|
| 2196 |
+
showlegend=False
|
| 2197 |
+
),
|
| 2198 |
+
row=2, col=1
|
| 2199 |
+
)
|
| 2200 |
+
|
| 2201 |
+
# Add category threshold lines
|
| 2202 |
+
thresholds = [34, 64, 83, 96, 113, 137]
|
| 2203 |
+
threshold_names = ['TS', 'C1', 'C2', 'C3', 'C4', 'C5']
|
| 2204 |
+
|
| 2205 |
+
for thresh, name in zip(thresholds, threshold_names):
|
| 2206 |
+
fig.add_trace(
|
| 2207 |
+
go.Scatter(
|
| 2208 |
+
x=[min(hours), max(hours)],
|
| 2209 |
+
y=[thresh, thresh],
|
| 2210 |
+
mode='lines',
|
| 2211 |
+
line=dict(color='gray', width=1, dash='dash'),
|
| 2212 |
+
name=name,
|
| 2213 |
+
showlegend=False,
|
| 2214 |
+
hovertemplate=f"{name} Threshold: {thresh} kt<extra></extra>"
|
| 2215 |
+
),
|
| 2216 |
+
row=2, col=1
|
| 2217 |
)
|
| 2218 |
|
| 2219 |
+
# Forward speed plot
|
| 2220 |
+
fig.add_trace(
|
| 2221 |
+
go.Scatter(
|
| 2222 |
+
x=hours,
|
| 2223 |
+
y=speeds,
|
| 2224 |
+
mode='lines+markers',
|
| 2225 |
+
line=dict(color='green', width=2),
|
| 2226 |
+
marker=dict(size=4, color='green'),
|
| 2227 |
+
name='Forward Speed',
|
| 2228 |
+
showlegend=False
|
| 2229 |
+
),
|
| 2230 |
+
row=2, col=2
|
| 2231 |
+
)
|
| 2232 |
+
|
| 2233 |
# Add uncertainty cone if requested
|
| 2234 |
if show_uncertainty and len(route_data) > 1:
|
| 2235 |
uncertainty_lats_upper = []
|
|
|
|
| 2239 |
|
| 2240 |
for i, point in enumerate(route_data):
|
| 2241 |
# Uncertainty grows with time and decreases with confidence
|
| 2242 |
+
base_uncertainty = 0.4 + (i / len(route_data)) * 1.8
|
| 2243 |
confidence_factor = point.get('confidence', 0.8)
|
| 2244 |
uncertainty = base_uncertainty / confidence_factor
|
| 2245 |
|
|
|
|
| 2261 |
line=dict(color='rgba(128,128,128,0.4)', width=1),
|
| 2262 |
name='Uncertainty Cone',
|
| 2263 |
showlegend=True
|
| 2264 |
+
),
|
| 2265 |
+
row=1, col=1
|
| 2266 |
)
|
| 2267 |
|
| 2268 |
+
# Enhanced layout
|
| 2269 |
fig.update_layout(
|
| 2270 |
+
title=f"Comprehensive Storm Development Analysis<br><sub>Starting from {prediction_results['genesis_info']['description']}</sub>",
|
| 2271 |
+
height=1000, # Taller for better subplot visibility
|
| 2272 |
+
width=1400, # Wider
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2273 |
showlegend=True
|
| 2274 |
)
|
| 2275 |
|
| 2276 |
+
# Update geo layout
|
| 2277 |
+
fig.update_geos(
|
| 2278 |
+
projection_type="natural earth",
|
| 2279 |
+
showland=True,
|
| 2280 |
+
landcolor="LightGray",
|
| 2281 |
+
showocean=True,
|
| 2282 |
+
oceancolor="LightBlue",
|
| 2283 |
+
showcoastlines=True,
|
| 2284 |
+
coastlinecolor="DarkGray",
|
| 2285 |
+
showlakes=True,
|
| 2286 |
+
lakecolor="LightBlue",
|
| 2287 |
+
center=dict(lat=np.mean(lats), lon=np.mean(lons)),
|
| 2288 |
+
projection_scale=2.0,
|
| 2289 |
+
row=1, col=1
|
| 2290 |
+
)
|
| 2291 |
+
|
| 2292 |
+
# Update subplot axes
|
| 2293 |
+
fig.update_xaxes(title_text="Forecast Hour", row=2, col=1)
|
| 2294 |
+
fig.update_yaxes(title_text="Wind Speed (kt)", row=2, col=1)
|
| 2295 |
+
fig.update_xaxes(title_text="Forecast Hour", row=2, col=2)
|
| 2296 |
+
fig.update_yaxes(title_text="Forward Speed (km/h)", row=2, col=2)
|
| 2297 |
+
|
| 2298 |
# Generate enhanced forecast text
|
| 2299 |
current = prediction_results['current_prediction']
|
| 2300 |
genesis_info = prediction_results['genesis_info']
|
| 2301 |
|
| 2302 |
+
# Calculate some statistics
|
| 2303 |
+
max_intensity = max(intensities)
|
| 2304 |
+
max_intensity_time = hours[intensities.index(max_intensity)]
|
| 2305 |
+
avg_speed = np.mean(speeds)
|
| 2306 |
+
|
| 2307 |
forecast_text = f"""
|
| 2308 |
+
COMPREHENSIVE STORM DEVELOPMENT FORECAST
|
| 2309 |
+
{'='*65}
|
| 2310 |
|
| 2311 |
GENESIS CONDITIONS:
|
| 2312 |
• Region: {current.get('genesis_region', 'Unknown')}
|
|
|
|
| 2314 |
• Starting Position: {lats[0]:.1f}°N, {lons[0]:.1f}°E
|
| 2315 |
• Initial Intensity: {current['intensity_kt']:.0f} kt (Tropical Depression)
|
| 2316 |
• Genesis Pressure: {current.get('pressure_hpa', 1008):.0f} hPa
|
| 2317 |
+
|
| 2318 |
+
STORM CHARACTERISTICS:
|
| 2319 |
+
• Peak Intensity: {max_intensity:.0f} kt at Hour {max_intensity_time}
|
| 2320 |
+
• Average Forward Speed: {avg_speed:.1f} km/h
|
| 2321 |
+
• Total Distance: {sum([speeds[i]/6 for i in range(len(speeds))]):.0f} km
|
| 2322 |
+
• Final Position: {lats[-1]:.1f}°N, {lons[-1]:.1f}°E
|
| 2323 |
+
• Forecast Duration: {hours[-1]} hours ({hours[-1]/24:.1f} days)
|
| 2324 |
|
| 2325 |
DEVELOPMENT TIMELINE:
|
| 2326 |
• Hour 0 (Genesis): {intensities[0]:.0f} kt - {categories[0]}
|
| 2327 |
+
• Hour 24: {intensities[min(4, len(intensities)-1)]:.0f} kt - {categories[min(4, len(categories)-1)]}
|
| 2328 |
+
• Hour 48: {intensities[min(8, len(intensities)-1)]:.0f} kt - {categories[min(8, len(categories)-1)]}
|
| 2329 |
+
• Hour 72: {intensities[min(12, len(intensities)-1)]:.0f} kt - {categories[min(12, len(categories)-1)]}
|
| 2330 |
+
• Final: {intensities[-1]:.0f} kt - {categories[-1]}
|
| 2331 |
+
|
| 2332 |
+
MOTION ANALYSIS:
|
| 2333 |
+
• Initial Motion: {speeds[0]:.1f} km/h
|
| 2334 |
+
• Peak Speed: {max(speeds):.1f} km/h at Hour {hours[speeds.index(max(speeds))]}
|
| 2335 |
+
• Final Motion: {speeds[-1]:.1f} km/h
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2336 |
|
| 2337 |
CONFIDENCE ASSESSMENT:
|
| 2338 |
• Genesis Likelihood: {prediction_results['confidence_scores'].get('genesis', 0.85)*100:.0f}%
|
|
|
|
| 2339 |
• 24-hour Track: {prediction_results['confidence_scores'].get('position_24h', 0.85)*100:.0f}%
|
| 2340 |
• 48-hour Track: {prediction_results['confidence_scores'].get('position_48h', 0.75)*100:.0f}%
|
| 2341 |
• 72-hour Track: {prediction_results['confidence_scores'].get('position_72h', 0.65)*100:.0f}%
|
| 2342 |
+
• Long-term: {prediction_results['confidence_scores'].get('long_term', 0.50)*100:.0f}%
|
| 2343 |
+
|
| 2344 |
+
FEATURES:
|
| 2345 |
+
{"✅ Animation Enabled - Use controls to watch development" if enable_animation else "📊 Static Analysis - All time steps displayed"}
|
| 2346 |
+
✅ Realistic Forward Speeds (15-25 km/h typical)
|
| 2347 |
+
✅ Environmental Coupling (ENSO, SST, Shear)
|
| 2348 |
+
✅ Multi-stage Development Cycle
|
| 2349 |
+
✅ Uncertainty Quantification
|
| 2350 |
|
| 2351 |
MODEL: {prediction_results['model_info']}
|
|
|
|
| 2352 |
"""
|
| 2353 |
|
| 2354 |
return fig, forecast_text.strip()
|
| 2355 |
|
| 2356 |
except Exception as e:
|
| 2357 |
+
error_msg = f"Error creating comprehensive visualization: {str(e)}"
|
| 2358 |
logging.error(error_msg)
|
| 2359 |
import traceback
|
| 2360 |
traceback.print_exc()
|
|
|
|
| 3094 |
pred_month = gr.Slider(1, 12, label="Month", value=9, info="Peak season: Jul-Oct")
|
| 3095 |
pred_oni = gr.Number(label="ONI Value", value=0.0, info="ENSO index (-3 to 3)")
|
| 3096 |
with gr.Row():
|
| 3097 |
+
forecast_hours = gr.Number(
|
|
|
|
| 3098 |
label="Forecast Length (hours)",
|
| 3099 |
value=72,
|
| 3100 |
+
minimum=20,
|
| 3101 |
+
maximum=1000,
|
| 3102 |
step=6,
|
| 3103 |
+
info="Extended forecasting: 20-1000 hours (42 days max)"
|
| 3104 |
)
|
| 3105 |
advanced_physics = gr.Checkbox(
|
| 3106 |
label="Advanced Physics",
|
|
|
|
| 3178 |
prediction_info_text = """
|
| 3179 |
### 🌊 Realistic Storm Genesis Features:
|
| 3180 |
- **Climatological Genesis Regions**: 10 realistic development zones based on historical data
|
| 3181 |
+
- **Tropical Depression Starting Point**: Storms begin at realistic 25-38 kt intensities
|
| 3182 |
+
- **Realistic Typhoon Speeds**: Forward motion 15-25 km/h (matching observations)
|
| 3183 |
+
- **Extended Time Range**: 20-1000 hours (up to 42 days) with user input control
|
| 3184 |
+
- **Comprehensive Animation**: Watch development with wind/speed/pressure plots
|
| 3185 |
- **Environmental Coupling**: Advanced physics with ENSO, SST, shear, β-drift, ridge patterns
|
|
|
|
| 3186 |
|
| 3187 |
### 📊 Enhanced Development Stages:
|
| 3188 |
- **Genesis (0-24h)**: Initial cyclogenesis at Tropical Depression level
|
| 3189 |
+
- **Development (24-72h)**: Rapid intensification in favorable environment (3-6 kt/h)
|
| 3190 |
- **Mature (72-120h)**: Peak intensity phase with environmental modulation
|
| 3191 |
+
- **Extended (120-240h)**: Continued tracking with realistic weakening
|
| 3192 |
+
- **Long-term (240h+)**: Extended forecasting for research and planning
|
| 3193 |
+
|
| 3194 |
+
### 🏃♂️ Realistic Motion Physics:
|
| 3195 |
+
- **Low Latitude (< 20°N)**: 12-15 km/h typical speeds
|
| 3196 |
+
- **Mid Latitude (20-30°N)**: 18-22 km/h moderate speeds
|
| 3197 |
+
- **High Latitude (> 30°N)**: 25-30 km/h fast extratropical transition
|
| 3198 |
+
- **Intensity Effects**: Stronger storms can move faster in steering flow
|
| 3199 |
+
- **Beta Drift**: Coriolis-induced poleward and westward motion
|
| 3200 |
+
- **Ridge Interaction**: Realistic recurvature patterns
|
| 3201 |
|
| 3202 |
### 🌍 Genesis Region Selection:
|
| 3203 |
- **Western Pacific MDR**: Peak activity zone near Guam (12.5°N, 145°E)
|
|
|
|
| 3207 |
- **Monsoon Trough**: Monsoon-driven genesis (10°N, 130°E)
|
| 3208 |
- **Other Basins**: Bay of Bengal, Eastern Pacific, Atlantic options
|
| 3209 |
|
| 3210 |
+
### 🎬 Enhanced Animation Features:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3211 |
- **Real-time Development**: Watch TD evolve to typhoon intensity
|
| 3212 |
+
- **Multi-plot Display**: Track + Wind Speed + Forward Speed plots
|
| 3213 |
+
- **Interactive Controls**: Play/pause/fast buttons and time slider
|
| 3214 |
- **Stage Tracking**: Visual indicators for development phases
|
| 3215 |
+
- **Speed Analysis**: Forward motion tracking throughout lifecycle
|
| 3216 |
+
- **Performance Optimized**: Smooth animation even for 1000+ hour forecasts
|
| 3217 |
+
|
| 3218 |
+
### 🔬 Advanced Physics Model:
|
| 3219 |
+
- **Realistic Intensification**: 3-6 kt/h development rates in favorable conditions
|
| 3220 |
+
- **Environmental Coupling**: SST, wind shear, Coriolis effects
|
| 3221 |
+
- **Steering Flow**: Subtropical ridge position and ENSO modulation
|
| 3222 |
+
- **Motion Variability**: Growing uncertainty with forecast time
|
| 3223 |
+
- **Pressure-Wind Relationship**: Realistic intensity-pressure coupling
|
| 3224 |
+
- **Long-term Climatology**: Extended forecasting using seasonal patterns
|
| 3225 |
"""
|
| 3226 |
gr.Markdown(prediction_info_text)
|
| 3227 |
|