{"spec_id":"map-tilegrid","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// map-tilegrid: Tile Grid Map for Equal-Area Geographic Comparison\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 84/100 | Created: 2026-09-05\n//# anyplot-orientation: square\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Renewable share of electricity generation (%) across European countries.\n// Every country gets an equally-sized tile, so a small nation (Iceland) reads\n// with the same visual weight as a large one (France) — the point of a tile\n// grid map. Row/col positions approximate each country's real position\n// (row 0 = north, col 0 = west); values are illustrative, not official stats.\nconst countries = [\n  { region: \"IS\", name: \"Iceland\", value: 85, row: 0, col: 0 },\n  { region: \"NO\", name: \"Norway\", value: 98, row: 0, col: 4 },\n  { region: \"SE\", name: \"Sweden\", value: 65, row: 0, col: 5 },\n  { region: \"FI\", name: \"Finland\", value: 55, row: 0, col: 6 },\n  { region: \"IE\", name: \"Ireland\", value: 40, row: 1, col: 0 },\n  { region: \"GB\", name: \"United Kingdom\", value: 43, row: 1, col: 1 },\n  { region: \"DK\", name: \"Denmark\", value: 62, row: 1, col: 4 },\n  { region: \"BE\", name: \"Belgium\", value: 24, row: 2, col: 1 },\n  { region: \"NL\", name: \"Netherlands\", value: 37, row: 2, col: 2 },\n  { region: \"DE\", name: \"Germany\", value: 46, row: 2, col: 3 },\n  { region: \"CZ\", name: \"Czechia\", value: 18, row: 2, col: 4 },\n  { region: \"PL\", name: \"Poland\", value: 16, row: 2, col: 5 },\n  { region: \"FR\", name: \"France\", value: 25, row: 3, col: 1 },\n  { region: \"CH\", name: \"Switzerland\", value: 74, row: 3, col: 2 },\n  { region: \"AT\", name: \"Austria\", value: 78, row: 3, col: 3 },\n  { region: \"RO\", name: \"Romania\", value: 43, row: 3, col: 5 },\n  { region: \"PT\", name: \"Portugal\", value: 61, row: 4, col: 0 },\n  { region: \"ES\", name: \"Spain\", value: 42, row: 4, col: 1 },\n  { region: \"IT\", name: \"Italy\", value: 37, row: 4, col: 3 },\n  { region: \"GR\", name: \"Greece\", value: 44, row: 5, col: 4 },\n];\nconst maxRow = Math.max(...countries.map((c) => c.row));\nconst maxCol = Math.max(...countries.map((c) => c.col));\nconst values = countries.map((c) => c.value);\nconst domainMin = Math.min(...values);\nconst domainMax = Math.max(...values);\n\n// --- Color helpers (Imprint imprint_seq, single-polarity) -------------------\nfunction hexToRgb(hex) {\n  const num = parseInt(hex.replace(\"#\", \"\"), 16);\n  return [(num >> 16) & 255, (num >> 8) & 255, num & 255];\n}\nfunction lerp(a, b, f) {\n  return Math.round(a + (b - a) * f);\n}\nfunction lerpColor(hexA, hexB, f) {\n  const a = hexToRgb(hexA);\n  const b = hexToRgb(hexB);\n  return [lerp(a[0], b[0], f), lerp(a[1], b[1], f), lerp(a[2], b[2], f)];\n}\nfunction sequentialRgb(value) {\n  const f = (value - domainMin) / (domainMax - domainMin);\n  return lerpColor(t.seq[0], t.seq[1], f);\n}\nfunction rgbToCss(rgb) {\n  return `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`;\n}\nfunction relativeLuminance(rgb) {\n  return 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2];\n}\nfunction textColorFor(rgb) {\n  return relativeLuminance(rgb) > 140 ? \"#1A1A17\" : \"#F0EFE8\";\n}\n\n// --- Mount -------------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Chart --------------------------------------------------------------\n// Chart.js has no native tile-grid/geo type; a scatter chart supplies the\n// row/col coordinate system while a custom plugin paints each equal-area\n// tile, its region label, and a colorbar directly on the canvas.\nconst tilePlugin = {\n  id: \"tileGrid\",\n  afterDraw(chart) {\n    const { ctx, chartArea, scales } = chart;\n    const xScale = scales.x;\n    const yScale = scales.y;\n    const tileW = xScale.getPixelForValue(1) - xScale.getPixelForValue(0);\n    const tileH = Math.abs(\n      yScale.getPixelForValue(1) - yScale.getPixelForValue(0),\n    );\n    const gap = 6;\n\n    ctx.save();\n    ctx.textAlign = \"center\";\n    ctx.textBaseline = \"middle\";\n\n    for (const country of countries) {\n      const rgb = sequentialRgb(country.value);\n      const cx = xScale.getPixelForValue(country.col);\n      const cy = yScale.getPixelForValue(country.row);\n      const left = cx - tileW / 2 + gap / 2;\n      const top = cy - tileH / 2 + gap / 2;\n      const size = Math.min(tileW, tileH) - gap;\n\n      ctx.fillStyle = rgbToCss(rgb);\n      ctx.fillRect(left, top, size, size);\n\n      ctx.fillStyle = textColorFor(rgb);\n      ctx.font = \"700 26px sans-serif\";\n      ctx.fillText(country.region, cx, cy);\n    }\n\n    // --- Colorbar (data-range domain) ---------------------------------------\n    const barW = 34;\n    const barX = chartArea.right + 60;\n    const barTop = chartArea.top;\n    const barBottom = chartArea.bottom;\n    const topColor = rgbToCss(sequentialRgb(domainMax));\n    const bottomColor = rgbToCss(sequentialRgb(domainMin));\n\n    const gradient = ctx.createLinearGradient(0, barTop, 0, barBottom);\n    gradient.addColorStop(0, topColor);\n    gradient.addColorStop(1, bottomColor);\n    ctx.fillStyle = gradient;\n    ctx.fillRect(barX, barTop, barW, barBottom - barTop);\n    ctx.strokeStyle = t.inkSoft;\n    ctx.lineWidth = 1.5;\n    ctx.strokeRect(barX, barTop, barW, barBottom - barTop);\n\n    ctx.font = \"16px sans-serif\";\n    ctx.fillStyle = t.inkSoft;\n    ctx.textAlign = \"left\";\n    ctx.textBaseline = \"middle\";\n    ctx.fillText(`${domainMax}%`, barX + barW + 10, barTop);\n    ctx.fillText(`${domainMin}%`, barX + barW + 10, barBottom);\n\n    ctx.save();\n    ctx.translate(barX + barW + 58, (barTop + barBottom) / 2);\n    ctx.rotate(-Math.PI / 2);\n    ctx.textAlign = \"center\";\n    ctx.font = \"18px sans-serif\";\n    ctx.fillStyle = t.ink;\n    ctx.fillText(\"Renewable Share of Electricity (%)\", 0, 0);\n    ctx.restore();\n\n    ctx.restore();\n  },\n};\n\nnew Chart(canvas, {\n  type: \"scatter\",\n  data: {\n    datasets: [\n      {\n        data: countries.map((c) => ({ x: c.col, y: c.row })),\n        pointStyle: false,\n        showLine: false,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: {\n      padding: { top: 10, right: 200, bottom: 10, left: 10 },\n    },\n    plugins: {\n      title: {\n        display: true,\n        text: \"Renewable Energy in Europe · map-tilegrid · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 19 },\n        padding: { bottom: 20 },\n      },\n      legend: { display: false },\n      tooltip: { enabled: false },\n    },\n    scales: {\n      x: {\n        type: \"linear\",\n        min: -0.5,\n        max: maxCol + 0.5,\n        display: false,\n      },\n      y: {\n        type: \"linear\",\n        min: -0.5,\n        max: maxRow + 0.5,\n        reverse: true,\n        display: false,\n      },\n    },\n  },\n  plugins: [tilePlugin],\n});\n"}