|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | polar-bar: Polar Bar Chart (Wind Rose) |
3 | | -Library: plotly 6.5.0 | Python 3.13.11 |
4 | | -Quality: 91/100 | Created: 2025-12-30 |
| 3 | +Library: plotly 6.7.0 | Python 3.13.13 |
| 4 | +Quality: 88/100 | Updated: 2026-05-13 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
| 8 | + |
7 | 9 | import numpy as np |
8 | 10 | import plotly.graph_objects as go |
9 | 11 |
|
10 | 12 |
|
| 13 | +# Theme tokens |
| 14 | +THEME = os.getenv("ANYPLOT_THEME", "light") |
| 15 | +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
| 16 | +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
| 17 | +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 18 | +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 19 | +GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)" |
| 20 | + |
| 21 | +# Okabe-Ito palette (first series always #009E73) |
| 22 | +OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] |
| 23 | +wind_speed_labels = ["Light (0-10 km/h)", "Moderate (10-20 km/h)", "Strong (20+ km/h)"] |
| 24 | + |
11 | 25 | # Data: Wind speed distribution by direction (8 compass points) |
12 | | -# Each direction has 3 speed ranges (stacked bars) |
13 | 26 | np.random.seed(42) |
14 | | - |
15 | 27 | directions = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"] |
16 | 28 |
|
17 | | -# Wind speed frequencies by direction (simulating typical coastal wind patterns) |
18 | | -# Light breeze (0-10 km/h) |
| 29 | +# Wind speed frequencies by direction (coastal wind pattern) |
19 | 30 | light = np.array([8, 5, 4, 3, 6, 12, 15, 10]) |
20 | | -# Moderate breeze (10-20 km/h) |
21 | 31 | moderate = np.array([6, 4, 3, 2, 4, 10, 12, 8]) |
22 | | -# Strong breeze (20+ km/h) |
23 | 32 | strong = np.array([3, 2, 1, 1, 2, 5, 6, 4]) |
24 | 33 |
|
25 | 34 | # Create figure with polar subplot |
26 | 35 | fig = go.Figure() |
27 | 36 |
|
28 | | -# Add stacked bars for each wind speed category (base is cumulative) |
29 | | -fig.add_trace( |
30 | | - go.Barpolar( |
31 | | - r=light, |
32 | | - theta=directions, |
33 | | - name="Light (0-10 km/h)", |
34 | | - marker=dict(color="#306998", line=dict(color="white", width=2)), |
35 | | - opacity=0.9, |
36 | | - ) |
37 | | -) |
38 | | - |
39 | | -fig.add_trace( |
40 | | - go.Barpolar( |
41 | | - r=moderate, |
42 | | - theta=directions, |
43 | | - name="Moderate (10-20 km/h)", |
44 | | - marker=dict(color="#FFD43B", line=dict(color="white", width=2)), |
45 | | - opacity=0.9, |
46 | | - ) |
47 | | -) |
48 | | - |
49 | | -fig.add_trace( |
50 | | - go.Barpolar( |
51 | | - r=strong, |
52 | | - theta=directions, |
53 | | - name="Strong (20+ km/h)", |
54 | | - marker=dict(color="#4ECDC4", line=dict(color="white", width=2)), |
55 | | - opacity=0.9, |
| 37 | +# Add stacked bars for each wind speed category |
| 38 | +for data, label, color in zip([light, moderate, strong], wind_speed_labels, OKABE_ITO, strict=True): |
| 39 | + fig.add_trace( |
| 40 | + go.Barpolar( |
| 41 | + r=data, |
| 42 | + theta=directions, |
| 43 | + name=label, |
| 44 | + marker=dict(color=color, line=dict(color=PAGE_BG, width=2)), |
| 45 | + hovertemplate="<b>%{theta}</b><br>%{customdata}<br>Frequency: %{r}<extra></extra>", |
| 46 | + customdata=[label] * len(directions), |
| 47 | + opacity=0.9, |
| 48 | + ) |
56 | 49 | ) |
57 | | -) |
58 | 50 |
|
59 | | -# Update layout for polar chart |
| 51 | +# Update layout for polar chart with theme-adaptive styling |
60 | 52 | fig.update_layout( |
61 | | - title=dict(text="polar-bar · plotly · pyplots.ai", font=dict(size=32, color="#333"), x=0.5, y=0.95), |
| 53 | + title=dict(text="polar-bar · plotly · anyplot.ai", font=dict(size=28, color=INK), x=0.5, xanchor="center"), |
| 54 | + paper_bgcolor=PAGE_BG, |
| 55 | + plot_bgcolor=PAGE_BG, |
62 | 56 | polar=dict( |
63 | 57 | barmode="stack", |
64 | 58 | radialaxis=dict( |
65 | 59 | visible=True, |
66 | 60 | range=[0, max(light + moderate + strong) + 3], |
67 | | - tickfont=dict(size=18), |
68 | | - tickangle=45, |
69 | | - gridcolor="rgba(0,0,0,0.2)", |
70 | | - linecolor="rgba(0,0,0,0.3)", |
71 | | - title=dict(text="Frequency (%)", font=dict(size=18)), |
| 61 | + tickfont=dict(size=18, color=INK_SOFT), |
| 62 | + gridcolor=GRID, |
| 63 | + linecolor=INK_SOFT, |
| 64 | + title=dict(text="Frequency", font=dict(size=20, color=INK)), |
72 | 65 | ), |
73 | 66 | angularaxis=dict( |
74 | | - tickfont=dict(size=22, color="#333"), |
75 | | - direction="clockwise", |
76 | | - rotation=90, # N at top |
77 | | - gridcolor="rgba(0,0,0,0.15)", |
78 | | - linecolor="rgba(0,0,0,0.3)", |
| 67 | + tickfont=dict(size=22, color=INK), direction="clockwise", rotation=90, gridcolor=GRID, linecolor=INK_SOFT |
79 | 68 | ), |
80 | | - bgcolor="rgba(255,255,255,0.95)", |
81 | 69 | ), |
82 | 70 | legend=dict( |
83 | | - font=dict(size=20), |
84 | | - x=0.85, |
85 | | - y=0.95, |
86 | | - bgcolor="rgba(255,255,255,0.9)", |
87 | | - bordercolor="rgba(0,0,0,0.2)", |
88 | | - borderwidth=1, |
| 71 | + font=dict(size=18, color=INK_SOFT), x=0.85, y=0.95, bgcolor=ELEVATED_BG, bordercolor=INK_SOFT, borderwidth=1 |
89 | 72 | ), |
90 | | - template="plotly_white", |
91 | | - margin=dict(l=80, r=180, t=120, b=80), |
| 73 | + margin=dict(l=100, r=200, t=120, b=100), |
92 | 74 | ) |
93 | 75 |
|
94 | | -# Save as PNG (4800 x 2700 px) |
95 | | -fig.write_image("plot.png", width=1600, height=900, scale=3) |
96 | | - |
97 | | -# Save interactive HTML |
98 | | -fig.write_html("plot.html", include_plotlyjs=True, full_html=True) |
| 76 | +# Save as PNG (4800 x 2700 px) and interactive HTML |
| 77 | +fig.write_image(f"plot-{THEME}.png", width=1600, height=900, scale=3) |
| 78 | +fig.write_html(f"plot-{THEME}.html", include_plotlyjs="cdn") |
0 commit comments