TL;DR
Use Google Maps 2D satellite, road and terrain tiles with Plotly in Streamlit, for production.
- Make a Google Cloud account to get an
API_KEY
[link] - Generate a
SESSION_KEY
of the desired map type [link] - Set the layer source to the tile
URL
fig = go.Figure(layout=go.Layout(mapbox=dict(
style="white-bg",
layers=[{"below": 'traces',
"sourcetype": "raster",
"sourceattribution": "Google",
"source": [URL]}],
In a bit more detail
Most maps plotted in Python use tiles.
These tiles are typically streamed from a server on request.
Quick and Dirty
You can access Google’s tiles directly without using a KEY
.
Use one of these URLs and the above code snippet to set the tile source.
However, this violates Google’s Terms of Service.
ROADMAPS_URL = 'https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}'
SATELLITE_URL = 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}'
TERRAIN_URL = 'https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}'
SATELLITE_HYBRID_URL = 'https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}'
Production Ready
This requires your to enable billing, don’t worry Google provide enough free requests to cover small projects.
Getting an API key
- Make Google Cloud Account + Project + enable billing [docs]
- Navigate to the Console → APIs & Services → Library
- Select and Enable Map Tiles API
- Navigate to the Console → APIs & Services → Credentials
- Select Create Credentials → API key
- Restrict the API key to Map Tiles only (optional)
Code: getting a session key & plotting
- Using python’s
requests
package we can retrieve a session key and assemble aURL
that will plot and conform to Googles T&Cs. - Using Streamlit’s session state we can store the session
URL
. - Set the Plotly layout to use the URL & plot data
import requests
import plotly.graph_objects as go
from streamlit import session_state as ss
import streamlit as st
def get_session_url(api_key):
create_session_url = "https://tile.googleapis.com/v1/createSession"
payload = {
"mapType": "satellite",
"language": "en-US",
"region": "US",
}
headers = {'Content-Type': 'application/json'}
response = requests.post(create_session_url,
json=payload,
headers=headers,
params={'key': api_key})
if response.status_code == 200:
session_token = response.json().get('session')
print("Session token:", session_token)
else:
print("Failed to create session:", response.text)
return ("https://tile.googleapis.com/v1/2dtiles/{z}/{x}/{y}?session="
+ session_token
+ "&key="
+ api_key)
def set_tile_layout(tile_url, lat, lon, zoom=15):
return go.Layout(
width=640,
height=640,
mapbox=dict(
style="white-bg",
layers=[{"below": 'traces',
"sourcetype": "raster",
"sourceattribution": "Google",
"source": [tile_url] }],
center=dict(lat=lat,
lon=lon),
zoom=15))
if 'tiles_url' not in ss:
ss.tiles_url = get_session_url(GOOGLE_API_KEY)
fig = go.Figure(layout=set_tile_layout(ss.tiles_url,
df[lat_key].mean(),
df[lon_key].mean()))
fig.add_trace(go.Scattermapbox(
mode="markers",
lat=df[lat_key],
lon=df[lon_key],
name='Data'))
fig.update_layout(margin={"r": 0, "t": 0, "l": 0, "b": 0})
st.plotly_chart(fig)