{"spec_id":"scatter-map-geographic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nscatter-map-geographic: Scatter Map with Geographic Points\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-18\n\"\"\"\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\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nLAND_COLOR = \"#E5E5E5\" if THEME == \"light\" else \"#3A3A36\"\nOCEAN_COLOR = \"#D4E8F2\" if THEME == \"light\" else \"#2A3F4D\"\nCOAST_COLOR = \"#999999\" if THEME == \"light\" else \"#666666\"\nCOUNTRY_COLOR = \"#CCCCCC\" if THEME == \"light\" else \"#555555\"\n\n# Data: Global environmental sensor network monitoring air quality\nnp.random.seed(42)\n\n# Sensor locations distributed across continents\n# Urban and industrial regions with higher sensor density\nn_points = 85\n\n# North America - Eastern US industrial corridor\nn_na = 25\nna_lat = np.concatenate(\n    [\n        np.random.uniform(40, 45, 15),  # Northeast corridor\n        np.random.uniform(32, 38, 10),  # Southeast\n    ]\n)\nna_lon = np.concatenate(\n    [\n        np.random.uniform(-82, -70, 15),  # Northeast\n        np.random.uniform(-85, -75, 10),  # Southeast\n    ]\n)\n\n# Europe - Industrial centers\nn_eu = 20\neu_lat = np.concatenate(\n    [\n        np.random.uniform(50, 55, 10),  # Central Europe\n        np.random.uniform(45, 50, 10),  # Mediterranean\n    ]\n)\neu_lon = np.concatenate(\n    [\n        np.random.uniform(5, 15, 10),  # Central Europe\n        np.random.uniform(10, 25, 10),  # Mediterranean\n    ]\n)\n\n# Asia - Rapid development zones\nn_asia = 25\nasia_lat = np.concatenate(\n    [\n        np.random.uniform(30, 40, 12),  # China, India\n        np.random.uniform(10, 20, 8),  # Southeast Asia\n        np.random.uniform(-10, 10, 5),  # Indonesia\n    ]\n)\nasia_lon = np.concatenate(\n    [\n        np.random.uniform(100, 120, 12),  # China, India\n        np.random.uniform(95, 110, 8),  # Southeast Asia\n        np.random.uniform(110, 140, 5),  # Indonesia\n    ]\n)\n\n# Africa - Growing urban centers\nn_africa = 10\nafrica_lat = np.random.uniform(-35, 20, n_africa)\nafrica_lon = np.random.uniform(-20, 55, n_africa)\n\n# Australia-Pacific\nn_pac = 5\npac_lat = np.random.uniform(-40, -15, n_pac)\npac_lon = np.random.uniform(110, 180, n_pac)\n\n# Combine all data\nlatitudes = np.concatenate([na_lat, eu_lat, asia_lat, africa_lat, pac_lat])\nlongitudes = np.concatenate([na_lon, eu_lon, asia_lon, africa_lon, pac_lon])\n\n# Air quality index (AQI) values scaled 0-500 (higher = worse air)\naqi_values = np.concatenate(\n    [\n        np.random.uniform(20, 120, n_na),  # North America - moderate\n        np.random.uniform(25, 140, n_eu),  # Europe - moderate to high\n        np.random.uniform(30, 250, n_asia),  # Asia - wide range\n        np.random.uniform(15, 180, n_africa),  # Africa - growing industrial\n        np.random.uniform(20, 80, n_pac),  # Pacific - cleaner\n    ]\n)\n\n# Measurement counts (number of readings per sensor in the last month)\nmeasurement_counts = np.concatenate(\n    [\n        np.random.uniform(15, 30, n_na),\n        np.random.uniform(20, 30, n_eu),\n        np.random.uniform(10, 30, n_asia),\n        np.random.uniform(8, 25, n_africa),\n        np.random.uniform(12, 28, n_pac),\n    ]\n)\n\n# Scale point sizes based on measurement counts\nsizes = (measurement_counts - measurement_counts.min()) / (\n    measurement_counts.max() - measurement_counts.min()\n)\nsizes = sizes * 28 + 6  # Scale to 6-34 range for visibility\n\n# Create hover text\nhover_texts = [\n    f\"AQI: {aqi:.0f}<br>Readings: {count:.0f}\"\n    for aqi, count in zip(aqi_values, measurement_counts, strict=True)\n]\n\n# Create figure with geographic scatter\nfig = go.Figure()\n\nfig.add_trace(\n    go.Scattergeo(\n        lat=latitudes,\n        lon=longitudes,\n        mode=\"markers\",\n        marker={\n            \"size\": sizes,\n            \"color\": aqi_values,\n            \"colorscale\": \"Viridis\",\n            \"colorbar\": {\n                \"title\": {\"text\": \"AQI\", \"font\": {\"size\": 20}},\n                \"tickfont\": {\"size\": 16},\n                \"len\": 0.6,\n                \"thickness\": 25,\n                \"x\": 1.02,\n            },\n            \"line\": {\"width\": 1, \"color\": INK},\n            \"opacity\": 0.85,\n        },\n        text=hover_texts,\n        hovertemplate=\"<b>Lat:</b> %{lat:.2f}°<br><b>Lon:</b> %{lon:.2f}°<br>%{text}<extra></extra>\",\n    )\n)\n\n# Layout with geographic projection\nfig.update_layout(\n    title={\n        \"text\": \"scatter-map-geographic · python · plotly · anyplot.ai\",\n        \"font\": {\"size\": 28, \"color\": INK},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n    },\n    geo={\n        \"projection_type\": \"natural earth\",\n        \"showland\": True,\n        \"landcolor\": LAND_COLOR,\n        \"showocean\": True,\n        \"oceancolor\": OCEAN_COLOR,\n        \"showcoastlines\": True,\n        \"coastlinecolor\": COAST_COLOR,\n        \"coastlinewidth\": 1,\n        \"showcountries\": True,\n        \"countrycolor\": COUNTRY_COLOR,\n        \"countrywidth\": 0.5,\n        \"showlakes\": True,\n        \"lakecolor\": OCEAN_COLOR,\n        \"bgcolor\": PAGE_BG,\n    },\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    margin={\"l\": 20, \"r\": 100, \"t\": 80, \"b\": 20},\n    font={\"color\": INK},\n)\n\n# Add size legend annotation\nfig.add_annotation(\n    x=1.02,\n    y=0.15,\n    xref=\"paper\",\n    yref=\"paper\",\n    text=\"<b>Point Size</b><br>= # Readings\",\n    showarrow=False,\n    font={\"size\": 16, \"color\": INK},\n    align=\"left\",\n)\n\n# Save as PNG and HTML\nscript_dir = os.path.dirname(os.path.abspath(__file__))\nfig.write_image(\n    os.path.join(script_dir, f\"plot-{THEME}.png\"), width=1600, height=900, scale=3\n)\nfig.write_html(os.path.join(script_dir, f\"plot-{THEME}.html\"), include_plotlyjs=\"cdn\")\n"}