Plotly Maps & Geo Cookbook





Plotly Maps & Geo Cookbook (Python) | Pythoneo






Plotly Maps & Geo Cookbook (Python)

Scatter Mapbox, choropleths, projections, custom GeoJSON, clustering, animations, performance, and troubleshooting—copy‑paste ready.

Updated: 2025‑08‑20
Covers: Plotly 5/6, Mapbox, GeoJSON
License: CC BY 4.0
Tiles

Scatter Mapbox (Tiles, Tokens, Clustering)

import plotly.express as px

df = px.data.carshare()
fig = px.scatter_mapbox(
    df, lat="centroid_lat", lon="centroid_lon",
    color="peak_hour", size="car_hours",
    hover_name="peak_hour", zoom=10, height=480
)
# Free tiles:
fig.update_layout(mapbox_style="open-street-map")
# or Mapbox styles (requires token): streets, outdoors, light, dark, satellite
# fig.update_layout(mapbox_style="mapbox://styles/mapbox/light-v11",
#                   mapbox_accesstoken="YOUR_MAPBOX_TOKEN")

fig.update_layout(margin=dict(l=0,r=0,t=40,b=0), title="Carshare Density")
fig.show()
For Mapbox styles, set mapbox_accesstoken once via plotly.io.orca.config or environment variable. Use open-street-map when you don’t need custom styles.

Marker Clustering (client‑side)

# Simple approach: aggregate points server‑side into hex bins or tiles, then plot centroids with size=counts.
# For advanced clustering, preprocess with h3/kepler.gl tools and render the aggregated result in Plotly.
Regions

Choropleth (Countries, States, Custom Regions)

import plotly.express as px

df = px.data.gapminder().query("year==2007")
fig = px.choropleth(
    df, locations="iso_alpha", color="lifeExp",
    hover_name="country", color_continuous_scale="Plasma",
    projection="natural earth"
)
fig.update_layout(title="Life Expectancy (2007)")
fig.show()

US States Example

import pandas as pd, plotly.express as px

data = pd.DataFrame({
  "fips":["01","02","04","06"],  # FIPS codes (AL, AK, AZ, CA)
  "value":[72.5, 75.3, 76.1, 80.2]
})
fig = px.choropleth(
    data, geojson="https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json",
    locations="fips", color="value", scope="usa", color_continuous_scale="Viridis"
)
fig.update_geos(fitbounds="locations", visible=False)
fig.update_layout(title="Sample State Values")
fig.show()
Use ISO‑3 codes for world maps and FIPS/GEOIDs for US counties/states. Always ensure your key column matches the GeoJSON feature id.
Custom

Custom GeoJSON Joins

import json, pandas as pd, plotly.express as px

# Load custom GeoJSON
with open("regions.geojson","r",encoding="utf-8") as f:
    gj = json.load(f)

# Your data must have a key that matches gj["features"][i]["properties"][""] or feature["id"]
df = pd.DataFrame({"region_id":, "score":[0.3,0.7,0.5]})

fig = px.choropleth(
    df, geojson=gj, locations="region_id", color="score",
    featureidkey="properties.region_id",  # property name in GeoJSON
    color_continuous_scale="YlGnBu"
)
fig.update_geos(fitbounds="locations", visible=False)
fig.update_layout(title="Custom Regions (GeoJSON Join)")
fig.show()
Use featureidkey to point at the exact property in GeoJSON that should match your locations field. Normalize id types (str vs int).
Projection

Map Projections (Geo Scope)

import plotly.graph_objects as go

fig = go.Figure(go.Choropleth(
    locations=["FRA","DEU","ITA"], z=, locationmode="ISO-3", colorscale="Blues"
))
fig.update_geos(projection_type="natural earth", scope="europe")
fig.update_layout(title="Europe: Natural Earth Projection")
fig.show()

Common Projections

  • natural earth (balanced world view)
  • equirectangular (simple, rectangular)
  • mercator (web maps; distorts poles)
  • orthographic (globe‑like)

