{"spec_id":"waffle-basic","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nwaffle-basic: Basic Waffle Chart\nLibrary: bokeh 3.9.0 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-05-05\n\"\"\"\n\nimport os\nimport time\nfrom pathlib import Path\n\nfrom bokeh.io import output_file, save\nfrom bokeh.models import ColumnDataSource, HoverTool, Legend, LegendItem\nfrom bokeh.plotting import figure\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Okabe-Ito palette (first series always brand green)\nPALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\"]\n\n# Data - Survey results on renewable energy adoption\ncategories = [\"Solar\", \"Wind\", \"Hydro\", \"Geothermal\"]\nvalues = [48, 25, 18, 9]  # Percentages summing to 100\n\n# Build waffle grid (10x10 = 100 squares)\ngrid_size = 10\ntotal_squares = grid_size * grid_size\n\n# Assign category to each square based on percentages\nsquare_categories = []\ncolor_map = []\nfor i, (cat, val) in enumerate(zip(categories, values, strict=True)):\n    square_categories.extend([cat] * val)\n    color_map.extend([PALETTE[i]] * val)\n\n# Create grid coordinates (fill left-to-right, bottom-to-top)\nx_coords = []\ny_coords = []\nfor idx in range(total_squares):\n    x_coords.append(idx % grid_size)\n    y_coords.append(idx // grid_size)\n\nsource = ColumnDataSource(data={\"x\": x_coords, \"y\": y_coords, \"category\": square_categories, \"color\": color_map})\n\n# Create figure\np = figure(\n    width=4800,\n    height=2700,\n    title=\"Renewable Energy Sources · waffle-basic · bokeh · anyplot.ai\",\n    x_range=(-0.7, grid_size - 0.3),\n    y_range=(-0.7, grid_size - 0.3),\n    tools=\"\",\n    toolbar_location=None,\n)\n\n# Draw squares with small gap between them\nsquare_size = 0.88\nrenderers = []\nfor i, (cat, color) in enumerate(zip(categories, PALETTE, strict=True)):\n    # Filter data for this category\n    indices = [j for j, c in enumerate(square_categories) if c == cat]\n    cat_source = ColumnDataSource(\n        data={\n            \"x\": [x_coords[j] for j in indices],\n            \"y\": [y_coords[j] for j in indices],\n            \"category\": [cat] * len(indices),\n            \"percentage\": [values[i]] * len(indices),\n        }\n    )\n    r = p.rect(\n        x=\"x\",\n        y=\"y\",\n        width=square_size,\n        height=square_size,\n        source=cat_source,\n        fill_color=color,\n        line_color=PAGE_BG,\n        line_width=2.5,\n        fill_alpha=1.0,\n    )\n    renderers.append((f\"{cat} ({values[i]}%)\", [r]))\n\n# Add hover tool\nhover = HoverTool(tooltips=[(\"Category\", \"@category\"), (\"Percentage\", \"@percentage%\")])\np.add_tools(hover)\n\n# Add legend\nlegend = Legend(items=[LegendItem(label=label, renderers=rends) for label, rends in renderers])\nlegend.label_text_font_size = \"18pt\"\nlegend.glyph_height = 35\nlegend.glyph_width = 35\nlegend.spacing = 12\nlegend.padding = 15\nlegend.location = \"top_right\"\nlegend.background_fill_color = ELEVATED_BG\nlegend.border_line_color = INK_SOFT\nlegend.label_text_color = INK_SOFT\np.add_layout(legend, \"right\")\n\n# Style the plot\np.title.text_font_size = \"28pt\"\np.title.align = \"center\"\np.title.text_color = INK\n\n# Hide axes\np.xaxis.visible = False\np.yaxis.visible = False\np.xgrid.visible = False\np.ygrid.visible = False\np.outline_line_color = None\n\n# Set background\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\n\n# Save HTML\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\n# Screenshot with headless Chrome\nW, H = 4800, 2700\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.set_window_size(W, H)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}