{"spec_id":"scatter-map-geographic","library":"makie","language":"julia","code":"# anyplot.ai\n# scatter-map-geographic: Scatter Map with Geographic Points\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 91/100 | Created: 2026-09-02\n\nusing CairoMakie\nusing Colors\nusing Random\n\nRandom.seed!(42)\n\n# --- Theme tokens -------------------------------------------------------\nTHEME       = get(ENV, \"ANYPLOT_THEME\", \"light\")\nPAGE_BG     = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nELEVATED_BG = THEME == \"light\" ? colorant\"#F2ECDD\" : colorant\"#2F2F29\"\nINK         = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nINK_SOFT    = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nLAND_FILL   = THEME == \"light\" ? colorant\"#EFEADA\" : colorant\"#332C1F\"\n\nIMPRINT_SEQ = cgrad([colorant\"#009E73\", colorant\"#4467A3\"])\n\n# --- Basemap: simplified continent silhouettes (equirectangular) -------\n# CairoMakie has no bundled world-map dataset (GeoMakie / NaturalEarth are\n# not installed), so the coastlines below are hand-digitized low-resolution\n# outlines — enough vertices to read as continents, not a survey product.\nnorth_america = Point2f.(\n    [-165, -165, -155, -130, -125, -124, -117, -110, -105, -97, -90, -84,\n     -97, -90, -81, -75, -70, -65, -60, -75, -95, -110, -140, -165],\n    [65, 55, 58, 55, 48, 40, 33, 24, 20, 16, 14, 9,\n     26, 29, 31, 35, 42, 45, 50, 60, 62, 68, 70, 65],\n)\nsouth_america = Point2f.(\n    [-77, -72, -60, -50, -35, -38, -48, -57, -62, -65, -68, -72, -71, -70,\n     -81, -79, -77],\n    [8, 11, 8, 0, -5, -13, -25, -35, -40, -50, -55, -52, -40, -18,\n     -5, 1, 8],\n)\nafrica = Point2f.(\n    [-17, -17, -10, 3, 9, 12, 12, 18, 26, 33, 40, 42, 51, 43, 38, 33, 25,\n     10, 0, -6, -17],\n    [21, 14, 6, 6, 4, -6, -18, -34, -33, -25, -15, 0, 12, 12, 15, 27, 32,\n     37, 35, 35, 21],\n)\n# Europe and Asia are one contiguous landmass at this simplification level —\n# a single Eurasia ring avoids a spurious seam where two separate rings\n# would otherwise leave a gap near the Urals.\neurasia = Point2f.(\n    [-9, -9, -2, 5, 20, 30, 60, 90, 140, 180, 160, 140, 130, 122, 110, 100,\n     95, 90, 80, 70, 60, 50, 45, 40, 35, 27, 19, 15, 12, 7, 3, -9],\n    [43, 53, 58, 62, 71, 70, 70, 75, 73, 66, 60, 45, 35, 30, 20, 8,\n     5, 22, 8, 20, 25, 25, 15, 15, 30, 41, 40, 38, 45, 43, 39, 43],\n)\naustralia = Point2f.(\n    [113, 122, 129, 137, 142, 145, 153, 150, 140, 135, 131, 129, 122, 115, 113],\n    [-22, -18, -14, -12, -11, -17, -28, -37, -38, -35, -32, -32, -34, -34, -22],\n)\ncontinents = (north_america, south_america, africa, eurasia, australia)\n\n# --- Data: global earthquake epicenters (magnitude + depth) ------------\n# Loosely follows real seismic belts (Ring of Fire, Alpide belt, mid-ocean\n# ridges) so the spatial pattern reads as plausible rather than uniform noise.\nclusters = [\n    (-72, -20, 4, 8, 25),    # Peru-Chile subduction zone\n    (-100, 17, 5, 5, 15),    # Mexico / Central America\n    (-122, 38, 4, 6, 12),    # California / Pacific Northwest\n    (-155, 57, 8, 3, 12),    # Alaska / Aleutians\n    (140, 37, 4, 5, 20),     # Japan\n    (118, -3, 8, 8, 25),     # Indonesia / Philippines\n    (175, -20, 6, 10, 15),   # Tonga / New Zealand\n    (35, 37, 10, 5, 15),     # Mediterranean / Anatolia\n    (85, 30, 10, 4, 12),     # Himalayan front\n    (-25, 5, 5, 30, 10),     # Mid-Atlantic ridge\n]\n\nlongitude = Float64[]\nlatitude  = Float64[]\nfor (clon, clat, lon_spread, lat_spread, n) in clusters\n    append!(longitude, clon .+ randn(n) .* lon_spread)\n    append!(latitude, clat .+ randn(n) .* lat_spread)\nend\nlatitude = clamp.(latitude, -70, 78)\n\nn_points  = length(longitude)\nmagnitude = clamp.(4.3 .+ abs.(randn(n_points)) .* 0.9, 4.0, 8.3)\ndepth_km  = clamp.(30 .+ abs.(randn(n_points)) .* 180, 5, 700)\n\nmag_to_size(m) = 6 + (m - 4.0) * 6.0\n\n# --- Plot ----------------------------------------------------------------\nfig = Figure(\n    resolution      = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title              = \"scatter-map-geographic · julia · makie · anyplot.ai\",\n    titlesize          = 20,\n    titlecolor         = INK,\n    xlabel             = \"Longitude (°)\",\n    ylabel             = \"Latitude (°)\",\n    xlabelsize         = 14,\n    ylabelsize         = 14,\n    xlabelcolor        = INK,\n    ylabelcolor        = INK,\n    xticklabelsize     = 12,\n    yticklabelsize     = 12,\n    xticklabelcolor    = INK_SOFT,\n    yticklabelcolor    = INK_SOFT,\n    xtickcolor         = INK_SOFT,\n    ytickcolor         = INK_SOFT,\n    xticks             = -180:60:180,\n    yticks             = -60:20:80,\n    backgroundcolor    = PAGE_BG,\n    topspinevisible    = false,\n    rightspinevisible  = false,\n    leftspinecolor     = INK_SOFT,\n    bottomspinecolor   = INK_SOFT,\n    xgridcolor         = RGBAf(INK.r, INK.g, INK.b, 0.15),\n    ygridcolor         = RGBAf(INK.r, INK.g, INK.b, 0.15),\n    xminorgridvisible  = false,\n    yminorgridvisible  = false,\n    # Plate-carree/equirectangular (raw lon/lat with DataAspect()), not a true\n    # Natural-Earth/Robinson projection — CairoMakie has no GeoMakie projection\n    # support installed, so this is an intentional, documented simplification.\n    aspect             = DataAspect(),\n)\nxlims!(ax, -180, 180)\nylims!(ax, -60, 80)\n\nfor outline in continents\n    poly!(ax, outline; color = LAND_FILL, strokecolor = INK_SOFT, strokewidth = 1)\nend\n\nsc = scatter!(\n    ax, longitude, latitude;\n    markersize  = mag_to_size.(magnitude),\n    color       = depth_km,\n    colormap    = IMPRINT_SEQ,\n    colorrange  = (0, 700),\n    alpha       = 0.75,\n    strokewidth = 0.75,\n    strokecolor = PAGE_BG,\n)\n\nColorbar(\n    fig[1, 2], sc;\n    label          = \"Depth (km)\",\n    labelcolor     = INK,\n    labelsize      = 13,\n    ticklabelsize  = 11,\n    ticklabelcolor = INK_SOFT,\n    tickcolor      = INK_SOFT,\n)\n\nlegend_magnitudes = [4.5, 6.0, 7.5]\nlegend_elements = [\n    MarkerElement(marker = :circle, color = INK_SOFT, markersize = mag_to_size(m))\n    for m in legend_magnitudes\n]\nLegend(\n    fig[1, 1], legend_elements, [\"M $(m)\" for m in legend_magnitudes], \"Magnitude\";\n    tellwidth       = false,\n    tellheight      = false,\n    halign          = :left,\n    valign          = :bottom,\n    margin          = (10, 10, 10, 10),\n    backgroundcolor = ELEVATED_BG,\n    framevisible    = true,\n    framecolor      = RGBAf(INK_SOFT.r, INK_SOFT.g, INK_SOFT.b, 0.35),\n    framewidth      = 1,\n    labelcolor      = INK_SOFT,\n    titlecolor      = INK,\n    labelsize       = 12,\n    titlesize       = 13,\n    patchsize       = (20, 20),\n)\n\n# --- Save ------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}