{"spec_id":"heatmap-stripes-climate","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nheatmap-stripes-climate: Climate Warming Stripes\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-06-02\n\"\"\"\n\nimport os as _os\nimport sys as _sys\n\n\n# The script is named altair.py; prevent it from shadowing the installed library\n# when Python's sys.path[0] points at this file's own directory.\n_p = _os.path.abspath(_sys.path[0]) if _sys.path else \"\"\nif _p and _os.path.exists(_os.path.join(_p, \"altair.py\")):\n    _sys.path = _sys.path[1:]\ndel _sys, _os, _p\n\nimport os\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\n\n\n# Theme tokens — Imprint palette\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\n\n# Data — synthetic global temperature anomalies (1850–2024) mimicking HadCRUT pattern\nnp.random.seed(42)\nyears = np.arange(1850, 2025)\nn = len(years)\n\nbaseline_trend = np.piecewise(\n    years.astype(float),\n    [years < 1910, (years >= 1910) & (years < 1980), years >= 1980],\n    [lambda y: -0.2 + (y - 1850) * (-0.001), lambda y: -0.3 + (y - 1910) * 0.005, lambda y: 0.05 + (y - 1980) * 0.022],\n)\nnoise = np.random.normal(0, 0.08, n)\nanomaly = baseline_trend + noise\ndf = pd.DataFrame({\"year\": years, \"anomaly\": anomaly})\n\n# Imprint diverging colormap: cold → blue (#4467A3), zero → page bg, warm → red (#AE3030)\nmax_abs = max(abs(anomaly.min()), abs(anomaly.max()))\ncolor_scale = alt.Scale(\n    domain=[-max_abs, 0, max_abs], range=[\"#4467A3\", PAGE_BG, \"#AE3030\"], type=\"linear\", interpolate=\"lab\"\n)\n\n# Warming stripes — no axes, no labels, no tick marks, no gridlines per spec\nstripes = (\n    alt.Chart(df)\n    .mark_rect()\n    .encode(\n        x=alt.X(\"year:O\", axis=None),\n        color=alt.Color(\"anomaly:Q\", scale=color_scale, legend=None),\n        tooltip=[alt.Tooltip(\"year:O\", title=\"Year\"), alt.Tooltip(\"anomaly:Q\", title=\"Anomaly (°C)\", format=\"+.2f\")],\n    )\n)\n\ntitle_str = \"heatmap-stripes-climate · python · altair · anyplot.ai\"\n\nchart = (\n    stripes.properties(\n        width=620,\n        height=320,\n        background=PAGE_BG,\n        title=alt.Title(title_str, fontSize=16, anchor=\"middle\", fontWeight=\"bold\", color=INK, offset=10),\n    )\n    .configure_view(strokeWidth=0, fill=PAGE_BG)\n    .configure_title(color=INK)\n)\n\n# Save PNG and pad to canonical 3200×1800\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        f\"Shrink chart .properties(width=, height=) values and re-render.\"\n    )\nif _w < TW or _h < TH:\n    _canvas = Image.new(\"RGB\", (TW, TH), PAGE_BG)\n    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))\n    _canvas.save(f\"plot-{THEME}.png\")\n\nchart.save(f\"plot-{THEME}.html\")\n"}