Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import os
|
2 |
import argparse
|
3 |
import logging
|
@@ -55,10 +56,10 @@ try:
|
|
55 |
# Test if TensorFlow actually works
|
56 |
tf.config.set_visible_devices([], 'GPU') # Disable GPU to avoid conflicts
|
57 |
CNN_AVAILABLE = True
|
58 |
-
print("
|
59 |
except Exception as e:
|
60 |
CNN_AVAILABLE = False
|
61 |
-
print(f"
|
62 |
|
63 |
try:
|
64 |
import cdsapi
|
@@ -1016,17 +1017,17 @@ def create_advanced_clustering_visualization(storm_features, typhoon_data, metho
|
|
1016 |
# Flatten column names for readability
|
1017 |
cluster_stats.columns = ['_'.join(col).strip() for col in cluster_stats.columns]
|
1018 |
|
1019 |
-
stats_text = "
|
1020 |
|
1021 |
for cluster in sorted(storm_features_viz['cluster'].unique()):
|
1022 |
if cluster == -1:
|
1023 |
-
stats_text += f"
|
1024 |
continue
|
1025 |
|
1026 |
cluster_row = cluster_stats.loc[cluster]
|
1027 |
storm_count = int(cluster_row['SID_count'])
|
1028 |
|
1029 |
-
stats_text += f"
|
1030 |
stats_text += f" Intensity: {cluster_row['USA_WIND_max_mean']:.1f} Β± {cluster_row['USA_WIND_max_std']:.1f} kt\n"
|
1031 |
stats_text += f" Pressure: {cluster_row['USA_PRES_min_mean']:.1f} Β± {cluster_row['USA_PRES_min_std']:.1f} hPa\n"
|
1032 |
stats_text += f" Track Length: {cluster_row['track_length_mean']:.1f} Β± {cluster_row['track_length_std']:.1f} points\n"
|
@@ -1035,15 +1036,15 @@ def create_advanced_clustering_visualization(storm_features, typhoon_data, metho
|
|
1035 |
stats_text += f" Avg Curvature: {cluster_row['avg_curvature_mean']:.3f} radians\n\n"
|
1036 |
|
1037 |
# Add feature importance summary
|
1038 |
-
stats_text += "
|
1039 |
stats_text += f" β’ Storm intensity (max/mean/std wind & pressure)\n"
|
1040 |
stats_text += f" β’ Track characteristics (length, curvature, distance)\n"
|
1041 |
stats_text += f" β’ Genesis location (lat/lon)\n"
|
1042 |
stats_text += f" β’ Geographic range (lat/lon span)\n"
|
1043 |
stats_text += f" β’ Total features: {len(feature_cols)}\n\n"
|
1044 |
|
1045 |
-
stats_text += f"
|
1046 |
-
stats_text += f"
|
1047 |
|
1048 |
except Exception as stats_error:
|
1049 |
stats_text = f"Error generating cluster statistics: {str(stats_error)}"
|
@@ -1633,11 +1634,11 @@ def generate_enhanced_track_video(year, typhoon_selection, standard):
|
|
1633 |
anim.save(temp_file.name, writer=writer, dpi=100)
|
1634 |
plt.close(fig)
|
1635 |
|
1636 |
-
print(f"
|
1637 |
return temp_file.name
|
1638 |
|
1639 |
except Exception as e:
|
1640 |
-
print(f"
|
1641 |
import traceback
|
1642 |
traceback.print_exc()
|
1643 |
return None
|
@@ -1721,36 +1722,36 @@ def create_interface():
|
|
1721 |
available_years = [str(year) for year in range(2000, 2026)]
|
1722 |
|
1723 |
with gr.Blocks(title="Enhanced Typhoon Analysis Platform", theme=gr.themes.Soft()) as demo:
|
1724 |
-
gr.Markdown("#
|
1725 |
gr.Markdown("Advanced ML clustering, CNN predictions, and comprehensive tropical cyclone analysis including Tropical Depressions")
|
1726 |
|
1727 |
-
with gr.Tab("
|
1728 |
gr.Markdown(f"""
|
1729 |
## Welcome to the Enhanced Typhoon Analysis Dashboard
|
1730 |
|
1731 |
This dashboard provides comprehensive analysis of typhoon data in relation to ENSO phases with advanced machine learning capabilities.
|
1732 |
|
1733 |
-
###
|
1734 |
-
-
|
1735 |
-
-
|
1736 |
-
-
|
1737 |
-
-
|
1738 |
-
-
|
1739 |
|
1740 |
-
###
|
1741 |
- **ONI Data**: {len(oni_data)} years loaded
|
1742 |
- **Typhoon Data**: {total_records} records loaded
|
1743 |
- **Merged Data**: {len(merged_data)} typhoons with ONI values
|
1744 |
- **Available Years**: {year_range_display}
|
1745 |
|
1746 |
-
###
|
1747 |
-
- **UMAP Clustering**: {"
|
1748 |
-
- **AI Predictions**: {"
|
1749 |
-
- **Enhanced Categorization**:
|
1750 |
-
- **Platform Compatibility**:
|
1751 |
""")
|
1752 |
|
1753 |
-
with gr.Tab("
|
1754 |
gr.Markdown("## Storm Pattern Analysis using UMAP/t-SNE with Route Visualization")
|
1755 |
gr.Markdown("**This tab shows both the dimensional clustering analysis AND the actual storm tracks colored by cluster**")
|
1756 |
|
@@ -1766,7 +1767,7 @@ def create_interface():
|
|
1766 |
info="Display actual storm tracks colored by cluster"
|
1767 |
)
|
1768 |
|
1769 |
-
analyze_clusters_btn = gr.Button("
|
1770 |
|
1771 |
with gr.Row():
|
1772 |
cluster_plot = gr.Plot(label="Storm Clustering with Route Visualization")
|
@@ -1792,14 +1793,14 @@ def create_interface():
|
|
1792 |
)
|
1793 |
|
1794 |
gr.Markdown("""
|
1795 |
-
###
|
1796 |
-
-
|
1797 |
-
-
|
1798 |
-
-
|
1799 |
-
-
|
1800 |
-
-
|
1801 |
|
1802 |
-
###
|
1803 |
- **Left Plot**: Each dot is a storm positioned by similarity (close = similar characteristics)
|
1804 |
- **Right Plot**: Actual geographic storm tracks, colored by which cluster they belong to
|
1805 |
- **Cluster Colors**: Each cluster gets a unique color to identify similar storm patterns
|
@@ -1810,11 +1811,11 @@ def create_interface():
|
|
1810 |
gr.Markdown("## AI-Powered Storm Intensity Forecasting")
|
1811 |
|
1812 |
if CNN_AVAILABLE:
|
1813 |
-
gr.Markdown("
|
1814 |
method_description = "Using Convolutional Neural Networks for advanced intensity prediction"
|
1815 |
else:
|
1816 |
-
gr.Markdown("
|
1817 |
-
gr.Markdown("
|
1818 |
method_description = "Using established meteorological relationships and climatology"
|
1819 |
|
1820 |
gr.Markdown(f"**Current Method**: {method_description}")
|
@@ -1825,7 +1826,7 @@ def create_interface():
|
|
1825 |
cnn_month = gr.Slider(1, 12, label="Month", value=9, info="Month of year (1=Jan, 12=Dec)")
|
1826 |
cnn_oni = gr.Number(label="ONI Value", value=0.0, info="Current ENSO index (-3 to 3)")
|
1827 |
|
1828 |
-
predict_btn = gr.Button("
|
1829 |
|
1830 |
with gr.Row():
|
1831 |
intensity_output = gr.Number(label="Predicted Max Wind (kt)")
|
@@ -1944,7 +1945,7 @@ def create_interface():
|
|
1944 |
value='atlantic'
|
1945 |
)
|
1946 |
|
1947 |
-
generate_video_btn = gr.Button("
|
1948 |
video_output = gr.Video(label="Storm Track Animation")
|
1949 |
|
1950 |
# Update storm options when year or basin changes
|
@@ -1963,17 +1964,17 @@ def create_interface():
|
|
1963 |
)
|
1964 |
|
1965 |
gr.Markdown("""
|
1966 |
-
###
|
1967 |
-
-
|
1968 |
-
-
|
1969 |
-
-
|
1970 |
-
-
|
1971 |
-
-
|
1972 |
-
-
|
1973 |
-
-
|
1974 |
""")
|
1975 |
|
1976 |
-
with gr.Tab("
|
1977 |
gr.Markdown("## Comprehensive Dataset Analysis")
|
1978 |
|
1979 |
# Create enhanced data summary
|
@@ -2079,27 +2080,27 @@ def create_interface():
|
|
2079 |
td_percentage = 0
|
2080 |
|
2081 |
gr.Markdown(f"""
|
2082 |
-
###
|
2083 |
- **Total Unique Storms**: {total_storms:,}
|
2084 |
- **Total Track Records**: {total_records:,}
|
2085 |
- **Year Range**: {year_range} ({years_covered} years)
|
2086 |
- **Basins Available**: {basins_available}
|
2087 |
- **Average Storms/Year**: {avg_storms_per_year:.1f}
|
2088 |
|
2089 |
-
###
|
2090 |
- **Tropical Depressions**: {td_storms:,} storms ({td_percentage:.1f}%)
|
2091 |
- **Tropical Storms**: {ts_storms:,} storms
|
2092 |
- **Typhoons (C1-C5)**: {typhoon_storms:,} storms
|
2093 |
|
2094 |
-
###
|
2095 |
-
-
|
2096 |
-
-
|
2097 |
-
-
|
2098 |
-
-
|
2099 |
-
-
|
2100 |
-
-
|
2101 |
|
2102 |
-
###
|
2103 |
- Climate change impact studies
|
2104 |
- Seasonal forecasting research
|
2105 |
- Storm pattern classification
|
@@ -2118,10 +2119,10 @@ def create_interface():
|
|
2118 |
def create_minimal_fallback_interface():
|
2119 |
"""Create a minimal fallback interface when main interface fails"""
|
2120 |
with gr.Blocks() as demo:
|
2121 |
-
gr.Markdown("#
|
2122 |
gr.Markdown("**Notice**: Loading with minimal interface due to data issues.")
|
2123 |
|
2124 |
-
with gr.Tab("
|
2125 |
gr.Markdown("""
|
2126 |
## Platform Status
|
2127 |
|
@@ -2143,7 +2144,7 @@ def create_minimal_fallback_interface():
|
|
2143 |
4. Try restarting the application
|
2144 |
""")
|
2145 |
|
2146 |
-
with gr.Tab("
|
2147 |
gr.Markdown("## Debug Information")
|
2148 |
|
2149 |
def get_debug_info():
|
@@ -2177,7 +2178,7 @@ def create_minimal_fallback_interface():
|
|
2177 |
|
2178 |
def test_color_conversion():
|
2179 |
"""Test color conversion functions"""
|
2180 |
-
print("
|
2181 |
|
2182 |
# Test all categories
|
2183 |
test_winds = [25, 40, 70, 85, 100, 120, 150] # TD, TS, C1, C2, C3, C4, C5
|
@@ -2189,7 +2190,7 @@ def test_color_conversion():
|
|
2189 |
|
2190 |
print(f"Wind: {wind:3d}kt β {category:20s} β Plotly: {plotly_color:15s} β Matplotlib: {matplotlib_color}")
|
2191 |
|
2192 |
-
print("
|
2193 |
|
2194 |
def test_rgb_conversion():
|
2195 |
"""Test RGB string to hex conversion"""
|
@@ -2200,12 +2201,12 @@ def test_rgb_conversion():
|
|
2200 |
'rgb(0, 0, 255)'
|
2201 |
]
|
2202 |
|
2203 |
-
print("
|
2204 |
for rgb_str in test_colors:
|
2205 |
hex_color = rgb_string_to_hex(rgb_str)
|
2206 |
print(f"{rgb_str:20s} β {hex_color}")
|
2207 |
|
2208 |
-
print("
|
2209 |
|
2210 |
# Create and launch the interface
|
2211 |
demo = create_interface()
|
|
|
1 |
+
# -*- coding: utf-8 -*-
|
2 |
import os
|
3 |
import argparse
|
4 |
import logging
|
|
|
56 |
# Test if TensorFlow actually works
|
57 |
tf.config.set_visible_devices([], 'GPU') # Disable GPU to avoid conflicts
|
58 |
CNN_AVAILABLE = True
|
59 |
+
print("TensorFlow successfully loaded - CNN features enabled")
|
60 |
except Exception as e:
|
61 |
CNN_AVAILABLE = False
|
62 |
+
print(f"TensorFlow not available - CNN features disabled: {str(e)[:100]}...")
|
63 |
|
64 |
try:
|
65 |
import cdsapi
|
|
|
1017 |
# Flatten column names for readability
|
1018 |
cluster_stats.columns = ['_'.join(col).strip() for col in cluster_stats.columns]
|
1019 |
|
1020 |
+
stats_text = "ADVANCED CLUSTER ANALYSIS RESULTS\n" + "="*50 + "\n\n"
|
1021 |
|
1022 |
for cluster in sorted(storm_features_viz['cluster'].unique()):
|
1023 |
if cluster == -1:
|
1024 |
+
stats_text += f"NOISE POINTS: {cluster_stats.loc[-1, 'SID_count']} storms\n\n"
|
1025 |
continue
|
1026 |
|
1027 |
cluster_row = cluster_stats.loc[cluster]
|
1028 |
storm_count = int(cluster_row['SID_count'])
|
1029 |
|
1030 |
+
stats_text += f"CLUSTER {cluster}: {storm_count} storms\n"
|
1031 |
stats_text += f" Intensity: {cluster_row['USA_WIND_max_mean']:.1f} Β± {cluster_row['USA_WIND_max_std']:.1f} kt\n"
|
1032 |
stats_text += f" Pressure: {cluster_row['USA_PRES_min_mean']:.1f} Β± {cluster_row['USA_PRES_min_std']:.1f} hPa\n"
|
1033 |
stats_text += f" Track Length: {cluster_row['track_length_mean']:.1f} Β± {cluster_row['track_length_std']:.1f} points\n"
|
|
|
1036 |
stats_text += f" Avg Curvature: {cluster_row['avg_curvature_mean']:.3f} radians\n\n"
|
1037 |
|
1038 |
# Add feature importance summary
|
1039 |
+
stats_text += "CLUSTERING FEATURES USED:\n"
|
1040 |
stats_text += f" β’ Storm intensity (max/mean/std wind & pressure)\n"
|
1041 |
stats_text += f" β’ Track characteristics (length, curvature, distance)\n"
|
1042 |
stats_text += f" β’ Genesis location (lat/lon)\n"
|
1043 |
stats_text += f" β’ Geographic range (lat/lon span)\n"
|
1044 |
stats_text += f" β’ Total features: {len(feature_cols)}\n\n"
|
1045 |
|
1046 |
+
stats_text += f"ALGORITHM: {method.upper()} + DBSCAN clustering\n"
|
1047 |
+
stats_text += f"CLUSTERS FOUND: {len([c for c in storm_features_viz['cluster'].unique() if c != -1])}\n"
|
1048 |
|
1049 |
except Exception as stats_error:
|
1050 |
stats_text = f"Error generating cluster statistics: {str(stats_error)}"
|
|
|
1634 |
anim.save(temp_file.name, writer=writer, dpi=100)
|
1635 |
plt.close(fig)
|
1636 |
|
1637 |
+
print(f"Video generated successfully: {temp_file.name}")
|
1638 |
return temp_file.name
|
1639 |
|
1640 |
except Exception as e:
|
1641 |
+
print(f"Error generating video: {e}")
|
1642 |
import traceback
|
1643 |
traceback.print_exc()
|
1644 |
return None
|
|
|
1722 |
available_years = [str(year) for year in range(2000, 2026)]
|
1723 |
|
1724 |
with gr.Blocks(title="Enhanced Typhoon Analysis Platform", theme=gr.themes.Soft()) as demo:
|
1725 |
+
gr.Markdown("# Enhanced Typhoon Analysis Platform")
|
1726 |
gr.Markdown("Advanced ML clustering, CNN predictions, and comprehensive tropical cyclone analysis including Tropical Depressions")
|
1727 |
|
1728 |
+
with gr.Tab("Overview"):
|
1729 |
gr.Markdown(f"""
|
1730 |
## Welcome to the Enhanced Typhoon Analysis Dashboard
|
1731 |
|
1732 |
This dashboard provides comprehensive analysis of typhoon data in relation to ENSO phases with advanced machine learning capabilities.
|
1733 |
|
1734 |
+
### Enhanced Features:
|
1735 |
+
- **Advanced ML Clustering**: UMAP/t-SNE storm pattern analysis with route visualization
|
1736 |
+
- **Optional CNN Predictions**: Deep learning intensity forecasting
|
1737 |
+
- **Complete TD Support**: Now includes Tropical Depressions (< 34 kt)
|
1738 |
+
- **2025 Data Ready**: Real-time compatibility with current year data
|
1739 |
+
- **Enhanced Animations**: High-quality storm track visualizations
|
1740 |
|
1741 |
+
### Data Status:
|
1742 |
- **ONI Data**: {len(oni_data)} years loaded
|
1743 |
- **Typhoon Data**: {total_records} records loaded
|
1744 |
- **Merged Data**: {len(merged_data)} typhoons with ONI values
|
1745 |
- **Available Years**: {year_range_display}
|
1746 |
|
1747 |
+
### Technical Capabilities:
|
1748 |
+
- **UMAP Clustering**: {"Available" if UMAP_AVAILABLE else "Limited to t-SNE/PCA"}
|
1749 |
+
- **AI Predictions**: {"Deep Learning" if CNN_AVAILABLE else "Physics-based"}
|
1750 |
+
- **Enhanced Categorization**: Tropical Depression to Super Typhoon
|
1751 |
+
- **Platform Compatibility**: Optimized for Hugging Face Spaces
|
1752 |
""")
|
1753 |
|
1754 |
+
with gr.Tab("Advanced ML Clustering with Routes"):
|
1755 |
gr.Markdown("## Storm Pattern Analysis using UMAP/t-SNE with Route Visualization")
|
1756 |
gr.Markdown("**This tab shows both the dimensional clustering analysis AND the actual storm tracks colored by cluster**")
|
1757 |
|
|
|
1767 |
info="Display actual storm tracks colored by cluster"
|
1768 |
)
|
1769 |
|
1770 |
+
analyze_clusters_btn = gr.Button("Analyze Storm Clusters & Routes", variant="primary")
|
1771 |
|
1772 |
with gr.Row():
|
1773 |
cluster_plot = gr.Plot(label="Storm Clustering with Route Visualization")
|
|
|
1793 |
)
|
1794 |
|
1795 |
gr.Markdown("""
|
1796 |
+
### Advanced Clustering Features:
|
1797 |
+
- **Multi-dimensional Analysis**: Uses 15+ storm characteristics including intensity, track shape, genesis location
|
1798 |
+
- **Route Visualization**: Shows actual storm tracks colored by cluster membership
|
1799 |
+
- **DBSCAN Clustering**: Automatically finds natural groupings without predefined cluster count
|
1800 |
+
- **Comprehensive Stats**: Detailed cluster analysis including intensity, pressure, track length, curvature
|
1801 |
+
- **Interactive**: Hover over points to see storm details, zoom and pan the route map
|
1802 |
|
1803 |
+
### How to Interpret:
|
1804 |
- **Left Plot**: Each dot is a storm positioned by similarity (close = similar characteristics)
|
1805 |
- **Right Plot**: Actual geographic storm tracks, colored by which cluster they belong to
|
1806 |
- **Cluster Colors**: Each cluster gets a unique color to identify similar storm patterns
|
|
|
1811 |
gr.Markdown("## AI-Powered Storm Intensity Forecasting")
|
1812 |
|
1813 |
if CNN_AVAILABLE:
|
1814 |
+
gr.Markdown("**Deep Learning models available** - TensorFlow loaded successfully")
|
1815 |
method_description = "Using Convolutional Neural Networks for advanced intensity prediction"
|
1816 |
else:
|
1817 |
+
gr.Markdown("**Physics-based models available** - Using climatological relationships")
|
1818 |
+
gr.Markdown("*Install TensorFlow for deep learning features: pip install tensorflow-cpu*")
|
1819 |
method_description = "Using established meteorological relationships and climatology"
|
1820 |
|
1821 |
gr.Markdown(f"**Current Method**: {method_description}")
|
|
|
1826 |
cnn_month = gr.Slider(1, 12, label="Month", value=9, info="Month of year (1=Jan, 12=Dec)")
|
1827 |
cnn_oni = gr.Number(label="ONI Value", value=0.0, info="Current ENSO index (-3 to 3)")
|
1828 |
|
1829 |
+
predict_btn = gr.Button("Predict Storm Intensity", variant="primary")
|
1830 |
|
1831 |
with gr.Row():
|
1832 |
intensity_output = gr.Number(label="Predicted Max Wind (kt)")
|
|
|
1945 |
value='atlantic'
|
1946 |
)
|
1947 |
|
1948 |
+
generate_video_btn = gr.Button("Generate Enhanced Animation", variant="primary")
|
1949 |
video_output = gr.Video(label="Storm Track Animation")
|
1950 |
|
1951 |
# Update storm options when year or basin changes
|
|
|
1964 |
)
|
1965 |
|
1966 |
gr.Markdown("""
|
1967 |
+
### Enhanced Animation Features:
|
1968 |
+
- **Full TD Support**: Now displays Tropical Depressions (< 34 kt) in gray
|
1969 |
+
- **2025 Compatibility**: Complete support for current year data
|
1970 |
+
- **Enhanced Maps**: Better cartographic projections with terrain features
|
1971 |
+
- **Smart Scaling**: Storm symbols scale dynamically with intensity
|
1972 |
+
- **Real-time Info**: Live position, time, and meteorological data display
|
1973 |
+
- **Professional Styling**: Publication-quality animations with proper legends
|
1974 |
+
- **Optimized Export**: Fast rendering with web-compatible video formats
|
1975 |
""")
|
1976 |
|
1977 |
+
with gr.Tab("Data Statistics & Insights"):
|
1978 |
gr.Markdown("## Comprehensive Dataset Analysis")
|
1979 |
|
1980 |
# Create enhanced data summary
|
|
|
2080 |
td_percentage = 0
|
2081 |
|
2082 |
gr.Markdown(f"""
|
2083 |
+
### Enhanced Dataset Summary:
|
2084 |
- **Total Unique Storms**: {total_storms:,}
|
2085 |
- **Total Track Records**: {total_records:,}
|
2086 |
- **Year Range**: {year_range} ({years_covered} years)
|
2087 |
- **Basins Available**: {basins_available}
|
2088 |
- **Average Storms/Year**: {avg_storms_per_year:.1f}
|
2089 |
|
2090 |
+
### Storm Category Breakdown:
|
2091 |
- **Tropical Depressions**: {td_storms:,} storms ({td_percentage:.1f}%)
|
2092 |
- **Tropical Storms**: {ts_storms:,} storms
|
2093 |
- **Typhoons (C1-C5)**: {typhoon_storms:,} storms
|
2094 |
|
2095 |
+
### New Platform Capabilities:
|
2096 |
+
- **Complete TD Analysis** - First platform to include comprehensive TD tracking
|
2097 |
+
- **Advanced ML Clustering** - DBSCAN pattern recognition with route visualization
|
2098 |
+
- **Real-time Predictions** - Physics-based and optional CNN intensity forecasting
|
2099 |
+
- **2025 Data Ready** - Full compatibility with current season data
|
2100 |
+
- **Enhanced Animations** - Professional-quality storm track videos
|
2101 |
+
- **Multi-basin Analysis** - Comprehensive Pacific and Atlantic coverage
|
2102 |
|
2103 |
+
### Research Applications:
|
2104 |
- Climate change impact studies
|
2105 |
- Seasonal forecasting research
|
2106 |
- Storm pattern classification
|
|
|
2119 |
def create_minimal_fallback_interface():
|
2120 |
"""Create a minimal fallback interface when main interface fails"""
|
2121 |
with gr.Blocks() as demo:
|
2122 |
+
gr.Markdown("# Enhanced Typhoon Analysis Platform")
|
2123 |
gr.Markdown("**Notice**: Loading with minimal interface due to data issues.")
|
2124 |
|
2125 |
+
with gr.Tab("Status"):
|
2126 |
gr.Markdown("""
|
2127 |
## Platform Status
|
2128 |
|
|
|
2144 |
4. Try restarting the application
|
2145 |
""")
|
2146 |
|
2147 |
+
with gr.Tab("Debug"):
|
2148 |
gr.Markdown("## Debug Information")
|
2149 |
|
2150 |
def get_debug_info():
|
|
|
2178 |
|
2179 |
def test_color_conversion():
|
2180 |
"""Test color conversion functions"""
|
2181 |
+
print("Testing color conversion...")
|
2182 |
|
2183 |
# Test all categories
|
2184 |
test_winds = [25, 40, 70, 85, 100, 120, 150] # TD, TS, C1, C2, C3, C4, C5
|
|
|
2190 |
|
2191 |
print(f"Wind: {wind:3d}kt β {category:20s} β Plotly: {plotly_color:15s} β Matplotlib: {matplotlib_color}")
|
2192 |
|
2193 |
+
print("Color conversion test complete!")
|
2194 |
|
2195 |
def test_rgb_conversion():
|
2196 |
"""Test RGB string to hex conversion"""
|
|
|
2201 |
'rgb(0, 0, 255)'
|
2202 |
]
|
2203 |
|
2204 |
+
print("Testing RGB to hex conversion...")
|
2205 |
for rgb_str in test_colors:
|
2206 |
hex_color = rgb_string_to_hex(rgb_str)
|
2207 |
print(f"{rgb_str:20s} β {hex_color}")
|
2208 |
|
2209 |
+
print("RGB conversion test complete!")
|
2210 |
|
2211 |
# Create and launch the interface
|
2212 |
demo = create_interface()
|