import gradio as gr
import json
import os
from datetime import datetime
from wave_data_puller import WaveDataPuller
from grib_wave_puller import GRIBWavePuller
import logging
import folium
import numpy as np
from folium import plugins
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def create_global_wave_map(global_data):
"""Create a Folium map with global GRIB wave data visualization"""
try:
if not global_data or 'sample_points' not in global_data:
return None
# Create base map centered on world
m = folium.Map(
location=[0, 0],
zoom_start=2,
tiles='OpenStreetMap'
)
sample_points = global_data['sample_points']
if not sample_points:
return None
# Color based on wave height (blue to red scale)
def get_color(height):
if height < 1.0:
return 'blue'
elif height < 2.0:
return 'green'
elif height < 3.0:
return 'orange'
elif height < 4.0:
return 'red'
else:
return 'darkred'
# Add sample points to map
for point in sample_points:
lat = point['lat']
lon = point['lon']
wave_height = point['wave_height']
popup_text = f"""
Global Wave Data
Lat: {lat}°, Lon: {lon}°
Wave Height: {wave_height} m
Source: {global_data.get('data_source', 'N/A')}
"""
folium.CircleMarker(
location=[lat, lon],
radius=max(3, wave_height * 2), # Scale radius with wave height
popup=popup_text,
color=get_color(wave_height),
fill=True,
fillColor=get_color(wave_height),
fillOpacity=0.7,
weight=2
).add_to(m)
# Add statistics popup with forecast info
stats = global_data.get('wave_statistics', {})
forecast_info = global_data.get('forecast_info', {})
grid_info = global_data.get('grid_info', {})
params_found = global_data.get('parameters_found', {})
# Handle regional coverage information
regions_info = ""
if 'regions_included' in grid_info:
regions = grid_info.get('regions_included', [])
regions_info = f"Regional Coverage: {', '.join(regions)}
"
stats_text = f"""
Global Wave Statistics
Max Wave Height: {stats.get('max_wave_height', 'N/A')} m
Mean Wave Height: {stats.get('mean_wave_height', 'N/A'):.2f} m
Data Points: {len(sample_points)}
{regions_info}
Data Coverage:
Lat: {grid_info.get('lat_min', 'N/A'):.1f}° to {grid_info.get('lat_max', 'N/A'):.1f}°
Lon: {grid_info.get('lon_min', 'N/A'):.1f}° to {grid_info.get('lon_max', 'N/A'):.1f}°
Parameters:
Height: {params_found.get('wave_height', 'N/A')}
Direction: {params_found.get('wave_direction', 'N/A')}
Period: {params_found.get('wave_period', 'N/A')}
Vectors: {'Yes' if params_found.get('has_velocity_components') else 'No'}
Forecast Info:
Forecast Hour: +{forecast_info.get('forecast_hour', 0)}h
Model Run: {forecast_info.get('model_run', 'N/A')}
Valid Time: {forecast_info.get('forecast_valid_time', 'N/A')[:16]}
Current: {'Yes' if forecast_info.get('is_current') else 'No'}
Source: {global_data.get('data_source', 'N/A')}
"""
folium.Marker(
location=[60, -120], # Top left corner
popup=stats_text,
icon=folium.Icon(color='black', icon='info-sign')
).add_to(m)
# Add legend
legend_html = '''
Unable to generate global map
" return formatted_data, "✅ Global wave data fetched successfully!", map_html else: error_msg = """ ❌ No real government wave data available at this time. Possible reasons: • NOAA GRIB servers may be temporarily unavailable • ECMWF free data access limited • Data files not yet published for requested forecast time • Server maintenance in progress Try again later or check NOAA/ECMWF status pages for updates. Only real government/institutional data is supported - no mock data.""" no_data_html = """Government wave data servers (NOAA/ECMWF) are currently unavailable.
Please try again later when data servers are accessible.
Only real government data is supported.
Error generating global map
" def fetch_wave_data(): """Fetch single location wave data and return formatted results with map""" try: puller = WaveDataPuller() data = puller.fetch_wave_data() if data: # Format the data for display formatted_data = json.dumps(data, indent=2) # Save to file puller.save_data(data) # Create map wave_map = create_wave_map(data) map_html = wave_map._repr_html_() if wave_map else "Unable to generate map
" return formatted_data, "✅ Data fetched successfully!", map_html else: return "No data available", "❌ Failed to fetch data", "No map data available
" except Exception as e: logger.error(f"Error in fetch_wave_data: {e}") return f"Error: {str(e)}", "❌ Error occurred", "Error generating map
" def get_recent_files(): """Get list of recent wave data files""" try: data_dir = "/tmp/wave_data" if not os.path.exists(data_dir): return "No data files found" files = [f for f in os.listdir(data_dir) if f.endswith('.json')] files.sort(reverse=True) # Most recent first if not files: return "No data files found" file_list = [] for file in files[:10]: # Show last 10 files filepath = os.path.join(data_dir, file) try: with open(filepath, 'r') as f: data = json.load(f) timestamp = data.get('timestamp', 'Unknown') file_list.append(f"📄 {file} (Generated: {timestamp})") except: file_list.append(f"📄 {file}") return "\n".join(file_list) except Exception as e: return f"Error reading files: {str(e)}" def view_file_content(filename): """View content of a specific data file with map""" try: if not filename: return "Please enter a filename", "No file selected
" # Clean filename (remove emoji and extra text) clean_filename = filename.split()[1] if " " in filename else filename filepath = os.path.join("/tmp/wave_data", clean_filename) if not os.path.exists(filepath): return f"File {clean_filename} not found", "File not found
" with open(filepath, 'r') as f: data = json.load(f) # Create map for this data wave_map = create_wave_map(data) map_html = wave_map._repr_html_() if wave_map else "Unable to generate map
" return json.dumps(data, indent=2), map_html except Exception as e: return f"Error reading file: {str(e)}", "Error generating map
" # Create Gradio interface with gr.Blocks(title="NWPS SWAN Wave Data Puller") as demo: gr.Markdown(""" # 🌊 Global Wave Data Puller This application fetches wave data from multiple sources: - **Global GRIB Data**: Worldwide wave heights from ECMWF/NOAA GRIB files - **Single Location**: NWPS SWAN model data for specific locations """) with gr.Tab("Global GRIB Data"): gr.Markdown("### Fetch Global Wave GRIB Data") gr.Markdown("Downloads global wave height data from ECMWF/NOAA GRIB files covering the entire world.") global_fetch_button = gr.Button("🌍 Fetch Global Wave Data", variant="primary") global_status_output = gr.Textbox(label="Status", interactive=False) with gr.Row(): with gr.Column(): global_data_output = gr.Textbox(label="Global Wave Data (JSON)", lines=20, interactive=False) with gr.Column(): global_map_output = gr.HTML(label="Global Wave Map") global_fetch_button.click( fn=fetch_global_wave_data, outputs=[global_data_output, global_status_output, global_map_output] ) with gr.Tab("Single Location Data"): gr.Markdown("### Fetch Single Location Wave Data") gr.Markdown("Fetches wave data for a specific location (original functionality).") fetch_button = gr.Button("🌊 Fetch Location Wave Data", variant="primary") status_output = gr.Textbox(label="Status", interactive=False) with gr.Row(): with gr.Column(): data_output = gr.Textbox(label="Wave Data (JSON)", lines=20, interactive=False) with gr.Column(): map_output = gr.HTML(label="Wave Data Map") fetch_button.click( fn=fetch_wave_data, outputs=[data_output, status_output, map_output] ) with gr.Tab("Data Files"): gr.Markdown("### Recent Data Files") refresh_button = gr.Button("🔄 Refresh File List") files_output = gr.Textbox(label="Recent Files", lines=10, interactive=False) gr.Markdown("### View File Content") filename_input = gr.Textbox(label="Filename (copy from list above)", placeholder="wave_data_20250826_203857.json") view_button = gr.Button("👁️ View File") with gr.Row(): with gr.Column(): content_output = gr.Textbox(label="File Content", lines=15, interactive=False) with gr.Column(): file_map_output = gr.HTML(label="File Data Map") refresh_button.click( fn=get_recent_files, outputs=files_output ) view_button.click( fn=view_file_content, inputs=filename_input, outputs=[content_output, file_map_output] ) # Removed auto-load on startup to prevent hanging during initialization # demo.load(fn=get_recent_files, outputs=files_output) if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=7860)