{"spec_id":"climograph-walter-lieth","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// climograph-walter-lieth: Walter-Lieth Climate Diagram\n// Library: chartjs 4.4.7 | JavaScript 22.22.3\n// Quality: 92/100 | Created: 2026-06-15\n\nconst t = window.ANYPLOT_TOKENS;\nconst THEME = window.ANYPLOT_THEME || 'light';\n\n// Station: Kızılırmak Basin, Turkey — continental Anatolia, 1991–2020 normals\nconst STATION  = \"Kızılırmak Basin, Turkey\";\nconst ELEVATION = 1050;\nconst PERIOD    = \"1991–2020\";\n\nconst months = [\"J\",\"F\",\"M\",\"A\",\"M\",\"J\",\"J\",\"A\",\"S\",\"O\",\"N\",\"D\"];\n\n// Monthly mean temperature (°C) and total precipitation (mm)\nconst temperature  = [-3, -1, 5, 11, 16, 21, 25, 25, 20, 13, 6, 0];\nconst precipitation = [48, 46, 50, 55, 48, 28, 12, 10, 19, 40, 42, 52];\n\n// Annual statistics\nconst tempMean    = (temperature.reduce((a, b) => a + b, 0) / 12).toFixed(1);\nconst precipTotal = precipitation.reduce((a, b) => a + b, 0);\n\n// Walter-Lieth axis bounds: precipitation scale = 2× temperature (10°C ↔ 20mm)\nconst TEMP_MIN = -10, TEMP_MAX = 40;\nconst PRCP_MIN = TEMP_MIN * 2, PRCP_MAX = TEMP_MAX * 2;  // -20, 80\n\n// Colors — semantic: temperature = red (#AE3030), precipitation = blue (#4467A3)\nconst TEMP_COL  = t.palette[4]; // #AE3030 matte red\nconst PRCP_COL  = t.palette[2]; // #4467A3 blue\nconst HUMID_CLR = 'rgba(68,103,163,0.28)';  // blue fill — humid period (precip > temp)\nconst ARID_CLR  = 'rgba(174,48,48,0.30)';   // red fill  — arid period (temp > precip)\nconst FROST_CLR = THEME === 'dark' ? 'rgba(240,239,232,0.85)' : 'rgba(26,26,23,0.80)';\n\n// --- Helper: fill quadrilateral between two curve segments ------------------\nfunction fillQuad(ctx, x0, ty0, py0, x1, ty1, py1, fill) {\n  ctx.save();\n  ctx.beginPath();\n  ctx.moveTo(x0, ty0);\n  ctx.lineTo(x1, ty1);\n  ctx.lineTo(x1, py1);\n  ctx.lineTo(x0, py0);\n  ctx.closePath();\n  ctx.fillStyle = fill;\n  ctx.fill();\n  ctx.restore();\n}\n\n// --- Plugin: Walter-Lieth fills (humid / arid zones between the two curves) -\nconst wlFillPlugin = {\n  id: 'wlFill',\n  beforeDatasetsDraw(chart) {\n    const { ctx } = chart;\n    const d0 = chart.getDatasetMeta(0).data;  // temperature pixel points\n    const d1 = chart.getDatasetMeta(1).data;  // precipitation pixel points\n    if (!d0.length || !d1.length) return;\n\n    for (let i = 0; i < d0.length - 1; i++) {\n      const x0 = d0[i].x,    x1 = d0[i + 1].x;\n      const ty0 = d0[i].y,   ty1 = d0[i + 1].y;\n      const py0 = d1[i].y,   py1 = d1[i + 1].y;\n\n      // Lower pixel-y = higher value; temp > precip means ty < py (arid)\n      const arid0 = ty0 < py0;\n      const arid1 = ty1 < py1;\n\n      if (arid0 === arid1) {\n        fillQuad(ctx, x0, ty0, py0, x1, ty1, py1, arid0 ? ARID_CLR : HUMID_CLR);\n      } else {\n        // Crossover — find intersection in pixel space\n        // ty0 + f*(ty1-ty0) = py0 + f*(py1-py0)  →  f = (py0-ty0) / [(ty1-ty0)-(py1-py0)]\n        const denom = (ty1 - ty0) - (py1 - py0);\n        const f = Math.abs(denom) < 0.01 ? 0.5 : (py0 - ty0) / denom;\n        const cx = x0 + f * (x1 - x0);\n        const cy = ty0 + f * (ty1 - ty0);\n        fillQuad(ctx, x0, ty0, py0, cx, cy,  cy,  arid0 ? ARID_CLR : HUMID_CLR);\n        fillQuad(ctx, cx, cy,  cy,  x1, ty1, py1, arid1 ? ARID_CLR : HUMID_CLR);\n      }\n    }\n  },\n};\n\n// --- Plugin: frost bands at chart bottom for months with mean T < 0°C ------\nconst frostPlugin = {\n  id: 'frost',\n  afterDatasetsDraw(chart) {\n    const { ctx, chartArea } = chart;\n    const d0 = chart.getDatasetMeta(0).data;\n    const n  = temperature.length;\n    const bandH = Math.max(8, Math.round(chartArea.height * 0.025));\n\n    for (let i = 0; i < n; i++) {\n      if (temperature[i] < 0) {\n        const cx = d0[i].x;\n        const dx = i < n - 1 ? d0[i + 1].x - cx : cx - d0[i - 1].x;\n        ctx.fillStyle = FROST_CLR;\n        ctx.fillRect(cx - dx * 0.45, chartArea.bottom - bandH, dx * 0.9, bandH);\n      }\n    }\n  },\n};\n\n// --- Plugin: dashed 0°C reference line -------------------------------------\nconst zeroLinePlugin = {\n  id: 'zeroLine',\n  afterDatasetsDraw(chart) {\n    const { ctx, chartArea, scales } = chart;\n    const zeroY = scales.yTemp.getPixelForValue(0);\n    ctx.save();\n    ctx.beginPath();\n    ctx.moveTo(chartArea.left, zeroY);\n    ctx.lineTo(chartArea.right, zeroY);\n    ctx.strokeStyle = t.inkSoft;\n    ctx.lineWidth = 1.5;\n    ctx.setLineDash([5, 5]);\n    ctx.stroke();\n    ctx.restore();\n  },\n};\n\n// --- Mount -----------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Chart -----------------------------------------------------------------\nnew Chart(canvas, {\n  type: 'line',\n  plugins: [wlFillPlugin, frostPlugin, zeroLinePlugin],\n  data: {\n    labels: months,\n    datasets: [\n      {\n        label: 'Temperature (°C)',\n        data: temperature,\n        yAxisID: 'yTemp',\n        borderColor: TEMP_COL,\n        backgroundColor: TEMP_COL,\n        borderWidth: 3.5,\n        pointRadius: 4,\n        pointBackgroundColor: TEMP_COL,\n        pointBorderColor: t.pageBg,\n        pointBorderWidth: 1.5,\n        fill: false,\n        tension: 0,\n      },\n      {\n        label: 'Precipitation (mm)',\n        data: precipitation,\n        yAxisID: 'yPrec',\n        borderColor: PRCP_COL,\n        backgroundColor: PRCP_COL,\n        borderWidth: 3.5,\n        pointRadius: 4,\n        pointBackgroundColor: PRCP_COL,\n        pointBorderColor: t.pageBg,\n        pointBorderWidth: 1.5,\n        fill: false,\n        tension: 0,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: { padding: { top: 8, right: 30, bottom: 30, left: 8 } },\n    plugins: {\n      title: {\n        display: true,\n        text: 'climograph-walter-lieth · javascript · chartjs · anyplot.ai',\n        color: t.ink,\n        font: { size: 22, weight: 'bold' },\n        padding: { bottom: 4 },\n      },\n      subtitle: {\n        display: true,\n        text: [\n          STATION + '  ·  ' + ELEVATION + ' m a.s.l.  ·  ' + PERIOD,\n          'Annual mean: ' + tempMean + ' °C   ·   Annual precipitation: ' + precipTotal + ' mm',\n        ],\n        color: t.inkSoft,\n        font: { size: 14 },\n        padding: { bottom: 14 },\n      },\n      legend: {\n        display: true,\n        labels: {\n          color: t.ink,\n          font: { size: 14 },\n          usePointStyle: true,\n          boxWidth: 20,\n          padding: 16,\n        },\n      },\n    },\n    scales: {\n      x: {\n        ticks:  { color: t.inkSoft, font: { size: 14 } },\n        grid:   { color: t.grid },\n        border: { color: t.inkSoft },\n      },\n      yTemp: {\n        type: 'linear',\n        position: 'left',\n        min: TEMP_MIN,\n        max: TEMP_MAX,\n        title: {\n          display: true,\n          text: 'Temperature (°C)',\n          color: TEMP_COL,\n          font: { size: 14, weight: 'bold' },\n        },\n        ticks: {\n          color: TEMP_COL,\n          font: { size: 13 },\n          stepSize: 10,\n        },\n        grid:   { color: t.grid },\n        border: { color: t.inkSoft },\n      },\n      yPrec: {\n        type: 'linear',\n        position: 'right',\n        min: PRCP_MIN,\n        max: PRCP_MAX,\n        title: {\n          display: true,\n          text: 'Precipitation (mm)',\n          color: PRCP_COL,\n          font: { size: 14, weight: 'bold' },\n        },\n        ticks: {\n          color: PRCP_COL,\n          font: { size: 13 },\n          stepSize: 20,\n          callback: (v) => v < 0 ? '' : v,\n        },\n        grid:   { drawOnChartArea: false },\n        border: { color: t.inkSoft },\n      },\n    },\n  },\n});\n"}