{"spec_id":"heatmap-annotated","library":"echarts","language":"javascript","code":"// anyplot.ai\n// heatmap-annotated: Annotated Heatmap\n// Library: echarts 6.1.0 | JavaScript 22.23.1\n// Quality: 90/100 | Created: 2026-08-05\n//# anyplot-orientation: square\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: correlation matrix of weather metrics (symmetric, diagonal = 1) -\nconst metrics = [\n  \"Temperature\", \"Humidity\", \"Wind Speed\", \"Pressure\",\n  \"Rainfall\", \"Cloud Cover\", \"UV Index\", \"Visibility\",\n];\n\nconst corr = [\n  [1.00, -0.42, -0.08, 0.35, -0.30, -0.25, 0.81, 0.40],\n  [-0.42, 1.00, 0.15, -0.38, 0.62, 0.55, -0.48, -0.52],\n  [-0.08, 0.15, 1.00, -0.20, 0.28, 0.18, -0.05, -0.22],\n  [0.35, -0.38, -0.20, 1.00, -0.45, -0.30, 0.25, 0.33],\n  [-0.30, 0.62, 0.28, -0.45, 1.00, 0.70, -0.40, -0.60],\n  [-0.25, 0.55, 0.18, -0.30, 0.70, 1.00, -0.65, -0.48],\n  [0.81, -0.48, -0.05, 0.25, -0.40, -0.65, 1.00, 0.45],\n  [0.40, -0.52, -0.22, 0.33, -0.60, -0.48, 0.45, 1.00],\n];\n\n// --- Cell-background-aware annotation contrast --------------------------\n// Interpolates the same imprint_div gradient the visualMap uses, so each\n// cell's label color is derived from that exact cell's rendered background.\nconst hexToRgb = (hex) => [\n  parseInt(hex.slice(1, 3), 16),\n  parseInt(hex.slice(3, 5), 16),\n  parseInt(hex.slice(5, 7), 16),\n];\nconst lerpRgb = (hexA, hexB, ratio) => {\n  const [a, b] = [hexToRgb(hexA), hexToRgb(hexB)];\n  return a.map((channel, i) => Math.round(channel + (b[i] - channel) * ratio));\n};\nconst divergingRgb = (value) => {\n  const ratio = (value + 1) / 2; // value in [-1, 1] -> [0, 1]\n  return ratio <= 0.5 ? lerpRgb(t.div[0], t.div[1], ratio / 0.5) : lerpRgb(t.div[1], t.div[2], (ratio - 0.5) / 0.5);\n};\nconst luma = ([r, g, b]) => (r * 299 + g * 587 + b * 114) / 1000;\nconst contrastColor = (value) => (luma(divergingRgb(value)) > 140 ? \"#1A1A17\" : \"#F0EFE8\");\n\n// --- Data storytelling: surface the strongest off-diagonal relationship ----\n// so the reader isn't left to hunt the matrix for the standout pair.\nlet strongest = { row: 0, col: 1, value: corr[0][1] };\nfor (let row = 0; row < metrics.length; row += 1) {\n  for (let col = row + 1; col < metrics.length; col += 1) {\n    if (Math.abs(corr[row][col]) > Math.abs(strongest.value)) {\n      strongest = { row, col, value: corr[row][col] };\n    }\n  }\n}\nconst isStrongestPair = (row, col) =>\n  row !== col && ((row === strongest.row && col === strongest.col) || (row === strongest.col && col === strongest.row));\n\nconst cells = [];\nfor (let row = 0; row < metrics.length; row += 1) {\n  for (let col = 0; col < metrics.length; col += 1) {\n    const value = corr[row][col];\n    const highlight = isStrongestPair(row, col);\n    cells.push({\n      value: [col, row, value],\n      label: { color: contrastColor(value), fontWeight: highlight ? 700 : 400 },\n      ...(highlight ? { itemStyle: { borderColor: t.ink, borderWidth: 4 } } : {}),\n    });\n  }\n}\n\n// --- Init -------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option -----------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"heatmap-annotated · javascript · echarts · anyplot.ai\",\n    subtext: `Strongest link: ${metrics[strongest.row]} ↔ ${metrics[strongest.col]} (${strongest.value.toFixed(2)})`,\n    left: \"center\",\n    top: 24,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: 500 },\n    subtextStyle: { color: t.inkSoft, fontSize: 14 },\n  },\n  grid: { left: 175, right: 205, top: 135, bottom: 170 },\n  xAxis: {\n    type: \"category\",\n    data: metrics,\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitLine: { show: false },\n    axisLabel: { color: t.inkSoft, fontSize: 14, rotate: 40 },\n  },\n  yAxis: {\n    type: \"category\",\n    data: metrics,\n    inverse: true,\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitLine: { show: false },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n  },\n  visualMap: {\n    type: \"continuous\",\n    min: -1,\n    max: 1,\n    calculable: true,\n    orient: \"vertical\",\n    right: 15,\n    top: \"middle\",\n    itemHeight: 360,\n    itemWidth: 18,\n    precision: 2,\n    inRange: { color: t.div },\n    textStyle: { color: t.inkSoft, fontSize: 13 },\n  },\n  series: [\n    {\n      type: \"heatmap\",\n      data: cells,\n      itemStyle: { borderColor: t.pageBg, borderWidth: 3 },\n      label: {\n        show: true,\n        formatter: (params) => params.value[2].toFixed(2),\n        fontSize: 13,\n      },\n      emphasis: {\n        itemStyle: { shadowBlur: 0, borderColor: t.ink, borderWidth: 2 },\n      },\n    },\n  ],\n});\n"}