Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 62 additions & 75 deletions plots/scatter-text/implementations/python/highcharts.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
""" pyplots.ai
""" anyplot.ai
scatter-text: Scatter Plot with Text Labels Instead of Points
Library: highcharts unknown | Python 3.13.11
Quality: 91/100 | Created: 2026-01-09
Library: highcharts unknown | Python 3.13.13
Quality: 94/100 | Updated: 2026-05-17
"""

import os
import ssl
import tempfile
import time
import urllib.request
Expand All @@ -17,35 +19,46 @@
from selenium.webdriver.chrome.options import Options


# Theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)"

# Okabe-Ito palette
OKABE_ITO = [
"#009E73", # position 1 (brand green - ALWAYS first series)
"#D55E00", # position 2
"#0072B2", # position 3
"#CC79A7", # position 4
]

# Data - Simulated word embeddings after dimensionality reduction
np.random.seed(42)

# Create clusters of related words in 2D space
programming_words = ["Python", "JavaScript", "Java", "C++", "Ruby", "Rust", "Go", "Swift"]
data_words = ["Pandas", "NumPy", "TensorFlow", "PyTorch", "Scikit-learn", "Keras"]
web_words = ["React", "Vue", "Angular", "Django", "Flask", "Node.js"]
database_words = ["PostgreSQL", "MongoDB", "Redis", "MySQL", "SQLite", "Cassandra"]

# Create clustered positions - more compact to avoid clipping
# Create clustered positions
x_coords = []
y_coords = []

# Programming languages cluster (center-left)
for _ in programming_words:
x_coords.append(np.random.normal(-2.5, 0.7))
y_coords.append(np.random.normal(2, 0.7))

# Data science tools cluster (top-right)
for _ in data_words:
x_coords.append(np.random.normal(2.5, 0.6))
y_coords.append(np.random.normal(2.5, 0.6))

# Web frameworks cluster (bottom-right)
for _ in web_words:
x_coords.append(np.random.normal(2, 0.5))
y_coords.append(np.random.normal(-2, 0.5))

# Databases cluster (bottom-left)
for _ in database_words:
x_coords.append(np.random.normal(-2, 0.6))
y_coords.append(np.random.normal(-2, 0.6))
Expand All @@ -55,90 +68,79 @@

# Download Highcharts JS
highcharts_url = "https://code.highcharts.com/highcharts.js"
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
ssl_context = ssl._create_unverified_context()
request = urllib.request.Request(highcharts_url, headers={"User-Agent": "Mozilla/5.0", "Referer": "https://anyplot.ai"})
with urllib.request.urlopen(request, timeout=30, context=ssl_context) as response:
highcharts_js = response.read().decode("utf-8")

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

# Chart configuration with margins to prevent clipping
# Chart configuration
chart.options.chart = {
"type": "scatter",
"width": 4800,
"height": 2700,
"backgroundColor": "#ffffff",
"backgroundColor": PAGE_BG,
"marginLeft": 250,
"marginRight": 300,
"marginTop": 200,
"marginBottom": 300,
}

# Title - large font for 4800px canvas
# Title
chart.options.title = {
"text": "scatter-text · highcharts · pyplots.ai",
"style": {"fontSize": "72px", "fontWeight": "bold", "color": "#333333"},
}

# Subtitle
chart.options.subtitle = {
"text": "Tech Stack Word Embeddings (2D Projection)",
"style": {"fontSize": "48px", "color": "#666666"},
"text": "scatter-text · Python · highcharts · anyplot.ai",
"style": {"fontSize": "28px", "fontWeight": "500", "color": INK},
}

# Axes - scaled for large canvas
# Axes
chart.options.x_axis = {
"title": {"text": "Dimension 1", "style": {"fontSize": "48px", "color": "#333333"}},
"labels": {"style": {"fontSize": "36px", "color": "#333333"}},
"title": {"text": "Dimension 1", "style": {"fontSize": "22px", "color": INK}},
"labels": {"style": {"fontSize": "18px", "color": INK_SOFT}},
"gridLineWidth": 1,
"gridLineColor": "rgba(0,0,0,0.15)",
"lineWidth": 2,
"lineColor": "#333333",
"tickWidth": 2,
"tickLength": 15,
"gridLineColor": GRID,
"lineColor": INK_SOFT,
"tickColor": INK_SOFT,
}

chart.options.y_axis = {
"title": {"text": "Dimension 2", "style": {"fontSize": "48px", "color": "#333333"}},
"labels": {"style": {"fontSize": "36px", "color": "#333333"}},
"title": {"text": "Dimension 2", "style": {"fontSize": "22px", "color": INK}},
"labels": {"style": {"fontSize": "18px", "color": INK_SOFT}},
"gridLineWidth": 1,
"gridLineColor": "rgba(0,0,0,0.15)",
"lineWidth": 2,
"lineColor": "#333333",
"tickWidth": 2,
"tickLength": 15,
"gridLineColor": GRID,
"lineColor": INK_SOFT,
"tickColor": INK_SOFT,
}

# Legend - show categories at top right to not interfere with data
# Legend
chart.options.legend = {
"enabled": True,
"itemStyle": {"fontSize": "36px", "fontWeight": "normal"},
"itemStyle": {"fontSize": "18px", "color": INK_SOFT},
"layout": "vertical",
"align": "right",
"verticalAlign": "top",
"x": -50,
"y": 100,
"borderWidth": 1,
"borderColor": "#cccccc",
"backgroundColor": "#ffffff",
"borderColor": INK_SOFT,
"backgroundColor": ELEVATED_BG,
"padding": 20,
}

# Create data points with text labels using dataLabels
# Group by category for different colors
# Create data with text labels
categories = {
"Programming Languages": (programming_words, "#306998", 0),
"Data Science": (data_words, "#FFD43B", len(programming_words)),
"Web Frameworks": (web_words, "#9467BD", len(programming_words) + len(data_words)),
"Databases": (database_words, "#17BECF", len(programming_words) + len(data_words) + len(web_words)),
"Programming Languages": (programming_words, OKABE_ITO[0], 0),
"Data Science": (data_words, OKABE_ITO[1], len(programming_words)),
"Web Frameworks": (web_words, OKABE_ITO[2], len(programming_words) + len(data_words)),
"Databases": (database_words, OKABE_ITO[3], len(programming_words) + len(data_words) + len(web_words)),
}

# Add series for each category
for cat_name, (words, color, start_idx) in categories.items():
series = ScatterSeries()
series.name = cat_name

# Create data points with labels - large fonts for 4800px canvas
data_points = []
for i, word in enumerate(words):
idx = start_idx + i
Expand All @@ -150,24 +152,21 @@
"dataLabels": {
"enabled": True,
"format": word,
"style": {"fontSize": "42px", "fontWeight": "bold", "color": color, "textOutline": "3px white"},
"allowOverlap": True,
"style": {"fontSize": "24px", "fontWeight": "500", "color": color, "textOutline": f"2px {PAGE_BG}"},
"allowOverlap": False,
},
}
)

series.data = data_points

# Hide markers - text labels are the visual elements
series.marker = {"enabled": False}

series.color = color

chart.add_series(series)

# Plot options for data labels
# Plot options
chart.options.plot_options = {
"scatter": {"marker": {"enabled": False}, "dataLabels": {"enabled": True, "style": {"fontSize": "42px"}}}
"scatter": {"marker": {"enabled": False}, "dataLabels": {"enabled": True, "style": {"fontSize": "24px"}}}
}

# Generate HTML with inline scripts
Expand All @@ -178,13 +177,17 @@
<meta charset="utf-8">
<script>{highcharts_js}</script>
</head>
<body style="margin:0;">
<body style="margin:0; background:{PAGE_BG};">
<div id="container" style="width: 4800px; height: 2700px;"></div>
<script>{html_str}</script>
</body>
</html>"""

# Write temp HTML and capture screenshot
# Save HTML artifact
with open(f"plot-{THEME}.html", "w", encoding="utf-8") as f:
f.write(html_content)

# Write temp HTML and take screenshot
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
f.write(html_content)
temp_path = f.name
Expand All @@ -199,23 +202,7 @@
driver = webdriver.Chrome(options=chrome_options)
driver.get(f"file://{temp_path}")
time.sleep(5)
driver.save_screenshot("plot.png")
driver.save_screenshot(f"plot-{THEME}.png")
driver.quit()

Path(temp_path).unlink()

# Save HTML for interactive version
with open("plot.html", "w", encoding="utf-8") as f:
# Use CDN for HTML file (works in browsers)
html_interactive = f"""<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://code.highcharts.com/highcharts.js"></script>
</head>
<body style="margin:0;">
<div id="container" style="width: 100%; height: 100vh;"></div>
<script>{html_str}</script>
</body>
</html>"""
f.write(html_interactive)
Loading
Loading