Last active
November 24, 2015 16:01
-
-
Save almarklein/d5421e187e4429cca6fb to your computer and use it in GitHub Desktop.
How to pick a line among multiple lines in Bokeh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
from bokeh.plotting import output_notebook, show, figure | |
from bokeh.models import Plot, CustomJS, TapTool, ColumnDataSource, Line, DataRange1d | |
output_notebook() | |
p = figure() | |
t = np.linspace(0, 5, 100) | |
l1 = p.line(t, np.sin(t), color='#ff0000', nonselection_color='#888', line_width=5) | |
l2 = p.line(t, np.sin(t+1), color='#00ff00', nonselection_color='#888', line_width=5) | |
#l3 = p.line(t, np.sin(t+2), color='#0000ff', nonselection_color='#888', line_width=5) | |
lines = [l1, l2] | |
code = """ | |
// Monkey patch to use until Bokeh has this functionality | |
LineView = line.default_view.prototype; // need example line object | |
if (!LineView._ori_hit_point) { | |
var dist2 = function(x1, y1, x2, y2) {return (x1-x2) * (x1-x2) + (y1-y2)* (y1-y2);}; | |
var dist_to_segment = function (p, v, w) { | |
var l2, t; | |
l2 = dist2(v.x, v.y, w.x, w.y); | |
if (l2 == 0) return dist2(p.x, p.y, v.x, v.y); | |
t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2; | |
if (t < 0) return dist2(p.x, p.y, v.x, v.y); | |
if (t > 1) return dist2(p.x, p.y, w.x, w.y); | |
return dist2(p.x, p.y, v.x + t * (w.x - v.x), v.y + t * (w.y - v.y)) | |
} | |
LineView._ori_hit_point = LineView._hit_point; | |
LineView._hit_point = function (geometry) { | |
var result, vx, vy, x, y, shortest, threshold, p0, p1, dist; | |
//result = this._ori_hit_point(geometry); | |
result = result = {'0d': {flag: false, indices: []}, | |
'1d': {indices: []}, | |
'2d': {indices: []}} | |
point = {x: this.renderer.plot_view.canvas.vx_to_sx(geometry.vx), | |
y: this.renderer.plot_view.canvas.vy_to_sy(geometry.vy)}; | |
shortest = 99999; | |
threshold = 2; | |
for (var i=0; i<this.sx.length-1; i++) { | |
p0 = {x: this.sx[i], y: this.sy[i]}; | |
p1 = {x: this.sx[i+1], y: this.sy[i+1]}; | |
dist = Math.sqrt(dist_to_segment(point, p0, p1)); | |
if (dist < threshold && dist < shortest) { | |
shortest = dist; | |
result['0d'].flag = true | |
result['0d'].indices = [i] | |
} | |
} | |
if (result['0d'].flag) result['0d'].glyph = this; | |
return result; | |
} | |
} | |
// Call Python func with id of the line being clicked | |
d0 = cb_obj.get("selected")["0d"]; | |
if (d0.flag && d0.glyph) { | |
IPython.notebook.kernel.execute('click_line("' + d0.glyph.renderer.model.id + '")'); | |
console.log(d0.glyph.id, 'picked') | |
} | |
window.p = plot; | |
""" | |
p.tools.append(TapTool(plot=p, callback=CustomJS(code=code, args=dict(line=l1.glyph, plot=p)))) | |
clicked_lines = [] | |
def click_line(id): | |
for line in lines: | |
if line._id == id: | |
clicked_lines.append(line) | |
show(p) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment