{"spec_id":"windrose-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// windrose-basic: Wind Rose Chart\n// Library: echarts 6.1.0 | JavaScript 22.23.1\n// Quality: 94/100 | Created: 2026-08-05\n\n//# anyplot-orientation: square\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// One year of hourly wind observations from a coastal weather station,\n// binned into 16 compass sectors and 5 speed classes (m/s).\nconst directions = [\n  \"N\", \"NNE\", \"NE\", \"ENE\", \"E\", \"ESE\", \"SE\", \"SSE\",\n  \"S\", \"SSW\", \"SW\", \"WSW\", \"W\", \"WNW\", \"NW\", \"NNW\",\n];\nconst speedBins = [\"0-3\", \"3-6\", \"6-9\", \"9-12\", \"12+\"];\n\n// Relative direction frequency - prevailing wind out of the SW quadrant,\n// with a calmer secondary lobe out of the N.\nconst directionWeight = [\n  6.0, 3.2, 2.0, 1.8, 2.4, 2.6, 3.4, 4.6,\n  6.8, 9.5, 13.0, 12.4, 10.2, 7.0, 4.4, 3.4,\n];\nconst totalWeight = directionWeight.reduce((a, b) => a + b, 0);\nconst directionPct = directionWeight.map((w) => (w / totalWeight) * 100);\n\n// Speed-bin shape shifts toward the higher bins as prevailing strength\n// grows - calmer secondary directions stay skewed toward the lowest bin.\nconst maxWeight = Math.max(...directionWeight);\nconst dominantDirection = directions[directionWeight.indexOf(maxWeight)];\nconst table = directionPct.map((pct, i) => {\n  const strength = directionWeight[i] / maxWeight;\n  const raw = [0.42, 0.28, 0.16, 0.09, 0.05].map(\n    (base, k) => base + (strength - 0.5) * k * 0.03\n  );\n  const sum = raw.reduce((a, b) => a + b, 0);\n  return raw.map((v) => (pct * v) / sum);\n});\n\n// --- Colors: sample the Imprint sequential ramp across the speed bins ------\nconst [seqLo, seqHi] = [t.seq[0], t.seq[1]].map((hex) =>\n  [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16))\n);\nconst speedColors = speedBins.map((_, i) => {\n  const f = i / (speedBins.length - 1);\n  const rgb = seqLo.map((v, k) => Math.round(v + (seqHi[k] - v) * f));\n  return \"#\" + rgb.map((v) => v.toString(16).padStart(2, \"0\")).join(\"\");\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: \"windrose-basic · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 20,\n    textStyle: { color: t.ink, fontSize: 26, fontWeight: \"bold\" },\n  },\n  legend: {\n    bottom: 20,\n    left: \"center\",\n    data: speedBins.map((label) => `${label} m/s`),\n    textStyle: { color: t.inkSoft, fontSize: 15 },\n    itemWidth: 18,\n    itemHeight: 14,\n  },\n  polar: { center: [\"50%\", \"52%\"], radius: \"62%\" },\n  angleAxis: {\n    type: \"category\",\n    data: directions,\n    // boundaryGap centers each category within its sector, so the start\n    // angle is offset by half a sector width to land N exactly at top.\n    startAngle: 90 + 180 / directions.length,\n    clockwise: true,\n    axisLabel: {\n      color: t.inkSoft,\n      fontSize: 14,\n      formatter: (value) => (value === dominantDirection ? `{bold|${value}}` : value),\n      rich: { bold: { color: t.ink, fontWeight: \"bold\", fontSize: 15 } },\n    },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  radiusAxis: {\n    type: \"value\",\n    splitNumber: 4,\n    axisLabel: { color: t.inkSoft, fontSize: 12, formatter: \"{value}%\" },\n    axisLine: { show: false },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  series: speedBins.map((label, j) => ({\n    name: `${label} m/s`,\n    type: \"bar\",\n    coordinateSystem: \"polar\",\n    stack: \"speed\",\n    barCategoryGap: \"20%\",\n    data: table.map((row) => Number(row[j].toFixed(2))),\n    itemStyle: { color: speedColors[j], borderColor: t.pageBg, borderWidth: 1 },\n  })),\n});\n"}