def make_plot(source): pal = RdYlGn[10] mapper = log_cmap(field_name="TransferAmount", palette=pal, low=10000, high=50000000) tooltips = [("Price", "@TransferAmount"), ("SaleYear", "@Tx_Year")] # slider = Slider(start=2000, end=2019, step=1, value=2015, title = 'Year') from bokeh.tile_providers import get_provider, Vendors fig = figure(x_range=x_range, y_range=y_range, x_axis_type='mercator', y_axis_type='mercator', tooltips=tooltips, title='San Diego Residential Housing Prices') fig.add_tile(get_provider(Vendors.CARTODBPOSITRON)) fig.circle(x='coords_latitude', y='coords_longitude', line_color=mapper, color=mapper, source=source) # Defines color bar attributes and location color_bar = ColorBar(color_mapper=mapper['transform'], width=8, location=(0, 0)) fig.add_layout(color_bar, 'right') # Defines layout of graph and widgets return fig
def test_defaults(self) -> None: t = bt.log_cmap("foo", ["red", "green"], 0, 10) assert isinstance(t, Field) assert t.field == "foo" assert isinstance(t.transform, LogColorMapper) assert t.transform.palette == ["red", "green"] assert t.transform.low == 0 assert t.transform.high == 10 assert t.transform.low_color is None assert t.transform.high_color is None assert t.transform.nan_color == "gray"
def test_defaults(self): t = bt.log_cmap("foo", ["red", "green"], 0, 10) assert isinstance(t, dict) assert set(t) == {"field", "transform"} assert t['field'] == "foo" assert isinstance(t['transform'], LogColorMapper) assert t['transform'].palette == ["red", "green"] assert t['transform'].low == 0 assert t['transform'].high is 10 assert t['transform'].low_color is None assert t['transform'].high_color is None assert t['transform'].nan_color == "gray"
def test_defaults(self): t = bt.log_cmap("foo", ["red", "green"], 0, 10) assert isinstance(t, dict) assert set(t) == {"field", "transform"} assert t['field'] == "foo" assert isinstance(t['transform'], LogColorMapper) assert t['transform'].palette == ["red", "green"] assert t['transform'].low == 0 assert t['transform'].high is 10 assert t['transform'].low_color is None assert t['transform'].high_color is None assert t['transform'].nan_color == "gray"
def test_basic(self): t = bt.log_cmap("foo", ["red", "green"], 0, 10, low_color="orange", high_color="blue", nan_color="pink") assert isinstance(t, dict) assert set(t) == {"field", "transform"} assert t['field'] == "foo" assert isinstance(t['transform'], LogColorMapper) assert t['transform'].palette == ["red", "green"] assert t['transform'].low == 0 assert t['transform'].high is 10 assert t['transform'].low_color == "orange" assert t['transform'].high_color == "blue" assert t['transform'].nan_color == "pink"
def test_basic(self): t = bt.log_cmap("foo", ["red", "green"], 0, 10, low_color="orange", high_color="blue", nan_color="pink") assert isinstance(t, dict) assert set(t) == {"field", "transform"} assert t['field'] == "foo" assert isinstance(t['transform'], LogColorMapper) assert t['transform'].palette == ["red", "green"] assert t['transform'].low == 0 assert t['transform'].high is 10 assert t['transform'].low_color == "orange" assert t['transform'].high_color == "blue" assert t['transform'].nan_color == "pink"
def draw_class_count_maps(self): min_count = self.qaqc_results_df[self.class_counts.index].min().min() max_count = self.qaqc_results_df[self.class_counts.index].max().max() palette = Blues[9] palette.reverse() class_count_plots = [] for i, class_field in enumerate(self.class_counts.index): color_mapper = log_cmap(field_name=class_field, palette=palette, low=min_count, high=max_count, nan_color='white') las_class = class_field.replace('class', '').zfill(2) title = 'Class {} ({})'.format(las_class, self.las_classes[las_class]) if i > 0: p = figure(title=title, x_axis_type="mercator", y_axis_type="mercator", x_range=class_count_plots[0].x_range, y_range=class_count_plots[0].y_range, plot_width=300, plot_height=300, match_aspect=True, tools=self.TOOLS) else: p = figure(title=title, x_axis_type="mercator", y_axis_type="mercator", plot_width=300, plot_height=300, match_aspect=True, tools=self.TOOLS) if int(las_class) in self.config.exp_cls_key: title_color = '#0074D9' else: title_color = '#FF851B' p.title.text_color = title_color p.toolbar.logo = None p.add_tile(get_provider(Vendors.CARTODBPOSITRON)) p.patches('xs', 'ys', source=self.qaqc_polygons, alpha=0.1) p.circle(x='x', y='y', size=5, alpha=0.5, source=self.qaqc_centroids, color=color_mapper) class_count_plots.append(p) class_count_plots = self.add_empty_plots_to_reshape(class_count_plots) class_count_grid_plot = gridplot(class_count_plots, ncols=3, plot_height=300, toolbar_location='right') tab2 = Panel(child=class_count_grid_plot, title="Class Counts") return tab2
def test_basic(self) -> None: t = bt.log_cmap("foo", ["red", "green"], 0, 10, low_color="orange", high_color="blue", nan_color="pink") assert isinstance(t, Field) assert t.field == "foo" assert isinstance(t.transform, LogColorMapper) assert t.transform.palette == ["red", "green"] assert t.transform.low == 0 assert t.transform.high == 10 assert t.transform.low_color == "orange" assert t.transform.high_color == "blue" assert t.transform.nan_color == "pink"
aspect = ydiff/xdiff # Initialize Light Curve Plot fig_cmd = figure(plot_height=280, plot_width=800, sizing_mode='scale_both', x_range=[xlow,xupp], y_range=[ylow,yupp], tools="pan,wheel_zoom,box_zoom,reset", title=None, min_border=10, min_border_left=50, min_border_right=50, toolbar_location="above", border_fill_color="whitesmoke") fig_cmd.toolbar.logo = None fig_cmd.xaxis.axis_label = 'Color (BP-RP)' fig_cmd.yaxis.axis_label = 'Absolute Magnitude (G)' # Generate Hexbin CMD for full Gaia Sample bins = hexbin(bprp, mag, 0.05, aspect_scale=aspect) palette = Greys256[::-1] fill_color = log_cmap('c', palette, 1, max(bins.counts)) source = ColumnDataSource(data=dict(q=bins.q, r=bins.r, c=bins.counts)) r = fig_cmd.hex_tile(q="q", r="r", size=0.05, aspect_scale=aspect, source=source, line_color=None, fill_color=fill_color) # Remove Grid Lines fig_cmd.xgrid.grid_line_color = None fig_cmd.ygrid.grid_line_color = None # Add a simple hover tool fig_cmd.add_tools(HoverTool( tooltips=[("count", "@c"), ("(q,r)", "(@q, @r)")], mode="mouse", point_policy="follow_mouse", renderers=[r])) scat = fig_cmd.scatter(X, Y, size=10, color="#3A5785", alpha=0.8) #, xlabel="G-R",ylabel="R") # Main HRD
from bokeh.io import output_file, show from bokeh.plotting import figure from bokeh.palettes import Spectral6 from bokeh.transform import log_cmap from bokeh.models import ColorBar, LogTicker import numpy as np x = np.linspace(1, 10, 100) y = np.exp(x) mapper = log_cmap(field_name='y', palette=Spectral6, low=min(y), high=max(y)) plot = figure(plot_height=300, plot_width=600) plot.circle(x, y, color=mapper, line_color='black', size=10) color_bar = ColorBar(color_mapper=mapper['transform'], width=8, location=(0, 0), ticker=LogTicker()) plot.add_layout(color_bar, 'right') output_file('color_mapper.html') show(plot)
data_new['y'].push(y[i]) } } source1.data = data_new """ import bokeh.models.callbacks # slider _arg = {'source': src, 'source1': src1} callback_slider = bokeh.models.callbacks.CustomJS(args=_arg, code=code_slider) slider = bokeh.models.Slider(start=5, end=100, value=0, step=1, title="% COMP") slider.js_on_change('value', callback_slider) from bokeh.transform import log_cmap cm = log_cmap('DEN', palette=bokeh.palettes.Viridis11, low=1, high=10000) p.scatter('x', 'y', source=src, radius=.05, alpha=1, legend_label='Faltantes') p.scatter('x', 'y', source=src, radius=.05, alpha=1, color=cm, legend_label='Densidad') p.scatter('x', 'y', source=src1, radius=.05, alpha=1, color='red',
def modify_doc(doc): """Add plots to the document Parameters ---------- doc : [type] A Bokeh document to which plots can be added """ curDir = os.path.dirname(__file__) root = tk.Tk() root.withdraw() ProgressBar().register() filepath = filedialog.askopenfilename() filename = filepath[filepath.rfind('/') + 1:filepath.rfind('.')] monteData = dd.read_csv(filepath) monteData.fillna(0) plotData = monteData.compute() gc.collect() plotData.drop_duplicates(subset='L-string', inplace=True) plotData.reset_index() for i in range(2, 6): plotData['{}-gram'.format(i)] = plotData['L-string'].apply( lambda x: [x[j:j + i] for j in range(0, len(x), i)]) gc.collect() scatter = ColumnDataSource(data=plotData) line = ColumnDataSource(data=dict(x=[0, 0], y=[0, 0])) rule1 = ColumnDataSource(data=dict(x=[0, 0], y=[0, 0])) rule2 = ColumnDataSource(data=dict(x=[0, 0], y=[0, 0])) polygon = ColumnDataSource(data=dict(x=[0], y=[0])) rule1_poly = ColumnDataSource(data=dict(x=[0, 0], y=[0, 0])) rule2_poly = ColumnDataSource(data=dict(x=[0, 0], y=[0, 0])) palette.reverse() mapper = log_cmap(field_name='Area', palette=palette, low=0, high=500) tooltips1 = [ ('index', '$index'), ('F', '@{% of F}{0.0%}'), ('+', '@{% of +}{0.0%}'), ('-', '@{% of -}{0.0%}'), ] tooltips2 = [ ('index', '$index'), ('F', '@{Longest F sequence}'), ('+', '@{Longest + sequence}'), ('-', '@{Longest - sequence}'), ] plots_width = 500 plots_height = 500 p1 = figure(plot_width=plots_width, plot_height=plots_height, tools='pan,wheel_zoom,box_zoom,reset,tap,save', title="Area", output_backend="webgl", tooltips=tooltips1) p1.xaxis.axis_label = 'Area' p1.yaxis.axis_label = '% of character' p1.scatter('Area', '% of F', size=7, source=scatter, color=mapper, alpha=0.6, nonselection_fill_color=mapper) p2 = figure(plot_width=plots_width, plot_height=plots_height, tools='pan,wheel_zoom,box_zoom,reset,tap,save', title="Area", output_backend="webgl", tooltips=tooltips2) p2.xaxis.axis_label = 'Area' p2.yaxis.axis_label = 'Length of sequence' p2.scatter('Area', 'Longest F sequence', size=7, source=scatter, fill_color='red', color=mapper, alpha=0.6, nonselection_fill_color=mapper) p3 = figure(plot_width=plots_width, plot_height=plots_height, tools='pan,wheel_zoom,box_zoom,reset,tap,save', title="Selected Creature", output_backend="webgl") p3.axis.visible = False p3.grid.visible = False p3.line(x='x', y='y', line_color='red', source=line) p3.multi_polygons(xs='x', ys='y', source=polygon) p4 = figure(plot_width=plots_width, plot_height=plots_height, tools='pan,wheel_zoom,box_zoom,reset,tap,save', title="Area", output_backend="webgl") p4.scatter('Area', 'Angle', size=7, source=scatter, color=mapper, alpha=0.6, nonselection_fill_color=mapper) p4.xaxis.axis_label = 'Area' p4.yaxis.axis_label = 'Angle (degrees)' p5 = figure(plot_width=plots_width, plot_height=plots_height // 2, title="Rule 1", output_backend="webgl") p5.line(x='x', y='y', line_color='red', source=rule1) p5.multi_polygons(xs='x', ys='y', source=rule1_poly) p5.axis.visible = False p5.grid.visible = False p6 = figure(plot_width=plots_width, plot_height=plots_height // 2, title="Rule 2", output_backend="webgl") p6.line(x='x', y='y', line_color='red', source=rule2) p6.multi_polygons(xs='x', ys='y', source=rule2_poly) p6.axis.visible = False p6.grid.visible = False L_string = Paragraph(text='Select creature', width=1500) grams = PreText(text='Select creature', width=400) rule_text = PreText(text='Select creature', width=400) area_label = Label( x=0, y=450, x_units='screen', y_units='screen', text='Select creature', render_mode='css', border_line_color='black', border_line_alpha=1.0, background_fill_color='white', background_fill_alpha=1.0, ) length_label = Label( x=0, y=420, x_units='screen', y_units='screen', text='Select creature', render_mode='css', border_line_color='black', border_line_alpha=1.0, background_fill_color='white', background_fill_alpha=1.0, ) p3.add_layout(area_label) p3.add_layout(length_label) def plot_source(coords): """[summary] Returns ------- [type] [description] """ instance_linestring = LineString(coords[:, 0:2]) instance_patch = instance_linestring.buffer(0.5) instance_x, instance_y = instance_patch.exterior.coords.xy return instance_x, instance_y def mapper(string, angle): theta = 0 num_chars = len(string) coords = np.zeros((num_chars + 1, 3), np.double) def makeRotMat(theta): rotMat = np.array(((cos(theta), -sin(theta), 0), (sin(theta), cos(theta), 0), (0, 0, 1))) return rotMat rotVec = makeRotMat(theta) dir_vec = np.array((0, 1, 0), np.float64) i = 1 for c in string: if c == 'F': coords[i] = (coords[i - 1] + (1 * dir_vec)) i += 1 if c == '-': theta = theta - angle rotVec = makeRotMat(theta) dir_vec = np.dot(rotVec, dir_vec) if c == '+': theta = theta + angle rotVec = makeRotMat(theta) dir_vec = np.dot(rotVec, dir_vec) coords = np.delete(coords, np.s_[i:], 0) return coords def plot_creature(event): line.data = dict(x=[0, 0], y=[0, 0]) polygon.data = dict(x=[0, 0], y=[0, 0]) rule1.data = dict(x=[0, 0], y=[0, 0]) rule2.data = dict(x=[0, 0], y=[0, 0]) rule1_poly.data = dict(x=[0, 0], y=[0, 0]) rule2_poly.data = dict(x=[0, 0], y=[0, 0]) L_string.text = 'Select creature' area_label.text = 'Select creature' length_label.text = 'Select creature' rule_text.text = 'Select creature' if len(scatter.selected.indices) > 0: creature_index = scatter.selected.indices[0] creature = plotData.iloc[creature_index, :] coords = np.array(ast.literal_eval(creature['Coordinates'])) L_string.text = '{}'.format(creature['L-string']) area_label.text = 'Area: {:.2f}'.format(creature['Area']) length_label.text = 'Length of L-string: {}'.format( len(creature['L-string'])) gram_frame_1 = pd.DataFrame.from_dict( { '2-gram': creature['2-gram'], '3-gram': creature['3-gram'], '4-gram': creature['4-gram'], '5-gram': creature['5-gram'], }, orient='index').T counts = [ pd.value_counts( gram_frame_1[i]).reset_index().astype(str).apply( ' '.join, 1) for i in gram_frame_1 ] out = pd.concat(counts, 1).fillna('') out.columns = gram_frame_1.columns grams.text = str(tabulate(out, headers='keys')) creature_linestring = LineString(coords[:, 0:2]) creature_patch = creature_linestring.buffer(0.5) patch_x, patch_y = creature_patch.exterior.coords.xy x_points = [list(patch_x)] y_points = [list(patch_y)] for i, _ in enumerate(creature_patch.interiors): x_in, y_in = creature_patch.interiors[i].coords.xy x_points.append(list(x_in)) y_points.append(list(y_in)) x_points = [[x_points]] y_points = [[y_points]] line.data = dict(x=coords[:, 0], y=coords[:, 1]) polygon.data = dict(x=x_points, y=y_points) p3.match_aspect = True rules = ast.literal_eval(creature['Rules']) rules = rules['X'] rules = rules['options'] rule_text.text = 'Rule 1: \t' + \ rules[0] + '\n' + 'Rule 2: \t' + rules[1] if any(char == 'F' for string in rules[0] for char in string): rule1_c = mapper(rules[0], creature['Angle']) rule1_morphology = LineString(rule1_c[:, 0:2]) rule1_patch = rule1_morphology.buffer(0.5) rpatch_x, rpatch_y = rule1_patch.exterior.coords.xy r1_points_x = [list(rpatch_x)] r1_points_y = [list(rpatch_y)] for i, _ in enumerate(rule1_patch.interiors): x_in, y_in = creature_patch.interiors[i].coords.xy r1_points_x.append(list(x_in)) r1_points_y.append(list(y_in)) r1_points_x = [[r1_points_x]] r1_points_y = [[r1_points_y]] rule1.data = dict(x=rule1_morphology.coords.xy[0], y=rule1_morphology.coords.xy[1]) rule1_poly.data = dict(x=r1_points_x, y=r1_points_y) p5.match_aspect = True if any(char == 'F' for string in rules[1] for char in string): rule2_c = mapper(rules[1], creature['Angle']) rule2_morphology = LineString(rule2_c[:, 0:2]) rule2_patch = rule2_morphology.buffer(0.5) r2patch_x, r2patch_y = rule2_patch.exterior.coords.xy r2_points_x = [list(r2patch_x)] r2_points_y = [list(r2patch_y)] for i, _ in enumerate(rule2_patch.interiors): x_in, y_in = creature_patch.interiors[i].coords.xy r2_points_x.append(list(x_in)) r2_points_y.append(list(y_in)) r2_points_x = [[r2_points_x]] r2_points_y = [[r2_points_y]] rule2.data = dict(x=rule2_morphology.coords.xy[0], y=rule2_morphology.coords.xy[1]) rule2_poly.data = dict(x=r2_points_x, y=r2_points_y) p6.match_aspect = True else: line.data = dict(x=[0, 0], y=[0, 0]) polygon.data = dict(x=[0, 0], y=[0, 0]) rule1.data = dict(x=[0, 0], y=[0, 0]) rule2.data = dict(x=[0, 0], y=[0, 0]) rule1_poly.data = dict(x=[0, 0], y=[0, 0]) rule2_poly.data = dict(x=[0, 0], y=[0, 0]) L_string.text = 'Select creature' area_label.text = 'Select creature' length_label.text = 'Select creature' rule_text.text = 'Select creature' p1.on_event(Tap, plot_creature) p2.on_event(Tap, plot_creature) p4.on_event(Tap, plot_creature) top_row = row(L_string) middle_row = row(p1, p2, p4) bottom_row_right = column(p5, p6) bottom_row_middle = column(grams, rule_text) bottom_row = row(p3, Spacer(width=50), bottom_row_middle, Spacer(width=50), bottom_row_right) layout = column(top_row, middle_row, bottom_row) doc.add_root(layout)
def create_us_map_tab(): "Factory for creating second tab of app: US Only Data" ## Data Sources source_df_confirmed, source_CDS = get_time_series_confirmed_US_data() source_CDS.data['number_per_capita'] = source_df_confirmed[ START_DATE_STRING] / source_df_confirmed['population'] ## Map color_mapper = log_cmap(field_name='number', palette=Spectral6, low=1, high=1e6) TOOLTIPS = [('County/Region', '@county'), ('State/Province', '@region'), ('Population', '@population'), ('Cases', '@number'), ('Cases Per Capita', '@number_per_capita')] map_figure = figure( title='Confirmed COVID-19 Cases in the United States', tooltips=TOOLTIPS, x_range=(-18367715, -6901808.43), y_range=(0, 13377019.78), x_axis_type='mercator', y_axis_type='mercator', active_scroll='wheel_zoom', ) tile_provider = get_provider(CARTODBPOSITRON) map_figure.add_tile(tile_provider) map_figure.circle(x='web_mercator_x', y='web_mercator_y', source=source_CDS, size='sizes', color=color_mapper) ## Colorbar color_bar = ColorBar(title='Num. Cases', title_standoff=20, color_mapper=color_mapper['transform'], label_standoff=20, width=8, location=(0, 0), ticker=LogTicker()) color_bar.formatter.use_scientific = False map_figure.add_layout(color_bar, 'right') ## Slider def slider_callback(attr, old, new): delta = datetime.timedelta(milliseconds=new) date = datetime.date(1970, 1, 1) + delta date_string = date.strftime('%Y-%m-%d') try: source_CDS.data['number'] = source_df_confirmed[date_string] source_CDS.data['sizes'] = source_df_confirmed[date_string].apply( scale) source_CDS.data['number_per_capita'] = source_df_confirmed[ date_string] / source_df_confirmed['population'] except KeyError: pass slider = DateSlider(title='Date', start=START_DATE, end=datetime.date.today(), step=1, value=START_DATE) slider.on_change('value', slider_callback) ## Data Table columns = [ TableColumn(field='county', title='County/Region'), TableColumn(field='region', title='State/Province'), TableColumn(field='population', title='Population'), TableColumn(field='number', title='Cases'), TableColumn(field='number_per_capita', title='Cases Per Capita'), ] data_table = DataTable( source=source_CDS, columns=columns, ) ## Cancel Selection Button def cancel_selection_callback(): source_CDS.selected.indices = [] cancel_selection_button = Button(label='Clear Selection', button_type='warning') cancel_selection_button.on_click(cancel_selection_callback) child = row([ column([slider, map_figure]), column([data_table, cancel_selection_button]) ]) return Panel(child=child, title='United States Map')
# for i, run in indexed_runs.items(): # source = ColumnDataSource(data=dict(x=run[:, 0] * day_ratio, y=run[:, 1])) # loss_plot.line('x', 'y', source=source, line_width=1, line_alpha=0.6, color=color_list[i]) # loss_plot.scatter('x', 'y', source=source, line_width=1, line_alpha=0.6, color=color_list[i]) source = ColumnDataSource(data=dict( xs=[run[:, 0] * day_ratio for run in indexed_runs], # x coords for each line (list of lists) ys=[run[:, 1] for run in indexed_runs], # y coords for each line (list of lists) params=params_per_run # data to use for colormapping )) loss_plot.multi_line('xs', 'ys', source=source, color=log_cmap('params', palette, min(params_per_run), max(params_per_run))) source = ColumnDataSource(data=dict( x=[compute for run in indexed_runs for compute in run[:, 0] * day_ratio], # x coords for each line (list of lists) y=[loss for run in indexed_runs for loss in run[:, 1]], # y coords for each line (list of lists) params=[ repeated_params for i, params in enumerate(params_per_run) for repeated_params in [params] * len(indexed_runs[i]) ] # data to use for colormapping )) loss_plot.scatter('x', 'y', source=source, color=log_cmap('params', palette, min(params_per_run), max(params_per_run)),
def linked_scatter_graph(master_source_dict, xproperties, yproperties, cproperties, sproperties, xlogs, ylogs, clogs, slogs, errorbar_xs, errorbar_ys, x_hists, y_hists, tooltip_list): '''Funtion to generate html for an interactive bokeh scatter/errorbar graph''' # copy source_dict to avoid overmapping source_dict = copy.deepcopy(master_source_dict) # size mapping (do before making the data source) for i, (sproperty, slog) in enumerate(zip(sproperties, slogs)): if sproperty == 'None': source_dict.update({'smap%i' % i: [4] * len(source_dict['name']) }) # size 4 is default else: arr = source_dict[sproperty] if slog: arr = np.log(arr) source_dict.update({ 'smap%i' % i: np.nan_to_num( np.multiply(np.subtract(arr, np.nanmin(arr)), 15 / np.nanmax(arr))).tolist() }) # make data source source = ColumnDataSource(data=source_dict) plots = [] for i in range(len(xproperties)): # don't use i for loops inside this xproperty, yproperty, cproperty, sproperty, xlog, ylog, clog, slog, errorbar_x, errorbar_y, x_hist, y_hist = xproperties[ i], yproperties[i], cproperties[i], sproperties[i], xlogs[ i], ylogs[i], clogs[i], slogs[i], errorbar_xs[i], errorbar_ys[ i], x_hists[i], y_hists[i] if xproperty != 'None' and yproperty != 'None': # units units = [''] * 4 properties = [xproperty, yproperty, cproperty, sproperty] for j, prop in enumerate( properties): # must use enumerate, not zip if prop != 'None': if 'host.' in prop: units[j] = Star.property_dict[prop.replace( 'host.', '')] else: units[j] = Exoplanet.property_dict[prop] (xunit, yunit, cunit, sunit) = (unit for unit in units) formatters = [''] * 4 # for datetime formatting in tooltips for j, unit in enumerate(units): if unit == 'date': formatters[j] = '{%F}' if unit == 'year': formatters[j] = '{%Y}' # axis scale if xlog: x_axis_type = 'log' else: x_axis_type = 'linear' if ylog: y_axis_type = 'log' else: y_axis_type = 'linear' if xunit == 'date' or xunit == 'year': x_axis_type = 'datetime' if yunit == 'date' or yunit == 'year': y_axis_type = 'datetime' # create tooltip table tooltips = [ ('(%s, %s)' % (xproperty.replace('.', '_'), yproperty.replace('.', '_')), '(@{%s}%s, @{%s}%s)' % (xproperty, formatters[0], yproperty, formatters[1])), ('(%s, %s)' % (cproperty.replace('.', '_'), sproperty.replace('.', '_')), '(@{%s}%s, @{%s}%s)' % (cproperty, formatters[2], sproperty, formatters[3])), ] for prop in tooltip_list: tooltips.append((prop, '@%s' % prop)) # create plot figure plot = figure( x_axis_type=x_axis_type, y_axis_type=y_axis_type, sizing_mode='stretch_both', tools=[ PanTool(), BoxZoomTool(), BoxSelectTool(), LassoSelectTool(), WheelZoomTool(), TapTool(), UndoTool(), RedoTool(), CrosshairTool(), SaveTool(), ResetTool(), HoverTool(tooltips=tooltips, formatters={ '@{discovered}': 'datetime', '@{updated}': 'datetime' }) ], toolbar_location='above', # title=str(datetime.date.today()), background_fill_alpha=0.7, border_fill_alpha=0.7, ) # invert scales for magnitudes if 'mag_' in xproperty: plot.x_range.flipped = True if 'mag_' in yproperty: plot.y_range.flipped = True # link points to exoplanet.eu url = 'http://www.exoplanet.eu/catalog/@name' taptool = plot.select(type=TapTool) taptool.callback = OpenURL(url=url.replace(' ', '_')) # label axes plot.xaxis.axis_label = xproperty.replace('_', ' ').replace( '.', ' ').capitalize() + ' (%s)' % xunit plot.yaxis.axis_label = yproperty.replace('_', ' ').replace( '.', ' ').capitalize() + ' (%s)' % yunit # font sizes fontsize = '12pt' plot.xaxis.axis_label_text_font_size = fontsize plot.yaxis.axis_label_text_font_size = fontsize # color mapping if cproperty == 'None': cmap = Category10[10][0] elif cproperty == 'detection_type': # detection type is categorical so needs special treatment detection_methods = [ 'Primary Transit', 'Radial Velocity', 'Imaging', 'Microlensing', 'Timing', 'Astrometry', 'TTV', 'Default', 'Primary Transit, TTV', 'Controversial' ] cmap = factor_cmap('detection_type', palette=Category10[10], factors=detection_methods) # counts counts = {method: 0 for method in detection_methods} for j, method in enumerate(source_dict['detection_type']): if np.isfinite(source_dict[xproperty][j]) and np.isfinite( source_dict[yproperty][j]): counts[method] += 1 # need to create legend manually so we can place it outside the plot area # legend placement depends on whether histograms are present if y_hist: blank = figure( width_policy='min', height_policy='min', width=205, height=290, outline_line_color=None, background_fill_alpha=0.7, border_fill_alpha=0.7, ) legend = categorical_legend(detection_methods, counts.values(), '', Category10[10], fig=blank) else: legend = categorical_legend(detection_methods, counts.values(), '', Category10[10], fig=plot) plot.add_layout(legend, 'right') else: if clog: cmap = log_cmap(cproperty, palette=Turbo256, low=np.nanmin(source_dict[cproperty]), high=np.nanmax(source_dict[cproperty])) color_bar = ColorBar(color_mapper=cmap['transform'], ticker=LogTicker(), width=8, location=(0, 0), background_fill_alpha=0) else: cmap = linear_cmap(cproperty, palette=Turbo256, low=np.nanmin(source_dict[cproperty]), high=np.nanmax(source_dict[cproperty])) color_bar = ColorBar(color_mapper=cmap['transform'], width=8, location=(0, 0), background_fill_alpha=0) plot.add_layout(color_bar, 'right') # bokeh does not have a native colorbar label item so we will use a plot title as a substitute plot.title.text = cproperty.replace('_', ' ').replace( '.', ' ').capitalize() + ' (%s)' % cunit plot.title_location = 'right' plot.title.align = 'center' plot.title.text_font_size = fontsize plot.title.text_font_style = 'italic' # plot graph plot.scatter( xproperty, yproperty, color=cmap, size='smap%i' % i, source=source, ) # errorbars if xproperty + '_error_min' in Exoplanet.property_dict or xproperty.replace( 'host.', '') + '_error_min' in Star.property_dict: wx = Whisker( source=source, dimension='width', visible=errorbar_x, base=yproperty, upper=xproperty + '_error_max', lower=xproperty + '_error_min', line_color=cmap, upper_head=None, lower_head=None, ) plot.add_layout(wx) if yproperty + '_error_min' in Exoplanet.property_dict or yproperty.replace( 'host.', '') + '_error_min' in Star.property_dict: wy = Whisker( source=source, dimension='height', visible=errorbar_y, base=xproperty, upper=yproperty + '_error_max', lower=yproperty + '_error_min', line_color=cmap, upper_head=None, lower_head=None, ) plot.add_layout(wy) # histograms full_colour = '#756bb1' sample_colour = '#74c476' if x_hist: # list setup full_list = source_dict[xproperty] short_list = [ xval for xval, yval in zip(source_dict[xproperty], source_dict[yproperty]) if not np.isnan(yval) ] min_ = np.nanmin(full_list) max_ = np.nanmax(full_list) # plot setup hxplot = figure( x_range=plot.x_range, x_axis_type=x_axis_type, height=300, sizing_mode='stretch_width', background_fill_alpha=0.7, border_fill_alpha=0.7, ) hxplot.xaxis.axis_label = xproperty.replace('_', ' ').replace( '.', ' ').capitalize() + ' (%s)' % xunit hxplot.yaxis.axis_label = 'Frequency' hxplot.xaxis.axis_label_text_font_size = fontsize hxplot.yaxis.axis_label_text_font_size = fontsize # bins if xlog: bins = np.logspace(np.log10(min_), np.log10(max_), 100) else: bins = np.linspace(min_, max_, 100) # full range histogram hist, edges = np.histogram(full_list, bins=bins) hxplot.quad(top=hist, bottom=0, left=edges[:-1], right=edges[1:], color=full_colour, fill_alpha=0.5, legend_label='All') # only plotted histogram hist, edges = np.histogram(short_list, bins=bins) hxplot.quad(top=hist, bottom=0, left=edges[:-1], right=edges[1:], color=sample_colour, fill_alpha=0.5, legend_label='Shown') # legend setup hxplot.legend.click_policy = 'hide' hxplot.legend.background_fill_alpha = 0 if y_hist: # list setup full_list = source_dict[yproperty] short_list = [ yval for xval, yval in zip(source_dict[xproperty], source_dict[yproperty]) if not np.isnan(xval) ] min_ = np.nanmin(full_list) max_ = np.nanmax(full_list) # plot setup hyplot = figure( y_range=plot.y_range, y_axis_type=y_axis_type, width=300, sizing_mode='stretch_height', background_fill_alpha=0.7, border_fill_alpha=0.7, ) hyplot.yaxis.axis_label = yproperty.replace('_', ' ').replace( '.', ' ').capitalize() + ' (%s)' % yunit hyplot.xaxis.axis_label = 'Frequency' hyplot.xaxis.axis_label_text_font_size = fontsize hyplot.yaxis.axis_label_text_font_size = fontsize # bins if xlog: bins = np.logspace(np.log10(min_), np.log10(max_), 100) else: bins = np.linspace(min_, max_, 100) # full range histogram hist, edges = np.histogram(full_list, bins=bins) hyplot.quad(right=hist, left=0, top=edges[:-1], bottom=edges[1:], color=full_colour, fill_alpha=0.5, legend_label='All') # only plotted histogram hist, edges = np.histogram(short_list, bins=bins) hyplot.quad(right=hist, left=0, top=edges[:-1], bottom=edges[1:], color=sample_colour, fill_alpha=0.5, legend_label='Shown') # legend setup hyplot.legend.click_policy = 'hide' hyplot.legend.background_fill_alpha = 0 # layouts if cproperty != 'detection_type': blank = None # don't need this plot if no legend is present if x_hist and y_hist: layout = gridplot([[hxplot, blank], [plot, hyplot]], sizing_mode='stretch_both') elif x_hist: layout = gridplot([[hxplot], [plot]], sizing_mode='stretch_both') elif y_hist: layout = gridplot([[plot, hyplot, blank]], sizing_mode='stretch_both') else: layout = gridplot([[plot]], sizing_mode='stretch_both') plots.append(layout) # layout layout = column(row(plots), sizing_mode='stretch_both') return file_html(layout, CDN)
def plot_class(x, y, names, xs, ys, names_s): red.reverse() white8 = 8 * ['#FFFFFF'] grey8 = 8 * ['#808080'] ratio = (max(y) - min(y)) / (max(x) - min(x)) size = 20 # (max(y)-min(y)) / (max(x)-min(x)) # print(ratio) p = figure(title="Hexbin for many points", match_aspect=True, aspect_scale=ratio, x_range=[min(x), max(x)], y_range=[min(y), max(y)], tools="wheel_zoom,reset", background_fill_color='#FFFFFF') p.grid.visible = False alpha = 0.8 # r, bins = p.hexbin(x, y, size=0.5, hover_color="pink", hover_alpha=0.8) df = hex_to_bin(x, y, size, ratio) dfs = hex_to_bin(xs, ys, size, ratio) # df_m2 = hex_to_bin(a, b, size=0.2) df['names'] = names dfs['names'] = names_s # df_m2['names'] = names? df = df.groupby(['q', 'r'])['names'].apply(list).reset_index(name='names') dfs = dfs.groupby(['q', 'r'])['names'].apply(list).reset_index(name='names') df = transform_df(df) dfs = transform_df(dfs) p.hex_tile(q="q", r="r", size=size, line_color=None, source=df, aspect_scale=ratio, fill_color=log_cmap('counts', grey8, 0, max(df.counts))) p.hex_tile(q="q", r="r", size=size, line_color=None, source=dfs, aspect_scale=ratio, fill_color=log_cmap('counts', red, 0, max(dfs.counts))) # r, bins = p.hexbin(x, y, size=0.5) hover = HoverTool(tooltips=[("count", "@counts"), ("name", "@names")]) # renderers=[r2] p.add_tools(hover) # output_file("plots/RPC_class_red_test1.html") show(p) return
<p>All the stations with relative water level above 1.5 are shown in the map below. DBSCAN clustering algorithm is used, the clusters are shown in the plot on the right. The color indicates relative water level, while the transparency shows the risk.</p>""" ) risky_stations_with_level = stations_level_over_threshold(stations, 1.5) risky_stations = [i[0] for i in risky_stations_with_level] if len(risky_stations) != 0: risky_source = convert_to_datasource(risky_stations) risky_source.add(["Moderate"] * len(risky_stations), name='risk') risky_source.add([0.3] * len(risky_stations), name='alpha') # building a hash table for quick search up of indices risky_name_to_indx = {i: indx for indx, i in enumerate(risky_source.data['name'])} mapper = log_cmap(field_name='relative_level', palette=Spectral10, low=1.0, high=risky_stations[0].relative_water_level()) origin2 = (52.561928, -1.464854) options2 = GMapOptions(lat=origin2[0], lng=origin2[1], map_type="roadmap", zoom=6) location_map2 = gmap(environ.get('API_KEY'), options2, title="Moderate to High Risk Stations", tools=tools, active_scroll="wheel_zoom") r2 = location_map2.circle(x="lng", y="lat", size=10, color=mapper, fill_alpha="alpha", source=risky_source) hover_tool = HoverTool(tooltips=[ ("Station Name", "@name"), ("River Name", "@river"), ("Town", "@town"), ("Latitude,Longitude", "(@lat, @lng)"), ("Typical Range (m)", "@typical_low - @typical_high"), ("Latest Level (m)", "@latest_level"), ("Risk", "@risk") ]) location_map2.add_tools(hover_tool)
def top_graph(self, ind, title, d_source, e_source, tooltip, **kwargs): """Generate the top graphs (KH, uptake, WC).""" # Generate figure dict plot_side_size = 400 fig_dict = dict(tools="pan,wheel_zoom,tap,reset,save", active_scroll="wheel_zoom", plot_width=plot_side_size, plot_height=plot_side_size, title=title) fig_dict.update(kwargs) # Create a colour mapper for number of isotherms mapper = log_cmap(field_name='{0}_n'.format(ind), palette="Viridis256", low_color='grey', high_color='yellow', low=3, high=100) # Create a new plot graph = figure(**fig_dict) # Add the hover tooltip graph.add_tools( HoverTool(names=["{0}_data".format(ind)], tooltips=tooltip.render(p=ind))) # Plot the data rend = graph.circle("{0}_x".format(ind), "{0}_y".format(ind), source=d_source, size=10, line_color=mapper, color=mapper, name="{0}_data".format(ind)) # Plot guide line graph.add_layout( Slope(gradient=1, y_intercept=0, line_color='black', line_dash='dashed', line_width=2)) # Plot the error margins graph.segment('{0}_x0'.format(ind), '{0}_y0'.format(ind), '{0}_x1'.format(ind), '{0}_y1'.format(ind), source=e_source, color="black", line_width=2, line_cap='square', line_dash='dotted') # Plot labels next to selected materials graph.add_layout( LabelSet( x='{0}_x'.format(ind), y='{0}_y'.format(ind), source=e_source, text='labels', level='glyph', x_offset=5, y_offset=5, render_mode='canvas', text_font_size='10pt', )) # Add the colorbar to the side graph.add_layout( ColorBar(color_mapper=mapper['transform'], ticker=LogTicker(desired_num_ticks=10), width=8, location=(0, 0)), 'right') return graph, rend
def plot_distributions(fit, mcmc_chains): """ Plot the fit parameters distributions. Only plot the best mcmc_chains are plotted. :param fit: a fit object. See the microlfits for more details. :param mcmc_best: a numpy array representing the best (<= 6 sigma) mcmc chains. :return: a multiple matplotlib subplot representing the parameters distributions (2D slice + histogram) :rtype: matplotlib_figure """ fig_size = [10, 10] max_plot_ticks = MAX_PLOT_TICKS dimensions = len(mcmc_chains[0]) - 1 figure_distributions, axes = plt.subplots(dimensions, dimensions, figsize=(fig_size[0], fig_size[1])) keys = list(fit.model.model_dictionnary) chains = np.copy(mcmc_chains) chains[:, 0] -= 2450000 figs = [] for i in range(len(chains[0]) - 1): figs_row = [] chain_i = chains[:, i] for j in range(len(chains[0]) - 1): chain_j = chains[:, j] hex = None if j == i: axes[j, i].hist(chain_i, 50, histtype='step') H, edges = np.histogram(chain_i, bins=50) hex = figure() hex.quad(top=H, bottom=0, left=edges[:-1], right=edges[1:]) else: axes[j, i].hist2d(chain_i, chain_j, 50, norm=LogNorm()) axes[j, i].yaxis.set_major_formatter(FormatStrFormatter('%.3f')) axes[j, i].xaxis.set_major_formatter(FormatStrFormatter('%.3f')) axes[j, i].xaxis.set_major_locator(MaxNLocator(2)) axes[j, i].yaxis.set_major_locator(MaxNLocator(2)) #axes[j, i].set_xticks([np.percentile(chain_i, 1), np.percentile(chain_i, 99)]) #axes[j, i].set_yticks([np.percentile(chain_j, 99), np.percentile(chain_j, 99)]) if j > i: figure_distributions.delaxes(axes[i, j]) if (i == 0): if j != len(chains[0]) - 2: axes[j, i].set_xticks([]) axes[j, i].set_ylabel(keys[j]) if j == len(chains[0]) - 2: if i != 0: axes[j, i].set_yticks([]) axes[j, i].set_xlabel(keys[i]) if (i != 0) and (j != len(chains[0]) - 2): axes[j, i].set_xticks([]) axes[j, i].set_yticks([]) if (i == 0) and (j == 0): axes[j, i].set_xticks([]) axes[j, i].set_yticks([]) axes[j, i].set_ylabel(None) if j < i: H, ye, xe = np.histogram2d(chain_i, chain_j, bins=50) hex = figure(x_range=(min(xe), max(xe)), y_range=(min(ye), max(ye))) hex.image(image=[np.log10(H)], x=xe[0], y=ye[0], dw=xe[-1] - xe[0], dh=ye[-1] - ye[0], color_mapper=log_cmap('counts', 'Viridis256', 0, np.max( np.log10(H)))['transform']) #bins = hexbin(chain_i, chain_j, 0.1) #hex = figure(title="Hexbin", match_aspect=True) #hex.hex_tile(q="q", r="r", size=0.1, line_color=None, source=bins, #fill_color=log_cmap('counts', 'Viridis256', 0, max(bins.counts))) if j == 0: try: hex.yaxis.axis_label = keys[i] except: pass if i == (len(chains[0]) - 2): try: hex.xaxis.axis_label = keys[j] except: pass try: hex.xaxis.major_label_orientation = np.pi / 4 except: pass figs_row.append(hex) figs.append(figs_row) figure_distributions.subplots_adjust(hspace=0.1, wspace=0.1) grid = gridplot(figs, plot_width=250, plot_height=250) show(grid) return figure_distributions
p.xaxis.axis_label_text_font_size = "20pt" p.yaxis.axis_label_text_font_size = "20pt" p.yaxis.major_label_text_font_size = "20pt" p.xaxis.major_label_text_font_size = "20pt" p.grid.visible = False p.title.text = " " p.title.align = "right" p.title.text_color = "orange" p.title.text_font_size = "25px" p.xaxis.axis_label = 'dBZ' p.yaxis.axis_label = 'Height(km)' color_mapper = LogColorMapper(palette="Spectral11", low=1, \ high=max(bins.counts)) from bokeh.transform import linear_cmap, log_cmap linearc=log_cmap('counts', 'Spectral11', 1, max(bins.counts)) p.hex_tile(q="q", r="r", size=0.5, line_color=None, source=bins, fill_color=linearc) color_mapper = LogColorMapper(palette="Spectral11", low=1, high=max(bins.counts)) color_bar = ColorBar(color_mapper=color_mapper, ticker=LogTicker(), label_standoff=12, border_line_color=None, location=(0,0)) color_bar.major_label_text_font_size="20pt" color_bar.title="Counts" color_bar.title_text_font_size="20pt" p.add_layout(color_bar, 'right') bokeh.io.export_png(p, filename="cfadKa_att.png") bokeh.io.output_file("hex_tile.html") bokeh.io.show(p)
def create_world_map_tab(): "Factory for creating first tab of app." ## Data Sources source_df, source_CDS = get_time_series_confirmed_data() ## Map color_mapper = log_cmap(field_name='number', palette=Spectral6, low=1, high=1e6) TOOLTIPS = [('Region', '@full_name'), ('Num. Cases', '@number{0,0}')] map_figure = figure(title='Confirmed COVID-19 Cases by Region', tooltips=TOOLTIPS, x_range=(-16697923.62, 18924313), y_range=(-8399737.89, 8399737.89), x_axis_type='mercator', y_axis_type='mercator', active_scroll='wheel_zoom') tile_provider = get_provider(CARTODBPOSITRON) map_figure.add_tile(tile_provider) map_figure.circle(x='web_mercator_x', y='web_mercator_y', source=source_CDS, size='sizes', color=color_mapper) ## Colorbar color_bar = ColorBar(title='Num. Cases', title_standoff=20, color_mapper=color_mapper['transform'], label_standoff=20, width=8, location=(0, 0), ticker=LogTicker()) color_bar.formatter.use_scientific = False map_figure.add_layout(color_bar, 'right') ## Slider def slider_callback(attr, old, new): delta = datetime.timedelta(milliseconds=new) date = datetime.date(1970, 1, 1) + delta date_string = date.strftime('%Y-%m-%d') try: source_CDS.data['number'] = source_df[date_string] source_CDS.data['sizes'] = source_df[date_string].apply(scale) except KeyError: pass slider = DateSlider(title='Date', start=START_DATE, end=datetime.date.today(), step=1, value=START_DATE) slider.on_change('value', slider_callback) ## Data Table columns = [ TableColumn(field='full_name', title='Region'), TableColumn(field='number', title='Cases') ] data_table = DataTable( source=source_CDS, columns=columns, ) ## Cancel Selection Button def cancel_selection_callback(): source_CDS.selected.indices = [] cancel_selection_button = Button(label='Clear Selection', button_type='warning') cancel_selection_button.on_click(cancel_selection_callback) child = row([ column([slider, map_figure]), column([data_table, cancel_selection_button]), ]) return Panel(child=child, title='World Map')
def display_data(selectedYear): yr = selectedYear df_yr = df[df['year_of_sale'] == yr] df_yr = df_yr[df_yr['sale_price'] >= 100000] return df_yr source = ColumnDataSource(data=display_data(2017)) pal = RdYlGn[6] mapper = log_cmap(field_name="sale_price", palette=pal, low=100000, low_color='green', high=23000000) tooltips = [("Price", "@sale_price"), ("Address", "@address"), ("Neighborhood", "@neighborhood")] slider = Slider(start=2003, end=2017, step=1, value=2017, title='Year') fig = figure(x_axis_type='mercator', y_axis_type='mercator', tooltips=tooltips, title='Brooklyn Residential Housing Prices, 2017') fig.add_tile(CARTODBPOSITRON) fig.circle(x='coords_x', y='coords_y', line_color=mapper,
label_standoff=12, border_line_color=None, title="Coloured by Element Frequency\n (low = purple, high = yellow)", location=(0, 0), major_label_text_color='black') p.add_layout(obj=color_bar, place='above') r = p.rect("group", "period", 0.95, 0.95, source=df, fill_alpha=0.6, color=log_cmap('count', palette=palettes.Plasma256, low=0, high=max(df['count']))) text_props = {"source": df, "text_align": "left", "text_baseline": "middle"} x = dodge("group", -0.4, range=p.x_range) p.text(x=x, y="period", text="symbol", text_font_style="bold", **text_props) p.text(x=x, y=dodge("period", 0.3, range=p.y_range), text="atomic number", text_font_size="10pt", **text_props) # p.text(x=x, y=dodge("period", -0.35, range=p.y_range), text="name",
def launch(network): callback_id = None # Labels for the play/pause button in paused and playing states # respectively BUTTON_LABEL_PAUSED = '► Start Pollution' BUTTON_LABEL_PLAYING = '❚❚ Pause' # Node scaling factor NODE_SCALING = 15 # Color of injection node (Light blue) # (color used by injection button, update in CSS too on change) injection_color = "#34c3eb" # Color of selected node (bright green) # (color used by highlight button, update in CSS too on change) highlight_color = "#07db1c" # Color of selected node type type_highlight_color = "purple" def update_highlights(): """Set the color and width for each node and edge in the graph.""" # Widths for outlines of highlighted and normal nodes highlight_width = 3.0 normal_width = 2.0 # Create a default dictionary for node types, any node with a type not # in the dictionary gets the default color colors = defaultdict(lambda: "magenta") colors.update({ 'Junction': 'gray', 'Reservoir': 'orange', 'Tank': 'green' }) injection = pollution_injection_select.value node_to_highlight = pollution_history_select.value type_highlight = node_type_select.value outline_colors = [] outline_widths = [] for node in G.nodes(): if node == injection: # Color injection node the injection color outline_colors.append(injection_color) outline_widths.append(highlight_width) elif node == node_to_highlight: # Color selected node bright green outline_colors.append(highlight_color) outline_widths.append(highlight_width) else: # Otherwise color based on the node type node_type = G.nodes[node]['type'] if node_type == type_highlight: outline_colors.append(type_highlight_color) else: outline_colors.append(colors[node_type]) outline_widths.append(normal_width) # Set colors for edges so that those connected to a colored node # are also that color, to increase visibility edge_colors = [] edge_widths = [] highlight_edge_width = shadow_width + 1.0 for edge in G.edges(): if edge[0] == injection or edge[1] == injection: edge_colors.append(injection_color) edge_widths.append(highlight_edge_width) elif edge[0] == node_to_highlight or edge[1] == node_to_highlight: edge_colors.append(highlight_color) edge_widths.append(highlight_edge_width) else: type1 = G.nodes[edge[0]]['type'] type2 = G.nodes[edge[1]]['type'] if type1 == type_highlight or type2 == type_highlight: edge_colors.append(type_highlight_color) else: edge_colors.append('gray') edge_widths.append(shadow_width) data = graph.node_renderer.data_source.data data['line_color'], data['line_width'] = outline_colors, outline_widths edge_data = graph_shadow.edge_renderer.data_source.data edge_data['line_color'], edge_data['line_width'] = (edge_colors, edge_widths) def update_pollution_history(): history_node = pollution_history_select.value history = pollution_history(scenario, history_node) # Set these at the same time to avoid bokeh user error pollution_history_source.data = { 'time': history.index, 'pollution_value': history.values } if history_node != 'None': y_end = max(history.values) if y_end == 0: # Bokeh can't render the plot correctly y_end = 1 # when the max value is 0 pollution_history_plot.x_range.update(start=0, end=max(history.index)) pollution_history_plot.y_range.update(start=0, end=y_end) else: pollution_history_plot.x_range.update(start=0, end=0) pollution_history_plot.y_range.update(start=0, end=0) def update(): """Update the appearance of the pollution dynamics network, including node and edge colors""" timestep = time_slider.value # Get pollution for each node for the given injection site and timestep series = pollution_series(scenario, timestep) data = graph.node_renderer.data_source.data # Set the timer text timer.text = timer_html(timestep) # Update node colours data['colors'] = list(series) # Update edge colours edge_values = [] for node1, node2 in G.edges(): node1_pollution = series[node1] node2_pollution = series[node2] # The edge color should be the mean of the connected nodes # except when one node is zero, which indicates pollution # is yet to spread through that edge (pipe) if node1_pollution == 0 or node2_pollution == 0: edge_values.append(0) else: edge_values.append((node1_pollution + node2_pollution) / 2.) graph.edge_renderer.data_source.data['colors'] = edge_values # Update timestep span on pollution history plot timestep_span.update(location=timestep) def update_pollution_history_node(attrname, old, new): """Select node to show pollution history for and highlight it green.""" update_highlights() update_pollution_history() history_node = new html = pollution_history_html(history_node, highlight_color) pollution_history_node_div.text = html title = "Pollution History Plot for Node " + history_node pollution_history_plot.title = Title(text=title) if old == "None" and new != "None": # Include history plot in layout for the graph and widgets new_layout_row = row(menu_bar, plot, pollution_history_plot, sizing_mode="stretch_both") layout.children[0] = new_layout_row if old != "None" and new == "None": # Remove history plot from layout for the graph and widgets layout.children[0] = layout_row def update_click_node(event): """Tap tool event action. This function will either change the selected pollution history node, or pollution injection node depending on the value of the 'what_click_does' radio group. """ nodes_clicked_ints = graph.node_renderer.data_source.selected.indices # It's possible to click multiple nodes when they overlap, but we only # want one first_clicked_node_int = nodes_clicked_ints[0] clicked_node = list(G.nodes())[first_clicked_node_int] if what_click_does.active == click_options['Pollution History Plot']: pollution_history_select.value = clicked_node if what_click_does.active == click_options['Pollution Injection Node']: pollution_injection_select.value = clicked_node def update_node_type_highlight(attrname, old, new): """Highlight node type drop down callback. As node colours depend on many widget values, this callback simply calls the update highlights function.""" update_highlights() node_type = node_type_select.value type_div.text = node_type_html(node_type, type_highlight_color) def update_time_slider(attrname, old, new): """Time slider callback. As node colours depend on many widget values, this callback simply calls the update function.""" update() def update_injection(attrname, old, new): """Pollution injection node location drop down callback. The nonlocal variable scenario, which holds the dataframe of pollution dynamics is updated. As the injection site affects both the node highlights and pollution data, his callback calls both the update highlights and the update functions""" nonlocal scenario scenario = pollution_scenario(pollution, new) update_highlights() update_pollution_history() update() injection_node = pollution_injection_select.value pollution_location_div.text = pollution_location_html( injection_node, injection_color) def update_node_size(attrname, old, new): """Node size slider callback. Updates the base size of the nodes in the graph""" graph.node_renderer.data_source.data['size'] = ( new + all_base_demands * NODE_SCALING) def step(): """Move the time slider by one step""" timestep = time_slider.value + step_size if timestep > end_step: timestep = start_step time_slider.value = timestep def animate(): """Move the time slider at animation_speed ms/frame on play button click""" nonlocal callback_id nonlocal animation_speed if play_button.label == BUTTON_LABEL_PAUSED: play_button.label = BUTTON_LABEL_PLAYING callback_id = curdoc().add_periodic_callback(step, animation_speed) elif play_button.label == BUTTON_LABEL_PLAYING: play_button.label = BUTTON_LABEL_PAUSED curdoc().remove_periodic_callback(callback_id) def update_speed(attrname, old, new): """Adjust the animation speed""" nonlocal callback_id nonlocal animation_speed # Update animation speed animation_speed = speeds[new] # If animation is playing recreate the periodic callback if play_button.label == BUTTON_LABEL_PLAYING: curdoc().remove_periodic_callback(callback_id) callback_id = curdoc().add_periodic_callback(step, animation_speed) def plot_bounds(locations): # Get lists of node locations xs = [coord[0] for coord in locations.values()] ys = [coord[1] for coord in locations.values()] # Find minimum and maximum coordinates x_max, x_min = max(xs), min(xs) y_max, y_min = max(ys), min(ys) # Add padding to boundary x_extra_range = (x_max - x_min) / 20 y_extra_range = (y_max - y_min) / 20 x_lower = x_min - x_extra_range x_upper = x_max + x_extra_range y_lower = y_min - y_extra_range y_upper = y_max + y_extra_range return Range1d(x_lower, x_upper), Range1d(y_lower, y_upper) G, locations, all_base_demands, include_map = load_water_network(network) (pollution, injection_nodes, start_node, start_step, end_step, step_size, max_pol, min_pol) = load_pollution_dynamics(network) # Create figure object x_bounds, y_bounds = plot_bounds(locations) plot = figure(x_range=x_bounds, y_range=y_bounds, active_scroll='wheel_zoom', x_axis_type="mercator", y_axis_type="mercator", min_border_bottom=50) # Add map to plot if specified if include_map: tile_provider = get_provider(Vendors.CARTODBPOSITRON) plot.add_tile(tile_provider) # Create bokeh graph from the NetworkX object graph = from_networkx(G, locations) # Define color map for pollution color_mapper = log_cmap('colors', cc.CET_L18, min_pol, max_pol) # Create nodes, set the node colors by pollution level and size # by base demand. Node outline color and thickness is different # for the pollution start node # Create node glyphs graph.node_renderer.glyph = Circle(size="size", fill_color=color_mapper, line_color="line_color", line_width="line_width") graph.node_renderer.nonselection_glyph = Circle(size="size", fill_color=color_mapper, line_color="line_color", line_width="line_width") # Add color bar as legend color_bar = ColorBar(color_mapper=color_mapper['transform'], ticker=LogTicker(), label_standoff=12, location=(0, 0)) plot.add_layout(color_bar, 'right') # Create edges edge_width = 3.0 graph.edge_renderer.glyph = MultiLine(line_width=edge_width, line_color=color_mapper) # Create 'shadow' of the network edges so that they stand out # against the map graph_shadow = from_networkx(G, locations) shadow_width = edge_width * 1.5 graph_shadow.edge_renderer.glyph = MultiLine(line_width="line_width", line_color="line_color") graph_shadow.node_renderer.glyph = Circle(size=0.1, fill_color="black", line_color="black", line_width=1) # Green hover for both nodes and edges hover_color = '#abdda4' graph.node_renderer.hover_glyph = Circle(size="size", fill_color=hover_color, line_color="line_color", line_width="line_width") graph.edge_renderer.hover_glyph = MultiLine(line_color=hover_color, line_width=edge_width) # When we hover over nodes, highlight adjacent edges too graph.selection_policy = NodesAndLinkedEdges() graph.inspection_policy = NodesAndLinkedEdges() # Add the network to plot plot.renderers.append(graph_shadow) plot.renderers.append(graph) # Show node names and type (e.g. junction, tank) on hover TOOLTIPS = [("Type", "@type"), ("Name", "@name"), ("Position", "@pos"), ("Elevation", "@elevation"), ("Connected", "@connected"), ("Base Demand", "@demand"), ("Pollution Level", "@colors")] plot.add_tools(HoverTool(tooltips=TOOLTIPS), TapTool()) # Set clicking a node to choose pollution history plot.select(type=TapTool) plot.on_event(Tap, update_click_node) # Pollution history plot pollution_history_source = ColumnDataSource( data=dict(time=[], pollution_value=[])) pollution_history_plot = figure(x_range=Range1d(0, 0), y_range=Range1d(0, 0), toolbar_location=None, min_border_bottom=50) pollution_history_plot.line('time', 'pollution_value', source=pollution_history_source, line_width=2.0) timestep_span = Span(location=0, dimension='height', line_dash='dashed', line_width=2.0) pollution_history_plot.add_layout(timestep_span) # Slider to change the timestep of the pollution data visualised time_slider = Slider(start=0, end=end_step, value=36000, step=step_size, title="Time (s)") time_slider.on_change('value', update_time_slider) # Play button to move the slider for the pollution timeseries play_button = Button(label=BUTTON_LABEL_PAUSED, button_type="success") play_button.on_click(animate) # Menu to highlight nodes green and display pollution history pollution_history_select = Select(title="Pollution History Plot Node", value="None", options=['None'] + list(G.nodes())) pollution_history_select.on_change('value', update_pollution_history_node) # Create a div to show the name of pollution history node pollution_history_node_div = Div(text=pollution_history_html()) # Dropdown menu to highlight a node type node_type_select = Select( title="Highlight Node Type", value='None', options=['None', 'Reservoir', 'Tank', 'Junction']) node_type_select.on_change('value', update_node_type_highlight) # Create a div to show the selected node type to highlight type_div = Div(text=node_type_html()) # Dropdown menu to choose pollution start location pollution_injection_select = Select(title="Pollution Injection Node", value=injection_nodes[0], options=injection_nodes) pollution_injection_select.on_change('value', update_injection) # Create a div to show the name of pollution start node injection_node = pollution_injection_select.value pol_html = pollution_location_html(injection_node, injection_color) pollution_location_div = Div(text=pol_html) # Dropdown menu to choose node size and demand weighting initial_node_size = 8 graph.node_renderer.data_source.data['size'] = ( initial_node_size + all_base_demands * NODE_SCALING) node_size_slider = Slider(start=5, end=20, value=initial_node_size, step=1, title="Base Node Size") node_size_slider.on_change('value', update_node_size) # Animation speeds in ms per frame speeds = [1000, 500, 100] speed_menu = ['Slow', 'Medium', 'Fast'] # Speed radio group widget speed_radio = RadioGroup(labels=speed_menu, active=1) speed_radio.on_change('active', update_speed) # Create a div for the timer timer = Div(text="") # Create a radio button to choose what clicking a node does click_options_menu = ['Pollution History Plot', 'Pollution Injection Node'] click_options = dict(zip(click_options_menu, [0, 1])) what_click_does = RadioGroup( labels=click_options_menu, active=click_options['Pollution History Plot']) # Create a div to go within the below menu that describes pollution spread info = "<p>Pollution Spread Scenario:</p>" info += "<p><i>Pollution injected at 10-11hr</i></p>" pollution_spread_info = Div(text=info) # Create menu bar menu_bar = column(network_select, row(pollution_history_select, pollution_history_node_div, sizing_mode="scale_height"), row(pollution_injection_select, pollution_location_div, sizing_mode="scale_height"), Div(text="Clicking a Node selects it as:"), what_click_does, row(node_type_select, type_div, sizing_mode="scale_height"), node_size_slider, pollution_spread_info, row(play_button, speed_radio, sizing_mode="scale_height"), time_slider, timer, width=220, sizing_mode="stretch_height") # Add the plots to a row, intially excluding pollution_history_plot layout_row = row(menu_bar, plot, sizing_mode="stretch_both") # Create the layout for the graph and widgets layout = column(layout_row, sizing_mode="stretch_both") # Initialise scenario = pollution_scenario(pollution, pollution_injection_select.value) animation_speed = speeds[speed_radio.active] update_pollution_history() update_highlights() update() curdoc().clear() curdoc().add_root(layout) curdoc().title = "Water Network Pollution"