Wind rose
Iowa Mesonet ASOS, station DSM, full year 2023
Example from the compendium of canonical charts
Python Code
"""Wind rose — Iowa Mesonet ASOS, station DSM, full year 2023."""
import numpy as np
import plotly.graph_objects as go
URL = (
"https://mesonet.agron.iastate.edu/cgi-bin/request/asos.py"
"?station=DSM&data=drct&data=sknt"
"&year1=2023&month1=1&day1=1"
"&year2=2024&month2=1&day2=1"
"&tz=Etc/UTC&format=onlycomma&missing=M&trace=T"
)
SPEED_BINS = [
# (label, lo_m/s, hi_m/s, color) — solid (opaque) violet shades light→dark,
# so the polar grid lines don't show through the bars.
("Calm", 0, 0.5, "rgb(237, 231, 252)"),
("0–5", 0.5, 5, "rgb(212, 199, 249)"),
("5–10", 5, 10, "rgb(184, 162, 245)"),
("10–15", 10, 15, "rgb(157, 126, 241)"),
(">15", 15, 999, VIOLET),
]
COMPASS_16 = ["N","NNE","NE","ENE","E","ESE","SE","SSE",
"S","SSW","SW","WSW","W","WNW","NW","NNW"]
def generate():
print(f"fetching Iowa Mesonet DSM 2023 …")
df = fetch_csv(URL, na_values=["M", "T"])
df = df.dropna(subset=["drct", "sknt"])
df["ws_ms"] = df["sknt"] * 0.5144 # knots → m/s
n_total = len(df)
n_calm = (df["ws_ms"] < 0.5).sum()
width = 360 / 16
centers = np.arange(0, 360, width) # 0°=N, clockwise
traces = []
for label, lo, hi, color in SPEED_BINS:
sub = df[(df["ws_ms"] >= lo) & (df["ws_ms"] < hi)]
rs = [
((sub["drct"] - c + 180) % 360 - 180).abs().lt(width / 2).sum() / n_total * 100
for c in centers
]
traces.append(go.Barpolar(
r=rs,
theta=centers,
width=width,
name=label,
marker_color=color,
hovertemplate="%{theta:.0f}°: %{r:.2f}%<extra>" + label + " m/s</extra>",
))
fig = go.Figure(traces)
fig.update_layout(
title=dict(text="Wind Rose — Des Moines (DSM), 2023", x=0.5),
polar=dict(
angularaxis=dict(
tickvals=list(centers),
ticktext=COMPASS_16,
direction="clockwise",
rotation=90,
),
radialaxis=dict(ticksuffix="%"),
),
legend=dict(title="Speed (m/s)"),
margin=dict(t=60, b=50, l=40, r=40),
height=500,
annotations=[dict(
x=0, y=-0.1, xref="paper", yref="paper", showarrow=False,
text=f"Calm ({n_calm / n_total * 100:.1f}% of obs) excluded from sectors.",
font=dict(size=11, color="#666"),
)],
)
return fig
if __name__ == "__main__":
generate()
Made with Plotly