{"spec_id":"map-tile-background","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nmap-tile-background: Map with Tile Background\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-05-27\n\"\"\"\n\nimport os\nimport sys\n\n\n# Remove this script's directory from sys.path so \"plotly.py\" doesn't shadow\n# the installed plotly package when Python prepends the script dir.\n_script_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p) != _script_dir]\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\n# Theme-adaptive chrome\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# Anyplot sequential colorscale: brand green → blue (single-polarity continuous)\nimprint_seq = [[0.0, \"#009E73\"], [1.0, \"#4467A3\"]]\n\n# Theme-adaptive map tile style\nmap_style = \"open-street-map\" if THEME == \"light\" else \"carto-darkmatter\"\n\n# Data — European City Landmarks with annual visitor counts\nnp.random.seed(42)\n\nnames = [\n    \"Eiffel Tower\",\n    \"Colosseum\",\n    \"Sagrada Familia\",\n    \"Big Ben\",\n    \"Brandenburg Gate\",\n    \"Anne Frank House\",\n    \"Acropolis\",\n    \"Charles Bridge\",\n    \"St. Stephen's Basilica\",\n    \"Royal Palace\",\n    \"Manneken Pis\",\n    \"Tivoli Gardens\",\n    \"Schönbrunn Palace\",\n    \"Dubrovnik Old Walls\",\n    \"Rialto Bridge\",\n]\nlats = [\n    48.8584,  # Eiffel Tower\n    41.8902,  # Colosseum\n    41.4036,  # Sagrada Familia\n    51.5007,  # Big Ben\n    52.5163,  # Brandenburg Gate\n    52.3752,  # Anne Frank House\n    37.9715,  # Acropolis\n    50.0865,  # Charles Bridge\n    47.5008,  # St. Stephen's Basilica\n    59.3268,  # Royal Palace\n    50.8450,  # Manneken Pis\n    55.6736,  # Tivoli Gardens\n    48.1845,  # Schönbrunn Palace\n    42.6411,  # Dubrovnik Old Walls\n    45.4380,  # Rialto Bridge\n]\nlons = [\n    2.2945,  # Eiffel Tower\n    12.4922,  # Colosseum\n    2.1744,  # Sagrada Familia\n    -0.1246,  # Big Ben\n    13.3777,  # Brandenburg Gate\n    4.8840,  # Anne Frank House\n    23.7267,  # Acropolis\n    14.4114,  # Charles Bridge\n    19.0538,  # St. Stephen's Basilica\n    18.0717,  # Royal Palace\n    4.3499,  # Manneken Pis\n    12.5681,  # Tivoli Gardens\n    16.3119,  # Schönbrunn Palace\n    18.1085,  # Dubrovnik Old Walls\n    12.3358,  # Rialto Bridge\n]\nvisitors = np.array(\n    [\n        7_000_000,  # Eiffel Tower\n        7_400_000,  # Colosseum\n        4_500_000,  # Sagrada Familia\n        2_000_000,  # Big Ben\n        3_000_000,  # Brandenburg Gate\n        1_300_000,  # Anne Frank House\n        3_000_000,  # Acropolis\n        5_000_000,  # Charles Bridge\n        1_000_000,  # St. Stephen's Basilica\n        1_500_000,  # Royal Palace\n        500_000,  # Manneken Pis\n        4_000_000,  # Tivoli Gardens\n        4_000_000,  # Schönbrunn Palace\n        1_200_000,  # Dubrovnik Old Walls\n        5_000_000,  # Rialto Bridge\n    ]\n)\n\n# Scale visitor counts to marker diameters (15–50 px range)\nsizes = 15 + (visitors - visitors.min()) / (visitors.max() - visitors.min()) * 35\nvmin, vmax = int(visitors.min()), int(visitors.max())\n\n# Split into two groups to alternate textposition for the crowded NW Europe cluster:\n# \"bottom center\" for Big Ben, St. Stephen's, Schönbrunn, Manneken Pis, Colosseum, Rialto\nbottom_idx = {3, 8, 12, 10, 1, 14}\ntop_idx = set(range(len(names))) - bottom_idx\n\n\ndef _select(seq, indices):\n    return [seq[i] for i in sorted(indices)]\n\n\nfig = go.Figure()\n\n# Main trace (top center labels) — carries colorbar\nfig.add_trace(\n    go.Scattermap(\n        lat=_select(lats, top_idx),\n        lon=_select(lons, top_idx),\n        mode=\"markers+text\",\n        marker={\n            \"size\": _select(list(sizes), top_idx),\n            \"color\": _select(list(visitors), top_idx),\n            \"colorscale\": imprint_seq,\n            \"cmin\": vmin,\n            \"cmax\": vmax,\n            \"colorbar\": {\n                \"title\": {\"text\": \"Annual Visitors\", \"font\": {\"size\": 12, \"color\": INK}},\n                \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n                \"bgcolor\": ELEVATED_BG,\n                \"bordercolor\": INK_SOFT,\n                \"borderwidth\": 1,\n                \"thickness\": 18,\n                \"len\": 0.65,\n                \"tickformat\": \",.0f\",\n            },\n            \"opacity\": 0.85,\n        },\n        text=_select(names, top_idx),\n        textposition=\"top center\",\n        textfont={\"size\": 11, \"color\": INK},\n        hovertemplate=(\"<b>%{text}</b><br>Visitors: %{marker.color:,.0f}<extra></extra>\"),\n    )\n)\n\n# Secondary trace (bottom center labels) — no colorbar, same scale\nfig.add_trace(\n    go.Scattermap(\n        lat=_select(lats, bottom_idx),\n        lon=_select(lons, bottom_idx),\n        mode=\"markers+text\",\n        marker={\n            \"size\": _select(list(sizes), bottom_idx),\n            \"color\": _select(list(visitors), bottom_idx),\n            \"colorscale\": imprint_seq,\n            \"cmin\": vmin,\n            \"cmax\": vmax,\n            \"showscale\": False,\n            \"opacity\": 0.85,\n        },\n        text=_select(names, bottom_idx),\n        textposition=\"bottom center\",\n        textfont={\"size\": 11, \"color\": INK},\n        hovertemplate=(\"<b>%{text}</b><br>Visitors: %{marker.color:,.0f}<extra></extra>\"),\n    )\n)\n\nfig.update_layout(\n    autosize=False,\n    paper_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    title={\n        \"text\": (\n            \"European Landmarks by Visitor Count<br>\"\n            \"<span style='font-size:12px'>\"\n            \"map-tile-background · python · plotly · anyplot.ai\"\n            \"</span>\"\n        ),\n        \"font\": {\"size\": 16, \"color\": INK},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n    },\n    map={\"style\": map_style, \"center\": {\"lat\": 48.5, \"lon\": 10.0}, \"zoom\": 3.5},\n    margin={\"l\": 20, \"r\": 20, \"t\": 80, \"b\": 20},\n    showlegend=False,\n    annotations=[\n        {\n            \"text\": \"<b>★ Top attraction</b><br>Colosseum · 7.4M visitors/yr\",\n            \"xref\": \"paper\",\n            \"yref\": \"paper\",\n            \"x\": 0.68,\n            \"y\": 0.22,\n            \"showarrow\": False,\n            \"font\": {\"size\": 11, \"color\": \"#009E73\"},\n            \"bgcolor\": ELEVATED_BG,\n            \"bordercolor\": \"#009E73\",\n            \"borderwidth\": 1,\n            \"borderpad\": 5,\n            \"align\": \"left\",\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"}