def test_js_on_change_executes(self, bokeh_model_page) -> None: slider = RangeSlider(start=0, end=10, value=(1, 5), title="bar", css_classes=["foo"], width=300) slider.js_on_change('value', CustomJS(code=RECORD("value", "cb_obj.value"))) page = bokeh_model_page(slider) drag_range_slider(page.driver, ".foo", "lower", 150) results = page.results assert float(results['value'][0]) > 1 assert float(results['value'][1]) == 5 drag_range_slider(page.driver, ".foo", "lower", 150) results = page.results assert float(results['value'][0]) > 1 assert float(results['value'][1]) > 5 assert page.has_no_console_errors()
def generate_frame_plot(frame): sparse_data = json.loads(frame.frame_data) img = np.array(sparse_to_dense(sparse_data)) source = ColumnDataSource(data={'data': img.flatten()}) plot = figure(x_range=(0, 256), y_range=(0, 256), width=750, height=750, tools='hover,box_zoom,crosshair,reset,save', tooltips=[("x", "$x"), ("y", "$y"), ("value", "@image")], title="Frame {}".format(frame.id)) im = plot.image(image=[img], x=0, y=0, dw=256, dh=256, palette="Viridis11") range_slider_callback = CustomJS(args=dict(source=source, im=im), code=""" var image_source = im.data_source; var d = image_source.data['image'][0]; var f = slider.value for (var i = 0; i < d.length; i++) { if( source.data['data'][i] >= f[1]){ d[i] = f[1]; }else if(source.data['data'][i] <= f[0]){ d[i] = f[0]; } else{ d[i] = source.data['data'][i]; } } image_source.change.emit(); """) for i, cluster in enumerate(frame.clusters): bbox = json.loads(cluster.bbox) label = Label(x=bbox[3], y=bbox[2], text=str(i + 1), text_color='red', text_font_size="8pt") plot.add_layout(label) slider = RangeSlider(start=1, end=np.max(img) + 2, step=1, title="View Window", value=(1, np.max(img))) slider.js_on_change('value', range_slider_callback) range_slider_callback.args['slider'] = slider plot.toolbar.logo = None layout = Column(slider, plot) return layout
def generate_cluster_plot(cluster): data = json.loads(cluster.intensity_image) bbox = json.loads(cluster.bbox) length = bbox[2] - bbox[0] width = bbox[3] - bbox[1] img = np.array(data) source = ColumnDataSource(data={'data': img.flatten()}) plot = figure(match_aspect=True, width=350, height=350, tools='hover,box_zoom,crosshair,reset,save', tooltips=[("x", "$x"), ("y", "$y"), ("value", "@image")], title="Cluster View") im = plot.image(image=[img], x=0, y=0, dw=width, dh=length, palette="Inferno11") range_slider_callback = CustomJS(args=dict(source=source, im=im), code=""" var image_source = im.data_source; var d = image_source.data['image'][0]; var f = slider.value for (var i = 0; i < d.length; i++) { if( source.data['data'][i] >= f[1]){ d[i] = f[1]; }else if(source.data['data'][i] <= f[0]){ d[i] = f[0]; } else{ d[i] = source.data['data'][i]; } } image_source.change.emit(); """) # plot.sizing_mode = "fixed" plot.toolbar.logo = None slider = RangeSlider(start=1, end=np.max(img) + 2, step=1, title="View Window", value=(1, np.max(img))) slider.js_on_change('value', range_slider_callback) range_slider_callback.args['slider'] = slider return Column(slider, plot)
def create_slider(plot, startYear, endYear): callback = CustomJS(args=dict(plot=plot), code=""" var a = cb_obj.value; plot.x_range.start = a[0]; plot.x_range.end = a[1]; """) range_slider = RangeSlider(start=startYear, end=endYear,value=(startYear, endYear), step=1, width= 440, title="Year Range") range_slider.js_on_change('value', callback) layout = column(plot,column(range_slider)) return layout
def test_js_on_change_executes(self, bokeh_model_page): slider = RangeSlider(start=0, end=10, value=(1, 5), title="bar", css_classes=["foo"], width=300) slider.js_on_change('value', CustomJS(code=RECORD("value", "cb_obj.value"))) page = bokeh_model_page(slider) drag_slider(page.driver, ".foo", "lower", 150) results = page.results assert float(results['value'][0]) > 1 assert float(results['value'][1]) == 5 drag_slider(page.driver, ".foo", "lower", 150) results = page.results assert float(results['value'][0]) > 1 assert float(results['value'][1]) > 5 assert page.has_no_console_errors()
var column = source.data['speed']; // iterate through rows of data source and see if each satisfies some constraint for (var i = 0; i < column.length; i++){ if(column[i]>min_speed && column[i]<max_speed){ indices.push(true); } else { indices.push(false); } } console.log("filter completed"); return indices; ''', args={'speed_slider': speed_slider}) speed_slider.js_on_change( "value", CustomJS(code="source.change.emit();", args=dict(source=source_visible))) # range slider for time start = datetime.datetime.fromtimestamp(df.timestamp.min()) end = datetime.datetime.fromtimestamp(df.timestamp.max()) time_slider = DateRangeSlider(title="Промежуток времени", value=(start, end), start=start, end=end, format="%H:%M", step=10 * 60 * 1000) time_filter = CustomJSFilter(code=''' var min_timestamp = time_slider.value[0]/1000 - 3600 var max_timestamp = time_slider.value[1]/1000 - 3600 console.log(min_timestamp, max_timestamp);
def compare_multiparameter_landscape_samples( Samples: List[List[multiparameter_landscape]], indices=None, TOOLTIPS=None, GroupLabels: List[str] = None, colors: List[str] = None): """ Plots the mean landscapes of each collection to a single axes in range index = [ind_min,ind_max] together with a boxplot comparing the distribution of the functional values yielded by integrating the landscapes over a user specified range :param Samples: List List of lists of multiparameter_landscape objects so Sample[k][i] contains the i^th multiparameter landscape from the k^th Sample Group. All multiparameter landscapes are assumed to have the same bounds as Samples[0][0]. :param indices: The range of indices to be plotted :param TOOLTIPS: A bokeh TOOLTIPS list to control features that can be probed in the plot :param GroupLabels: A list of the names of the Sample Groups :param colors: A list of the colors for the Sample Groups """ for k in range(len(Samples)): if not (Samples[k][0].bounds.lower_left == Samples[0][0].bounds.lower_left and Samples[k][0].bounds.upper_right == Samples[0][0].bounds.upper_right): raise TypeError( 'Inconsistent bounds for landscapes in Sample List') for i in range(len(Samples[k])): if not (Samples[k][i].bounds.lower_left == Samples[0][0].bounds.lower_left and Samples[k][i].bounds.upper_right == Samples[0][0].bounds.upper_right): raise TypeError( 'Inconsistent bounds for landscapes in Sample List') for k in range(len(Samples)): if not (Samples[k][0].weight == Samples[0][0].weight).all(): raise TypeError( 'Inconsistent weights for landscapes in Sample List') for i in range(len(Samples[k])): if not (Samples[k][i].weight == Samples[k][0].weight).all(): raise TypeError( 'Inconsistent weights for landscapes in Sample List') for k in range(len(Samples)): if not Samples[k][0].grid_step_size == Samples[0][0].grid_step_size: raise TypeError( 'Inconsistent grid step size for landscapes in Sample List') for i in range(len(Samples[k])): if not Samples[k][i].grid_step_size == Samples[k][0].grid_step_size: raise TypeError( 'Inconsistent grid step size for landscapes in Sample List' ) if indices is None: indices = [1, Samples[0][0].maximum_landscape_depth] elif type(indices[0]) is int and type(indices[1]) is int and \ (indices[1] <= Samples[0][0].maximum_landscape_depth): indices = indices else: raise TypeError( 'Index range must provide integer values no bigger that the landscape depth' ) if TOOLTIPS is None: TOOLTIPS = [("x", "$x"), ("y", "$y"), ("value", "@image")] if GroupLabels is None: GroupLabels = ['Group ' + str(s + 1) for s in range(len(Samples)) ] * (indices[1] - indices[0] + 1) else: if not len(GroupLabels) == len(Samples): raise TypeError( 'GroupLabels list is not the same length as the sample list') if colors is None: colors = [ 'plum', 'powderblue', 'gold', 'greenyellow', 'mediumblue', 'firebrick' ] else: if not len(colors) == len(Samples): raise TypeError( 'Color list is not the same length as the sample list') bounds = Samples[0][0].bounds grid_step_size = Samples[0][0].grid_step_size weight = Samples[0][0].weight ###################### # Define ColumnDataSources # rectangle_source = ColumnDataSource( data=dict(x=[(bounds.lower_left[0] + bounds.upper_right[0]) / 2], y=[(bounds.lower_left[1] + bounds.upper_right[1]) / 2], width=[bounds.upper_right[0] - bounds.lower_left[0]], height=[bounds.upper_right[1] - bounds.lower_left[1]])) rect = Rect( x='x', y='y', width='width', height='height', fill_alpha=0.2, fill_color='white', line_color='white', ) landscape_properties = ColumnDataSource(data=dict( bounds=[[bounds.lower_left, bounds.upper_right]], x_steps=[ int( round((bounds.upper_right[0] - bounds.lower_left[0]) / grid_step_size * weight[1] + 1)) ], y_steps=[ int( round((bounds.upper_right[1] - bounds.lower_left[1]) / grid_step_size * weight[0] + 1)) ], indices=[indices])) boxplot_source = ColumnDataSource( data=dict(cats=[ "Group " + str(s + 1) + ", k=" + str(k + 1) for k in range(indices[0] - 1, indices[1]) for s in range(len(Samples)) ], colors=[ colors[s] for _ in range(indices[0] - 1, indices[1]) for s in range(len(Samples)) ], q1=[-1] * (len(Samples) * (indices[1] - indices[0] + 1)), q2=[0] * (len(Samples) * (indices[1] - indices[0] + 1)), q3=[1] * (len(Samples) * (indices[1] - indices[0] + 1)), lower=[-2] * (len(Samples) * (indices[1] - indices[0] + 1)), upper=[2] * (len(Samples) * (indices[1] - indices[0] + 1)), labels=GroupLabels)) ###################### # Callback Functions # rotated_samples = [ rotate_stack(stack_landscapes(Samples[s])) for s in range(len(Samples)) ] callback_x = CustomJS(args=dict(rectangle_source=rectangle_source, lp=landscape_properties, bp=boxplot_source, samples=rotated_samples), code=""" function Find_Median(list) { var Size = list.length; list = Array.from(list).sort(function(a, b) { return a - b }); console.log('sorted') console.log(list) var Final_Number = 0; var HalfWay = 0; if (Size % 2 == 0) { HalfWay = Math.round((list.length) / 2)-1; console.log('Halfway index') console.log(HalfWay) var Value1 = list[HalfWay]; var Value2 = list[HalfWay + 1]; console.log('Value1') console.log(Value1) console.log('Value2') console.log(Value2) var Number = Value1 + Value2; Final_Number = Number / 2; console.log('Final_Number') console.log(Final_Number) } else { HalfWay = Math.round(list.length / 2)-1; console.log('Halfway index') console.log(HalfWay) Final_Number = list[HalfWay]; console.log('Final_Number') console.log(Final_Number) } console.log('Final_Number') console.log(Final_Number) return Final_Number; } function BiggerElements(val) { return function(evalue, index, array) { return (evalue >= val); }; } function SmallerElements(val) { return function(evalue, index, array) { return (evalue <= val); }; } var data = rectangle_source.data; var xrange = cb_obj.value data['x'] = [(xrange[0]+xrange[1])/2] data['width'] = [(xrange[1]-xrange[0])] rectangle_source.change.emit(); var bpdata = bp.data; var ylow = data['y'][0] - data['height'][0]/2; var yhigh = data['y'][0] + data['height'][0]/2; var a = Math.round(Math.PI) var bounds = lp.data['bounds'][0]; var x_steps = lp.data['x_steps']; var y_steps = lp.data['y_steps']; var indices = lp.data['indices'][0]; var xmin = Math.round(xrange[0]/ bounds.upper_right[0]* x_steps[0]); var xmax = Math.round(xrange[1]/ bounds.upper_right[0]* x_steps[0]); var ymin = Math.round(ylow/ bounds.upper_right[1]* y_steps[0]); var ymax = Math.round(yhigh/ bounds.upper_right[1]* y_steps[0]); var q1 = bpdata['q1'] var q2 = bpdata['q2'] var q3 = bpdata['q3'] var lower = bpdata['lower'] var upper = bpdata['upper'] var number_of_samples = samples.length console.log("number_of_samples") console.log(number_of_samples) console.log(samples[0]) console.log(samples[0].length) for (var s = 0; s < number_of_samples; s++ ) { for (var k = 0; k < indices[1]-indices[0]+1 ; k++ ) { console.log("Index") console.log(k) console.log('samples[s].length') console.log(samples[s].length) var holder = Array.from(Array(samples[s].length), () => 0) console.log('holder') console.log(holder) for (var l = 0; l < samples[s].length; l++ ) { console.log("Sample") console.log(l) var value = 0 for (var i = xmin; i < xmax; i++) { for (var j = ymin; j < ymax ; j++) { value += samples[s][l][k][j][i] } } console.log('value') console.log(value) holder[l] = value / (x_steps * y_steps) } console.log('holder') console.log(holder) console.log('Median') console.log(Find_Median(holder)) console.log('Median^') q2[number_of_samples*k + s] = Find_Median(holder) q1[number_of_samples*k + s] = Find_Median(holder.filter(SmallerElements(Find_Median(holder)))) q3[number_of_samples*k + s] = Find_Median(holder.filter(BiggerElements(Find_Median(holder)))) lower[number_of_samples*k + s] = Math.min.apply(Math,holder) upper[number_of_samples*k + s] = Math.max.apply(Math,holder) } } console.log('q2') console.log(q2) bp.change.emit() """) callback_y = CustomJS(args=dict(rectangle_source=rectangle_source, lp=landscape_properties, bp=boxplot_source, samples=rotated_samples), code=""" function Find_Median(list) { var Size = list.length; list = Array.from(list).sort(function(a, b) { return a - b }); console.log('sorted') console.log(list) var Final_Number = 0; var HalfWay = 0; if (Size % 2 == 0) { HalfWay = Math.round((list.length) / 2)-1; console.log('Halfway index') console.log(HalfWay) var Value1 = list[HalfWay]; var Value2 = list[HalfWay + 1]; console.log('Value1') console.log(Value1) console.log('Value2') console.log(Value2) var Number = Value1 + Value2; Final_Number = Number / 2; console.log('Final_Number') console.log(Final_Number) } else { HalfWay = Math.round(list.length / 2)-1; console.log('Halfway index') console.log(HalfWay) Final_Number = list[HalfWay]; console.log('Final_Number') console.log(Final_Number) } console.log('Final_Number') console.log(Final_Number) return Final_Number; } function BiggerElements(val) { return function(evalue, index, array) { return (evalue >= val); }; } function SmallerElements(val) { return function(evalue, index, array) { return (evalue <= val); }; } var data = rectangle_source.data; var yrange = cb_obj.value data['y'] = [(yrange[0]+yrange[1])/2] data['height'] = [(yrange[1]-yrange[0])] rectangle_source.change.emit(); var bpdata = bp.data; var bounds = lp.data['bounds'][0]; var x_steps = lp.data['x_steps']; var y_steps = lp.data['y_steps']; var indices = lp.data['indices'][0]; var xlow = data['x'][0] - data['width'][0]/2; var xhigh = data['x'][0] + data['width'][0]/2; var x_steps = lp.data['x_steps']; var y_steps = lp.data['y_steps']; var xmin = Math.round(xlow/ bounds.upper_right[0]* x_steps[0]); var xmax = Math.round(xhigh/ bounds.upper_right[0]* x_steps[0]); var ymin = Math.round(yrange[0]/ bounds.upper_right[1]* y_steps[0]); var ymax = Math.round(yrange[1]/ bounds.upper_right[1]* y_steps[0]); var q1 = bpdata['q1'] var q2 = bpdata['q2'] var q3 = bpdata['q3'] var lower = bpdata['lower'] var upper = bpdata['upper'] var number_of_samples = samples.length console.log("number_of_samples") console.log(number_of_samples) console.log(samples[0]) console.log(samples[0].length) for (var s = 0; s < number_of_samples; s++ ) { for (var k = 0; k < indices[1]-indices[0]+1 ; k++ ) { console.log("Index") console.log(k) console.log('samples[s].length') console.log(samples[s].length) var holder = Array.from(Array(samples[s].length), () => 0) console.log('holder') console.log(holder) for (var l = 0; l < samples[s].length; l++ ) { console.log("Sample") console.log(l) var value = 0 for (var i = xmin; i < xmax; i++) { for (var j = ymin; j < ymax ; j++) { value += samples[s][l][k][j][i] } } console.log('value') console.log(value) holder[l] = value / (x_steps * y_steps) } console.log('holder') console.log(holder) console.log('Median') console.log(Find_Median(holder)) console.log('Median^') q2[number_of_samples*k + s] = Find_Median(holder) q1[number_of_samples*k + s] = Find_Median(holder.filter(SmallerElements(Find_Median(holder)))) q3[number_of_samples*k + s] = Find_Median(holder.filter(BiggerElements(Find_Median(holder)))) lower[number_of_samples*k + s] = Math.min.apply(Math,holder) upper[number_of_samples*k + s] = Math.max.apply(Math,holder) } } console.log('q2') console.log(q2) bp.change.emit() """) ######################### # Widgets and callbacks # x_range_slider = RangeSlider(start=bounds.lower_left[0], end=bounds.upper_right[0], value=(bounds.lower_left[0], bounds.upper_right[0]), step=.1, title="x_range", width=400) y_range_slider = RangeSlider(start=bounds.lower_left[1], end=bounds.upper_right[1], value=(bounds.lower_left[1], bounds.upper_right[1]), step=.1, title="y_range", width=400) x_range_slider.js_on_change('value', callback_x) y_range_slider.js_on_change('value', callback_y) ######################### # Make Plots # mean_landscape_plots = [] for s in range(len(Samples)): mean_landscape = compute_mean_landscape(Samples[s]) row_of_plots = plot_multiparameter_landscapes(mean_landscape, indices, TOOLTIPS=TOOLTIPS) row_of_plots.sizing_mode = "scale_both" mean_landscape_plots.append(row_of_plots) for plot in row_of_plots.children: plot.add_glyph(rectangle_source, rect) plot.plot_height = 250 plot.plot_width = 250 plot.border_fill_color = colors[s] plot.min_border = 15 boxplots = figure(x_range=boxplot_source.data['cats'], height=350) # stems boxplots.segment(source=boxplot_source, x0='cats', y0='upper', x1='cats', y1='q3', color="black") boxplots.segment(source=boxplot_source, x0='cats', y0='lower', x1='cats', y1='q1', color="black") # boxes boxplots.vbar(source=boxplot_source, x='cats', top='q3', bottom='q2', width=0.5, fill_color='colors', line_color='black', legend_group='labels') boxplots.vbar(source=boxplot_source, x='cats', top='q2', bottom='q1', width=0.5, fill_color='colors', line_color='black') # whiskers (almost-0 height rects simpler than segments) boxplots.rect(source=boxplot_source, x='cats', y='lower', width=0.2, height=0.000001, color='black') boxplots.rect(source=boxplot_source, x='cats', y='upper', width=0.2, height=0.000001, color='black') boxplots.legend.glyph_width = 100 boxplots.legend.glyph_height = 40 boxplots.legend.label_text_font_size = '14pt' boxplots.xgrid.grid_line_color = None boxplots.sizing_mode = "scale_both" # ########################## # # Layout and Show Plot # sliders = column([x_range_slider, y_range_slider]) DOM = column(column(mean_landscape_plots), sliders, boxplots) return DOM
def show_camera(content, geom, pad_width, pad_height, label, titles=None, showlog=True, display_range=None, content_lowlim=None, content_upplim=None): """ Parameters ---------- content: pixel-wise quantity to be plotted, ndarray with shape (N, number_of_pixels) where N is the number of different sets of pixel values, for example N different data runs or whatever. The shape can also be just (number_of_pixels), in case a single camera display is to be shown geom: camera geometry pad_width: width in pixels of each of the 3 pads in the plot pad_height: height in pixels of each of the 3 pads in the plot label: string to label the quantity which is displayed, the same for the N sets of pixels inside "content" titles: list of N strings, with the title specific to each of the sets of pixel values to be displayed: for example, indicating run numbers content_lowlim: scalar or ndarray of shape(N, number_of_pixels), same as content: lowest value of "content" which is considered healthy, below which a message will be written out content_upplim: highest value considered healthy, same as above display_range: range of "content" to be displayed Returns ------- [slider, p1, range_slider, p2, p3]: three bokeh figures, intended for showing them on the same row, and two sliders, one for the run numbers ( or whatever "sets" of data we are displaying) and the other for the z-range of the plots. p1 is the camera display (with "content" in linear & logarithmic scale) p2: content vs. pixel p3: histogram of content (with one entry per pixel) """ # patch to reduce gaps between bokeh's cam circular pixels: camgeom = copy.deepcopy(geom) numsets = 1 if np.ndim(content) > 1: numsets = content.shape[0] # numsets is the number of different sets of pixel data to be displayed allimages = [] if np.ndim(content) == 1: allimages.append(content) else: for i in range(1, numsets + 1): allimages.append(content[i - 1]) if titles is None: titles = [''] * numsets # By default we plot the range which contains 99.8 of all events, so that # outliers do not prevent us from seing the bulk of the data: display_min = np.nanquantile(allimages, 0.001) display_max = np.nanquantile(allimages, 0.999) if display_range is not None: display_min = display_range[0] display_max = display_range[1] cam = CameraDisplay(camgeom, display_min, display_max, label, titles[0], use_notebook=False, autoshow=False) cam.image = allimages[0] cam.figure.title.text = titles[0] allimageslog = [] camlog = None source1log = None color_mapper_log = None titlelog = None if showlog: for image in allimages: logcontent = np.copy(image) for i, x in enumerate(logcontent): # workaround as long as log z-scale is not implemented in bokeh camera: if x <= 0: logcontent[i] = np.nan else: logcontent[i] = np.log10(image[i]) allimageslog.append(logcontent) camlog = CameraDisplay(camgeom, np.nanquantile(allimageslog, 0.001), np.nanquantile(allimageslog, 0.999), label, titles[0], use_notebook=False, autoshow=False) camlog.image = allimageslog[0] camlog.figure.title.text = titles[0] source1log = camlog.datasource color_mapper_log = camlog._color_mapper titlelog = camlog.figure.title cluster_i = [] cluster_j = [] pix_id_in_cluster = [] for i in camgeom.pix_id: data = get_pixel_location(i) cluster_i.append(data[0]) cluster_j.append(data[1]) pix_id_in_cluster.append(data[2]) for c in [cam, camlog]: if c is None: continue c.datasource.add(list(c.geom.pix_id), 'pix_id') c.datasource.add(cluster_i, 'cluster_i') c.datasource.add(cluster_j, 'cluster_j') c.datasource.add(pix_id_in_cluster, 'pix_id_in_cluster') # c.add_colorbar() c.figure.plot_width = pad_width c.figure.plot_height = int(pad_height * 0.85) c.figure.grid.visible = False c.figure.axis.visible = True c.figure.xaxis.axis_label = 'X position (m)' c.figure.yaxis.axis_label = 'Y position (m)' c.figure.add_tools( HoverTool(tooltips=[('pix_id', '@pix_id'), ('value', '@image'), ('cluster (i,j)', '(@cluster_i, @cluster_j)'), ('pix # in cluster', '@pix_id_in_cluster')], mode='mouse', point_policy='snap_to_data')) tab1 = Panel(child=cam.figure, title='linear') if showlog: tab2 = Panel(child=camlog.figure, title='logarithmic') p1 = Tabs(tabs=[tab1, tab2]) else: p1 = Tabs(tabs=[tab1]) p1.margin = (0, 0, 0, 25) p2 = figure(background_fill_color='#ffffff', y_range=(display_min, display_max), x_axis_label='Pixel id', y_axis_label=label) p2.min_border_top = 60 p2.min_border_bottom = 70 source2 = ColumnDataSource( data=dict(pix_id=cam.geom.pix_id, value=cam.image)) pixel_data = p2.circle(x='pix_id', y='value', size=2, source=source2) if content_lowlim is None: content_lowlim = np.nan * np.ones_like(content) if content_upplim is None: content_upplim = np.nan * np.ones_like(content) if np.isscalar(content_lowlim): content_lowlim = content_lowlim * np.ones_like(content) source2_lowlim = ColumnDataSource( data=dict(pix_id=cam.geom.pix_id, value=content_lowlim[0])) p2.line(x='pix_id', y='value', source=source2_lowlim, line_dash='dashed', color='orange', line_width=2) if np.isscalar(content_upplim): content_upplim = content_upplim * np.ones_like(content) source2_upplim = ColumnDataSource( data=dict(pix_id=cam.geom.pix_id, value=content_upplim[0])) p2.line(x='pix_id', y='value', source=source2_upplim, line_dash='dashed', color='red') p2.add_tools( HoverTool(tooltips=[('(pix_id, value)', '(@pix_id, @value)')], mode='mouse', point_policy='snap_to_data', renderers=[pixel_data])) p2.y_range = Range1d(display_min, display_max) allhists = [] alledges = [] # We define 100 bins between display_min and display_max # Note that values beyond that range won't be histogrammed and hence will # not appear on the "p3" figure below. nbins = 100 for image in allimages: hist, edges = np.histogram(image[~np.isnan(image)], bins=nbins, range=(display_min, display_max)) allhists.append(hist) alledges.append(edges) source3 = ColumnDataSource(data=dict(top=allhists[0], bottom=0.7 * np.ones_like(allhists[0]), left=alledges[0][:-1], right=alledges[0][1:])) p3 = figure(background_fill_color='#ffffff', y_range=(0.7, np.max(allhists) * 1.1), x_range=(display_min, display_max), x_axis_label=label, y_axis_label='Number of pixels', y_axis_type='log') p3.quad(top='top', bottom='bottom', left='left', right='right', source=source3) if titles is None: titles = [None] * len(allimages) cdsdata = dict(z=allimages, hist=allhists, edges=alledges, titles=titles) # BEWARE!! these have to be lists of arrays. Not 2D numpy arrays!! cdsdata['lowlim'] = [x for x in content_lowlim] cdsdata['upplim'] = [x for x in content_upplim] if showlog: cdsdata['zlog'] = allimageslog cds_allimages = ColumnDataSource(data=cdsdata) # One has to add here everything that must change when moving the slider: callback = CustomJS(args=dict(source1=cam.datasource, source1log=source1log, source2=source2, source2_lowlim=source2_lowlim, source2_upplim=source2_upplim, source3=source3, zz=cds_allimages, title=cam.figure.title, titlelog=titlelog, showlog=showlog), code=""" var slider_value = cb_obj.value var z = zz.data['z'] varzlow = zz.data['lowlim'] varzupp = zz.data['upplim'] var edges = zz.data['edges'] var hist = zz.data['hist'] for (var i = 0; i < source1.data['image'].length; i++) { source1.data['image'][i] = z[slider_value-1][i] if (showlog) { var zlog = zz.data['zlog'] source1log.data['image'][i] = zlog[slider_value-1][i] } source2.data['value'][i] = source1.data['image'][i] source2_lowlim.data['value'][i] = varzlow[slider_value-1][i] source2_upplim.data['value'][i] = varzupp[slider_value-1][i] } for (var j = 0; j < source3.data['top'].length; j++) { source3.data['top'][j] = hist[slider_value-1][j] source3.data['left'][j] = edges[slider_value-1][j] source3.data['right'][j] = edges[slider_value-1][j+1] } title.text = zz.data['titles'][slider_value-1] source1.change.emit() if (showlog) { titlelog.text = title.text source1log.change.emit() } source2.change.emit() source2_lowlim.change.emit() source2_upplim.change.emit() source3.change.emit() """) slider = None if numsets > 1: slider_height = 300 # WARNING: the html won't look nice for number of sets much larger # than 300! But in this way we avoid that the slider skips elements: if numsets > 299: slider_height = numsets + 1 slider = Slider(start=1, end=numsets, value=1, step=1, title="run", orientation='vertical', show_value=False, height=slider_height) slider.margin = (0, 0, 0, 35) slider.js_on_change('value', callback) callback2 = CustomJS(args=dict(color_mapper=cam._color_mapper, color_mapper_log=color_mapper_log, showlog=showlog), code=""" var range = cb_obj.value color_mapper.low = range[0] color_mapper.high = range[1] color_mapper.change.emit() if (showlog) { if (range[0] > 0.) color_mapper_log.low = Math.log(range[0])/Math.LN10 color_mapper_log.high = Math.log(range[1])/Math.LN10 color_mapper_log.change.emit() } """) step = (display_max - display_min) / 100. range_slider = RangeSlider(start=display_min, end=display_max, value=(display_min, display_max), step=step, title="z_range", orientation='vertical', direction='rtl', height=300, show_value=False) range_slider.js_on_change('value', callback2) return [slider, p1, range_slider, p2, p3]
var avg = arrSum(avgList) / avgList.length for (var i = 0; i < alpha.length; i++){ line_y[i] = avg } source.change.emit(); line_source.change.emit(); """) p.add_tools(bar_hover) p.add_tools(line_hover) p.sizing_mode = 'scale_width' p.background_fill_color = "#fdf6e3" p.border_fill_color = "#fdf6e3" p.yaxis[0].formatter = NumeralTickFormatter(format="$0,0") p.outline_line_color = None range_slider = RangeSlider(start=0, end=max(id_range), value=(0, max(id_range)), step=1, title="Showing IDs") range_slider.js_on_change('value', callback) # range_slider.js_on_change("value", CustomJS(code=""" # console.log('range_slider: value=' + this.value[0], this.toString()) # """)) range_slider.background = "#fdf6e3" layout = Column(p, range_slider) layout.sizing_mode = "scale_width" # show the results show(layout)
def create_bokeh(G, title, fname, extract_size=15, feature_size=10): # Add attributes this way try: max_weight = max(nx.get_edge_attributes(G, 'weight').values()) except ValueError: max_weight = 1.0 calc_alpha = lambda x: 0.1 + 0.6 * (x / max_weight) edge_attrs = {(s,e): calc_alpha(d['weight']) for s,e,d in G.edges(data=True)} node_attrs = { k: { "_size": feature_size if v['_type']=='spin' else extract_size, "_num_members": len(eval(v.get('members', '[]'))) } for k,v in G.nodes(data=True) } nx.set_edge_attributes(G, edge_attrs, "_alpha") nx.set_node_attributes(G, node_attrs) # Show with Bokeh plot = Plot(plot_width=1100, plot_height=700, x_range=Range1d(-1.1, 1.1), y_range=Range1d(-1.1, 1.1)) plot.title.text = title node_hover_tool = HoverTool(tooltips=[("Name", "@index"), ("Members", "@members")]) plot.add_tools(node_hover_tool, WheelZoomTool(), BoxZoomTool(), ResetTool(), TapTool()) # Tried this but default layout is just better... # def layout(x, **kwargs): # return nx.spring_layout(x, **kwargs, k=1.1/sqrt(x.number_of_nodes()),iterations=10000) # graph_renderer = from_networkx(G, layout, scale=1, center=(0, 0)) graph_renderer = from_networkx(G, nx.spring_layout, scale=1, center=(0, 0)) graph_renderer.node_renderer.glyph = Circle(size='_size', fill_color="_color") graph_renderer.node_renderer.selection_glyph = Circle(size=15, fill_color="orange")#works graph_renderer.node_renderer.hover_glyph = Circle(size=15, fill_color="red")#works graph_renderer.edge_renderer.glyph = MultiLine(line_alpha="_alpha", line_width=1) plot.renderers.append(graph_renderer) # Add filter slider df = nx.to_pandas_edgelist(G) max_members = max(nx.get_node_attributes(G,"_num_members").values()) backup_node_data = graph_renderer.node_renderer.data_source.data backup_edge_data = graph_renderer.edge_renderer.data_source.data code = """ const min_nodes = cb_obj.value[0]; const max_nodes = cb_obj.value[1]; var to_keep; to_keep = ndata.index.map((v,i) =>{ if (ndata._type[i] !== "spin"){return i} else{if(ndata._num_members[i]<=max_nodes && ndata._num_members[i]>=min_nodes) {return i}} }).filter(x => Boolean(x) | x === 0); let keep_names = ndata.index.filter((_,i)=>to_keep.includes(i)) let new_nodes = {}; Object.keys(ndata).forEach(k => { new_nodes[k] = ndata[k].filter((_,i)=>to_keep.includes(i)); }) let new_edges = {}; Object.keys(edata).forEach(k => { new_edges[k] = edata[k].filter((_,i)=>{ let end = false; let start = false; if (keep_names.includes(edata.start[i])) start = true if (keep_names.includes(edata.end[i])) end = true return end && start; }); }); // Update graph graph_setup.node_renderer.data_source.data = new_nodes; graph_setup.edge_renderer.data_source.data = new_edges; graph_setup.node_renderer.data_source.change.emit(); graph_setup.edge_renderer.data_source.change.emit(); """ min_cb = CustomJS(args=dict(graph_setup=graph_renderer, start=df['source'].values, end=df['target'].values, ndata=backup_node_data, edata=backup_edge_data, type="min"), code=code) min_slider = RangeSlider(title='Filter by Number of Members', start=1, end=max_members, value=(1, max_members)) min_slider.js_on_change('value', min_cb) output_file(fname) layout = Column(plot, min_slider) save(layout)
def bokeh_plot_sequence(preds, name=None, n=2, cutoff=.95, cutoff_method='default', width=1000, color_sequence=False, title=''): """Plot sequence view of binders """ from bokeh.plotting import figure from bokeh.models import ColumnDataSource, LinearAxis, Grid, Range1d, Text, Rect, CustomJS, Slider, RangeSlider, FactorRange from bokeh.layouts import gridplot, column colors = [] seqs = [] text = [] alleles = [] ylabels = [] pcolors = get_bokeh_colors() for P in preds: print(P.name) df = P.data #get sequence from results dataframe seq = base.sequence_from_peptides(df) l = base.get_length(df) b = P.get_binders(name=name, cutoff=cutoff, cutoff_method=cutoff_method) pb = P.promiscuous_binders(name=name, cutoff=cutoff, n=n, cutoff_method=cutoff_method) b = b[b.pos.isin(pb.pos)] #only promiscuous grps = b.groupby('allele') al = list(grps.groups) alleles.extend(al) ylabels.extend([P.name + ' ' + i for i in al]) currseq = [seq for i in al] seqs.extend(currseq) t = [i for s in currseq for i in s] text.extend(t) print(len(seqs), len(text)) for a in al: pos = [] f = list(b[b.allele == a].pos) for i in f: pos.extend(np.arange(i, i + l)) if color_sequence is True: c = plotting.get_sequence_colors(seq) else: c = ['white' for i in seq] for i in pos: c[i] = pcolors[P.name] colors.extend(c) #put into columndatasource for plotting N = len(seqs[0]) S = len(alleles) x = np.arange(1, N + 1) y = np.arange(0, S, 1) xx, yy = np.meshgrid(x, y) gx = xx.ravel() gy = yy.flatten() recty = gy + .5 source = ColumnDataSource( dict(x=gx, y=gy, recty=recty, text=text, colors=colors)) plot_height = len(seqs) * 15 + 60 x_range = Range1d(0, N + 1, bounds='auto') L = 100 if len(seq) < 100: L = len(seq) view_range = (0, L) viewlen = view_range[1] - view_range[0] fontsize = "8.5pt" tools = "xpan, reset, save" p = figure(title=title, plot_width=width, plot_height=plot_height, x_range=view_range, y_range=ylabels, tools=tools, min_border=0, sizing_mode='stretch_both', lod_factor=10, lod_threshold=1000) seqtext = Text(x="x", y="y", text="text", text_align='center', text_color="black", text_font="monospace", text_font_size=fontsize) rects = Rect(x="x", y="recty", width=1, height=1, fill_color="colors", line_color='gray', fill_alpha=0.6) p.add_glyph(source, rects) p.add_glyph(source, seqtext) p.xaxis.major_label_text_font_style = "bold" p.grid.visible = False p.toolbar.logo = None #preview view (no text) p1 = figure(title=None, plot_width=width, plot_height=S * 3 + 5, x_range=x_range, y_range=(0, S), tools=[], min_border=0, sizing_mode='stretch_width', lod_factor=10, lod_threshold=10) rects = Rect(x="x", y="recty", width=1, height=1, fill_color="colors", line_color=None, fill_alpha=0.6) previewrect = Rect(x=viewlen / 2, y=S / 2, width=viewlen, height=S * .99, line_color='darkblue', fill_color=None) p1.add_glyph(source, rects) p1.add_glyph(source, previewrect) p1.yaxis.visible = False p1.grid.visible = False p1.toolbar_location = None #callback for slider move jscode = """ var start = cb_obj.value[0]; var end = cb_obj.value[1]; x_range.setv({"start": start, "end": end}) rect.width = end-start; rect.x = start+rect.width/2; var fac = rect.width/width; console.log(fac); if (fac>=.14) { fontsize = 0;} else { fontsize = 8.5; } text.text_font_size=fontsize+"pt"; """ callback = CustomJS(args=dict(x_range=p.x_range, rect=previewrect, text=seqtext, width=p.plot_width), code=jscode) slider = RangeSlider(start=0, end=N, value=(0, L), step=10) #, callback_policy="throttle") slider.js_on_change('value_throttled', callback) #callback for plot drag jscode = """ start = parseInt(range.start); end = parseInt(range.end); slider.value[0] = start; rect.width = end-start; rect.x = start+rect.width/2; """ #p.x_range.callback = CustomJS(args=dict(slider=slider, range=p.x_range, rect=previewrect), # code=jscode) p = gridplot([[p1], [p], [slider]], toolbar_location="below", merge_tools=False) return p
def plot_sequence_alignment(aln, fontsize="8pt", plot_width=800, sizing_mode='stretch_width', palette='tab20', row_height=10, annot=None): """Bokeh sequence alignment viewer. Args: aln: biopython Multiple Sequence Alignment """ seqs = [rec.seq for rec in (aln)] ids = [rec.id for rec in aln] if len(seqs) <= 1: p = plot_empty('needs at least two sequences', plot_width) return p #ids=range(len(seqs)) text = [i for s in list(seqs) for i in s] colors = utils.get_sequence_colors(seqs, palette=palette) cons = utils.get_cons(aln) N = len(seqs[0]) S = len(seqs) width = .4 x = np.arange(1, N + 1) y = np.arange(0, S, 1) #print (y[:20]) xx, yy = np.meshgrid(x, y) gx = xx.ravel() gy = yy.flatten() recty = gy + .5 h = 1 / S #print (text) source = ColumnDataSource( dict(x=gx, y=gy, recty=recty, text=text, colors=colors)) plot_height = len(seqs) * row_height + 50 x_range = Range1d(0, N + 1, bounds='auto') L = 100 if len(seqs[0]) < 100: L = len(seqs[0]) view_range = (0, L) viewlen = view_range[1] - view_range[0] tools = "xpan, xwheel_zoom, tap, reset, save" #box_select = BoxSelectTool(callback=callback_select) #preview sequence view (no text) p = figure(title=None, plot_width=plot_width, plot_height=S * 2 + 25, x_range=x_range, y_range=(0, S), tools=tools, min_border=0, toolbar_location='below', sizing_mode='stretch_width') rects = Rect(x="x", y="recty", width=1, height=1, fill_color="colors", line_color=None, fill_alpha=0.4) p.add_glyph(source, rects) previewrect = Rect(x=viewlen / 2, y=S / 2, width=viewlen, height=S * .99, line_color='darkblue', fill_color=None) p.add_glyph(source, previewrect) p.yaxis.visible = False p.grid.visible = False #full sequence text view p1 = figure(title=None, plot_width=plot_width, plot_height=plot_height, x_range=view_range, y_range=ids, tools="xpan,reset", min_border=0, toolbar_location='below') #, lod_factor=1) seqtext = Text(x="x", y="y", text="text", text_align='center', text_color="black", text_font="monospace", text_font_size=fontsize) rects = Rect(x="x", y="recty", width=1, height=1, fill_color="colors", line_color=None, fill_alpha=0.5) p1.add_glyph(source, rects) p1.add_glyph(source, seqtext) p1.grid.visible = False p1.xaxis.major_label_text_font_style = "bold" p1.yaxis.minor_tick_line_width = 0 p1.yaxis.major_tick_line_width = 0 p1.toolbar.logo = None source2 = ColumnDataSource(dict(x=x, cons=cons)) p3 = figure(title=None, plot_width=plot_width, plot_height=50, x_range=p1.x_range, y_range=(Range1d(min(cons), .5)), tools="xpan") rects2 = Rect(x="x", y=0, width=1, height="cons", fill_color="gray", line_color=None, fill_alpha=0.7) p3.add_glyph(source2, rects2) p3.xaxis.visible = False p3.yaxis.visible = False p3.grid.visible = False p3.background_fill_color = "beige" if annot != None: p4 = figure(plot_width=plot_width, plot_height=35, x_range=p1.x_range, y_range=(0, 1)) #for a in annot: x = [annot[i] for i in annot] p4.text(x, .5, text=[i for i in annot], text_baseline="middle", text_align="center") p4.grid.visible = False p4.xaxis.visible = False p4.yaxis.visible = False else: p4 = None #callback for click jscode = """ row = parseInt(cb_obj.y); console.log(row); """ clicked = CustomJS(args=dict(source=source), code=jscode) p1.js_on_event(Tap, clicked) #callback for slider move jscode = """ var start = cb_obj.value[0]; var end = cb_obj.value[1]; x_range.setv({"start": start, "end": end}) rect.width = end-start; rect.x = start+rect.width/2; var fac = rect.width/width; if (fac>=.22) { fontsize = 0;} else { fontsize = 8.5; } text.text_font_size=fontsize+"pt"; """ callback = CustomJS(args=dict(x_range=p1.x_range, rect=previewrect, text=seqtext, width=p1.plot_width), code=jscode) slider = RangeSlider(start=0, end=N, value=(0, L), step=10, callback_policy="throttle") slider.js_on_change('value_throttled', callback) #callback for plot drag jscode = """ start = parseInt(range.start); end = parseInt(range.end); slider.value[0] = start; rect.width = end-start; rect.x = start+rect.width/2; """ p1.x_range.callback = CustomJS(args=dict(slider=slider, range=p1.x_range, rect=previewrect), code=jscode) p = gridplot([[p], [slider], [p3], [p1], [p4]], toolbar_location='below', sizing_mode=sizing_mode) return p
class RangeAnalyserGui: move_spans = \ """ start_span.location = cb_obj.value[0]; start_span.change.emit(); end_span.location = cb_obj.value[1]; end_span.change.emit(); """ update_range_slide = \ """ range_slide.start = cb_obj.start; range_slide.end = cb_obj.end; range_slide.change.emit(); """ def __init__(self, analyser): """ Creates and manages the Gui for RangeAnalysis """ self.analyser = analyser self.fig = self.make_fig() init_start = self.analyser.x.min() * 1.1 init_end = self.analyser.x.max() * 0.9 span_kwargs = dict(dimension='height', line_dash='dashed', line_width=3) self.start_span = Span(location=init_start, line_color='green', **span_kwargs) self.end_span = Span(location=init_end, line_color='red', **span_kwargs) self.range_slide = RangeSlider(start=self.analyser.x.min(), end=self.analyser.x.max(), step=self.analyser.dx, value=(init_start, init_end), width=self.fig.plot_width) self.start_connected = widgets.Button(label="start", button_type='success') self.end_connected = widgets.Button(label="end", button_type='success') self.setup_callbacks() self.app = self.make_app() self.doc = None @property def start(self): """ current start value of slider """ return self.range_slide.value[0] @start.setter def start(self, val): old = self.range_slide.value new = (val, old[1]) self.range_slide.value = new #self.range_slide.trigger('value', old, new) old_loc = self.start_span.location self.start_span.location= val # self.start_span.trigger('location', old_loc, val) @property def end(self): """ current end value of slider """ return self.range_slide.value[1] @end.setter def end(self, val): old = self.range_slide.value new = (old[0], val) self.range_slide.value = new #self.range_slide.trigger('value', old, new) old_loc = self.end_span.location self.end_span.location = val # self.end_span.trigger('location', old_loc, val) def make_fig(self): fig = bok.figure() fig.tools.append(HoverTool( tooltips=[(f"{self.analyser.xlabel if self.analyser.xlabel else 'x'}, {self.analyser.ylabel}", "$x, $y")])) fig.line(self.analyser.x, self.analyser.y, legend=self.analyser.ylabel) return fig def setup_callbacks(self): self.range_slide.js_on_change('value', CustomJS(code=self.move_spans, args={'start_span': self.start_span, 'end_span': self.end_span})) self.fig.x_range.callback = CustomJS(code=self.update_range_slide, args={'range_slide': self.range_slide}) self.start_connected.on_click(self.toggle_start_connected) self.end_connected.on_click(self.toggle_end_connected) def make_app(self): self.fig.add_layout(self.start_span) self.fig.add_layout(self.end_span) analysis_guis = [] for Analysis in self.analyser.analysis_types: analysis = Analysis(self.analyser) self.analyser.analyses[analysis.name] = analysis analysis_guis.append(analysis.gui.as_row()) layout = bklayouts.layout( [self.fig, bklayouts.column(*analysis_guis, bklayouts.row(self.start_connected, self.end_connected, width=300))], self.range_slide) def modify_doc(doc): self.doc = doc doc.add_root(layout) doc.add_periodic_callback(self.update_connected, 100) handler = FunctionHandler(modify_doc) app = Application(handler) return app def update_connected(self): old_start, old_end = self.start_connected.button_type, self.end_connected.button_type self.start_connected.button_type = "success" if self.analyser.start_connected else "danger" self.end_connected.button_type = "success" if self.analyser.end_connected else "danger" def toggle_start_connected(self): if self.analyser.start_connected: self.analyser.start = self.start else: self.analyser.start = None self.update_connected() def toggle_end_connected(self): if self.analyser.end_connected: self.analyser.end = self.end else: self.analyser.end = None self.update_connected()
from bokeh.io import show from bokeh.models import CustomJS, RangeSlider range_slider = RangeSlider(start=0, end=10, value=(1, 9), step=.1, title="Stuff") range_slider.js_on_change( "value", CustomJS(code=""" console.log('range_slider: value=' + this.value, this.toString()) """)) show(range_slider)
def get_dashboard(local_data=False): """ Assemble the dashboard. Define the layout, controls and its callbacks, as well as data sources. """ # Load data video_data = {} if local_data: data = pd.json_normalize( pd.read_json('data/channel/processed_data.json')['items']) with open('data/channel/processed_data.json', 'r') as f: data = json.load(f) video_data = data['items'] else: video_data = handle_channel_data.get_data_firebase()['items'] # X axis categories x_axis_map = { "Climber": "climber", "Zone": "zone", "Grade": "grade", } # Y axis categories y_axis_map = { "Count": "count", "Views": "viewCount", # "Favourites": "favoriteCount", "Likes": "likeCount", "Dislikes": "dislikeCount", "Comments": "commentCount" } # get ready to plot data barchart_data = prepare_barchart_data(video_data, x_axis_map) # html template to place the plots desc = Div(text=open(join(dirname(__file__), "templates/stats.html")).read(), sizing_mode="stretch_width") # initial data source fill data_to_plot = barchart_data['grade']['raw'] od = collections.OrderedDict( sorted(data_to_plot.items(), key=lambda x: x[0])) x_to_plot = np.array([key for key, _ in od.items()]) y_to_plot = np.array([val['count'] for _, val in od.items()]) source = ColumnDataSource(data=dict(x=x_to_plot, y=y_to_plot)) # initial data x_init = x_to_plot[0:NUM_RESULTS] y_init = y_to_plot[0:NUM_RESULTS] # Create Input controls checkbox_limit_results = CheckboxGroup( labels=["Show only first 50 results"], active=[0]) label_slider = Slider(start=0, end=90, value=90, step=1, title="Label Angle") range_slider = RangeSlider(title="Value Range", start=0, end=max(y_to_plot), value=(0, max(y_to_plot)), step=1) min_year = Slider(title="From", start=2015, end=2020, value=2015, step=1) max_year = Slider(title="To", start=2015, end=2020, value=2020, step=1) sort_order = RadioButtonGroup( labels=["Alphabetically", "Decreasing", "Increasing"], active=0) x_axis = Select(title="X Axis", options=sorted(x_axis_map.keys()), value="Grade") y_axis = Select(title="Y Axis", options=sorted(y_axis_map.keys()), value="Count") checkbox = CheckboxGroup( labels=["Show ratio with respect to number of videos"], active=[]) # show number of categories x_count_source = ColumnDataSource( data=dict(x_count=[len(x_init)], category=[x_axis.value])) columns = [ TableColumn(field="category", title="Category"), TableColumn(field="x_count", title="Count"), ] x_count_data_table = DataTable(source=x_count_source, columns=columns, width=320, height=280) # Generate the actual plot # p = figure(x_range=x_to_plot, y_range=(0, max(y_to_plot)), plot_height=250, title="{} {}".format(x_axis.value, y_axis.value), # toolbar_location="above") p = figure(x_range=x_init, y_range=(0, max(y_init)), plot_height=250, title="{} {}".format(x_axis.value, y_axis.value), toolbar_location="above") # Fill it with data and format it p.vbar(x='x', top='y', width=0.9, source=source) p.xaxis.major_label_orientation = math.pi / 2 p.add_tools(HoverTool(tooltips=[("name", "@x"), ("count", "@y")])) # Controls controls = [ checkbox_limit_results, range_slider, min_year, max_year, sort_order, x_axis, y_axis, checkbox, label_slider, x_count_data_table ] # Callbacks for controls label_callback = CustomJS(args=dict(axis=p.xaxis[0]), code=""" axis.major_label_orientation = cb_obj.value * Math.PI / 180; """) label_slider.js_on_change('value', label_callback) # limit checkbox checkbox_limit_results_callback = CustomJS( args=dict(source=source, x_source=x_count_source, o_data=barchart_data, sort_order=sort_order, x_axis_map=x_axis_map, x_axis=x_axis, y_axis_map=y_axis_map, y_axis=y_axis, range_slider=range_slider, checkbox=checkbox, fig=p, title=p.title), code=SORT_FUNCTION + JS_NUM_RESULTS + """ var data = o_data[x_axis_map[x_axis.value]]; var x = data['x']; var y = data['y']; var apply_limit = cb_obj.active.length > 0; var is_ratio = checkbox.active.length > 0; title.text = x_axis.value.concat(" ", y_axis.value); // Sort data var sorted_data = sortData(data['raw'], sort_order.active, y_axis_map[y_axis.value], is_ratio); var new_y = []; var new_x = []; var final_x = []; var final_y = []; for (var i = 0; i < x.length; i++) { if (apply_limit) { if(sorted_data[i][1] >= range_slider.value[0] && sorted_data[i][1] <= range_slider.value[1]) { new_x.push(sorted_data[i][0]); new_y.push(sorted_data[i][1]); } } else { new_x.push(sorted_data[i][0]); new_y.push(sorted_data[i][1]); } } if (apply_limit) { final_x = new_x.slice(0, num_results); final_y = new_y.slice(0, num_results); window.should_update_range = false; } else { final_x = new_x; final_y = new_y; window.should_update_range = true; } x_source.data['x_count'] = [final_x.length]; x_source.data['category'] = [x_axis.value]; x_source.change.emit(); source.data['x'] = new_x; source.data['y'] = new_y; source.change.emit(); fig.x_range.factors = []; fig.x_range.factors = final_x; if (Array.isArray(new_y) && new_y.length) { // range init and end cannot have same value var range_end = Math.max.apply(Math, new_y); if (range_end == 0 || range_end == -Infinity) { range_end = 1; } range_slider.value = [0, Math.max.apply(Math, final_y)]; range_slider.end = range_end; fig.y_range.end = Math.max.apply(Math, final_y); fig.change.emit(); } """) checkbox_limit_results.js_on_change('active', checkbox_limit_results_callback) # ratio checkbox checkbox_callback = CustomJS(args=dict( source=source, x_source=x_count_source, o_data=barchart_data, sort_order=sort_order, x_axis_map=x_axis_map, x_axis=x_axis, y_axis_map=y_axis_map, y_axis=y_axis, range_slider=range_slider, checkbox_limit_results=checkbox_limit_results, fig=p, title=p.title), code=SORT_FUNCTION + JS_NUM_RESULTS + """ var data = o_data[x_axis_map[x_axis.value]]; var x = data['x']; var y = data['y']; var is_ratio = cb_obj.active.length > 0; title.text = x_axis.value.concat(" ", y_axis.value); if (is_ratio) { title.text = x_axis.value.concat(" ", y_axis.value, " per video"); } // Sort data var sorted_data = sortData(data['raw'], sort_order.active, y_axis_map[y_axis.value], is_ratio); var new_y = []; var new_x = []; var final_x = []; var final_y = []; for (var i = 0; i < x.length; i++) { if (checkbox_limit_results.active.length <= 0) { if(sorted_data[i][1] >= range_slider.value[0] && sorted_data[i][1] <= range_slider.value[1]) { new_x.push(sorted_data[i][0]); new_y.push(sorted_data[i][1]); } } else { new_x.push(sorted_data[i][0]); new_y.push(sorted_data[i][1]); } } if (checkbox_limit_results.active.length > 0) { final_x = new_x.slice(0, num_results); final_y = new_y.slice(0, num_results); window.should_update_range = false; } else { final_x = new_x; final_y = new_y; window.should_update_range = true; } x_source.data['x_count'] = [final_x.length]; x_source.data['category'] = [x_axis.value]; x_source.change.emit(); source.data['x'] = new_x; source.data['y'] = new_y; source.change.emit(); fig.x_range.factors = []; fig.x_range.factors = final_x; if (Array.isArray(new_y) && new_y.length) { // range init and end cannot have same value var range_end = Math.max.apply(Math, new_y); if (range_end == 0 || range_end == -Infinity) { range_end = 1; } range_slider.value = [0, Math.max.apply(Math, final_y)]; range_slider.end = range_end; fig.y_range.end = Math.max.apply(Math, final_y); fig.change.emit(); } """) checkbox.js_on_change('active', checkbox_callback) # range slider range_callback = CustomJS(args=dict( source=source, x_source=x_count_source, o_data=barchart_data, sort_order=sort_order, x_axis_map=x_axis_map, x_axis=x_axis, y_axis_map=y_axis_map, y_axis=y_axis, checkbox=checkbox, checkbox_limit_results=checkbox_limit_results, fig=p), code=SORT_FUNCTION + """ if (window.should_update_range == true) { var data = o_data[x_axis_map[x_axis.value]]; var x = data['x']; var y = data['y']; var is_ratio = checkbox.active.length > 0; // Sort data var sorted_data = sortData(data['raw'], sort_order.active, y_axis_map[y_axis.value], is_ratio); var new_y = []; var new_x = []; for (var i = 0; i < x.length; i++) { if (checkbox_limit_results.active.length <= 0) { if (sorted_data[i][1] >= cb_obj.value[0] && sorted_data[i][1] <= cb_obj.value[1]) { new_x.push(sorted_data[i][0]); new_y.push(sorted_data[i][1]); } } else { new_x.push(sorted_data[i][0]); new_y.push(sorted_data[i][1]); } } x_source.data['x_count'] = [new_x.length]; x_source.data['category'] = [x_axis.value]; x_source.change.emit(); source.data['x'] = new_x; source.data['y'] = new_y; source.change.emit(); fig.x_range.factors = []; fig.x_range.factors = new_x; if (Array.isArray(new_y) && new_y.length) { fig.y_range.end = Math.max.apply(Math, new_y); } } else { window.should_update_range = true; } """) range_slider.js_on_change('value', range_callback) # variable to group data x_axis_callback = CustomJS(args=dict( source=source, x_source=x_count_source, o_data=barchart_data, x_axis_map=x_axis_map, y_axis_map=y_axis_map, y_axis=y_axis, range_slider=range_slider, sort_order=sort_order, checkbox=checkbox, checkbox_limit_results=checkbox_limit_results, fig=p, title=p.title), code=SORT_FUNCTION + JS_NUM_RESULTS + """ title.text = cb_obj.value.concat(" ", y_axis.value); var data = o_data[x_axis_map[cb_obj.value]]; var x = data['x']; var y = data['y']; var is_ratio = checkbox.active.length > 0; if (is_ratio) { title.text = title.text.concat(" per video"); } var sorted_data = sortData(data['raw'], sort_order.active, y_axis_map[y_axis.value], is_ratio); var new_y = []; var new_x = []; var final_x = []; var final_y = []; for (var i = 0; i < x.length; i++) { new_x.push(sorted_data[i][0]); new_y.push(sorted_data[i][1]); } if (checkbox_limit_results.active.length > 0) { final_x = new_x.slice(0, num_results); final_y = new_y.slice(0, num_results); window.should_update_range = false; } else { final_x = new_x; final_y = new_y; window.should_update_range = true; } x_source.data['x_count'] = [final_x.length]; x_source.data['category'] = [cb_obj.value]; x_source.change.emit(); source.data['x'] = new_x; source.data['y'] = new_y; source.change.emit(); fig.x_range.factors = []; fig.x_range.factors = final_x; if (new_y && Array.isArray(new_y) && new_y.length) { // range init and end cannot have same value var range_end = Math.max.apply(Math, new_y); if (range_end == 0 || range_end == -Infinity) { range_end = 1; } range_slider.value = [0, Math.max.apply(Math, final_y)]; range_slider.end = range_end; fig.y_range.end = Math.max.apply(Math, final_y); fig.change.emit(); } """) x_axis.js_on_change('value', x_axis_callback) # variable to group data y_axis_callback = CustomJS(args=dict( source=source, x_source=x_count_source, o_data=barchart_data, x_axis_map=x_axis_map, x_axis=x_axis, y_axis_map=y_axis_map, range_slider=range_slider, sort_order=sort_order, checkbox=checkbox, checkbox_limit_results=checkbox_limit_results, fig=p, title=p.title), code=SORT_FUNCTION + JS_NUM_RESULTS + """ title.text = x_axis.value.concat(" ", cb_obj.value); var data = o_data[x_axis_map[x_axis.value]]; var x = data['x']; var y = data['y']; var is_ratio = checkbox.active.length > 0; if (is_ratio) { title.text = x_axis.value.concat(" ", cb_obj.value, " per video"); } var sorted_data = sortData(data['raw'], sort_order.active, y_axis_map[cb_obj.value], is_ratio); var new_y = []; var new_x = []; var final_x = []; var final_y = []; for (var i = 0; i < x.length; i++) { new_x.push(sorted_data[i][0]); new_y.push(sorted_data[i][1]); } if (checkbox_limit_results.active.length > 0) { final_x = new_x.slice(0, num_results); final_y = new_y.slice(0, num_results); window.should_update_range = false; } else { final_x = new_x; final_y = new_y; window.should_update_range = true; } x_source.data['x_count'] = [final_x.length]; x_source.data['category'] = [x_axis.value]; x_source.change.emit(); source.data['x'] = new_x; source.data['y'] = new_y; source.change.emit(); fig.x_range.factors = []; fig.x_range.factors = final_x; if (new_y && Array.isArray(new_y) && new_y.length) { // range init and end cannot have same value var range_end = Math.max.apply(Math, new_y); if (range_end == 0 || range_end == -Infinity) { range_end = 1; } range_slider.value = [0, Math.max.apply(Math, final_y)]; range_slider.end = range_end; fig.y_range.end = Math.max.apply(Math, final_y); } """) y_axis.js_on_change('value', y_axis_callback) # sort order control sort_order_callback = CustomJS(args=dict( source=source, x_source=x_count_source, o_data=barchart_data, x_axis_map=x_axis_map, x_axis=x_axis, y_axis_map=y_axis_map, y_axis=y_axis, range_slider=range_slider, checkbox=checkbox, checkbox_limit_results=checkbox_limit_results, fig=p), code=SORT_FUNCTION + JS_NUM_RESULTS + """ var data = o_data[x_axis_map[x_axis.value]]; var x = data['x']; var y = data['y']; // Sort data var is_ratio = checkbox.active.length > 0; var sorted_data = sortData(data['raw'], cb_obj.active, y_axis_map[y_axis.value], is_ratio); var new_y = []; var new_x = []; var final_x = []; var final_y = []; // push data if it lies inside range for (var i = 0; i < x.length; i++) { if (checkbox_limit_results.active.length <= 0) { if (sorted_data[i][1] >= cb_obj.value[0] && sorted_data[i][1] <= cb_obj.value[1]) { new_x.push(sorted_data[i][0]); new_y.push(sorted_data[i][1]); } } else { new_x.push(sorted_data[i][0]); new_y.push(sorted_data[i][1]); } } if (checkbox_limit_results.active.length > 0) { final_x = new_x.slice(0, 50); final_y = new_y.slice(0, 50); window.should_update_range = false; } else { final_x = new_x; final_y = new_y; window.should_update_range = true; } x_source.data['x_count'] = [final_x.length]; x_source.data['category'] = [x_axis.value]; x_source.change.emit(); source.data['x'] = new_x; source.data['y'] = new_y; source.change.emit(); fig.x_range.factors = []; fig.x_range.factors = final_x; if (new_y && Array.isArray(new_y) && new_y.length) { // range init and end cannot have same value var range_end = Math.max.apply(Math, new_y); if (range_end == 0 || range_end == -Infinity) { range_end = 1; } range_slider.value = [0, Math.max.apply(Math, final_y)]; range_slider.end = range_end; fig.y_range.end = Math.max.apply(Math, final_y); fig.change.emit(); } """) sort_order.js_on_change('active', sort_order_callback) # Define layout inputs = column(*controls, width=320, height=1000) inputs.sizing_mode = "fixed" l = layout([ [desc], [inputs, p], ], sizing_mode="scale_both") return l
def make_explorer(blocks_path: str): ''' Loads a blocks path and creates an explorer for viewing blocks based on certain criteria ''' blocks = gpd.read_file(blocks_path).to_crs("EPSG:3857") p_width = 1200 p_height = 600 blocks['x'] = blocks['geometry'].apply( lambda p: get_polygon_coords(p, 'x')) blocks['y'] = blocks['geometry'].apply( lambda p: get_polygon_coords(p, 'y')) blocks['color'] = ['red'] * blocks.shape[0] blocks_df = blocks.drop(columns=['geometry']) blocks_source = ColumnDataSource(data=blocks_df) TOOLTIPS = [('OpenArea', '@max_dist'), ('BldgDensity', '@bldg_density'), ('BlockID', '@id')] p = figure(background_fill_color="lightgrey", tooltips=TOOLTIPS, plot_width=p_width, plot_height=p_height, match_aspect=True) p_map = figure(background_fill_color="lightgrey", tooltips=TOOLTIPS, plot_width=p_width, plot_height=p_height, x_range=p.x_range, y_range=p.y_range) # p = figure(background_fill_color="lightgrey", tooltips=TOOLTIPS, plot_width=p_width, plot_height=p_height, # x_axis_type="mercator", y_axis_type="mercator", match_aspect=True) # p_map = figure(background_fill_color="lightgrey", tooltips=TOOLTIPS, plot_width=p_width, plot_height=p_height, # x_axis_type="mercator", y_axis_type="mercator", x_range=p.x_range, y_range=p.y_range) blocks_source = ColumnDataSource(data=blocks_df) # Add patch renderer to figure. tile_provider = get_provider(Vendors.ESRI_IMAGERY) tile_provider2 = get_provider(Vendors.ESRI_IMAGERY) p.add_tile(tile_provider) p.patches('x', 'y', source=blocks_source, fill_color='color', line_color='black', line_width=.5, fill_alpha=0.5) p_map.add_tile(tile_provider2) density_slider = RangeSlider(start=0.0, end=1.0, value=(0.0, 1.0), step=.01, title="Selected density") b_min = blocks['max_dist'].min() b_max = blocks['max_dist'].max() area_slider = RangeSlider(start=b_min, end=b_max, value=(b_min, b_max), title='Selected open area') javascript_string = """ var data = source.data; var color = data['color']; var v_min = cb_obj.value[0]; var v_max = cb_obj.value[1]; var col_data = data[col]; var v_min_other = other_slider.value[0]; var v_max_other = other_slider.value[1]; var other_col_data = data[other_col]; var l = color.length; var cur_col_data = 0.0; var col_bool = true; var other_cur_col_data = 0.0; var other_col_bool = true; for (var i = 0; i < l; i++) { cur_col_data = col_data[i]; other_cur_col_data = other_col_data[i]; col_bool = cur_col_data >= v_min && cur_col_data <= v_max; other_col_bool = other_cur_col_data >= v_min_other && other_cur_col_data <= v_max_other; if (col_bool && other_col_bool) { color[i] = 'red'; } else { color[i] = 'blue'; } } source.change.emit(); """ density_callback = CustomJS(args=dict(source=blocks_source, col='bldg_density', other_slider=area_slider, other_col='max_dist'), code=javascript_string) area_callback = CustomJS(args=dict(source=blocks_source, col='max_dist', other_slider=density_slider, other_col='bldg_density'), code=javascript_string) density_slider.js_on_change('value', density_callback) area_slider.js_on_change('value', area_callback) layout = column(density_slider, area_slider, p, p_map) return layout, blocks