Plotly Maps & Geo Cookbook (Python)
Scatter Mapbox, choropleths, projections, custom GeoJSON, clustering, animations, performance, and troubleshooting—copy‑paste ready.
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()
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.
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()
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()
featureidkey
to point at the exact property in GeoJSON that should match your locations
field. Normalize id types (str vs int).
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)
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()
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)
clickData
and hoverData
properties of dcc.Graph
to drill into regions or display details on selection.
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.
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)
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).
© 2025 Pythoneo · Designed to be highly linkable: comprehensive, evergreen, copy‑paste friendly, with performance and troubleshooting built‑in.