{"spec_id":"area-elevation-profile","library":"muix","language":"javascript","code":"// anyplot.ai\n// area-elevation-profile: Terrain Elevation Profile Along Transect\n// Library: muix 7.29.1 | JavaScript 22.22.3\n// Quality: 90/100 | Created: 2026-06-10\n//# anyplot-orientation: landscape\n// anyplot.ai\n// area-elevation-profile: Terrain Elevation Profile Along Transect\n// Library: muix 7.29.1 | JavaScript 22\n// License: @mui/x-charts — MIT (community). Pro/Premium are out of scope.\n// Quality: pending | Created: 2026-06-10\n\nimport { LineChart } from \"@mui/x-charts/LineChart\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\n\nconst t = window.ANYPLOT_TOKENS;\n\n// Tour du Mont Blanc — Alpine hiking segment, 40 km\n// 81 elevation samples at 500 m intervals (deterministic, no RNG)\nfunction lerp(a, b, f) {\n  return Math.round(a + (b - a) * Math.max(0, Math.min(1, f)));\n}\n\n// Control points [km, m]: key terrain waypoints along the route\nconst ctrl = [\n  [0, 1010], [2.5, 1280], [4, 1653], [6, 1710], [8, 1780],\n  [10, 2020], [13, 2195], [14, 2360], [15, 2516], [16.5, 2260],\n  [18, 1973], [20, 2180], [22, 2435], [24, 2230], [26, 2010],\n  [28, 1820], [30, 1650], [32, 1490], [34, 1370], [36, 1280],\n  [38, 1250], [40, 1224],\n];\n\nconst distKm = [];\nconst elevM = [];\nfor (let i = 0; i <= 80; i++) {\n  const d = i * 0.5;\n  distKm.push(d);\n  let j = 0;\n  while (j < ctrl.length - 2 && ctrl[j + 1][0] <= d) j++;\n  const [d0, e0] = ctrl[j];\n  const [d1, e1] = ctrl[j + 1];\n  elevM.push(lerp(e0, e1, (d - d0) / (d1 - d0)));\n}\n\n// Notable landmarks: mountain passes along the route\nconst landmarks = [\n  { x: 4,  elev: 1653, label: \"Col de Voza\" },\n  { x: 15, elev: 2516, label: \"Col de la Seigne\" },\n  { x: 22, elev: 2435, label: \"Col du Mont Favre\" },\n];\n\nconst TITLE = \"Tour du Mont Blanc · area-elevation-profile · javascript · muix · anyplot.ai\";\nconst TITLE_FS = Math.max(16, Math.round(22 * 67 / TITLE.length));\n\n// Chart coordinate space constants — shared by LineChart and overlay labels\nconst Y_MIN = 800;\nconst Y_MAX = 2700;\nconst X_MIN = 0;\nconst X_MAX = 40;\nconst MARGIN = { top: 20, right: 30, bottom: 72, left: 82 };\n\nfunction toPixel(dx, dy, chartW, chartH) {\n  const pw = chartW - MARGIN.left - MARGIN.right;\n  const ph = chartH - MARGIN.top - MARGIN.bottom;\n  return {\n    left: MARGIN.left + ((dx - X_MIN) / (X_MAX - X_MIN)) * pw,\n    top:  MARGIN.top  + (1 - (dy - Y_MIN) / (Y_MAX - Y_MIN)) * ph,\n  };\n}\n\nexport default function Chart() {\n  const W = window.ANYPLOT_SIZE.width;\n  const H = window.ANYPLOT_SIZE.height;\n\n  const PAD_TOP  = 16;\n  const HEADER_H = 52;\n  const FOOTER_H = 28;\n  const PAD_H    = 48;\n  const chartW   = W - PAD_H;\n  const chartH   = H - PAD_TOP - HEADER_H - FOOTER_H;\n\n  const startPx = toPixel(X_MIN, 1010, chartW, chartH);\n  const endPx   = toPixel(X_MAX, 1224, chartW, chartH);\n\n  return (\n    <div style={{\n      width:           W,\n      height:          H,\n      backgroundColor: t.pageBg,\n      display:         \"flex\",\n      flexDirection:   \"column\",\n      padding:         `${PAD_TOP}px 24px 0`,\n      boxSizing:       \"border-box\",\n      fontFamily:      \"sans-serif\",\n    }}>\n      {/* Chart title */}\n      <div style={{\n        color:       t.ink,\n        fontSize:    TITLE_FS,\n        fontWeight:  600,\n        lineHeight:  `${HEADER_H}px`,\n        flexShrink:  0,\n        whiteSpace:  \"nowrap\",\n        overflow:    \"hidden\",\n        textOverflow:\"ellipsis\",\n      }}>\n        {TITLE}\n      </div>\n\n      {/* Elevation profile chart with start/end overlay labels */}\n      <div style={{ position: \"relative\", width: chartW, height: chartH, flexShrink: 0 }}>\n        <LineChart\n          width={chartW}\n          height={chartH}\n          margin={MARGIN}\n          skipAnimation\n          grid={{ horizontal: true }}\n          xAxis={[{\n            data:           distKm,\n            label:          \"Distance (km)\",\n            min:            X_MIN,\n            max:            X_MAX,\n            tickMinStep:    5,\n            tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n            labelStyle:     { fontSize: 16, fill: t.ink },\n          }]}\n          yAxis={[{\n            label:          \"Elevation (m)\",\n            min:            Y_MIN,\n            max:            Y_MAX,\n            tickMinStep:    200,\n            tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n            labelStyle:     { fontSize: 16, fill: t.ink },\n          }]}\n          series={[{\n            data:      elevM,\n            area:      true,\n            showMark:  false,\n            color:     t.palette[0],\n            curve:     \"linear\",\n          }]}\n          sx={{\n            \"& .MuiAreaElement-root\": { fillOpacity: 0.35 },\n            \"& .MuiLineElement-root\": { strokeWidth: 2.5 },\n            \"& .MuiChartsGrid-line\":  { stroke: t.grid, strokeDasharray: \"4 3\" },\n            \"& .MuiChartsAxis-line\":  { stroke: t.inkSoft, strokeOpacity: 0.4 },\n            \"& .MuiChartsAxis-tick\":  { stroke: t.inkSoft, strokeOpacity: 0.4 },\n          }}\n        >\n          {landmarks.map(lm => (\n            <ChartsReferenceLine\n              key={lm.x}\n              x={lm.x}\n              label={`${lm.label} · ${lm.elev} m`}\n              labelAlign=\"start\"\n              lineStyle={{ stroke: t.inkSoft, strokeDasharray: \"5 3\", strokeWidth: 1.5 }}\n              labelStyle={{ fontSize: 12, fill: t.ink }}\n            />\n          ))}\n        </LineChart>\n\n        {/* Start point: Les Houches */}\n        <div style={{\n          position:      \"absolute\",\n          left:           startPx.left + 6,\n          top:            startPx.top - 46,\n          pointerEvents: \"none\",\n          fontFamily:    \"sans-serif\",\n        }}>\n          <div style={{ color: t.ink,     fontSize: 13, fontWeight: 700 }}>Les Houches</div>\n          <div style={{ color: t.inkSoft, fontSize: 12 }}>1010 m  ▶</div>\n        </div>\n\n        {/* End point: Courmayeur */}\n        <div style={{\n          position:      \"absolute\",\n          left:           endPx.left - 6,\n          top:            endPx.top - 46,\n          transform:     \"translateX(-100%)\",\n          textAlign:     \"right\",\n          pointerEvents: \"none\",\n          fontFamily:    \"sans-serif\",\n        }}>\n          <div style={{ color: t.ink,     fontSize: 13, fontWeight: 700 }}>Courmayeur</div>\n          <div style={{ color: t.inkSoft, fontSize: 12 }}>◀  1224 m</div>\n        </div>\n      </div>\n\n      {/* Footer: vertical exaggeration note */}\n      <div style={{\n        color:       t.inkSoft,\n        fontSize:    13,\n        textAlign:   \"center\",\n        lineHeight:  `${FOOTER_H}px`,\n        flexShrink:  0,\n        fontFamily:  \"sans-serif\",\n      }}>\n        Vertical exaggeration ≈ 10× · Tour du Mont Blanc (Italy / France)\n      </div>\n    </div>\n  );\n}\n"}