{"spec_id":"contour-map-geographic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\ncontour-map-geographic: Contour Lines on Geographic Map\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 88/100 | Created: 2026-05-20\n\"\"\"\n\nimport os\nimport sys\n\n\n# Remove script directory so \"import plotnine\" resolves to the library, not this file\n_sd = os.path.abspath(os.path.dirname(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p) != _sd]\n\nimport matplotlib\n\n\nmatplotlib.use(\"Agg\")\n\nimport numpy as np\nimport pandas as pd\nfrom contourpy import contour_generator\nfrom plotnine import (\n    aes,\n    coord_cartesian,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_label,\n    geom_path,\n    geom_raster,\n    ggplot,\n    labs,\n    scale_fill_cmap,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n)\nfrom scipy.ndimage import gaussian_filter\n\n\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# Synthetic atmospheric pressure (hPa) over North Atlantic / Europe\nnp.random.seed(42)\nlon_vec = np.linspace(-30, 50, 80)\nlat_vec = np.linspace(35, 75, 60)\nlon_grid, lat_grid = np.meshgrid(lon_vec, lat_vec)\n\nbase = 1013.0\n# Azores High — subtropical anticyclone\npressure = base + 18 * np.exp(-((lon_grid + 20) ** 2 + (lat_grid - 37) ** 2) / 350)\n# Icelandic Low — subpolar cyclone\npressure -= 22 * np.exp(-((lon_grid + 15) ** 2 + (lat_grid - 65) ** 2) / 180)\n# Baltic / Scandinavian High\npressure += 10 * np.exp(-((lon_grid - 22) ** 2 + (lat_grid - 57) ** 2) / 220)\n# Gentle Mediterranean trough\npressure -= 6 * np.exp(-((lon_grid - 12) ** 2 + (lat_grid - 40) ** 2) / 280)\npressure = gaussian_filter(pressure, sigma=3)\n\ndf_raster = pd.DataFrame({\"lon\": lon_grid.ravel(), \"lat\": lat_grid.ravel(), \"pressure\": pressure.ravel()})\n\n# Compute isobar paths using contourpy (pure path-computation library, no pyplot)\np_lo = int(np.floor(pressure.min() / 4) * 4)\np_hi = int(np.ceil(pressure.max() / 4) * 4)\ncontour_levels = list(range(p_lo, p_hi + 4, 4))\n\ngen = contour_generator(x=lon_vec, y=lat_vec, z=pressure)\n\nisobar_rows: list = []\nlabel_rows: list = []\nfor i, level in enumerate(contour_levels):\n    segs = gen.lines(level)\n    if not segs:\n        continue\n    # Label every 2nd contour level using midpoint of the longest segment\n    if i % 2 == 0:\n        longest = max(segs, key=len)\n        mid = len(longest) // 2\n        label_rows.append({\"lon\": float(longest[mid, 0]), \"lat\": float(longest[mid, 1]), \"label\": str(int(level))})\n    for seg_idx, seg in enumerate(segs):\n        if len(seg) < 2:\n            continue\n        group = f\"iso_{level:.0f}_{seg_idx}\"\n        for lon_c, lat_c in seg:\n            isobar_rows.append({\"lon\": lon_c, \"lat\": lat_c, \"group\": group})\n\ndf_isobars = pd.DataFrame(isobar_rows)\ndf_labels = pd.DataFrame(label_rows)\n\n# Simplified European geographic outlines (hardcoded, no geopandas required)\n# Western European continental coast (south → north)\neuro_coast = {\n    \"lons\": [-5.6, -9.0, -9.5, -9.2, -8.7, -9.2, -1.8, -1.5, -4.7, -2.5, -1.5, 0.0, 1.5, 2.5, 4.0, 5.0, 8.0, 8.5, 5.0],\n    \"lats\": [\n        36.1,\n        37.0,\n        38.5,\n        39.5,\n        43.0,\n        42.9,\n        43.5,\n        44.0,\n        48.0,\n        47.5,\n        49.0,\n        49.2,\n        50.5,\n        51.0,\n        52.5,\n        53.0,\n        55.0,\n        57.0,\n        58.0,\n    ],\n    \"group\": \"euro\",\n}\n# British Isles (closed polygon)\nuk_coast = {\n    \"lons\": [-5.7, 0.0, 1.7, 1.5, 0.5, -1.0, -2.5, -4.5, -6.0, -5.5, -4.5, -4.0, -3.5, -4.5, -5.0, -5.7],\n    \"lats\": [50.1, 51.0, 52.5, 53.5, 54.5, 57.5, 58.0, 58.5, 57.5, 56.0, 55.5, 54.5, 53.5, 52.5, 51.5, 50.1],\n    \"group\": \"uk\",\n}\n# Scandinavian Peninsula (closed polygon)\nscand_coast = {\n    \"lons\": [5.0, 8.0, 11.0, 12.5, 16.0, 18.0, 20.0, 22.0, 24.0, 27.0, 28.5, 25.0, 20.0, 16.0, 14.0, 12.0, 10.0, 5.0],\n    \"lats\": [\n        57.5,\n        57.5,\n        56.0,\n        56.0,\n        57.5,\n        59.0,\n        60.0,\n        63.0,\n        65.5,\n        69.0,\n        71.0,\n        71.0,\n        68.0,\n        66.0,\n        64.0,\n        62.0,\n        59.5,\n        57.5,\n    ],\n    \"group\": \"scand\",\n}\n# Iceland\niceland_coast = {\n    \"lons\": [-13.5, -18.0, -24.0, -24.5, -22.0, -18.0, -14.0, -13.5, -13.5],\n    \"lats\": [63.5, 63.5, 64.0, 65.5, 66.5, 66.5, 65.5, 64.5, 63.5],\n    \"group\": \"iceland\",\n}\n\noutline_rows = []\nfor feature in [euro_coast, uk_coast, scand_coast, iceland_coast]:\n    for lon_c, lat_c in zip(feature[\"lons\"], feature[\"lats\"], strict=True):\n        outline_rows.append({\"lon\": lon_c, \"lat\": lat_c, \"group\": feature[\"group\"]})\n\ndf_outlines = pd.DataFrame(outline_rows)\n\nanyplot_theme = theme(\n    figure_size=(8, 4.5),\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG),\n    panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10),\n    panel_grid_minor=element_blank(),\n    panel_border=element_rect(color=INK_SOFT, fill=None),\n    axis_title=element_text(color=INK, size=10),\n    axis_text=element_text(color=INK_SOFT, size=8),\n    axis_line=element_line(color=INK_SOFT),\n    plot_title=element_text(color=INK, size=12),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_text=element_text(color=INK_SOFT, size=8),\n    legend_title=element_text(color=INK, size=9),\n)\n\nlon_breaks = [-20, -10, 0, 10, 20, 30, 40]\nlon_labels = [f\"{abs(x)}°W\" if x < 0 else (\"0°\" if x == 0 else f\"{x}°E\") for x in lon_breaks]\nlat_breaks = [40, 50, 60, 70]\nlat_labels = [f\"{y}°N\" for y in lat_breaks]\n\ntitle = \"contour-map-geographic · python · plotnine · anyplot.ai\"\n\nplot = (\n    ggplot(df_raster, aes(\"lon\", \"lat\"))\n    + geom_raster(aes(fill=\"pressure\"))\n    + geom_path(\n        data=df_isobars,\n        mapping=aes(x=\"lon\", y=\"lat\", group=\"group\"),\n        color=INK_SOFT,\n        size=0.4,\n        alpha=0.65,\n        inherit_aes=False,\n    )\n    + geom_path(data=df_outlines, mapping=aes(x=\"lon\", y=\"lat\", group=\"group\"), color=INK, size=0.6, inherit_aes=False)\n    + geom_label(\n        data=df_labels,\n        mapping=aes(x=\"lon\", y=\"lat\", label=\"label\"),\n        color=INK,\n        fill=PAGE_BG,\n        size=6,\n        label_padding=0.1,\n        label_size=0.2,\n        inherit_aes=False,\n    )\n    + scale_fill_cmap(cmap_name=\"BrBG\", name=\"Pressure\\n(hPa)\")\n    + scale_x_continuous(breaks=lon_breaks, labels=lon_labels)\n    + scale_y_continuous(breaks=lat_breaks, labels=lat_labels)\n    + coord_cartesian(xlim=(-30, 50), ylim=(35, 75))\n    + labs(x=\"Longitude\", y=\"Latitude\", title=title)\n    + anyplot_theme\n)\n\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=8, height=4.5, units=\"in\", verbose=False)\n"}