{"spec_id":"contour-map-geographic","library":"muix","language":"javascript","code":"// anyplot.ai\n// contour-map-geographic: Contour Lines on Geographic Map\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 89/100 | Updated: 2026-09-02\n\nimport { ChartContainer } from \"@mui/x-charts/ChartContainer\";\nimport { ChartsXAxis } from \"@mui/x-charts/ChartsXAxis\";\nimport { ChartsYAxis } from \"@mui/x-charts/ChartsYAxis\";\nimport { useDrawingArea } from \"@mui/x-charts/hooks\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst { width, height } = window.ANYPLOT_SIZE;\nconst THEME = window.ANYPLOT_THEME || \"light\";\nconst INK_MUTED = THEME === \"light\" ? \"#6B6A63\" : \"#A8A79F\";\n\n// --- Geographic domain: south-central Washington Cascades, home to three\n// well-known stratovolcanoes (lon/lat grid, WGS84 degrees) --------------------\nconst LON_MIN = -123.6;\nconst LON_MAX = -120.4;\nconst LAT_MIN = 45.5;\nconst LAT_MAX = 47.3;\n// Grid resolution: fine enough that marching-squares rounds out the tight\n// peak sigmas (~0.13-0.22 deg) into smooth isolines instead of faceting into\n// visible hexagons near the summits, and dense enough that adjacent raster\n// cells blend without visible seams.\nconst NLON = 101;\nconst NLAT = 61;\n\nconst LONS = Array.from(\n  { length: NLON },\n  (_, i) => LON_MIN + (i * (LON_MAX - LON_MIN)) / (NLON - 1),\n);\nconst LATS = Array.from(\n  { length: NLAT },\n  (_, j) => LAT_MIN + (j * (LAT_MAX - LAT_MIN)) / (NLAT - 1),\n);\n\n// Synthetic elevation field: a gently rising foothill baseline plus three\n// Gaussian peaks anchored at the real summit coordinates. Deterministic —\n// no RNG needed for a smooth terrain surface.\nconst PEAKS = [\n  { lon: -121.7603, lat: 46.8523, height: 4392, sigmaLon: 0.22, sigmaLat: 0.17, name: \"Mount Rainier\" },\n  { lon: -121.4906, lat: 46.2024, height: 3743, sigmaLon: 0.18, sigmaLat: 0.15, name: \"Mount Adams\" },\n  { lon: -122.1956, lat: 46.1912, height: 2549, sigmaLon: 0.16, sigmaLat: 0.13, name: \"Mount St. Helens\" },\n];\n\nfunction elevationAt(lon: number, lat: number): number {\n  // Baseline stays above the lowest contour level (see CONTOUR_STEP below) so\n  // the flat lowlands render as unbroken fill instead of a stray low-value\n  // isoline running the full width of the map.\n  const baseline = 460 + 60 * Math.sin(((lon - LON_MIN) / (LON_MAX - LON_MIN)) * Math.PI);\n  let elevation = baseline;\n  for (const p of PEAKS) {\n    const dLon = lon - p.lon;\n    const dLat = lat - p.lat;\n    elevation +=\n      p.height *\n      Math.exp(-((dLon * dLon) / (2 * p.sigmaLon * p.sigmaLon) + (dLat * dLat) / (2 * p.sigmaLat * p.sigmaLat)));\n  }\n  return elevation;\n}\n\nconst GRID: number[][] = LATS.map((lat) => LONS.map((lon) => elevationAt(lon, lat)));\n\nlet MIN_ELEV = Infinity;\nlet MAX_ELEV = -Infinity;\nfor (const row of GRID) {\n  for (const v of row) {\n    if (v < MIN_ELEV) MIN_ELEV = v;\n    if (v > MAX_ELEV) MAX_ELEV = v;\n  }\n}\n\n// Meaningful contour interval for this elevation range: 400 m intermediate\n// contours, with every other one (800 m) drawn heavier and labeled — the\n// classic topographic-map \"index contour\" convention.\nconst CONTOUR_STEP = 400;\nconst INDEX_STEP = 800;\nconst LEVELS: number[] = [];\nfor (let lvl = Math.ceil(MIN_ELEV / CONTOUR_STEP) * CONTOUR_STEP; lvl <= MAX_ELEV; lvl += CONTOUR_STEP) {\n  LEVELS.push(lvl);\n}\n\n// --- Marching squares: extract isoline segments (in lon/lat space) for one\n// contour level. Ambiguous 4-crossing (saddle) cells are resolved by\n// comparing the cell's mean value against the level. -------------------------\ntype Point = [number, number];\ntype Segment = [Point, Point];\n\nfunction marchingSquares(grid: number[][], lons: number[], lats: number[], level: number): Segment[] {\n  const segments: Segment[] = [];\n  const nLat = grid.length;\n  const nLon = grid[0].length;\n  for (let j = 0; j < nLat - 1; j++) {\n    for (let i = 0; i < nLon - 1; i++) {\n      const tl = grid[j][i];\n      const tr = grid[j][i + 1];\n      const bl = grid[j + 1][i];\n      const br = grid[j + 1][i + 1];\n      const x0 = lons[i];\n      const x1 = lons[i + 1];\n      const y0 = lats[j];\n      const y1 = lats[j + 1];\n      const edges: { v0: number; v1: number; p0: Point; p1: Point }[] = [\n        { v0: tl, v1: tr, p0: [x0, y0], p1: [x1, y0] }, // top\n        { v0: tr, v1: br, p0: [x1, y0], p1: [x1, y1] }, // right\n        { v0: bl, v1: br, p0: [x0, y1], p1: [x1, y1] }, // bottom\n        { v0: tl, v1: bl, p0: [x0, y0], p1: [x0, y1] }, // left\n      ];\n      const crossings: Point[] = [];\n      for (const e of edges) {\n        if ((e.v0 - level) * (e.v1 - level) < 0) {\n          const frac = (level - e.v0) / (e.v1 - e.v0);\n          crossings.push([e.p0[0] + frac * (e.p1[0] - e.p0[0]), e.p0[1] + frac * (e.p1[1] - e.p0[1])]);\n        }\n      }\n      if (crossings.length === 2) {\n        segments.push([crossings[0], crossings[1]]);\n      } else if (crossings.length === 4) {\n        const meanValue = (tl + tr + bl + br) / 4;\n        // crossings order here is [top, right, bottom, left]\n        if (meanValue >= level) {\n          segments.push([crossings[0], crossings[3]]);\n          segments.push([crossings[1], crossings[2]]);\n        } else {\n          segments.push([crossings[0], crossings[1]]);\n          segments.push([crossings[2], crossings[3]]);\n        }\n      }\n    }\n  }\n  return segments;\n}\n\nconst CONTOURS = LEVELS.map((level) => ({\n  level,\n  segments: marchingSquares(GRID, LONS, LATS, level),\n}));\n\n// --- Imprint sequential colormap for the filled raster (single-polarity data:\n// elevation only rises above the baseline) ------------------------------------\nfunction hexRgb(hex: string): [number, number, number] {\n  return [parseInt(hex.slice(1, 3), 16), parseInt(hex.slice(3, 5), 16), parseInt(hex.slice(5, 7), 16)];\n}\nconst SEQ_LOW = hexRgb(t.seq[0]);\nconst SEQ_HIGH = hexRgb(t.seq[1]);\nfunction elevationColor(value: number): string {\n  const frac = Math.max(0, Math.min(1, (value - MIN_ELEV) / (MAX_ELEV - MIN_ELEV)));\n  const r = Math.round(SEQ_LOW[0] + (SEQ_HIGH[0] - SEQ_LOW[0]) * frac);\n  const g = Math.round(SEQ_LOW[1] + (SEQ_HIGH[1] - SEQ_LOW[1]) * frac);\n  const b = Math.round(SEQ_LOW[2] + (SEQ_HIGH[2] - SEQ_LOW[2]) * frac);\n  return `rgb(${r},${g},${b})`;\n}\n\nconst CELLS = [];\nfor (let j = 0; j < NLAT - 1; j++) {\n  for (let i = 0; i < NLON - 1; i++) {\n    const mean = (GRID[j][i] + GRID[j][i + 1] + GRID[j + 1][i] + GRID[j + 1][i + 1]) / 4;\n    CELLS.push({\n      lonMin: LONS[i],\n      lonMax: LONS[i + 1],\n      latMin: LATS[j],\n      latMax: LATS[j + 1],\n      color: elevationColor(mean),\n    });\n  }\n}\n\n// The Columbia River forms the domain's southern geographic anchor — a real\n// hydrological feature, hand-traced as a gently meandering polyline.\nconst RIVER: Point[] = [\n  [-123.6, 45.62],\n  [-123.1, 45.65],\n  [-122.6, 45.6],\n  [-122.1, 45.68],\n  [-121.6, 45.72],\n  [-121.1, 45.66],\n  [-120.4, 45.6],\n];\n\nconst TITLE_STR = \"Cascade Range Elevation Contours · contour-map-geographic · javascript · muix · anyplot.ai\";\nconst TITLE_FONT_SIZE = Math.max(15, Math.round(22 * Math.min(1, 67 / TITLE_STR.length)));\n\nconst MARGIN = { top: 130, right: 310, bottom: 110, left: 110 };\n\n// --- Filled elevation raster + isolines, positioned via the drawing area's\n// own pixel rectangle so lon/lat map exactly onto the axes below. ------------\nfunction ContourLayer() {\n  const { left, top, width: areaW, height: areaH } = useDrawingArea();\n  const xOf = (lon: number) => left + ((lon - LON_MIN) / (LON_MAX - LON_MIN)) * areaW;\n  const yOf = (lat: number) => top + (1 - (lat - LAT_MIN) / (LAT_MAX - LAT_MIN)) * areaH;\n  // Peak-name pills sit in a fixed box directly above each summit. Index-\n  // contour labels default to the ring's middle segment (as before), but if\n  // that spot falls inside a peak-name pill, we scan outward along the ring\n  // for the nearest segment that clears every pill instead.\n  const peakLabelBoxes = PEAKS.map((p) => {\n    const cx = xOf(p.lon);\n    const cy = yOf(p.lat);\n    return { x0: cx - 44, x1: cx + 44, y0: cy - 52, y1: cy - 34 };\n  });\n  const clearsPeakLabels = (mx: number, my: number) =>\n    peakLabelBoxes.every((b) => mx + 28 < b.x0 || mx - 28 > b.x1 || my + 10 < b.y0 || my - 10 > b.y1);\n  const pickLabelSegment = (segments: Segment[]): Segment => {\n    const mid = Math.floor(segments.length / 2);\n    for (let d = 0; d < segments.length; d++) {\n      for (const idx of d === 0 ? [mid] : [mid + d, mid - d]) {\n        if (idx < 0 || idx >= segments.length) continue;\n        const seg = segments[idx];\n        const mx = (xOf(seg[0][0]) + xOf(seg[1][0])) / 2;\n        const my = (yOf(seg[0][1]) + yOf(seg[1][1])) / 2;\n        if (clearsPeakLabels(mx, my)) return seg;\n      }\n    }\n    return segments[mid];\n  };\n\n  return (\n    <g>\n      {CELLS.map((cell, idx) => (\n        <rect\n          key={idx}\n          x={xOf(cell.lonMin)}\n          y={yOf(cell.latMax)}\n          width={xOf(cell.lonMax) - xOf(cell.lonMin)}\n          height={yOf(cell.latMin) - yOf(cell.latMax)}\n          fill={cell.color}\n          stroke=\"none\"\n        />\n      ))}\n      {CONTOURS.map(({ level, segments }) => {\n        const isIndex = level % INDEX_STEP === 0;\n        const d = segments\n          .map(([[lon0, lat0], [lon1, lat1]]) => `M${xOf(lon0)},${yOf(lat0)} L${xOf(lon1)},${yOf(lat1)}`)\n          .join(\" \");\n        return (\n          <path\n            key={level}\n            d={d}\n            fill=\"none\"\n            stroke={t.ink}\n            strokeWidth={isIndex ? 2.2 : 1}\n            strokeOpacity={isIndex ? 0.85 : 0.4}\n            strokeLinecap=\"round\"\n          />\n        );\n      })}\n      {CONTOURS.filter(({ level }) => level % INDEX_STEP === 0 && level > MIN_ELEV).map(({ level, segments }) => {\n        if (segments.length === 0) return null;\n        const [[lon0, lat0], [lon1, lat1]] = pickLabelSegment(segments);\n        const cx = (xOf(lon0) + xOf(lon1)) / 2;\n        const cy = (yOf(lat0) + yOf(lat1)) / 2;\n        return (\n          <g key={`label-${level}`}>\n            <rect x={cx - 28} y={cy - 12} width={56} height={20} rx={4} fill={t.pageBg} opacity={0.85} />\n            <text x={cx} y={cy + 4} textAnchor=\"middle\" fontSize={13} fontWeight={600} fill={t.ink}>\n              {level} m\n            </text>\n          </g>\n        );\n      })}\n      <polyline\n        points={RIVER.map(([lon, lat]) => `${xOf(lon)},${yOf(lat)}`).join(\" \")}\n        fill=\"none\"\n        stroke={t.palette[2]}\n        strokeWidth={3}\n        strokeLinecap=\"round\"\n        strokeOpacity={0.75}\n      />\n      <g>\n        <rect x={xOf(RIVER[0][0]) + 2} y={yOf(RIVER[0][1]) - 22} width={104} height={18} rx={4} fill={t.pageBg} opacity={0.85} />\n        <text\n          x={xOf(RIVER[0][0]) + 6}\n          y={yOf(RIVER[0][1]) - 10}\n          fontSize={13}\n          fontStyle=\"italic\"\n          fill={INK_MUTED}\n        >\n          Columbia River\n        </text>\n      </g>\n      {PEAKS.map((p) => {\n        const cx = xOf(p.lon);\n        const cy = yOf(p.lat);\n        // Peak names sit well above the summit, clear of the tight innermost\n        // contour ring and its numeric label (which land right at the\n        // summit point for the tallest peaks) — no separate summit dot, to\n        // avoid colliding with that ring label.\n        return (\n          <g key={p.name}>\n            <rect x={cx - 44} y={cy - 52} width={88} height={18} rx={4} fill={t.pageBg} opacity={0.85} />\n            <text x={cx} y={cy - 39} textAnchor=\"middle\" fontSize={12} fontWeight={600} fill={t.ink}>\n              {p.name}\n            </text>\n          </g>\n        );\n      })}\n    </g>\n  );\n}\n\n// --- Vertical colorbar legend for the continuous elevation field ------------\nfunction Colorbar() {\n  const { left, top, width: areaW, height: areaH } = useDrawingArea();\n  const barX = left + areaW + 60;\n  const barW = 24;\n  const barTop = top;\n  const barBottom = top + areaH;\n  const ticks = [0, 0.25, 0.5, 0.75, 1];\n\n  return (\n    <g>\n      <defs>\n        <linearGradient id=\"elevationRamp\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n          <stop offset=\"0%\" stopColor={t.seq[1]} />\n          <stop offset=\"100%\" stopColor={t.seq[0]} />\n        </linearGradient>\n      </defs>\n      <text x={barX + barW / 2} y={barTop - 16} textAnchor=\"middle\" fontSize={14} fill={t.ink}>\n        Elevation (m)\n      </text>\n      <rect x={barX} y={barTop} width={barW} height={barBottom - barTop} fill=\"url(#elevationRamp)\" rx={3} />\n      {ticks.map((f) => {\n        const value = Math.round(MIN_ELEV + f * (MAX_ELEV - MIN_ELEV));\n        const y = barBottom - f * (barBottom - barTop);\n        return (\n          <g key={f}>\n            <line x1={barX + barW} y1={y} x2={barX + barW + 6} y2={y} stroke={t.inkSoft} strokeWidth={1} />\n            <text x={barX + barW + 12} y={y + 4} fontSize={13} fill={t.inkSoft}>\n              {value}\n            </text>\n          </g>\n        );\n      })}\n    </g>\n  );\n}\n\nfunction ChartTitle() {\n  const { top } = useDrawingArea();\n  return (\n    <>\n      <text x={width / 2} y={top - 62} textAnchor=\"middle\" fontSize={TITLE_FONT_SIZE} fontWeight={600} fill={t.ink}>\n        {TITLE_STR}\n      </text>\n      <text x={width / 2} y={top - 36} textAnchor=\"middle\" fontSize={14} fill={t.inkSoft}>\n        Rainier, Adams &amp; St. Helens · index contours every {INDEX_STEP} m, intermediate every {CONTOUR_STEP} m\n      </text>\n    </>\n  );\n}\n\nexport default function Chart() {\n  return (\n    <ChartContainer\n      width={width}\n      height={height}\n      series={[]}\n      skipAnimation\n      margin={MARGIN}\n      xAxis={[\n        {\n          scaleType: \"linear\",\n          min: LON_MIN,\n          max: LON_MAX,\n          label: \"Longitude\",\n          valueFormatter: (v: number) => `${Math.abs(v).toFixed(1)}°W`,\n          tickLabelStyle: { fontSize: 13, fill: t.inkSoft },\n          labelStyle: { fontSize: 16, fill: t.ink },\n        },\n      ]}\n      yAxis={[\n        {\n          scaleType: \"linear\",\n          min: LAT_MIN,\n          max: LAT_MAX,\n          label: \"Latitude\",\n          valueFormatter: (v: number) => `${v.toFixed(1)}°N`,\n          // ChartsYAxis offsets the rotated axis label using this deprecated\n          // spacing prop (tickFontSize + tickSize + 10), not the actual\n          // rendered tick text width — bump it well past the true tick font\n          // size (set via tickLabelStyle below) so \"46.4°N\"-width labels\n          // don't collide with the axis title.\n          tickFontSize: 56,\n          tickLabelStyle: { fontSize: 13, fill: t.inkSoft },\n          labelStyle: { fontSize: 16, fill: t.ink },\n        },\n      ]}\n      sx={{\n        \"& .MuiChartsAxis-line\": { stroke: t.inkSoft },\n        \"& .MuiChartsAxis-tick\": { stroke: t.inkSoft },\n      }}\n    >\n      <ChartTitle />\n      <ContourLayer />\n      <ChartsXAxis />\n      <ChartsYAxis />\n      <Colorbar />\n    </ChartContainer>\n  );\n}\n"}