{"spec_id":"contour-map-geographic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\ncontour-map-geographic: Contour Lines on Geographic Map\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 84/100 | Updated: 2026-05-20\n\"\"\"\n\nimport os\nimport sys\n\n\n# Remove the script's own directory from sys.path so the sibling matplotlib.py\n# implementation file does not shadow the system matplotlib package.\n_here = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if p and os.path.abspath(p) != _here]\n\nimport matplotlib\n\n\nmatplotlib.use(\"Agg\")\nimport matplotlib.colors as mcolors\nimport matplotlib.pyplot as plt\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\"\nLAND_COLOR = \"#D8D3BB\" if THEME == \"light\" else \"#2A2A25\"\nOCEAN_BG = \"#C5D8E8\" if THEME == \"light\" else \"#19242E\"\nCOAST_COLOR = \"#888880\" if THEME == \"light\" else \"#666660\"\n\n# Data - Simulated North Atlantic SST\nnp.random.seed(42)\nlat_range = np.linspace(30, 60, 60)\nlon_range = np.linspace(-60, 0, 60)\nlon_grid, lat_grid = np.meshgrid(lon_range, lat_range)\n\nbase_temp = 20 - 0.5 * (lat_grid - 30)\ngulf_stream = 3 * np.exp(-((lon_grid + 30) ** 2) / 400)\nvariation = 2 * np.sin(lat_grid / 5) * np.cos(lon_grid / 8)\ntemperature = base_temp + gulf_stream + variation\n\nT_MIN, T_MAX = 4, 24\nlevels = np.arange(T_MIN, T_MAX + 1, 2)\n\n# Viridis colormap — perceptually-uniform, spec-compliant for sequential temperature data\nnorm = mcolors.Normalize(vmin=T_MIN, vmax=T_MAX)\ncmap = plt.get_cmap(\"viridis\")\n\n# Compute contour paths using matplotlib off-screen (allsegs API, matplotlib 3.8+)\n_fig, _ax = plt.subplots()\ncs_fill = _ax.contourf(lon_range, lat_range, temperature, levels=levels, cmap=cmap, norm=norm)\ncs_line = _ax.contour(lon_range, lat_range, temperature, levels=levels)\nplt.close(\"all\")\n\nfig = go.Figure()\n\n# Filled contour patches on the geographic map.\n# Plotly's fill='toself' fills the interior for polygons whose top edge touches\n# lat=60 (the data domain top), but fills the exterior for all other polygons.\n# Reversing the vertex order for non-top-touching polygons corrects the winding.\nfor i, segs in enumerate(cs_fill.allsegs):\n    mid_val = (levels[i] + levels[i + 1]) / 2\n    r, g, b, _ = cmap(norm(mid_val))\n    fill_color = f\"rgba({int(r * 255)},{int(g * 255)},{int(b * 255)},0.85)\"\n    for seg in segs:\n        if len(seg) < 3:\n            continue\n        seg = seg[::-1] if seg[:, 1].max() < 59.9 else seg\n        fig.add_trace(\n            go.Scattergeo(\n                lon=seg[:, 0].tolist(),\n                lat=seg[:, 1].tolist(),\n                mode=\"lines\",\n                fill=\"toself\",\n                fillcolor=fill_color,\n                line=dict(width=0),\n                showlegend=False,\n                hoverinfo=\"skip\",\n            )\n        )\n\n# Contour isolines as Scattergeo line traces\nfor i, segs in enumerate(cs_line.allsegs):\n    level_val = float(cs_line.levels[i])\n    for seg in segs:\n        if len(seg) < 2:\n            continue\n        fig.add_trace(\n            go.Scattergeo(\n                lon=seg[:, 0].tolist(),\n                lat=seg[:, 1].tolist(),\n                mode=\"lines\",\n                line=dict(width=1, color=INK_SOFT),\n                showlegend=False,\n                hovertemplate=f\"{level_val:.0f}°C<extra></extra>\",\n            )\n        )\n\n# Isoline value labels at midpoints of selected levels (every 4°C)\nlabel_levels = {8.0, 12.0, 16.0, 20.0}\nfor i, segs in enumerate(cs_line.allsegs):\n    level_val = float(cs_line.levels[i])\n    if level_val not in label_levels:\n        continue\n    for seg in segs:\n        if len(seg) < 10:\n            continue\n        mid = len(seg) // 2\n        fig.add_trace(\n            go.Scattergeo(\n                lon=[float(seg[mid, 0])],\n                lat=[float(seg[mid, 1])],\n                mode=\"text\",\n                text=[f\"{level_val:.0f}°C\"],\n                textfont=dict(size=9, color=INK),\n                showlegend=False,\n                hoverinfo=\"skip\",\n            )\n        )\n\n# Dummy trace for standalone colorbar\nfig.add_trace(\n    go.Scattergeo(\n        lon=[None],\n        lat=[None],\n        mode=\"markers\",\n        marker=dict(\n            color=[0],\n            colorscale=\"viridis\",\n            cmin=T_MIN,\n            cmax=T_MAX,\n            showscale=True,\n            colorbar=dict(\n                title=dict(text=\"Temperature (°C)\", font=dict(size=12, color=INK)),\n                tickfont=dict(size=10, color=INK_SOFT),\n                len=0.75,\n                thickness=20,\n                bgcolor=ELEVATED_BG,\n                bordercolor=INK_SOFT,\n                borderwidth=1,\n                x=1.0,\n            ),\n        ),\n        showlegend=False,\n    )\n)\n\n# Native Plotly geographic basemap with Natural Earth coastlines and borders\nfig.update_geos(\n    projection_type=\"mercator\",\n    lataxis_range=[27, 63],\n    lonaxis_range=[-64, 4],\n    showcoastlines=True,\n    coastlinecolor=COAST_COLOR,\n    coastlinewidth=1.5,\n    showland=True,\n    landcolor=LAND_COLOR,\n    showocean=True,\n    oceancolor=OCEAN_BG,\n    showlakes=True,\n    lakecolor=OCEAN_BG,\n    showcountries=True,\n    countrycolor=COAST_COLOR,\n    countrywidth=0.5,\n    bgcolor=OCEAN_BG,\n    showframe=True,\n    framecolor=INK_SOFT,\n    framewidth=1,\n)\n\nfig.update_layout(\n    autosize=False,\n    paper_bgcolor=PAGE_BG,\n    title=dict(\n        text=\"North Atlantic SST · contour-map-geographic · python · plotly · anyplot.ai\",\n        font=dict(size=16, color=INK),\n        x=0.5,\n        xanchor=\"center\",\n    ),\n    font=dict(color=INK),\n    margin=dict(l=40, r=120, t=80, b=40),\n    annotations=[\n        dict(\n            x=0.12,\n            y=0.72,\n            text=\"Newfoundland\",\n            showarrow=False,\n            font=dict(size=10, color=INK_SOFT),\n            xref=\"paper\",\n            yref=\"paper\",\n        ),\n        dict(\n            x=0.85,\n            y=0.68,\n            text=\"Ireland\",\n            showarrow=False,\n            font=dict(size=10, color=INK_SOFT),\n            xref=\"paper\",\n            yref=\"paper\",\n        ),\n        dict(\n            x=0.60,\n            y=0.20,\n            text=\"Azores\",\n            showarrow=False,\n            font=dict(size=10, color=INK_SOFT),\n            xref=\"paper\",\n            yref=\"paper\",\n        ),\n    ],\n)\n\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}