{"spec_id":"contour-filled","library":"d3","language":"javascript","code":"// anyplot.ai\n// contour-filled: Filled Contour Plot\n// Library: d3 7.9.0 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-04\n\nconst t = window.ANYPLOT_TOKENS;\nconst { width: W, height: H } = window.ANYPLOT_SIZE;\nconst margin = { top: 90, right: 190, bottom: 90, left: 100 };\nconst iw = W - margin.left - margin.right;\nconst ih = H - margin.top - margin.bottom;\n\n// --- Data: sea-level pressure anomaly on a regular lon/lat grid ------------\n// Deterministic combination of a high-pressure ridge and a low-pressure\n// trough plus a mild sinusoidal ripple, evaluated on a 64x40 mesh (within\n// the spec's 30x30-100x100 range).\nconst gridWidth = 64;\nconst gridHeight = 40;\nconst lonMin = 0;\nconst lonMax = 16;\nconst latMin = 0;\nconst latMax = 10;\n\nfunction pressureAnomaly(lon, lat) {\n  const ridge = 9 * Math.exp(-(((lon - 4) ** 2) / (2 * 3.2 ** 2) + ((lat - 7.5) ** 2) / (2 * 2.4 ** 2)));\n  const trough = -11 * Math.exp(-(((lon - 12) ** 2) / (2 * 3.6 ** 2) + ((lat - 2.8) ** 2) / (2 * 2.6 ** 2)));\n  const ripple = 1.5 * Math.sin(lon / 3) * Math.cos(lat / 2.5);\n  return ridge + trough + ripple;\n}\n\nconst lonAt = (i) => lonMin + (i / (gridWidth - 1)) * (lonMax - lonMin);\nconst latAt = (j) => latMin + (j / (gridHeight - 1)) * (latMax - latMin);\n\nconst values = new Array(gridWidth * gridHeight);\nfor (let j = 0; j < gridHeight; j++) {\n  for (let i = 0; i < gridWidth; i++) {\n    values[j * gridWidth + i] = pressureAnomaly(lonAt(i), latAt(j));\n  }\n}\nconst [zMin, zMax] = d3.extent(values);\nconst maxAbs = Math.max(Math.abs(zMin), Math.abs(zMax));\n\n// --- Scales ------------------------------------------------------------------\nconst lonScale = d3.scaleLinear().domain([lonMin, lonMax]).range([0, iw]);\nconst latScale = d3.scaleLinear().domain([latMin, latMax]).range([ih, 0]);\n// Diverging colormap centered on the physically meaningful zero anomaly.\nconst colorScale = d3.scaleSequential(d3.interpolateRgbBasis(t.div)).domain([-maxAbs, maxAbs]);\n\n// --- Contour bands (cumulative-area technique: each band is the full region\n// >= its threshold, so painting low-to-high builds the filled color bands and\n// each polygon boundary doubles as a precise isoline) --------------------------\nconst bands = d3.contours().size([gridWidth, gridHeight]).thresholds(14)(values);\n\nconst geoTransform = d3.geoTransform({\n  point(px, py) {\n    this.stream.point(lonScale(lonAt(px)), latScale(latAt(py)));\n  },\n});\nconst path = d3.geoPath(geoTransform);\n\n// --- SVG mount ----------------------------------------------------------------\nconst svg = d3.select(\"#container\").append(\"svg\").attr(\"width\", W).attr(\"height\", H);\nconst g = svg.append(\"g\").attr(\"transform\", `translate(${margin.left},${margin.top})`);\n\n// Clip so band polygons (which extend to the mesh border) never bleed past the plot area.\nsvg\n  .append(\"clipPath\")\n  .attr(\"id\", \"plot-clip\")\n  .append(\"rect\")\n  .attr(\"width\", iw)\n  .attr(\"height\", ih);\n\ng.append(\"g\")\n  .attr(\"clip-path\", \"url(#plot-clip)\")\n  .selectAll(\"path\")\n  .data(bands)\n  .join(\"path\")\n  .attr(\"d\", path)\n  .attr(\"fill\", (d) => colorScale(d.value))\n  .attr(\"stroke\", t.ink)\n  .attr(\"stroke-opacity\", 0.12)\n  .attr(\"stroke-width\", 1);\n\n// --- Axes ----------------------------------------------------------------------\nconst xAxis = g\n  .append(\"g\")\n  .attr(\"transform\", `translate(0,${ih})`)\n  .call(d3.axisBottom(lonScale).ticks(8).tickFormat((d) => `${d}°`));\nconst yAxis = g.append(\"g\").call(d3.axisLeft(latScale).ticks(6).tickFormat((d) => `${d}°`));\nfor (const axis of [xAxis, yAxis]) {\n  axis.selectAll(\"text\").attr(\"fill\", t.inkSoft).style(\"font-size\", \"14px\");\n  axis.selectAll(\"line\").attr(\"stroke\", t.inkSoft);\n  axis.select(\".domain\").attr(\"stroke\", t.inkSoft);\n}\n\ng.append(\"text\")\n  .attr(\"x\", iw / 2)\n  .attr(\"y\", ih + 60)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"16px\")\n  .text(\"Longitude offset (°E)\");\n\ng.append(\"text\")\n  .attr(\"transform\", \"rotate(-90)\")\n  .attr(\"x\", -ih / 2)\n  .attr(\"y\", -70)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"16px\")\n  .text(\"Latitude offset (°N)\");\n\n// --- Colorbar legend ------------------------------------------------------------\nconst barX = iw + 50;\nconst barWidth = 24;\nconst barHeight = ih * 0.75;\nconst barY = (ih - barHeight) / 2;\n\nconst gradient = svg\n  .append(\"defs\")\n  .append(\"linearGradient\")\n  .attr(\"id\", \"contour-legend-gradient\")\n  .attr(\"x1\", \"0%\")\n  .attr(\"x2\", \"0%\")\n  .attr(\"y1\", \"0%\")\n  .attr(\"y2\", \"100%\");\nconst stopCount = 20;\nfor (let s = 0; s <= stopCount; s++) {\n  const frac = s / stopCount;\n  gradient\n    .append(\"stop\")\n    .attr(\"offset\", `${frac * 100}%`)\n    .attr(\"stop-color\", colorScale(maxAbs - frac * 2 * maxAbs));\n}\n\nconst legend = g.append(\"g\").attr(\"transform\", `translate(${barX},0)`);\nlegend\n  .append(\"rect\")\n  .attr(\"width\", barWidth)\n  .attr(\"height\", barHeight)\n  .attr(\"y\", barY)\n  .attr(\"fill\", \"url(#contour-legend-gradient)\")\n  .attr(\"stroke\", t.inkSoft)\n  .attr(\"stroke-width\", 1);\n\nconst legendScale = d3.scaleLinear().domain([-maxAbs, maxAbs]).range([barY + barHeight, barY]);\nconst legendAxis = legend\n  .append(\"g\")\n  .attr(\"transform\", `translate(${barWidth},0)`)\n  .call(d3.axisRight(legendScale).ticks(6).tickFormat(d3.format(\".1f\")));\nlegendAxis.selectAll(\"text\").attr(\"fill\", t.inkSoft).style(\"font-size\", \"13px\");\nlegendAxis.selectAll(\"line\").attr(\"stroke\", t.inkSoft);\nlegendAxis.select(\".domain\").attr(\"stroke\", t.inkSoft);\n\nlegend\n  .append(\"text\")\n  .attr(\"x\", barWidth / 2)\n  .attr(\"y\", barY - 16)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.inkSoft)\n  .style(\"font-size\", \"13px\")\n  .text(\"Anomaly (hPa)\");\n\n// --- Title ------------------------------------------------------------------\nsvg\n  .append(\"text\")\n  .attr(\"x\", W / 2)\n  .attr(\"y\", 44)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"22px\")\n  .style(\"font-weight\", \"600\")\n  .text(\"Pressure Anomaly · contour-filled · javascript · d3 · anyplot.ai\");\n"}