Scope Options

  • world, europe, asia, africa, north america, south america
  • usa (with state/county detail via GeoJSON)

Motion

Animated Maps (Time)

import plotly.express as px
df = px.data.gapminder().query("continent=='Europe'")
fig = px.choropleth(
    df, locations="iso_alpha", color="lifeExp", hover_name="country",
    animation_frame="year", color_continuous_scale="Viridis", projection="natural earth"
)
fig.update_layout(title="Europe Life Expectancy Over Time")
fig.show()
Keep frame sizes moderate for smooth animations. Consider filtering to a region or sampling years.
Dash

Dash Map Patterns (Filters, Hover, Clicks)

import dash
from dash import dcc, html, Input, Output
import plotly.express as px

df = px.data.gapminder().query("year==2007")
app = dash.Dash(__name__)
fig = px.choropleth(df, locations="iso_alpha", color="lifeExp", hover_name="country",
                    projection="natural earth", color_continuous_scale="Plasma")

app.layout = html.Div([
    html.H3("Interactive World Map"),
    dcc.Dropdown(id="continent", options=[{"label":c,"value":c} for c in sorted(df["continent"].unique())], value=None, placeholder="Filter by continent"),
    dcc.Graph(id="map", figure=fig),
    html.Pre(id="info")
])

@app.callback(Output("map","figure"), Input("continent","value"))
def filter_map(cont):
    d = df if not cont else df[df["continent"]==cont]
    return px.choropleth(d, locations="iso_alpha", color="lifeExp", hover_name="country",
                         projection="natural earth", color_continuous_scale="Plasma")

if __name__ == "__main__":
    app.run_server(debug=True)
Use clickData and hoverData properties of dcc.Graph to drill into regions or display details on selection.
Speed

Performance (Large Geo Data)

  • Simplify GeoJSON geometry (reduce coordinates) before plotting.
  • Aggregate points to tiles/hex bins; avoid plotting millions of markers.
  • Use fitbounds="locations" to minimize initial render cost.
  • Reuse figures and call update_* methods rather than recreating them.
  • For live apps, paginate or filter data server‑side; stream subsets.
# Example: geometry simplification (preprocess step)
# Use tools like mapshaper, topojson-simplify, or shapely to simplify polygons before feeding into Plotly.
A11y

Accessibility & Export

Accessible Maps

  • Add clear titles and aria‑labels in surrounding HTML.
  • Ensure colorblind‑friendly scales (e.g., Viridis, Cividis) and adequate contrast.
  • Provide a data table/CSV download for screen‑reader users.

Export

# Interactive HTML
fig.write_html("map.html", include_plotlyjs="cdn")

# Static image (requires kaleido)
fig.write_image("map.png", width=1200, height=700, scale=2)

Fix

Troubleshooting & FAQ

“Key mismatch” on choropleth

# Ensure your df.locations matches feature ids:
# - ISO-3 codes for world
# - FIPS for US counties/states
# - Use featureidkey for custom properties

Blank map with Mapbox style

Set a valid Mapbox token or switch to open-street-map style.

Slow render with large GeoJSON

Simplify geometry, reduce properties, and use fitbounds="locations". Consider splitting layers.

Markers overlap heavily

Aggregate by tiles/hex; scale size by counts; use heatmap style via imshow on a grid if needed.

FAQ

When to use Scatter Mapbox vs Choropleth? Scatter for points/coordinates; choropleth for region‑based values aggregated by polygons.

How to align colors across multiple maps? Share the same color scale, range, and tick formatting; document units in the title/legend.

Can I mix map types? Yes—use subplots with different specs or overlay scatter layers on a choropleth (advanced layout).

If this page helped, consider linking it from “Python Mapping,” “Geo Analytics,” or “Dashboard Engineering” resources. Sharing supports more free content like this.

© 2025 Pythoneo · Designed to be highly linkable: comprehensive, evergreen, copy‑paste friendly, with performance and troubleshooting built‑in.