{"spec_id":"heatmap-stripes-climate","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nheatmap-stripes-climate: Climate Warming Stripes\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-06-02\n\"\"\"\n\nimport sys\n\n\nsys.path = sys.path[1:]  # Prevent self-import: script dir is removed so 'import plotly' finds the package\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\n# Theme tokens\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Data\nnp.random.seed(42)\nyears = np.arange(1850, 2025)\nn_years = len(years)\n\ntrend = np.linspace(-0.35, 0.85, n_years)\nnoise = np.random.normal(0, 0.12, n_years)\n\nvolcanic_events = {1883: -0.25, 1912: -0.15, 1942: -0.20, 1991: -0.15}\nvolcanic_dips = np.zeros(n_years)\nfor year, dip in volcanic_events.items():\n    volcanic_dips[year - years[0]] = dip\n\nanomalies = trend + noise + volcanic_dips\nvmax = max(abs(anomalies.min()), abs(anomalies.max()))\n\n# Colorscale: imprint_div diverging (reversed: cold=blue at zmin, warm=red at zmax)\n# Midpoint is PAGE_BG so near-zero bars blend with the page surface (theme-adaptive)\ncolorscale = [\n    [0.0, \"#4467A3\"],  # cool / negative anomaly → Imprint blue\n    [0.5, PAGE_BG],  # neutral / zero anomaly  → theme-adaptive page surface\n    [1.0, \"#AE3030\"],  # warm / positive anomaly → Imprint matte red\n]\n\n# Hover labels per bar\nlabels = np.where(\n    anomalies > 0.3,\n    \"Strong warming\",\n    np.where(anomalies > 0, \"Warm\", np.where(anomalies > -0.3, \"Cool\", \"Strong cooling\")),\n)\n\n# Plot — warming stripes heatmap\nfig = go.Figure(\n    data=go.Heatmap(\n        z=[anomalies],\n        x=years,\n        y=[\"\"],\n        colorscale=colorscale,\n        zmin=-vmax,\n        zmax=vmax,\n        showscale=False,\n        xgap=0,\n        ygap=0,\n        customdata=[np.column_stack([labels])],\n        hovertemplate=\"<b>%{x}</b><br>Anomaly: %{z:+.2f} °C<br>%{customdata[0]}<extra></extra>\",\n    )\n)\n\n# Subtle decade markers (semi-transparent, adapts to theme)\ndecade_color = \"rgba(255,255,255,0.30)\" if THEME == \"light\" else \"rgba(0,0,0,0.40)\"\nfor decade in [1900, 1950, 2000]:\n    fig.add_shape(type=\"line\", x0=decade, x1=decade, y0=-0.5, y1=0.5, line={\"color\": decade_color, \"width\": 1.5})\n\n# Start / end year labels (theme-adaptive ink)\nfor x_pos, anchor, label in [(0.01, \"left\", \"1850\"), (0.99, \"right\", \"2024\")]:\n    fig.add_annotation(\n        x=x_pos,\n        y=-0.04,\n        text=label,\n        showarrow=False,\n        font={\"size\": 16, \"color\": INK_MUTED},\n        xanchor=anchor,\n        yanchor=\"top\",\n        xref=\"paper\",\n        yref=\"paper\",\n    )\n\ntitle_text = \"heatmap-stripes-climate · python · plotly · anyplot.ai\"\nfig.update_layout(\n    autosize=False,\n    title={\"text\": title_text, \"font\": {\"size\": 16, \"color\": INK}, \"x\": 0.5, \"xanchor\": \"center\"},\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    xaxis={\"showgrid\": False, \"showticklabels\": False, \"zeroline\": False, \"showline\": False, \"range\": [1849.5, 2024.5]},\n    yaxis={\"showgrid\": False, \"showticklabels\": False, \"zeroline\": False, \"showline\": False, \"fixedrange\": True},\n    margin={\"l\": 20, \"r\": 20, \"t\": 70, \"b\": 40},\n    showlegend=False,\n)\n\n# Save — landscape 3200×1800 canvas\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(\n    f\"plot-{THEME}.html\",\n    include_plotlyjs=\"cdn\",\n    config={\"displayModeBar\": False, \"scrollZoom\": False, \"staticPlot\": False},\n)\n"}