Mapping Geo Data

Bokeh has started adding support for working with Geographical data. There are a number of powerful features already available, but we still have more to add. Please tell use your use cases through the mailing list or on github so that we can continue to build out these features to meet your needs.

GeoJSON Datasource

GeoJSON is a popular open standard for representing geographical features with JSON. It describes points, lines and polygons (called Patches in Bokeh) as a collection of features. Each feature can also have a set of properties.

Bokeh’s GeoJSONDataSource can be used almost seamlessly in place of Bokeh’s ColumnDataSource. For example:

from bokeh.io import output_file, show
from bokeh.models import GeoJSONDataSource
from bokeh.plotting import figure
from bokeh.sampledata.sample_geojson import geojson

geo_source = GeoJSONDataSource(geojson=geojson)

p = figure()
p.circle(x='x', y='y', alpha=0.9, source=geo_source)
output_file("geojson.html")
show(p)

The important thing to know is that behind the scenes, Bokeh converts the GeoJSON coordinates into columns called x and y (z where appropriate) or xs and ys depending on whether the features are Points, Lines, MultiLines, Polygons or MultiPolygons. Properties with clashing names will be overridden when the GeoJSON is converted, so the following code would not behave as expected.

Warning

If your GeoJSON properties contain a property x and you want to use this to set the size of your circles, and you do this:

Antipattern this will not work.

p.circle(size='x', alpha=0.9, source=geo_source)

You will not get the plot you expect because this is equivalent to

p.circle(x='x', y='y', size='x', alpha=0.9, source=geo_source)

and the x value from your properties will be overridden with the longitude values from your geometry coordinates.

Google Maps support

With the GMapPlot, you can plot any bokeh glyphs over a Google Map.

from bokeh.io import output_file, show
from bokeh.models import (
  GMapPlot, GMapOptions, ColumnDataSource, Circle, Range1d, PanTool, WheelZoomTool, BoxSelectTool
)

map_options = GMapOptions(lat=30.29, lng=-97.73, map_type="roadmap", zoom=11)

plot = GMapPlot(x_range=Range1d(), y_range=Range1d(), map_options=map_options)
plot.title.text = "Austin"

# For GMaps to function, Google requires you obtain and enable an API key:
#
#     https://developers.google.com/maps/documentation/javascript/get-api-key
#
# Replace the value below with your personal API key:
plot.api_key = "GOOGLE_API_KEY"

source = ColumnDataSource(
    data=dict(
        lat=[30.29, 30.20, 30.29],
        lon=[-97.70, -97.74, -97.78],
    )
)

circle = Circle(x="lon", y="lat", size=15, fill_color="blue", fill_alpha=0.8, line_color=None)
plot.add_glyph(source, circle)

plot.add_tools(PanTool(), WheelZoomTool(), BoxSelectTool())
output_file("gmap_plot.html")
show(plot)

Warning

There is an open issue documenting points appearing to be ~10px off from their intended location.

Google has its own terms of service for using Google Maps API and any use of Bokeh with Google Maps must be within Google’s Terms of Service

Tile Providers

Bokeh plots can also consume XYZ tile services which use the Web Mercator projection. The module bokeh.tile_providers contains several pre-configured tile sources with appropriate attribution which can be added to a plot using the .add_tile() method.

from bokeh.io import output_file, show
from bokeh.plotting import figure
from bokeh.tile_providers import STAMEN_TONER

bound = 20000000 # meters
fig = figure(tools='pan, wheel_zoom', x_range=(-bound, bound), y_range=(-bound, bound))
fig.axis.visible = False
fig.add_tile(STAMEN_TONER)
output_file("stamen_toner_plot.html")
show(fig)