Skip to content

Commit 0122a38

Browse files
feat(bokeh): implement rose-basic (#5598)
## Implementation: `rose-basic` - python/bokeh Implements the **python/bokeh** version of `rose-basic`. **File:** `plots/rose-basic/implementations/python/bokeh.py` **Parent Issue:** #1003 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/25151817338)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent 23695c3 commit 0122a38

2 files changed

Lines changed: 226 additions & 181 deletions

File tree

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,60 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
rose-basic: Basic Rose Chart
3-
Library: bokeh 3.8.1 | Python 3.13.11
4-
Quality: 92/100 | Created: 2025-12-23
3+
Library: bokeh 3.9.0 | Python 3.13.13
4+
Quality: 84/100 | Updated: 2026-04-30
55
"""
66

7+
import os
8+
import sys
9+
10+
11+
# Remove this script's directory from sys.path to prevent bokeh.py from
12+
# shadowing the installed bokeh package when Python adds the script dir.
13+
_script_dir = os.path.dirname(os.path.abspath(__file__))
14+
sys.path = [p for p in sys.path if os.path.abspath(p or ".") != _script_dir]
15+
716
import numpy as np
817
from bokeh.io import export_png, output_file, save
918
from bokeh.models import ColumnDataSource
1019
from bokeh.plotting import figure
1120

1221

22+
# Theme tokens
23+
THEME = os.getenv("ANYPLOT_THEME", "light")
24+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
25+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
26+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
27+
BRAND = "#009E73"
28+
1329
# Data - Monthly rainfall (mm) showing seasonal patterns
14-
np.random.seed(42)
1530
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
1631
values = [85, 70, 65, 45, 30, 20, 15, 25, 40, 60, 75, 90]
17-
32+
max_val = max(values)
1833
n = len(months)
34+
angle_width = 2 * np.pi / n
1935

2036
# Calculate wedge angles (equal slices, starting from top/north)
21-
angle_width = 2 * np.pi / n
2237
start_angles = np.array([np.pi / 2 - angle_width / 2 - i * angle_width for i in range(n)])
2338
end_angles = start_angles - angle_width
39+
center_angles = (start_angles + end_angles) / 2
2440

25-
# Normalize values to radius (max value = 1.0 for full radius)
26-
max_val = max(values)
41+
# Normalize values to radius (max value = 1.0)
2742
radii = [v / max_val for v in values]
2843

29-
# Colors - gradient from Python Yellow to Python Blue based on value
30-
colors = ["#306998" if v >= 60 else "#4A89B8" if v >= 40 else "#7AB3D8" if v >= 25 else "#FFD43B" for v in values]
44+
# Brand green with alpha varying by rainfall intensity
45+
alphas = [0.35 + 0.65 * (v / max_val) for v in values]
3146

3247
source = ColumnDataSource(
33-
data={
34-
"start_angle": start_angles,
35-
"end_angle": end_angles,
36-
"radius": radii,
37-
"months": months,
38-
"values": values,
39-
"colors": colors,
40-
}
48+
data={"start_angle": start_angles, "end_angle": end_angles, "radius": radii, "alphas": alphas}
4149
)
4250

43-
# Create figure with matching x/y ranges for circular shape
51+
# Create figure
4452
p = figure(
4553
width=4800,
4654
height=2700,
47-
title="Monthly Rainfall · rose-basic · bokeh · pyplots.ai",
48-
x_range=(-1.3, 1.3),
49-
y_range=(-1.2, 1.1),
55+
title="Monthly Rainfall · rose-basic · bokeh · anyplot.ai",
56+
x_range=(-1.5, 1.5),
57+
y_range=(-1.3, 1.35),
5058
tools="",
5159
toolbar_location=None,
5260
)
@@ -59,56 +67,56 @@
5967
start_angle="end_angle",
6068
end_angle="start_angle",
6169
source=source,
62-
fill_color="colors",
63-
fill_alpha=0.8,
64-
line_color="white",
70+
fill_color=BRAND,
71+
fill_alpha="alphas",
72+
line_color=PAGE_BG,
6573
line_width=2,
6674
)
6775

68-
# Add radial gridlines (concentric circles)
76+
# Radial gridlines (concentric circles)
77+
theta = np.linspace(0, 2 * np.pi, 200)
6978
for r in [0.25, 0.5, 0.75, 1.0]:
70-
theta = np.linspace(0, 2 * np.pi, 100)
71-
p.line(r * np.cos(theta), r * np.sin(theta), line_color="gray", line_alpha=0.3, line_width=1, line_dash="dashed")
79+
p.line(r * np.cos(theta), r * np.sin(theta), line_color=INK, line_alpha=0.22, line_width=1.5, line_dash="dashed")
7280

73-
# Add radial lines from center
81+
# Radial divider lines from center
7482
for i in range(n):
7583
angle = np.pi / 2 - i * angle_width
76-
p.line([0, 1.05 * np.cos(angle)], [0, 1.05 * np.sin(angle)], line_color="gray", line_alpha=0.2, line_width=1)
84+
p.line([0, 1.05 * np.cos(angle)], [0, 1.05 * np.sin(angle)], line_color=INK, line_alpha=0.18, line_width=1)
7785

78-
# Add month labels around the outside
79-
label_radius = 1.12
86+
# Month labels centered in each wedge
87+
label_radius = 1.15
8088
for i, month in enumerate(months):
81-
angle = np.pi / 2 - i * angle_width
82-
x = label_radius * np.cos(angle)
83-
y = label_radius * np.sin(angle)
89+
angle = center_angles[i]
8490
p.text(
85-
x=[x],
86-
y=[y],
91+
x=[label_radius * np.cos(angle)],
92+
y=[label_radius * np.sin(angle)],
8793
text=[month],
8894
text_align="center",
8995
text_baseline="middle",
90-
text_font_size="18pt",
91-
text_color="#333333",
96+
text_font_size="20pt",
97+
text_color=INK,
9298
)
9399

94-
# Add value scale labels on right side
95-
p.text(x=[1.05], y=[0.25], text=["25%"], text_font_size="14pt", text_color="gray", text_align="left")
96-
p.text(x=[1.05], y=[0.5], text=["50%"], text_font_size="14pt", text_color="gray", text_align="left")
97-
p.text(x=[1.05], y=[0.75], text=["75%"], text_font_size="14pt", text_color="gray", text_align="left")
98-
p.text(x=[1.05], y=[1.0], text=["100%"], text_font_size="14pt", text_color="gray", text_align="left")
100+
# Rainfall scale labels (actual mm values, right side)
101+
for r in [0.25, 0.5, 0.75, 1.0]:
102+
val_label = f"{int(r * max_val + 0.5)} mm"
103+
p.text(x=[1.2], y=[r], text=[val_label], text_font_size="15pt", text_color=INK_SOFT, text_align="left")
99104

100-
# Styling
105+
# Title and chrome
101106
p.title.text_font_size = "28pt"
102107
p.title.align = "center"
103-
p.background_fill_color = "white"
104-
p.border_fill_color = "white"
108+
p.title.text_color = INK
109+
p.title.text_font_style = "normal"
110+
111+
p.background_fill_color = PAGE_BG
112+
p.border_fill_color = PAGE_BG
105113
p.outline_line_color = None
106114

107115
# Hide axes (not needed for rose chart)
108116
p.axis.visible = False
109117
p.grid.visible = False
110118

111-
# Save as PNG and HTML
112-
export_png(p, filename="plot.png")
113-
output_file("plot.html")
119+
# Save
120+
export_png(p, filename=f"plot-{THEME}.png")
121+
output_file(f"plot-{THEME}.html")
114122
save(p)

0 commit comments

Comments
 (0)