Spaces:
Sleeping
Sleeping
File size: 13,184 Bytes
170f330 75fd269 170f330 75fd269 e8c040b 170f330 e8c040b 170f330 e8c040b 170f330 e8c040b 170f330 e8c040b 170f330 48e63c4 e8c040b 48e63c4 170f330 75fd269 170f330 75fd269 170f330 75fd269 e8c040b 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 75fd269 170f330 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
import gradio as gr
import xarray as xr
import pandas as pd
import numpy as np
import plotly.graph_objects as go
from datetime import datetime, timedelta
import warnings
import logging
import traceback
warnings.filterwarnings('ignore')
# Set up detailed logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Catalog configuration - using latest.zarr URLs as per dynamical-org/notebooks
CATALOG = {
"NOAA GFS Analysis (Hourly)": {
"url": "https://data.dynamical.org/noaa/gfs/analysis-hourly/latest.zarr?email=treesixtyweather@gmail.com",
"type": "analysis",
"variables": ["temperature_2m", "precipitation", "wind_u_10m", "wind_v_10m", "mean_sea_level_pressure"]
},
"NOAA GFS Forecast": {
"url": "https://data.dynamical.org/noaa/gfs/forecast/latest.zarr?email=treesixtyweather@gmail.com",
"type": "forecast",
"variables": ["temperature_2m", "precipitation", "wind_u_10m", "wind_v_10m", "mean_sea_level_pressure"]
},
"NOAA GEFS Analysis": {
"url": "https://data.dynamical.org/noaa/gefs/analysis/latest.zarr?email=treesixtyweather@gmail.com",
"type": "analysis",
"variables": ["temperature_2m", "precipitation", "wind_u_10m", "wind_v_10m"]
},
"NOAA GEFS Forecast (35-day)": {
"url": "https://data.dynamical.org/noaa/gefs/forecast-35-day/latest.zarr?email=treesixtyweather@gmail.com",
"type": "forecast",
"variables": ["temperature_2m", "precipitation", "wind_u_10m", "wind_v_10m"]
},
"NOAA HRRR Forecast (48-hour)": {
"url": "https://data.dynamical.org/noaa/hrrr/forecast-48-hour/latest.zarr?email=treesixtyweather@gmail.com",
"type": "forecast",
"variables": ["temperature_2m", "precipitation", "wind_u_10m", "wind_v_10m"]
}
}
# Cache for loaded datasets
dataset_cache = {}
def load_dataset(dataset_name, use_cache=True):
"""Load a dataset from the Dynamical catalog"""
logger.info(f"=== Loading dataset: {dataset_name} ===")
if use_cache and dataset_name in dataset_cache:
logger.info(f"Dataset found in cache: {dataset_name}")
return dataset_cache[dataset_name], None
try:
url = CATALOG[dataset_name]["url"]
logger.info(f"Opening zarr store at: {url}")
# Open zarr store - using approach from dynamical-org/notebooks
logger.info("Opening zarr store with chunks=None")
ds = xr.open_zarr(url, chunks=None)
logger.info(f"Successfully opened zarr store")
logger.info(f"Dataset dimensions: {dict(ds.dims)}")
logger.info(f"Dataset variables: {list(ds.data_vars)}")
logger.info(f"Dataset coordinates: {list(ds.coords)}")
if use_cache:
dataset_cache[dataset_name] = ds
logger.info(f"Dataset cached: {dataset_name}")
return ds, None
except Exception as e:
error_msg = f"Error loading dataset: {str(e)}"
logger.error(f"=== ERROR loading {dataset_name} ===")
logger.error(f"URL: {CATALOG[dataset_name]['url']}")
logger.error(f"Exception type: {type(e).__name__}")
logger.error(f"Exception message: {str(e)}")
logger.error(f"Traceback:\n{traceback.format_exc()}")
return None, error_msg
def create_map_visualization(dataset_name, variable, time_index=0):
"""Create an interactive map visualization of the selected variable"""
logger.info(f"=== Creating map visualization ===")
logger.info(f"Dataset: {dataset_name}, Variable: {variable}, Time index: {time_index}")
try:
ds, error = load_dataset(dataset_name)
if ds is None:
logger.error(f"Dataset loading returned None: {error}")
return None, f"Error loading dataset: {dataset_name}\n{error}"
logger.info(f"Dataset loaded successfully")
# Check if variable exists
if variable not in ds.variables:
available_vars = list(ds.data_vars)
logger.error(f"Variable '{variable}' not found. Available: {available_vars}")
return None, f"Variable '{variable}' not found. Available: {available_vars}"
logger.info(f"Variable '{variable}' found in dataset")
# Get the data
data_var = ds[variable]
logger.info(f"Variable shape: {data_var.shape}, dims: {data_var.dims}")
# Handle time dimension
if 'time' in data_var.dims:
logger.info(f"Time dimension found, length: {len(ds.time)}")
if time_index >= len(ds.time):
time_index = 0
data_var = data_var.isel(time=time_index)
logger.info(f"Selected time index: {time_index}")
# Handle ensemble dimension if present
if 'ensemble' in data_var.dims:
logger.info(f"Ensemble dimension found, selecting ensemble 0")
data_var = data_var.isel(ensemble=0)
logger.info(f"Data variable shape after slicing: {data_var.shape}")
# Load data into memory (subsample for performance)
step = max(1, len(ds.latitude) // 200) # Limit to ~200 points per dimension
logger.info(f"Subsampling with step: {step}")
data_var = data_var.isel(latitude=slice(None, None, step), longitude=slice(None, None, step))
logger.info(f"Computing data values...")
data_values = data_var.compute().values
logger.info(f"Data values shape: {data_values.shape}, min: {data_values.min()}, max: {data_values.max()}")
# Get coordinates
lats = ds.latitude.isel(latitude=slice(None, None, step)).values
lons = ds.longitude.isel(longitude=slice(None, None, step)).values
logger.info(f"Lat range: [{lats.min()}, {lats.max()}], Lon range: [{lons.min()}, {lons.max()}]")
# Create plotly figure
fig = go.Figure(data=go.Heatmap(
z=data_values,
x=lons,
y=lats,
colorscale='RdBu_r',
hovertemplate='Lat: %{y:.2f}<br>Lon: %{x:.2f}<br>Value: %{z:.2f}<extra></extra>'
))
time_str = ""
if 'time' in ds[variable].dims:
time_val = pd.to_datetime(ds.time.isel(time=time_index).values)
time_str = f" - {time_val.strftime('%Y-%m-%d %H:%M UTC')}"
fig.update_layout(
title=f"{dataset_name}: {variable}{time_str}",
xaxis_title="Longitude",
yaxis_title="Latitude",
height=600,
hovermode='closest'
)
logger.info(f"Map visualization created successfully")
return fig, f"Successfully loaded {dataset_name}"
except Exception as e:
error_msg = f"Error creating visualization: {str(e)}"
logger.error(f"=== ERROR creating visualization ===")
logger.error(f"Exception type: {type(e).__name__}")
logger.error(f"Exception message: {str(e)}")
logger.error(f"Traceback:\n{traceback.format_exc()}")
return None, error_msg
def get_point_forecast(dataset_name, lat, lon, variable):
"""Get forecast data for a specific point"""
logger.info(f"=== Getting point forecast ===")
logger.info(f"Dataset: {dataset_name}, Lat: {lat}, Lon: {lon}, Variable: {variable}")
try:
ds, error = load_dataset(dataset_name)
if ds is None:
logger.error(f"Dataset loading failed: {error}")
return None, f"Error loading dataset: {error}"
if variable not in ds.variables:
logger.error(f"Variable '{variable}' not found in dataset")
return None, f"Variable '{variable}' not found in dataset"
logger.info(f"Selecting nearest point to ({lat}, {lon})")
# Find nearest point
data_var = ds[variable].sel(latitude=lat, longitude=lon, method='nearest')
# Handle ensemble dimension
if 'ensemble' in data_var.dims:
logger.info(f"Handling ensemble dimension")
data_var = data_var.isel(ensemble=0)
logger.info(f"Point data shape: {data_var.shape}, dims: {data_var.dims}")
# Load data
logger.info(f"Computing point data values...")
data_values = data_var.compute().values
logger.info(f"Point data computed, shape: {data_values.shape}")
# Create time series plot
if 'time' in ds[variable].dims:
times = pd.to_datetime(ds.time.values)
logger.info(f"Creating time series plot with {len(times)} time steps")
fig = go.Figure()
fig.add_trace(go.Scatter(
x=times,
y=data_values,
mode='lines+markers',
name=variable
))
fig.update_layout(
title=f"Point Forecast: {variable} at ({lat:.2f}, {lon:.2f})",
xaxis_title="Time (UTC)",
yaxis_title=variable,
height=400,
hovermode='x unified'
)
# Create data table
df = pd.DataFrame({
'Time (UTC)': times,
variable: data_values
})
logger.info(f"Point forecast created successfully")
return fig, df.to_html(index=False)
else:
logger.warning(f"No time dimension found for {variable}")
return None, f"No time dimension found for {variable}"
except Exception as e:
error_msg = f"Error getting point forecast: {str(e)}"
logger.error(f"=== ERROR getting point forecast ===")
logger.error(f"Exception type: {type(e).__name__}")
logger.error(f"Exception message: {str(e)}")
logger.error(f"Traceback:\n{traceback.format_exc()}")
return None, error_msg
def update_available_variables(dataset_name):
"""Update the variable dropdown based on selected dataset"""
logger.info(f"=== Updating available variables for {dataset_name} ===")
try:
ds, error = load_dataset(dataset_name, use_cache=False)
if ds is None:
logger.warning(f"Could not load dataset, using default variables: {error}")
return gr.Dropdown(choices=CATALOG[dataset_name]["variables"], value=CATALOG[dataset_name]["variables"][0])
available_vars = list(ds.data_vars)
logger.info(f"Available variables: {available_vars}")
return gr.Dropdown(choices=available_vars, value=available_vars[0] if available_vars else None)
except Exception as e:
logger.error(f"Error updating variables: {str(e)}")
logger.error(f"Traceback:\n{traceback.format_exc()}")
return gr.Dropdown(choices=CATALOG[dataset_name]["variables"], value=CATALOG[dataset_name]["variables"][0])
# Create Gradio interface
with gr.Blocks(title="Dynamical Weather Catalog Viewer") as app:
gr.Markdown("""
# 🌍 Dynamical Weather Catalog Viewer
Explore weather analysis and forecast data from the [Dynamical.org catalog](https://dynamical.org/catalog/).
**Features:**
- Visualize global weather data on interactive maps
- Click a location to get point forecasts
- Browse multiple datasets: NOAA GFS, GEFS, and HRRR
""")
with gr.Row():
with gr.Column(scale=1):
dataset_dropdown = gr.Dropdown(
choices=list(CATALOG.keys()),
value=list(CATALOG.keys())[0],
label="Select Dataset"
)
variable_dropdown = gr.Dropdown(
choices=CATALOG[list(CATALOG.keys())[0]]["variables"],
value=CATALOG[list(CATALOG.keys())[0]]["variables"][0],
label="Select Variable"
)
time_slider = gr.Slider(
minimum=0,
maximum=10,
step=1,
value=0,
label="Time Index"
)
load_btn = gr.Button("Load Map", variant="primary")
status_text = gr.Textbox(label="Status", interactive=False)
with gr.Column(scale=2):
map_plot = gr.Plot(label="Map Visualization")
gr.Markdown("## 📍 Point Forecast")
gr.Markdown("Enter coordinates to get a time series forecast for a specific location")
with gr.Row():
lat_input = gr.Number(value=40.7, label="Latitude", precision=2)
lon_input = gr.Number(value=-74.0, label="Longitude", precision=2)
forecast_btn = gr.Button("Get Point Forecast", variant="secondary")
with gr.Row():
forecast_plot = gr.Plot(label="Time Series Forecast")
forecast_table = gr.HTML(label="Forecast Data")
# Event handlers
dataset_dropdown.change(
fn=update_available_variables,
inputs=[dataset_dropdown],
outputs=[variable_dropdown]
)
load_btn.click(
fn=create_map_visualization,
inputs=[dataset_dropdown, variable_dropdown, time_slider],
outputs=[map_plot, status_text]
)
forecast_btn.click(
fn=get_point_forecast,
inputs=[dataset_dropdown, lat_input, lon_input, variable_dropdown],
outputs=[forecast_plot, forecast_table]
)
if __name__ == "__main__":
app.launch()
|