def test_Quadratic(): glyph = Quadratic() assert glyph.x0 == "x0" assert glyph.y0 == "y0" assert glyph.x1 == "x1" assert glyph.y1 == "y1" assert glyph.cx == "cx" assert glyph.cy == "cy" yield check_line, glyph yield check_props, glyph, ["x0", "y0", "x1", "y1", "cx", "cy"], LINE
def test_Quadratic() -> None: glyph = Quadratic() assert glyph.x0 == field("x0") assert glyph.y0 == field("y0") assert glyph.x1 == field("x1") assert glyph.y1 == field("y1") assert glyph.cx == field("cx") assert glyph.cy == field("cy") check_line_properties(glyph) check_properties_existence(glyph, [ "x0", "y0", "x1", "y1", "cx", "cy", ], LINE, GLYPH)
def test_Quadratic(): glyph = Quadratic() assert glyph.x0 is None assert glyph.y0 is None assert glyph.x1 is None assert glyph.y1 is None assert glyph.cx is None assert glyph.cy is None check_line_properties(glyph) check_properties_existence(glyph, [ "x0", "y0", "x1", "y1", "cx", "cy", ], LINE, GLYPH)
def test_Quadratic(): glyph = Quadratic() assert glyph.x0 is None assert glyph.y0 is None assert glyph.x1 is None assert glyph.y1 is None assert glyph.cx is None assert glyph.cy is None yield check_line, glyph yield (check_props, glyph, [ "x0", "y0", "x1", "y1", "cx", "cy", ], LINE)
ydr = DataRange1d() plot = Plot(title=None, x_range=xdr, y_range=ydr, plot_width=300, plot_height=300, h_symmetry=False, v_symmetry=False, min_border=0, toolbar_location=None) glyph = Quadratic(x0="x", y0="y", x1="xp02", y1="y", cx="xp01", cy="yp01", line_color="#4DAF4A", line_width=3) plot.add_glyph(source, glyph) xaxis = LinearAxis() plot.add_layout(xaxis, 'below') yaxis = LinearAxis() plot.add_layout(yaxis, 'left') plot.add_layout(Grid(dimension=0, ticker=xaxis.ticker)) plot.add_layout(Grid(dimension=1, ticker=yaxis.ticker)) doc = Document()
def draw(self): from bokeh.plotting import figure if self._data['time_set'].instantaneous: min_time = min(self._data['time_set']) max_time = max(self._data['time_set']) else: min_time = min(k[0] for k in self._data['time_set']) max_time = max(k[1] for k in self._data['time_set']) # get nodes nodes = sorted([str(n) for n in self._data['node_set']]) # map them to numbers ln = dict(enumerate(nodes)) nl = {n: i for i, n in enumerate(nodes)} pallete = self._make_pallete(len(self._data['temporal_linkset'])) # width height w, h = (max_time - min_time) * 70, len(nodes) * 50 division = (1 if self._data['temporal_nodeset'].discrete else 0.1) division = self.map_time(min_time + division) - self.map_time(min_time) min_time, max_time = self.map_time(min_time), self.map_time(max_time) # plot nodes node_points, time_points = [], [] if self._data['temporal_nodeset'].instantaneous: for (u, ts) in self._data['temporal_nodeset']: ts = self.map_time(ts) if ts >= min_time and ts <= max_time: # add circular points node_points.append(nl[str(u)]) time_points.append(ts) else: for k in self._data['temporal_nodeset']: for ts in np.arange(max(self.map_time(k[1]), min_time), min(self.map_time(k[2]), max_time), division): node_points.append(nl[str(k[0])]) time_points.append(ts) # plot links height = 0.5 data, nan, l, b = self._data['temporal_linkset'], float('nan'), [], [] if len(data): for ls, color in zip(data, pallete): lp_x, lp_y = [], [] x0, y0, x1, y1, cx, cy = [], [], [], [], [], [] if ls.instantaneous: for (u, v, ts) in ls: u, v = str(u), str(v) ts = self.map_time(ts) yu, yv = sorted((nl[u], nl[v])) cr = curving(yu, yv) * division * 0.7 xm = ts + cr ym = (yu + yv) * height if cr > 0.: x0.append(ts) x1.append(ts) y0.append(yu) y1.append(yv) cx.append(xm) cy.append(ym) else: lp_x += [ts, ts, nan] lp_y += [yu, yv, nan] else: for k in ls: u, v = str(k[0]), str(k[1]) ts, tf = k[2:4] ts = self.map_time(ts) tf = self.map_time(tf) yu, yv = sorted((nl[u], nl[v])) cr = curving(yu, yv) * division * 0.7 xm = ts + cr ym = (yu + yv) * height if cr > 0.: x0.append(ts) x1.append(ts) y0.append(yu) y1.append(yv) cx.append(xm) cy.append(ym) if yu + yv == 2 * ym: t = 0.5 else: t = (sqrt( (yu - ym) * (ym - yv)) + yu - ym) / (yu - 2 * ym + yv) assert 0 <= t <= 1 xm = ( (1 - t)**2 + t**2) + 2 * t * (1 - t) * (xm + 1) else: lp_x += [ts, ts, nan] lp_y += [yu, yv, nan] lp_x += [xm, tf, nan] lp_y += [ym, ym, nan] l.append(((lp_x, lp_y), color)) b.append(((x0, y0, x1, y1, cx, cy), color)) aspect_scale = w / float(h) if self.date_map is None: self.p = figure(aspect_scale=aspect_scale) if self._data['time_set'].instantaneous: self.p.xaxis.ticker = list(self._data['time_set']) elif self._data['time_set'].discrete: self.p.xaxis.ticker = [ t for ts, tf in self._data['time_set'] for t in range(ts, tf + 1) ] else: assert callable(self.date_map) or isinstance(self.date_map, dict) self.p = figure(aspect_scale=aspect_scale, x_axis_type="datetime") if self.y_axis_label is not None: self.p.yaxis.axis_label = str(self.y_axis_label) self.p.xaxis.axis_label = (str(self.x_axis_label) if self.x_axis_label is not None else 'time') self.p.yaxis.ticker = list(range(len(nodes))) self.p.yaxis.major_label_overrides = ln from bokeh.models import ColumnDataSource from bokeh.models.glyphs import Quadratic self.p.circle(time_points, node_points, size=8, color="black") for ((lp_x, lp_y), c) in l: self.p.line(lp_x, lp_y, line_width=2, color='black') for ((x0, y0, x1, y1, cx, cy), c) in b: data = dict(x0=x0, y0=y0, x1=x1, y1=y1, cx=cx, cy=cy) self.p.add_glyph( ColumnDataSource(data), Quadratic(x0='x0', y0='y0', x1='x1', y1='y1', cx='cx', cy='cy', line_color=c, line_width=2))
def add_path(self, source_label, links=None, edge_type='curved', tooltips=None, legend=None, **kwargs): """ Connect all points in the datasource in a path (to show order). `self.prepare_plot` must have been called previous to this. Parameters: source_label (str): string corresponding to a label previously called in `self.add_source` order_col: the column of a data source specifying the order of the records tooltips: string or list of tuples (passed to Bokeh HoverTool) legend (str): name to assign to this layer in the plot legend kwargs: options passed to the objected for `bokeh_model` This method allows two special kwargs: 'color' and 'alpha'. When used with a bokeh model that has 'fill_color' and 'line_color' and 'fill_alpha' and 'line_alpha' properties, calling the special kwarg will use the same value for both. """ self._validate_workflow('add_path') source = self.sources[source_label].copy() suffix = '_transform' if type(self.plot) != GMapPlot else '' x_point_label = 'x_coord_point{}'.format(suffix) y_point_label = 'y_coord_point{}'.format(suffix) if all(isinstance(x, (int, float)) for x in source[links].tolist()): x1 = source.sort_values(links)[x_point_label].values y1 = source.sort_values(links)[y_point_label].values x2 = x1[1:] y2 = y1[1:] x1 = x1[:-1] y1 = y1[:-1] x3 = (x1 + x2) / 2 y3 = (y1 + y2) / 2 xc = x3 + abs(y3 - y2) yc = y3 + abs(x3 - x2) new_source = { 'x1': x1, 'x2': x2, 'xc': xc, 'y1': y1, 'y2': y2, 'yc': yc } for c in source.columns: if (c not in self.omit_columns) and c not in new_source: new_source[c] = source[c].values[:-1] elif all( isinstance(x, (list, tuple, set)) for x in source[links].tolist()): if 'uid' not in source.columns: raise ValueError( 'Source must contain column `uid` when links is a list of iterables.' ) nodes = source['uid'].tolist() edges = source[links].tolist() node_x = source.set_index('uid')[x_point_label].to_dict() node_y = source.set_index('uid')[y_point_label].to_dict() a_vals, x1, x2, y1, y2 = zip(*[(a, node_x[a], node_x[b], node_y[a], node_y[b]) for a, bs in zip(nodes, edges) for b in bs]) x1, x2, y1, y2 = array(x1), array(x2), array(y1), array(y2) x3 = (x1 + x2) / 2 y3 = (y1 + y2) / 2 xc = x3 + abs(y3 - y2) yc = y3 + abs(x3 - x2) new_source = { 'x1': x1, 'x2': x2, 'xc': xc, 'y1': y1, 'y2': y2, 'yc': yc } for c in source.columns: if (c not in self.omit_columns) and c not in new_source: col_dict = source.set_index('uid', drop=False)[c].to_dict() new_source[c] = [col_dict[v] for v in a_vals] else: raise ValueError( 'Values of `links` field must be numeric or a list, set, or tuple of values from the `uid` field.' ) if 'color' in kwargs.keys(): color = kwargs.pop('color') for v in Quadratic.dataspecs(): if 'color' in v: kwargs[v] = color if 'alpha' in kwargs.keys(): alpha = kwargs.pop('alpha') for v in Quadratic.dataspecs(): if 'alpha' in v: kwargs[v] = alpha if edge_type == 'curved': model_object = Quadratic(x0='x1', y0='y1', x1="x2", y1="y2", cx="xc", cy="yc", name=source_label, **kwargs) elif edge_type == 'straight': model_object = Segment(x0='x1', y0='y1', x1="x2", y1="y2", name=source_label, **kwargs) else: raise ValueError( 'Keyword `edge_type` must be either "curved" or "straight".') source = ColumnDataSource(new_source, name=source_label) rend = self.plot.add_glyph(source, model_object) if legend is not None: li = LegendItem(label=legend, renderers=[rend]) self.legend.items.append(li) if tooltips is not None: self.plot.add_tools(HoverTool(tooltips=tooltips, renderers=[rend])) return self