Skip to content
150 changes: 95 additions & 55 deletions plots/pie-basic/implementations/highcharts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
""" pyplots.ai
pie-basic: Basic Pie Chart
Library: highcharts unknown | Python 3.13.11
Quality: 92/100 | Created: 2025-12-23
Library: highcharts 1.10.3 | Python 3.14.0
Quality: 90/100 | Created: 2025-12-23
"""

import tempfile
Expand All @@ -12,89 +12,130 @@
from highcharts_core.chart import Chart
from highcharts_core.options import HighchartsOptions
from highcharts_core.options.series.pie import PieSeries
from PIL import Image
from selenium import webdriver
from selenium.webdriver.chrome.options import Options


# Data - Market share distribution (5 categories, realistic business context)
categories = ["Product A", "Product B", "Product C", "Product D", "Product E"]
values = [35, 25, 20, 12, 8]
# Data — Cloud infrastructure market share (5 categories, realistic business context)
categories = ["AWS", "Azure", "Google Cloud", "Alibaba", "Others"]
values = [31, 25, 11, 4, 29]

# Colorblind-safe colors (Python Blue first, then complementary)
colors = ["#306998", "#FFD43B", "#9467BD", "#17BECF", "#8C564B"]
# Colorblind-safe palette (Python Blue first, then complementary)
# Replaced cyan (#17BECF) with softer teal (#2CA089) for better palette harmony
colors = ["#306998", "#FFD43B", "#E07B54", "#2CA089", "#9467BD"]

# Create chart with container specified
# Compute top-3 share for subtitle storytelling
top3_share = sum(values[:3])

# Chart
chart = Chart(container="container")
chart.options = HighchartsOptions()

# Chart configuration for 3600x3600 square (ideal for pie charts)
chart.options.chart = {
"type": "pie",
"width": 3600,
"height": 3600,
"width": 4800,
"height": 2700,
"backgroundColor": "#ffffff",
"spacingTop": 80,
"spacingBottom": 80,
"spacingLeft": 80,
"spacingRight": 80,
"spacingTop": 30,
"spacingBottom": 25,
"spacingLeft": 60,
"spacingRight": 60,
"style": {"fontFamily": "'Segoe UI', Tahoma, Geneva, Verdana, sans-serif"},
}

# Title
chart.options.title = {
"text": "pie-basic · highcharts · pyplots.ai",
"style": {"fontSize": "48px", "fontWeight": "bold"},
"margin": 40,
"text": "Cloud Infrastructure Market Share · pie-basic · highcharts · pyplots.ai",
"style": {"fontSize": "50px", "fontWeight": "bold", "color": "#1a1a2e"},
"margin": 10,
}

# Subtitle with storytelling context and insight callout
chart.options.subtitle = {
"text": (
f"Global cloud spending by provider, 2024 \u2014 Top 3 providers control {top3_share}% of the market"
'<br><span style="font-style: italic; color: #1a1a2e; font-weight: 600;">'
"AWS leads with nearly \u2153 of global cloud revenue</span>"
),
"useHTML": True,
"style": {"fontSize": "34px", "color": "#555555", "fontWeight": "normal", "textAlign": "center"},
}

# Colors
chart.options.colors = colors

# Plot options for pie with percentage labels and legend
# Credits
chart.options.credits = {"enabled": False}

# Plot options with enhanced visual refinement
chart.options.plot_options = {
"pie": {
"allowPointSelect": True,
"cursor": "pointer",
"borderWidth": 2,
"borderColor": "#ffffff",
"shadow": {"color": "rgba(0,0,0,0.12)", "offsetX": 3, "offsetY": 3, "width": 8},
"dataLabels": {
"enabled": True,
"format": "<b>{point.name}</b>: {point.percentage:.1f}%",
"style": {"fontSize": "32px", "textOutline": "none"},
"distance": 40,
"format": "<b>{point.name}</b><br>{point.percentage:.1f}%",
"style": {"fontSize": "38px", "textOutline": "none", "fontWeight": "normal", "color": "#333333"},
"distance": 55,
"connectorWidth": 2,
"connectorColor": "#999999",
"softConnector": True,
"connectorShape": "crookedLine",
},
"showInLegend": True,
"slicedOffset": 25,
"size": "70%",
"center": ["40%", "50%"],
"slicedOffset": 40,
"size": "75%",
"center": ["50%", "55%"],
"startAngle": -45,
"innerSize": "0%",
"states": {"hover": {"halo": {"size": 15, "opacity": 0.25}}, "inactive": {"opacity": 0.5}},
}
}

