Maps: choropleths and point maps

Plotly · Practical · 12 min

Location is a natural dimension for business and public data, and Plotly makes interactive maps a one-liner.

Plotly has two families of maps.

Outline (geo) maps draw country and region shapes without a tile server: px.choropleth colors regions, and px.scatter_geo / px.line_geo place points and lines.

px.choropleth(df, locations="iso_alpha", color="lifeExp",
              hover_name="country", projection="natural earth")

locations are matched by locationmode: ISO-3 codes by default, or "USA-states" for two-letter state codes. Country names are supported but matched strictly in Plotly 7, so prefer ISO-3 codes. For other regions, pass a GeoJSON geojson= and featureidkey="properties.id" to say which property matches your locations column. Since Plotly 7, geo maps zoom to fit the plotted locations by default (fitbounds="locations").

Tile maps draw on a street-map background rendered by MapLibre: px.scatter_map(df, lat="lat", lon="lon", zoom=10), px.density_map for heatmaps of many points, px.choropleth_map for GeoJSON regions on tiles. Choose the background with map_style ("open-street-map", "carto-positron", "carto-darkmatter" ...); these need no access token. The older Mapbox-based functions (scatter_mapbox and friends) were removed in Plotly 7.

Use a sequential color scale for quantities and normalize by population or area: raw counts on a choropleth mostly show where people live.

Example

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="Viridis",
                    projection="natural earth")
print(fig.data[0].type, fig.data[0].locationmode)    # choropleth None (plotly.js default: ISO-3)

cities = px.data.carshare()                          # Montreal car-share points
tiles = px.scatter_map(cities, lat="centroid_lat", lon="centroid_lon",
                       color="peak_hour", size="car_hours", size_max=12,
                       zoom=10, map_style="carto-positron")
print(tiles.data[0].type)                            # scattermap
print(tiles.layout.map.style)                        # carto-positron
fig.write_html("life_map.html")
tiles.write_html("carshare.html")

Remember

  • Geo maps (choropleth, scatter_geo) need no tiles; tile maps use MapLibre
  • Match regions by ISO-3 codes, state codes, or GeoJSON with featureidkey
  • In Plotly 7 use scatter_map/choropleth_map; the mapbox functions are gone

Watch out for

  • Mapping raw counts on a choropleth, which mostly reflects population rather than the rate you care about.

Check yourself

  1. Your regions are custom sales territories defined in a GeoJSON file. What do you pass to px.choropleth?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: plotly.com/python/choropleth-maps/