Skip to content

Commit f385917

Browse files
feat(highcharts): implement subplot-mosaic (#3036)
## Implementation: `subplot-mosaic` - highcharts Implements the **highcharts** version of `subplot-mosaic`. **File:** `plots/subplot-mosaic/implementations/highcharts.py` **Parent Issue:** #3002 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20617512681)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 52a1e6f commit f385917

2 files changed

Lines changed: 393 additions & 0 deletions

File tree

Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
""" pyplots.ai
2+
subplot-mosaic: Mosaic Subplot Layout with Varying Sizes
3+
Library: highcharts unknown | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-31
5+
"""
6+
7+
import json
8+
import tempfile
9+
import time
10+
import urllib.request
11+
from pathlib import Path
12+
13+
import numpy as np
14+
from selenium import webdriver
15+
from selenium.webdriver.chrome.options import Options
16+
17+
18+
# Data
19+
np.random.seed(42)
20+
21+
# Panel A (large left): Time series - monthly sales data
22+
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23+
sales = [42, 48, 52, 45, 58, 62, 55, 68, 72, 65, 78, 85]
24+
25+
# Panel B (top right): Bar chart - regional performance
26+
regions = ["North", "South", "East", "West"]
27+
regional_sales = [245, 312, 189, 276]
28+
29+
# Panel C (bottom wide): Scatter - product analysis
30+
product_count = 80
31+
product_price = np.random.uniform(10, 100, product_count)
32+
product_revenue = product_price * np.random.uniform(50, 200, product_count) + np.random.normal(0, 500, product_count)
33+
product_data = [[round(float(x), 2), round(float(y), 2)] for x, y in zip(product_price, product_revenue, strict=False)]
34+
35+
# Download Highcharts JS
36+
highcharts_url = "https://code.highcharts.com/highcharts.js"
37+
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
38+
highcharts_js = response.read().decode("utf-8")
39+
40+
# Convert data to JSON strings for embedding in JavaScript
41+
months_json = json.dumps(months)
42+
sales_json = json.dumps(sales)
43+
regions_json = json.dumps(regions)
44+
regional_sales_json = json.dumps(regional_sales)
45+
product_data_json = json.dumps(product_data)
46+
47+
# Build HTML with mosaic layout
48+
# Layout: "AAB" / "AAB" / "CCC" - A is large left (2x2), B is small right (1x2), C is wide bottom (3x1)
49+
html_content = f"""<!DOCTYPE html>
50+
<html>
51+
<head>
52+
<meta charset="utf-8">
53+
<script>{highcharts_js}</script>
54+
<style>
55+
body {{
56+
margin: 0;
57+
padding: 40px;
58+
background-color: #ffffff;
59+
font-family: Arial, sans-serif;
60+
}}
61+
.main-title {{
62+
text-align: center;
63+
font-size: 48px;
64+
font-weight: bold;
65+
color: #333333;
66+
margin-bottom: 30px;
67+
font-family: Arial, sans-serif;
68+
}}
69+
.mosaic-container {{
70+
display: grid;
71+
grid-template-columns: 2fr 2fr 1fr;
72+
grid-template-rows: 1fr 1fr 1fr;
73+
gap: 30px;
74+
width: 4680px;
75+
height: 2450px;
76+
}}
77+
#panelA {{
78+
grid-column: 1 / 3;
79+
grid-row: 1 / 3;
80+
background-color: #fafafa;
81+
border-radius: 8px;
82+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
83+
}}
84+
#panelB {{
85+
grid-column: 3 / 4;
86+
grid-row: 1 / 3;
87+
background-color: #fafafa;
88+
border-radius: 8px;
89+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
90+
}}
91+
#panelC {{
92+
grid-column: 1 / 4;
93+
grid-row: 3 / 4;
94+
background-color: #fafafa;
95+
border-radius: 8px;
96+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
97+
}}
98+
</style>
99+
</head>
100+
<body>
101+
<div class="main-title">subplot-mosaic · highcharts · pyplots.ai</div>
102+
<div class="mosaic-container">
103+
<div id="panelA"></div>
104+
<div id="panelB"></div>
105+
<div id="panelC"></div>
106+
</div>
107+
<script>
108+
// Panel A: Time series line chart (large left panel)
109+
Highcharts.chart('panelA', {{
110+
chart: {{
111+
type: 'line',
112+
backgroundColor: '#fafafa'
113+
}},
114+
title: {{
115+
text: 'Monthly Sales Trend',
116+
style: {{
117+
color: '#333333',
118+
fontSize: '36px',
119+
fontWeight: 'bold',
120+
fontFamily: 'Arial, sans-serif'
121+
}}
122+
}},
123+
xAxis: {{
124+
categories: {months_json},
125+
title: {{
126+
text: 'Month',
127+
style: {{
128+
color: '#555555',
129+
fontSize: '24px',
130+
fontFamily: 'Arial, sans-serif'
131+
}}
132+
}},
133+
labels: {{
134+
style: {{
135+
color: '#666666',
136+
fontSize: '20px',
137+
fontFamily: 'Arial, sans-serif'
138+
}}
139+
}}
140+
}},
141+
yAxis: {{
142+
title: {{
143+
text: 'Sales (Units)',
144+
style: {{
145+
color: '#555555',
146+
fontSize: '24px',
147+
fontFamily: 'Arial, sans-serif'
148+
}}
149+
}},
150+
labels: {{
151+
style: {{
152+
color: '#666666',
153+
fontSize: '20px',
154+
fontFamily: 'Arial, sans-serif'
155+
}}
156+
}},
157+
gridLineColor: '#e0e0e0',
158+
gridLineWidth: 1
159+
}},
160+
legend: {{
161+
enabled: true,
162+
itemStyle: {{
163+
color: '#555555',
164+
fontSize: '20px',
165+
fontFamily: 'Arial, sans-serif'
166+
}}
167+
}},
168+
plotOptions: {{
169+
line: {{
170+
lineWidth: 4,
171+
marker: {{
172+
enabled: true,
173+
radius: 8,
174+
fillColor: '#306998'
175+
}},
176+
color: '#306998'
177+
}}
178+
}},
179+
series: [{{
180+
name: 'Sales',
181+
data: {sales_json}
182+
}}],
183+
credits: {{ enabled: false }}
184+
}});
185+
186+
// Panel B: Bar chart (small right panel)
187+
Highcharts.chart('panelB', {{
188+
chart: {{
189+
type: 'bar',
190+
backgroundColor: '#fafafa'
191+
}},
192+
title: {{
193+
text: 'Regional Performance',
194+
style: {{
195+
color: '#333333',
196+
fontSize: '36px',
197+
fontWeight: 'bold',
198+
fontFamily: 'Arial, sans-serif'
199+
}}
200+
}},
201+
xAxis: {{
202+
categories: {regions_json},
203+
title: {{
204+
text: null
205+
}},
206+
labels: {{
207+
style: {{
208+
color: '#666666',
209+
fontSize: '20px',
210+
fontFamily: 'Arial, sans-serif'
211+
}}
212+
}}
213+
}},
214+
yAxis: {{
215+
title: {{
216+
text: 'Sales (Units)',
217+
style: {{
218+
color: '#555555',
219+
fontSize: '24px',
220+
fontFamily: 'Arial, sans-serif'
221+
}}
222+
}},
223+
labels: {{
224+
style: {{
225+
color: '#666666',
226+
fontSize: '20px',
227+
fontFamily: 'Arial, sans-serif'
228+
}}
229+
}},
230+
gridLineColor: '#e0e0e0',
231+
gridLineWidth: 1
232+
}},
233+
legend: {{
234+
enabled: false
235+
}},
236+
plotOptions: {{
237+
bar: {{
238+
colorByPoint: true,
239+
colors: ['#306998', '#FFD43B', '#9467BD', '#17BECF'],
240+
borderRadius: 4,
241+
dataLabels: {{
242+
enabled: true,
243+
style: {{
244+
fontSize: '18px',
245+
fontWeight: 'bold',
246+
color: '#333333'
247+
}}
248+
}}
249+
}}
250+
}},
251+
series: [{{
252+
name: 'Sales',
253+
data: {regional_sales_json}
254+
}}],
255+
credits: {{ enabled: false }}
256+
}});
257+
258+
// Panel C: Scatter chart (wide bottom panel)
259+
Highcharts.chart('panelC', {{
260+
chart: {{
261+
type: 'scatter',
262+
backgroundColor: '#fafafa'
263+
}},
264+
title: {{
265+
text: 'Product Price vs Revenue Analysis',
266+
style: {{
267+
color: '#333333',
268+
fontSize: '36px',
269+
fontWeight: 'bold',
270+
fontFamily: 'Arial, sans-serif'
271+
}}
272+
}},
273+
xAxis: {{
274+
title: {{
275+
text: 'Product Price ($)',
276+
style: {{
277+
color: '#555555',
278+
fontSize: '24px',
279+
fontFamily: 'Arial, sans-serif'
280+
}}
281+
}},
282+
labels: {{
283+
style: {{
284+
color: '#666666',
285+
fontSize: '20px',
286+
fontFamily: 'Arial, sans-serif'
287+
}}
288+
}},
289+
gridLineColor: '#e0e0e0',
290+
gridLineWidth: 1
291+
}},
292+
yAxis: {{
293+
title: {{
294+
text: 'Revenue ($)',
295+
style: {{
296+
color: '#555555',
297+
fontSize: '24px',
298+
fontFamily: 'Arial, sans-serif'
299+
}}
300+
}},
301+
labels: {{
302+
style: {{
303+
color: '#666666',
304+
fontSize: '20px',
305+
fontFamily: 'Arial, sans-serif'
306+
}}
307+
}},
308+
gridLineColor: '#e0e0e0',
309+
gridLineWidth: 1
310+
}},
311+
legend: {{
312+
enabled: true,
313+
itemStyle: {{
314+
color: '#555555',
315+
fontSize: '20px',
316+
fontFamily: 'Arial, sans-serif'
317+
}}
318+
}},
319+
plotOptions: {{
320+
scatter: {{
321+
marker: {{
322+
radius: 10,
323+
symbol: 'circle',
324+
fillColor: 'rgba(48, 105, 152, 0.7)',
325+
lineWidth: 2,
326+
lineColor: '#306998'
327+
}}
328+
}}
329+
}},
330+
series: [{{
331+
name: 'Products',
332+
data: {product_data_json}
333+
}}],
334+
credits: {{ enabled: false }}
335+
}});
336+
</script>
337+
</body>
338+
</html>"""
339+
340+
# Write temp HTML
341+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
342+
f.write(html_content)
343+
temp_path = f.name
344+
345+
# Capture screenshot with Selenium
346+
chrome_options = Options()
347+
chrome_options.add_argument("--headless")
348+
chrome_options.add_argument("--no-sandbox")
349+
chrome_options.add_argument("--disable-dev-shm-usage")
350+
chrome_options.add_argument("--disable-gpu")
351+
chrome_options.add_argument("--window-size=4800,2700")
352+
353+
driver = webdriver.Chrome(options=chrome_options)
354+
driver.get(f"file://{temp_path}")
355+
time.sleep(8) # Wait for charts to render
356+
driver.save_screenshot("plot.png")
357+
358+
# Also save HTML for interactive version
359+
with open("plot.html", "w", encoding="utf-8") as f:
360+
# Replace inline JS with CDN link for HTML file
361+
html_for_save = html_content.replace(
362+
f"<script>{highcharts_js}</script>", '<script src="https://code.highcharts.com/highcharts.js"></script>'
363+
)
364+
f.write(html_for_save)
365+
366+
driver.quit()
367+
Path(temp_path).unlink()
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
library: highcharts
2+
specification_id: subplot-mosaic
3+
created: '2025-12-31T10:57:50Z'
4+
updated: '2025-12-31T11:08:27Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20617512681
7+
issue: 3002
8+
python_version: 3.13.11
9+
library_version: unknown
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/subplot-mosaic/highcharts/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/subplot-mosaic/highcharts/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/subplot-mosaic/highcharts/plot.html
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent mosaic layout implementation using CSS Grid with AAB/AAB/CCC pattern
17+
- Clean visual hierarchy with larger panel A dominating, supporting panels B and
18+
C
19+
- Good use of colorblind-safe palette across all panels
20+
- Descriptive axis labels with units
21+
- Realistic business dashboard scenario
22+
- Proper title format and overall professional appearance
23+
weaknesses:
24+
- Legends in Panel A and C are quite small and could be larger for better visibility
25+
- Could use Highcharts built-in dashboard/synchronized chart features for more native
26+
implementation

0 commit comments

Comments
 (0)