# Legend on the right side
# Legend — bottom horizontal
chart.options.legend = {
"enabled": True,
"align": "right",
"verticalAlign": "middle",
"layout": "vertical",
"itemStyle": {"fontSize": "36px", "fontWeight": "normal"},
"itemMarginTop": 20,
"itemMarginBottom": 20,
"symbolRadius": 10,
"align": "center",
"verticalAlign": "bottom",
"layout": "horizontal",
"itemStyle": {"fontSize": "36px", "fontWeight": "normal", "color": "#444444"},
"itemHoverStyle": {"color": "#1a1a2e"},
"symbolRadius": 8,
"symbolHeight": 20,
"symbolWidth": 20,
"x": -80,
"margin": 8,
"padding": 8,
}

# Tooltip
chart.options.tooltip = {
"pointFormat": "<b>{point.percentage:.1f}%</b> market share",
"style": {"fontSize": "28px"},
"backgroundColor": "rgba(255,255,255,0.95)",
"borderColor": "#cccccc",
"borderRadius": 8,
"shadow": {"color": "rgba(0,0,0,0.08)", "offsetX": 1, "offsetY": 1, "width": 3},
}

# Create pie series with data - first slice (largest) is exploded for emphasis
# Series — largest slice (AWS) exploded for emphasis
series = PieSeries()
series.name = "Market Share"
series.data = [
{"name": cat, "y": val, "sliced": i == 0, "selected": i == 0}
for i, (cat, val) in enumerate(zip(categories, values, strict=True))
]

series_data = []
for i, (cat, val) in enumerate(zip(categories, values, strict=True)):
point = {"name": cat, "y": val, "sliced": i == 0, "selected": i == 0}
if i == 0:
point["borderWidth"] = 3
point["borderColor"] = "#1e4060"
series_data.append(point)

series.data = series_data
chart.add_series(series)

# Download Highcharts JS for inline embedding (required for headless Chrome)
# Download Highcharts JS for inline embedding
highcharts_url = "https://code.highcharts.com/highcharts.js"
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
highcharts_js = response.read().decode("utf-8")
Expand All @@ -108,38 +149,37 @@
<script>{highcharts_js}</script>
</head>
<body style="margin:0;">
<div id="container" style="width: 3600px; height: 3600px;"></div>
<div id="container" style="width: 4800px; height: 2700px;"></div>
<script>{html_str}</script>
</body>
</html>"""

# Write temp HTML and take screenshot
# Write temp HTML and save interactive version
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
f.write(html_content)
temp_path = f.name

# Also save the HTML for interactive version
with open("plot.html", "w", encoding="utf-8") as f:
f.write(html_content)

# Setup Chrome for screenshot
# Screenshot via headless Chrome
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument("--window-size=3600,3800")
chrome_options.add_argument("--window-size=4800,2700")

driver = webdriver.Chrome(options=chrome_options)

# Adjust window to get exact 4800x2700 viewport (compensate for browser chrome)
inner_h = driver.execute_script("return window.innerHeight")
outer_h = driver.get_window_size()["height"]
driver.set_window_size(4800, 2700 + (outer_h - inner_h))

driver.get(f"file://{temp_path}")
time.sleep(5) # Wait for chart to render
driver.save_screenshot("plot_raw.png")
time.sleep(5)
driver.save_screenshot("plot.png")
driver.quit()

# Crop to exact 3600x3600 dimensions
img = Image.open("plot_raw.png")
img_cropped = img.crop((0, 0, 3600, 3600))
img_cropped.save("plot.png")
Path("plot_raw.png").unlink()

Path(temp_path).unlink() # Clean up temp file
Path(temp_path).unlink()
Loading