13 Deploying Bokeh Apps

In the previous sections we discovered how to use a HoloMap to build a Jupyter notebook with interactive visualizations that can be exported to a standalone HTML file, as well as how to use DynamicMap and Streams to set up dynamic interactivity backed by the Jupyter Python kernel. However, frequently we want to package our visualization or dashboard for wider distribution, backed by Python but run outside of the notebook environment. Bokeh Server provides a flexible and scalable architecture to deploy complex interactive visualizations and dashboards, integrating seamlessly with Bokeh and with HoloViews.

Bokeh server apps can be used for a wide range of applications, but here we will show how to use them with Datashader and related libraries:

For a detailed background on Bokeh Server see the Bokeh user guide . In this tutorial we will discover how to deploy the visualizations we have created so far as a standalone Bokeh Server app, and how to flexibly combine HoloViews and ParamBokeh to build complex apps. We will also reuse a lot of what we have learned so far---loading large, tabular datasets, applying Datashader operations to them, and adding linked Streams to our app.

A simple Bokeh app

The preceding sections of this tutorial focused solely on the Jupyter notebook, but now let's look at a bare Python script that can be deployed using Bokeh Server:

In [1]:
with open('../apps/server_app.py', 'r') as f:
import dask.dataframe as dd
import holoviews as hv
from holoviews.operation.datashader import datashade


# 1. Load data and Datashade it
ddf = dd.read_parquet('../data/nyc_taxi_wide.parq')[['dropoff_x', 'dropoff_y']].persist()
points = hv.Points(ddf, kdims=['dropoff_x', 'dropoff_y'])
shaded = datashade(points).opts(plot=dict(width=800, height=600))

# 2. Instead of Jupyter's automatic rich display, render the object as a bokeh document
doc = hv.renderer('bokeh').server_doc(shaded)
doc.title = 'HoloViews Bokeh App'

Step 1 of this app should be very familiar by now -- declare that we are using Bokeh to render plots, load some taxi dropoff locations, declare a Points object, Datashade them, and set some plot options.

At this point, if we were working with this code in a notebook, we would simply type shaded and let Jupyter's rich display support take over, rendering the object into a Bokeh plot and displaying it inline. Here, step 2 adds the code necessary to do those steps explicitly:

  • get a handle on the Bokeh renderer object using hv.renderer
  • create a Bokeh document from shaded by passing it to the renderer's server_doc method
  • optionally, change some properties of the Bokeh document like the title.

This simple chunk of boilerplate code can be added to turn any HoloViews object into a fully functional, deployable Bokeh app!

Deploying the app

Assuming that you have a terminal window open with the pyviz environment activated, in the ../apps/ directory, you can launch this app using Bokeh Server:

bokeh serve --show server_app.py

If you don't already have a favorite way to get a terminal, one way is to open it from within Jupyter , then make sure you are in the ../apps directory, and make sure you are in the right Conda environment if you created one (activating it using source activate pyviz (or activate pyviz on Windows)).

In [2]:
# Exercise: Modify the app to display the pickup locations and add a tilesource, then run the app with bokeh serve
# Tip: Refer to the previous notebook

Building an app with custom widgets

The above app script can be built entirely without using Jupyter, though we displayed it here using Jupyter for convenience in the tutorial. Jupyter notebooks are also often helpful when initially developing such apps, allowing you to quickly iterate over visualizations in the notebook, deploying it as a standalone app only once we are happy with it. In this section we will combine everything we have learned so far including declaring of various parameters to control our visualization using a set of widgets.

We begin as usual with a set of imports:

In [3]:
import holoviews as hv, geoviews as gv, param, parambokeh, dask.dataframe as dd

from colorcet import cm_n
from bokeh.document import Document
from holoviews.operation.datashader import datashade
from holoviews.streams import RangeXY
from cartopy import crs

hv.extension('bokeh', logo=False)