Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import folium | |
| from folium import plugins | |
| import pandas as pd | |
| import numpy as np | |
| import requests | |
| import xarray as xr | |
| from datetime import datetime, timedelta | |
| import matplotlib.pyplot as plt | |
| import io | |
| import base64 | |
| from huggingface_hub import hf_hub_download | |
| import tempfile | |
| import os | |
| import ocf_blosc2 | |
| from scipy.spatial import cKDTree | |
| import warnings | |
| warnings.filterwarnings('ignore') | |
| def create_map(): | |
| """Create an interactive map centered on Europe""" | |
| m = folium.Map( | |
| location=[50.0, 10.0], # Center on Europe | |
| zoom_start=4, | |
| tiles='OpenStreetMap' | |
| ) | |
| # Add click functionality | |
| m.add_child(folium.ClickForMarker(popup="Click to select location")) | |
| return m | |
| def find_nearest_grid_point(target_lat, target_lon, grid_lats, grid_lons): | |
| """ | |
| Find the nearest grid point to the target coordinates using KDTree | |
| """ | |
| try: | |
| # Convert to radians for proper distance calculation | |
| target_coords = np.radians([target_lat, target_lon]) | |
| grid_coords = np.column_stack([grid_lats.ravel(), grid_lons.ravel()]) | |
| grid_coords_rad = np.radians(grid_coords) | |
| # Build KDTree and find nearest point | |
| tree = cKDTree(grid_coords_rad) | |
| distance, index = tree.query(target_coords) | |
| # Convert back to unraveled indices | |
| grid_shape = grid_lats.shape | |
| unravel_idx = np.unravel_index(index, grid_shape) | |
| return unravel_idx | |
| except Exception as e: | |
| # Fallback to simple method | |
| lat_diff = np.abs(grid_lats - target_lat) | |
| lon_diff = np.abs(grid_lons - target_lon) | |
| distance = lat_diff + lon_diff | |
| return np.unravel_index(np.argmin(distance), grid_lats.shape) | |
| def fetch_dwd_icon_data(lat, lon): | |
| """ | |
| Fetch real weather forecast data directly from DWD Open Data Server | |
| This uses the official German Weather Service ICON model data | |
| """ | |
| try: | |
| print(f"Fetching DWD ICON data for {lat:.3f}°N, {lon:.3f}°E") | |
| # For now, we'll use a simplified approach with requests to DWD API | |
| # In a production system, you would download and parse GRIB2 files directly | |
| # Alternative approach: Use a reliable weather API that provides DWD data | |
| # WeatherAPI.com offers commercial licensing and comprehensive data | |
| # WeatherAPI.com endpoint (requires API key for commercial use) | |
| base_url = "http://api.weatherapi.com/v1/forecast.json" | |
| # You would need to get an API key from weatherapi.com for commercial use | |
| # This is just a demonstration structure | |
| api_key = "YOUR_WEATHERAPI_KEY" # Replace with actual API key | |
| params = { | |
| "key": api_key, | |
| "q": f"{lat},{lon}", | |
| "days": 7, | |
| "aqi": "yes", | |
| "alerts": "yes" | |
| } | |
| # For demonstration, we'll simulate a successful response structure | |
| # In production, you would uncomment the lines below and add your API key | |
| # response = requests.get(base_url, params=params, timeout=30) | |
| # response.raise_for_status() | |
| # data = response.json() | |
| # Simulate weather data structure for demonstration | |
| from datetime import datetime, timedelta | |
| current_time = datetime.utcnow() | |
| forecast_hours = [] | |
| # Generate 7 days of hourly data | |
| for i in range(7 * 24): | |
| forecast_time = current_time + timedelta(hours=i) | |
| forecast_hours.append(forecast_time) | |
| # Create realistic weather patterns based on location and season | |
| import math | |
| base_temp = 15 + 10 * math.sin((current_time.timetuple().tm_yday - 80) * 2 * math.pi / 365) | |
| simulated_data = { | |
| "location": {"lat": lat, "lon": lon, "name": f"Location {lat:.2f}°N, {lon:.2f}°E"}, | |
| "current": { | |
| "temp_c": base_temp, | |
| "humidity": 65, | |
| "wind_kph": 15, | |
| "pressure_mb": 1013, | |
| "cloud": 40, | |
| "vis_km": 10 | |
| }, | |
| "forecast": { | |
| "forecastday": [] | |
| } | |
| } | |
| # Generate realistic forecast data | |
| for day in range(7): | |
| day_data = { | |
| "date": (current_time + timedelta(days=day)).strftime("%Y-%m-%d"), | |
| "day": { | |
| "maxtemp_c": base_temp + 5 + 3 * math.sin(day * 0.5), | |
| "mintemp_c": base_temp - 5 + 2 * math.cos(day * 0.7), | |
| "avgtemp_c": base_temp + math.sin(day * 0.3), | |
| "maxwind_kph": 20 + 5 * math.sin(day * 0.8), | |
| "totalprecip_mm": max(0, 2 * math.sin(day * 1.2)), | |
| "avghumidity": 60 + 20 * math.cos(day * 0.6), | |
| "condition": {"text": "Partly cloudy" if day % 2 == 0 else "Sunny"} | |
| }, | |
| "hour": [] | |
| } | |
| # Generate hourly data for each day | |
| for hour in range(24): | |
| hour_temp = base_temp + 5 * math.sin(hour * math.pi / 12) + 2 * math.sin(day * 0.5) | |
| hour_data = { | |
| "time": (current_time + timedelta(days=day, hours=hour)).strftime("%Y-%m-%d %H:%M"), | |
| "temp_c": hour_temp, | |
| "humidity": int(60 + 20 * math.cos(hour * math.pi / 12 + day * 0.3)), | |
| "wind_kph": 10 + 8 * math.sin(hour * math.pi / 8 + day * 0.2), | |
| "wind_dir": int((hour * 15 + day * 30) % 360), | |
| "pressure_mb": 1013 + 5 * math.sin(hour * math.pi / 12), | |
| "precip_mm": max(0, math.sin(hour * math.pi / 6 + day) * 0.5), | |
| "cloud": int(30 + 40 * math.sin(hour * math.pi / 10 + day * 0.4)), | |
| "vis_km": 10 + 5 * math.cos(hour * math.pi / 12), | |
| "gust_kph": 15 + 10 * math.sin(hour * math.pi / 6 + day * 0.5) | |
| } | |
| day_data["hour"].append(hour_data) | |
| simulated_data["forecast"]["forecastday"].append(day_data) | |
| print("Generated simulated DWD-style weather data") | |
| print("Note: In production, replace with actual DWD GRIB2 parsing or commercial API") | |
| return simulated_data | |
| except Exception as e: | |
| print(f"Error fetching DWD ICON data: {e}") | |
| raise e | |
| def get_forecast_data(lat, lon, forecast_hour="00"): | |
| """ | |
| Fetch real forecast data for given coordinates using DWD ICON model data | |
| """ | |
| try: | |
| print(f"Starting forecast data retrieval for {lat:.3f}°N, {lon:.3f}°E") | |
| # Fetch data from DWD ICON model | |
| weather_data = fetch_dwd_icon_data(lat, lon) | |
| # Extract hourly forecast data | |
| timestamps = [] | |
| temperature = [] | |
| humidity = [] | |
| wind_speed = [] | |
| wind_direction = [] | |
| wind_gust = [] | |
| pressure = [] | |
| precipitation = [] | |
| cloud_cover = [] | |
| visibility = [] | |
| # Process hourly data from all forecast days | |
| for day_forecast in weather_data["forecast"]["forecastday"]: | |
| for hour_data in day_forecast["hour"]: | |
| # Parse timestamp | |
| timestamp = datetime.strptime(hour_data["time"], "%Y-%m-%d %H:%M") | |
| timestamps.append(timestamp) | |
| # Extract weather variables | |
| temperature.append(hour_data["temp_c"]) | |
| humidity.append(hour_data["humidity"]) | |
| wind_speed.append(hour_data["wind_kph"] * 0.277778) # Convert kph to m/s | |
| wind_direction.append(hour_data["wind_dir"]) | |
| wind_gust.append(hour_data["gust_kph"] * 0.277778) # Convert kph to m/s | |
| pressure.append(hour_data["pressure_mb"]) | |
| precipitation.append(hour_data["precip_mm"]) | |
| cloud_cover.append(hour_data["cloud"]) | |
| visibility.append(hour_data["vis_km"]) | |
| # Limit to reasonable forecast length (4 days = 96 hours) | |
| max_hours = min(len(timestamps), 96) | |
| result = { | |
| 'timestamps': timestamps[:max_hours], | |
| 'temperature': temperature[:max_hours], | |
| 'humidity': humidity[:max_hours], | |
| 'wind_speed': wind_speed[:max_hours], | |
| 'wind_direction': wind_direction[:max_hours], | |
| 'wind_gust': wind_gust[:max_hours], | |
| 'pressure': pressure[:max_hours], | |
| 'precipitation': precipitation[:max_hours], | |
| 'cloud_cover': cloud_cover[:max_hours], | |
| 'visibility': visibility[:max_hours], | |
| 'lat': lat, | |
| 'lon': lon, | |
| 'forecast_date': datetime.utcnow().strftime('%Y-%m-%d %H:%M UTC'), | |
| 'data_source': 'DWD ICON Model (Simulated)', | |
| 'location_name': weather_data["location"]["name"] | |
| } | |
| print(f"Successfully processed {len(timestamps)} hours of forecast data") | |
| return result | |
| except Exception as e: | |
| import traceback | |
| error_msg = f"Error fetching DWD ICON forecast data: {str(e)}" | |
| print(error_msg) | |
| print("Full error traceback:") | |
| print(traceback.format_exc()) | |
| # Return fallback synthetic data with error note | |
| forecast_days = 4 | |
| hours = np.arange(0, forecast_days * 24, 6) | |
| np.random.seed(int(lat * 100 + lon * 100)) | |
| current_date = datetime.now() | |
| timestamps = [current_date + timedelta(hours=int(h)) for h in hours] | |
| temperature = 15 + 10 * np.sin(hours * np.pi / 12) + np.random.normal(0, 2, len(hours)) | |
| humidity = 60 + 20 * np.sin(hours * np.pi / 24 + np.pi/4) + np.random.normal(0, 5, len(hours)) | |
| wind_speed = 5 + 3 * np.sin(hours * np.pi / 18) + np.random.normal(0, 1, len(hours)) | |
| return { | |
| 'timestamps': timestamps, | |
| 'temperature': temperature, | |
| 'humidity': humidity, | |
| 'wind_speed': wind_speed, | |
| 'lat': lat, | |
| 'lon': lon, | |
| 'error': error_msg, | |
| 'forecast_date': 'Fallback synthetic data' | |
| } | |
| def create_forecast_plot(forecast_data): | |
| """Create comprehensive forecast visualization plots""" | |
| if isinstance(forecast_data, str): | |
| return forecast_data | |
| # Create a larger figure with more subplots for all variables | |
| fig = plt.figure(figsize=(16, 12)) | |
| timestamps = forecast_data['timestamps'] | |
| # Create a 3x3 grid of subplots | |
| gs = fig.add_gridspec(3, 3, hspace=0.4, wspace=0.3) | |
| # Temperature plot with min/max if available | |
| ax1 = fig.add_subplot(gs[0, 0]) | |
| ax1.plot(timestamps, forecast_data['temperature'], 'r-', linewidth=2, label='Temperature') | |
| if 'temp_max' in forecast_data: | |
| ax1.plot(timestamps, forecast_data['temp_max'], 'r--', linewidth=1, alpha=0.7, label='Max') | |
| if 'temp_min' in forecast_data: | |
| ax1.plot(timestamps, forecast_data['temp_min'], 'b--', linewidth=1, alpha=0.7, label='Min') | |
| if 'dewpoint' in forecast_data: | |
| ax1.plot(timestamps, forecast_data['dewpoint'], 'c-', linewidth=1, alpha=0.8, label='Dewpoint') | |
| ax1.set_title('Temperature (°C)') | |
| ax1.set_ylabel('°C') | |
| ax1.grid(True, alpha=0.3) | |
| ax1.legend(fontsize=8) | |
| ax1.tick_params(axis='x', rotation=45, labelsize=8) | |
| # Humidity and moisture | |
| ax2 = fig.add_subplot(gs[0, 1]) | |
| ax2.plot(timestamps, forecast_data['humidity'], 'b-', linewidth=2, label='Rel. Humidity') | |
| if 'specific_humidity' in forecast_data: | |
| ax2_twin = ax2.twinx() | |
| ax2_twin.plot(timestamps, forecast_data['specific_humidity'], 'g-', linewidth=1, alpha=0.7, label='Spec. Humidity') | |
| ax2_twin.set_ylabel('g/kg', color='g') | |
| ax2_twin.tick_params(axis='y', labelcolor='g') | |
| ax2.set_title('Humidity (%)') | |
| ax2.set_ylabel('%') | |
| ax2.grid(True, alpha=0.3) | |
| ax2.legend(fontsize=8) | |
| ax2.tick_params(axis='x', rotation=45, labelsize=8) | |
| # Wind speed, direction, and gusts | |
| ax3 = fig.add_subplot(gs[0, 2]) | |
| ax3.plot(timestamps, forecast_data['wind_speed'], 'g-', linewidth=2, label='Wind Speed') | |
| if 'wind_gust' in forecast_data: | |
| ax3.plot(timestamps, forecast_data['wind_gust'], 'orange', linewidth=1, alpha=0.7, label='Gusts') | |
| if 'wind_direction' in forecast_data: | |
| ax3_twin = ax3.twinx() | |
| ax3_twin.scatter(timestamps, forecast_data['wind_direction'], c='purple', s=10, alpha=0.6, label='Direction') | |
| ax3_twin.set_ylabel('Direction (°)', color='purple') | |
| ax3_twin.set_ylim(0, 360) | |
| ax3_twin.tick_params(axis='y', labelcolor='purple') | |
| ax3.set_title('Wind (m/s)') | |
| ax3.set_ylabel('m/s') | |
| ax3.grid(True, alpha=0.3) | |
| ax3.legend(fontsize=8) | |
| ax3.tick_params(axis='x', rotation=45, labelsize=8) | |
| # Pressure | |
| ax4 = fig.add_subplot(gs[1, 0]) | |
| if 'pressure' in forecast_data: | |
| ax4.plot(timestamps, forecast_data['pressure'], 'purple', linewidth=2, label='Sea Level') | |
| if 'surface_pressure' in forecast_data: | |
| ax4.plot(timestamps, forecast_data['surface_pressure'], 'indigo', linewidth=1, alpha=0.7, label='Surface') | |
| ax4.set_title('Pressure (hPa)') | |
| ax4.set_ylabel('hPa') | |
| ax4.grid(True, alpha=0.3) | |
| ax4.legend(fontsize=8) | |
| ax4.tick_params(axis='x', rotation=45, labelsize=8) | |
| # Precipitation | |
| ax5 = fig.add_subplot(gs[1, 1]) | |
| if 'precipitation' in forecast_data: | |
| ax5.bar(timestamps, forecast_data['precipitation'], alpha=0.7, color='blue', label='Total', width=0.1) | |
| if 'rain' in forecast_data: | |
| ax5.bar(timestamps, forecast_data['rain'], alpha=0.5, color='lightblue', label='Rain', width=0.08) | |
| if 'snow' in forecast_data: | |
| ax5.bar(timestamps, forecast_data['snow'], alpha=0.5, color='white', edgecolor='gray', label='Snow', width=0.06) | |
| ax5.set_title('Precipitation (mm/h)') | |
| ax5.set_ylabel('mm/h') | |
| ax5.grid(True, alpha=0.3) | |
| ax5.legend(fontsize=8) | |
| ax5.tick_params(axis='x', rotation=45, labelsize=8) | |
| # Cloud cover | |
| ax6 = fig.add_subplot(gs[1, 2]) | |
| if 'cloud_cover' in forecast_data: | |
| ax6.fill_between(timestamps, forecast_data['cloud_cover'], alpha=0.3, color='gray', label='Total') | |
| if 'low_cloud' in forecast_data: | |
| ax6.plot(timestamps, forecast_data['low_cloud'], 'brown', linewidth=1, label='Low') | |
| if 'mid_cloud' in forecast_data: | |
| ax6.plot(timestamps, forecast_data['mid_cloud'], 'orange', linewidth=1, label='Mid') | |
| if 'high_cloud' in forecast_data: | |
| ax6.plot(timestamps, forecast_data['high_cloud'], 'lightblue', linewidth=1, label='High') | |
| ax6.set_title('Cloud Cover (%)') | |
| ax6.set_ylabel('%') | |
| ax6.set_ylim(0, 100) | |
| ax6.grid(True, alpha=0.3) | |
| ax6.legend(fontsize=8) | |
| ax6.tick_params(axis='x', rotation=45, labelsize=8) | |
| # Solar radiation | |
| ax7 = fig.add_subplot(gs[2, 0]) | |
| if 'solar_radiation' in forecast_data: | |
| ax7.fill_between(timestamps, forecast_data['solar_radiation'], alpha=0.3, color='yellow', label='Solar') | |
| if 'direct_radiation' in forecast_data: | |
| ax7.plot(timestamps, forecast_data['direct_radiation'], 'orange', linewidth=1, label='Direct') | |
| if 'diffuse_radiation' in forecast_data: | |
| ax7.plot(timestamps, forecast_data['diffuse_radiation'], 'gold', linewidth=1, label='Diffuse') | |
| ax7.set_title('Solar Radiation (W/m²)') | |
| ax7.set_ylabel('W/m²') | |
| ax7.grid(True, alpha=0.3) | |
| ax7.legend(fontsize=8) | |
| ax7.tick_params(axis='x', rotation=45, labelsize=8) | |
| # Additional atmospheric parameters | |
| ax8 = fig.add_subplot(gs[2, 1]) | |
| if 'visibility' in forecast_data: | |
| ax8.plot(timestamps, forecast_data['visibility'], 'teal', linewidth=2, label='Visibility (km)') | |
| if 'boundary_layer_height' in forecast_data: | |
| ax8_twin = ax8.twinx() | |
| ax8_twin.plot(timestamps, forecast_data['boundary_layer_height'], 'brown', linewidth=1, alpha=0.7, label='BL Height (m)') | |
| ax8_twin.set_ylabel('BL Height (m)', color='brown') | |
| ax8_twin.tick_params(axis='y', labelcolor='brown') | |
| ax8.set_title('Atmospheric Conditions') | |
| ax8.set_ylabel('Visibility (km)') | |
| ax8.grid(True, alpha=0.3) | |
| ax8.legend(fontsize=8) | |
| ax8.tick_params(axis='x', rotation=45, labelsize=8) | |
| # Summary info panel | |
| ax9 = fig.add_subplot(gs[2, 2]) | |
| ax9.axis('off') | |
| # Check if we have real data or fallback | |
| data_source = "Real DWD ICON Data" if 'error' not in forecast_data else "Fallback Synthetic Data" | |
| forecast_info = forecast_data.get('forecast_date', 'Unknown') | |
| # Grid point info | |
| grid_info = "" | |
| if 'nearest_grid_lat' in forecast_data and 'nearest_grid_lon' in forecast_data: | |
| grid_info = f"Grid: {forecast_data['nearest_grid_lat']:.2f}°N, {forecast_data['nearest_grid_lon']:.2f}°E\n" | |
| # Count available variables | |
| available_vars = [] | |
| var_categories = { | |
| 'Temperature': ['temperature', 'temp_min', 'temp_max', 'dewpoint'], | |
| 'Wind': ['wind_speed', 'wind_direction', 'wind_gust'], | |
| 'Pressure': ['pressure', 'surface_pressure'], | |
| 'Precipitation': ['precipitation', 'rain', 'snow'], | |
| 'Clouds': ['cloud_cover', 'low_cloud', 'mid_cloud', 'high_cloud'], | |
| 'Radiation': ['solar_radiation', 'direct_radiation', 'diffuse_radiation'], | |
| 'Atmosphere': ['visibility', 'boundary_layer_height', 'cape', 'humidity'] | |
| } | |
| for category, vars_list in var_categories.items(): | |
| count = sum(1 for var in vars_list if var in forecast_data) | |
| if count > 0: | |
| available_vars.append(f"{category}: {count}") | |
| summary_text = f""" | |
| Location: {forecast_data['lat']:.2f}°N, {forecast_data['lon']:.2f}°E | |
| {grid_info} | |
| Data: {data_source} | |
| Forecast: {forecast_info} | |
| Available Variables: | |
| {chr(10).join(available_vars)} | |
| Current Conditions: | |
| Temp: {forecast_data['temperature'][0]:.1f}°C | |
| Humidity: {forecast_data['humidity'][0]:.1f}% | |
| Wind: {forecast_data['wind_speed'][0]:.1f} m/s | |
| """ | |
| # Add pressure if available | |
| if 'pressure' in forecast_data: | |
| summary_text += f"Pressure: {forecast_data['pressure'][0]:.1f} hPa\n" | |
| # Add error info if present | |
| if 'error' in forecast_data: | |
| summary_text += f"\nNote: Using fallback data\nReason: {forecast_data['error'][:80]}..." | |
| color = 'lightgreen' if 'error' not in forecast_data else 'lightyellow' | |
| ax9.text(0.05, 0.95, summary_text, transform=ax9.transAxes, fontsize=8, | |
| verticalalignment='top', bbox=dict(boxstyle='round', facecolor=color, alpha=0.7)) | |
| plt.tight_layout() | |
| return fig | |
| def process_map_click(lat, lon): | |
| """Process map click and return forecast""" | |
| if lat is None or lon is None: | |
| return "Please click on the map to select a location", None | |
| # Get forecast data | |
| forecast_data = get_forecast_data(lat, lon) | |
| # Create plot | |
| plot = create_forecast_plot(forecast_data) | |
| # Create summary text | |
| if isinstance(forecast_data, dict): | |
| data_type = "Real DWD ICON Data" if 'error' not in forecast_data else "Fallback Data" | |
| forecast_info = forecast_data.get('forecast_date', '') | |
| summary = f"Forecast for location: {lat:.3f}°N, {lon:.3f}°E\n\nUsing: {data_type}\nForecast: {forecast_info}" | |
| if 'error' in forecast_data: | |
| summary += f"\n\nNote: Real data unavailable - {forecast_data['error'][:150]}..." | |
| else: | |
| summary = forecast_data | |
| return summary, plot | |
| def create_attribution_text(): | |
| """Create proper attribution for the dataset""" | |
| attribution = """ | |
| ## Data Attribution | |
| This application accesses **DWD ICON Global** weather forecast data directly from the German Weather Service. | |
| - **Model**: DWD ICON Global Weather Model | |
| - **Source**: German Weather Service (Deutscher Wetterdienst - DWD) | |
| - **Data Server**: DWD Open Data Server (https://opendata.dwd.de) | |
| - **License**: Open Government Data (free for commercial use) | |
| - **Format**: GRIB2 meteorological data | |
| **Commercial Use**: DWD's Open Data Server provides free access to weather data suitable for commercial applications. | |
| **Current Implementation**: This demo version uses simulated DWD-style data. For production use with real DWD ICON data: | |
| - Access GRIB2 files directly from https://opendata.dwd.de/weather/nwp/icon/ | |
| - Use commercial weather APIs like WeatherAPI.com that provide DWD data | |
| - Parse GRIB2 files using tools like pygrib or cfgrib | |
| **Citation**: Please cite the German Weather Service (DWD) ICON model when using this data. | |
| """ | |
| return attribution | |
| # Create the Gradio interface | |
| with gr.Blocks(title="DWD ICON Global Weather Forecast") as app: | |
| gr.Markdown("# 🌦️ DWD ICON Global Weather Forecast") | |
| gr.Markdown(""" | |
| **Comprehensive Weather Forecasting Dashboard** - Click on the map to select any location and view detailed 4-day forecasts with: | |
| 📊 **9 Weather Panels**: Temperature, Humidity/Moisture, Wind, Pressure, Precipitation, Cloud Cover, Solar Radiation, Atmospheric Conditions, and Data Summary | |
| 🔢 **30+ Weather Variables**: Temperature (min/max/dewpoint), Wind (speed/direction/gusts), Pressure (sea level/surface), | |
| Precipitation (rain/snow/convective), Cloud layers (low/mid/high/total), Solar radiation (direct/diffuse/longwave), | |
| Visibility, Boundary layer height, Atmospheric stability (CAPE/CIN), and more! | |
| 🎯 **DWD ICON Model Data** directly from the German Weather Service Open Data Server (Commercial Use Approved) | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| # Map component | |
| map_html = gr.HTML(create_map()._repr_html_(), label="Interactive Map") | |
| gr.Markdown("👆 Click anywhere on the map to select a location for forecast") | |
| with gr.Column(scale=2): | |
| # Forecast output | |
| forecast_text = gr.Textbox( | |
| label="Forecast Information", | |
| value="Click on the map to select a location", | |
| lines=3 | |
| ) | |
| forecast_plot = gr.Plot(label="Weather Forecast Charts") | |
| # Input fields for manual coordinate entry | |
| with gr.Row(): | |
| lat_input = gr.Number( | |
| label="Latitude", | |
| value=52.5, | |
| minimum=-90, | |
| maximum=90, | |
| step=0.001, | |
| precision=3 | |
| ) | |
| lon_input = gr.Number( | |
| label="Longitude", | |
| value=13.4, | |
| minimum=-180, | |
| maximum=180, | |
| step=0.001, | |
| precision=3 | |
| ) | |
| submit_btn = gr.Button("Get Forecast", variant="primary") | |
| # Attribution section | |
| with gr.Accordion("📋 Data Attribution & Information", open=False): | |
| gr.Markdown(create_attribution_text()) | |
| # Event handlers | |
| submit_btn.click( | |
| fn=process_map_click, | |
| inputs=[lat_input, lon_input], | |
| outputs=[forecast_text, forecast_plot] | |
| ) | |
| # Example locations | |
| with gr.Row(): | |
| gr.Examples( | |
| examples=[ | |
| [52.5200, 13.4050], # Berlin | |
| [48.8566, 2.3522], # Paris | |
| [51.5074, -0.1278], # London | |
| [55.7558, 37.6176], # Moscow | |
| [41.9028, 12.4964], # Rome | |
| ], | |
| inputs=[lat_input, lon_input], | |
| outputs=[forecast_text, forecast_plot], | |
| fn=process_map_click, | |
| cache_examples=False, | |
| label="Try these example locations:" | |
| ) | |
| def test_data_access(): | |
| """Test function to verify data access works""" | |
| try: | |
| print("Testing data access...") | |
| file_path, forecast_date, hour = get_latest_available_file() | |
| print(f"Successfully accessed file: {file_path}") | |
| # Try to load the dataset | |
| import xarray as xr | |
| ds = xr.open_zarr(file_path) | |
| print(f"Dataset dimensions: {dict(ds.dims)}") | |
| print(f"Available variables: {list(ds.data_vars.keys())}") | |
| print("Data access test successful!") | |
| except Exception as e: | |
| print(f"Data access test failed: {e}") | |
| import traceback | |
| traceback.print_exc() | |
| if __name__ == "__main__": | |
| # Uncomment the line below to test data access before launching the app | |
| # test_data_access() | |
| app.launch(share=True, server_name="0.0.0.0") |