Bokeh Tutorial

09. Geographic Plots

In [1]:
from import output_notebook, show
Loading BokehJS ...

It is often useful to be able to relate datasets with their real-world context. You can plot geographic data just like any other type of data, as in the Texas Unemployment example, but Bokeh also Bokeh provides several specialized mechanisms for plotting data in geographic coordinates:

Google Maps plots

Bokeh can render its own interactive glyphs on top of a Google Maps underlay. To use this functionality, call the gmap method on a standard figure as shown below. You will need to supply a valid Google API key for the plot to function, see:

In [2]:
import os
from bokeh.models import GMapOptions
from bokeh.plotting import gmap

map_options = GMapOptions(lat=30.2861, lng=-97.7394, map_type="roadmap", zoom=11)

# Replace the value below with your personal API key:
api_key = os.environ["GOOGLE_API_KEY"]

p = gmap(api_key, map_options, title="Austin")

data = dict(lat=[ 30.29,  30.20,  30.29],
            lon=[-97.70, -97.74, -97.78])"lon", y="lat", size=15, fill_color="blue", fill_alpha=0.8, source=data)


WMTS Tile Source

WTMS is the most common web standard for tiled map data, i.e. maps supplied as standard-sized image patches from which the overall map can be constructed at a given zoom level. WTMS uses Web Mercator format, measuring distances from Greenwich, England as meters north and meters west, which is easy to compute but does distort the global shape.

First let's create an empty Bokeh plot covering the USA, with bounds specified in meters:

In [3]:
from bokeh.plotting import figure
from bokeh.models import WMTSTileSource

# web mercator coordinates
USA = x_range,y_range = ((-13884029,-7453304), (2698291,6455972))

p = figure(tools='pan, wheel_zoom', x_range=x_range, y_range=y_range, 
           x_axis_type="mercator", y_axis_type="mercator")

A few WTMS tile sources are already defined in bokeh.tile_providers, but here we'll show how to specify the interface using a format string showing Bokeh how to request a tile with the required zoom, x, and y values from a given tile provider:

In [4]:
url = '{Z}/{X}/{Y}.png'
attribution = "Tiles by Carto, under CC BY 3.0. Data by OSM, under ODbL"

p.add_tile(WMTSTileSource(url=url, attribution=attribution))
id = '34fad12b-328d-4093-b8de-e5de0d34ee4b', …)

If you show the figure, you can then use the wheel zoom and pan tools to navigate over any zoom level, and Bokeh will request the appropriate tiles from the server and insert them at the correct locations in the plot:

In [5]:

That's all it takes to put map data into your plot! Of course, you'll usually want to show other data as well, or you could just use the tile server's own web address. You can now add anything you would normally use in a Bokeh plot, as long as you can obtain coordinates for it in Web Mercator format. For example:

In [6]:
import pandas as pd
import numpy as np

def wgs84_to_web_mercator(df, lon="lon", lat="lat"):
    """Converts decimal longitude/latitude to Web Mercator format"""
    k = 6378137
    df["x"] = df[lon] * (k * np.pi/180.0)
    df["y"] = np.log(np.tan((90 + df[lat]) * np.pi/360.0)) * k
    return df

df = pd.DataFrame(dict(name=["Austin", "NYC"], lon=[-97.7431,-74.0059], lat=[30.2672,40.7128]))
name lon lat x y
0 Austin -97.7431 30.2672 -1.088071e+07 3.537942e+06
1 NYC -74.0059 40.7128 -8.238299e+06 4.970072e+06
In [7]:
p = figure(tools='pan, wheel_zoom', x_range=x_range, y_range=y_range, 
           x_axis_type="mercator", y_axis_type="mercator")

p.add_tile(WMTSTileSource(url=url, attribution=attribution))['x'], y=df['y'], fill_color='orange', size=10)
In [8]:
# EXERCISE: find some data in lat, lon (e.g. at, 
# import it into a dataframe or data source, and add it on the map above.