{"spec_id":"heatmap-geographic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nheatmap-geographic: Geographic Heatmap for Spatial Density\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-05-19\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    coord_fixed,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_polygon,\n    geom_tile,\n    ggplot,\n    labs,\n    scale_fill_gradient,\n    theme,\n    theme_minimal,\n)\nfrom scipy.ndimage import gaussian_filter\nfrom scipy.stats import gaussian_kde\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\"\nOCEAN_BG = \"#D4E8F7\" if THEME == \"light\" else \"#1A2A3A\"\nCONTINENT_COLOR = \"#505050\" if THEME == \"light\" else \"#A0A09A\"\n\n# Data\nnp.random.seed(42)\n\n# Generate synthetic earthquake event data (Pacific Ring of Fire and major seismic zones)\n\njapan_lon = np.random.normal(140, 3, 400)\njapan_lat = np.random.normal(36, 4, 400)\n\nindo_lon = np.random.normal(115, 8, 350)\nindo_lat = np.random.normal(-5, 4, 350)\n\nchile_lon = np.random.normal(-72, 2, 300)\nchile_lat = np.random.normal(-30, 10, 300)\n\ncalif_lon = np.random.normal(-120, 3, 250)\ncalif_lat = np.random.normal(37, 5, 250)\n\nphil_lon = np.random.normal(122, 3, 200)\nphil_lat = np.random.normal(12, 4, 200)\n\nnz_lon = np.random.normal(175, 3, 150)\nnz_lat = np.random.normal(-40, 3, 150)\n\nmed_lon = np.random.normal(25, 8, 200)\nmed_lat = np.random.normal(38, 3, 200)\n\nscatter_lon = np.random.uniform(-170, 170, 150)\nscatter_lat = np.random.uniform(-50, 60, 150)\n\nall_lon = np.concatenate([japan_lon, indo_lon, chile_lon, calif_lon, phil_lon, nz_lon, med_lon, scatter_lon])\nall_lat = np.concatenate([japan_lat, indo_lat, chile_lat, calif_lat, phil_lat, nz_lat, med_lat, scatter_lat])\n\n# Kernel density estimation on a geographic grid\nlon_grid = np.linspace(-180, 180, 120)\nlat_grid = np.linspace(-60, 80, 70)\nlon_mesh, lat_mesh = np.meshgrid(lon_grid, lat_grid)\n\npoints = np.vstack([all_lon, all_lat])\nkde = gaussian_kde(points, bw_method=0.08)\ndensity = kde(np.vstack([lon_mesh.ravel(), lat_mesh.ravel()]))\ndensity = density.reshape(lon_mesh.shape)\ndensity = gaussian_filter(density, sigma=1.5)\n\nheatmap_rows = []\nfor i in range(len(lat_grid)):\n    for j in range(len(lon_grid)):\n        heatmap_rows.append({\"longitude\": lon_grid[j], \"latitude\": lat_grid[i], \"density\": density[i, j]})\n\ndf_heatmap = pd.DataFrame(heatmap_rows)\n\n# Filter low-density ocean tiles to show clear hotspots only\nthreshold = df_heatmap[\"density\"].quantile(0.45)\ndf_heatmap = df_heatmap[df_heatmap[\"density\"] > threshold].copy()\n\n# Simplified continent outlines for geographic context\ncontinents = []\n\nna_lon = [\n    -170,\n    -168,\n    -140,\n    -125,\n    -124,\n    -117,\n    -105,\n    -97,\n    -82,\n    -77,\n    -68,\n    -55,\n    -52,\n    -80,\n    -87,\n    -97,\n    -105,\n    -125,\n    -145,\n    -165,\n    -170,\n]\nna_lat = [60, 65, 70, 55, 48, 33, 25, 26, 25, 35, 45, 48, 45, 27, 30, 20, 22, 50, 60, 55, 60]\nfor i in range(len(na_lon)):\n    continents.append({\"continent\": \"N. America\", \"order\": i, \"lon\": na_lon[i], \"lat\": na_lat[i]})\n\nsa_lon = [-80, -68, -60, -50, -35, -40, -50, -55, -68, -72, -75, -80, -82, -80]\nsa_lat = [10, 12, 5, 0, -5, -22, -35, -52, -55, -18, -5, 0, 8, 10]\nfor i in range(len(sa_lon)):\n    continents.append({\"continent\": \"S. America\", \"order\": i, \"lon\": sa_lon[i], \"lat\": sa_lat[i]})\n\neu_lon = [-10, 0, 10, 20, 30, 40, 50, 60, 50, 35, 25, 20, 10, 0, -10, -10]\neu_lat = [35, 37, 36, 35, 35, 40, 45, 55, 70, 70, 70, 65, 60, 50, 40, 35]\nfor i in range(len(eu_lon)):\n    continents.append({\"continent\": \"Europe\", \"order\": i, \"lon\": eu_lon[i], \"lat\": eu_lat[i]})\n\naf_lon = [-17, -5, 10, 35, 50, 52, 43, 35, 30, 15, 0, -17, -17]\naf_lat = [15, 37, 37, 32, 12, 0, -25, -35, -35, -25, 5, 20, 15]\nfor i in range(len(af_lon)):\n    continents.append({\"continent\": \"Africa\", \"order\": i, \"lon\": af_lon[i], \"lat\": af_lat[i]})\n\nas_lon = [60, 80, 100, 120, 140, 145, 140, 130, 105, 100, 80, 60, 45, 30, 25, 30, 35, 50, 60]\nas_lat = [55, 70, 75, 70, 55, 45, 35, 30, 0, 5, 10, 25, 30, 35, 42, 55, 70, 70, 55]\nfor i in range(len(as_lon)):\n    continents.append({\"continent\": \"Asia\", \"order\": i, \"lon\": as_lon[i], \"lat\": as_lat[i]})\n\nau_lon = [113, 125, 135, 145, 152, 150, 140, 130, 115, 113]\nau_lat = [-22, -15, -12, -15, -25, -38, -38, -33, -35, -22]\nfor i in range(len(au_lon)):\n    continents.append({\"continent\": \"Australia\", \"order\": i, \"lon\": au_lon[i], \"lat\": au_lat[i]})\n\ndf_continents = pd.DataFrame(continents)\n\n# Plot\nplot = (\n    ggplot()\n    + geom_tile(aes(x=\"longitude\", y=\"latitude\", fill=\"density\"), data=df_heatmap, width=3.1, height=2.1, alpha=0.90)\n    + scale_fill_gradient(low=\"#FFFFCC\", high=\"#BD0026\", name=\"Density\")\n    + geom_polygon(\n        aes(x=\"lon\", y=\"lat\", group=\"continent\"), data=df_continents, fill=\"none\", color=CONTINENT_COLOR, size=0.8\n    )\n    + coord_fixed(ratio=1.0, xlim=(-180, 180), ylim=(-60, 80))\n    + labs(\n        title=\"Seismic Activity Density · heatmap-geographic · python · plotnine · anyplot.ai\",\n        x=\"Longitude (°)\",\n        y=\"Latitude (°)\",\n    )\n    + theme_minimal()\n    + theme(\n        figure_size=(16, 9),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=OCEAN_BG),\n        panel_grid_major=element_blank(),\n        panel_grid_minor=element_blank(),\n        axis_line=element_line(color=INK_SOFT, size=0.5),\n        plot_title=element_text(size=24, weight=\"bold\", color=INK),\n        axis_title=element_text(size=20, color=INK),\n        axis_text=element_text(size=16, color=INK_SOFT),\n        legend_title=element_text(size=18, color=INK),\n        legend_text=element_text(size=14, color=INK_SOFT),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_position=\"right\",\n    )\n)\n\n# Save\nplot.save(f\"plot-{THEME}.png\", dpi=300, verbose=False)\n"}