Plotly maps#
Various map types and overlays are available.
Open Street Map is open for use without a token.
Overlays include scatter, lines, densities, etc.
Choropleths (coloured map sections) can be used as overlays or as separate plots when a GeoJSON formated dictionary of map polygons are available.
# The following renders plotly graphs in Jupyter Notebook, Jupyter Lab and VS Code formats
import plotly.io as pio
pio.renderers.default = "notebook+plotly_mimetype"
/Users/kristian/miniforge3/envs/ind320_25/lib/python3.12/site-packages/kaleido/__init__.py:14: UserWarning:
Warning: You have Plotly version 5.24.1, which is not compatible with this version of Kaleido (1.0.0).
This means that static image generation (e.g. `fig.write_image()`) will not work.
Please upgrade Plotly to version 6.1.1 or greater, or downgrade Kaleido to version 0.2.1.
Map#
Various maps with user defined overlays are available, e.g., scatter_map.
From Plotly version 5.24, Mapbox-es are deprecated, e.g., scatter_mapbox.
Writer has, as of 16. November 2024, not made the switch yet.
In most cases, switching between map and mapbox does not require other code changes.
import plotly.express as px
import pandas as pd
us_cities = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv"
)
fig = px.scatter_mapbox(
us_cities,
lat="lat",
lon="lon",
hover_name="City",
hover_data=["State", "Population"],
color_discrete_sequence=["fuchsia"],
zoom=3,
height=300,
width=600,
)
fig.update_layout(mapbox_style="open-street-map")
fig.update_layout(margin={"r": 0, "t": 0, "l": 0, "b": 0})
fig.update_layout(mapbox_bounds={"west": -180, "east": -50, "south": 20, "north": 90})
fig
A local example from Ås#
import pandas as pd
# Local restaurants and cafes
restaurants = pd.read_csv('../../data/restaurants.csv')
restaurants
name | lat | lon | type | |
---|---|---|---|---|
0 | Sushiko Ås | 59.664773 | 10.789674 | Restaurant |
1 | Charlie's Diner | 59.664663 | 10.788735 | Restaurant |
2 | Jojo's Pizza | 59.665496 | 10.795366 | Restaurant |
3 | Desi Zaiqa | 59.664281 | 10.792276 | Restaurant |
4 | Babylon Pizza | 59.663602 | 10.792977 | Restaurant |
5 | NT kiosk | 59.663743 | 10.793237 | Kiosk |
6 | Aas Bistro | 59.663465 | 10.793558 | Restaurant |
7 | Whytes Coffee | 59.664686 | 10.790830 | Café |
8 | Station kiosk Ås | 59.663324 | 10.794335 | Café |
fig_restaurants = px.scatter_mapbox(
restaurants,
lat="lat",
lon="lon",
hover_name="name",
hover_data=["type","lat","lon"],
color_discrete_sequence=["fuchsia"],
zoom=14,
height=600,
width=700,
)
fig_restaurants.update_layout(mapbox_style="open-street-map")
fig_restaurants.update_layout(margin={"r": 0, "t": 0, "l": 0, "b": 0})
#fig_restaurants.update_traces(cluster=dict(enabled=True)) # Group restaurants when zooming out
fig_restaurants
print(fig_restaurants)
Figure({
'data': [{'customdata': array([['Restaurant', 59.6647728, 10.7896737],
['Restaurant', 59.6646628, 10.7887347],
['Restaurant', 59.6654962, 10.7953659],
['Restaurant', 59.6642813, 10.7922755],
['Restaurant', 59.6636019, 10.7929767],
['Kiosk', 59.6637434, 10.7932373],
['Restaurant', 59.663465, 10.7935577],
['Café', 59.6646862, 10.7908304],
['Café', 59.663324, 10.7943351]], dtype=object),
'hovertemplate': ('<b>%{hovertext}</b><br><br>lat' ... '{customdata[0]}<extra></extra>'),
'hovertext': array(['Sushiko Ås', "Charlie's Diner", "Jojo's Pizza", 'Desi Zaiqa',
'Babylon Pizza', 'NT kiosk', 'Aas Bistro', 'Whytes Coffee',
'Station kiosk Ås'], dtype=object),
'lat': array([59.6647728, 59.6646628, 59.6654962, 59.6642813, 59.6636019, 59.6637434,
59.663465 , 59.6646862, 59.663324 ]),
'legendgroup': '',
'lon': array([10.7896737, 10.7887347, 10.7953659, 10.7922755, 10.7929767, 10.7932373,
10.7935577, 10.7908304, 10.7943351]),
'marker': {'color': 'fuchsia'},
'mode': 'markers',
'name': '',
'showlegend': False,
'subplot': 'mapbox',
'type': 'scattermapbox'}],
'layout': {'height': 600,
'legend': {'tracegroupgap': 0},
'mapbox': {'center': {'lat': np.float64(59.66422595555556), 'lon': np.float64(10.792331888888889)},
'domain': {'x': [0.0, 1.0], 'y': [0.0, 1.0]},
'style': 'open-street-map',
'zoom': 14},
'margin': {'b': 0, 'l': 0, 'r': 0, 't': 0},
'template': '...',
'width': 700}
})
Streamsync integration#
Streamsync has handlers that easily connect interaction with reactive Python code.

Choropleths#
A pure choropleth can be plotted using a GeoJSON file, e.g., from https://norgeskart.no/json/norge/.
In addition a DataFrame containing the map region properties to use for colouring and hover information is needed.
# Choropleth map of US counties with unemployment rate
from urllib.request import urlopen
import json
with urlopen('https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json') as response:
counties = json.load(response)
import pandas as pd
df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv",
dtype={"fips": str})
import plotly.express as px
fig_chl = px.choropleth(df, geojson=counties, locations='fips', color='unemp',
color_continuous_scale="Viridis",
range_color=(0, 12),
scope="usa",
labels={'unemp':'unemployment rate'}
)
fig_chl.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig_chl
#counties
# Gapminder dataset as a map
import plotly.express as px
df = px.data.gapminder()
fig_chl2 = px.choropleth(df, locations="iso_alpha", color="lifeExp", hover_name="country", animation_frame="year", range_color=[20,80])
fig_chl2