Map with Tile Background — Pygal

A map visualization that displays data points on top of tile-based backgrounds such as OpenStreetMap, Stamen, or satellite imagery. Unlike vector-based basemaps, tile backgrounds provide rich geographic context including street-level detail, terrain, or satellite imagery that loads dynamically as the user navigates. This visualization is essential for location-based analysis where real-world geographic context enhances data interpretation.

Map with Tile Background rendered with Pygal

Python source (Pygal)

""" anyplot.ai
map-tile-background: Map with Tile Background
Library: pygal 3.1.0 | Python 3.13.13
Quality: 80/100 | Updated: 2026-05-27
"""

import importlib
import os
import sys


# Remove cwd from path to avoid importing this file instead of the pygal library
_cwd = sys.path[0] if sys.path and sys.path[0] else None
if _cwd:
    sys.path.remove(_cwd)
_pygal = importlib.import_module("pygal")
_style_mod = importlib.import_module("pygal.style")
if _cwd:
    sys.path.insert(0, _cwd)

Style = _style_mod.Style

# Theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")

PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
# Map-inspired plot background simulates geographic tile context
# Light: warm land beige; Dark: near-black land surface
LAND_BG = "#eae6df" if THEME == "light" else "#1A1A17"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"

# Semantic color exception: temperature carries strong cold→blue / hot→red expectation
# (Imprint palette positions 3, 1, 5 in semantic role order)
COLOR_COOL = "#4467A3"  # blue  — cool stations (annual mean < 12 °C)
COLOR_MILD = "#009E73"  # green — mild stations (12–22 °C)
COLOR_WARM = "#AE3030"  # red   — warm stations (> 22 °C)

# Data: US weather monitoring network — mean annual surface temperatures
stations = [
    {"label": "Seattle", "lat": 47.6, "lon": -122.3, "temp_c": 11.0},
    {"label": "Portland", "lat": 45.5, "lon": -122.7, "temp_c": 12.0},
    {"label": "San Francisco", "lat": 37.8, "lon": -122.4, "temp_c": 13.5},
    {"label": "Los Angeles", "lat": 34.1, "lon": -118.2, "temp_c": 19.0},
    {"label": "Phoenix", "lat": 33.4, "lon": -112.1, "temp_c": 28.5},
    {"label": "Denver", "lat": 39.7, "lon": -104.9, "temp_c": 10.5},
    {"label": "Dallas", "lat": 32.8, "lon": -96.8, "temp_c": 19.5},
    {"label": "Houston", "lat": 29.8, "lon": -95.4, "temp_c": 24.5},
    {"label": "Minneapolis", "lat": 44.9, "lon": -93.2, "temp_c": 7.5},
    {"label": "Chicago", "lat": 41.9, "lon": -87.6, "temp_c": 9.5},
    {"label": "Detroit", "lat": 42.3, "lon": -83.0, "temp_c": 8.5},
    {"label": "Atlanta", "lat": 33.7, "lon": -84.4, "temp_c": 17.5},
    {"label": "Miami", "lat": 25.8, "lon": -80.2, "temp_c": 27.0},
    {"label": "Washington DC", "lat": 38.9, "lon": -77.0, "temp_c": 13.5},
    {"label": "New York", "lat": 40.7, "lon": -74.0, "temp_c": 12.5},
    {"label": "Boston", "lat": 42.4, "lon": -71.1, "temp_c": 10.0},
    {"label": "Nashville", "lat": 36.2, "lon": -86.8, "temp_c": 15.5},
    {"label": "New Orleans", "lat": 30.0, "lon": -90.1, "temp_c": 23.0},
    {"label": "Kansas City", "lat": 39.1, "lon": -94.6, "temp_c": 12.5},
    {"label": "Salt Lake City", "lat": 40.8, "lon": -111.9, "temp_c": 11.5},
    {"label": "Las Vegas", "lat": 36.2, "lon": -115.2, "temp_c": 20.0},
    {"label": "Albuquerque", "lat": 35.1, "lon": -106.7, "temp_c": 14.5},
    {"label": "Memphis", "lat": 35.1, "lon": -90.0, "temp_c": 18.0},
    {"label": "Charlotte", "lat": 35.2, "lon": -80.8, "temp_c": 16.0},
    {"label": "Tampa", "lat": 28.0, "lon": -82.5, "temp_c": 25.0},
]

# Continental US bounds; chart xrange padded to give tick labels breathing room
lat_min, lat_max = 24, 51
lon_min, lon_max = -128, -62
chart_xrange = (-132, -58)

title = "map-tile-background · python · pygal · anyplot.ai"
n = len(title)
ratio = 67 / n if n > 67 else 1.0
title_font_size = max(44, round(66 * ratio))

custom_style = Style(
    background=PAGE_BG,
    plot_background=LAND_BG,
    foreground=INK,
    foreground_strong=INK,
    foreground_subtle=INK_MUTED,
    colors=(COLOR_COOL, COLOR_MILD, COLOR_WARM),
    title_font_size=title_font_size,
    label_font_size=56,
    major_label_font_size=44,
    legend_font_size=44,
    value_font_size=36,
    stroke_width=2.5,
    opacity=0.9,
    opacity_hover=1.0,
)

chart = _pygal.XY(
    width=3200,
    height=1800,
    style=custom_style,
    title=title,
    x_title="Longitude (°)",
    y_title="Latitude (°)",
    show_legend=True,
    legend_at_bottom=True,
    legend_box_size=36,
    show_x_guides=True,
    show_y_guides=True,
    dots_size=18,
    stroke=False,
    margin=60,
    margin_top=110,
    margin_bottom=240,
    margin_left=200,
    margin_right=80,
    range=(lat_min, lat_max),
    xrange=chart_xrange,
    print_values=False,
    truncate_legend=-1,
    truncate_label=-1,
    explicit_size=True,
)

# Group stations by annual mean temperature
cool_pts = []
mild_pts = []
warm_pts = []

for s in stations:
    pt = {"value": (s["lon"], s["lat"]), "label": f"{s['label']}: {s['temp_c']} °C"}
    if s["temp_c"] < 12:
        cool_pts.append(pt)
    elif s["temp_c"] < 22:
        mild_pts.append(pt)
    else:
        warm_pts.append(pt)

chart.add("Cool (< 12 °C)", cool_pts, dots_size=16)
chart.add("Mild (12–22 °C)", mild_pts, dots_size=20)
chart.add("Warm (> 22 °C)", warm_pts, dots_size=24)

chart.x_labels = [f"{lon}°" for lon in range(lon_min, lon_max + 1, 10)]
chart.y_labels = [f"{lat}°" for lat in range(lat_min, lat_max + 1, 5)]

# Save PNG and interactive HTML
chart.render_to_png(f"plot-{THEME}.png")

with open(f"plot-{THEME}.html", "wb") as f:
    f.write(chart.render())

Part of Map with Tile Background on anyplot.ai.

Other implementations