Interactive map-based data visualizations with Streamlit and Bokeh.js

Ricky H. Putra
4 min readNov 6, 2021

I need to build a simple dashboard web prototype that takes input data from users and reads geographical data such as latitude and longitude, and then visualize it as a map on the website which allow users to interact with it.

Geo data visualization with Bokeh.js Map

In this tutorial, I will walkthrough five main technical building blocks:

  1. Building web interface with Streamlit framework which allows me to quickly build the UI prototype.
  2. Mapping service such as Google geocoding service to translate address into latlong coordinate.
  3. Database connection, in this case MySQL and MongoDB.
  4. Making JSON HTTP requests
  5. Map plotting with Bokeh.js

Build UI with Streamlit

Streamlit allows me to build simple UI on my dashboard for users to fill in query parameters. It is super easy to install and use.

Install with pip:

pip install streamlit

Put some input text fields for user to fill in query parameters:

import streamlit as st

st.title(‘Streamlit dashboard’)

CUSTOMER_ID = st.text_input(“Customer ID”, 111)
APPLICATION_ID = st.text_input(“Application ID”, “222”)
FROM_DATE = st.date_input(“From date”)
UNTIL_DATE = st.date_input(“Until date”)

It takes just a few lines of code to actually build the query form. Two text fields and two date fields. Streamlist also has many other UI components such as slider, radio button, etc. See the list of display interactive widgets here.

Adding a UI widget is the same as declaring variable. No need backend code, define routes, handle HTTP requests, write HTML, CSS, JavaScript, etc. This is what I think makes Streamlit magically simply to use.

Last but not least, if you prefer cloud based deployment, it can not be easier than Streamlit instant deployment which they offer freely for up to 3 public apps.

Google Maps service

I use Google Maps geocoding API to convert addresses (like “1st boulevard street, city, province”) into geographic coordinates (like “latitude 36.429010 and longitude -121.103638)

GEOCODE_URL = 'https://maps.googleapis.com/maps/api/geocode/json?address='+customer['address'][0]+'&key='+GOOGLE_API_KEY
geo_response = requests.request("GET", GEOCODE_URL)
geodata = json.loads(geo_response.text)
try:
print(geodata['results'][0]['geometry']['location'])
latlong = [geodata['results'][0]['geometry']['location']['lat'],geodata['results'][0]['geometry']['location']['lng']]
except IndexError:
latlong = None
st.write('latlong not found')

You need to register and obtain Google API key from Google Cloud Platform console.

Making MySQL database connection

There are plenty of Python libraries that you can use to work with MySQL databases such as SQL Alchemy. It is simple yet powerful with object-relational mapper (ORM) feature, an optional component that provides the data mapper pattern, where classes can be mapped to the database in open ended, multiple ways — allowing the object model and database schema to develop in a cleanly decoupled way from the beginning.

import sqlalchemyengine = sqlalchemy.create_engine('mysql+mysqlconnector://user:pass@mysql:port/db')df = pd.read_sql("select * from customers", con = engine)

JSON HTTP requests

Making JSON request is pretty straightforward with requests package

import requestsresponse = requests.request("POST", url, headers=headers, data=payload)

headers: HTTP headers to the request e.g. {‘user-agent’ : ‘xyz’}

payloads: a key value pairs dictionary object e.g. {‘key1’ : ‘value1’}

Plot map with Bokeh.js

Bokeh.js has comprehensive components to build map visualization such as gmap. It can be built as embedded map or separately save as file. The built-in map also has basic interaction tool like zoom and pan. It is very easy to annotate as well as you can see in below code.

First we import the bokeh.* packages

from bokeh.io import show
from bokeh.plotting import gmap
from bokeh.models import GMapOptions
from bokeh.models import ColumnDataSource
from bokeh.palettes import Set3
from bokeh.palettes import Category20
from bokeh.palettes import RdBu3
from bokeh.resources import CDN
from bokeh.embed import file_html

Set the map width and height

bokeh_width, bokeh_height = 1024,768

Write a function to plot data as map

def plotAll(data, zoom=15, map_type='roadmap'):
gmap_options = GMapOptions(lat=data[0][1], lng=data[0][2],
map_type=map_type, zoom=zoom)
p = gmap(GOOGLE_API_KEY, gmap_options, title='AwanTunai - Risk Intelligence',
width=bokeh_width, height=bokeh_height)

latArr = []
longArr = []
colorArr = []
labelArr = []
colidx = 0
colpalette =
Category20.get(20)
print(‘palette length: ‘, len(Set3))

for x in data:
if(x[4] == ‘Stationary’):
latArr.append(x[1])
longArr.append(x[2])
labelArr.append(x[3])
if(colidx == len(colpalette)):
colidx=0
colorArr.append(colpalette[colidx])
colidx+=1

print(‘latArr: ‘, latArr)
print(‘longArr: ‘, longArr)
print(‘colorArr: ‘, colorArr)
print(‘label: ‘, labelArr)

source = ColumnDataSource(dict(
x=longArr,
y=latArr,
color=colorArr,
label=labelArr
))

center = p.circle(x=’x’, y=’y’, size=10, alpha=0.9, color=’color’, legend_group=’label’, source=source)

if RESIDENCE_LATLONG is not None:
p.triangle([RESIDENCE_LATLONG[1]], [RESIDENCE_LATLONG[0]], size=10, alpha=0.9, color=’red’)
if BUSINESS_LATLONG is not None:
p.triangle([BUSINESS_LATLONG[1]], [BUSINESS_LATLONG[0]], size=10, alpha=0.9, color=’blue’)
html = file_html(p, CDN, "User locations")
return html

And finally using streamlit components to render the map from html generated from bokeh.embed.file_html function.

import streamlit.components.v1 as componentsif len(data) > 0:
components.html(plotAll(data, 15, 'satellite'), height = bokeh_height + 100, width = bokeh_width + 100)
else:
st.write('no location data found')

Please follow me if you find this article useful, it would motivate me to write more useful articles and helping others to learn. Thank you.

--

--

Ricky H. Putra

Leading digitization initiatives in AwanTunai focusing on strengthening Indonesia MSME businesses with technology. Software Dev | Automation | Data Science | AI