Skip to content

Instantly share code, notes, and snippets.

@gmerritt123
Created February 16, 2023 01:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gmerritt123/8c8a40411c6977b9ca5d6be7e64faea4 to your computer and use it in GitHub Desktop.
Save gmerritt123/8c8a40411c6977b9ca5d6be7e64faea4 to your computer and use it in GitHub Desktop.
d3 tricontour and bokeh
# -*- coding: utf-8 -*-
"""
Created on Wed Feb 15 19:24:01 2023
Example demonstrating interoperability between d3 and bokeh for powerful CustomJS callbacks.
@author: Gaelen Merritt
"""
import numpy as np
from bokeh.plotting import figure
from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, PointDrawTool, Slider, ColorBar
from bokeh.palettes import Sunset8
from bokeh.transform import linear_cmap
from bokeh.embed import components
from bokeh.resources import Resources
x = np.random.random(100)*3
y = np.random.random(100)*2
z = 1.3*np.exp(-2.5*((x-1.3)**2 + (y-0.8)**2)) - 1.2*np.exp(-2*((x-1.8)**2 + (y-1.3)**2))
src = ColumnDataSource({'x':x,'y':y,'z':z})
p = figure(width=800, height=400, x_range=(-.5, 3.5), y_range=(-0.5, 2.5)
,title='Move some points!')
cmap = linear_cmap(field_name='z',palette=Sunset8,low=-1,high=1)
r = p.scatter(x='x',y='y'
,fill_color=cmap
,line_alpha=cmap
,size=8
,source=src)
color_bar = ColorBar(color_mapper=cmap.transform, label_standoff=12)
p.add_layout(color_bar, "right")
et = PointDrawTool(renderers=[r],num_objects=50)
p.add_tools(et)
p.toolbar.active_tap = et
csrc = ColumnDataSource(data={'xs':[],'ys':[],'z':[]})
cr = p.multi_line(xs='xs',ys='ys',line_color=cmap,line_width=4,source=csrc)
sl = Slider(value=8,start=2,end=16,title='Desired Number of Contour Intervals')
cb = CustomJS(args=dict(src=src,csrc=csrc,sl=sl)
,code='''
//transforms columndatasource data to array of objects
function cds_to_objarray(cds_data){
var keys = Object.keys(cds_data)
var z = d3.transpose(Object.values(cds_data))
var o = z.map(x=>Object.assign(...keys.map((k, i) => ({[k]: x[i]}))))
return o}
//translates a d3 contour result into multiline-ready CDS data
function contours_to_cds(d3_contour){
var cxs = []
var cys = []
var cv = []
for (var vi=0;vi<d3_contour.length;vi++){
for (var pi=0; pi<d3_contour[vi].coordinates.length;pi++){
for (var ppi = 0; ppi<d3_contour[vi].coordinates[pi].length;ppi++){
var xy = d3.transpose(d3_contour[vi].coordinates[pi][ppi])
cxs.push(xy[0])
cys.push(xy[1])
cv.push(d3_contour[vi].value)
}
}
}
return {'z':cv,'xs':cxs,'ys':cys}
}
var d = cds_to_objarray(src.data)
var tri = d3.tricontour().thresholds(sl.value)
var contours = tri(d.map(x=>[x.x,x.y,x.z]))
csrc.data = contours_to_cds(contours)
''')
src.js_on_change('data',cb)
sl.js_on_change('value',cb)
def save_html_wJSResources(bk_obj,fname,resources_list,html_title='Bokeh Plot'):
'''function to save a bokeh figure/layout/widget but with additional JS resources imported at the top of the html
resources_list is a list input of where to import additional JS libs so they can be utilized into CustomJS etc in bokeh work
e.g. ['http://d3js.org/d3.v6.js']
'''
script, div = components(bk_obj)
resources = Resources()
# print(resources)
tpl = '''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>'''+html_title+'''</title>
'''
tpl = tpl+resources.render_js()
for r in resources_list:
tpl = tpl +'''\n<script src="'''+r+'''"></script>'''
tpl = tpl+script+\
'''
</head>
<body>'''\
+div+\
'''</body>
</html>'''
with open(fname,'w') as f:
f.write(tpl)
save_html_wJSResources(bk_obj=column([p,sl]),fname='d3Contouring.html'
,resources_list=['https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.0/d3.min.js'
,'https://cdn.jsdelivr.net/npm/d3-tricontour@1']
,html_title='d3 Contouring')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment