def value_slider(source, plots): val_min = source.data["xmin"].min() val_max = source.data["xmax"].max() if "ci_lower" in source.column_names: val_min = min(val_min, source.data["ci_lower"].min()) if "ci_upper" in source.column_names: val_max = max(val_max, source.data["ci_upper"].max()) x_range = val_max - val_min value_column_slider = RangeSlider( start=val_min - 0.02 * x_range, end=val_max + 0.02 * x_range, value=(val_min, val_max), step=x_range / 500, title="Value", name="value_slider", ) code = """ var lower_end = cb_obj.value[0] var upper_end = cb_obj.value[1] for (var i = 0; i < plots.length; ++ i){ plots[i].x_range.start = lower_end; plots[i].x_range.end = upper_end; } """ callback = CustomJS(args={"plots": plots}, code=code) value_column_slider.js_on_change("value", callback) return value_column_slider
def make_plot1(df): data = ColumnDataSource( data={ "x": df["Saniye"], "y": df["ECG"], "z": df["AFIB_STATUS"], "r": df[df["Peaks"] == "R"], "q": df[df["Peaks"] == "Q"], "s": df[df["Peaks"] == "S"], "p": df[df["Peaks"] == "QRS_complex_on"], "t": df[df["Peaks"] == "QRS_complex_off"] }) hover_tool1 = HoverTool(tooltips=[("ECG Value", "@y"), ("AFIB Status", "@z"), ("Second", "@x")]) plot1 = figure(tools=[hover_tool1], x_axis_label="Second", y_axis_label="ECG Value", title="Basic ECG Analysis", plot_width=1500, plot_height=500) plot1.xaxis.ticker = SingleIntervalTicker(interval=0.04) plot1.xgrid.grid_line_color = "LightPink" plot1.yaxis.ticker = SingleIntervalTicker(interval=0.04) plot1.ygrid.grid_line_color = "LightPink" plot1.line(x="x", y="y", source=data) for data, name, color in zip([ data.data["r"], data.data["q"], data.data["s"], data.data["p"], data.data["t"] ], ["R", "Q", "S", "QRS_complex_on", "QRS_complex_off"], ["red", "green", "gray", "cyan", "black"]): #df = pd.DataFrame(data) plot1.circle(data["Saniye"], data["ECG"], color=color, alpha=0.8, muted_color=color, muted_alpha=0.1, legend_label=name, size=8) plot1.legend.location = "top_right" plot1.legend.click_policy = "mute" callback2 = CustomJS(args=dict(plot1=plot1), code=""" var a = cb_obj.value; plot1.x_range.start = a[0]; plot1.x_range.end = a[1]; plot1.change.emit(); cb_obj.title = "Interval is " + a[0] + " Second to " + a[1] + " Second" """) slider_widget = RangeSlider(start=df["Saniye"].min(), end=df["Saniye"].max(), step=1, value=(df["Saniye"].min(), df["Saniye"].max()), title="Select Second Interval") slider_widget.js_on_change("value_throttled", callback2) return plot1, slider_widget
def _subgroup_slider(source, subgroup_col): sorted_uniques = np.array(sorted(set(source.data[subgroup_col]))) min_val, max_val = sorted_uniques[0], sorted_uniques[-1] min_dist_btw_vals = (sorted_uniques[1:] - sorted_uniques[:-1]).min() slider = RangeSlider( start=min_val - 0.05 * min_val, end=max_val + 0.05 * max_val, value=(min_val, max_val), step=min_dist_btw_vals, title=subgroup_col.title(), name="subgroup_widget", ) slider.js_on_change( "value", CustomJS(code="source.change.emit();", args={"source": source})) return slider
def _get_xaxis_slider(self, plot_obj): """ Creates an "x-axis slider" that allows the user to interactively change the boundaries of the axes :param plot_obj: Bokeh plot object :return: x-axis slider """ callback = CustomJS(args={'plot': plot_obj}, code=""" var a = cb_obj.value; plot.x_range.start = a[0]; plot.x_range.end = a[1]; """) slider = RangeSlider(start=0, end=50, value=(self.df['x'].min(), self.df['x'].max()), step=1, title="x-axis range slider", width=200) slider.js_on_change('value', callback) return slider
def make_plot2(df): TOOLS = "tap" hover_tool = HoverTool(tooltips=[("Distance", "@dist")]) plot1 = figure(x_axis_label="Second", y_axis_label="ECG Value", title="Basic AFIB Analysis", plot_width=1500, plot_height=500, tools=[TOOLS, hover_tool]) plot1.xaxis.ticker = SingleIntervalTicker(interval=0.04) plot1.xgrid.grid_line_color = "LightPink" plot1.yaxis.ticker = SingleIntervalTicker(interval=0.04) plot1.ygrid.grid_line_color = "LightPink" data3 = ColumnDataSource(data={ "true": df[df["AFIB_STATUS"] == 1], "false": df[df["AFIB_STATUS"] == 0] }) for data, name, color in zip([data3.data["true"], data3.data["false"]], ["Yes", "No"], ["red", "blue"]): plot1.line(data["Saniye"], data["ECG"], color=color, alpha=1, muted_color=color, muted_alpha=0.1, legend_label=name) plot1.legend.location = "top_left" plot1.legend.click_policy = "mute" data = ColumnDataSource( data={ "r": df[df["Peaks"] == "R"], "q": df[df["Peaks"] == "Q"], "s": df[df["Peaks"] == "S"], "p": df[df["Peaks"] == "QRS_complex_on"], "t": df[df["Peaks"] == "QRS_complex_off"], }) for data, name, color in zip([ data.data["r"], data.data["q"], data.data["s"], data.data["p"], data.data["t"] ], ["R", "Q", "S", "QRS_complex_on", "QRS_complex_off"], ["red", "green", "gray", "cyan", "black"]): #df = pd.DataFrame(data) plot1.circle(data["Saniye"], data["ECG"], color=color, alpha=0.8, muted_color=color, muted_alpha=0.1, legend_label=name, size=4) plot1.legend.location = "top_right" plot1.legend.click_policy = "mute" source = ColumnDataSource(data=dict(x=[], y=[], dist=[])) plot1.line(x="x", y="y", source=source) plot1.circle(source=source, x='x', y='y', size=8) callback2 = CustomJS(args=dict(plot1=plot1), code=""" var a = cb_obj.value; plot1.x_range.start = a[0]; plot1.x_range.end = a[1]; plot1.change.emit(); cb_obj.title = "Interval is " + a[0] + " Second to " + a[1] + " Second" """) slider_widget = RangeSlider(start=df["Saniye"].min(), end=df["Saniye"].max(), step=1, value=(df["Saniye"].min(), df["Saniye"].max()), title="Select Second Interval") slider_widget.js_on_change("value", callback2) callback = CustomJS(args=dict(source=source, plot1=plot1, slider_widget=slider_widget), code=""" // the event that triggered the callback is cb_obj: // The event type determines the relevant attributes var data = source.data; var x = data['x'] var y = data['y'] var dist = data['dist'] if(x.length < 1) { x.push(cb_obj.x) y.push(cb_obj.y) dist.push(cb_obj.x - 0) } else if(x.length == 1) { x.push(cb_obj.x) y.push(cb_obj.y) dist.push(cb_obj.x - x[0]) } else { source.data["x"] = [] source.data["y"] = [] } source.change.emit(); """) plot1.js_on_event('tap', callback) callback3 = CustomJS(args=dict(plot1=plot1, slider_widget=slider_widget), code=""" var a = slider_widget.value; plot1.x_range.start = a[0]; plot1.x_range.end = a[1]; plot1.change.emit(); """) plot1.js_on_event(MouseMove, callback3) return plot1, slider_widget
yaxis.axis_line_width = 1 yaxis.axis_label_text_font_style = "italic" p.add_layout(yaxis, 'left') x_slider = RangeSlider(start=500, end=1800, value=(500,1800), step=.1, title="Wavelength(nm)",width=250 ) y_slider = RangeSlider(start=-5000, end=5000, value=(-5000,5000), step=.1, title="polarizability (a.u.)",width=250 ) callback = CustomJS(args=dict(p=p, x_slide=x_slider, y_slide=y_slider), code=""" p.x_range.start = x_slide.value[0]; p.x_range.end = x_slide.value[1]; p.y_range.start = y_slide.value[0]; p.y_range.end = y_slide.value[1]; """) x_slider.js_on_change('value', callback) y_slider.js_on_change('value', callback) # p.scatter(source = source, x= 'wavelength', y= 'polarizability' , marker="circle", legend='Cs6s',size=2,alpha=0.9,color="green" ) # p.scatter(source = source2, x= 'wavelength', y= 'polarizability' , marker="circle", legend='7p1',size=2,alpha=0.9,color= "navy", line_join='bevel', line_cap='round' ) x_data,y_data = data_function(df) for x,y in zip(x_data,y_data): p.line(x= x, y=y ,color="#ff005b", legend_label='CS6s') x_data,y_data = data_function(df2) for x,y in zip(x_data,y_data): p.line( x =x, y=y ,color="navy", legend_label='7p1' ) source = ColumnDataSource(data=dict(x=[1194.3, 1758.7], y=[0.115, 0.67], names=['x=1194.3, y=0.115','x=1758.7, y=0.67']))
mass_finder_exact_mass_text = Div(text= "Enter exact Mass (Da)", width= 150, height=30 ) mass_finder_exact_mass_sele = TextInput(value=str(mass_finder_mass.value*1000), disabled=False, width=100, height=30) mass_finder_line_text = Div(text= "Show mz prediction", width= 150, height=30 ) mass_finder_line_sele = Toggle(label='off', active=False, width=100, height=30, callback=toggle_cb) mass_finder_cb =CustomJS(args=dict(mass_finder_line_sele=mass_finder_line_sele, raw_mz=raw_mz, mass_finder_data=mass_finder_data, mass_finder_exact_mass_sele=mass_finder_exact_mass_sele, mass_finder_mass=mass_finder_mass, mass_finder_range_slider=mass_finder_range_slider, mfl=mfl), code=open(os.path.join(os.getcwd(), 'JS_Functions', "mass_finder_cb.js")).read()) mass_finder_exact_cb =CustomJS(args=dict(mass_finder_line_sele=mass_finder_line_sele, mass_finder_exact_mass_sele=mass_finder_exact_mass_sele, mass_finder_mass=mass_finder_mass), code=open(os.path.join(os.getcwd(), 'JS_Functions', "mass_finder_exact_cb.js")).read()) mass_finder_exact_mass_sele.js_on_change('value', mass_finder_exact_cb) mass_finder_column=Column(mass_finder_header,mass_finder_mass, mass_finder_range_slider, Row(mass_finder_exact_mass_text,mass_finder_exact_mass_sele), Row(mass_finder_line_text, mass_finder_line_sele), visible=False) mass_finder.js_link('active', mass_finder_column, 'visible') mass_finder_line_sele.js_link('active', mfl, 'visible') mass_finder_mass.js_on_change('value', mass_finder_cb) mass_finder_line_sele.js_on_change('active', mass_finder_cb) mass_finder_range_slider.js_on_change('value',mass_finder_cb) ### DATA PROCESSING ### cropping = Div(text= " Range mz:", width= 150, height=30 ) # crop_max = Div(text= " ", width= 150, height=30 ) gau_name = Div(text= " Gaussian Smoothing:", width= 150, height=30 ) n_smooth_name = Div(text= " Repeats of Smoothing:", width= 150, height=30 ) # bin_name = Div(text= " Bin Every:", width= 150, height=30 ) int_name = Div(text= " Intensity Threshold (%)", width= 150, height=30 ) sub_name = Select(options=['Substract Minimum', 'Substract Line', 'Substract Curved'], name='sub_mode', value='Substract Minimum', width= 150, height=30 ) # add_name = Div(text= " Adduct Mass (Da)", width= 150, height=30 ) # dat_name = Div(text= " Data Reduction (%)", width= 150, height=30 ) #pro_name = Div(text= " bla", width= 150, height=30 ) dt_name = Div(text= " <h2>Data Processing</h2>", height=45 ) dtp_update=CustomJS(args=dict(DataProcessingParameters=DataProcessingParameters), code=open(os.path.join(os.getcwd(), 'JS_Functions', "DataProcessingParameters_update_cb.js")).read())
class QCWorkTool: """The almighty QC-tool. Used for quality control of high resolution CTD-data (oceanography). """ # TODO: Well, this is great.. However, we need to simplify and # divide this class into widgets instead.. to be continued.. # - delete color fields? use flag fields instead? def __init__( self, dataframe, datasets=None, settings=None, tabs=None, ctdpy_session=None, export_folder=False, output_filename="CTD_QC_VIZ.html", output_as_notebook=False, output_as_standalone=False, ): """Initiate.""" self.seconds = ColumnDataSource( data=dict(tap_time=[None], reset_time=[None])) self.ctd_session = ctdpy_session self.multi_sensors = settings.multi_sensors self.combo_plots = settings.combo_plots self.map = None self.plot_keys = settings.plot_keys self.datasets = datasets self.key_ds_mapper = self.get_mapper_key_to_ds( settings.file_name_elements) self.parameters = settings.data_parameters_with_units self.plot_parameters_mapping = settings.plot_parameters_mapping self.color_fields = settings.q_colors self.size_fields = settings.scatter_size self.qflag_fields = settings.q_parameters self.auto_qflag_fields = settings.q0_plot_keys self.tabs = tabs self.output_as_notebook = output_as_notebook self.as_standalone = output_as_standalone if self.output_as_notebook: raise NotImplementedError( 'Not yet applicable to work with notebooks!') elif self.as_standalone: output_file(output_filename) self.tile_provider = get_provider(Vendors.CARTODBPOSITRON_RETINA) self.figures = {} xrange_callbacks = {} y_range_setting = None for p in self.plot_parameters_mapping: if p == 'y' or 'q' in p or p[0].isupper() or \ p.startswith('color') or p.startswith('size'): continue param = self.plot_parameters_mapping.get(p) self.figures[p] = figure( tools="pan,reset,wheel_zoom,lasso_select,save", active_drag="lasso_select", title="", height=400, width=400, y_range=y_range_setting, tooltips=[(param, "@{}".format(p)), ("Pressure [dbar]", "@y"), ("Auto-QC", "@{}_q0".format(p))]) self.figures[p].title.align = 'center' self.figures[p].xaxis.axis_label = param self.figures[p].xaxis.axis_label_text_font_style = 'bold' self.figures[p].ygrid.band_fill_alpha = 0.05 self.figures[p].ygrid.band_fill_color = "black" self.figures[p].toolbar.active_scroll = self.figures[p].select_one( WheelZoomTool) if not y_range_setting or (self.multi_sensors and p == 'x4'): self.figures[p].yaxis.axis_label = 'Pressure (dbar)' self.figures[p].yaxis.axis_label_text_font_style = 'bold' y_range_setting = y_range_setting or self.figures[p].y_range xrange_callbacks[p] = cbs.x_range_callback( x_range_obj=self.figures[p].x_range, seconds=self.seconds) self.figures[p].x_range.js_on_change('start', xrange_callbacks[p]) if self.combo_plots and self.multi_sensors: # TODO check the p2 (_) variable.. Should it not be used? for name, p1, _ in zip(('COMBO_TEMP', 'COMBO_SALT', 'COMBO_DOXY'), ('x1', 'x2', 'x3'), ('x4', 'x5', 'x6')): param = self.plot_parameters_mapping.get(p1) self.figures[name] = figure( tools="pan,reset,wheel_zoom,lasso_select,save", active_drag="lasso_select", title="", height=400, width=400, y_range=y_range_setting, ) self.figures[name].title.align = 'center' self.figures[name].xaxis.axis_label = param self.figures[name].xaxis.axis_label_text_font_style = 'bold' self.figures[name].ygrid.band_fill_alpha = 0.05 self.figures[name].ygrid.band_fill_color = "black" self.figures[name].toolbar.active_scroll = \ self.figures[name].select_one(WheelZoomTool) if p1 == 'x1': self.figures[name].yaxis.axis_label = 'Pressure (dbar)' self.figures[ name].yaxis.axis_label_text_font_style = 'bold' cbs.add_hlinked_crosshairs(*(fig_obj for i, fig_obj in self.figures.items())) self.ts = figure( title="CTD TS Diagram", tools=[PanTool(), WheelZoomTool(), ResetTool(), SaveTool()], tooltips=[("Serie", "@key")], height=400, width=400, x_range=(2, 36), y_range=(-2, 20)) self.ts.title.align = 'center' self._setup_position_source(dataframe) self.data_source = setup_data_source( dataframe, pmap=self.plot_parameters_mapping, key_list=np.unique(self.position_source.data['KEY']), parameter_list=self.color_fields + self.size_fields + self.plot_keys + self.auto_qflag_fields + ['COMNT_SAMP'] # noqa: E501 ) self.ts_source = TS_Source() self.ts_source.setup_source(dataframe, self.plot_parameters_mapping) self.ts_plot_source = ColumnDataSource( data=dict(x=[], y=[], color=[], key=[])) self._setup_month_selector() self._setup_comnt_inputs() self._setup_selection_widgets() self._setup_multiflag_widget() self._setup_flag_widgets() self._setup_reset_callback(**xrange_callbacks) self._setup_datasource_callbacks() self._setup_download_button(settings.user_download_directory, export_folder=export_folder, icons=settings.icons) self._setup_get_file_button() self._setup_serie_table() self._setup_info_block(export_folder) self._setup_map() self.ts_axis_ranges = { 't_min': 0, 't_max': 25, 's_min': 2, 's_max': 36 } def get_mapper_key_to_ds(self, file_name_elements): """Return mapper between dataset filename and key. Key = serie key (eg. '20191009_77SE_0005') """ selected_keys = ('SDATE', 'SHIPC', 'SERNO') mapper = {} for ds_name in self.datasets: name_map = { k: v for k, v in zip(file_name_elements, ds_name.replace('.txt', '').split('_')) } mapper['_'.join( (name_map.get(k) for k in selected_keys))] = ds_name return mapper @staticmethod def _get_monthly_keys(position_df): """Return mapper of monthly keys.""" # FIXME Is this really necessary ? dictionary = {'All': position_df['KEY'].to_list()} for month in [str(m).zfill(2) for m in range(1, 13)]: boolean = position_df['MONTH'] == month dictionary[month] = position_df.loc[boolean, 'KEY'].to_list() return dictionary def _setup_position_source(self, df): """Set position data sources. self.position_source: Contains all position information. self.position_plot_source: Contains only the selected information. """ position_df = df[[ 'STATION', 'LATITUDE_DD', 'LONGITUDE_DD', 'KEY', 'MONTH' ]].drop_duplicates(keep='first').reset_index(drop=True) for pos_row in position_df.itertuples(): try: float(pos_row.LATITUDE_DD) except Exception: print('not valid', pos_row.KEY) xs, ys = convert_projection( position_df['LATITUDE_DD'].astype(float).values, position_df['LONGITUDE_DD'].astype(float).values) position_df['LONGI'] = xs position_df['LATIT'] = ys comnts = [] for key in position_df['KEY']: ds_meta = self.datasets[self.key_ds_mapper.get(key)]['metadata'] cv_boolean = ds_meta.str.startswith('//METADATA;COMNT_VISIT;') value = ds_meta[cv_boolean].values[0].replace( '//METADATA;COMNT_VISIT;', '') comnts.append(value) position_df['COMNT_VISIT'] = comnts self.monthly_keys = self._get_monthly_keys(position_df) self.position_source = ColumnDataSource(data=position_df) self.position_plot_source = ColumnDataSource(data=position_df) def _setup_month_selector(self): """Set month selection widget.""" callback = cbs.month_selection_callback( position_source=self.position_source, position_plot_source=self.position_plot_source) self.month_selector = Select( title="Select month", value='All', options=['All'] + pd.date_range( start='2020-01', freq='M', periods=12).month_name().to_list()) self.month_selector.js_on_change('value', callback) # self.month_selector.title.text_align = 'center' callback.args["month"] = self.month_selector def _setup_download_button(self, savepath, export_folder=None, icons=None): """Set download widget.""" self.download_button = cbs.get_download_widget( self.datasets, self.position_plot_source, self.ctd_session, self.key_ds_mapper, savepath, export_folder=export_folder, icons=icons) def _setup_get_file_button(self): """Set file button widget.""" self.file_button = cbs.get_file_widget() def _setup_serie_table(self): """Create data table associated to the position_plot_source object.""" columns = [ TableColumn(field="STATION", title="Station"), TableColumn(field="KEY", title="Key"), ] self.selected_series = DataTable(source=self.position_plot_source, columns=columns, width=300, height=322) def _setup_flag_widgets(self): """Set flag widgets.""" self.flag_widgets = {} for fig_key in self.figures.keys(): if fig_key.startswith('COMBO'): continue parameter = self.plot_parameters_mapping.get(fig_key).split()[0] self.flag_widgets[fig_key] = cbs.get_flag_buttons_widget( self.position_plot_source, self.data_source['main_source'], self.datasets, key_mapper=self.key_ds_mapper, figure_objs=self.figures, flag_keys=self.plot_parameters_mapping[parameter].get( 'q_flags'), color_keys=self.plot_parameters_mapping[parameter].get( 'color_keys'), size_keys=self.plot_parameters_mapping[parameter].get( 'size_keys'), select_button=self.select_all_button) def _setup_comnt_inputs(self): """Set comment input widgets.""" self.comnt_visit = TextInput(value="", title="COMNT_VISIT:") self.comnt_visit_button = cbs.comnt_visit_change_button( datasets=self.datasets, position_source=self.position_plot_source, key_mapper=self.key_ds_mapper, comnt_obj=self.comnt_visit, ) self.comnt_samp = TextInput(value="", title="COMNT_SAMP:") self.comnt_samp_button = cbs.comnt_samp_change_button( datasets=self.datasets, position_source=self.position_plot_source, key_mapper=self.key_ds_mapper, data_source=self.data_source['main_source'], comnt_obj=self.comnt_samp, ) self.comnt_samp_selector = cbs.comnt_samp_selection( data_source=self.data_source['main_source'], comnt_obj=self.comnt_samp, ) def _setup_info_block(self, export_folder): """Set text block objects.""" self.info_block = get_info_block() self.text_export = get_export_info_block(export_folder) self.text_index_selection = standard_block_header( text='Profile index selection', height=30) self.text_multi_serie_flagging = standard_block_header( text='Multi serie parameter flagging', height=30) self.text_meta = standard_block_header(text='Change comments', height=30) self.text_import = standard_block_header(text='Not yet applicable', height=30) def _setup_selection_widgets(self): """Set selection widgets.""" self.select_all_button = cbs.select_button( data_source=self.data_source['main_source']) self.deselect_all_button = cbs.deselect_button( data_source=self.data_source['main_source']) self.pressure_slider = RangeSlider(start=0, end=100, value=(0, 100), step=0.5, title="Select with pressure range", width=300) callback = cbs.range_selection_callback( data_source=self.data_source['main_source']) self.pressure_slider.js_on_change('value', callback) def _setup_multiflag_widget(self): """Set multiflag widgets.""" def sorted_params(plist): para_list = [] i = 0 while i < len(plist): if '2' in plist[i]: para_list.extend([plist[i + 1], plist[i]]) i += 2 else: para_list.append(plist[i]) i += 1 return para_list parameter_list = [] for p in self.figures.keys(): if not p.startswith('COMBO'): parameter_list.append( self.plot_parameters_mapping.get(p).split()[0]) parameter_list = sorted_params(sorted(parameter_list)) self.parameter_selector = Select(title="Select parameter", value=parameter_list[0], options=parameter_list) self.multi_flag_widget = cbs.get_multi_serie_flag_widget( self.position_plot_source, self.data_source, self.datasets, key_mapper=self.key_ds_mapper, parameter_selector=self.parameter_selector, parameter_mapping=self.plot_parameters_mapping, figure_objs=None) def _setup_reset_callback(self, **kwargs): """Set linked figure reset functionality. Autoreset all figures. """ for p in self.figures.keys(): xr_cbs = (xr_cb for xr_cb in kwargs.values()) self.figures[p].js_on_event('reset', cbs.reset_callback(self.seconds), *xr_cbs) def _setup_datasource_callbacks(self): """Set multiflag widgets.""" set_button_type_callback = cbs.change_button_type_callback( button=self.select_all_button, btype='default') self.data_source['main_source'].selected.js_on_change( 'indices', set_button_type_callback) def _setup_map(self): """Initiate the map object including all the connected interactivity.""" pan = PanTool() save = SaveTool() tap = TapTool() lasso = LassoSelectTool() reset = ResetTool() wheel = WheelZoomTool() tooltips = HoverTool(tooltips=[("Station", "@STATION"), ("Serie", "@KEY")]) # range bounds supplied in web mercator coordinates self.map = figure( x_range=(0, 4000000), y_range=(7100000, 9850000), x_axis_type="mercator", y_axis_type="mercator", plot_height=420, plot_width=1000, tools=[pan, wheel, tap, lasso, tooltips, reset, save]) self.map.yaxis.axis_label = ' ' # in order to aline y-axis with figure window below self.map.xgrid.grid_line_color = None self.map.ygrid.grid_line_color = None self.map.toolbar.active_scroll = self.map.select_one(WheelZoomTool) self.map.add_tile(self.tile_provider) station_data_callback = cbs.station_callback( position_source=self.position_plot_source, data_source=self.data_source, figures=self.figures, seconds=self.seconds, pmap=self.plot_parameters_mapping) tap.callback = station_data_callback # When we mark stations on the map using lasso selection, we activate the TS-diagram. lasso_callback = cbs.lasso_callback(monthly_keys=self.monthly_keys, in_data=self.ts_source, plot_data=self.ts_plot_source, x_range=self.ts.x_range, y_range=self.ts.y_range) station_data_callback_2 = cbs.station_callback( position_source=self.position_plot_source, data_source=self.data_source, figures=self.figures, seconds=self.seconds, pmap=self.plot_parameters_mapping) comnt_callback = cbs.comnt_callback( position_source=self.position_plot_source, comnt_obj=self.comnt_visit, single_select=1) comnt_samp_callback = cbs.comnt_samp_callback( position_source=self.position_plot_source, data_source=self.data_source['main_source'], comnt_obj=self.comnt_samp, comnt_selector=self.comnt_samp_selector, single_select=1, ) update_slider_callback = cbs.range_slider_update_callback( slider=self.pressure_slider, data_source=self.data_source['main_source'], ) select_button_type_callback = cbs.change_button_type_callback( button=self.select_all_button, btype='default') lasso_callback.args["month"] = self.month_selector self.position_plot_source.selected.js_on_change( 'indices', lasso_callback, station_data_callback_2, comnt_callback, update_slider_callback, select_button_type_callback, comnt_samp_callback, ) def plot_stations(self): """Plot loaded stations on the map.""" renderer = self.map.circle('LONGI', 'LATIT', source=self.position_plot_source, color="#5BC798", line_color="aquamarine", size=10, alpha=0.7) selected_circle = Circle(fill_alpha=0.5, fill_color="#FF0202", line_color="aquamarine") nonselected_circle = Circle(fill_alpha=0.3, fill_color="#5BC798", line_color="aquamarine") renderer.selection_glyph = selected_circle renderer.nonselection_glyph = nonselected_circle def plot_data(self): """Plot default data in figures. Default data: x=[1] and y=[1]. """ combo_mapping = { 'COMBO_TEMP': ('x1', 'x4'), 'COMBO_SALT': ('x2', 'x5'), 'COMBO_DOXY': ('x3', 'x6') } nonselected_circle = Circle(fill_alpha=0.1, fill_color="#898989", line_color="lightgrey") for p, item in self.figures.items(): if p.startswith('COMBO'): p1, p2 = combo_mapping.get(p) item.line(p1, 'y', color=f"color_{p1}", line_color="navy", line_width=1, alpha=0.3, source=self.data_source['main_source']) item.circle(p1, 'y', color=f"color_{p1}", line_color="white", size=f"size_{p1}", alpha=0.5, source=self.data_source['main_source']) item.line(p2, 'y', color=f"color_{p2}", line_color="navy", line_width=1, alpha=0.3, source=self.data_source['main_source']) item.cross(p2, 'y', color=f"color_{p2}", size=f"size_{p2}", alpha=0.5, source=self.data_source['main_source']) item.y_range.flipped = True else: item.line(p, 'y', color=f"color_{p}", line_color="navy", line_width=1, alpha=0.3, source=self.data_source['main_source']) renderer = item.circle(p, 'y', color=f"color_{p}", line_color="white", size=f"size_{p}", alpha=0.5, source=self.data_source['main_source']) renderer.nonselection_glyph = nonselected_circle item.y_range.flipped = True # T/S - diagram self.ts.circle('x', 'y', color='color', size=3, alpha=0.8, source=self.ts_plot_source, legend_label='Sensor 1') self.ts.toolbar.active_scroll = self.ts.select_one(WheelZoomTool) self.ts.legend.location = "top_left" number_of_colors = int(self.ts_source.data[ self.plot_parameters_mapping.get('y')].max()) * 2 number_of_colors += 1 cm_map = cm.get_cmap('cool', number_of_colors) color_array = [ colors.to_hex(cm_map(c)) for c in range(number_of_colors) ] color_bar = ColorBar( color_mapper=LinearColorMapper( palette=color_array, low=0, high=self.ts_source.data[self.plot_parameters_mapping.get( 'y')].max()), location=(0, 0), ) self.ts.add_layout(color_bar, 'right') x_min, x_max, y_min, y_max = 0, 40, -10, 30 contour_data = get_contour_data(x_min, x_max, y_min, y_max) for key in contour_data.keys(): self.ts.line(contour_data[key]['salt'], contour_data[key]['temp'], line_color="grey", line_alpha=0.8, line_width=1.5) def append_qc_comment(self, meta_series): """Append commnet at the end of the metadata serie. Will be placed on the row just above the data table in each data file. """ time_stamp = get_time_as_format(now=True, fmt='%Y%m%d%H%M') meta_series[len(meta_series) + 1] = '//QC_COMNT; MANUAL QC PERFORMED BY {}; TIMESTAMP {}' \ ''.format(self.ctd_session.settings.user, time_stamp) def get_tab_layout(self): """Return bokeh tab widget.""" fig_tabs = [ Panel(child=column([Spacer(height=30, width=20), self.ts]), title="TS") ] for p, item in self.figures.items(): if (self.multi_sensors and p not in ['x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'COMBO_TEMP', 'COMBO_SALT', 'COMBO_DOXY']) \ or (not self.multi_sensors and p not in ['x1', 'x2', 'x3']): tab_layout = column([self.flag_widgets[p], item]) tab_name = self.plot_parameters_mapping.get( p).split()[0].replace('_CTD', '') pan = Panel(child=tab_layout, title=tab_name) fig_tabs.append(pan) return Tabs(tabs=fig_tabs) def get_tabs(self, **kwargs): """Return bokeh tab widget.""" tabs = [] for name, item in kwargs.items(): tab = self.get_column(item) pan = Panel(child=tab, title=name) tabs.append(pan) return Tabs(tabs=tabs) def get_column(self, item): """Return bokeh column layout.""" c_list = [] for attr in item: if type(attr) == str: c_list.append(self.__getattribute__(attr)) elif type(attr) == tuple: r = row([self.__getattribute__(a) for a in attr], sizing_mode="stretch_width") c_list.append(r) return column(c_list) def get_std_parameter_tab_layout(self): """Return list of bokeh column layouts.""" def pan_title(string): return string.split()[0].replace('_CTD', '') columns = [] if self.multi_sensors: for params in zip(('x1', 'x2', 'x3'), ('x4', 'x5', 'x6'), ('COMBO_TEMP', 'COMBO_SALT', 'COMBO_DOXY')): pans = [] for p in params: if p in self.figures: tab_cols = [] if p in self.flag_widgets: tab_cols.append(self.flag_widgets[p]) else: tab_cols.append(Spacer(width=20, height=41)) tab_cols.append(self.figures[p]) tab = column(tab_cols) pan = Panel(child=tab, title=pan_title( self.plot_parameters_mapping.get(p) or p)) pans.append(pan) columns.append(column([Tabs(tabs=pans)])) else: for p1 in ('x1', 'x2', 'x3'): columns.append( column([self.flag_widgets[p1], self.figures[p1]])) return columns def get_layout(self): """Return the complete bokeh layout.""" tabs = self.get_tab_layout() tab_kwargs = { 'Data': [ 'text_index_selection', ('select_all_button', 'deselect_all_button'), 'pressure_slider', 'text_multi_serie_flagging', 'parameter_selector', 'multi_flag_widget' ], 'Metadata': [ 'text_meta', 'comnt_visit', 'comnt_visit_button', 'comnt_samp', 'comnt_samp_selector', 'comnt_samp_button' ], 'Info': ['info_block'] } if not self.as_standalone: tab_kwargs['Import'] = ['text_import', 'file_button'] tab_kwargs['Export'] = ['text_export', 'download_button'] meta_tabs = self.get_tabs(**tab_kwargs) std_parameter_tabs = self.get_std_parameter_tab_layout() widgets_1 = column( [self.month_selector, self.spacer, self.selected_series], sizing_mode="fixed", height=400, width=200) widgets_2 = column([Spacer(height=10, width=125)], sizing_mode="fixed", height=10, width=125) widgets_3 = column([meta_tabs], sizing_mode="stretch_both", height=100, width=100) return grid([ row([self.map, widgets_1, widgets_2, widgets_3]), row([*std_parameter_tabs, column([tabs])]) ]) @property def spacer(self): """Return bokeh spacer.""" return Spacer(height=10, width=20) def return_layout(self): """Return the layout. Can be displayed in an embedded bokeh server. """ return self.get_layout() def show_plot(self): """Show layout in a html-file or in jupyter notebook.""" show(self.get_layout())
p.line('y', 'x', source=source, line_width=2) range_slider = RangeSlider(title="Data Range Slider: ", start=0, end=len(lon), value=(0, len(lon)), step=1) callback = CustomJS(args=dict(source=source, slider=range_slider, long=lon, lati=lat), code=""" var data = source.data; const start = slider.value[0]; const end = slider.value[1]; data['x'] = long.slice(start, end) data['y'] = lati.slice(start, end) source.change.emit(); """) range_slider.js_on_change('value', callback) layout = row(p, range_slider) output_file("diag_plot_bike_data.html") show(layout)
def plotIt(xName, yName, widgetList, generator, sourceMeta): ''' generate the initial plots and populate them with data ''' LINE_ARGS = dict(color="#3A8785", line_color=None) TOOLS = "reset,save,pan,wheel_zoom,box_zoom,box_select,lasso_select,hover" TS_TOOLS = "reset,save,pan,xwheel_zoom,hover" firstCycle = sourceMeta["timeRange"][0]["first"] lastCycle = sourceMeta["timeRange"][0]["last"] startCycle, x, y = generator.adapter.get_points( firstCycle, lastCycle, sourceMeta["timeRange"][0]["units"], [xName, yName], -1) source = ColumnDataSource(data=dict(x=x, y=y, index=startCycle)) scatterView = CDSView(source=source, filters=[IndexFilter(np.arange(len(x)))]) # create the scatter plot p = figure(tools=TOOLS, plot_width=700, plot_height=700, min_border=10, min_border_left=30, toolbar_location="above", title="Comparison", output_backend="webgl", x_axis_label=xName, y_axis_label=yName) xAxis = p.xaxis[0] yAxis = p.yaxis[0] p.background_fill_color = "#fafafa" p.select(BoxSelectTool).select_every_mousemove = False p.select(LassoSelectTool).select_every_mousemove = False scatter = p.scatter('x', 'y', source=source, size=3, marker='diamond', color="navy", alpha=0.85, view=scatterView) # create the horizontal histogram hedges, hhist = generator.generate_histogram( firstCycle, lastCycle, sourceMeta["timeRange"][0]["units"], xName, -1) hh = figure(toolbar_location=None, plot_width=p.plot_width, plot_height=200, x_range=p.x_range, min_border=10, min_border_left=50, y_axis_location="right", y_axis_label="count", output_backend="webgl") hh.xgrid.grid_line_color = None hh.yaxis.major_label_orientation = np.pi / 4 hh.background_fill_color = "#fafafa" horzPanelSource = ColumnDataSource( data=dict(bottom=np.zeros(len(hedges) - 1), left=hedges[:-1], right=hedges[1:], top=hhist)) hh.quad(left='left', bottom='bottom', top='top', right='right', source=horzPanelSource, color="rgb(170,186,215)", line_color="rgb(144,159,186)") horzHistSource = ColumnDataSource( data=dict(bottom=np.zeros(len(hedges) - 1), left=hedges[:-1], right=hedges[1:], top=np.zeros(len(hedges) - 1))) hh.quad(left='left', bottom='bottom', top='top', right='right', source=horzHistSource, alpha=0.5, **LINE_ARGS) # create the vertical histogram vedges, vhist = generator.generate_histogram( firstCycle, lastCycle, sourceMeta["timeRange"][0]["units"], yName, -1) pv = figure(toolbar_location=None, plot_width=200, plot_height=p.plot_height, y_range=p.y_range, min_border=10, y_axis_location="right", x_axis_label="count", output_backend="webgl") pv.ygrid.grid_line_color = None pv.xaxis.major_label_orientation = np.pi / 4 pv.background_fill_color = "#fafafa" vertPanelSource = ColumnDataSource(data=dict(top=vedges[1:], bottom=vedges[:-1], right=vhist, left=np.zeros(len(vedges)))) pv.quad(left='left', bottom='bottom', top='top', right='right', source=vertPanelSource, color="rgb(170,186,215)", line_color="rgb(144,159,186)") vertHistSource1 = ColumnDataSource( data=dict(top=vedges[1:], bottom=vedges[:-1], right=np.zeros(len(vedges) - 1), left=np.zeros(len(vedges) - 1))) # vh1 = pv.quad(left='left', bottom='bottom', top='top', right='right', source=vertHistSource1, alpha=0.5, **LINE_ARGS) rs = RangeSlider(start=firstCycle, end=lastCycle, value=(firstCycle, lastCycle), step=10, title="Visible Range", width=535, align="center", show_value=True) # compute the trend line try: xValues, yValues = generator.generate_regression_line( firstCycle, lastCycle, sourceMeta["timeRange"][0]["units"], [xName, yName], -1) regLineSource = ColumnDataSource("x", "y", data=dict(x=xValues, y=yValues)) regLine = p.line('x', 'y', source=regLineSource, color="#f44242", line_width=2.5) except LinAlgError: regLine.visible = False box = BoxAnnotation(fill_alpha=0.5, line_alpha=0.5, level='underlay', left=rs.start, right=rs.end) # time series 1 ts1 = figure(plot_width=600, plot_height=225, title=xName, tools=TS_TOOLS, active_scroll="xwheel_zoom", x_range=Range1d(firstCycle, lastCycle, bounds="auto")) ts1.line( x='index', y='x', source=source, ) ts1.add_layout(box) xTsLabel = ts1.title ts2 = figure( plot_width=600, plot_height=225, title=yName, tools=TS_TOOLS, active_scroll="xwheel_zoom", # y_range = Range1d(-1.1 * max(y), 1.1 * max(y), bounds = (-1.1 * max(y), 1.1 * max(y))), x_range=ts1.x_range) ts2.line( x='index', y='y', source=source, ) ts2.add_layout(box) yTsLabel = ts2.title # setup JS callbacks for widgets w1, w2 = widgetList rs.js_on_change( 'value', CustomJS(args=dict(rs=rs, box=box, scatter=scatter, scatterView=scatterView, ts1=ts1, ts2=ts2, source=source, p=p, w1=w1, w2=w2, xAxis=xAxis, yAxis=yAxis, horzPanelSource=horzPanelSource, horzHistSource=horzHistSource, vertPanelSource=vertPanelSource, vertHistSource1=vertHistSource1, regLineSource=regLineSource, xName=xTsLabel, yName=yTsLabel), code=''' rangeUpdateDebounceCB(rs, box, scatter, scatterView, ts1, ts2, source, p, w1, w2, xAxis,yAxis, horzPanelSource, horzHistSource, vertPanelSource, vertHistSource1, regLineSource, xName, yName); ''')) timeSeriesPanJS = CustomJS(args=dict(rs=rs, box=box, scatter=scatter, scatterView=scatterView, ts1=ts1, ts2=ts2, source=source, p=p, w1=w1, w2=w2, xAxis=xAxis, yAxis=yAxis, horzPanelSource=horzPanelSource, horzHistSource=horzHistSource, vertPanelSource=vertPanelSource, vertHistSource1=vertHistSource1, regLineSource=regLineSource, xName=xTsLabel, yName=yTsLabel), code=''' timeSeriesPanDebounceCB(rs, box, scatter, scatterView, ts1, ts2, source, p, w1, w2, xAxis, yAxis, horzPanelSource, horzHistSource, vertPanelSource, vertHistSource1, regLineSource, xName, yName);''') ts1.js_on_event(PanEnd, timeSeriesPanJS) ts1.js_on_event(Pinch, timeSeriesPanJS) ts1.js_on_event(Reset, timeSeriesPanJS) ts1.js_on_event(MouseWheel, timeSeriesPanJS) ts2.js_on_event(PanEnd, timeSeriesPanJS) ts2.js_on_event(Pinch, timeSeriesPanJS) ts2.js_on_event(Reset, timeSeriesPanJS) ts2.js_on_event(MouseWheel, timeSeriesPanJS) dropdownCallback = CustomJS(args=dict( source=source, p=p, w1=w1, w2=w2, xAxis=xAxis, yAxis=yAxis, horzPanelSource=horzPanelSource, horzHistSource=horzHistSource, vertPanelSource=vertPanelSource, vertHistSource1=vertHistSource1, regLineSource=regLineSource, xName=xTsLabel, yName=yTsLabel, rs=rs, scatterView=scatterView, ), code=''' updateAll(cb_obj, source, w1.value, w2.value, xAxis, yAxis, vertPanelSource, vertHistSource1, horzPanelSource, horzHistSource, regLineSource, xName, yName, rs, scatterView); ''') w1.js_on_change("value", dropdownCallback) w2.js_on_change("value", dropdownCallback) # w2.js_on_change("select", CustomJS(code='''console.log("asdf");''')) # global layout newLayout = layout([ row([p, pv, column([widgetList[0], widgetList[1], rs, ts1, ts2])]), [hh] ]) return newLayout
def getPlot(score,allscore,classification,counts,rest,tabledict,title,maxval): classification=sorted(classification) legendlist=[] for l in classification: legendlist.append(bokutils.makeLegendKey(l)) labellist=[] for l in counts: labellist.append(str(l)) hover = HoverTool(tooltips=""" <div style=" opacity: .8; padding: 5px; background-color: @type_color;color: @font_color;> <h1 style="margin: 0; font-size: 12px;"> @legendlist</h1> <h1 style="margin: 0; font-size: 24px;"><strong> @counts </strong></h1> <table> @subgroups{safe} </table> </div> """ ) subdict={} for i, val in enumerate(classification): subdict[i]=tabledict[val] ## Left diagram classlen=len(classification) my_palette=bokutils.getColors(classlen,True,False) colordict={} for i, val in enumerate(my_palette): colordict[i]=val source = ColumnDataSource(data=dict(classification=classification, counts=counts, legendlist=legendlist, labellist=labellist, type_color=[colordict[x] for x in colordict], font_color=[bokutils.contrasting_text_color(colordict[x]) for x in colordict], subgroups=[subdict[x] for x in subdict])) p = figure(x_range=classification, plot_height=bokutils.PLOT_HEIGHT,plot_width=bokutils.PLOT_WIDTH, tools=[hover],toolbar_location=None, title=title) p.vbar(x='classification', top='counts', width=0.9, source=source, legend="legendlist", line_color='white', fill_color=factor_cmap('classification', palette=my_palette, factors=classification)) p.xgrid.grid_line_color = None p.y_range.start = 0 p.y_range.end = maxval p.legend.orientation = "vertical" #p.legend.location = "top_right" text_source = ColumnDataSource({'year': ['%s' % 1968]}) text= Text(x=1, y=35, text='year', text_font_size='80pt', text_color='#EEEEEE' ) p.add_glyph(text_source, text) p.xaxis.major_label_orientation = 1.2 blabel = LabelSet(x='classification', y='counts', text='labellist', level='glyph', x_offset=5, y_offset=5, source=source, render_mode='canvas') p.add_layout(blabel) new_legend = p.legend[0] p.legend[0].plot = None p.add_layout(new_legend, 'right') blabel = LabelSet(x='classification', y='counts', text='labellist', level='glyph', x_offset=5, y_offset=5, source=source, render_mode='canvas') p.add_layout(blabel) slider = RangeSlider( start=bokutils.FIRST_YEAR, end=bokutils.LAST_YEAR, value=[bokutils.LAST_YEAR, bokutils.LAST_YEAR], step=1, title='Year', bar_color="#b3cccc") def slider_update(source=source, slider=slider, plot=p,window=None): year0 = slider.value[0] year1 = slider.value[1] return slider.js_on_change('value', bokutils.translatepy2js(slider_update)) wb=widgetbox(children=[slider], sizing_mode='scale_width') thisrow=Column(children=[p, wb], sizing_mode='scale_both') return thisrow
def home(): #Access the SQL database engine = create_engine('sqlite:///../tapestation.db', echo=False) #Make a plot of strongest peaks, sizes and peak molarity #Get the data peak_df = pd.read_sql('SELECT * FROM peaks', engine) #Get region data region_df = pd.read_sql('SELECT * from regions', engine) region_cols = ['ts_data_id', 'well_id', 'avg_size'] region_size_df = region_df[region_cols] #Choose only the first peak as well as from samples only first_peak_mask = (peak_df['samp_desc'] != 'Electronic Ladder') & (peak_df['peak_id'] == 1) first_peak_df = peak_df[first_peak_mask].copy() first_peak_cols = ['ts_data_id', 'well_id', 'peak_mol', 'int_area', 'size', 'cal_conc'] first_peak_df = first_peak_df[first_peak_cols] #Merging the data combined_df = pd.merge(first_peak_df, region_size_df, on=['ts_data_id', 'well_id'], how='outer') #print(combined_df) source_data = ColumnDataSource(combined_df) #Have two instances of the data so for callback purposes #Because we are essentially filtering the data #We don't want to lose the data when altering widgets, keep an 'unchanged' copy unchanged_data = ColumnDataSource(combined_df) first_peak_plot = figure() first_peak_plot.circle(x='size', y='peak_mol', source=source_data) first_peak_plot.title.text = 'Size vs Peak Molarity of Strongest Peaks' first_peak_plot.xaxis.axis_label = 'Size [bp]' first_peak_plot.yaxis.axis_label = 'Peak Molarity [pmol/l]' #Add a hover tool for the relevant information from peaks table hover_first_peak = HoverTool() hover_first_peak.tooltips=[ ('TS Data ID', '@ts_data_id'), ('Well', '@well_id'), ('Size [bp]','@size'), ('Peak Molarity [pmol/l]', '@peak_mol'), ('Calibrated Concentration [pg/mul]', '@cal_conc'), ('% Integrated Area', '@int_area') ] first_peak_plot.add_tools(hover_first_peak) #Make a plot comparing well size to average size size_plot = figure(match_aspect=True) #match_aspect keep aspect ratio the same size_plot.circle(x='size', y='avg_size', source=source_data) size_plot.title.text = 'Size of Strongest Peak vs Avg Size of Region [bp]' size_plot.xaxis.axis_label = 'Size of Strongest Peak [bp]' size_plot.yaxis.axis_label = 'Avg Size of Region [bp]' size_plot.ray(x=[0,2,3], y=[0,2,3], length=0, angle=[45], angle_units='deg', color="#FB8072") #Add a hover tool for the relevant information hover_size = HoverTool() hover_size.tooltips = [ ('TS Data ID', '@ts_data_id'), ('Well', '@well_id'), ('Size [bp]', '@size'), ('Avg size [bp]', '@avg_size') ] size_plot.add_tools(hover_size) #Slider callbacks with CustomJS callback_JS=""" //Get the value from out int area slider var int_area_cutoff = int_area_slider.value; //Get the range of our calibrated concentration slider var conc_min = conc_slider.value[0]; var conc_max = conc_slider.value[1]; //Get the values from ts data multi_select var ts_data_list = ts_data_multi_select.value; //console.log(ts_data_list); //Call the unchanged data var uc_data = unchanged_data.data; var uc_ts_data_id_data = uc_data['ts_data_id']; var uc_well_id_data = uc_data['well_id']; var uc_cal_conc_data = uc_data['cal_conc']; var uc_int_area_data = uc_data['int_area']; var uc_size_data = uc_data['size']; var uc_peak_mol_data = uc_data['peak_mol']; var uc_avg_size = uc_data['avg_size']; //Call the data that we'll change var data = source_data.data; //The four columns we will change are cal_conc, int_area, size, and peak_mol var cal_conc=[]; var int_area=[]; var size=[]; var peak_mol=[]; var ts_data_id = []; var well_id = []; var avg_size = []; //Filter the data for(var i=0; i<uc_cal_conc_data.length;i++){ if(uc_int_area_data[i] >= int_area_cutoff){ if(uc_cal_conc_data[i] >= conc_min && uc_cal_conc_data[i] <= conc_max){ if(ts_data_list.includes(uc_ts_data_id_data[i])){ cal_conc.push(uc_cal_conc_data[i]); int_area.push(uc_int_area_data[i]); size.push(uc_size_data[i]); peak_mol.push(uc_peak_mol_data[i]); ts_data_id.push(uc_ts_data_id_data[i]); well_id.push(uc_well_id_data[i]); avg_size.push(uc_avg_size[i]); } } } } //Change the index too, since lengths are not the same any longer var index=[]; for(var j=0; j<cal_conc.length;j++){ index.push(j); } //Replace the data and emit it to source data['ts_data_id'] = ts_data_id; data['well_id'] = well_id; data['cal_conc'] = cal_conc; data['int_area'] = int_area; data['size'] = size; data['peak_mol'] = peak_mol; data['index'] = index; data['avg_size'] = avg_size; //console.log(data) source_data.change.emit(); """ callback = CustomJS(args=dict(source_data=source_data, unchanged_data=unchanged_data), code=callback_JS) # Want to make a slider for integrate_area cutoff int_area_N = 0 int_area_slider = Slider(start=0, end=100, step=1, value = int_area_N, title="% Integrated Area Cutoff") int_area_slider.js_on_change('value', callback) #Want to make a rangeslider for calibrated concentration cal_conc_min = first_peak_df['cal_conc'].min() cal_conc_max = first_peak_df['cal_conc'].max() conc_slider = RangeSlider(start=cal_conc_min, end=cal_conc_max, value=(cal_conc_min, cal_conc_max),step=(cal_conc_max - cal_conc_min) / 100, title='Range of Calibrated Concentration [pg/mul]') conc_slider.js_on_change('value',callback) #Make a Multiselect tool ts_data_list = first_peak_df['ts_data_id'].unique().tolist() ts_data_multi_select = MultiSelect(title='Select Data: ', value=ts_data_list, options=ts_data_list, height=130) ts_data_multi_select.js_on_change('value',callback) #Callback arguments callback.args['int_area_slider'] = int_area_slider callback.args['conc_slider'] = conc_slider callback.args['ts_data_multi_select'] = ts_data_multi_select #Create a paragraph to discuss first two plots readme = Paragraph(text = """ These first two plots correspond to the strongest peaks of the datasets. The first plot showing the size vs peak molarity, where the second plot shows the size vs the average size of the region. The user can use the following tools to filter the data being plotted. 'Select Data' is a MultiSelect Table,from which users can select which data to plot. 'Range of Calibrated Concentration allows users to choose the range of calibrated concentration. '% Integrated Area Cutoff allows user to choose the minimum required % Integrated Area for the strongest peak. """, width=1000) #Make another plot of the lower markers and the upper markers #marker_df = pd.read_sql('SELECT * FROM markers', engine) #marker_cols = ['ts_data_id', 'well_id', 'peak_mol', 'int_area', 'size', 'cal_conc', 'marker_id'] #marker_df = marker_df[marker_cols] #Separate the two #lower_mask = marker_df['marker_id'] == 'Lower Marker' #upper_mask = marker_df['marker_id'] == 'Upper Marker' #lower_df = marker_df[lower_mask].copy() #upper_df = marker_df[upper_mask].copy() #sc_data_lower = ColumnDataSource(lower_df) #uc_data_lower = ColumnDataSource(lower_df) #sc_data_upper = ColumnDataSource(upper_df) #Plot the two's sizes vs l = layout( [WidgetBox(readme)], [ts_data_multi_select], [conc_slider], [int_area_slider], [first_peak_plot, size_plot] ) script, div_dict = components(l) return render_template('homepage.html', script=script, div=div_dict, bokeh_version=BOKEH_VERSION)
def image_review_helperf(args): converthu = args["converthu"] invert = args["invert"] calculate_histogram = args["calculate_histogram"] colormap = args["colormap"] w = args["w"] general_functions.set_configuration(args["config"]) temp_folder, file_path = RestToolbox.GetSingleDcm(config.ORTHANC_URL, w) try: img = pylinac_image.DicomImage(file_path) if invert: img.invert() if converthu: # Convert back to pixel values if needed. img.array = (img.array - int(img.metadata.RescaleIntercept)) / int(img.metadata.RescaleSlope) img_array = np.flipud(img.array) except: return template("error_template", {"error_message": "Cannot read image."}) size_x = img_array.shape[1] size_y = img_array.shape[0] img_min = np.min(img_array) img_max = np.max(img_array) x1 = np.arange(0, size_x, 1).tolist() y1 = img_array[int(size_y//2), :].tolist() y2 = np.arange(0, size_y, 1).tolist() x2 = img_array[:, int(size_x//2)].tolist() source = ColumnDataSource(data=dict(x1=x1, y1=y1)) # Bottom plot source2 = ColumnDataSource(data=dict(x2=x2, y2=y2)) # Right plot sourcebp = ColumnDataSource(data=dict(xbp=[], ybp=[])) sourcerp = ColumnDataSource(data=dict(xrp=[], yrp=[])) # Add table for pixel position and pixel value hfmt = NumberFormatter(format="0.00") source6 = ColumnDataSource(dict( x=[], y=[], value=[] )) columns2 = [TableColumn(field="x", title="x", formatter=hfmt), TableColumn(field="y", title="y", formatter=hfmt), TableColumn(field="value", title="value", formatter=hfmt)] table2 = DataTable(source=source6, columns=columns2, editable=False, height=50, width = 220) # Plotting profiles callback = CustomJS(args=dict(source=source, source2=source2, sourcebp=sourcebp, sourcerp=sourcerp, source6=source6), code=""" var geometry = cb_data['geometry']; var x_data = parseInt(geometry.x); // current mouse x position in plot coordinates var y_data = parseInt(geometry.y); // current mouse y position in plot coordinates var data = source.data; var data2 = source2.data; var array = """+json.dumps(img_array.tolist())+"""; data['y1'] = array[y_data]; var column = []; for(var i=0; i < array.length; i++){ column.push(array[i][x_data]); } data2['x2'] = column; source.change.emit(); source2.change.emit(); var length_x = array[0].length; var length_y = array.length; if ((x_data<=length_x && x_data>=0) && (y_data<=length_y && y_data>=0)){ // Add points to plot: sourcebp.data['xbp'] = [x_data]; sourcebp.data['ybp'] = [array[y_data][x_data]]; sourcerp.data['xrp'] = [array[y_data][x_data]]; sourcerp.data['yrp'] = [y_data]; // Get position and pixel value for table source6.data["x"] = [geometry.x]; source6.data["y"] = [geometry.y]; source6.data["value"] = [array[y_data][x_data]]; sourcebp.change.emit(); sourcerp.change.emit(); source6.change.emit(); } """) # Add callback for calculating average value inside Rectangular ROI x3 = np.arange(0, size_x, 1).tolist() y3 = np.arange(0, size_y, 1).tolist() source3 = ColumnDataSource(data=dict(x3=x3, y3=y3)) source4 = ColumnDataSource(data=dict(x4=[], y4=[], width4=[], height4=[])) # Add table for mean and std source5 = ColumnDataSource(dict( mean=[], median=[], std=[], minn=[], maxx=[] )) columns = [TableColumn(field="mean", title="mean", formatter=hfmt), TableColumn(field="median", title="median", formatter=hfmt), TableColumn(field="std", title="std", formatter=hfmt), TableColumn(field="minn", title="min", formatter=hfmt), TableColumn(field="maxx", title="max", formatter=hfmt)] table = DataTable(source=source5, columns=columns, editable=False, height=50, width = 480) # Calculate things within ROI callback2 = CustomJS(args=dict(source3=source3, source4=source4, source5=source5), code=""" var geometry = cb_obj['geometry']; var data3 = source3.data; var data4 = source4.data; var data5 = source5.data; // Get data var x0 = parseInt(geometry['x0']); var x1 = parseInt(geometry['x1']); var y0 = parseInt(geometry['y0']); var y1 = parseInt(geometry['y1']); // calculate Rect attributes var width = x1 - x0; var height = y1 - y0; var x = x0 + width/2; var y = y0 + height/2; // update data source with new Rect attributes data4['x4'] = [x]; data4['y4'] = [y]; data4['width4'] = [width]; data4['height4'] = [height]; // Get average value inside ROI var array = """+json.dumps(img_array.tolist())+"""; var length_x = array[0].length; var length_y = array.length; if ((x0<=length_x && x0>=0) && (x1<=length_x && x1>=0) && (y0<=length_y && y0>=0) && (y1<=length_y && y1>=0)){ var avg_ROI = []; for (var i=y0; i< y1; i++){ for (var j=x0; j<x1; j++){ avg_ROI.push(array[i][j]); } } if (avg_ROI == undefined || avg_ROI.length==0){ data5["mean"] = [0]; data5["median"] = [0]; data5["std"] = [0]; } else{ data5["mean"] = [math.mean(avg_ROI)]; data5["median"] = [math.median(avg_ROI)]; data5["std"] = [math.std(avg_ROI, 'uncorrected')]; data5["maxx"] = [math.max(avg_ROI)]; data5["minn"] = [math.min(avg_ROI)]; } source4.change.emit(); source5.change.emit(); } """) plot_width = 500 plot_height = int((plot_width-20)*size_y/size_x) fig = figure(x_range=[0, size_x], y_range=[0, size_y], plot_width=plot_width, plot_height=plot_height, title="", toolbar_location="right", tools=["crosshair, wheel_zoom, pan, reset"]) fig_r = figure(x_range=[img_min, img_max], y_range=fig.y_range, plot_width=250, plot_height=plot_height, title="", toolbar_location="right", tools=[]) fig_b = figure(x_range=fig.x_range, y_range=[img_min, img_max], plot_width=plot_width, plot_height=200, title="", toolbar_location="right", tools=[]) # Define matplotlib palette and make it possible to be used dynamically. cmap = matplotlib.cm.get_cmap(colormap) #chose any matplotlib colormap here bokehpalette = [matplotlib.colors.rgb2hex(m) for m in cmap(np.arange(cmap.N)[::-1])] # Reversed direction of colormap mapper = LinearColorMapper(palette=bokehpalette, low=np.min(img_array), high=np.max(img_array)) callback3 = CustomJS(args=dict(mapper=mapper, x_range=fig_r.x_range, y_range=fig_b.y_range), code=""" mapper.palette = """+json.dumps(bokehpalette)+"""; var start = cb_obj.value[0]; var end = cb_obj.value[1]; mapper.low = start; mapper.high = end; x_range.setv({"start": start, "end": end}); y_range.setv({"start": start, "end": end}); //mapper.change.emit(); """) range_slider = RangeSlider(start=np.min(img_array), end=np.max(img_array), value=(np.min(img_array), np.max(img_array)), step=10, title="Level") range_slider.js_on_change('value', callback3) fig.image([img_array], x=0, y=0, dw=size_x, dh=size_y, color_mapper=mapper) fig_b.line(x='x1', y='y1', source=source) fig_r.line(x='x2', y='y2', source=source2) fig_b.circle(x='xbp', y='ybp', source=sourcebp, size=7, fill_color="red", fill_alpha=0.5, line_color=None) # For point connected to crosshair fig_r.circle(x='xrp', y='yrp', source=sourcerp, size=7, fill_color="red", fill_alpha=0.5, line_color=None) # For point connected to crosshair fig.rect(x='x4', y='y4', width='width4', height='height4', source=source4, fill_alpha=0.3, fill_color='#009933') fig.add_tools(HoverTool(tooltips=None, callback=callback)) fig.add_tools(BoxSelectTool()) fig.js_on_event(SelectionGeometry, callback2) # Add event_callback to Boxselecttool grid = gridplot([[table, range_slider], [fig, fig_r], [fig_b, table2]]) script, div = components(grid) # Calculate pixel value histogram if calculate_histogram: try: bitsstored = int(img.metadata.BitsStored) except: bitsstored = 16 max_pixelvalue = 2**bitsstored-1 counts, bins = np.histogram(img_array.flatten(), density=False, range=(0, max_pixelvalue), bins=max_pixelvalue+1) fig_hist = figure(x_range = [-100, max_pixelvalue*1.05], y_range=[0.5, np.max(counts)], plot_width=750, plot_height=400, title="Pixel value histogram", y_axis_type="log", toolbar_location="right", tools=["box_zoom, pan, reset"]) fig_hist.quad(top=counts, bottom=0.5, left=bins[:-1], right=bins[1:], alpha=0.5) fig_hist.grid.visible = False fig_hist.yaxis.axis_label = "Pixel Count" fig_hist.xaxis.axis_label = "Pixel Value" script_hist, div_hist = components(fig_hist) else: script_hist, div_hist = ["", ""] variables = {"script": script, "div": div, "bokeh_file_css": BOKEH_FILE_CSS, "bokeh_file_js": BOKEH_FILE_JS, "bokeh_widgets_js": BOKEH_WIDGETS_JS, "bokeh_tables_js": BOKEH_TABLES_JS, "script_hist": script_hist, "div_hist": div_hist } #gc.collect() general_functions.delete_files_in_subfolders([temp_folder]) # Delete image return template("image_review_results", variables)