| name | metocean-visualizer |
| description | Create interactive metocean visualizations including time series plots, wave roses, scatter plots, geographic maps, and dashboards. Use for data exploration, reporting, and operational monitoring. |
Metocean Visualizer Skill
Interactive visualization toolkit for metocean data analysis using Plotly
When to Use This Skill
Use this skill when you need to:
- Plot wave data or create metocean dashboards
- Visualize buoy observations or hindcast data
- Generate wave roses or wind roses
- Create time series charts for Hs, Tp, wind speed
- Build scatter plots (Hs vs Tp, wind vs waves)
- Map metocean stations with interactive overlays
- Compare forecasts vs observations
- Generate joint distribution contour plots
- Create operational monitoring dashboards
Trigger phrases:
- "Plot wave data", "Create metocean dashboard"
- "Visualize buoy observations", "Generate wave rose"
- "Time series chart", "Scatter plot Hs vs Tp"
- "Map metocean stations", "Interactive plot"
- "Compare forecasts vs observations"
Visualization Types
Time Series Plots
- Single parameter trends (Hs, Tp, wind speed)
- Multi-parameter comparison on shared x-axis
- Quality flag indicators with color coding
- Forecast vs observation overlays
Directional Roses
- Wave roses (Hs by direction)
- Wind roses (speed by direction)
- Current roses (velocity by direction)
- Customizable sectors (8, 12, 16, 36)
Scatter Plots
- Hs vs Tp (wave height vs period)
- Wind speed vs wave height correlations
- Joint distribution contours
- Color-coded by time/quality/source
Geographic Maps
- Station locations with Scattermapbox
- Data overlays (latest values)
- Regional coverage visualization
- Interactive hover details
Joint Distribution Charts
- 2D histograms (Histogram2d)
- Contour plots for environmental design
- Conditional distributions
- Return period contours
Statistical Charts
- Histograms with kernel density
- CDFs (cumulative distribution functions)
- QQ plots for distribution comparison
- Box plots by month/season
Core Patterns
"""
ABOUTME: Interactive visualization toolkit for metocean data analysis
ABOUTME: Provides chart templates for waves, wind, currents, and mapping
"""
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
import numpy as np
from typing import Optional
class MetoceanChartBuilder:
"""Build interactive metocean charts with Plotly."""
def time_series(
self,
df: pd.DataFrame,
output_path: Optional[str] = None
) -> go.Figure:
"""Create interactive time series of wave parameters."""
fig = make_subplots(
rows=3, cols=1,
shared_xaxes=True,
vertical_spacing=0.05,
subplot_titles=('Wave Height', 'Wave Period', 'Wind Speed')
)
fig.add_trace(
go.Scatter(
x=df['time'], y=df['wave_height_m'],
name='Hs', line=dict(color='#1f77b4')
),
row=1, col=1
)
fig.add_trace(
go.Scatter(
x=df['time'], y=df['wave_period_s'],
name='Tp', line=dict(color=)
),
row=, col=
)
fig.add_trace(
go.Scatter(
x=df[], y=df[],
name=, line=(color=)
),
row=, col=
)
fig.update_layout(
height=,
title=,
hovermode=
)
fig.update_yaxes(title_text=, row=, col=)
fig.update_yaxes(title_text=, row=, col=)
fig.update_yaxes(title_text=, row=, col=)
output_path:
fig.write_html(output_path)
fig
() -> go.Figure:
sector_width = / n_sectors
bins = np.arange(, + sector_width, sector_width)
df[] = pd.cut(
df[direction_col],
bins=bins,
labels=bins[:-] + sector_width /
)
stats = df.groupby().agg({
height_col: [, ]
}).reset_index()
stats.columns = [, , ]
total = stats[].()
stats[] = * stats[] / total
fig = go.Figure()
fig.add_trace(go.Barpolar(
r=stats[],
theta=stats[],
width=sector_width * ,
marker_color=stats[],
marker_colorscale=,
marker_colorbar=(title=),
hovertemplate=(
)
))
fig.update_layout(
polar=(
radialaxis=(visible=, =[, stats[].() * ]),
angularaxis=(direction=, rotation=)
),
title=,
showlegend=
)
output_path:
fig.write_html(output_path)
fig
() -> go.Figure:
fig = go.Figure()
fig.add_trace(go.Histogram2d(
x=df[],
y=df[],
colorscale=,
showscale=,
colorbar=(title=),
nbinsx=,
nbinsy=
))
fig.add_trace(go.Scatter(
x=df[],
y=df[],
mode=,
marker=(size=, color=, opacity=),
hovertemplate=
))
fig.update_layout(
xaxis_title=,
yaxis_title=,
title=
)
output_path:
fig.write_html(output_path)
fig
() -> go.Figure:
lats = [s[] s stations]
lons = [s[] s stations]
names = [s[] s stations]
fig = go.Figure(go.Scattermapbox(
lat=lats,
lon=lons,
mode=,
marker=(size=, color=),
text=names,
hovertemplate=
))
fig.update_layout(
mapbox=(
style=,
center=(lat=(lats) / (lats), lon=(lons) / (lons)),
zoom=
),
title=,
margin=(l=, r=, t=, b=)
)
output_path:
fig.write_html(output_path)
fig
Dashboard Template
def create_metocean_dashboard(
df: pd.DataFrame,
stations: list,
output_path: str = 'reports/metocean_dashboard.html'
) -> go.Figure:
"""Create comprehensive metocean dashboard."""
fig = make_subplots(
rows=2, cols=2,
specs=[
[{"type": "scatter"}, {"type": "polar"}],
[{"type": "scatter"}, {"type": "scattermapbox"}]
],
subplot_titles=('Time Series', 'Wave Rose', 'Hs vs Tp', 'Station Map'),
vertical_spacing=0.12,
horizontal_spacing=0.1
)
fig.add_trace(
go.Scatter(
x=df['time'], y=df['wave_height_m'],
name='Hs', line=dict(color='#1f77b4')
),
row=1, col=1
)
dir_stats = calculate_directional_stats(df)
fig.add_trace(
go.Barpolar(
r=dir_stats['occurrence_pct'],
theta=dir_stats['direction'],
marker_color=dir_stats['mean_hs'],
marker_colorscale='Viridis'
),
row=1, col=2
)
fig.add_trace(
go.Scatter(
x=df['wave_height_m'], y=df['wave_period_s'],
mode=, marker=(size=, opacity=),
name=
),
row=, col=
)
fig.add_trace(
go.Scattermapbox(
lat=[s[] s stations],
lon=[s[] s stations],
mode=,
marker=(size=),
text=[s[] s stations]
),
row=, col=
)
fig.update_layout(
height=,
title=,
mapbox=(style=, zoom=)
)
fig.write_html(output_path)
fig
() -> pd.DataFrame:
sector_width = / n_sectors
bins = np.arange(, + sector_width, sector_width)
df_copy = df.copy()
df_copy[] = pd.cut(
df_copy[],
bins=bins,
labels=bins[:-] + sector_width /
)
stats = df_copy.groupby().agg({
: [, ]
}).reset_index()
stats.columns = [, , ]
total = stats[].()
stats[] = * stats[] / total
stats
Wind Rose with Matplotlib (windrose package)
from windrose import WindroseAxes
import matplotlib.pyplot as plt
import numpy as np
def plot_wind_rose_matplotlib(
speeds: np.ndarray,
directions: np.ndarray,
output_path: Optional[str] = None,
title: str = 'Wind Rose'
) -> plt.Figure:
"""Create wind rose diagram using windrose package."""
fig = plt.figure(figsize=(10, 10))
ax = WindroseAxes.from_ax(fig=fig)
ax.bar(
directions, speeds,
normed=True, opening=0.8,
bins=np.arange(0, 25, 5),
cmap=plt.cm.viridis
)
ax.set_legend(title='Speed (m/s)')
ax.set_title(title)
if output_path:
plt.savefig(output_path, dpi=150, bbox_inches='tight')
return fig
Forecast vs Observation Comparison
def plot_forecast_comparison(
obs_df: pd.DataFrame,
fcst_df: pd.DataFrame,
param: str = 'wave_height_m',
output_path: Optional[str] = None
) -> go.Figure:
"""Compare forecast vs observation time series."""
fig = go.Figure()
fig.add_trace(go.Scatter(
x=obs_df['time'], y=obs_df[param],
name='Observation',
mode='lines+markers',
marker=dict(size=4),
line=dict(color='#1f77b4')
))
fig.add_trace(go.Scatter(
x=fcst_df['time'], y=fcst_df[param],
name='Forecast',
mode='lines',
line=dict(color='#ff7f0e', dash='dash')
))
fig.update_layout(
title=f'{param} - Forecast vs Observation',
xaxis_title='Time',
yaxis_title=param,
hovermode='x unified',
legend=dict(yanchor='top', y=0.99, xanchor='left', x=0.01)
)
if output_path:
fig.write_html(output_path)
return fig
HTML Report Generation
import plotly.io as pio
def generate_metocean_report(
df: pd.DataFrame,
station_info: dict,
output_path: str = 'reports/metocean_report.html'
) -> str:
"""Generate comprehensive HTML metocean report."""
builder = MetoceanChartBuilder()
ts_fig = builder.time_series(df)
rose_fig = builder.wave_rose(df)
scatter_fig = builder.scatter_hs_tp(df)
html_content = f'''<!DOCTYPE html>
<html>
<head>
<title>Metocean Report - {station_info["id"]}</title>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<style>
body {{ font-family: Arial, sans-serif; margin: 20px; }}
.plot-container {{ margin: 20px 0; }}
h1 {{ color: #333; }}
h2 {{ color: #555; border-bottom: 1px solid #ddd; padding-bottom: 5px; }}
.metadata {{ background: #f5f5f5; padding: 15px; border-radius: 5px; }}
.metadata p {{ margin: 5px 0; }}
</style>
</head>
<body>
<h1>Metocean Report: Station {station_info["id"]}</h1>
<div class="metadata">
<p><strong>Location:</strong> {station_info["lat"]:.3f} N, {station_info["lon"]:.3f} W</p>
<p><strong>Period:</strong> {df["time"].min()} to {df["time"].max()}</p>
<p><strong>Records:</strong> {len(df):,}</p>
<p><strong>Data Source:</strong> {station_info.get("source", "N/A")}</p>
</div>
<h2>Time Series</h2>
<div class="plot-container">
{pio.to_html(ts_fig, include_plotlyjs=False, full_html=False)}
</div>
<h2>Wave Rose</h2>
<div class="plot-container">
</div>
<h2>Joint Distribution</h2>
<div class="plot-container">
</div>
</body>
</html>'''
(output_path, ) f:
f.write(html_content)
output_path
Usage Examples
from worldenergydata.metocean.visualize import MetoceanChartBuilder
import pandas as pd
df = pd.read_csv('data/processed/buoy_data.csv', parse_dates=['time'])
builder = MetoceanChartBuilder()
fig_ts = builder.time_series(df)
fig_ts.write_html('reports/time_series.html')
fig_rose = builder.wave_rose(df)
fig_rose.write_html('reports/wave_rose.html')
fig_scatter = builder.scatter_hs_tp(df)
fig_scatter.write_html('reports/scatter_hs_tp.html')
stations = [
{'station_id': 'NDBC-41001', 'latitude': 34.68, 'longitude': -72.66},
{'station_id': 'NDBC-41002', 'latitude': 31.76, 'longitude': -74.84}
]
fig_map = builder.station_map(stations)
fig_map.write_html('reports/station_map.html')
generate_metocean_report(df, {'id': 'NDBC-41001', 'lat': 34.68, 'lon': -72.66})
External Tool Integration
windrose package (for matplotlib roses):
pip install windrose
MetOceanViewer Patterns (Desktop Reference):
- Map-based station selection
- Time series with multiple y-axes
- Model-observation comparison panels
- Geographic data overlays
Best Practices
- Always use interactive plots - Plotly preferred for web-based reports
- Include hover information - Provide relevant data on all points
- Use relative paths - Store data in
/data/raw/ or /data/processed/
- Consistent color schemes - Use same colors across related plots
- Include metadata - Station info, time range, data source
- Export standalone HTML - Embed Plotly.js for portability
- Optimize for both screen and print - Consider responsive layouts
Output Formats
| Format | Use Case | Method |
|---|
| Interactive HTML | Web dashboards, reports | fig.write_html() |
| PNG/SVG | Static reports, publications | fig.write_image() |
| JSON | Data interchange | fig.to_json() |
| Dashboard HTML | Multi-panel views | make_subplots() |
Common Workflows
- Station Overview: Fetch data -> Time series + Rose + Map -> HTML report
- Comparison Dashboard: Multiple stations -> Side-by-side plots -> Export
- Operational Monitoring: Real-time fetch -> Update dashboard -> Alert thresholds
- Design Analysis: Joint distributions -> Environmental contours -> Export
Related Skills
energy-data-visualizer - General energy visualization patterns
metocean-data-fetcher - Data source for visualization
metocean-statistics - Statistical analysis for contours