{"spec_id":"column-stratigraphic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// column-stratigraphic: Stratigraphic Column with Lithology Patterns\n// Library: echarts 5.5.1 | JavaScript 22.22.3\n// Quality: 94/100 | Created: 2026-06-17\n//# anyplot-orientation: landscape\n\nconst t = window.ANYPLOT_TOKENS;\nconst theme = window.ANYPLOT_THEME;\n\n// Theme-adaptive chrome\nconst INK = t.ink; // primary text\nconst INK_SOFT = t.inkSoft; // secondary text\nconst GRID = t.grid; // gridlines\n// Decal (lithology symbol) ink — texture flips with theme so the FGDC-style\n// hatch stays legible on the saturated Imprint fills on both surfaces.\nconst DECAL_INK = theme === \"dark\" ? \"rgba(240,239,232,0.82)\" : \"rgba(26,26,23,0.80)\";\n\n// --- Lithology key ----------------------------------------------------------\n// Each rock type pairs an Imprint hue with a decal approximating the standard\n// FGDC/USGS map symbol (dots = sandstone, dashes = shale, brick = limestone,\n// diagonal = siltstone, pebbles = conglomerate, fine laminae = mudstone).\n// Brand green (#009E73) anchors the first lithology; the semantic-red slot is\n// skipped — these are neutral earth categories.\nfunction decalFor(kind) {\n  switch (kind) {\n    case \"dots\": // sandstone — stipple\n      return { symbol: \"circle\", color: DECAL_INK, symbolSize: 0.4, dashArrayX: [6, 6], dashArrayY: [6, 6] };\n    case \"hdash\": // shale — broken horizontal dashes\n      return { symbol: \"rect\", color: DECAL_INK, symbolSize: 0.85, dashArrayX: [26, 16], dashArrayY: [6, 20] };\n    case \"brick\": // limestone — staggered blocks\n      return { symbol: \"rect\", color: DECAL_INK, symbolSize: 0.9,\n               dashArrayX: [[16, 5], [16, 5]], dashArrayY: [11, 5], maxTileWidth: 40, maxTileHeight: 32 };\n    case \"diagonal\": // siltstone — diagonal hatch\n      return { symbol: \"line\", color: DECAL_INK, dashArrayX: [1, 0], dashArrayY: [3, 8], rotation: Math.PI / 5 };\n    case \"pebbles\": // conglomerate — coarse clasts\n      return { symbol: \"circle\", color: DECAL_INK, symbolSize: 0.62, dashArrayX: [24, 18], dashArrayY: [22, 18] };\n    case \"fline\": // mudstone — fine continuous laminae\n      return { symbol: \"rect\", color: DECAL_INK, symbolSize: 0.7, dashArrayX: [44, 0], dashArrayY: [3, 12] };\n    default:\n      return { symbol: \"rect\", color: DECAL_INK };\n  }\n}\n\nconst LITHO = {\n  Sandstone: { color: t.palette[0], decal: \"dots\" }, // brand green — first series\n  Shale: { color: t.palette[1], decal: \"hdash\" }, // lavender\n  Limestone: { color: t.palette[2], decal: \"brick\" }, // blue\n  Siltstone: { color: t.palette[3], decal: \"diagonal\" }, // ochre\n  Conglomerate: { color: t.palette[5], decal: \"pebbles\" }, // cyan (skip red slot 5)\n  Mudstone: { color: t.palette[6], decal: \"fline\" }, // rose\n};\n\n// --- Data (synthetic well section, depth increasing downward) ---------------\n// Top→bottom borehole log spanning Paleocene to Late Jurassic.\nconst layers = [\n  { top: 0, bottom: 42, lithology: \"Sandstone\", formation: \"Willow Creek Fm\", age: \"Paleocene\" },\n  { top: 42, bottom: 108, lithology: \"Shale\", formation: \"Bearpaw Shale\", age: \"Late Cretaceous\" },\n  { top: 108, bottom: 152, lithology: \"Limestone\", formation: \"Niobrara Fm\", age: \"Late Cretaceous\" },\n  { top: 152, bottom: 205, lithology: \"Siltstone\", formation: \"Carlile Fm\", age: \"Late Cretaceous\" },\n  { top: 205, bottom: 246, lithology: \"Sandstone\", formation: \"Frontier Fm\", age: \"Late Cretaceous\" },\n  { top: 246, bottom: 318, lithology: \"Shale\", formation: \"Mowry Shale\", age: \"Early Cretaceous\" },\n  { top: 318, bottom: 352, lithology: \"Conglomerate\", formation: \"Dakota Cgl.\", age: \"Early Cretaceous\" },\n  { top: 352, bottom: 418, lithology: \"Limestone\", formation: \"Sundance Fm\", age: \"Late Jurassic\" },\n  { top: 418, bottom: 470, lithology: \"Mudstone\", formation: \"Morrison Fm\", age: \"Late Jurassic\" },\n];\nconst maxDepth = 470;\nconst BAR_WIDTH = 300; // chart px; column width (final PNG is 2×)\n\n// One stacked bar series per layer — decal + color carry the lithology; the\n// solid INK border draws the layer boundary lines.\nconst barSeries = layers.map((l) => ({\n  name: l.lithology,\n  type: \"bar\",\n  stack: \"section\",\n  barWidth: BAR_WIDTH,\n  data: [l.bottom - l.top],\n  itemStyle: {\n    color: LITHO[l.lithology].color,\n    decal: decalFor(LITHO[l.lithology].decal),\n    borderColor: INK,\n    borderWidth: 2,\n  },\n  emphasis: { disabled: true },\n}));\n\n// --- Label overlay: a custom series in the same grid draws the formation\n// (right of column) and age/period (left of column) for each layer. -----------\nfunction renderLabels(params, api) {\n  const i = params.dataIndex;\n  const l = layers[i];\n  const mid = (l.top + l.bottom) / 2;\n  const center = api.coord([0, mid]); // [centerX_px, y_px] for the single category\n  const cx = center[0];\n  const cy = center[1];\n  const half = BAR_WIDTH / 2;\n\n  const formation = {\n    type: \"text\",\n    style: {\n      text: l.formation,\n      x: cx + half + 20,\n      y: cy - 11,\n      textAlign: \"left\",\n      textVerticalAlign: \"middle\",\n      fill: INK,\n      fontSize: 17,\n      fontWeight: \"bold\",\n    },\n  };\n  const lithology = {\n    type: \"text\",\n    style: {\n      text: l.lithology,\n      x: cx + half + 20,\n      y: cy + 12,\n      textAlign: \"left\",\n      textVerticalAlign: \"middle\",\n      fill: INK_SOFT,\n      fontSize: 14,\n    },\n  };\n  const age = {\n    type: \"text\",\n    style: {\n      text: l.age,\n      x: cx - half - 20,\n      y: cy,\n      textAlign: \"right\",\n      textVerticalAlign: \"middle\",\n      fill: INK_SOFT,\n      fontSize: 15,\n    },\n  };\n  return { type: \"group\", children: [age, formation, lithology] };\n}\n\n// --- Init -------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  color: t.palette,\n  title: {\n    text: \"column-stratigraphic · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 14,\n    textStyle: { color: INK, fontSize: 22, fontWeight: \"bold\" },\n  },\n  legend: {\n    data: Object.keys(LITHO),\n    bottom: 16,\n    textStyle: { color: INK_SOFT, fontSize: 15 },\n    itemWidth: 34,\n    itemHeight: 18,\n    itemGap: 28,\n  },\n  grid: { left: 96, right: 40, top: 78, bottom: 78 },\n  xAxis: {\n    type: \"category\",\n    data: [\"Section\"],\n    show: false,\n  },\n  yAxis: {\n    type: \"value\",\n    name: \"Depth (m)\",\n    nameLocation: \"middle\",\n    nameGap: 58,\n    nameTextStyle: { color: INK, fontSize: 16, fontWeight: \"bold\" },\n    min: 0,\n    max: maxDepth,\n    inverse: true,\n    interval: 50,\n    axisLine: { lineStyle: { color: INK_SOFT } },\n    axisTick: { lineStyle: { color: INK_SOFT } },\n    axisLabel: { color: INK_SOFT, fontSize: 14 },\n    splitLine: { lineStyle: { color: GRID } },\n  },\n  series: [\n    ...barSeries,\n    {\n      name: \"labels\",\n      type: \"custom\",\n      renderItem: renderLabels,\n      data: layers.map((l) => [(l.top + l.bottom) / 2]),\n      silent: true,\n      z: 10,\n      tooltip: { show: false },\n    },\n  ],\n});\n"}