Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1587,164 +1587,191 @@ 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
|
| 1591 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1592 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1593 |
results = {
|
| 1594 |
'current_prediction': {},
|
| 1595 |
'route_forecast': [],
|
| 1596 |
'confidence_scores': {},
|
| 1597 |
-
'model_info': '
|
|
|
|
| 1598 |
}
|
| 1599 |
|
| 1600 |
-
#
|
| 1601 |
-
base_intensity =
|
| 1602 |
|
| 1603 |
-
#
|
| 1604 |
-
if oni_value > 1.
|
| 1605 |
-
intensity_modifier = -
|
| 1606 |
elif oni_value > 0.5: # Moderate El Niño
|
| 1607 |
-
intensity_modifier = -
|
| 1608 |
-
elif oni_value < -1.
|
| 1609 |
-
intensity_modifier =
|
| 1610 |
elif oni_value < -0.5: # Moderate La Niña
|
| 1611 |
-
intensity_modifier =
|
| 1612 |
else: # Neutral
|
| 1613 |
-
intensity_modifier = oni_value *
|
| 1614 |
|
| 1615 |
-
#
|
| 1616 |
seasonal_factors = {
|
| 1617 |
-
1: -
|
| 1618 |
-
7:
|
| 1619 |
}
|
| 1620 |
seasonal_modifier = seasonal_factors.get(month, 0)
|
| 1621 |
|
| 1622 |
-
#
|
| 1623 |
-
|
| 1624 |
-
|
| 1625 |
-
|
| 1626 |
-
|
| 1627 |
-
|
| 1628 |
-
|
| 1629 |
-
|
| 1630 |
-
|
| 1631 |
-
|
| 1632 |
-
|
| 1633 |
-
|
| 1634 |
-
|
| 1635 |
-
|
| 1636 |
-
|
| 1637 |
-
|
| 1638 |
-
|
| 1639 |
-
|
| 1640 |
-
|
| 1641 |
-
|
| 1642 |
-
|
| 1643 |
-
elif 15 < lat <= 25: # Moderate shear
|
| 1644 |
-
shear_modifier = 5
|
| 1645 |
-
else: # High shear
|
| 1646 |
-
shear_modifier = -20
|
| 1647 |
-
|
| 1648 |
-
# Calculate enhanced current intensity
|
| 1649 |
-
predicted_intensity = (base_intensity + intensity_modifier + seasonal_modifier +
|
| 1650 |
-
lat_modifier + sst_modifier + shear_modifier)
|
| 1651 |
-
predicted_intensity = max(20, min(200, predicted_intensity))
|
| 1652 |
-
|
| 1653 |
-
# Add realistic uncertainty
|
| 1654 |
-
intensity_uncertainty = np.random.normal(0, 3)
|
| 1655 |
predicted_intensity += intensity_uncertainty
|
|
|
|
| 1656 |
|
| 1657 |
results['current_prediction'] = {
|
| 1658 |
'intensity_kt': predicted_intensity,
|
| 1659 |
-
'pressure_hpa':
|
| 1660 |
-
'category': categorize_typhoon_enhanced(predicted_intensity)
|
|
|
|
| 1661 |
}
|
| 1662 |
|
| 1663 |
-
# Enhanced route prediction with
|
| 1664 |
current_lat = lat
|
| 1665 |
current_lon = lon
|
| 1666 |
current_intensity = predicted_intensity
|
| 1667 |
|
| 1668 |
route_points = []
|
| 1669 |
|
| 1670 |
-
#
|
| 1671 |
for hour in range(0, forecast_hours + 6, 6):
|
| 1672 |
-
|
| 1673 |
-
|
| 1674 |
-
# Beta drift (Coriolis effect)
|
| 1675 |
-
beta_drift_lat = 0.
|
| 1676 |
-
beta_drift_lon = -0.
|
| 1677 |
-
|
| 1678 |
-
# Seasonal
|
| 1679 |
-
if month in [6, 7, 8, 9]: #
|
| 1680 |
-
ridge_strength = 1.
|
| 1681 |
-
ridge_position = 30 +
|
| 1682 |
-
else: #
|
| 1683 |
-
ridge_strength = 0.
|
| 1684 |
ridge_position = 25
|
| 1685 |
|
| 1686 |
-
#
|
| 1687 |
-
if current_lat < ridge_position -
|
| 1688 |
-
lat_tendency = 0.
|
| 1689 |
-
lon_tendency = -0.
|
| 1690 |
-
elif current_lat > ridge_position -
|
| 1691 |
-
lat_tendency = 0.
|
| 1692 |
-
lon_tendency = 0.
|
| 1693 |
-
else: # In
|
| 1694 |
lat_tendency = 0.15 + beta_drift_lat
|
| 1695 |
-
lon_tendency = -0.
|
| 1696 |
-
|
| 1697 |
-
#
|
| 1698 |
-
if oni_value > 0.5: # El Niño -
|
| 1699 |
-
lon_tendency += 0.
|
| 1700 |
-
|
| 1701 |
-
|
| 1702 |
-
|
| 1703 |
-
|
| 1704 |
-
|
| 1705 |
-
|
| 1706 |
-
|
| 1707 |
-
lat_tendency *= speed_factor
|
| 1708 |
-
lon_tendency *= speed_factor
|
| 1709 |
-
|
| 1710 |
-
# Add realistic motion variability
|
| 1711 |
-
motion_noise = 0.03 if hour < 48 else 0.05 # Increasing uncertainty
|
| 1712 |
-
lat_noise = np.random.normal(0, motion_noise)
|
| 1713 |
-
lon_noise = np.random.normal(0, motion_noise)
|
| 1714 |
|
| 1715 |
# Update position
|
| 1716 |
current_lat += lat_tendency + lat_noise
|
| 1717 |
current_lon += lon_tendency + lon_noise
|
| 1718 |
|
| 1719 |
-
#
|
| 1720 |
|
| 1721 |
-
#
|
| 1722 |
-
if hour <
|
| 1723 |
-
|
| 1724 |
-
|
| 1725 |
-
|
| 1726 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1727 |
intensity_tendency = -1.5
|
| 1728 |
|
| 1729 |
# Environmental modulation
|
| 1730 |
-
if current_lat > 35: # High latitude rapid weakening
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1731 |
intensity_tendency -= 8
|
| 1732 |
-
elif current_lat
|
| 1733 |
-
intensity_tendency -= 4
|
| 1734 |
-
elif current_lon < 120: # Land interaction (crude approximation)
|
| 1735 |
-
intensity_tendency -= 12
|
| 1736 |
-
elif 130 <= current_lon <= 150 and 15 <= current_lat <= 25: # Favorable environment
|
| 1737 |
intensity_tendency += 1
|
| 1738 |
|
| 1739 |
-
# SST effects (
|
| 1740 |
-
if current_lat <
|
|
|
|
|
|
|
| 1741 |
intensity_tendency += 1.5
|
| 1742 |
elif current_lat > 30: # Cooler waters
|
| 1743 |
-
intensity_tendency -=
|
| 1744 |
|
| 1745 |
-
# Update intensity
|
| 1746 |
current_intensity += intensity_tendency
|
| 1747 |
-
current_intensity = max(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1748 |
|
| 1749 |
route_points.append({
|
| 1750 |
'hour': hour,
|
|
@@ -1752,46 +1779,42 @@ def predict_storm_route_and_intensity_enhanced(lat, lon, month, oni_value, model
|
|
| 1752 |
'lon': current_lon,
|
| 1753 |
'intensity_kt': current_intensity,
|
| 1754 |
'category': categorize_typhoon_enhanced(current_intensity),
|
| 1755 |
-
'confidence':
|
|
|
|
| 1756 |
})
|
| 1757 |
|
| 1758 |
results['route_forecast'] = route_points
|
| 1759 |
|
| 1760 |
-
#
|
| 1761 |
-
base_confidence = 0.85
|
| 1762 |
-
time_decay = min(0.6, forecast_hours / 120) # Slower decay for longer forecasts
|
| 1763 |
-
|
| 1764 |
results['confidence_scores'] = {
|
| 1765 |
-
'
|
| 1766 |
-
'
|
| 1767 |
-
'
|
| 1768 |
-
'
|
| 1769 |
-
'
|
| 1770 |
-
'
|
|
|
|
|
|
|
|
|
|
| 1771 |
}
|
| 1772 |
|
| 1773 |
-
#
|
| 1774 |
-
|
| 1775 |
-
results['model_info'] = "Hybrid CNN-Physics Model with Environmental Coupling"
|
| 1776 |
-
elif use_advanced_physics:
|
| 1777 |
-
results['model_info'] = "Advanced Physics Model with Multi-Factor Analysis"
|
| 1778 |
-
else:
|
| 1779 |
-
results['model_info'] = "Enhanced Climatological Model"
|
| 1780 |
|
| 1781 |
return results
|
| 1782 |
|
| 1783 |
except Exception as e:
|
| 1784 |
-
logging.error(f"
|
| 1785 |
return {
|
| 1786 |
'error': f"Prediction error: {str(e)}",
|
| 1787 |
-
'current_prediction': {'intensity_kt':
|
| 1788 |
'route_forecast': [],
|
| 1789 |
'confidence_scores': {},
|
| 1790 |
'model_info': 'Error in prediction'
|
| 1791 |
}
|
| 1792 |
|
| 1793 |
-
def
|
| 1794 |
-
"""Create
|
| 1795 |
try:
|
| 1796 |
if 'route_forecast' not in prediction_results or not prediction_results['route_forecast']:
|
| 1797 |
return None, "No route forecast data available"
|
|
@@ -1804,72 +1827,218 @@ def create_route_visualization(prediction_results, show_uncertainty=True):
|
|
| 1804 |
lons = [point['lon'] for point in route_data]
|
| 1805 |
intensities = [point['intensity_kt'] for point in route_data]
|
| 1806 |
categories = [point['category'] for point in route_data]
|
|
|
|
|
|
|
| 1807 |
|
| 1808 |
-
|
| 1809 |
-
|
| 1810 |
-
|
| 1811 |
-
subplot_titles=('Forecast Track', 'Intensity Evolution', 'Position Uncertainty', 'Category Timeline'),
|
| 1812 |
-
specs=[[{"type": "geo", "colspan": 2}, None],
|
| 1813 |
-
[{"type": "xy"}, {"type": "xy"}]],
|
| 1814 |
-
vertical_spacing=0.1
|
| 1815 |
-
)
|
| 1816 |
-
|
| 1817 |
-
# 1. Route visualization on geographic plot
|
| 1818 |
-
for i in range(len(route_data)):
|
| 1819 |
-
point = route_data[i]
|
| 1820 |
-
color = enhanced_color_map.get(point['category'], 'rgb(128,128,128)')
|
| 1821 |
-
|
| 1822 |
-
# Convert rgb to regular color format
|
| 1823 |
-
color_hex = rgb_string_to_hex(color)
|
| 1824 |
-
|
| 1825 |
-
if i == 0: # Current position
|
| 1826 |
-
marker_size = 15
|
| 1827 |
-
opacity = 1.0
|
| 1828 |
-
symbol = 'star'
|
| 1829 |
-
else:
|
| 1830 |
-
marker_size = 8 + (point['intensity_kt'] / 20) # Size based on intensity
|
| 1831 |
-
opacity = max(0.3, 1.0 - (i / len(route_data)) * 0.7)
|
| 1832 |
-
symbol = 'circle'
|
| 1833 |
|
|
|
|
| 1834 |
fig.add_trace(
|
| 1835 |
go.Scattergeo(
|
| 1836 |
-
lon=
|
| 1837 |
-
lat=
|
| 1838 |
-
mode='
|
| 1839 |
-
|
| 1840 |
-
|
| 1841 |
-
|
| 1842 |
-
|
| 1843 |
-
|
| 1844 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1845 |
),
|
| 1846 |
-
|
| 1847 |
-
|
| 1848 |
-
|
| 1849 |
-
|
| 1850 |
-
|
| 1851 |
-
|
| 1852 |
-
|
| 1853 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1854 |
)
|
| 1855 |
-
)
|
| 1856 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1857 |
)
|
| 1858 |
|
| 1859 |
-
#
|
| 1860 |
-
fig.add_trace(
|
| 1861 |
-
go.Scattergeo(
|
| 1862 |
-
lon=lons,
|
| 1863 |
-
lat=lats,
|
| 1864 |
-
mode='lines',
|
| 1865 |
-
line=dict(color='black', width=3, dash='solid'),
|
| 1866 |
-
name='Forecast Track',
|
| 1867 |
-
showlegend=True
|
| 1868 |
-
),
|
| 1869 |
-
row=1, col=1
|
| 1870 |
-
)
|
| 1871 |
-
|
| 1872 |
-
# Uncertainty cone (if requested)
|
| 1873 |
if show_uncertainty and len(route_data) > 1:
|
| 1874 |
uncertainty_lats_upper = []
|
| 1875 |
uncertainty_lats_lower = []
|
|
@@ -1877,7 +2046,11 @@ def create_route_visualization(prediction_results, show_uncertainty=True):
|
|
| 1877 |
uncertainty_lons_lower = []
|
| 1878 |
|
| 1879 |
for i, point in enumerate(route_data):
|
| 1880 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1881 |
uncertainty_lats_upper.append(point['lat'] + uncertainty)
|
| 1882 |
uncertainty_lats_lower.append(point['lat'] - uncertainty)
|
| 1883 |
uncertainty_lons_upper.append(point['lon'] + uncertainty)
|
|
@@ -1896,121 +2069,81 @@ def create_route_visualization(prediction_results, show_uncertainty=True):
|
|
| 1896 |
line=dict(color='rgba(128,128,128,0.4)', width=1),
|
| 1897 |
name='Uncertainty Cone',
|
| 1898 |
showlegend=True
|
| 1899 |
-
)
|
| 1900 |
-
row=1, col=1
|
| 1901 |
-
)
|
| 1902 |
-
|
| 1903 |
-
# 2. Intensity evolution plot (regular subplot - no geo conflicts)
|
| 1904 |
-
fig.add_trace(
|
| 1905 |
-
go.Scatter(
|
| 1906 |
-
x=hours,
|
| 1907 |
-
y=intensities,
|
| 1908 |
-
mode='lines+markers',
|
| 1909 |
-
line=dict(color='red', width=3),
|
| 1910 |
-
marker=dict(size=6, color='red'),
|
| 1911 |
-
name='Intensity',
|
| 1912 |
-
showlegend=False
|
| 1913 |
-
),
|
| 1914 |
-
row=2, col=1
|
| 1915 |
-
)
|
| 1916 |
-
|
| 1917 |
-
# Add category threshold lines (NOT using add_hline to avoid geo conflicts)
|
| 1918 |
-
thresholds = [34, 64, 83, 96, 113, 137]
|
| 1919 |
-
threshold_names = ['TS', 'C1', 'C2', 'C3', 'C4', 'C5']
|
| 1920 |
-
|
| 1921 |
-
for thresh, name in zip(thresholds, threshold_names):
|
| 1922 |
-
fig.add_trace(
|
| 1923 |
-
go.Scatter(
|
| 1924 |
-
x=[min(hours), max(hours)],
|
| 1925 |
-
y=[thresh, thresh],
|
| 1926 |
-
mode='lines',
|
| 1927 |
-
line=dict(color='gray', width=1, dash='dash'),
|
| 1928 |
-
name=name,
|
| 1929 |
-
showlegend=False,
|
| 1930 |
-
hovertemplate=f"{name} Threshold: {thresh} kt<extra></extra>"
|
| 1931 |
-
),
|
| 1932 |
-
row=2, col=1
|
| 1933 |
)
|
| 1934 |
|
| 1935 |
-
#
|
| 1936 |
-
uncertainties = [0.3 + (i / len(route_data)) * 1.5 for i in range(len(route_data))]
|
| 1937 |
-
fig.add_trace(
|
| 1938 |
-
go.Scatter(
|
| 1939 |
-
x=hours,
|
| 1940 |
-
y=uncertainties,
|
| 1941 |
-
mode='lines+markers',
|
| 1942 |
-
line=dict(color='orange', width=2),
|
| 1943 |
-
marker=dict(size=4, color='orange'),
|
| 1944 |
-
name='Position Error',
|
| 1945 |
-
showlegend=False
|
| 1946 |
-
),
|
| 1947 |
-
row=2, col=2
|
| 1948 |
-
)
|
| 1949 |
-
|
| 1950 |
-
# Update layout
|
| 1951 |
fig.update_layout(
|
| 1952 |
-
|
| 1953 |
-
|
| 1954 |
-
|
| 1955 |
-
|
| 1956 |
-
|
| 1957 |
-
|
| 1958 |
-
|
| 1959 |
-
|
| 1960 |
-
|
| 1961 |
-
|
| 1962 |
-
|
| 1963 |
-
|
| 1964 |
-
|
| 1965 |
-
|
| 1966 |
-
|
| 1967 |
-
|
| 1968 |
-
|
| 1969 |
)
|
| 1970 |
|
| 1971 |
-
#
|
| 1972 |
-
fig.update_xaxes(title_text="Forecast Hour", row=2, col=1)
|
| 1973 |
-
fig.update_yaxes(title_text="Intensity (kt)", row=2, col=1)
|
| 1974 |
-
fig.update_xaxes(title_text="Forecast Hour", row=2, col=2)
|
| 1975 |
-
fig.update_yaxes(title_text="Position Error (°)", row=2, col=2)
|
| 1976 |
-
|
| 1977 |
-
# Generate detailed forecast text
|
| 1978 |
current = prediction_results['current_prediction']
|
|
|
|
|
|
|
| 1979 |
forecast_text = f"""
|
| 1980 |
-
|
| 1981 |
-
{'='*
|
| 1982 |
-
|
| 1983 |
-
|
| 1984 |
-
•
|
| 1985 |
-
•
|
| 1986 |
-
•
|
| 1987 |
-
|
| 1988 |
-
|
| 1989 |
-
•
|
| 1990 |
-
|
| 1991 |
-
|
| 1992 |
-
•
|
| 1993 |
-
|
| 1994 |
-
|
| 1995 |
-
•
|
| 1996 |
-
|
| 1997 |
-
|
| 1998 |
-
•
|
| 1999 |
-
|
| 2000 |
-
|
| 2001 |
-
•
|
| 2002 |
-
•
|
| 2003 |
-
|
| 2004 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2005 |
|
| 2006 |
MODEL: {prediction_results['model_info']}
|
|
|
|
| 2007 |
"""
|
| 2008 |
|
| 2009 |
return fig, forecast_text.strip()
|
| 2010 |
|
| 2011 |
except Exception as e:
|
| 2012 |
-
error_msg = f"Error creating route visualization: {str(e)}"
|
| 2013 |
-
|
| 2014 |
import traceback
|
| 2015 |
traceback.print_exc()
|
| 2016 |
return None, error_msg
|
|
@@ -2698,57 +2831,85 @@ def create_interface():
|
|
| 2698 |
"""
|
| 2699 |
gr.Markdown(cluster_info_text)
|
| 2700 |
|
| 2701 |
-
with gr.Tab("
|
| 2702 |
-
gr.Markdown("## 🌊
|
| 2703 |
|
| 2704 |
if CNN_AVAILABLE:
|
| 2705 |
gr.Markdown("🧠 **Deep Learning models available** - TensorFlow loaded successfully")
|
| 2706 |
-
method_description = "
|
| 2707 |
else:
|
| 2708 |
gr.Markdown("🔬 **Physics-based models available** - Using climatological relationships")
|
| 2709 |
-
|
| 2710 |
-
method_description = "Using established meteorological relationships and climatology"
|
| 2711 |
|
| 2712 |
gr.Markdown(f"**Current Method**: {method_description}")
|
|
|
|
|
|
|
|
|
|
| 2713 |
|
| 2714 |
with gr.Row():
|
| 2715 |
with gr.Column(scale=2):
|
| 2716 |
-
gr.Markdown("###
|
| 2717 |
-
|
| 2718 |
-
|
| 2719 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2720 |
with gr.Row():
|
| 2721 |
-
pred_month = gr.Slider(1, 12, label="Month", value=9, info="
|
| 2722 |
-
pred_oni = gr.Number(label="ONI Value", value=0.0, info="
|
| 2723 |
with gr.Row():
|
| 2724 |
forecast_hours = gr.Slider(
|
| 2725 |
6, 240,
|
| 2726 |
label="Forecast Length (hours)",
|
| 2727 |
value=72,
|
| 2728 |
step=6,
|
| 2729 |
-
info="Extended forecasting up to 10 days
|
| 2730 |
)
|
| 2731 |
advanced_physics = gr.Checkbox(
|
| 2732 |
-
label="
|
| 2733 |
value=True,
|
| 2734 |
-
info="
|
| 2735 |
)
|
| 2736 |
with gr.Row():
|
| 2737 |
show_uncertainty = gr.Checkbox(label="Show Uncertainty Cone", value=True)
|
| 2738 |
-
|
| 2739 |
-
label="
|
| 2740 |
-
value=
|
| 2741 |
-
info="
|
| 2742 |
)
|
| 2743 |
|
| 2744 |
with gr.Column(scale=1):
|
| 2745 |
gr.Markdown("### ⚙️ Prediction Controls")
|
| 2746 |
-
predict_btn = gr.Button("
|
| 2747 |
|
| 2748 |
-
gr.Markdown("### 📊
|
| 2749 |
-
current_intensity = gr.Number(label="
|
| 2750 |
-
current_category = gr.Textbox(label="
|
| 2751 |
-
model_confidence = gr.Textbox(label="Model
|
| 2752 |
|
| 2753 |
with gr.Row():
|
| 2754 |
route_plot = gr.Plot(label="🗺️ Advanced Route & Intensity Forecast")
|
|
@@ -2756,69 +2917,88 @@ def create_interface():
|
|
| 2756 |
with gr.Row():
|
| 2757 |
forecast_details = gr.Textbox(label="📋 Detailed Forecast Summary", lines=20, max_lines=25)
|
| 2758 |
|
| 2759 |
-
def
|
| 2760 |
try:
|
| 2761 |
-
# Run
|
| 2762 |
-
results =
|
| 2763 |
-
|
| 2764 |
forecast_hours=hours,
|
| 2765 |
use_advanced_physics=advanced_phys
|
| 2766 |
)
|
| 2767 |
|
| 2768 |
-
# Extract
|
| 2769 |
current = results['current_prediction']
|
| 2770 |
intensity = current['intensity_kt']
|
| 2771 |
category = current['category']
|
| 2772 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2773 |
|
| 2774 |
-
|
| 2775 |
-
fig, forecast_text = create_route_visualization(results, uncertainty)
|
| 2776 |
|
| 2777 |
return (
|
| 2778 |
intensity,
|
| 2779 |
category,
|
| 2780 |
-
|
| 2781 |
fig,
|
| 2782 |
forecast_text
|
| 2783 |
)
|
| 2784 |
except Exception as e:
|
| 2785 |
-
error_msg = f"
|
| 2786 |
logging.error(error_msg)
|
|
|
|
|
|
|
| 2787 |
return (
|
| 2788 |
-
|
| 2789 |
-
None, f"Error generating forecast: {str(e)}"
|
| 2790 |
)
|
| 2791 |
|
| 2792 |
predict_btn.click(
|
| 2793 |
-
fn=
|
| 2794 |
-
inputs=[
|
| 2795 |
outputs=[current_intensity, current_category, model_confidence, route_plot, forecast_details]
|
| 2796 |
)
|
| 2797 |
|
| 2798 |
prediction_info_text = """
|
| 2799 |
-
###
|
| 2800 |
-
- **
|
| 2801 |
-
- **
|
| 2802 |
-
- **
|
| 2803 |
-
- **
|
| 2804 |
-
- **
|
| 2805 |
-
- **
|
| 2806 |
-
|
| 2807 |
-
### 📊 Enhanced
|
| 2808 |
-
- **
|
| 2809 |
-
- **
|
| 2810 |
-
- **
|
| 2811 |
-
- **
|
| 2812 |
-
|
| 2813 |
-
|
| 2814 |
-
|
| 2815 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2816 |
- **Beta Drift**: Coriolis-induced storm motion (latitude-dependent)
|
| 2817 |
- **Ridge Interaction**: Seasonal subtropical ridge position effects
|
| 2818 |
- **ENSO Modulation**: Non-linear intensity and steering effects
|
| 2819 |
- **SST Coupling**: Ocean temperature impact on intensification
|
| 2820 |
- **Shear Analysis**: Environmental wind shear impact assessment
|
| 2821 |
- **Recurvature Physics**: Advanced extratropical transition modeling
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2822 |
"""
|
| 2823 |
gr.Markdown(prediction_info_text)
|
| 2824 |
|
|
@@ -3158,4 +3338,4 @@ def create_minimal_fallback_interface():
|
|
| 3158 |
demo = create_interface()
|
| 3159 |
|
| 3160 |
if __name__ == "__main__":
|
| 3161 |
-
demo.launch()
|
|
|
|
| 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 {
|
| 1593 |
+
"Western Pacific Main Development Region": {"lat": 12.5, "lon": 145.0, "description": "Peak activity zone (Guam area)"},
|
| 1594 |
+
"South China Sea": {"lat": 15.0, "lon": 115.0, "description": "Secondary development region"},
|
| 1595 |
+
"Philippine Sea": {"lat": 18.0, "lon": 135.0, "description": "Recurving storm region"},
|
| 1596 |
+
"Marshall Islands": {"lat": 8.0, "lon": 165.0, "description": "Eastern development zone"},
|
| 1597 |
+
"Monsoon Trough": {"lat": 10.0, "lon": 130.0, "description": "Monsoon-driven genesis"},
|
| 1598 |
+
"ITCZ Region": {"lat": 6.0, "lon": 140.0, "description": "Near-equatorial development"},
|
| 1599 |
+
"Subtropical Region": {"lat": 22.0, "lon": 125.0, "description": "Late season development"},
|
| 1600 |
+
"Bay of Bengal": {"lat": 15.0, "lon": 88.0, "description": "Indian Ocean cyclones"},
|
| 1601 |
+
"Eastern Pacific": {"lat": 12.0, "lon": -105.0, "description": "Hurricane development zone"},
|
| 1602 |
+
"Atlantic MDR": {"lat": 12.0, "lon": -45.0, "description": "Main Development Region"}
|
| 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 starting from TD intensity in proper genesis regions"""
|
| 1607 |
try:
|
| 1608 |
+
genesis_locations = get_realistic_genesis_locations()
|
| 1609 |
+
|
| 1610 |
+
if genesis_region not in genesis_locations:
|
| 1611 |
+
genesis_region = "Western Pacific Main Development Region" # Default
|
| 1612 |
+
|
| 1613 |
+
genesis_info = genesis_locations[genesis_region]
|
| 1614 |
+
lat = genesis_info["lat"]
|
| 1615 |
+
lon = genesis_info["lon"]
|
| 1616 |
+
|
| 1617 |
results = {
|
| 1618 |
'current_prediction': {},
|
| 1619 |
'route_forecast': [],
|
| 1620 |
'confidence_scores': {},
|
| 1621 |
+
'model_info': 'Realistic Genesis Model',
|
| 1622 |
+
'genesis_info': genesis_info
|
| 1623 |
}
|
| 1624 |
|
| 1625 |
+
# REALISTIC starting intensity - Tropical Depression level
|
| 1626 |
+
base_intensity = 28 # Start at TD level (25-33 kt)
|
| 1627 |
|
| 1628 |
+
# Environmental factors for genesis (much smaller effects initially)
|
| 1629 |
+
if oni_value > 1.0: # Strong El Niño - suppressed development
|
| 1630 |
+
intensity_modifier = -5
|
| 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
|
| 1634 |
+
intensity_modifier = +8
|
| 1635 |
elif oni_value < -0.5: # Moderate La Niña
|
| 1636 |
+
intensity_modifier = +5
|
| 1637 |
else: # Neutral
|
| 1638 |
+
intensity_modifier = oni_value * 2
|
| 1639 |
|
| 1640 |
+
# Seasonal genesis effects (realistic for TD formation)
|
| 1641 |
seasonal_factors = {
|
| 1642 |
+
1: -8, 2: -6, 3: -4, 4: -2, 5: 2, 6: 5,
|
| 1643 |
+
7: 8, 8: 10, 9: 12, 10: 8, 11: 4, 12: -5
|
| 1644 |
}
|
| 1645 |
seasonal_modifier = seasonal_factors.get(month, 0)
|
| 1646 |
|
| 1647 |
+
# Genesis region favorability
|
| 1648 |
+
region_factors = {
|
| 1649 |
+
"Western Pacific Main Development Region": 6,
|
| 1650 |
+
"South China Sea": 3,
|
| 1651 |
+
"Philippine Sea": 4,
|
| 1652 |
+
"Marshall Islands": 5,
|
| 1653 |
+
"Monsoon Trough": 4,
|
| 1654 |
+
"ITCZ Region": 2,
|
| 1655 |
+
"Subtropical Region": 1,
|
| 1656 |
+
"Bay of Bengal": 3,
|
| 1657 |
+
"Eastern Pacific": 5,
|
| 1658 |
+
"Atlantic MDR": 4
|
| 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)) # Cap at weak TS level
|
| 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)) # Keep in TD-weak TS range
|
| 1670 |
|
| 1671 |
results['current_prediction'] = {
|
| 1672 |
'intensity_kt': predicted_intensity,
|
| 1673 |
+
'pressure_hpa': 1008 - (predicted_intensity - 25) * 0.5, # Realistic TD pressure
|
| 1674 |
+
'category': categorize_typhoon_enhanced(predicted_intensity),
|
| 1675 |
+
'genesis_region': genesis_region
|
| 1676 |
}
|
| 1677 |
|
| 1678 |
+
# Enhanced route prediction with REALISTIC development cycle
|
| 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 |
+
# Realistic storm motion based on beta drift and steering
|
| 1689 |
+
# Beta drift (Coriolis effect)
|
| 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 |
+
# Seasonal steering patterns
|
| 1694 |
+
if month in [6, 7, 8, 9]: # Peak season
|
| 1695 |
+
ridge_strength = 1.0
|
| 1696 |
+
ridge_position = 30 + 3 * np.sin(2 * np.pi * (month - 6) / 4)
|
| 1697 |
+
else: # Off season
|
| 1698 |
+
ridge_strength = 0.7
|
| 1699 |
ridge_position = 25
|
| 1700 |
|
| 1701 |
+
# Motion based on position relative to subtropical ridge
|
| 1702 |
+
if current_lat < ridge_position - 8: # Well south of ridge
|
| 1703 |
+
lat_tendency = 0.1 + beta_drift_lat
|
| 1704 |
+
lon_tendency = -0.35 + beta_drift_lon
|
| 1705 |
+
elif current_lat > ridge_position - 3: # Near ridge - recurvature
|
| 1706 |
+
lat_tendency = 0.25 + beta_drift_lat
|
| 1707 |
+
lon_tendency = 0.05 + beta_drift_lon
|
| 1708 |
+
else: # In between
|
| 1709 |
lat_tendency = 0.15 + beta_drift_lat
|
| 1710 |
+
lon_tendency = -0.25 + beta_drift_lon
|
| 1711 |
+
|
| 1712 |
+
# ENSO steering modulation
|
| 1713 |
+
if oni_value > 0.5: # El Niño - more eastward motion
|
| 1714 |
+
lon_tendency += 0.1
|
| 1715 |
+
elif oni_value < -0.5: # La Niña - more westward
|
| 1716 |
+
lon_tendency -= 0.08
|
| 1717 |
+
|
| 1718 |
+
# Add motion uncertainty that grows with time
|
| 1719 |
+
motion_uncertainty = 0.02 + (hour / 72) * 0.03
|
| 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 - storms don't just weaken!
|
| 1728 |
|
| 1729 |
+
# Development phase (first 48-72 hours)
|
| 1730 |
+
if hour <= 48:
|
| 1731 |
+
if current_intensity < 65: # Still developing
|
| 1732 |
+
if 15 <= current_lat <= 25 and 120 <= current_lon <= 160: # Favorable environment
|
| 1733 |
+
intensity_tendency = 3.5 if current_intensity < 45 else 2.0
|
| 1734 |
+
else:
|
| 1735 |
+
intensity_tendency = 1.5
|
| 1736 |
+
else: # Already strong
|
| 1737 |
+
intensity_tendency = 0.5
|
| 1738 |
+
# Mature phase (48-96 hours)
|
| 1739 |
+
elif hour <= 96:
|
| 1740 |
+
if current_lat < 30 and current_lon > 120: # Still in favorable waters
|
| 1741 |
+
intensity_tendency = 0.5 if current_intensity < 100 else -0.5
|
| 1742 |
+
else:
|
| 1743 |
+
intensity_tendency = -1.0
|
| 1744 |
+
# Decay phase (96+ hours)
|
| 1745 |
+
else:
|
| 1746 |
intensity_tendency = -1.5
|
| 1747 |
|
| 1748 |
# Environmental modulation
|
| 1749 |
+
if current_lat > 35: # High latitude - rapid weakening
|
| 1750 |
+
intensity_tendency -= 6
|
| 1751 |
+
elif current_lat > 30: # Moderate latitude
|
| 1752 |
+
intensity_tendency -= 2
|
| 1753 |
+
elif current_lon < 115: # Land interaction
|
| 1754 |
intensity_tendency -= 8
|
| 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 near equator (weak Coriolis)
|
| 1760 |
+
intensity_tendency += 0.5
|
| 1761 |
+
elif 8 <= current_lat <= 20: # Sweet spot
|
| 1762 |
intensity_tendency += 1.5
|
| 1763 |
elif current_lat > 30: # Cooler waters
|
| 1764 |
+
intensity_tendency -= 3
|
| 1765 |
|
| 1766 |
+
# Update intensity with realistic bounds
|
| 1767 |
current_intensity += intensity_tendency
|
| 1768 |
+
current_intensity = max(20, min(180, current_intensity)) # Realistic range
|
| 1769 |
+
|
| 1770 |
+
# Calculate confidence based on forecast time and environment
|
| 1771 |
+
base_confidence = 0.9
|
| 1772 |
+
time_penalty = (hour / 72) * 0.4
|
| 1773 |
+
environment_penalty = 0.1 if current_lat > 30 or current_lon < 120 else 0
|
| 1774 |
+
confidence = max(0.25, base_confidence - time_penalty - environment_penalty)
|
| 1775 |
|
| 1776 |
route_points.append({
|
| 1777 |
'hour': hour,
|
|
|
|
| 1779 |
'lon': current_lon,
|
| 1780 |
'intensity_kt': current_intensity,
|
| 1781 |
'category': categorize_typhoon_enhanced(current_intensity),
|
| 1782 |
+
'confidence': confidence,
|
| 1783 |
+
'development_stage': 'Genesis' if hour <= 24 else ('Development' if hour <= 72 else ('Mature' if hour <= 120 else 'Decay'))
|
| 1784 |
})
|
| 1785 |
|
| 1786 |
results['route_forecast'] = route_points
|
| 1787 |
|
| 1788 |
+
# Realistic confidence scores
|
|
|
|
|
|
|
|
|
|
| 1789 |
results['confidence_scores'] = {
|
| 1790 |
+
'genesis': 0.85,
|
| 1791 |
+
'early_development': 0.80,
|
| 1792 |
+
'position_24h': 0.85,
|
| 1793 |
+
'position_48h': 0.75,
|
| 1794 |
+
'position_72h': 0.65,
|
| 1795 |
+
'intensity_24h': 0.70,
|
| 1796 |
+
'intensity_48h': 0.60,
|
| 1797 |
+
'intensity_72h': 0.50,
|
| 1798 |
+
'long_term': max(0.3, 0.8 - (forecast_hours / 120) * 0.5)
|
| 1799 |
}
|
| 1800 |
|
| 1801 |
+
# Model information
|
| 1802 |
+
results['model_info'] = f"Realistic Genesis Model - {genesis_region}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1803 |
|
| 1804 |
return results
|
| 1805 |
|
| 1806 |
except Exception as e:
|
| 1807 |
+
logging.error(f"Realistic prediction error: {str(e)}")
|
| 1808 |
return {
|
| 1809 |
'error': f"Prediction error: {str(e)}",
|
| 1810 |
+
'current_prediction': {'intensity_kt': 30, 'category': 'Tropical Depression'},
|
| 1811 |
'route_forecast': [],
|
| 1812 |
'confidence_scores': {},
|
| 1813 |
'model_info': 'Error in prediction'
|
| 1814 |
}
|
| 1815 |
|
| 1816 |
+
def create_animated_route_visualization(prediction_results, show_uncertainty=True, enable_animation=True):
|
| 1817 |
+
"""Create animated and expandable route visualization"""
|
| 1818 |
try:
|
| 1819 |
if 'route_forecast' not in prediction_results or not prediction_results['route_forecast']:
|
| 1820 |
return None, "No route forecast data available"
|
|
|
|
| 1827 |
lons = [point['lon'] for point in route_data]
|
| 1828 |
intensities = [point['intensity_kt'] for point in route_data]
|
| 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 |
+
# Create animated figure
|
| 1835 |
+
fig = go.Figure()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1836 |
|
| 1837 |
+
# Add complete track as background
|
| 1838 |
fig.add_trace(
|
| 1839 |
go.Scattergeo(
|
| 1840 |
+
lon=lons,
|
| 1841 |
+
lat=lats,
|
| 1842 |
+
mode='lines',
|
| 1843 |
+
line=dict(color='lightgray', width=2, dash='dot'),
|
| 1844 |
+
name='Complete Track',
|
| 1845 |
+
showlegend=True,
|
| 1846 |
+
opacity=0.5
|
| 1847 |
+
)
|
| 1848 |
+
)
|
| 1849 |
+
|
| 1850 |
+
# Add frames for animation
|
| 1851 |
+
frames = []
|
| 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 |
+
# Track line up to current point
|
| 1864 |
+
go.Scattergeo(
|
| 1865 |
+
lon=frame_lons,
|
| 1866 |
+
lat=frame_lats,
|
| 1867 |
+
mode='lines+markers',
|
| 1868 |
+
line=dict(color='blue', width=3),
|
| 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
|
| 1878 |
+
go.Scattergeo(
|
| 1879 |
+
lon=[frame_lons[-1]],
|
| 1880 |
+
lat=[frame_lats[-1]],
|
| 1881 |
+
mode='markers',
|
| 1882 |
+
marker=dict(
|
| 1883 |
+
size=current_size,
|
| 1884 |
+
color=current_color,
|
| 1885 |
+
symbol='star',
|
| 1886 |
+
line=dict(width=2, color='white')
|
| 1887 |
+
),
|
| 1888 |
+
name='Current Position',
|
| 1889 |
+
showlegend=False,
|
| 1890 |
+
hovertemplate=(
|
| 1891 |
+
f"<b>Hour {route_data[i]['hour']}</b><br>"
|
| 1892 |
+
f"Position: {lats[i]:.1f}°N, {lons[i]:.1f}°E<br>"
|
| 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 |
+
|
| 1902 |
+
frames.append(go.Frame(
|
| 1903 |
+
data=frame_data,
|
| 1904 |
+
name=str(i),
|
| 1905 |
+
layout=go.Layout(
|
| 1906 |
+
title=f"Storm Forecast Animation - Hour {route_data[i]['hour']}<br>"
|
| 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 buttons
|
| 1914 |
+
fig.update_layout(
|
| 1915 |
+
updatemenus=[
|
| 1916 |
+
{
|
| 1917 |
+
"buttons": [
|
| 1918 |
+
{
|
| 1919 |
+
"args": [None, {"frame": {"duration": 800, "redraw": True},
|
| 1920 |
+
"fromcurrent": True, "transition": {"duration": 300}}],
|
| 1921 |
+
"label": "▶️ Play",
|
| 1922 |
+
"method": "animate"
|
| 1923 |
+
},
|
| 1924 |
+
{
|
| 1925 |
+
"args": [[None], {"frame": {"duration": 0, "redraw": True},
|
| 1926 |
+
"mode": "immediate", "transition": {"duration": 0}}],
|
| 1927 |
+
"label": "⏸️ Pause",
|
| 1928 |
+
"method": "animate"
|
| 1929 |
+
}
|
| 1930 |
+
],
|
| 1931 |
+
"direction": "left",
|
| 1932 |
+
"pad": {"r": 10, "t": 87},
|
| 1933 |
+
"showactive": False,
|
| 1934 |
+
"type": "buttons",
|
| 1935 |
+
"x": 0.1,
|
| 1936 |
+
"xanchor": "right",
|
| 1937 |
+
"y": 0,
|
| 1938 |
+
"yanchor": "top"
|
| 1939 |
+
}
|
| 1940 |
+
],
|
| 1941 |
+
sliders=[{
|
| 1942 |
+
"active": 0,
|
| 1943 |
+
"yanchor": "top",
|
| 1944 |
+
"xanchor": "left",
|
| 1945 |
+
"currentvalue": {
|
| 1946 |
+
"font": {"size": 20},
|
| 1947 |
+
"prefix": "Hour:",
|
| 1948 |
+
"visible": True,
|
| 1949 |
+
"xanchor": "right"
|
| 1950 |
+
},
|
| 1951 |
+
"transition": {"duration": 300, "easing": "cubic-in-out"},
|
| 1952 |
+
"pad": {"b": 10, "t": 50},
|
| 1953 |
+
"len": 0.9,
|
| 1954 |
+
"x": 0.1,
|
| 1955 |
+
"y": 0,
|
| 1956 |
+
"steps": [
|
| 1957 |
+
{
|
| 1958 |
+
"args": [[str(i)], {"frame": {"duration": 300, "redraw": True},
|
| 1959 |
+
"mode": "immediate", "transition": {"duration": 300}}],
|
| 1960 |
+
"label": f"Hour {route_data[i]['hour']}",
|
| 1961 |
+
"method": "animate"
|
| 1962 |
+
}
|
| 1963 |
+
for i in range(len(route_data))
|
| 1964 |
+
]
|
| 1965 |
+
}]
|
| 1966 |
+
)
|
| 1967 |
+
|
| 1968 |
+
else:
|
| 1969 |
+
# Create static expandable figure
|
| 1970 |
+
fig = go.Figure()
|
| 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']],
|
| 2007 |
+
lat=[point['lat']],
|
| 2008 |
+
mode='markers',
|
| 2009 |
+
marker=dict(
|
| 2010 |
+
size=size,
|
| 2011 |
+
color=color,
|
| 2012 |
+
opacity=point.get('confidence', 0.8),
|
| 2013 |
+
line=dict(width=1, color='white')
|
| 2014 |
+
),
|
| 2015 |
+
name=f"Hour {point['hour']}" if i % 8 == 0 else None,
|
| 2016 |
+
showlegend=(i % 8 == 0),
|
| 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"Confidence: {point.get('confidence', 0.8)*100:.0f}%<br>"
|
| 2024 |
+
"<extra></extra>"
|
| 2025 |
+
)
|
| 2026 |
+
)
|
| 2027 |
+
)
|
| 2028 |
+
|
| 2029 |
+
# Connect points with track line
|
| 2030 |
+
fig.add_trace(
|
| 2031 |
+
go.Scattergeo(
|
| 2032 |
+
lon=lons,
|
| 2033 |
+
lat=lats,
|
| 2034 |
+
mode='lines',
|
| 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 = []
|
| 2044 |
uncertainty_lats_lower = []
|
|
|
|
| 2046 |
uncertainty_lons_lower = []
|
| 2047 |
|
| 2048 |
for i, point in enumerate(route_data):
|
| 2049 |
+
# Uncertainty grows with time and decreases with confidence
|
| 2050 |
+
base_uncertainty = 0.3 + (i / len(route_data)) * 1.2
|
| 2051 |
+
confidence_factor = point.get('confidence', 0.8)
|
| 2052 |
+
uncertainty = base_uncertainty / confidence_factor
|
| 2053 |
+
|
| 2054 |
uncertainty_lats_upper.append(point['lat'] + uncertainty)
|
| 2055 |
uncertainty_lats_lower.append(point['lat'] - uncertainty)
|
| 2056 |
uncertainty_lons_upper.append(point['lon'] + uncertainty)
|
|
|
|
| 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 with larger size
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2076 |
fig.update_layout(
|
| 2077 |
+
title=f"Realistic Storm Development Forecast<br><sub>Starting from {prediction_results['genesis_info']['description']}</sub>",
|
| 2078 |
+
geo=dict(
|
| 2079 |
+
projection_type="natural earth",
|
| 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 |
+
REALISTIC STORM DEVELOPMENT FORECAST
|
| 2102 |
+
{'='*60}
|
| 2103 |
+
|
| 2104 |
+
GENESIS CONDITIONS:
|
| 2105 |
+
• Region: {current.get('genesis_region', 'Unknown')}
|
| 2106 |
+
• Description: {genesis_info['description']}
|
| 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 |
+
• Genesis Category: {current['category']}
|
| 2111 |
+
|
| 2112 |
+
DEVELOPMENT TIMELINE:
|
| 2113 |
+
• Hour 0 (Genesis): {intensities[0]:.0f} kt - {categories[0]}
|
| 2114 |
+
• Hour 24 (Early Dev): {intensities[min(4, len(intensities)-1)]:.0f} kt - {categories[min(4, len(categories)-1)]}
|
| 2115 |
+
• Hour 48 (Development): {intensities[min(8, len(intensities)-1)]:.0f} kt - {categories[min(8, len(categories)-1)]}
|
| 2116 |
+
• Hour 72 (Mature): {intensities[min(12, len(intensities)-1)]:.0f} kt - {categories[min(12, len(categories)-1)]}
|
| 2117 |
+
|
| 2118 |
+
FORECAST TRACK:
|
| 2119 |
+
• Genesis: {lats[0]:.1f}°N, {lons[0]:.1f}°E
|
| 2120 |
+
• 24-hour: {lats[min(4, len(lats)-1)]:.1f}°N, {lons[min(4, len(lons)-1)]:.1f}°E
|
| 2121 |
+
• 48-hour: {lats[min(8, len(lats)-1)]:.1f}°N, {lons[min(8, len(lons)-1)]:.1f}°E
|
| 2122 |
+
• 72-hour: {lats[min(12, len(lats)-1)]:.1f}°N, {lons[min(12, len(lons)-1)]:.1f}°E
|
| 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 animated route visualization: {str(e)}"
|
| 2146 |
+
logging.error(error_msg)
|
| 2147 |
import traceback
|
| 2148 |
traceback.print_exc()
|
| 2149 |
return None, error_msg
|
|
|
|
| 2831 |
"""
|
| 2832 |
gr.Markdown(cluster_info_text)
|
| 2833 |
|
| 2834 |
+
with gr.Tab("🌊 Realistic Storm Genesis & Prediction"):
|
| 2835 |
+
gr.Markdown("## 🌊 Realistic Typhoon Development from Genesis")
|
| 2836 |
|
| 2837 |
if CNN_AVAILABLE:
|
| 2838 |
gr.Markdown("🧠 **Deep Learning models available** - TensorFlow loaded successfully")
|
| 2839 |
+
method_description = "Hybrid CNN-Physics genesis modeling with realistic development cycles"
|
| 2840 |
else:
|
| 2841 |
gr.Markdown("🔬 **Physics-based models available** - Using climatological relationships")
|
| 2842 |
+
method_description = "Advanced physics-based genesis modeling with environmental coupling"
|
|
|
|
| 2843 |
|
| 2844 |
gr.Markdown(f"**Current Method**: {method_description}")
|
| 2845 |
+
gr.Markdown("**🌊 Realistic Genesis**: Select from climatologically accurate development regions")
|
| 2846 |
+
gr.Markdown("**📈 TD Starting Point**: Storms begin at realistic Tropical Depression intensities (25-35 kt)")
|
| 2847 |
+
gr.Markdown("**🎬 Animation Support**: Watch storm development unfold over time")
|
| 2848 |
|
| 2849 |
with gr.Row():
|
| 2850 |
with gr.Column(scale=2):
|
| 2851 |
+
gr.Markdown("### 🌊 Genesis Configuration")
|
| 2852 |
+
genesis_options = list(get_realistic_genesis_locations().keys())
|
| 2853 |
+
genesis_region = gr.Dropdown(
|
| 2854 |
+
choices=genesis_options,
|
| 2855 |
+
value="Western Pacific Main Development Region",
|
| 2856 |
+
label="Typhoon Genesis Region",
|
| 2857 |
+
info="Select realistic development region based on climatology"
|
| 2858 |
+
)
|
| 2859 |
+
|
| 2860 |
+
# Display selected region info
|
| 2861 |
+
def update_genesis_info(region):
|
| 2862 |
+
locations = get_realistic_genesis_locations()
|
| 2863 |
+
if region in locations:
|
| 2864 |
+
info = locations[region]
|
| 2865 |
+
return f"📍 Location: {info['lat']:.1f}°N, {info['lon']:.1f}°E\n📝 {info['description']}"
|
| 2866 |
+
return "Select a genesis region"
|
| 2867 |
+
|
| 2868 |
+
genesis_info_display = gr.Textbox(
|
| 2869 |
+
label="Selected Region Info",
|
| 2870 |
+
lines=2,
|
| 2871 |
+
interactive=False,
|
| 2872 |
+
value=update_genesis_info("Western Pacific Main Development Region")
|
| 2873 |
+
)
|
| 2874 |
+
|
| 2875 |
+
genesis_region.change(
|
| 2876 |
+
fn=update_genesis_info,
|
| 2877 |
+
inputs=[genesis_region],
|
| 2878 |
+
outputs=[genesis_info_display]
|
| 2879 |
+
)
|
| 2880 |
+
|
| 2881 |
with gr.Row():
|
| 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.Slider(
|
| 2886 |
6, 240,
|
| 2887 |
label="Forecast Length (hours)",
|
| 2888 |
value=72,
|
| 2889 |
step=6,
|
| 2890 |
+
info="Extended forecasting up to 10 days"
|
| 2891 |
)
|
| 2892 |
advanced_physics = gr.Checkbox(
|
| 2893 |
+
label="Advanced Physics",
|
| 2894 |
value=True,
|
| 2895 |
+
info="Enhanced environmental modeling"
|
| 2896 |
)
|
| 2897 |
with gr.Row():
|
| 2898 |
show_uncertainty = gr.Checkbox(label="Show Uncertainty Cone", value=True)
|
| 2899 |
+
enable_animation = gr.Checkbox(
|
| 2900 |
+
label="Enable Animation",
|
| 2901 |
+
value=True,
|
| 2902 |
+
info="Animated storm development vs static view"
|
| 2903 |
)
|
| 2904 |
|
| 2905 |
with gr.Column(scale=1):
|
| 2906 |
gr.Markdown("### ⚙️ Prediction Controls")
|
| 2907 |
+
predict_btn = gr.Button("🌊 Generate Realistic Storm Forecast", variant="primary", size="lg")
|
| 2908 |
|
| 2909 |
+
gr.Markdown("### 📊 Genesis Conditions")
|
| 2910 |
+
current_intensity = gr.Number(label="Genesis Intensity (kt)", interactive=False)
|
| 2911 |
+
current_category = gr.Textbox(label="Initial Category", interactive=False)
|
| 2912 |
+
model_confidence = gr.Textbox(label="Model Info", interactive=False)
|
| 2913 |
|
| 2914 |
with gr.Row():
|
| 2915 |
route_plot = gr.Plot(label="🗺️ Advanced Route & Intensity Forecast")
|
|
|
|
| 2917 |
with gr.Row():
|
| 2918 |
forecast_details = gr.Textbox(label="📋 Detailed Forecast Summary", lines=20, max_lines=25)
|
| 2919 |
|
| 2920 |
+
def run_realistic_prediction(region, month, oni, hours, advanced_phys, uncertainty, animation):
|
| 2921 |
try:
|
| 2922 |
+
# Run realistic prediction with genesis region
|
| 2923 |
+
results = predict_storm_route_and_intensity_realistic(
|
| 2924 |
+
region, month, oni,
|
| 2925 |
forecast_hours=hours,
|
| 2926 |
use_advanced_physics=advanced_phys
|
| 2927 |
)
|
| 2928 |
|
| 2929 |
+
# Extract genesis conditions
|
| 2930 |
current = results['current_prediction']
|
| 2931 |
intensity = current['intensity_kt']
|
| 2932 |
category = current['category']
|
| 2933 |
+
genesis_info = results.get('genesis_info', {})
|
| 2934 |
+
|
| 2935 |
+
# Create enhanced visualization
|
| 2936 |
+
fig, forecast_text = create_animated_route_visualization(
|
| 2937 |
+
results, uncertainty, animation
|
| 2938 |
+
)
|
| 2939 |
|
| 2940 |
+
model_info = f"{results['model_info']}\nGenesis: {genesis_info.get('description', 'Unknown')}"
|
|
|
|
| 2941 |
|
| 2942 |
return (
|
| 2943 |
intensity,
|
| 2944 |
category,
|
| 2945 |
+
model_info,
|
| 2946 |
fig,
|
| 2947 |
forecast_text
|
| 2948 |
)
|
| 2949 |
except Exception as e:
|
| 2950 |
+
error_msg = f"Realistic prediction failed: {str(e)}"
|
| 2951 |
logging.error(error_msg)
|
| 2952 |
+
import traceback
|
| 2953 |
+
traceback.print_exc()
|
| 2954 |
return (
|
| 2955 |
+
30, "Tropical Depression", f"Prediction failed: {str(e)}",
|
| 2956 |
+
None, f"Error generating realistic forecast: {str(e)}"
|
| 2957 |
)
|
| 2958 |
|
| 2959 |
predict_btn.click(
|
| 2960 |
+
fn=run_realistic_prediction,
|
| 2961 |
+
inputs=[genesis_region, pred_month, pred_oni, forecast_hours, advanced_physics, show_uncertainty, enable_animation],
|
| 2962 |
outputs=[current_intensity, current_category, model_confidence, route_plot, forecast_details]
|
| 2963 |
)
|
| 2964 |
|
| 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-35 kt intensities
|
| 2969 |
+
- **Animated Development**: Watch storm evolution from genesis through maturity to decay
|
| 2970 |
+
- **Extended Forecasting**: Up to 240 hours (10 days) with realistic development cycles
|
| 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)**: Natural intensification in favorable environment
|
| 2977 |
+
- **Mature (72-120h)**: Peak intensity phase with environmental modulation
|
| 2978 |
+
- **Decay (120h+)**: Realistic weakening due to latitude/land/cooler waters
|
| 2979 |
+
|
| 2980 |
+
### 🌍 Genesis Region Selection:
|
| 2981 |
+
- **Western Pacific MDR**: Peak activity zone near Guam (12.5°N, 145°E)
|
| 2982 |
+
- **South China Sea**: Secondary development region (15°N, 115°E)
|
| 2983 |
+
- **Philippine Sea**: Recurving storm region (18°N, 135°E)
|
| 2984 |
+
- **Marshall Islands**: Eastern development zone (8°N, 165°E)
|
| 2985 |
+
- **Monsoon Trough**: Monsoon-driven genesis (10°N, 130°E)
|
| 2986 |
+
- **Other Basins**: Bay of Bengal, Eastern Pacific, Atlantic options
|
| 2987 |
+
|
| 2988 |
+
### 🔬 Enhanced Physics Model:
|
| 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 |
+
- **Interactive Controls**: Play/pause buttons and time slider
|
| 2999 |
+
- **Stage Tracking**: Visual indicators for development phases
|
| 3000 |
+
- **Confidence Evolution**: See how uncertainty grows with time
|
| 3001 |
+
- **Genesis Highlighting**: Special markers for initial development location
|
| 3002 |
"""
|
| 3003 |
gr.Markdown(prediction_info_text)
|
| 3004 |
|
|
|
|
| 3338 |
demo = create_interface()
|
| 3339 |
|
| 3340 |
if __name__ == "__main__":
|
| 3341 |
+
demo.launch(share=True) # Enable sharing with public link
|