{"spec_id":"contour-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\ncontour-basic: Basic Contour Plot\nLibrary: altair 6.2.2 | Python 3.13.14\nQuality: 86/100 | Updated: 2026-06-25\n\"\"\"\n\nimport importlib\nimport math\nimport os\nimport sys\n\nfrom PIL import Image\n\n\n# Drop this script's dir so `altair` resolves to the installed package, not this file\nsys.path[:] = [p for p in sys.path if os.path.abspath(p or \".\") != os.path.dirname(os.path.abspath(__file__))]\nalt = importlib.import_module(\"altair\")\nnp = importlib.import_module(\"numpy\")\npd = importlib.import_module(\"pandas\")\n\n# Theme tokens — Imprint palette, theme-adaptive chrome\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Data — topographic elevation of a 10 km × 10 km mountain region\nx = np.linspace(0, 10, 80)\ny = np.linspace(0, 10, 80)\nX, Y = np.meshgrid(x, y)\nelevation = (\n    850 * np.exp(-((X - 7) ** 2 + (Y - 7) ** 2) / 4.0)\n    + 550 * np.exp(-((X - 2.5) ** 2 + (Y - 3) ** 2) / 3.0)\n    - 180 * np.exp(-((X - 5) ** 2 + (Y - 5) ** 2) / 8.0)\n    + 12 * X\n    + 350\n)\ndf_fill = pd.DataFrame({\"x\": X.ravel(), \"y\": Y.ravel(), \"elevation\": elevation.ravel()})\n\n# Contour line segments — marching squares\nlevels = np.arange(400, 1251, 100)\nsegments = []\n# Collect all midpoints per major level for angular spread placement\nlevel_midpts = {lv: [] for lv in range(400, 1201, 200)}\n\nfor level in levels:\n    lv = int(level)\n    for i in range(len(y) - 1):\n        for j in range(len(x) - 1):\n            z00 = elevation[i, j]\n            z10 = elevation[i + 1, j]\n            z01 = elevation[i, j + 1]\n            z11 = elevation[i + 1, j + 1]\n            case = int(z00 >= level) | (int(z10 >= level) << 1) | (int(z01 >= level) << 2) | (int(z11 >= level) << 3)\n            if case == 0 or case == 15:\n                continue\n            x0, x1, y0, y1 = x[j], x[j + 1], y[i], y[i + 1]\n            edges = []\n            if (case & 1) != ((case >> 1) & 1):\n                t = (level - z00) / (z10 - z00) if z10 != z00 else 0.5\n                edges.append((x0, y0 + t * (y1 - y0)))\n            if ((case >> 1) & 1) != ((case >> 3) & 1):\n                t = (level - z10) / (z11 - z10) if z11 != z10 else 0.5\n                edges.append((x0 + t * (x1 - x0), y1))\n            if ((case >> 2) & 1) != ((case >> 3) & 1):\n                t = (level - z01) / (z11 - z01) if z11 != z01 else 0.5\n                edges.append((x1, y0 + t * (y1 - y0)))\n            if (case & 1) != ((case >> 2) & 1):\n                t = (level - z00) / (z01 - z00) if z01 != z00 else 0.5\n                edges.append((x0 + t * (x1 - x0), y0))\n            if len(edges) >= 2:\n                segments.append(\n                    {\"x1\": edges[0][0], \"y1\": edges[0][1], \"x2\": edges[1][0], \"y2\": edges[1][1], \"level\": float(lv)}\n                )\n                if lv % 200 == 0:\n                    mx = (edges[0][0] + edges[1][0]) / 2\n                    my = (edges[0][1] + edges[1][1]) / 2\n                    level_midpts[lv].append((mx, my))\n                if len(edges) == 4:\n                    segments.append(\n                        {\"x1\": edges[2][0], \"y1\": edges[2][1], \"x2\": edges[3][0], \"y2\": edges[3][1], \"level\": float(lv)}\n                    )\n\ndf_lines = pd.DataFrame(segments)\ndf_major = (\n    df_lines[df_lines[\"level\"] % 200 == 0].copy()\n    if not df_lines.empty\n    else pd.DataFrame(columns=[\"x1\", \"y1\", \"x2\", \"y2\", \"level\"])\n)\n\n# Spread elevation labels at distinct angles from main peak to avoid clustering\nPEAK_X, PEAK_Y = 7.0, 7.0\n# Each level targets a different angular direction from the main peak (degrees)\nspread_targets = {400: 0, 600: 72, 800: 144, 1000: 216, 1200: 288}\nlabel_pts = {}\nfor lv, pts in sorted(level_midpts.items()):\n    if not pts:\n        continue\n    target_rad = math.radians(spread_targets[lv])\n    best_score = float(\"inf\")\n    best_pt = None\n    for mx, my in pts:\n        ang = math.atan2(my - PEAK_Y, mx - PEAK_X)\n        diff = abs(ang - target_rad)\n        diff = min(diff, 2 * math.pi - diff)\n        if diff < best_score:\n            best_score = diff\n            best_pt = (mx, my)\n    if best_pt:\n        label_pts[lv] = best_pt\n\ndf_labels = pd.DataFrame([{\"x\": v[0], \"y\": v[1], \"label\": f\"{k} m\"} for k, v in sorted(label_pts.items())])\n\n# Plot title — 64 chars ≤ 67-char baseline, no font-size reduction needed\ntitle_str = \"Mountain Terrain · contour-basic · python · altair · anyplot.ai\"\n\n# Filled contour — step=0.125 matches data spacing (80 pts / 10 km) to minimize pixelation\nfilled = (\n    alt.Chart(df_fill)\n    .mark_rect()\n    .encode(\n        x=alt.X(\"x:Q\", bin=alt.Bin(step=0.125), title=\"Distance East (km)\"),\n        y=alt.Y(\"y:Q\", bin=alt.Bin(step=0.125), title=\"Distance North (km)\"),\n        color=alt.Color(\n            \"mean(elevation):Q\",\n            scale=alt.Scale(range=[\"#009E73\", \"#4467A3\"]),\n            title=\"Elevation (m)\",\n            legend=alt.Legend(titleFontSize=12, labelFontSize=10, gradientLength=200, gradientThickness=16),\n        ),\n        tooltip=[\n            alt.Tooltip(\"x:Q\", title=\"East (km)\", format=\".1f\"),\n            alt.Tooltip(\"y:Q\", title=\"North (km)\", format=\".1f\"),\n            alt.Tooltip(\"mean(elevation):Q\", title=\"Elevation (m)\", format=\".0f\"),\n        ],\n    )\n)\n\n# Minor contour lines (all levels, semi-transparent white)\nlines = (\n    alt.Chart(df_lines)\n    .mark_rule(strokeWidth=1.2, opacity=0.35, color=\"white\")\n    .encode(x=\"x1:Q\", y=\"y1:Q\", x2=\"x2:Q\", y2=\"y2:Q\")\n)\n\n# Major contour lines at 200 m intervals (thicker, more opaque)\nmajor_lines = (\n    alt.Chart(df_major)\n    .mark_rule(strokeWidth=2.2, opacity=0.90, color=\"white\")\n    .encode(x=\"x1:Q\", y=\"y1:Q\", x2=\"x2:Q\", y2=\"y2:Q\")\n)\n\n# Elevation labels spread radially across the field to avoid clustering at peak\nlevel_labels = (\n    alt.Chart(df_labels)\n    .mark_text(fontSize=11, fontWeight=\"bold\", color=INK, dx=4, dy=-5)\n    .encode(x=\"x:Q\", y=\"y:Q\", text=\"label:N\")\n)\n\nchart = (\n    (filled + lines + major_lines + level_labels)\n    .properties(\n        width=620, height=340, title=alt.Title(title_str, fontSize=16, anchor=\"middle\", color=INK), background=PAGE_BG\n    )\n    .interactive()\n    .configure_view(fill=PAGE_BG, stroke=None)\n    .configure_axis(\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        gridColor=INK,\n        gridOpacity=0.10,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        labelFontSize=10,\n        titleFontSize=12,\n        tickSize=6,\n    )\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\n# Save PNG then pad to exactly 3200 × 1800 (altair.md canvas rule)\nchart.save(f\"plot-{THEME}.png\", scale_factor=4.0)\n\nTW, TH = 3200, 1800\n_img = Image.open(f\"plot-{THEME}.png\").convert(\"RGB\")\n_w, _h = _img.size\nif _w > TW or _h > TH:\n    raise SystemExit(\n        f\"altair vl-convert produced {_w}×{_h}, exceeds target {TW}×{TH}. \"\n        \"Shrink chart .properties(width=, height=) values and re-render.\"\n    )\nif _w < TW or _h < TH:\n    _bg = (250, 248, 241) if THEME == \"light\" else (26, 26, 23)\n    _canvas = Image.new(\"RGB\", (TW, TH), _bg)\n    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))\n    _canvas.save(f\"plot-{THEME}.png\")\n\n# Save interactive HTML\nchart.save(f\"plot-{THEME}.html\")\n"}