Skip to content

Commit e7d0753

Browse files
feat(bokeh): implement contour-filled (#2516)
## Implementation: `contour-filled` - bokeh Implements the **bokeh** version of `contour-filled`. **File:** `plots/contour-filled/implementations/bokeh.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20585401172)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 8d5581d commit e7d0753

2 files changed

Lines changed: 158 additions & 0 deletions

File tree

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
""" pyplots.ai
2+
contour-filled: Filled Contour Plot
3+
Library: bokeh 3.8.1 | Python 3.13.11
4+
Quality: 90/100 | Created: 2025-12-30
5+
"""
6+
7+
import numpy as np
8+
from bokeh.io import export_png, output_file, save
9+
from bokeh.models import BasicTicker, ColorBar, HoverTool, LinearColorMapper
10+
from bokeh.palettes import Viridis256
11+
from bokeh.plotting import figure
12+
from contourpy import contour_generator
13+
14+
15+
# Data - Terrain elevation surface with multiple peaks
16+
np.random.seed(42)
17+
x = np.linspace(-3, 3, 80)
18+
y = np.linspace(-3, 3, 80)
19+
X, Y = np.meshgrid(x, y)
20+
21+
# Create surface with multiple Gaussian peaks (terrain elevation in meters)
22+
Z = (
23+
1.5 * np.exp(-((X - 1) ** 2 + (Y - 1) ** 2))
24+
+ 2.0 * np.exp(-((X + 1.5) ** 2 + (Y + 0.5) ** 2) / 1.5)
25+
+ 1.0 * np.exp(-((X - 0.5) ** 2 + (Y + 1.5) ** 2) / 0.8)
26+
- 0.5 * np.exp(-((X + 0.5) ** 2 + (Y - 1.5) ** 2) / 0.5)
27+
)
28+
29+
# Scale to realistic elevation values (0-2000 meters)
30+
Z = (Z - Z.min()) / (Z.max() - Z.min()) * 2000
31+
32+
# Create figure at 4800x2700 px with interactive tools
33+
p = figure(
34+
width=4800,
35+
height=2700,
36+
title="contour-filled · bokeh · pyplots.ai",
37+
x_range=(x.min(), x.max()),
38+
y_range=(y.min(), y.max()),
39+
tools="pan,wheel_zoom,box_zoom,reset,save",
40+
)
41+
42+
# Explicitly set axis labels after figure creation
43+
p.xaxis.axis_label = "Distance East (km)"
44+
p.yaxis.axis_label = "Distance North (km)"
45+
46+
# Color mapper for the filled surface
47+
color_mapper = LinearColorMapper(palette=Viridis256, low=Z.min(), high=Z.max())
48+
49+
# Draw the filled surface using image
50+
p.image(image=[Z], x=x.min(), y=y.min(), dw=x.max() - x.min(), dh=y.max() - y.min(), color_mapper=color_mapper)
51+
52+
# Overlay contour lines at specific levels for precise identification
53+
n_contour_lines = 12
54+
contour_levels = np.linspace(Z.min(), Z.max(), n_contour_lines + 2)[1:-1]
55+
56+
# Create contour generator
57+
cont_gen = contour_generator(x=X, y=Y, z=Z)
58+
59+
# Draw contour lines with high contrast (white with dark outline effect)
60+
for level in contour_levels:
61+
lines = cont_gen.lines(level)
62+
for line in lines:
63+
# Draw white line for visibility on dark backgrounds
64+
p.line(line[:, 0], line[:, 1], line_width=4, color="white", alpha=0.9)
65+
# Draw thinner dark line on top for contrast on light backgrounds
66+
p.line(line[:, 0], line[:, 1], line_width=1.5, color="#1a1a1a", alpha=0.8)
67+
68+
# Add colorbar with terrain context
69+
color_bar = ColorBar(
70+
color_mapper=color_mapper,
71+
ticker=BasicTicker(desired_num_ticks=10),
72+
label_standoff=25,
73+
title="Terrain Elevation (m)",
74+
title_text_font_size="22pt",
75+
title_standoff=20,
76+
major_label_text_font_size="18pt",
77+
width=50,
78+
padding=40,
79+
)
80+
p.add_layout(color_bar, "right")
81+
82+
# Style text for large canvas
83+
p.title.text_font_size = "28pt"
84+
p.xaxis.axis_label_text_font_size = "22pt"
85+
p.yaxis.axis_label_text_font_size = "22pt"
86+
p.xaxis.major_label_text_font_size = "18pt"
87+
p.yaxis.major_label_text_font_size = "18pt"
88+
89+
# Grid styling - subtle overlay grid (reduced alpha per review feedback)
90+
p.xgrid.grid_line_color = "white"
91+
p.ygrid.grid_line_color = "white"
92+
p.xgrid.grid_line_alpha = 0.3
93+
p.ygrid.grid_line_alpha = 0.3
94+
p.xgrid.grid_line_width = 2
95+
p.ygrid.grid_line_width = 2
96+
p.xgrid.grid_line_dash = [8, 4]
97+
p.ygrid.grid_line_dash = [8, 4]
98+
p.xgrid.level = "overlay"
99+
p.ygrid.level = "overlay"
100+
101+
# Add hover tool for interactive elevation display
102+
# Create a grid of hover points for better interactivity
103+
hover_x, hover_y, hover_z = [], [], []
104+
step = 4 # Sample every 4th point for responsive hovering
105+
for i in range(0, len(x), step):
106+
for j in range(0, len(y), step):
107+
hover_x.append(x[i])
108+
hover_y.append(y[j])
109+
hover_z.append(round(Z[j, i], 1))
110+
111+
# Add invisible scatter points for hover detection
112+
hover_renderer = p.scatter(hover_x, hover_y, size=30, fill_alpha=0, line_alpha=0, name="hover_points")
113+
hover_renderer.data_source.data["elevation"] = hover_z
114+
115+
# Configure hover tool to show elevation values
116+
hover = HoverTool(
117+
renderers=[hover_renderer],
118+
tooltips=[("Location", "(@x{0.0} km E, @y{0.0} km N)"), ("Elevation", "@elevation{0} m")],
119+
mode="mouse",
120+
)
121+
p.add_tools(hover)
122+
123+
# Background
124+
p.background_fill_color = None
125+
p.border_fill_color = None
126+
127+
# Save as PNG and HTML
128+
export_png(p, filename="plot.png")
129+
130+
# Also save as HTML for interactive viewing with hover tooltips
131+
output_file("plot.html")
132+
save(p)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
library: bokeh
2+
specification_id: contour-filled
3+
created: '2025-12-30T00:03:42Z'
4+
updated: '2025-12-30T00:36:43Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20585401172
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: 3.8.1
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/contour-filled/bokeh/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/contour-filled/bokeh/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/contour-filled/bokeh/plot.html
13+
quality_score: 90
14+
review:
15+
strengths:
16+
- Excellent use of Viridis colormap for colorblind accessibility and visual appeal
17+
- Smart contour line styling with white base + dark outline for visibility across
18+
all elevation levels
19+
- Well-implemented terrain elevation scenario with realistic 0-2000m scale
20+
- Clean integration of contourpy for precise contour line generation
21+
- Proper text sizing for 4800x2700 canvas (28pt title, 22pt labels, 18pt ticks)
22+
- HoverTool implementation provides interactivity in HTML output
23+
weaknesses:
24+
- Grid styling still somewhat prominent (alpha 0.3 with width 2); could be reduced
25+
further for cleaner contour visualization
26+
- Colorbar title is slightly small relative to other text elements

0 commit comments

Comments
 (0)