Source code for straxen.bokeh_utils

import bokeh.plotting as bklt
from bokeh.embed import file_html
from bokeh.resources import CDN
import numpy as np
import strax

export, __all__ = strax.exporter()


[docs]@export def bokeh_to_wiki(fig, outputfile=None): """Function which converts bokeh HTML code to a wiki readable code. :param fig: Figure to be conerted :param outputfile: String of absolute file path. If specified output is writen to the file. Else output is print to the notebook and can be simply copied into the wiki. """ # convert plot to wiki format: html = file_html(fig, CDN) html = "\n".join((["<html>"] + html.split("\n")[2:])) if outputfile: with open(outputfile, mode="w") as file: file.write(html) else: print(html)
def get_peaks_source(peaks, relative_start=0, time_scaler=1, keep_amplitude_per_sample=True): """Computes bokeh.plotting.ColumnDataSource for given peaks. :param peaks: Peaks to be plotted. :param relative_start: t0 from which on the peaks should be plotted. :param time_scaler: Factor to rescale the time from ns to other scale. E.g. =1000 scales to µs. :param keep_amplitude_per_sample: Boolean if true amplitude of the plotted peaks is in pe/sample. False pe/ns. :return: bokeh.plotting.ColumnDataSource instance which can be used to plot peaks. """ if not (np.all(peaks["type"] == peaks[0]["type"])): raise ValueError("All peaks must be of the same type (S1, S2 or Unknown)!") x_p = [] y_p = [] for ind, p in enumerate(peaks): x, y = _patches_x_y(p, keep_amplitude_per_sample=keep_amplitude_per_sample) x -= relative_start # relative to first peak x = x / time_scaler x_p.append(x) y_p.append(y) if peaks[0]["type"] == 2: scaler = 10**-3 else: scaler = 1 source = bklt.ColumnDataSource( data={ "xs": x_p, # Coordinates for peak patches "ys": y_p, "x": peaks["x"], # XY-Pos in PMZ Hitpattern "y": peaks["y"], "dt": peaks["dt"], "time": peaks["time"], "center_time": peaks["center_time"], "endtime": strax.endtime(peaks), "width_50": peaks["range_50p_area"] * scaler, "width_90": peaks["range_90p_area"] * scaler, "rise": peaks["rise_time"] * scaler, "rel_center_time": (peaks["center_time"] - peaks["time"]) * scaler, "area": peaks["area"], "aft": peaks["area_fraction_top"], "nhits": peaks["n_hits"], } ) return source def _patches_x_y(peak, keep_amplitude_per_sample=False): """Creates x,y coordinates needed to draw peaks via bokeh.models.patches. :param peak: Peak for which we need the x/y samples :param keep_amplitude_per_sample: If y-data should be in units of "per sample" or "per ns". :return: Tuple of x, y """ if keep_amplitude_per_sample: dt_a = 1 else: dt_a = peak["dt"] x = np.arange(peak["length"]) xx = np.zeros(2 * len(x), dtype=x.dtype) mx = 0.5 * (x[1::] + x[:-1]) xx[1:-1:2] = mx xx[2::2] = mx xx[0] = 1.5 * x[0] - 0.5 * x[1] xx[-1] = 1.5 * x[-1] - 0.5 * x[-2] xx = np.array(xx) * peak["dt"] + peak["time"] y = peak["data"][: peak["length"]] yy = np.zeros(2 * len(x)) yy[0::2] = y yy[1::2] = y yy = np.array(yy) / dt_a # baseline since we'll fill underneath xx = np.concatenate([[xx[0]], xx, [xx[-1]]]) yy = np.concatenate([[0], yy, [0]]) return xx, yy def peak_tool_tip(peak_type): """Default mouseover tooltip for peaks. :param peak_type: If 2, all time variables are in µs else in ns. :return: dictionary of tooltips. Can be converted to a list for bokeh.models.HoverTool. """ # Add static time parameters: tool_tip = { "time_static": ("time [ns]", "@time"), "center_time": ("center_time [ns]", "@center_time"), "endtime": ("endtime [ns]", "@endtime"), } # Now ns/µs parameters for S1 and S2 tool_tip["dt"] = ("dt [ns/sample]", "@dt") tool_tip["time_dynamic"] = ("time [ns]", "$x") tool_tip["rel_center_time"] = ("center time [ns]", "@rel_center_time") tool_tip["range_50p_width"] = ("50% width [ns]", "@width_50") tool_tip["range_90p_width"] = ("90% width [ns]", "@width_90") tool_tip["rise_time"] = ("rise time [ns]", "@rise") # Add non-time parameters (results in an ordered tooltip) tool_tip["amplitude"] = ("Amplitude [pe/ns]", "$y") tool_tip["area"] = ("area [pe]", "@area") tool_tip["aft"] = ("AFT", "@aft") tool_tip["nhits"] = ("nhits", "@nhits") if peak_type == 2: for k, i in tool_tip.items(): if k not in ["time_static", "center_time", "endtime"]: tool_tip[k] = (i[0].replace("[ns]", "[µs]"), i[1]) return tool_tip def default_fig(width=400, height=400, title="", **kwargs): """Helper function which returns a bokeh.plotting.figure instance with sizing_mode set to 'scale_both' and an aspect ratio set according to the specified width and height. :param width: Plot width in pixels :param height: PLot height in pixels. :param title: Title of the plot. Also allows for additional kwargs accepted by bokeh.plotting. :return: bokeh.plotting.figure instance. """ fig = bklt.figure( width=width, height=height, sizing_mode="scale_width", title=title, tools="pan,box_zoom,reset,save", **kwargs, ) return fig