{"spec_id":"choropleth-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// choropleth-basic: Choropleth Map with Regional Coloring\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 89/100 | Created: 2026-09-02\n//# anyplot-orientation: landscape\n\nconst t = window.ANYPLOT_TOKENS;\n// ANYPLOT_TOKENS has no \"muted\" entry — derive it the same way the Python/R\n// implementations do (default-style-guide.md \"Theme-adaptive Chrome\").\nconst MUTED = t.theme === \"light\" ? \"#6B6A63\" : \"#A8A79F\";\n\n// --- Data: renewable electricity share (%) by U.S. state -------------------\n// No real GeoJSON boundaries are available offline, so states are laid out as\n// a tile-grid cartogram (a standard choropleth variant that keeps every state\n// equally legible regardless of its real land area). A small fixed-seed LCG\n// stands in for Math.random(), which is not reproducible in the browser.\nlet seed = 42;\nfunction nextRandom() {\n  seed = (seed * 1103515245 + 12345) & 0x7fffffff;\n  return seed / 0x7fffffff;\n}\n\n// [abbr, full name, grid row (0 = north), grid col (0 = west)]\nconst STATE_GRID = [\n  [\"AK\", \"Alaska\", 0, 0], [\"ME\", \"Maine\", 0, 12],\n  [\"WA\", \"Washington\", 1, 1], [\"ID\", \"Idaho\", 1, 2], [\"MT\", \"Montana\", 1, 3],\n  [\"ND\", \"North Dakota\", 1, 4], [\"MN\", \"Minnesota\", 1, 5], [\"WI\", \"Wisconsin\", 1, 6],\n  [\"MI\", \"Michigan\", 1, 7], [\"NY\", \"New York\", 1, 9], [\"VT\", \"Vermont\", 1, 10], [\"NH\", \"New Hampshire\", 1, 11],\n  [\"OR\", \"Oregon\", 2, 1], [\"NV\", \"Nevada\", 2, 2], [\"WY\", \"Wyoming\", 2, 3],\n  [\"SD\", \"South Dakota\", 2, 4], [\"IA\", \"Iowa\", 2, 5], [\"IL\", \"Illinois\", 2, 6],\n  [\"IN\", \"Indiana\", 2, 7], [\"OH\", \"Ohio\", 2, 8], [\"PA\", \"Pennsylvania\", 2, 9],\n  [\"MA\", \"Massachusetts\", 2, 10], [\"RI\", \"Rhode Island\", 2, 11],\n  [\"CA\", \"California\", 3, 1], [\"UT\", \"Utah\", 3, 2], [\"CO\", \"Colorado\", 3, 3],\n  [\"NE\", \"Nebraska\", 3, 4], [\"MO\", \"Missouri\", 3, 5], [\"KY\", \"Kentucky\", 3, 6],\n  [\"WV\", \"West Virginia\", 3, 7], [\"VA\", \"Virginia\", 3, 8], [\"MD\", \"Maryland\", 3, 9],\n  [\"NJ\", \"New Jersey\", 3, 10], [\"CT\", \"Connecticut\", 3, 11],\n  [\"AZ\", \"Arizona\", 4, 2], [\"NM\", \"New Mexico\", 4, 3], [\"KS\", \"Kansas\", 4, 4],\n  [\"AR\", \"Arkansas\", 4, 5], [\"TN\", \"Tennessee\", 4, 6], [\"NC\", \"North Carolina\", 4, 7],\n  [\"SC\", \"South Carolina\", 4, 8], [\"DE\", \"Delaware\", 4, 9], [\"DC\", \"District of Columbia\", 4, 10],\n  [\"OK\", \"Oklahoma\", 5, 4], [\"MS\", \"Mississippi\", 5, 5], [\"AL\", \"Alabama\", 5, 6], [\"GA\", \"Georgia\", 5, 7],\n  [\"TX\", \"Texas\", 6, 3], [\"LA\", \"Louisiana\", 6, 5],\n  [\"FL\", \"Florida\", 7, 7], [\"HI\", \"Hawaii\", 7, 0],\n];\n\n// A handful of states report no data this cycle -> rendered as muted gray.\nconst NO_DATA = new Set([\"RI\", \"DE\", \"WV\"]);\n\n// The south/west \"renewable potential\" trend now dominates the per-state\n// noise (boost spans ~0-46 vs. +/-9 of random jitter), so the tile colors\n// visibly cluster warm-toward-blue in the south/west and cool-toward-green\n// in the northeast rather than reading as random.\nconst stateValues = {};\nSTATE_GRID.forEach(([abbr, , row, col]) => {\n  if (NO_DATA.has(abbr)) return;\n  const southwestBoost = (12 - col) * 1.9 + row * 3.4; // more solar/wind potential toward the south/west\n  const jitter = (nextRandom() - 0.5) * 18; // +/-9 points of per-state noise\n  const value = 14 + southwestBoost + jitter;\n  stateValues[abbr] = Math.round(Math.max(10, Math.min(value, 78)) * 10) / 10;\n});\n\n// --- Build a tile-grid GeoJSON: one padded square polygon per state --------\nconst PAD = 0.08;\nconst features = STATE_GRID.map(([abbr, , row, col]) => ({\n  type: \"Feature\",\n  properties: { name: abbr },\n  geometry: {\n    type: \"Polygon\",\n    coordinates: [[\n      [col + PAD, -row - 1 + PAD],\n      [col + 1 - PAD, -row - 1 + PAD],\n      [col + 1 - PAD, -row - PAD],\n      [col + PAD, -row - PAD],\n      [col + PAD, -row - 1 + PAD],\n    ]],\n  },\n}));\necharts.registerMap(\"usStateTileGrid\", { type: \"FeatureCollection\", features });\n\nconst values = Object.values(stateValues);\nconst minValue = Math.min(...values);\nconst maxValue = Math.max(...values);\nconst highestAbbr = Object.keys(stateValues).find((abbr) => stateValues[abbr] === maxValue);\nconst lowestAbbr = Object.keys(stateValues).find((abbr) => stateValues[abbr] === minValue);\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: \"choropleth-basic · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 24,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: 500 },\n  },\n  tooltip: {\n    trigger: \"item\",\n    formatter: (p) => (NO_DATA.has(p.name) ? `${p.name}: no data` : `${p.name}: ${p.value}%`),\n  },\n  graphic: [\n    {\n      type: \"text\",\n      right: 70,\n      top: 74,\n      style: {\n        text: `Highest: ${highestAbbr} ${maxValue}%\\nLowest: ${lowestAbbr} ${minValue}%`,\n        fill: t.inkSoft,\n        fontSize: 15,\n        lineHeight: 22,\n        textAlign: \"right\",\n      },\n    },\n  ],\n  visualMap: {\n    type: \"continuous\",\n    min: minValue,\n    max: maxValue,\n    calculable: false,\n    orient: \"horizontal\",\n    left: \"center\",\n    bottom: 24,\n    itemWidth: 18,\n    itemHeight: 260,\n    inRange: { color: t.seq },\n    text: [`${maxValue}%`, `${minValue}%`],\n    textStyle: { color: t.inkSoft, fontSize: 14 },\n  },\n  series: [\n    {\n      type: \"map\",\n      map: \"usStateTileGrid\",\n      roam: false,\n      layoutCenter: [\"50%\", \"52%\"],\n      layoutSize: \"92%\",\n      // NaN values fall outside visualMap's dimension and paint with this\n      // fallback instead of the sequential ramp — the \"gray\" missing-data\n      // treatment the spec calls for.\n      itemStyle: {\n        areaColor: MUTED,\n        borderColor: t.pageBg,\n        borderWidth: 3,\n      },\n      label: {\n        show: true,\n        color: \"#FFFDF6\",\n        fontSize: 13,\n        fontWeight: \"bold\",\n      },\n      data: STATE_GRID.map(([abbr]) => ({\n        name: abbr,\n        value: NO_DATA.has(abbr) ? NaN : stateValues[abbr],\n      })),\n    },\n  ],\n});\n"}