{"spec_id":"map-tilegrid","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nmap-tilegrid: Tile Grid Map for Equal-Area Geographic Comparison\nLibrary: bokeh 3.10.0 | Python 3.13.15\nQuality: 95/100 | Created: 2026-08-24\n\"\"\"\n\nimport os\nimport time\nfrom pathlib import Path\n\nfrom bokeh.io import output_file, save\nfrom bokeh.models import (\n    BasicTicker,\n    ColorBar,\n    ColumnDataSource,\n    HoverTool,\n    LinearColorMapper,\n    NumeralTickFormatter,\n    Range1d,\n    Title,\n)\nfrom bokeh.plotting import figure\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\n# Theme tokens (see prompts/default-style-guide.md \"Theme-adaptive Chrome\")\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\"\nLABEL_LIGHT = \"#F0EFE8\"\n\n# Data — renewable energy share of European countries, tile positions\n# approximate each country's real-world location (row 0 = north, col 0 = west)\ncountries = [\n    {\"code\": \"IS\", \"row\": 0, \"col\": 0, \"value\": 85},\n    {\"code\": \"NO\", \"row\": 0, \"col\": 3, \"value\": 71},\n    {\"code\": \"SE\", \"row\": 0, \"col\": 5, \"value\": 65},\n    {\"code\": \"FI\", \"row\": 0, \"col\": 7, \"value\": 47},\n    {\"code\": \"IE\", \"row\": 1, \"col\": 0, \"value\": 36},\n    {\"code\": \"UK\", \"row\": 1, \"col\": 1, \"value\": 42},\n    {\"code\": \"DK\", \"row\": 1, \"col\": 4, \"value\": 43},\n    {\"code\": \"EE\", \"row\": 1, \"col\": 8, \"value\": 38},\n    {\"code\": \"BE\", \"row\": 2, \"col\": 0, \"value\": 26},\n    {\"code\": \"NL\", \"row\": 2, \"col\": 1, \"value\": 37},\n    {\"code\": \"DE\", \"row\": 2, \"col\": 4, \"value\": 46},\n    {\"code\": \"PL\", \"row\": 2, \"col\": 6, \"value\": 21},\n    {\"code\": \"LV\", \"row\": 2, \"col\": 8, \"value\": 43},\n    {\"code\": \"FR\", \"row\": 3, \"col\": 1, \"value\": 22},\n    {\"code\": \"CH\", \"row\": 3, \"col\": 3, \"value\": 30},\n    {\"code\": \"CZ\", \"row\": 3, \"col\": 5, \"value\": 19},\n    {\"code\": \"SK\", \"row\": 3, \"col\": 6, \"value\": 21},\n    {\"code\": \"LT\", \"row\": 3, \"col\": 8, \"value\": 29},\n    {\"code\": \"PT\", \"row\": 4, \"col\": 0, \"value\": 34},\n    {\"code\": \"ES\", \"row\": 4, \"col\": 1, \"value\": 24},\n    {\"code\": \"AT\", \"row\": 4, \"col\": 4, \"value\": 78},\n    {\"code\": \"HU\", \"row\": 4, \"col\": 6, \"value\": 15},\n    {\"code\": \"RO\", \"row\": 4, \"col\": 7, \"value\": 30},\n    {\"code\": \"IT\", \"row\": 5, \"col\": 2, \"value\": 20},\n    {\"code\": \"SI\", \"row\": 5, \"col\": 4, \"value\": 27},\n    {\"code\": \"HR\", \"row\": 5, \"col\": 5, \"value\": 32},\n    {\"code\": \"BG\", \"row\": 5, \"col\": 7, \"value\": 24},\n    {\"code\": \"GR\", \"row\": 6, \"col\": 5, \"value\": 22},\n    {\"code\": \"MT\", \"row\": 7, \"col\": 4, \"value\": 13},\n]\nnum_rows = max(c[\"row\"] for c in countries) + 1\nmax_col = max(c[\"col\"] for c in countries)\n\nCOUNTRY_NAMES = {\n    \"IS\": \"Iceland\", \"NO\": \"Norway\", \"SE\": \"Sweden\", \"FI\": \"Finland\",\n    \"IE\": \"Ireland\", \"UK\": \"United Kingdom\", \"DK\": \"Denmark\", \"EE\": \"Estonia\",\n    \"BE\": \"Belgium\", \"NL\": \"Netherlands\", \"DE\": \"Germany\", \"PL\": \"Poland\",\n    \"LV\": \"Latvia\", \"FR\": \"France\", \"CH\": \"Switzerland\", \"CZ\": \"Czechia\",\n    \"SK\": \"Slovakia\", \"LT\": \"Lithuania\", \"PT\": \"Portugal\", \"ES\": \"Spain\",\n    \"AT\": \"Austria\", \"HU\": \"Hungary\", \"RO\": \"Romania\", \"IT\": \"Italy\",\n    \"SI\": \"Slovenia\", \"HR\": \"Croatia\", \"BG\": \"Bulgaria\", \"GR\": \"Greece\",\n    \"MT\": \"Malta\",\n}  # fmt: skip\n\n\ndef _lerp_hex(c0, c1, t):\n    r0, g0, b0 = (int(c0[i : i + 2], 16) for i in (1, 3, 5))\n    r1, g1, b1 = (int(c1[i : i + 2], 16) for i in (1, 3, 5))\n    r, g, b = (int(round(a + (b - a) * t)) for a, b in ((r0, r1), (g0, g1), (b0, b1)))\n    return f\"#{r:02X}{g:02X}{b:02X}\"\n\n\ndef _luminance(hex_color):\n    r, g, b = (int(hex_color[i : i + 2], 16) / 255 for i in (1, 3, 5))\n    return 0.2126 * r + 0.7152 * g + 0.0722 * b\n\n\n# Continuous Imprint colormap — unipolar data (renewable share, no midpoint)\nANYPLOT_SEQ256 = [_lerp_hex(\"#009E73\", \"#4467A3\", t / 255.0) for t in range(256)]\nlow = min(c[\"value\"] for c in countries)\nhigh = max(c[\"value\"] for c in countries)\n\nxs, ys, values, codes, names, label_colors = [], [], [], [], [], []\nfor c in countries:\n    xs.append(c[\"col\"])\n    ys.append(num_rows - 1 - c[\"row\"])  # flip so row 0 (north) renders at top\n    values.append(c[\"value\"])\n    codes.append(c[\"code\"])\n    names.append(COUNTRY_NAMES[c[\"code\"]])\n    t = (c[\"value\"] - low) / (high - low)\n    tile_color = ANYPLOT_SEQ256[min(255, int(round(t * 255)))]\n    label_colors.append(INK if _luminance(tile_color) > 0.55 else LABEL_LIGHT)\n\nsource = ColumnDataSource(\n    data={\"x\": xs, \"y\": ys, \"value\": values, \"code\": codes, \"name\": names, \"label_color\": label_colors}\n)\nmapper = LinearColorMapper(palette=ANYPLOT_SEQ256, low=low, high=high)\n\n# Title — compute fontsize per the length-scaling formula (default 50pt / floor 34pt)\ntitle = \"Renewable Energy Share in Europe · map-tilegrid · python · bokeh · anyplot.ai\"\nn = len(title)\nratio = 67 / n if n > 67 else 1.0\ntitle_font_size = f\"{max(34, round(50 * ratio))}pt\"\n\n# Plot\np = figure(\n    width=2400,\n    height=2400,\n    title=title,\n    toolbar_location=None,  # bokeh's default toolbar shrinks the PNG below `height=`\n    x_range=Range1d(-0.75, max_col + 0.75),\n    y_range=Range1d(-0.75, num_rows - 1 + 0.75),\n    match_aspect=True,\n    min_border_top=190,\n    min_border_bottom=60,\n    min_border_left=60,\n    min_border_right=340,\n)\ntile_renderer = p.rect(\n    x=\"x\",\n    y=\"y\",\n    width=0.88,\n    height=0.88,\n    source=source,\n    fill_color={\"field\": \"value\", \"transform\": mapper},\n    line_color=PAGE_BG,\n    line_width=6,\n)\nhover = HoverTool(renderers=[tile_renderer], tooltips=[(\"Country\", \"@name\"), (\"Renewable share\", \"@value{0}%\")])\np.add_tools(hover)\np.text(\n    x=\"x\",\n    y=\"y\",\n    text=\"code\",\n    source=source,\n    text_align=\"center\",\n    text_baseline=\"middle\",\n    text_font_size=\"34pt\",\n    text_font_style=\"bold\",\n    text_color=\"label_color\",\n)\n\n# Colorbar\ncolor_bar = ColorBar(\n    color_mapper=mapper,\n    ticker=BasicTicker(desired_num_ticks=5),\n    formatter=NumeralTickFormatter(format=\"0\"),\n    title=\"Renewable share (%)\",\n    title_text_font_size=\"34pt\",\n    title_text_color=INK,\n    major_label_text_font_size=\"30pt\",\n    major_label_text_color=INK_SOFT,\n    background_fill_color=ELEVATED_BG,\n    border_line_color=INK_SOFT,\n    label_standoff=12,\n    width=40,\n    location=(0, 0),\n)\np.add_layout(color_bar, \"right\")\n\n# Style — theme-adaptive chrome; no axes for a schematic tile grid\np.title.text_font_size = title_font_size\np.title.text_color = INK\n\n# Subtitle callout — sharpens the color-gradient insight into an explicit takeaway\np.add_layout(\n    Title(\n        text=\"Nordic and Alpine countries lead Europe's renewable transition\",\n        text_font_size=\"26pt\",\n        text_font_style=\"italic\",\n        text_color=INK_SOFT,\n    ),\n    \"above\",\n)\n\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\np.outline_line_color = None\np.axis.visible = False\np.grid.visible = False\n\n# Save (HTML + PNG via headless Chrome — see prompts/library/bokeh.md)\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\nW, H = 2400, 2400\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()}\")\ndriver.execute_cdp_cmd(\n    \"Emulation.setDeviceMetricsOverride\", {\"width\": W, \"height\": H, \"deviceScaleFactor\": 1, \"mobile\": False}\n)\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}