def add_china_map_2basemap(ax,name ="province", facecolor='none', edgecolor='c', lw=2, encoding='utf-8', **kwargs): """ Add china province boundary to basemap instance. :param mp: basemap instance. :param ax: matplotlib axes instance. :param name: map name. :param facecolor: fill color, default is none. :param edgecolor: edge color. :param lw: line width. :param kwargs: keywords passing to Polygon. :return: None. """ # map name names = {'nation': "bou1_4p", 'province': "bou2_4p", 'county': "BOUNT_poly", 'river': "hyd1_4p", 'river_high': "hyd2_4p"} # get shape file and information shpfile = pkg_resources.resource_filename( 'meteva', "resources/maps/" + names[name]) shp1 = readshapefile(shpfile, default_encoding=encoding) lines = LineCollection(shp1,antialiaseds=(1,)) lines.set_color(edgecolor) lines.set_linewidth(lw) lines.set_label('_nolabel_') ax.add_collection(lines)
def plot_multicolor_line(splot, x, y, z, cmap=None, vmin=None, vmax=None, lw=None, label=None, off_x=0, off_y=0): """ Adds a line to a plot that changes color based on the z-value and a cmap (default=viridis) """ # Based on the matplotlib example: https://matplotlib.org/gallery/lines_bars_and_markers/multicolored_line.html points = np.array([x, y]).T.reshape(-1, 1, 2) segments = np.concatenate([points[:-1], points[1:]], axis=1) xo = np.ones(len(segments)) * off_x yo = np.ones(len(segments)) * off_y # xyo = list(zip(xo, yo)) xyo = np.array([xo, yo]).T lc = LineCollection(segments, cmap=cmap, offsets=xyo, transOffset=matplotlib.transforms.IdentityTransform()) lc.set_array(z) lc.set_clim(vmin=vmin, vmax=vmax) if lw is not None: lc.set_linewidth(lw) if label is not None: lc.set_label(label) return splot.add_collection(lc)
def loadlines(self, name, curdir='beijingJson', zorder=None, linewidth=0.5, color='k', antialiased=1, ax=None, default_encoding='utf-8', linestyle='-', linesalpha=1): # get current axes instance (if none specified). filename = curdir + '/' + name + '.json' coords = json.load(codecs.open(filename, 'r', 'utf-8')) coords = self.projectcoords(coords) ax = ax or self._check_ax() # make LineCollections for each polygon. lines = LineCollection(coords, antialiaseds=(1, )) lines.set_color(color) lines.set_linewidth(linewidth) lines.set_linestyle(linestyle) lines.set_alpha(linesalpha) lines.set_label('_nolabel_') if zorder is not None: lines.set_zorder(zorder) ax.add_collection(lines) # set axes limits to fit map region. self.set_axes_limits(ax=ax) # clip boundaries to map limbs lines, c = self._cliplimb(ax, lines) self.__dict__[name] = coords return lines
def draw_swap_gate(axes, gate_pos, wire_pos1, wire_pos2, plot_params): """ Draws the symbol for a SWAP gate. Args: axes (matplotlib.axes.Axes): axes object x (float): x coordinate [data units] y1 (float): y coordinate of the 1st qubit wire y2 (float): y coordinate of the 2nd qubit wire plot_params (dict): plot parameters """ delta = plot_params['swap_delta'] lines = [] for wire_pos in (wire_pos1, wire_pos2): lines.append([(gate_pos - delta, wire_pos - delta), (gate_pos + delta, wire_pos + delta)]) lines.append([(gate_pos - delta, wire_pos + delta), (gate_pos + delta, wire_pos - delta)]) lines.append([(gate_pos, wire_pos1), (gate_pos, wire_pos2)]) gate = LineCollection(lines, colors='k', linewidths=plot_params['linewidth']) gate.set_label('SWAP') axes.add_collection(gate)
def add_trajectory(fig_lung, name, color_map, line_label, show_predicted, data): plt.figure(fig_lung.number) # set fig as current figure if show_predicted: T = len(data.dx_predicted) for i in range(T): if i % 50 == 0: plt.plot(data.dx_predicted[i][:, 1], data.dx_predicted[i][:, 0], '.-', color = purple, alpha = 0.1, label = 'predicted trajectory') y = data.x_sensed[:,1] - data.x_sensed[0,1] x = data.x_sensed[:,0] - data.x_sensed[0,0] t = np.linspace(2, 10, len(y)) points = np.array([y, x]).T.reshape(-1, 1, 2) segments = np.concatenate([points[:-1], points[1:]], axis=1) lc = LineCollection(segments, cmap=color_map, norm=plt.Normalize(0, 10)) lc.set_array(t) lc.set_linewidth(3) lc.set_label(line_label) plt.gca().add_collection(lc) plt.plot(data.x_desired[:,1] - data.x_sensed[0,1], data.x_desired[:,0] - data.x_sensed[0,0], 'g.', label = 'desired') point_count = 0 last_xd = np.array([]) offset_y = 0.75 offset_x = 1 for i, xd in enumerate(data.x_desired): if not np.array_equal(xd, last_xd): plt.text(xd[1] + offset_y, xd[0] + offset_x, str(point_count), color = green) last_xd = xd.copy() point_count += 1 ax = plt.gca() handles, labels = ax.get_legend_handles_labels() newLabels, newHandles = [], [] for handle, label in zip(handles, labels): if label not in newLabels: newLabels.append(label) newHandles.append(handle) plt.legend(newHandles, newLabels, loc = 'upper left', bbox_to_anchor = [1,1]) legend = ax.get_legend() for i, name in enumerate(newLabels): if name.lower() == 'ukf + mpc': legend.legendHandles[i].set_color(blue) elif name.lower() == 'mlc': legend.legendHandles[i].set_color(red) elif name.lower() == 'tip trajectory': legend.legendHandles[i].set_color(purple)
def plot_magnetic_trace(self, fig, x, y): plt.figure(fig.number) t = np.linspace(2, 10, len(x)) points = np.array([x,y]).T.reshape(-1, 1, 2) segments = np.concatenate([points[:-1], points[1:]], axis=1) lc = LineCollection(segments, cmap=blues_cmap, norm=plt.Normalize(0, 10)) lc.set_array(t) lc.set_linewidth(3) lc.set_label('sensed position') plt.gca().add_collection(lc) return fig
def plot(self, plot, col): # Set the scale of the plot if self.log_x: plot.xscale('log') if self.log_y: plot.yscale('log') # Process the values values = [] for x in self.get_x_data_gen().get_values(): for y in self.get_y_data_gen().get_values(): values.append(zip(x,y)) lines = LineCollection(values) lines.set_color(col) lines.set_label(str(self.get_name())) # Plot the values plot.add_collection(lines)
def draw_wires(axes, n_labels, gate_grid, wire_grid, plot_params): """ Draws all the circuit qubit wires. Args: axes (matplotlib.axes.Axes): axes object n_labels (int): number of qubit gate_grid (ndarray): array with the ref. x positions of the gates wire_grid (ndarray): array with the ref. y positions of the qubit wires plot_params (dict): plot parameters """ # pylint: disable=invalid-name lines = [] for i in range(n_labels): lines.append(((gate_grid[0] - plot_params['column_spacing'], wire_grid[i]), (gate_grid[-1], wire_grid[i]))) all_lines = LineCollection(lines, linewidths=plot_params['linewidth'], edgecolor='k') all_lines.set_label('qubit_wires') axes.add_collection(all_lines)
class DecayLine(object): def __init__(self, n_points, tail_length, rgb_color, zorder=2, label=None): self.n_points = int(n_points) self.tail_length = int(tail_length) self.rgb_color = rgb_color self._zorder = zorder self.lc = None self._label = label self.dbg = True def __str__(self): if not hasattr(self, 'points') or not hasattr(self, 'segments'): return "" res = "DecayLine:" + "\n\t{}".format(self.segments) return res def set_data(self, x=None, y=None): if x is None or y is None: self.lc = LineCollection([], linewidths=1.0, zorder=self._zorder) else: # ensure we don't start with more points than we want x = x[-self.n_points:] y = y[-self.n_points:] # create a list of points with shape (len(x), 1, 2) # array([[[ x0 , y0 ]], # [[ x1 , y1 ]], # ..., # [[ xn , yn ]]]) #self.points = np.array([x, y]).T.reshape(-1, 1, 2) if not hasattr(self, 'points'): self.points = deque([[x[i], y[i]] for i in range(len(x))]) else: for i in range(len(x)): self.points.append([x[i], y[i]]) # group each point with the one following it (shape (len(x)-1, 2, 2)): # array([[[ x0 , y0 ], # [ x1 , y1 ]], # [[ x1 , y1 ], # [ x2 , y2 ]], # ... # self.segments = np.concatenate([self.points[:-1], self.points[1:]], axis=1) if len(self.points) > 1: pts = np.array(self.points).reshape(-1, 1, 2) self.segments = np.concatenate([pts[:-1], pts[1:]], axis=1) if hasattr(self, 'alphas'): del self.alphas if hasattr(self, 'rgba_colors'): del self.rgba_colors if self.lc is None: self.lc = LineCollection( self.segments, colors=self.get_colors(), linewidths=1.0, zorder=self._zorder, label="" if self._label is None else self._label) self.lc.set_segments(self.segments) colours = self.get_colors() if self.dbg: print(colours) self.dbg = False self.lc.set_color(colours) self.lc.set_edgecolors(colours) # self.lc.set_facecolors(colours) else: if self.lc is None: self.lc = LineCollection( [], linewidths=1.0, colors=(self.rgb_color[0], self.rgb_color[1], self.rgb_color[2], 1.0), zorder=self._zorder, label="" if self._label is None else self._label) def get_LineCollection(self): if not hasattr(self, 'lc'): self.set_data() return self.lc def get_label(self): """Return the label used for this artist in the legend.""" if self.lc is None: return "" if self._label is None else self._label return self.lc.get_label() def set_label(self, s): self._label = s if self.lc is not None: self.lc.set_label(self._label) def add_point(self, x, y): if not hasattr(self, 'points') or not hasattr(self, 'segments'): self.set_data([x], [y]) else: # TODO: could use a circular buffer to reduce memory operations... # self.segments = np.concatenate((self.segments,[[self.points[-1][0],[x,y]]])) # self.points = np.concatenate((self.points, [[[x,y]]])) self.points.append([x, y]) # remove points if necessary: while len(self.points) > self.n_points: self.points.popleft() pts = np.array(self.points).reshape(-1, 1, 2) self.segments = np.concatenate([pts[:-1], pts[1:]], axis=1) self.lc.set_segments(self.segments) colours = self.get_colors() self.lc.set_color(colours) self.lc.set_edgecolors(colours) # self.lc.set_facecolors(colours) @property def npoints(self): if not hasattr(self, 'points'): return 0 else: #return self.points.shape[0] return len(self.points) def get_alphas(self): n_segments = self.n_points - 1 n = len(self.segments) # n = self.points.shape[0] if n < n_segments: rest_length = n_segments - self.tail_length if n <= rest_length: return np.ones(n) else: tail_length = n - rest_length tail = np.linspace(1. / tail_length, 1., tail_length) rest = np.ones(rest_length) return np.concatenate((tail, rest), axis=None) else: # n == n_segments if not hasattr(self, 'alphas'): tail = np.linspace(1. / self.tail_length, 1., self.tail_length) rest = np.ones(n_segments - self.tail_length) self.alphas = np.concatenate((tail, rest), axis=None) return self.alphas def get_colors(self): n_segments = self.n_points - 1 n = len(self.segments) if n < 2: # return [self.rgb_color+[1.] for i in range(n)] colour = None if isinstance(self.rgb_color, np.ndarray): colour = np.concatenate([self.rgb_color, [ 1.0, ]]) elif type(self.rgb_color) in [list, tuple]: colour = self.rgb_color + [1.] return [colour for i in range(n)] if n < n_segments: alphas = self.get_alphas() rgba_colors = np.zeros((n, 4), dtype=np.float32) # first place the rgb color in the first three columns rgba_colors[:, 0:3] = self.rgb_color[:] # and the fourth column needs to be your alphas rgba_colors[:, 3] = alphas # self.rgba_colors = rgba_colors # return self.rgba_colors return rgba_colors else: if hasattr(self, 'rgba_colors'): pass else: alphas = self.get_alphas() rgba_colors = np.zeros((n, 4), dtype=np.float32) # first place the rgb color in the first three columns rgba_colors[:, 0:3] = self.rgb_color[:] # and the fourth column needs to be your alphas rgba_colors[:, 3] = alphas self.rgba_colors = rgba_colors return self.rgba_colors get_color = get_colors # for compatibility with old versions
G = nx.read_gpickle(args['graph']) graph_data = nx.get_node_attributes(G, 'pos') data = np.array(tuple(graph_data.values())) try: labels = nx.get_node_attributes(G, 'label') labels = np.array(list(labels.values())) except: labels = np.zeros(len(data)) instances = np.zeros(len(data)) edgelist = list(G.edges()) edge_pos = np.asarray([(graph_data[e[0]], graph_data[e[1]]) for e in edgelist]) edge_collection = LineCollection(edge_pos) edge_collection.set_zorder(1) # edges go behind nodes edge_collection.set_label(None) fig, ax = plt.subplots() pts = ax.scatter(data[:, 0], data[:, 1], s=80, c=labels) ax.add_collection(edge_collection) selector = SelectFromCollection(ax, pts) fc = pts.get_facecolors() mode = 'SELECT' instance = 1 fig.suptitle("Mode: " + mode + " | Instance: " + str(instance), x=0.5, y=0.05) ann_list = []
def draw_networkx_edges( G, pos, edgelist=None, width=1.0, edge_color="k", style="solid", alpha=None, arrowstyle="-|>", arrowsize=10, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=True, label=None, node_size=300, nodelist=None, node_shape="o", connectionstyle=None, min_source_margin=0, min_target_margin=0, ): """Draw the edges of the graph G. This draws only the edges of the graph G. Parameters ---------- G : graph A networkx graph pos : dictionary A dictionary with nodes as keys and positions as values. Positions should be sequences of length 2. edgelist : collection of edge tuples Draw only specified edges(default=G.edges()) width : float, or array of floats Line width of edges (default=1.0) edge_color : color or array of colors (default='k') Edge color. Can be a single color or a sequence of colors with the same length as edgelist. Color can be string, or rgb (or rgba) tuple of floats from 0-1. If numeric values are specified they will be mapped to colors using the edge_cmap and edge_vmin,edge_vmax parameters. style : string Edge line style (default='solid') (solid|dashed|dotted,dashdot) alpha : float The edge transparency (default=None) edge_ cmap : Matplotlib colormap Colormap for mapping intensities of edges (default=None) edge_vmin,edge_vmax : floats Minimum and maximum for edge colormap scaling (default=None) ax : Matplotlib Axes object, optional Draw the graph in the specified Matplotlib axes. arrows : bool, optional (default=True) For directed graphs, if True draw arrowheads. Note: Arrows will be the same color as edges. arrowstyle : str, optional (default='-|>') For directed graphs, choose the style of the arrow heads. See :py:class: `matplotlib.patches.ArrowStyle` for more options. arrowsize : int, optional (default=10) For directed graphs, choose the size of the arrow head head's length and width. See :py:class: `matplotlib.patches.FancyArrowPatch` for attribute `mutation_scale` for more info. connectionstyle : str, optional (default=None) Pass the connectionstyle parameter to create curved arc of rounding radius rad. For example, connectionstyle='arc3,rad=0.2'. See :py:class: `matplotlib.patches.ConnectionStyle` and :py:class: `matplotlib.patches.FancyArrowPatch` for more info. label : [None| string] Label for legend min_source_margin : int, optional (default=0) The minimum margin (gap) at the begining of the edge at the source. min_target_margin : int, optional (default=0) The minimum margin (gap) at the end of the edge at the target. Returns ------- matplotlib.collection.LineCollection `LineCollection` of the edges list of matplotlib.patches.FancyArrowPatch `FancyArrowPatch` instances of the directed edges Depending whether the drawing includes arrows or not. Notes ----- For directed graphs, arrows are drawn at the head end. Arrows can be turned off with keyword arrows=False. Be sure to include `node_size` as a keyword argument; arrows are drawn considering the size of nodes. Examples -------- >>> G = nx.dodecahedral_graph() >>> edges = nx.draw_networkx_edges(G, pos=nx.spring_layout(G)) >>> G = nx.DiGraph() >>> G.add_edges_from([(1, 2), (1, 3), (2, 3)]) >>> arcs = nx.draw_networkx_edges(G, pos=nx.spring_layout(G)) >>> alphas = [0.3, 0.4, 0.5] >>> for i, arc in enumerate(arcs): # change alpha values of arcs ... arc.set_alpha(alphas[i]) Also see the NetworkX drawing examples at https://networkx.github.io/documentation/latest/auto_examples/index.html See Also -------- draw() draw_networkx() draw_networkx_nodes() draw_networkx_labels() draw_networkx_edge_labels() """ try: import matplotlib.pyplot as plt from matplotlib.colors import colorConverter, Colormap, Normalize from matplotlib.collections import LineCollection from matplotlib.patches import FancyArrowPatch import numpy as np except ImportError as e: raise ImportError("Matplotlib required for draw()") from e except RuntimeError: print("Matplotlib unable to open display") raise if ax is None: ax = plt.gca() if edgelist is None: edgelist = list(G.edges()) if len(edgelist) == 0: # no edges! if not G.is_directed() or not arrows: return LineCollection(None) else: return [] if nodelist is None: nodelist = list(G.nodes()) # FancyArrowPatch handles color=None different from LineCollection if edge_color is None: edge_color = "k" # set edge positions edge_pos = np.asarray([(pos[e[0]], pos[e[1]]) for e in edgelist]) # Check if edge_color is an array of floats and map to edge_cmap. # This is the only case handled differently from matplotlib if (np.iterable(edge_color) and (len(edge_color) == len(edge_pos)) and np.alltrue([isinstance(c, Number) for c in edge_color])): if edge_cmap is not None: assert isinstance(edge_cmap, Colormap) else: edge_cmap = plt.get_cmap() if edge_vmin is None: edge_vmin = min(edge_color) if edge_vmax is None: edge_vmax = max(edge_color) color_normal = Normalize(vmin=edge_vmin, vmax=edge_vmax) edge_color = [edge_cmap(color_normal(e)) for e in edge_color] if not G.is_directed() or not arrows: edge_collection = LineCollection( edge_pos, colors=edge_color, linewidths=width, antialiaseds=(1, ), linestyle=style, transOffset=ax.transData, alpha=alpha, ) edge_collection.set_cmap(edge_cmap) edge_collection.set_clim(edge_vmin, edge_vmax) edge_collection.set_zorder(1) # edges go behind nodes edge_collection.set_label(label) ax.add_collection(edge_collection) return edge_collection arrow_collection = None if G.is_directed() and arrows: # Note: Waiting for someone to implement arrow to intersection with # marker. Meanwhile, this works well for polygons with more than 4 # sides and circle. def to_marker_edge(marker_size, marker): if marker in "s^>v<d": # `large` markers need extra space return np.sqrt(2 * marker_size) / 2 else: return np.sqrt(marker_size) / 2 # Draw arrows with `matplotlib.patches.FancyarrowPatch` arrow_collection = [] mutation_scale = arrowsize # scale factor of arrow head # FancyArrowPatch doesn't handle color strings arrow_colors = colorConverter.to_rgba_array(edge_color, alpha) for i, (src, dst) in enumerate(edge_pos): x1, y1 = src x2, y2 = dst shrink_source = 0 # space from source to tail shrink_target = 0 # space from head to target if np.iterable(node_size): # many node sizes source, target = edgelist[i][:2] source_node_size = node_size[nodelist.index(source)] target_node_size = node_size[nodelist.index(target)] shrink_source = to_marker_edge(source_node_size, node_shape) shrink_target = to_marker_edge(target_node_size, node_shape) else: shrink_source = shrink_target = to_marker_edge( node_size, node_shape) if shrink_source < min_source_margin: shrink_source = min_source_margin if shrink_target < min_target_margin: shrink_target = min_target_margin if len(arrow_colors) == len(edge_pos): arrow_color = arrow_colors[i] elif len(arrow_colors) == 1: arrow_color = arrow_colors[0] else: # Cycle through colors arrow_color = arrow_colors[i % len(arrow_colors)] if np.iterable(width): if len(width) == len(edge_pos): line_width = width[i] else: line_width = width[i % len(width)] else: line_width = width arrow = FancyArrowPatch( (x1, y1), (x2, y2), arrowstyle=arrowstyle, shrinkA=shrink_source, shrinkB=shrink_target, mutation_scale=mutation_scale, color=arrow_color, linewidth=line_width, connectionstyle=connectionstyle, linestyle=style, zorder=1, ) # arrows go behind nodes # There seems to be a bug in matplotlib to make collections of # FancyArrowPatch instances. Until fixed, the patches are added # individually to the axes instance. arrow_collection.append(arrow) ax.add_patch(arrow) # update view minx = np.amin(np.ravel(edge_pos[:, :, 0])) maxx = np.amax(np.ravel(edge_pos[:, :, 0])) miny = np.amin(np.ravel(edge_pos[:, :, 1])) maxy = np.amax(np.ravel(edge_pos[:, :, 1])) w = maxx - minx h = maxy - miny padx, pady = 0.05 * w, 0.05 * h corners = (minx - padx, miny - pady), (maxx + padx, maxy + pady) ax.update_datalim(corners) ax.autoscale_view() ax.tick_params( axis="both", which="both", bottom=False, left=False, labelbottom=False, labelleft=False, ) return arrow_collection
def draw_edges(G, pos, ax, edgelist=None, width=1.0, width_adjuster=50, edge_color='k', style='solid', alpha=None, edge_cmap=None, edge_vmin=None, edge_vmax=None, traversal_weight=1.0, edge_delengthify=0.15, arrows=True, label=None, zorder=1, **kwds): """ Code cleaned-up version of networkx.draw_networkx_edges New args: width_adjuster - the line width is generated from the weight if present, use this adjuster to thicken the lines (multiply) """ if edgelist is None: edgelist = G.edges() if not edgelist or len(edgelist) == 0: # no edges! return None # set edge positions edge_pos = [(pos[e[0]], pos[e[1]]) for e in edgelist] new_ep = [] for e in edge_pos: x, y = e[0] dx, dy = e[1] # Get edge length elx = (dx - x) * edge_delengthify ely = (dy - y) * edge_delengthify x += elx y += ely dx -= elx dy -= ely new_ep.append(((x, y), (dx, dy))) edge_pos = numpy.asarray(new_ep) if not cb.iterable(width): #print [G.get_edge_data(n[0], n[1])['weight'] for n in edgelist] # see if I can find an edge attribute: if 'weight' in G.get_edge_data(edgelist[0][0], edgelist[0][1]): # Test an edge lw = [ 0.5 + ((G.get_edge_data(n[0], n[1])['weight'] - traversal_weight) * width_adjuster) for n in edgelist ] else: lw = (width, ) else: lw = width if not is_string_like(edge_color) and cb.iterable(edge_color) and len( edge_color) == len(edge_pos): if numpy.alltrue([cb.is_string_like(c) for c in edge_color]): # (should check ALL elements) # list of color letters such as ['k','r','k',...] edge_colors = tuple( [colorConverter.to_rgba(c, alpha) for c in edge_color]) elif numpy.alltrue([not cb.is_string_like(c) for c in edge_color]): # If color specs are given as (rgb) or (rgba) tuples, we're OK if numpy.alltrue( [cb.iterable(c) and len(c) in (3, 4) for c in edge_color]): edge_colors = tuple(edge_color) else: # numbers (which are going to be mapped with a colormap) edge_colors = None else: raise ValueError( 'edge_color must consist of either color names or numbers') else: if is_string_like(edge_color) or len(edge_color) == 1: edge_colors = (colorConverter.to_rgba(edge_color, alpha), ) else: raise ValueError( 'edge_color must be a single color or list of exactly m colors where m is the number or edges' ) edge_collection = LineCollection(edge_pos, colors=edge_colors, linewidths=lw, antialiaseds=(1, ), linestyle=style, transOffset=ax.transData, zorder=zorder) edge_collection.set_label(label) ax.add_collection(edge_collection) if cb.is_numlike(alpha): edge_collection.set_alpha(alpha) if edge_colors is None: if edge_cmap is not None: assert (isinstance(edge_cmap, Colormap)) edge_collection.set_array(numpy.asarray(edge_color)) edge_collection.set_cmap(edge_cmap) if edge_vmin is not None or edge_vmax is not None: edge_collection.set_clim(edge_vmin, edge_vmax) else: edge_collection.autoscale() # update view ''' minx = numpy.amin(numpy.ravel(edge_pos[:,:,0])) maxx = numpy.amax(numpy.ravel(edge_pos[:,:,0])) miny = numpy.amin(numpy.ravel(edge_pos[:,:,1])) maxy = numpy.amax(numpy.ravel(edge_pos[:,:,1])) w = maxx-minx h = maxy-miny padx, pady = 0.05*w, 0.05*h corners = (minx-padx, miny-pady), (maxx+padx, maxy+pady) ax.update_datalim(corners) ax.autoscale_view() ''' return (edge_collection)
def animate_convolution(x, h, y, t, tau, td, taud, interval=75): """Plot animation of graphical representation of linear convolution. Parameters ---------- x : sympy function First function to be convolved. h: sympy function Second function to be convolved. t: sympy variable Independent variable for functions (e.g. time). tau: sympy variable Integration variable for convolution. td: array like Discrete values of independent variable evaluated for plot. taud: Discrete values of integration variable evaluated for animation. interval: Interval in ms between frames of animation. Returns ------- matplotlib.animation.FuncAnimation object. """ def animate(ti): p = sym.plot(x.subs(t, tau), (tau, taud[0], taud[-1]), show=False) line_x.set_segments(p[0].get_segments()) p = sym.plot(h.subs(t, t - tau).subs(t, ti), (tau, taud[0], taud[-1]), show=False) line_h.set_segments(p[0].get_segments()) p = sym.plot(y, (t, taud[0], taud[-1]), show=False) line_y.set_segments(p[0].get_segments()) p = sym.plot(x.subs(t, tau) * h.subs(t, ti - tau), (tau, -5, 5), show=False) points = p[0].get_points() verts = [[(xi[0], xi[1]) for xi in np.transpose(np.array(points))]] fill.set_verts(verts) dot.set_data(ti, y.subs(t, ti)) # define line/fill collections and setup plot default_figsize = plt.rcParams.get('figure.figsize') fig, ax = plt.subplots(2, 1, figsize=(default_figsize[0], 1.5 * default_figsize[1])) fig.subplots_adjust(hspace=0.2) plt.close() # suppresses empty plot in notebook fill = PolyCollection([], facecolors='r') fill.set_alpha(0.3) ax[0].add_collection(fill) line_x = LineCollection([], colors='C0') line_x.set_label(r'$x(\tau)$') ax[0].add_collection(line_x) line_h = LineCollection([], colors='C1') line_h.set_label(r'$h(t - \tau)$') ax[0].add_collection(line_h) line_y = LineCollection([], colors='C2') line_y.set_label(r'$y(t)$') ax[1].add_collection(line_y) dot, = ax[1].plot([], 'ro') for axi in ax: axi.spines['left'].set_position('zero') axi.spines['bottom'].set_position('zero') axi.spines['right'].set_color('none') axi.spines['top'].set_color('none') axi.xaxis.set_ticks_position('bottom') axi.yaxis.set_ticks_position('left') axi.set_xlim((-3, 4)) axi.set_ylim((-.1, 1.2)) ax[0].set_xlabel(r'$\tau$', horizontalalignment='right', x=1.0) ax[0].legend(loc='upper right') ax[1].set_xlabel(r'$t$', horizontalalignment='right', x=1.0) ax[1].legend(loc='upper right') return FuncAnimation(fig, animate, td, interval=interval)
def draw_networkx_edges(G, pos, edgelist=None, width=1.0, edge_color='k', style='solid', alpha=1.0, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=True, arrowstyle='thick', label=None, **kwds): try: import matplotlib import matplotlib.pyplot as plt import matplotlib.cbook as cb import matplotlib.patches as patches from matplotlib.colors import colorConverter, Colormap from matplotlib.collections import LineCollection from matplotlib.path import Path import numpy except ImportError: raise ImportError("Matplotlib required for draw()") except RuntimeError: print("Matplotlib unable to open display") raise # print "drawing_edges" if ax is None: ax = plt.gca() if edgelist is None: edgelist = G.edges() if not edgelist or len(edgelist) == 0: # no edges! return None # set edge positions edge_pos = numpy.asarray([(pos[e[0]], pos[e[1]]) for e in edgelist]) # for e in edge_pos: # print e if not cb.iterable(width): lw = (width, ) else: lw = width if not cb.is_string_like(edge_color) \ and cb.iterable(edge_color) \ and len(edge_color) == len(edge_pos): if numpy.alltrue([cb.is_string_like(c) for c in edge_color]): # (should check ALL elements) # list of color letters such as ['k','r','k',...] edge_colors = tuple( [colorConverter.to_rgba(c, alpha) for c in edge_color]) elif numpy.alltrue([not cb.is_string_like(c) for c in edge_color]): # If color specs are given as (rgb) or (rgba) tuples, we're OK if numpy.alltrue( [cb.iterable(c) and len(c) in (3, 4) for c in edge_color]): edge_colors = tuple(edge_color) else: # numbers (which are going to be mapped with a colormap) edge_colors = None else: raise ValueError( 'edge_color must consist of either color names or numbers') else: if cb.is_string_like(edge_color) or len(edge_color) == 1: edge_colors = (colorConverter.to_rgba(edge_color, alpha), ) else: raise ValueError( 'edge_color must be a single color or list of exactly m colors where m is the number or edges' ) edge_collection = LineCollection( edge_pos, colors=edge_colors, linewidths=lw, antialiaseds=(1, ), linestyle=style, transOffset=ax.transData, ) # print type(edge_collection) edge_collection.set_zorder(1) # edges go behind nodes edge_collection.set_label(label) ax.add_collection(edge_collection) # Note: there was a bug in mpl regarding the handling of alpha values for # each line in a LineCollection. It was fixed in matplotlib in r7184 and # r7189 (June 6 2009). We should then not set the alpha value globally, # since the user can instead provide per-edge alphas now. Only set it # globally if provided as a scalar. if cb.is_numlike(alpha): edge_collection.set_alpha(alpha) if edge_colors is None: if edge_cmap is not None: assert (isinstance(edge_cmap, Colormap)) edge_collection.set_array(numpy.asarray(edge_color)) edge_collection.set_cmap(edge_cmap) if edge_vmin is not None or edge_vmax is not None: edge_collection.set_clim(edge_vmin, edge_vmax) else: edge_collection.autoscale() arrow_collection = None if G.is_directed() and arrows: # a directed graph hack-fix # draws arrows at each # waiting for someone else to implement arrows that will work arrow_colors = edge_colors a_pos = [] p = .1 # make arrows 10% of total length angle = 2.7 #angle for arrows for src, dst in edge_pos: x1, y1 = src x2, y2 = dst dx = x2 - x1 # x offset dy = y2 - y1 # y offset d = numpy.sqrt(float(dx**2 + dy**2)) # length of edge theta = numpy.arctan2(dy, dx) if d == 0: # source and target at same position continue if dx == 0: # vertical edge xa = x2 ya = dy + y1 if dy == 0: # horizontal edge ya = y2 xa = dx + x1 else: # xa = p*d*numpy.cos(theta)+x1 # ya = p*d*numpy.sin(theta)+y1 #corrects the endpoints to better draw x2 -= .04 * numpy.cos(theta) y2 -= .04 * numpy.sin(theta) lx1 = p * d * numpy.cos(theta + angle) + (x2) lx2 = p * d * numpy.cos(theta - angle) + (x2) ly1 = p * d * numpy.sin(theta + angle) + (y2) ly2 = p * d * numpy.sin(theta - angle) + (y2) a_pos.append(((lx1, ly1), (x2, y2))) a_pos.append(((lx2, ly2), (x2, y2))) arrow_collection = LineCollection( a_pos, colors=arrow_colors, linewidths=[1 * ww for ww in lw], antialiaseds=(1, ), transOffset=ax.transData, ) arrow_collection.set_zorder(1) # edges go behind nodes arrow_collection.set_label(label) # print type(ax) ax.add_collection(arrow_collection) #drawing self loops d = 1 c = 0.0707 selfedges = [] verts = [ (0.1 * d - 0.1 * d, 0.0), # P0 (c * d - 0.1 * d, c * d), # P0 (0.0 - 0.1 * d, 0.1 * d), # P0 (-c * d - 0.1 * d, c * d), # P0 (-0.1 * d - 0.1 * d, 0.0), # P0 (-c * d - 0.1 * d, -c * d), # P0 (0.0 - 0.1 * d, -0.1 * d), # P0 (c * d - 0.1 * d, -c * d), # P0 (0.1 * d - 0.1 * d, 0.0) ] # print verts codes = [ Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO, ] for e in edge_pos: if (numpy.array_equal(e[0], e[1])): nodes = verts[:] for i in range(len(nodes)): nodes[i] += e[0] # print nodes path = Path(nodes, codes) patch = patches.PathPatch(path, color=None, facecolor=None, edgecolor=edge_colors[0], fill=False, lw=4) ax.add_patch(patch) # update view minx = numpy.amin(numpy.ravel(edge_pos[:, :, 0])) maxx = numpy.amax(numpy.ravel(edge_pos[:, :, 0])) miny = numpy.amin(numpy.ravel(edge_pos[:, :, 1])) maxy = numpy.amax(numpy.ravel(edge_pos[:, :, 1])) w = maxx - minx h = maxy - miny padx, pady = 0.05 * w, 0.05 * h corners = (minx - padx, miny - pady), (maxx + padx, maxy + pady) # print ax ax.update_datalim(corners) ax.autoscale_view() # if arrow_collection: return edge_collection
def plot2d(x: Union[core.NeuronObject, core.Volume, np.ndarray, List[Union[core.NeuronObject, np.ndarray, core.Volume]]], method: Union[Literal['2d'], Literal['3d'], Literal['3d_complex']] = '2d', **kwargs) -> Tuple[mpl.figure.Figure, mpl.axes.Axes]: """ Generate 2D plots of neurons and neuropils. The main advantage of this is that you can save plot as vector graphics. Important --------- This function uses matplotlib which "fakes" 3D as it has only very limited control over layers. Therefore neurites aren't necessarily plotted in the right Z order which becomes especially troublesome when plotting a complex scene with lots of neurons criss-crossing. See the ``method`` parameter for details. All methods use orthogonal projection. Parameters ---------- x : skeleton IDs | TreeNeuron | NeuronList | Volume | Dotprops | np.ndarray Objects to plot:: - int is intepreted as skeleton ID(s) - str is intepreted as volume name(s) - multiple objects can be passed as list (see examples) - numpy array of shape (n,3) is intepreted as scatter method : '2d' | '3d' | '3d_complex' Method used to generate plot. Comes in three flavours: 1. '2d' uses normal matplotlib. Neurons are plotted in the order their are provided. Well behaved when plotting neuropils and connectors. Always gives frontal view. 2. '3d' uses matplotlib's 3D axis. Here, matplotlib decide the order of plotting. Can chance perspective either interacively or by code (see examples). 3. '3d_complex' same as 3d but each neuron segment is added individually. This allows for more complex crossing patterns to be rendered correctly. Slows down rendering though. **kwargs See Notes for permissible keyword arguments. Examples -------- >>> import navis >>> import matplotlib.pyplot as plt Plot list of neurons as simple 2d >>> nl = navis.example_neurons() >>> fig, ax = navis.plot2d(nl) >>> plt.show() Add a volume >>> v = navis.example_volume('LH') >>> fig, ax = navis.plot2d([nl, vol]) >>> plt.show() Change neuron colors >>> fig, ax = navis.plot2d(nl, color=['r', 'g', 'b', 'm', 'c', 'y']) >>> plt.show() Plot in "fake" 3D >>> fig, ax = navis.plot2d(nl, method='3d') >>> plt.show() >>> # Try dragging the window Plot in "fake" 3D and change perspective >>> fig, ax = navis.plot2d(nl, method='3d') >>> # Change view to lateral >>> ax.azim = 0 >>> ax.elev = 0 >>> # Change view to top >>> ax.azim = -90 >>> ax.elev = 90 >>> # Tilted top view >>> ax.azim = -135 >>> ax.elev = 45 >>> # Move camera closer (will make image bigger) >>> ax.dist = 5 >>> plt.show() Plot using depth-coloring >>> fig, ax = navis.plot2d(nl, method='3d', depth_coloring=True) >>> plt.show() Returns -------- fig, ax : matplotlib figure and axis object Notes ----- Optional keyword arguments: ``soma`` (bool, default = True) Plot soma if one exists. ``connectors`` (boolean, default = True) Plot connectors (synapses, gap junctions, abutting) ``connectors_only`` (boolean, default = False) Plot only connectors, not the neuron. ``cn_size`` (int | float, default = 1) Size of connectors. ``linewidth``/``lw`` (int | float, default = .5) Width of neurites. ``linestyle``/``ls`` (str, default = '-') Line style of neurites. ``autoscale`` (bool, default=True) If True, will scale the axes to fit the data. ``scalebar`` (int | float, default=False) Adds scale bar. Provide integer/float to set size of scalebar. For methods '3d' and '3d_complex', this will create an axis object. ``ax`` (matplotlib ax, default=None) Pass an ax object if you want to plot on an existing canvas. ``figsize`` (tuple, default = (8, 8)) Size of figure. ``color`` (tuple | list | str | dict) Tuples/lists (r,g,b) and str (color name) are interpreted as a single colors that will be applied to all neurons. Dicts will be mapped onto neurons by skeleton ID. ``alpha`` (float [0-1], default = .9) Alpha value for neurons. Overriden if alpha is provided as fourth value in ``color``. ``use_neuron_color`` (bool, default = False) If True, will attempt to use ``.color`` attribute of neurons. ``depth_coloring`` (bool, default = False) If True, will color encode depth (Z). Overrides ``color``. Does not work with ``method = '3d_complex'``. ``depth_scale`` (bool, default = True) If True and ``depth_coloring=True`` will plot a scale. ``cn_mesh_colors`` (bool, default = False) If True, will use the neuron's color for its connectors too. ``group_neurons`` (bool, default = False) If True, neurons will be grouped. Works with SVG export (not PDF). Does NOT work with ``method='3d_complex'``. ``scatter_kws`` (dict, default = {}) Parameters to be used when plotting points. Accepted keywords are: ``size`` and ``color``. ``view`` (tuple, default = ("x", "y")) Sets view for ``method='2d'``. See Also -------- :func:`navis.plot3d` Use this if you want interactive, perspectively correct renders and if you don't need vector graphics as outputs. :func:`navis.plot1d` A nifty way to visualise neurons in a single dimension. """ # Filter kwargs _ACCEPTED_KWARGS = [ 'soma', 'connectors', 'connectors_only', 'ax', 'color', 'colors', 'c', 'view', 'scalebar', 'cn_mesh_colors', 'linewidth', 'cn_size', 'group_neurons', 'scatter_kws', 'figsize', 'linestyle', 'alpha', 'depth_coloring', 'autoscale', 'depth_scale', 'use_neuron_color', 'ls', 'lw' ] wrong_kwargs = [a for a in kwargs if a not in _ACCEPTED_KWARGS] if wrong_kwargs: raise KeyError(f'Unknown kwarg(s): {",".join(wrong_kwargs)}. ' f'Currently accepted: {",".join(_ACCEPTED_KWARGS)}') _METHOD_OPTIONS = ['2d', '3d', '3d_complex'] if method not in _METHOD_OPTIONS: raise ValueError(f'Unknown method "{method}". Please use either: ' f'{",".join(_METHOD_OPTIONS)}') # Set axis to plot for method '2d' axis1, axis2 = kwargs.get('view', ('x', 'y')) plot_soma = kwargs.get('soma', True) connectors = kwargs.get('connectors', False) connectors_only = kwargs.get('connectors_only', False) cn_mesh_colors = kwargs.get('cn_mesh_colors', False) use_neuron_color = kwargs.get('use_neuron_color', False) ax = kwargs.get('ax', None) color = kwargs.get('color', kwargs.get('c', kwargs.get('colors', None))) scalebar = kwargs.get('scalebar', None) group_neurons = kwargs.get('group_neurons', False) # This is overwritten if color specifies alphas alpha = kwargs.get('alpha', .9) # Depth coloring depth_coloring = kwargs.get('depth_coloring', False) depth_scale = kwargs.get('depth_scale', True) scatter_kws = kwargs.get('scatter_kws', {}) linewidth = kwargs.get('linewidth', kwargs.get('lw', .5)) cn_size = kwargs.get('cn_size', 1) linestyle = kwargs.get('linestyle', kwargs.get('ls', '-')) autoscale = kwargs.get('autoscale', True) # Keep track of limits if necessary lim = [] # Parse objects skdata, dotprops, volumes, points, visuals = utils.parse_objects(x) # Generate the colormaps (neuron_cmap, dotprop_cmap, volumes_cmap) = prepare_colormap(color, skdata, dotprops, volumes, use_neuron_color=use_neuron_color, color_range=1) # Make sure axes are projected orthogonally if method in ['3d', '3d_complex']: proj3d.persp_transformation = _orthogonal_proj # Generate axes if not ax: if method == '2d': fig, ax = plt.subplots(figsize=kwargs.get('figsize', (8, 8))) ax.set_aspect('equal') elif method in ['3d', '3d_complex']: fig = plt.figure(figsize=kwargs.get('figsize', plt.figaspect(1) * 1.5)) ax = fig.gca(projection='3d') # This sets front view ax.azim = -90 ax.elev = 0 ax.dist = 7 # Disallowed for 3D in matplotlib 3.1.0 # ax.set_aspect('equal') # Check if correct axis were provided else: if not isinstance(ax, mpl.axes.Axes): raise TypeError('Ax must be of type "mpl.axes.Axes", ' f'not "{type(ax)}"') fig = ax.get_figure() if method in ['3d', '3d_complex']: if ax.name != '3d': raise TypeError('Axis must be 3d.') # Add existing limits to ax lim += np.array((ax.get_xlim3d(), ax.get_zlim3d(), ax.get_ylim3d())).T.tolist() elif method == '2d': if ax.name == '3d': raise TypeError('Axis must be 2d.') # Prepare some stuff for depth coloring if depth_coloring and method == '3d_complex': raise Exception(f'Depth coloring unavailable for method "{method}"') elif depth_coloring and method == '2d': all_co = skdata.nodes[['x', 'y', 'z']] norm = plt.Normalize(vmin=all_co.z.min(), vmax=all_co.z.max()) # Plot volumes first if volumes: for i, v in enumerate(volumes): c = volumes_cmap[i] if method == '2d': vpatch = mpatches.Polygon(v.to_2d(view=f'{axis1}{axis2}', invert_y=True), closed=True, lw=0, fill=True, fc=c, alpha=c[3] if len(c) == 4 else 1) ax.add_patch(vpatch) elif method in ['3d', '3d_complex']: verts = np.vstack(v.vertices) # Invert y-axis verts[:, 1] *= -1 # Add alpha if len(c) == 3: c = (c[0], c[1], c[2], .1) ts = ax.plot_trisurf(verts[:, 0], verts[:, 2], v.faces, verts[:, 1], label=v.name, color=c) ts.set_gid(v.name) # Keep track of limits lim.append(verts.max(axis=0)) lim.append(verts.min(axis=0)) # Create lines from segments line3D_collections = [] surf3D_collections = [] for i, neuron in enumerate( config.tqdm(skdata.itertuples(), desc='Plot neurons', total=skdata.shape[0], leave=False, disable=config.pbar_hide | len(dotprops) == 0)): this_color = neuron_cmap[i] if neuron.nodes.empty: logger.warning(f'Skipping neuron w/o nodes: {neuron.uuid}') continue if not connectors_only: # Now make traces (invert y axis) coords = segments_to_coords(neuron, neuron.segments, modifier=(1, -1, 1)) if method == '2d': if not depth_coloring: # We have to add (None, None, None) to the end of each # slab to make that line discontinuous there coords = np.vstack( [np.append(t, [[None] * 3], axis=0) for t in coords]) this_line = mlines.Line2D( coords[:, 0], coords[:, 1], lw=linewidth, ls=linestyle, alpha=alpha, color=this_color, label= f'{getattr(neuron, "name", "NA")} - #{neuron.uuid}') ax.add_line(this_line) else: coords = tn_pairs_to_coords(neuron, modifier=(1, -1, 1)) lc = LineCollection(coords[:, :, [0, 1]], cmap='jet', norm=norm) lc.set_array(neuron.nodes.loc[neuron.nodes.parent_id >= 0, 'z'].values) lc.set_linewidth(linewidth) lc.set_alpha(alpha) lc.set_linestyle(linestyle) lc.set_label( f'{getattr(neuron, "name", "NA")} - #{neuron.uuid}') line = ax.add_collection(lc) if plot_soma and not isinstance(neuron.soma, type(None)): soma = utils.make_iterable(neuron.soma) for s in soma: n = neuron.nodes.set_index('node_id').loc[s] r = getattr(n, neuron.soma_radius) if isinstance( neuron.soma_radius, str) else neuron.soma_radius if depth_coloring: this_color = mpl.cm.jet(norm(n.z)) s = mpatches.Circle((int(n.x), int(-n.y)), radius=r, alpha=alpha, fill=True, fc=this_color, zorder=4, edgecolor='none') ax.add_patch(s) elif method in ['3d', '3d_complex']: cmap = mpl.cm.jet if depth_coloring else None # For simple scenes, add whole neurons at a time -> will speed # up rendering if method == '3d': if depth_coloring: this_coords = tn_pairs_to_coords( neuron, modifier=(1, -1, 1))[:, :, [0, 2, 1]] else: this_coords = [c[:, [0, 2, 1]] for c in coords] lc = Line3DCollection(this_coords, color=this_color, label=neuron.uuid, alpha=alpha, cmap=cmap, lw=linewidth, linestyle=linestyle) if group_neurons: lc.set_gid(neuron.uuid) ax.add_collection3d(lc) line3D_collections.append(lc) # For complex scenes, add each segment as a single collection # -> help preventing Z-order errors elif method == '3d_complex': for c in coords: lc = Line3DCollection([c[:, [0, 2, 1]]], color=this_color, lw=linewidth, alpha=alpha, linestyle=linestyle) if group_neurons: lc.set_gid(neuron.uuid) ax.add_collection3d(lc) coords = np.vstack(coords) lim.append(coords.max(axis=0)) lim.append(coords.min(axis=0)) surf3D_collections.append([]) if plot_soma and not isinstance(neuron.soma, type(None)): soma = utils.make_iterable(neuron.soma) for s in soma: n = neuron.nodes.set_index('node_id').loc[s] r = getattr(n, neuron.soma_radius) if isinstance( neuron.soma_radius, str) else neuron.soma_radius resolution = 20 u = np.linspace(0, 2 * np.pi, resolution) v = np.linspace(0, np.pi, resolution) x = r * np.outer(np.cos(u), np.sin(v)) + n.x y = r * np.outer(np.sin(u), np.sin(v)) - n.y z = r * np.outer(np.ones(np.size(u)), np.cos(v)) + n.z surf = ax.plot_surface(x, z, y, color=this_color, shade=False, alpha=alpha) if group_neurons: surf.set_gid(neuron.uuid) surf3D_collections[-1].append(surf) if (connectors or connectors_only) and neuron.has_connectors: if not cn_mesh_colors: cn_types = {0: 'red', 1: 'blue', 2: 'green', 3: 'magenta'} else: cn_types = { 0: this_color, 1: this_color, 2: this_color, 3: this_color } if method == '2d': for c in cn_types: this_cn = neuron.connectors[neuron.connectors.relation == c] ax.scatter(this_cn.x.values, (-this_cn.y).values, c=cn_types[c], alpha=alpha, zorder=4, edgecolor='none', s=cn_size) ax.get_children()[-1].set_gid(f'CN_{neuron.uuid}') elif method in ['3d', '3d_complex']: all_cn = neuron.connectors c = [cn_types[i] for i in all_cn.relation.values] ax.scatter(all_cn.x.values, all_cn.z.values, -all_cn.y.values, c=c, s=cn_size, depthshade=False, edgecolor='none', alpha=alpha) ax.get_children()[-1].set_gid(f'CN_{neuron.uuid}') coords = neuron.connectors[['x', 'y', 'z']].values coords[:, 1] *= -1 lim.append(coords.max(axis=0)) lim.append(coords.min(axis=0)) for i, neuron in enumerate( config.tqdm(dotprops.itertuples(), desc='Plt dotprops', total=dotprops.shape[0], leave=False, disable=config.pbar_hide | len(dotprops) == 0)): # Prepare lines - this is based on nat:::plot3d.dotprops halfvect = neuron.points[['x_vec', 'y_vec', 'z_vec']] / 2 starts = neuron.points[['x', 'y', 'z']].values - halfvect.values ends = neuron.points[['x', 'y', 'z']].values + halfvect.values try: this_color = dotprop_cmap[i] except BaseException: this_color = (.1, .1, .1) if method == '2d': # Add None between segments x_coords = [ n for sublist in zip(starts[:, 0], ends[:, 0], [None] * starts.shape[0]) for n in sublist ] y_coords = [ n for sublist in zip(starts[:, 1] * -1, ends[:, 1] * -1, [None] * starts.shape[0]) for n in sublist ] this_line = mlines.Line2D(x_coords, y_coords, lw=linewidth, ls=linestyle, alpha=alpha, color=this_color, label='%s' % (neuron.gene_name)) ax.add_line(this_line) # Add soma if plot_soma: s = mpatches.Circle((neuron.X, -neuron.Y), radius=2, alpha=alpha, fill=True, fc=this_color, zorder=4, edgecolor='none') ax.add_patch(s) elif method in ['3d', '3d_complex']: # Combine coords by weaving starts and ends together coords = np.empty((starts.shape[0] * 2, 3), dtype=starts.dtype) coords[0::2] = starts coords[1::2] = ends # Invert y-axis coords[:, 1] *= -1 # For simple scenes, add whole neurons at a time # -> will speed up rendering if method == '3d': lc = Line3DCollection(np.split(coords[:, [0, 2, 1]], starts.shape[0]), color=this_color, label=neuron.gene_name, lw=linewidth, alpha=alpha, linestyle=linestyle) if group_neurons: lc.set_gid(neuron.gene_name) ax.add_collection3d(lc) # For complex scenes, add each segment as a single collection # -> help preventing Z-order errors elif method == '3d_complex': for c in np.split(coords[:, [0, 2, 1]], starts.shape[0]): lc = Line3DCollection([c], color=this_color, lw=linewidth, alpha=alpha, linestyle=linestyle) if group_neurons: lc.set_gid(neuron.gene_name) ax.add_collection3d(lc) lim.append(coords.max(axis=0)) lim.append(coords.min(axis=0)) resolution = 20 u = np.linspace(0, 2 * np.pi, resolution) v = np.linspace(0, np.pi, resolution) x = 2 * np.outer(np.cos(u), np.sin(v)) + neuron.X y = 2 * np.outer(np.sin(u), np.sin(v)) - neuron.Y z = 2 * np.outer(np.ones(np.size(u)), np.cos(v)) + neuron.Z surf = ax.plot_surface(x, z, y, color=this_color, shade=False, alpha=alpha) if group_neurons: surf.set_gid(neuron.gene_name) if points: for p in points: if method == '2d': default_settings = dict(c='black', zorder=4, edgecolor='none', s=1) default_settings.update(scatter_kws) default_settings = _fix_default_dict(default_settings) ax.scatter(p[:, 0], p[:, 1] * -1, **default_settings) elif method in ['3d', '3d_complex']: default_settings = dict(c='black', s=1, depthshade=False, edgecolor='none') default_settings.update(scatter_kws) default_settings = _fix_default_dict(default_settings) ax.scatter(p[:, 0], p[:, 2], p[:, 1] * -1, **default_settings) coords = p coords[:, 1] *= -1 lim.append(coords.max(axis=0)) lim.append(coords.min(axis=0)) if autoscale: if method == '2d': ax.autoscale() elif method in ['3d', '3d_complex']: lim = np.vstack(lim) lim_min = lim.min(axis=0) lim_max = lim.max(axis=0) center = lim_min + (lim_max - lim_min) / 2 max_dim = (lim_max - lim_min).max() new_min = center - max_dim / 2 new_max = center + max_dim / 2 ax.set_xlim(new_min[0], new_max[0]) ax.set_ylim(new_min[2], new_max[2]) ax.set_zlim(new_min[1], new_max[1]) if scalebar is not None: # Convert sc size to nm sc_size = scalebar * 1000 # Hard-coded offset from figure boundaries ax_offset = 1000 if method == '2d': xlim = ax.get_xlim() ylim = ax.get_ylim() coords = np.array( [[xlim[0] + ax_offset, ylim[0] + ax_offset], [xlim[0] + ax_offset + sc_size, ylim[0] + ax_offset]]) sbar = mlines.Line2D(coords[:, 0], coords[:, 1], lw=3, alpha=.9, color='black') sbar.set_gid(f'{scalebar}_um') ax.add_line(sbar) elif method in ['3d', '3d_complex']: left = lim_min[0] + ax_offset bottom = lim_min[1] + ax_offset front = lim_min[2] + ax_offset sbar = [ np.array([[left, front, bottom], [left, front, bottom]]), np.array([[left, front, bottom], [left, front, bottom]]), np.array([[left, front, bottom], [left, front, bottom]]) ] sbar[0][1][0] += sc_size sbar[1][1][1] += sc_size sbar[2][1][2] += sc_size lc = Line3DCollection(sbar, color='black', lw=1) lc.set_gid(f'{scalebar}_um') ax.add_collection3d(lc) def set_depth(): """Sets depth information for neurons according to camera position.""" # Modifier for soma coordinates modifier = np.array([1, 1, -1]) # Get all coordinates all_co = np.concatenate( [lc._segments3d[:, 0, :] for lc in line3D_collections], axis=0) # Get projected coordinates proj_co = mpl_toolkits.mplot3d.proj3d.proj_points( all_co, ax.get_proj()) # Get min and max of z coordinates z_min, z_max = min(proj_co[:, 2]), max(proj_co[:, 2]) # Generate a new normaliser norm = plt.Normalize(vmin=z_min, vmax=z_max) # Go over all neurons and update Z information for neuron, lc, surf in zip(skdata, line3D_collections, surf3D_collections): # Get this neurons coordinates this_co = lc._segments3d[:, 0, :] # Get projected coordinates this_proj = mpl_toolkits.mplot3d.proj3d.proj_points( this_co, ax.get_proj()) # Normalise z coordinates ns = norm(this_proj[:, 2]).data # Set array lc.set_array(ns) # No need for normaliser - already happened lc.set_norm(None) if not isinstance(neuron.soma, type(None)): # Get depth of soma(s) soma = utils.make_iterable(neuron.soma) soma_co = neuron.nodes.set_index('node_id').loc[soma][[ 'x', 'z', 'y' ]].values soma_proj = mpl_toolkits.mplot3d.proj3d.proj_points( soma_co * modifier, ax.get_proj()) soma_cs = norm(soma_proj[:, 2]).data # Set soma color for cs, s in zip(soma_cs, surf): s.set_color(cmap(cs)) def Update(event): set_depth() if depth_coloring: if method == '2d' and depth_scale: fig.colorbar(line, ax=ax, fraction=.075, shrink=.5, label='Depth') elif method == '3d': fig.canvas.mpl_connect('draw_event', Update) set_depth() plt.axis('off') logger.debug('Done. Use matplotlib.pyplot.show() to show plot.') return fig, ax
def draw_networkx_edges(G, pos, edgelist=None, width=1.0, edge_color='k', style='solid', alpha=1.0, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=True, label=None, **kwds): """Draw the edges of the graph G. This draws only the edges of the graph G. Parameters ---------- G : graph A networkx graph pos : dictionary A dictionary with nodes as keys and positions as values. Positions should be sequences of length 2. edgelist : collection of edge tuples Draw only specified edges(default=G.edges()) width : float, or array of floats Line width of edges (default=1.0) edge_color : color string, or array of floats Edge color. Can be a single color format string (default='r'), or a sequence of colors with the same length as edgelist. If numeric values are specified they will be mapped to colors using the edge_cmap and edge_vmin,edge_vmax parameters. style : string Edge line style (default='solid') (solid|dashed|dotted,dashdot) alpha : float The edge transparency (default=1.0) edge_ cmap : Matplotlib colormap Colormap for mapping intensities of edges (default=None) edge_vmin,edge_vmax : floats Minimum and maximum for edge colormap scaling (default=None) ax : Matplotlib Axes object, optional Draw the graph in the specified Matplotlib axes. arrows : bool, optional (default=True) For directed graphs, if True draw arrowheads. label : [None| string] Label for legend Returns ------- matplotlib.collection.LineCollection `LineCollection` of the edges Notes ----- For directed graphs, "arrows" (actually just thicker stubs) are drawn at the head end. Arrows can be turned off with keyword arrows=False. Yes, it is ugly but drawing proper arrows with Matplotlib this way is tricky. Examples -------- >>> G=nx.dodecahedral_graph() >>> edges=nx.draw_networkx_edges(G,pos=nx.spring_layout(G)) Also see the NetworkX drawing examples at http://networkx.github.io/documentation/latest/gallery.html See Also -------- draw() draw_networkx() draw_networkx_nodes() draw_networkx_labels() draw_networkx_edge_labels() """ try: import matplotlib import matplotlib.pyplot as plt import matplotlib.cbook as cb from matplotlib.colors import colorConverter, Colormap from matplotlib.collections import LineCollection import numpy except ImportError: raise ImportError("Matplotlib required for draw()") except RuntimeError: print("Matplotlib unable to open display") raise if ax is None: ax = plt.gca() if edgelist is None: edgelist = list(G.edges()) if not edgelist or len(edgelist) == 0: # no edges! return None # set edge positions edge_pos = numpy.asarray([(pos[e[0]], pos[e[1]]) for e in edgelist]) if not cb.iterable(width): lw = (width,) else: lw = width if not cb.is_string_like(edge_color) \ and cb.iterable(edge_color) \ and len(edge_color) == len(edge_pos): if numpy.alltrue([cb.is_string_like(c) for c in edge_color]): # (should check ALL elements) # list of color letters such as ['k','r','k',...] edge_colors = tuple([colorConverter.to_rgba(c, alpha) for c in edge_color]) elif numpy.alltrue([not cb.is_string_like(c) for c in edge_color]): # If color specs are given as (rgb) or (rgba) tuples, we're OK if numpy.alltrue([cb.iterable(c) and len(c) in (3, 4) for c in edge_color]): edge_colors = tuple(edge_color) else: # numbers (which are going to be mapped with a colormap) edge_colors = None else: raise ValueError('edge_color must consist of either color names or numbers') else: if cb.is_string_like(edge_color) or len(edge_color) == 1: edge_colors = (colorConverter.to_rgba(edge_color, alpha), ) else: raise ValueError('edge_color must be a single color or list of exactly m colors where m is the number or edges') edge_collection = LineCollection(edge_pos, colors=edge_colors, linewidths=lw, antialiaseds=(1,), linestyle=style, transOffset = ax.transData, ) edge_collection.set_zorder(1) # edges go behind nodes edge_collection.set_label(label) ax.add_collection(edge_collection) # Note: there was a bug in mpl regarding the handling of alpha values for # each line in a LineCollection. It was fixed in matplotlib in r7184 and # r7189 (June 6 2009). We should then not set the alpha value globally, # since the user can instead provide per-edge alphas now. Only set it # globally if provided as a scalar. if cb.is_numlike(alpha): edge_collection.set_alpha(alpha) if edge_colors is None: if edge_cmap is not None: assert(isinstance(edge_cmap, Colormap)) edge_collection.set_array(numpy.asarray(edge_color)) edge_collection.set_cmap(edge_cmap) if edge_vmin is not None or edge_vmax is not None: edge_collection.set_clim(edge_vmin, edge_vmax) else: edge_collection.autoscale() arrow_collection = None if G.is_directed() and arrows: # a directed graph hack # draw thick line segments at head end of edge # waiting for someone else to implement arrows that will work arrow_colors = edge_colors a_pos = [] p = 1.0-0.25 # make head segment 25 percent of edge length for src, dst in edge_pos: x1, y1 = src x2, y2 = dst dx = x2-x1 # x offset dy = y2-y1 # y offset d = numpy.sqrt(float(dx**2 + dy**2)) # length of edge if d == 0: # source and target at same position continue if dx == 0: # vertical edge xa = x2 ya = dy*p+y1 if dy == 0: # horizontal edge ya = y2 xa = dx*p+x1 else: theta = numpy.arctan2(dy, dx) xa = p*d*numpy.cos(theta)+x1 ya = p*d*numpy.sin(theta)+y1 a_pos.append(((xa, ya), (x2, y2))) arrow_collection = LineCollection(a_pos, colors=arrow_colors, linewidths=[4*ww for ww in lw], antialiaseds=(1,), transOffset = ax.transData, ) arrow_collection.set_zorder(1) # edges go behind nodes arrow_collection.set_label(label) ax.add_collection(arrow_collection) # update view minx = numpy.amin(numpy.ravel(edge_pos[:, :, 0])) maxx = numpy.amax(numpy.ravel(edge_pos[:, :, 0])) miny = numpy.amin(numpy.ravel(edge_pos[:, :, 1])) maxy = numpy.amax(numpy.ravel(edge_pos[:, :, 1])) w = maxx-minx h = maxy-miny padx, pady = 0.05*w, 0.05*h corners = (minx-padx, miny-pady), (maxx+padx, maxy+pady) ax.update_datalim(corners) ax.autoscale_view() # if arrow_collection: return edge_collection
def draw_networkx_edges( G, pos, edgelist=None, width=1.0, edge_color="k", style="solid", alpha=None, arrowstyle="-|>", arrowsize=3, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=True, label=None, node_size=300, nodelist=None, node_shape="o", connectionstyle=None, min_source_margin=0, min_target_margin=0, ): """Draw the edges of the graph G. Adjusted from networkx.""" try: import matplotlib.pyplot as plt from matplotlib.colors import colorConverter, Colormap, Normalize from matplotlib.collections import LineCollection from matplotlib.patches import FancyArrowPatch from numbers import Number except ImportError: raise ImportError("Matplotlib required for draw()") except RuntimeError: print("Matplotlib unable to open display") raise if ax is None: ax = plt.gca() if edgelist is None: edgelist = list(G.edges()) if not edgelist or len(edgelist) == 0: # no edges! return None if nodelist is None: nodelist = list(G.nodes()) # FancyArrowPatch handles color=None different from LineCollection if edge_color is None: edge_color = "k" # set edge positions edge_pos = np.asarray([(pos[e[0]], pos[e[1]]) for e in edgelist]) # Check if edge_color is an array of floats and map to edge_cmap. # This is the only case handled differently from matplotlib if ( np.iterable(edge_color) and (len(edge_color) == len(edge_pos)) and np.alltrue([isinstance(c, Number) for c in edge_color]) ): if edge_cmap is not None: assert isinstance(edge_cmap, Colormap) else: edge_cmap = plt.get_cmap() if edge_vmin is None: edge_vmin = min(edge_color) if edge_vmax is None: edge_vmax = max(edge_color) color_normal = Normalize(vmin=edge_vmin, vmax=edge_vmax) edge_color = [edge_cmap(color_normal(e)) for e in edge_color] if not G.is_directed() or not arrows: edge_collection = LineCollection( edge_pos, colors=edge_color, linewidths=width, antialiaseds=(1,), linestyle=style, transOffset=ax.transData, alpha=alpha, ) edge_collection.set_cmap(edge_cmap) edge_collection.set_clim(edge_vmin, edge_vmax) edge_collection.set_zorder(1) # edges go behind nodes edge_collection.set_label(label) ax.add_collection(edge_collection) return edge_collection arrow_collection = None if G.is_directed() and arrows: # Note: Waiting for someone to implement arrow to intersection with # marker. Meanwhile, this works well for polygons with more than 4 # sides and circle. def to_marker_edge(marker_size, marker): if marker in "s^>v<d": # `large` markers need extra space return np.sqrt(2 * marker_size) / 2 else: return np.sqrt(marker_size) / 2 # Draw arrows with `matplotlib.patches.FancyarrowPatch` arrow_collection = [] mutation_scale = arrowsize # scale factor of arrow head # FancyArrowPatch doesn't handle color strings arrow_colors = colorConverter.to_rgba_array(edge_color, alpha) for i, (src, dst) in enumerate(edge_pos): x1, y1 = src x2, y2 = dst shrink_source = 0 # space from source to tail shrink_target = 0 # space from head to target if np.iterable(node_size): # many node sizes source, target = edgelist[i][:2] source_node_size = node_size[nodelist.index(source)] target_node_size = node_size[nodelist.index(target)] shrink_source = to_marker_edge(source_node_size, node_shape) shrink_target = to_marker_edge(target_node_size, node_shape) else: shrink_source = shrink_target = to_marker_edge(node_size, node_shape) if shrink_source < min_source_margin: shrink_source = min_source_margin if shrink_target < min_target_margin: shrink_target = min_target_margin if len(arrow_colors) == len(edge_pos): arrow_color = arrow_colors[i] elif len(arrow_colors) == 1: arrow_color = arrow_colors[0] else: # Cycle through colors arrow_color = arrow_colors[i % len(arrow_colors)] if np.iterable(width): if len(width) == len(edge_pos): line_width = width[i] else: line_width = width[i % len(width)] else: line_width = width arrow = FancyArrowPatch( (x1, y1), (x2, y2), arrowstyle=arrowstyle, shrinkA=shrink_source, shrinkB=shrink_target, mutation_scale=mutation_scale, color=arrow_color, linewidth=line_width, connectionstyle=connectionstyle, linestyle=style, zorder=1, ) # arrows go behind nodes # There seems to be a bug in matplotlib to make collections of # FancyArrowPatch instances. Until fixed, the patches are added # individually to the axes instance. arrow_collection.append(arrow) ax.add_patch(arrow) # update view minx = np.amin(np.ravel(edge_pos[:, :, 0])) maxx = np.amax(np.ravel(edge_pos[:, :, 0])) miny = np.amin(np.ravel(edge_pos[:, :, 1])) maxy = np.amax(np.ravel(edge_pos[:, :, 1])) w = maxx - minx h = maxy - miny padx, pady = 0.05 * w, 0.05 * h corners = (minx - padx, miny - pady), (maxx + padx, maxy + pady) ax.update_datalim(corners) ax.autoscale_view() ax.tick_params( axis="both", which="both", bottom=False, left=False, labelbottom=False, labelleft=False, ) return arrow_collection
def QuickPlot(conf=None, bodies=None, xaxis=None, yaxis=None, aaxis=None, interactive=True, nolog=False): ''' ''' # Assign defaults if conf is None: conf = GetConf() if bodies is None: bodies = conf.bodies if xaxis is None: xaxis = conf.xaxis if yaxis is None: yaxis = conf.yaxis if aaxis is None: aaxis = conf.aaxis if nolog: conf.xlog = False conf.ylog = False # Output object output = GetArrays(bodies=bodies) # Names of all available params param_names = list( set([param.name for body in output.bodies for param in body.params])) if len(param_names) == 0: raise Exception("There don't seem to be any parameters to be plotted.") # The x-axis parameter x = param_names[np.argmax( np.array( [name.lower().startswith(xaxis.lower()) for name in param_names]))] if not x.lower().startswith(xaxis.lower()): raise Exception('Parameter not understood: %s.' % xaxis) # The array of y-axis parameters # Ensure it's a list if type(yaxis) is str: yaxis = [yaxis] if len(yaxis) == 0: # User didn't specify any; let's plot all of them yarr = list(sorted(set(param_names) - set([x]))) else: yarr = [] for yn in yaxis: y = param_names[np.argmax( np.array([ name.lower().startswith(yn.lower()) for name in param_names ]))] if not y.lower().startswith(yn.lower()): raise Exception('Parameter not found: %s.' % yn) yarr.append(y) # The alpha parameter if aaxis is not None and aaxis != 'None': a = param_names[np.argmax( np.array([ name.lower().startswith(aaxis.lower()) for name in param_names ]))] if not a.lower().startswith(aaxis.lower()): raise Exception('Parameter not found: %s.' % aaxis) # We will only plot up to a maximum of ``conf.maxrows`` if columns are off if (not conf.columns) and len(yarr) > conf.maxrows: yarr = yarr[:conf.maxrows] rows = conf.maxrows elif conf.columns and len(yarr) > conf.maxrows: columns = int(np.ceil(float(len(yarr)) / conf.maxrows)) rows = conf.maxrows else: columns = 1 rows = len(yarr) # Correct for border cases if len(yarr) <= (columns * (rows - 1)): rows -= 1 elif len(yarr) <= ((columns - 1) * rows): columns -= 1 # Set up the plot # See http://stackoverflow.com/a/19627237 mpl.rcParams['figure.autolayout'] = False fig = pl.figure(1, figsize=(conf.figwidth, conf.figheight)) gs = gridspec.GridSpec(rows, columns) for i, ss in enumerate(gs): if i == 0: ax = [fig.add_subplot(gs[0])] else: ax.append(fig.add_subplot(ss, sharex=ax[0])) # Hide empty subplots empty = range(len(yarr), rows * columns) for i in empty: ax[i].set_visible(False) # Loop over all parameters (one subplot per each) for i, y in enumerate(yarr): # Axes limits ymin = np.inf ymax = -np.inf xmin = np.inf xmax = -np.inf # Loop over all bodies for b, body in enumerate(output.bodies): # Get indices of the parameters if len(body.params): xi = np.where( np.array([param.name for param in body.params]) == x)[0] yi = np.where( np.array([param.name for param in body.params]) == y)[0] else: xi = np.array([], dtype=int) yi = np.array([], dtype=int) if aaxis is not None and aaxis != 'None': ai = np.where( a == np.array([param.name for param in body.params]))[0] else: ai = [] # Are both x and y arrays preset for this body? if len(xi) and len(yi): xi = xi[0] yi = yi[0] else: continue if len(ai): ai = ai[0] else: ai = None # Get the arrays xpts = body.params[xi].array ypts = body.params[yi].array # Decide whether to plot individual legends if conf.legend_all: label = body.name else: label = None # Should we plot the first point if x = 0? if xpts[0] == 0. and conf.xlog and conf.skip_xzero_log: xpts = xpts[1:] ypts = ypts[1:] # Plot the curve if ai is None: # A simple solid color line ax[i].plot(xpts, ypts, conf.line_styles[b], label=label, lw=conf.linewidth) else: # Let's get fancy: make the curve opacity proportional # to the `aaxis` parameter apts = body.params[ai].array # Make logarithmic if necessary if conf.alog: if apts[0] == 0.: apts = apts[1:] apts = np.log10(apts) points = np.array([xpts, ypts]).T.reshape(-1, 1, 2) segments = np.concatenate([points[:-1], points[1:]], axis=1) lc = LineCollection( segments, cmap=AlphaMap( *ColorConverter().to_rgb(conf.line_styles[b][0])), norm=pl.Normalize(np.nanmin(apts), np.nanmax(apts))) lc.set_array(apts) lc.set_linestyle(conf.line_styles[b][1:]) lc.set_linewidth(conf.linewidth) lc.set_label(label) ax[i].add_collection(lc) # Limits if np.nanmin(xpts) < xmin: xmin = np.nanmin(xpts) if np.nanmin(ypts) < ymin: ymin = np.nanmin(ypts) if np.nanmax(xpts) > xmax: xmax = np.nanmax(xpts) if np.nanmax(ypts) > ymax: ymax = np.nanmax(ypts) # Labels ax[i].set_xlabel(body.params[xi].label(conf.short_labels), fontsize=conf.xlabel_fontsize) ax[i].set_ylabel(body.params[yi].label(conf.short_labels, conf.maxylabelsize), fontsize=conf.ylabel_fontsize) # Tick sizes for tick in ax[i].xaxis.get_major_ticks(): tick.label.set_fontsize(conf.xticklabel_fontsize) for tick in ax[i].yaxis.get_major_ticks(): tick.label.set_fontsize(conf.yticklabel_fontsize) # Log scale? if conf.xlog: if np.log(xmax) - np.log(xmin) > conf.xlog: if xmin > 0: ax[i].set_xscale('log') if conf.ylog: if np.log(ymax) - np.log(ymin) > conf.ylog: if ymin > 0: ax[i].set_yscale('log') # Tighten things up a bit if i < len(yarr) - columns: if conf.tight_layout: ax[i].set_xticklabels([]) ax[i].set_xlabel('') # Add y-axis margins (doesn't work well with log) ax[i].margins(0., conf.ymargin) # Add a legend if conf.legend_all: ax[i].legend(loc=conf.legend_loc, fontsize=conf.legend_fontsize) elif i == 0: xlim = ax[i].get_xlim() ylim = ax[i].get_ylim() for b, body in enumerate(output.bodies): # HACK: Plot a line of length zero in the center # of the plot so that it will show up on the legend foo = ax[i].plot([(xlim[0] + xlim[1]) / 2.], [(ylim[0] + ylim[1]) / 2.], conf.line_styles[b], label=body.name) ax[i].legend(loc=conf.legend_loc, fontsize=conf.legend_fontsize) # Add title if conf.title: if len(yarr) == 1: pl.title('VPLANET: %s' % output.sysname, fontsize=24) else: pl.suptitle('VPLANET: %s' % output.sysname, fontsize=24) try: gs.tight_layout(fig, rect=[0, 0.03, 1, 0.95]) except: # No biggie pass # Show or save? if interactive and conf.interactive: pl.show() else: fig.savefig(conf.figname, bbox_inches='tight') return fig, ax
def _plot_skeleton(neuron, color, method, ax, **kwargs): """Plot skeleton.""" depth_coloring = kwargs.get('depth_coloring', False) linewidth = kwargs.get('linewidth', kwargs.get('lw', .5)) linestyle = kwargs.get('linestyle', kwargs.get('ls', '-')) alpha = kwargs.get('alpha', .9) norm = kwargs.get('norm') plot_soma = kwargs.get('soma', True) group_neurons = kwargs.get('group_neurons', False) # Generate by-segment coordinates coords = segments_to_coords(neuron, neuron.segments, modifier=(1, 1, 1)) if method == '2d': if not depth_coloring: # We have to add (None, None, None) to the end of each # slab to make that line discontinuous there coords = np.vstack( [np.append(t, [[None] * 3], axis=0) for t in coords]) this_line = mlines.Line2D(coords[:, 0], coords[:, 1], lw=linewidth, ls=linestyle, alpha=alpha, color=color, label=f'{getattr(neuron, "name", "NA")} - #{neuron.id}') ax.add_line(this_line) else: coords = tn_pairs_to_coords(neuron, modifier=(1, 1, 1)) lc = LineCollection(coords[:, :, [0, 1]], cmap='jet', norm=norm) lc.set_array(neuron.nodes.loc[neuron.nodes.parent_id >= 0, 'z'].values) lc.set_linewidth(linewidth) lc.set_alpha(alpha) lc.set_linestyle(linestyle) lc.set_label(f'{getattr(neuron, "name", "NA")} - #{neuron.id}') ax.add_collection(lc) if plot_soma and not isinstance(neuron.soma, type(None)): soma = utils.make_iterable(neuron.soma) # If soma detection is messed up we might end up producing # dozens of soma which will freeze the kernel if len(soma) >= 10: logger.warning(f'{neuron.id} - {len(soma)} somas found.') for s in soma: n = neuron.nodes.set_index('node_id').loc[s] r = getattr(n, neuron.soma_radius) if isinstance(neuron.soma_radius, str) else neuron.soma_radius if depth_coloring: color = mpl.cm.jet(norm(n.z)) s = mpatches.Circle((int(n.x), int(n.y)), radius=r, alpha=alpha, fill=True, fc=color, zorder=4, edgecolor='none') ax.add_patch(s) return None, None elif method in ['3d', '3d_complex']: # For simple scenes, add whole neurons at a time -> will speed # up rendering if method == '3d': if depth_coloring: this_coords = tn_pairs_to_coords(neuron, modifier=(1, 1, 1))[:, :, [0, 1, 2]] else: this_coords = [c[:, [0, 1, 2]] for c in coords] lc = Line3DCollection(this_coords, color=color, label=neuron.id, alpha=alpha, cmap=mpl.cm.jet if depth_coloring else None, lw=linewidth, linestyle=linestyle) if group_neurons: lc.set_gid(neuron.id) # Need to get this before adding data line3D_collection = ax.add_collection3d(lc) # For complex scenes, add each segment as a single collection # -> helps preventing Z-order errors elif method == '3d_complex': for c in coords: lc = Line3DCollection([c], color=color, lw=linewidth, alpha=alpha, linestyle=linestyle) if group_neurons: lc.set_gid(neuron.id) ax.add_collection3d(lc) line3D_collection = None surf3D_collections = [] if plot_soma and not isinstance(neuron.soma, type(None)): soma = utils.make_iterable(neuron.soma) # If soma detection is messed up we might end up producing # dozens of soma which will freeze the kernel if len(soma) >= 5: logger.warning(f'Neuron {neuron.id} appears to have {len(soma)}' ' somas. Skipping plotting its somas.') else: for s in soma: n = neuron.nodes.set_index('node_id').loc[s] r = getattr(n, neuron.soma_radius) if isinstance(neuron.soma_radius, str) else neuron.soma_radius resolution = 20 u = np.linspace(0, 2 * np.pi, resolution) v = np.linspace(0, np.pi, resolution) x = r * np.outer(np.cos(u), np.sin(v)) + n.x y = r * np.outer(np.sin(u), np.sin(v)) + n.y z = r * np.outer(np.ones(np.size(u)), np.cos(v)) + n.z surf = ax.plot_surface(x, y, z, color=color, shade=False, alpha=alpha) if group_neurons: surf.set_gid(neuron.id) surf3D_collections.append(surf) return line3D_collection, surf3D_collections
def readshapefileext(self, shapefile, name, drawbounds=True, zorder=None, linewidth=0.5, color='k', antialiased=1, ax=None, default_encoding='utf-8', linestyle='-'): """ Read in shape file, optionally draw boundaries on map. .. note:: - Assumes shapes are 2D - only works for Point, MultiPoint, Polyline and Polygon shapes. - vertices/points must be in geographic (lat/lon) coordinates. Mandatory Arguments: .. tabularcolumns:: |l|L| ============== ==================================================== Argument Description ============== ==================================================== shapefile path to shapefile components. Example: shapefile='/home/jeff/esri/world_borders' assumes that world_borders.shp, world_borders.shx and world_borders.dbf live in /home/jeff/esri. name name for Basemap attribute to hold the shapefile vertices or points in map projection coordinates. Class attribute name+'_info' is a list of dictionaries, one for each shape, containing attributes of each shape from dbf file, For example, if name='counties', self.counties will be a list of x,y vertices for each shape in map projection coordinates and self.counties_info will be a list of dictionaries with shape attributes. Rings in individual Polygon shapes are split out into separate polygons, and additional keys 'RINGNUM' and 'SHAPENUM' are added to the shape attribute dictionary. ============== ==================================================== The following optional keyword arguments are only relevant for Polyline and Polygon shape types, for Point and MultiPoint shapes they are ignored. .. tabularcolumns:: |l|L| ============== ==================================================== Keyword Description ============== ==================================================== drawbounds draw boundaries of shapes (default True). zorder shape boundary zorder (if not specified, default for mathplotlib.lines.LineCollection is used). linewidth shape boundary line width (default 0.5) color shape boundary line color (default black) antialiased antialiasing switch for shape boundaries (default True). ax axes instance (overrides default axes instance) ============== ==================================================== A tuple (num_shapes, type, min, max) containing shape file info is returned. num_shapes is the number of shapes, type is the type code (one of the SHPT* constants defined in the shapelib module, see http://shapelib.maptools.org/shp_api.html) and min and max are 4-element lists with the minimum and maximum values of the vertices. If ``drawbounds=True`` a matplotlib.patches.LineCollection object is appended to the tuple. """ import shapefile as shp from shapefile import Reader shp.default_encoding = default_encoding if not os.path.exists('%s.shp' % shapefile): raise IOError('cannot locate %s.shp' % shapefile) if not os.path.exists('%s.shx' % shapefile): raise IOError('cannot locate %s.shx' % shapefile) if not os.path.exists('%s.dbf' % shapefile): raise IOError('cannot locate %s.dbf' % shapefile) # open shapefile, read vertices for each object, convert # to map projection coordinates (only works for 2D shape types). try: shf = Reader(shapefile) except: raise IOError('error reading shapefile %s.shp' % shapefile) fields = shf.fields coords = [] attributes = [] msg = dedent(""" shapefile must have lat/lon vertices - it looks like this one has vertices in map projection coordinates. You can convert the shapefile to geographic coordinates using the shpproj utility from the shapelib tools (http://shapelib.maptools.org/shapelib-tools.html)""") shptype = shf.shapes()[0].shapeType bbox = shf.bbox.tolist() info = (shf.numRecords, shptype, bbox[0:2] + [0., 0.], bbox[2:] + [0., 0.]) npoly = 0 for shprec in shf.shapeRecords(): shp = shprec.shape rec = shprec.record npoly = npoly + 1 if shptype != shp.shapeType: raise ValueError( 'readshapefile can only handle a single shape type per file' ) if shptype not in [1, 3, 5, 8]: raise ValueError( 'readshapefile can only handle 2D shape types') verts = shp.points if shptype in [1, 8]: # a Point or MultiPoint shape. lons, lats = list(zip(*verts)) if max(lons) > 721. or min(lons) < -721. or max( lats) > 90.01 or min(lats) < -90.01: raise ValueError(msg) # if latitude is slightly greater than 90, truncate to 90 lats = [max(min(lat, 90.0), -90.0) for lat in lats] if len(verts) > 1: # MultiPoint x, y = self(lons, lats) coords.append(list(zip(x, y))) else: # single Point x, y = self(lons[0], lats[0]) coords.append((x, y)) attdict = {} for r, key in zip(rec, fields[1:]): attdict[key[0]] = r attributes.append(attdict) else: # a Polyline or Polygon shape. parts = shp.parts.tolist() ringnum = 0 for indx1, indx2 in zip(parts, parts[1:] + [len(verts)]): ringnum = ringnum + 1 lons, lats = list(zip(*verts[indx1:indx2])) if max(lons) > 721. or min(lons) < -721. or max( lats) > 90.01 or min(lats) < -90.01: raise ValueError(msg) # if latitude is slightly greater than 90, truncate to 90 lats = [max(min(lat, 90.0), -90.0) for lat in lats] x, y = self(lons, lats) coords.append(list(zip(x, y))) attdict = {} for r, key in zip(rec, fields[1:]): attdict[key[0]] = r # add information about ring number to dictionary. attdict['RINGNUM'] = ringnum attdict['SHAPENUM'] = npoly attributes.append(attdict) # draw shape boundaries for polylines, polygons using LineCollection. if shptype not in [1, 8] and drawbounds: # get current axes instance (if none specified). ax = ax or self._check_ax() # make LineCollections for each polygon. lines = LineCollection(coords, antialiaseds=(1, )) lines.set_color(color) lines.set_linewidth(linewidth) lines.set_linestyle(linestyle) lines.set_label('_nolabel_') if zorder is not None: lines.set_zorder(zorder) ax.add_collection(lines) # set axes limits to fit map region. self.set_axes_limits(ax=ax) # clip boundaries to map limbs lines, c = self._cliplimb(ax, lines) info = info + (lines, ) self.__dict__[name] = coords self.__dict__[name + '_info'] = attributes return info
def _plot_skeleton(neuron, color, method, ax, **kwargs): """Plot skeleton.""" depth_coloring = kwargs.get('depth_coloring', False) linewidth = kwargs.get('linewidth', kwargs.get('lw', .5)) linestyle = kwargs.get('linestyle', kwargs.get('ls', '-')) alpha = kwargs.get('alpha', .9) norm = kwargs.get('norm') plot_soma = kwargs.get('soma', True) group_neurons = kwargs.get('group_neurons', False) view = kwargs.get('view', ('x', 'y')) if method == '2d': if not depth_coloring and not (isinstance(color, np.ndarray) and color.ndim == 2): # Generate by-segment coordinates coords = segments_to_coords(neuron, neuron.segments, modifier=(1, 1, 1)) # We have to add (None, None, None) to the end of each # slab to make that line discontinuous there coords = np.vstack( [np.append(t, [[None] * 3], axis=0) for t in coords]) x, y = _parse_view2d(coords, view) this_line = mlines.Line2D( x, y, lw=linewidth, ls=linestyle, alpha=alpha, color=color, label=f'{getattr(neuron, "name", "NA")} - #{neuron.id}') ax.add_line(this_line) else: coords = tn_pairs_to_coords(neuron, modifier=(1, 1, 1)) xy = _parse_view2d(coords, view) lc = LineCollection(xy, cmap='jet' if depth_coloring else None, norm=norm if depth_coloring else None, joinstyle='round') lc.set_linewidth(linewidth) lc.set_linestyle(linestyle) lc.set_label(f'{getattr(neuron, "name", "NA")} - #{neuron.id}') if depth_coloring: lc.set_alpha(alpha) lc.set_array(neuron.nodes.loc[neuron.nodes.parent_id >= 0, 'z'].values) elif (isinstance(color, np.ndarray) and color.ndim == 2): # If we have a color for each node, we need to drop the roots if color.shape[1] != coords.shape[0]: lc.set_color(color[neuron.nodes.parent_id.values >= 0]) else: lc.set_color(color) ax.add_collection(lc) if plot_soma and np.any(neuron.soma): soma = utils.make_iterable(neuron.soma) # If soma detection is messed up we might end up producing # dozens of soma which will freeze the kernel if len(soma) >= 10: logger.warning(f'{neuron.id} - {len(soma)} somas found.') for s in soma: if isinstance(color, np.ndarray) and color.ndim > 1: s_ix = np.where(neuron.nodes.node_id == s)[0][0] soma_color = color[s_ix] else: soma_color = color n = neuron.nodes.set_index('node_id').loc[s] r = getattr(n, neuron.soma_radius) if isinstance( neuron.soma_radius, str) else neuron.soma_radius if depth_coloring: d = [n.x, n.y, n.z][_get_depth_axis(view)] soma_color = mpl.cm.jet(norm(d)) sx, sy = _parse_view2d(np.array([[n.x, n.y, n.z]]), view) c = mpatches.Circle((sx[0], sy[0]), radius=r, alpha=alpha, fill=True, fc=soma_color, zorder=4, edgecolor='none') ax.add_patch(c) return None, None elif method in ['3d', '3d_complex']: # For simple scenes, add whole neurons at a time to speed up rendering if method == '3d': if (isinstance(color, np.ndarray) and color.ndim == 2) or depth_coloring: coords = tn_pairs_to_coords(neuron, modifier=(1, 1, 1)) # If we have a color for each node, we need to drop the roots if isinstance( color, np.ndarray) and color.shape[1] != coords.shape[0]: line_color = color[neuron.nodes.parent_id.values >= 0] else: line_color = color else: # Generate by-segment coordinates coords = segments_to_coords(neuron, neuron.segments, modifier=(1, 1, 1)) line_color = color lc = Line3DCollection( coords, color=line_color, label=neuron.id, alpha=alpha if not kwargs.get('shade_by', False) else None, cmap=mpl.cm.jet if depth_coloring else None, lw=linewidth, joinstyle='round', linestyle=linestyle) if group_neurons: lc.set_gid(neuron.id) # Need to get this before adding data line3D_collection = lc ax.add_collection3d(lc) # For complex scenes, add each segment as a single collection # -> helps reducing Z-order errors elif method == '3d_complex': # Generate by-segment coordinates coords = segments_to_coords(neuron, neuron.segments, modifier=(1, 1, 1)) for c in coords: lc = Line3DCollection([c], color=color, lw=linewidth, alpha=alpha, linestyle=linestyle) if group_neurons: lc.set_gid(neuron.id) ax.add_collection3d(lc) line3D_collection = None surf3D_collections = [] if plot_soma and not isinstance(getattr(neuron, 'soma', None), type(None)): soma = utils.make_iterable(neuron.soma) # If soma detection is messed up we might end up producing # dozens of soma which will freeze the kernel if len(soma) >= 5: logger.warning( f'Neuron {neuron.id} appears to have {len(soma)}' ' somas. Skipping plotting its somas.') else: for s in soma: if isinstance(color, np.ndarray) and color.ndim > 1: s_ix = np.where(neuron.nodes.node_id == s)[0][0] soma_color = color[s_ix] else: soma_color = color n = neuron.nodes.set_index('node_id').loc[s] r = getattr(n, neuron.soma_radius) if isinstance( neuron.soma_radius, str) else neuron.soma_radius resolution = 20 u = np.linspace(0, 2 * np.pi, resolution) v = np.linspace(0, np.pi, resolution) x = r * np.outer(np.cos(u), np.sin(v)) + n.x y = r * np.outer(np.sin(u), np.sin(v)) + n.y z = r * np.outer(np.ones(np.size(u)), np.cos(v)) + n.z surf = ax.plot_surface(x, y, z, color=soma_color, shade=False, alpha=alpha) if group_neurons: surf.set_gid(neuron.id) surf3D_collections.append(surf) return line3D_collection, surf3D_collections
def drawMap(filename,names,totalCount): countries = json.loads(open(filename).read())["features"] for country in countries: name = country["properties"]["name"] polygonList = country["geometry"]["coordinates"] polygonType = country["geometry"]["type"] shpsegs = [] print "Processing %s [%d/%s] ..." % (unicode(name).encode("utf-8"), len(polygonList), polygonType) maxverts = 0 maxindex = 0 counter = 0 for polygon in polygonList: if polygonType == "MultiPolygon": for p in polygon: if len(p) == 1: p = p[0] if p[0] != p[-1]: p.append(p[0]) lons, lats = zip(*p) x, y = m(lons, lats) shpsegs.append(zip(x,y)) if len(x) > maxverts: maxverts = len(x) maxindex = counter counter+=1 else: if len(polygon) == 1: polygon = polygon[0] if polygon[0] != polygon[-1]: polygon.append(polygon[0]) lons, lats = zip(*polygon) x, y = m(lons, lats) shpsegs.append(zip(x,y)) if len(x) > maxverts: maxverts = len(x) maxindex = counter counter+=1 # Compute centroid centroid = Polygon(shpsegs[maxindex]).centroid # Create country shape lines = LineCollection(shpsegs,antialiaseds=(1,)) lines.set_edgecolors('k') lines.set_linewidth(0.5) #import brewer2mpl #colormap = brewer2mpl.get_map('Paired', 'qualitative', 12).mpl_colors colormap = [(0.6509803921568628, 0.807843137254902, 0.8901960784313725), (0.12156862745098039, 0.47058823529411764, 0.7058823529411765), (0.6980392156862745, 0.8745098039215686, 0.5411764705882353), (0.2, 0.6274509803921569, 0.17254901960784313), (0.984313725490196, 0.6039215686274509, 0.6), (0.8901960784313725, 0.10196078431372549, 0.10980392156862745), (0.9921568627450981, 0.7490196078431373, 0.43529411764705883), (1.0, 0.4980392156862745, 0.0), (0.792156862745098, 0.6980392156862745, 0.8392156862745098), (0.41568627450980394, 0.23921568627450981, 0.6039215686274509), (1.0, 1.0, 0.6), (0.6941176470588235, 0.34901960784313724, 0.1568627450980392)] # Add color and label if covered by Safecast if name in names.keys(): color = colormap[(int((float(names[name][0])/totalCount)*12)+1)] lines.set_label("%s - %0.1fK (%d)" % (name, names[name][0]/1000.0, names[name][1]) ) #lines.set_label(name) lines.set_edgecolors(color) lines.set_facecolors(color) label = plt.text(centroid.x, centroid.y, "%d" % names[name][1], fontsize=5, ha='center', va='center', color='k', fontweight='bold') plt.setp(label, path_effects=[PathEffects.withStroke(linewidth=2, foreground="w")]) ax.add_collection(lines)
def draw_networkx_edges(G, pos, edgelist=None, width=1.0, edge_color='k', style='solid', alpha=1.0, arrowstyle='-|>', arrowsize=10, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=True, label=None, node_size=300, nodelist=None, node_shape="o", **kwds): """Draw the edges of the graph G. This draws only the edges of the graph G. Parameters ---------- G : graph A networkx graph pos : dictionary A dictionary with nodes as keys and positions as values. Positions should be sequences of length 2. edgelist : collection of edge tuples Draw only specified edges(default=G.edges()) width : float, or array of floats Line width of edges (default=1.0) edge_color : color string, or array of floats Edge color. Can be a single color format string (default='r'), or a sequence of colors with the same length as edgelist. If numeric values are specified they will be mapped to colors using the edge_cmap and edge_vmin,edge_vmax parameters. style : string Edge line style (default='solid') (solid|dashed|dotted,dashdot) alpha : float The edge transparency (default=1.0) edge_ cmap : Matplotlib colormap Colormap for mapping intensities of edges (default=None) edge_vmin,edge_vmax : floats Minimum and maximum for edge colormap scaling (default=None) ax : Matplotlib Axes object, optional Draw the graph in the specified Matplotlib axes. arrows : bool, optional (default=True) For directed graphs, if True draw arrowheads. Note: Arrows will be the same color as edges. arrowstyle : str, optional (default='-|>') For directed graphs, choose the style of the arrow heads. See :py:class: `matplotlib.patches.ArrowStyle` for more options. arrowsize : int, optional (default=10) For directed graphs, choose the size of the arrow head head's length and width. See :py:class: `matplotlib.patches.FancyArrowPatch` for attribute `mutation_scale` for more info. label : [None| string] Label for legend Returns ------- matplotlib.collection.LineCollection `LineCollection` of the edges list of matplotlib.patches.FancyArrowPatch `FancyArrowPatch` instances of the directed edges Depending whether the drawing includes arrows or not. Notes ----- For directed graphs, arrows are drawn at the head end. Arrows can be turned off with keyword arrows=False. Be sure to include `node_size' as a keyword argument; arrows are drawn considering the size of nodes. Examples -------- >>> G = nx.dodecahedral_graph() >>> edges = nx.draw_networkx_edges(G, pos=nx.spring_layout(G)) >>> G = nx.DiGraph() >>> G.add_edges_from([(1, 2), (1, 3), (2, 3)]) >>> arcs = nx.draw_networkx_edges(G, pos=nx.spring_layout(G)) >>> alphas = [0.3, 0.4, 0.5] >>> for i, arc in enumerate(arcs): # change alpha values of arcs ... arc.set_alpha(alphas[i]) Also see the NetworkX drawing examples at https://networkx.github.io/documentation/latest/auto_examples/index.html See Also -------- draw() draw_networkx() draw_networkx_nodes() draw_networkx_labels() draw_networkx_edge_labels() """ try: import matplotlib import matplotlib.pyplot as plt import matplotlib.cbook as cb from matplotlib.colors import colorConverter, Colormap, Normalize from matplotlib.collections import LineCollection from matplotlib.patches import FancyArrowPatch import numpy as np except ImportError: raise ImportError("Matplotlib required for draw()") except RuntimeError: print("Matplotlib unable to open display") raise if ax is None: ax = plt.gca() if edgelist is None: edgelist = list(G.edges()) if not edgelist or len(edgelist) == 0: # no edges! return None if nodelist is None: nodelist = list(G.nodes()) # set edge positions edge_pos = np.asarray([(pos[e[0]], pos[e[1]]) for e in edgelist]) if not cb.iterable(width): lw = (width,) else: lw = width if not is_string_like(edge_color) \ and cb.iterable(edge_color) \ and len(edge_color) == len(edge_pos): if np.alltrue([is_string_like(c) for c in edge_color]): # (should check ALL elements) # list of color letters such as ['k','r','k',...] edge_colors = tuple([colorConverter.to_rgba(c, alpha) for c in edge_color]) elif np.alltrue([not is_string_like(c) for c in edge_color]): # If color specs are given as (rgb) or (rgba) tuples, we're OK if np.alltrue([cb.iterable(c) and len(c) in (3, 4) for c in edge_color]): edge_colors = tuple(edge_color) else: # numbers (which are going to be mapped with a colormap) edge_colors = None else: raise ValueError('edge_color must contain color names or numbers') else: if is_string_like(edge_color) or len(edge_color) == 1: edge_colors = (colorConverter.to_rgba(edge_color, alpha), ) else: msg = 'edge_color must be a color or list of one color per edge' raise ValueError(msg) if (not G.is_directed() or not arrows): edge_collection = LineCollection(edge_pos, colors=edge_colors, linewidths=lw, antialiaseds=(1,), linestyle=style, transOffset=ax.transData, ) edge_collection.set_zorder(1) # edges go behind nodes edge_collection.set_label(label) ax.add_collection(edge_collection) # Note: there was a bug in mpl regarding the handling of alpha values # for each line in a LineCollection. It was fixed in matplotlib by # r7184 and r7189 (June 6 2009). We should then not set the alpha # value globally, since the user can instead provide per-edge alphas # now. Only set it globally if provided as a scalar. if cb.is_numlike(alpha): edge_collection.set_alpha(alpha) if edge_colors is None: if edge_cmap is not None: assert(isinstance(edge_cmap, Colormap)) edge_collection.set_array(np.asarray(edge_color)) edge_collection.set_cmap(edge_cmap) if edge_vmin is not None or edge_vmax is not None: edge_collection.set_clim(edge_vmin, edge_vmax) else: edge_collection.autoscale() return edge_collection arrow_collection = None if G.is_directed() and arrows: # Note: Waiting for someone to implement arrow to intersection with # marker. Meanwhile, this works well for polygons with more than 4 # sides and circle. def to_marker_edge(marker_size, marker): if marker in "s^>v<d": # `large` markers need extra space return np.sqrt(2 * marker_size) / 2 else: return np.sqrt(marker_size) / 2 # Draw arrows with `matplotlib.patches.FancyarrowPatch` arrow_collection = [] mutation_scale = arrowsize # scale factor of arrow head arrow_colors = edge_colors if arrow_colors is None: if edge_cmap is not None: assert(isinstance(edge_cmap, Colormap)) else: edge_cmap = plt.get_cmap() # default matplotlib colormap if edge_vmin is None: edge_vmin = min(edge_color) if edge_vmax is None: edge_vmax = max(edge_color) color_normal = Normalize(vmin=edge_vmin, vmax=edge_vmax) for i, (src, dst) in enumerate(edge_pos): x1, y1 = src x2, y2 = dst arrow_color = None line_width = None shrink_source = 0 # space from source to tail shrink_target = 0 # space from head to target if cb.iterable(node_size): # many node sizes src_node, dst_node = edgelist[i] index_node = nodelist.index(dst_node) marker_size = node_size[index_node] shrink_target = to_marker_edge(marker_size, node_shape) else: shrink_target = to_marker_edge(node_size, node_shape) if arrow_colors is None: arrow_color = edge_cmap(color_normal(edge_color[i])) elif len(arrow_colors) > 1: arrow_color = arrow_colors[i] else: arrow_color = arrow_colors[0] if len(lw) > 1: line_width = lw[i] else: line_width = lw[0] arrow = FancyArrowPatch((x1, y1), (x2, y2), arrowstyle=arrowstyle, shrinkA=shrink_source, shrinkB=shrink_target, mutation_scale=mutation_scale, color=arrow_color, linewidth=line_width, zorder=1) # arrows go behind nodes # There seems to be a bug in matplotlib to make collections of # FancyArrowPatch instances. Until fixed, the patches are added # individually to the axes instance. arrow_collection.append(arrow) ax.add_patch(arrow) # update view minx = np.amin(np.ravel(edge_pos[:, :, 0])) maxx = np.amax(np.ravel(edge_pos[:, :, 0])) miny = np.amin(np.ravel(edge_pos[:, :, 1])) maxy = np.amax(np.ravel(edge_pos[:, :, 1])) w = maxx - minx h = maxy - miny padx, pady = 0.05 * w, 0.05 * h corners = (minx - padx, miny - pady), (maxx + padx, maxy + pady) ax.update_datalim(corners) ax.autoscale_view() return arrow_collection
def draw_animation_edges(G, pos, edgelist=None, width=1.0, edge_color='k', style='solid', alpha=1.0, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=True, label=None, **kwds): try: import matplotlib import matplotlib.pyplot as plt import matplotlib.cbook as cb from matplotlib.colors import colorConverter, Colormap from matplotlib.collections import LineCollection import numpy except ImportError: raise ImportError("Matplotlib required for draw()") except RuntimeError: print("Matplotlib unable to open display") raise if ax is None: ax = plt.gca() if edgelist is None: edgelist = list(G.edges()) if not edgelist or len(edgelist) == 0: # no edges! return None # set edge positions box_pos = numpy.asarray([(pos[e[0]], pos[e[1]]) for e in edgelist]) p = 0.25 edge_pos = [] for edge in edgelist: src, dst = np.array(pos[edge[0]]), np.array(pos[edge[1]]) s = dst - src # src = src + p * s # Box at beginning # dst = src + (1-p) * s # Box at the end dst = src # No edge at all edge_pos.append((src, dst)) edge_pos = numpy.asarray(edge_pos) if not cb.iterable(width): lw = (width, ) else: lw = width if not cb.is_scalar_or_string(edge_color) \ and cb.iterable(edge_color) \ and len(edge_color) == len(edge_pos): if numpy.alltrue([cb.is_scalar_or_string(c) for c in edge_color]): # (should check ALL elements) # list of color letters such as ['k','r','k',...] edge_colors = tuple( [colorConverter.to_rgba(c, alpha) for c in edge_color]) elif numpy.alltrue( [not cb.is_scalar_or_string(c) for c in edge_color]): # If color specs are given as (rgb) or (rgba) tuples, we're OK if numpy.alltrue( [cb.iterable(c) and len(c) in (3, 4) for c in edge_color]): edge_colors = tuple(edge_color) else: # numbers (which are going to be mapped with a colormap) edge_colors = None else: raise ValueError( 'edge_color must consist of either color names or numbers') else: if cb.is_scalar_or_string(edge_color) or len(edge_color) == 1: edge_colors = (colorConverter.to_rgba(edge_color, alpha), ) else: raise ValueError( 'edge_color must be a single color or list of exactly m colors where m is the number or edges' ) ''' modEdgeColors = list(edge_colors) modEdgeColors = tuple(modEdgeColors + [colorConverter.to_rgba('w', alpha) for c in edge_color]) #print(modEdgeColors) edge_collection = LineCollection(np.asarray(list(edge_pos)*2), colors=modEdgeColors, linewidths=[6]*len(list(edge_colors))+[4]*len(list(edge_colors)), antialiaseds=(1,), linestyle=style, transOffset=ax.transData, ) ''' edge_collection = LineCollection( edge_pos, colors=edge_colors, linewidths=6, antialiaseds=(1, ), linestyle=style, transOffset=ax.transData, ) edge_collection.set_zorder(1) # edges go behind nodes edge_collection.set_label(label) ax.add_collection(edge_collection) tube_collection = LineCollection( edge_pos, colors=tuple([ colorConverter.to_rgba('lightgrey', alpha) for c in edge_color ]), linewidths=4, antialiaseds=(1, ), linestyle=style, transOffset=ax.transData, ) tube_collection.set_zorder(1) # edges go behind nodes tube_collection.set_label(label) ax.add_collection(tube_collection) # Note: there was a bug in mpl regarding the handling of alpha values for # each line in a LineCollection. It was fixed in matplotlib in r7184 and # r7189 (June 6 2009). We should then not set the alpha value globally, # since the user can instead provide per-edge alphas now. Only set it # globally if provided as a scalar. if cb.is_numlike(alpha): edge_collection.set_alpha(alpha) if edge_colors is None: if edge_cmap is not None: assert (isinstance(edge_cmap, Colormap)) edge_collection.set_array(numpy.asarray(edge_color)) edge_collection.set_cmap(edge_cmap) if edge_vmin is not None or edge_vmax is not None: edge_collection.set_clim(edge_vmin, edge_vmax) else: edge_collection.autoscale() box_collection = Utilities.get_boxes(edge_colors=edge_colors, edge_pos=box_pos) box_collection.set_zorder(1) # edges go behind nodes box_collection.set_label(label) ax.add_collection(box_collection) arrow_collection = Utilities.get_arrows_on_edges( edge_colors=edge_colors, edge_pos=box_pos) arrow_collection.set_zorder(0) if arrows: # Visualize them only if wanted ax.add_collection(arrow_collection) return edge_collection, box_collection, tube_collection, arrow_collection
def draw_networkx_edges(G, pos, edgelist=None, width=1.0, edge_color='k', style='solid', alpha=1.0, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=True, label=None, **kwds): """Draw the edges of the graph G. This draws only the edges of the graph G. Parameters ---------- G : graph A networkx graph pos : dictionary A dictionary with nodes as keys and positions as values. Positions should be sequences of length 2. edgelist : collection of edge tuples Draw only specified edges(default=G.edges()) width : float, or array of floats Line width of edges (default=1.0) edge_color : color string, or array of floats Edge color. Can be a single color format string (default='r'), or a sequence of colors with the same length as edgelist. If numeric values are specified they will be mapped to colors using the edge_cmap and edge_vmin,edge_vmax parameters. style : string Edge line style (default='solid') (solid|dashed|dotted,dashdot) alpha : float The edge transparency (default=1.0) edge_ cmap : Matplotlib colormap Colormap for mapping intensities of edges (default=None) edge_vmin,edge_vmax : floats Minimum and maximum for edge colormap scaling (default=None) ax : Matplotlib Axes object, optional Draw the graph in the specified Matplotlib axes. arrows : bool, optional (default=True) For directed graphs, if True draw arrowheads. label : [None| string] Label for legend Returns ------- matplotlib.collection.LineCollection `LineCollection` of the edges Notes ----- For directed graphs, "arrows" (actually just thicker stubs) are drawn at the head end. Arrows can be turned off with keyword arrows=False. Yes, it is ugly but drawing proper arrows with Matplotlib this way is tricky. Examples -------- >>> G = nx.dodecahedral_graph() >>> edges = nx.draw_networkx_edges(G, pos=nx.spring_layout(G)) Also see the NetworkX drawing examples at https://networkx.github.io/documentation/latest/auto_examples/index.html See Also -------- draw() draw_networkx() draw_networkx_nodes() draw_networkx_labels() draw_networkx_edge_labels() """ try: import matplotlib import matplotlib.pyplot as plt import matplotlib.cbook as cb from matplotlib.colors import colorConverter, Colormap from matplotlib.collections import LineCollection import numpy as np except ImportError: raise ImportError("Matplotlib required for draw()") except RuntimeError: print("Matplotlib unable to open display") raise if ax is None: ax = plt.gca() if edgelist is None: edgelist = list(G.edges()) if not edgelist or len(edgelist) == 0: # no edges! return None # set edge positions edge_pos = np.asarray([(pos[e[0]], pos[e[1]]) for e in edgelist]) if not cb.iterable(width): lw = (width,) else: lw = width if not cb.is_string_like(edge_color) \ and cb.iterable(edge_color) \ and len(edge_color) == len(edge_pos): if np.alltrue([cb.is_string_like(c) for c in edge_color]): # (should check ALL elements) # list of color letters such as ['k','r','k',...] edge_colors = tuple([colorConverter.to_rgba(c, alpha) for c in edge_color]) elif np.alltrue([not cb.is_string_like(c) for c in edge_color]): # If color specs are given as (rgb) or (rgba) tuples, we're OK if np.alltrue([cb.iterable(c) and len(c) in (3, 4) for c in edge_color]): edge_colors = tuple(edge_color) else: # numbers (which are going to be mapped with a colormap) edge_colors = None else: raise ValueError('edge_color must consist of either color names or numbers') else: if cb.is_string_like(edge_color) or len(edge_color) == 1: edge_colors = (colorConverter.to_rgba(edge_color, alpha), ) else: raise ValueError( 'edge_color must be a single color or list of exactly m colors where m is the number or edges') edge_collection = LineCollection(edge_pos, colors=edge_colors, linewidths=lw, antialiaseds=(1,), linestyle=style, transOffset=ax.transData, ) edge_collection.set_zorder(1) # edges go behind nodes edge_collection.set_label(label) ax.add_collection(edge_collection) # Note: there was a bug in mpl regarding the handling of alpha values for # each line in a LineCollection. It was fixed in matplotlib in r7184 and # r7189 (June 6 2009). We should then not set the alpha value globally, # since the user can instead provide per-edge alphas now. Only set it # globally if provided as a scalar. if cb.is_numlike(alpha): edge_collection.set_alpha(alpha) if edge_colors is None: if edge_cmap is not None: assert(isinstance(edge_cmap, Colormap)) edge_collection.set_array(np.asarray(edge_color)) edge_collection.set_cmap(edge_cmap) if edge_vmin is not None or edge_vmax is not None: edge_collection.set_clim(edge_vmin, edge_vmax) else: edge_collection.autoscale() arrow_collection = None if G.is_directed() and arrows: # a directed graph hack # draw thick line segments at head end of edge # waiting for someone else to implement arrows that will work arrow_colors = edge_colors a_pos = [] p = 1.0 - 0.25 # make head segment 25 percent of edge length for src, dst in edge_pos: x1, y1 = src x2, y2 = dst dx = x2 - x1 # x offset dy = y2 - y1 # y offset d = np.sqrt(float(dx**2 + dy**2)) # length of edge if d == 0: # source and target at same position continue if dx == 0: # vertical edge xa = x2 ya = dy * p + y1 if dy == 0: # horizontal edge ya = y2 xa = dx * p + x1 else: theta = np.arctan2(dy, dx) xa = p * d * np.cos(theta) + x1 ya = p * d * np.sin(theta) + y1 a_pos.append(((xa, ya), (x2, y2))) arrow_collection = LineCollection(a_pos, colors=arrow_colors, linewidths=[4 * ww for ww in lw], antialiaseds=(1,), transOffset=ax.transData, ) arrow_collection.set_zorder(1) # edges go behind nodes arrow_collection.set_label(label) ax.add_collection(arrow_collection) # update view minx = np.amin(np.ravel(edge_pos[:, :, 0])) maxx = np.amax(np.ravel(edge_pos[:, :, 0])) miny = np.amin(np.ravel(edge_pos[:, :, 1])) maxy = np.amax(np.ravel(edge_pos[:, :, 1])) w = maxx - minx h = maxy - miny padx, pady = 0.05 * w, 0.05 * h corners = (minx - padx, miny - pady), (maxx + padx, maxy + pady) ax.update_datalim(corners) ax.autoscale_view() # if arrow_collection: return edge_collection
def plot_tree(tree, figure_name, color_by_trait, initial_branch_width, tip_size, start_date, end_date, include_color_bar): """Plot a BioPython Phylo tree in the BALTIC-style. """ # Plot H3N2 tree in BALTIC style from Bio.Phylo tree. mpl.rcParams['savefig.dpi'] = 120 mpl.rcParams['figure.dpi'] = 100 mpl.rcParams['font.weight'] = 300 mpl.rcParams['axes.labelweight'] = 300 mpl.rcParams['font.size'] = 14 yvalues = [node.yvalue for node in tree.find_clades()] y_span = max(yvalues) y_unit = y_span / float(len(yvalues)) # Setup colors. trait_name = color_by_trait traits = [k.attr[trait_name] for k in tree.find_clades()] norm = mpl.colors.Normalize(min(traits), max(traits)) cmap = mpl.cm.viridis # # Setup the figure grid. # if include_color_bar: fig = plt.figure(figsize=(8, 6), facecolor='w') gs = gridspec.GridSpec(2, 1, height_ratios=[14, 1], width_ratios=[1], hspace=0.1, wspace=0.1) ax = fig.add_subplot(gs[0]) colorbar_ax = fig.add_subplot(gs[1]) else: fig = plt.figure(figsize=(8, 4), facecolor='w') gs = gridspec.GridSpec(1, 1) ax = fig.add_subplot(gs[0]) L = len([k for k in tree.find_clades() if k.is_terminal()]) # Setup arrays for tip and internal node coordinates. tip_circles_x = [] tip_circles_y = [] tip_circles_color = [] tip_circle_sizes = [] node_circles_x = [] node_circles_y = [] node_circles_color = [] node_line_widths = [] node_line_segments = [] node_line_colors = [] branch_line_segments = [] branch_line_widths = [] branch_line_colors = [] branch_line_labels = [] for k in tree.find_clades(): ## iterate over objects in tree x = k.attr["num_date"] ## or from x position determined earlier y = k.yvalue ## get y position from .drawTree that was run earlier, but could be anything else if k.parent is None: xp = None else: xp = k.parent.attr[ "num_date"] ## get x position of current object's parent if x == None: ## matplotlib won't plot Nones, like root x = 0.0 if xp == None: xp = x c = 'k' if trait_name in k.attr: c = cmap(norm(k.attr[trait_name])) branchWidth = 2 if k.is_terminal(): ## if leaf... s = tip_size ## tip size can be fixed tip_circle_sizes.append(s) tip_circles_x.append(x) tip_circles_y.append(y) tip_circles_color.append(c) else: ## if node... k_leaves = [ child for child in k.find_clades() if child.is_terminal() ] # Scale branch widths by the number of tips. branchWidth += initial_branch_width * len(k_leaves) / float(L) if len(k.clades) == 1: node_circles_x.append(x) node_circles_y.append(y) node_circles_color.append(c) ax.plot([x, x], [k.clades[-1].yvalue, k.clades[0].yvalue], lw=branchWidth, color=c, ls='-', zorder=9, solid_capstyle='round') branch_line_segments.append([(xp, y), (x, y)]) branch_line_widths.append(branchWidth) branch_line_colors.append(c) branch_lc = LineCollection(branch_line_segments, zorder=9) branch_lc.set_color(branch_line_colors) branch_lc.set_linewidth(branch_line_widths) branch_lc.set_label(branch_line_labels) branch_lc.set_linestyle("-") ax.add_collection(branch_lc) # Add circles for tips and internal nodes. tip_circle_sizes = np.array(tip_circle_sizes) ax.scatter(tip_circles_x, tip_circles_y, s=tip_circle_sizes, facecolor=tip_circles_color, edgecolor='none', zorder=11) ## plot circle for every tip ax.scatter(tip_circles_x, tip_circles_y, s=tip_circle_sizes * 2, facecolor='k', edgecolor='none', zorder=10) ## plot black circle underneath ax.scatter( node_circles_x, node_circles_y, facecolor=node_circles_color, s=50, edgecolor='none', zorder=10, lw=2, marker='|' ) ## mark every node in the tree to highlight that it's a multitype tree #ax.set_ylim(-10, y_span - 300) ax.spines['top'].set_visible(False) ## no axes ax.spines['right'].set_visible(False) ax.spines['left'].set_visible(False) ax.grid(axis='x', ls='-', color='grey') ax.tick_params(axis='y', size=0) ax.set_yticklabels([]) if start_date: # Always add a buffer to the left edge of the plot so data up to the # given end date can be clearly seen. ax.set_xlim(left=timestamp_to_float(pd.to_datetime(start_date)) - 2.0) if end_date: # Always add a buffer of 3 months to the right edge of the plot so data # up to the given end date can be clearly seen. ax.set_xlim(right=timestamp_to_float(pd.to_datetime(end_date)) + 0.25) if include_color_bar: cb1 = mpl.colorbar.ColorbarBase(colorbar_ax, cmap=cmap, norm=norm, orientation='horizontal') cb1.set_label(color_by_trait) gs.tight_layout(fig) plt.savefig(figure_name)
def draw_networkx_edges(G, pos, edgelist=None, width=1.0, edge_color='k', style='solid', alpha=1.0, arrowstyle='-|>', arrowsize=10, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=True, label=None, node_size=300, nodelist=None, node_shape="o", connectionstyle=None, **kwds): """Draw the edges of the graph G. This draws only the edges of the graph G. Parameters ---------- G : graph A networkx graph pos : dictionary A dictionary with nodes as keys and positions as values. Positions should be sequences of length 2. edgelist : collection of edge tuples Draw only specified edges(default=G.edges()) width : float, or array of floats Line width of edges (default=1.0) edge_color : color string, or array of floats Edge color. Can be a single color format string (default='r'), or a sequence of colors with the same length as edgelist. If numeric values are specified they will be mapped to colors using the edge_cmap and edge_vmin,edge_vmax parameters. style : string Edge line style (default='solid') (solid|dashed|dotted,dashdot) alpha : float The edge transparency (default=1.0) edge_ cmap : Matplotlib colormap Colormap for mapping intensities of edges (default=None) edge_vmin,edge_vmax : floats Minimum and maximum for edge colormap scaling (default=None) ax : Matplotlib Axes object, optional Draw the graph in the specified Matplotlib axes. arrows : bool, optional (default=True) For directed graphs, if True draw arrowheads. Note: Arrows will be the same color as edges. arrowstyle : str, optional (default='-|>') For directed graphs, choose the style of the arrow heads. See :py:class: `matplotlib.patches.ArrowStyle` for more options. arrowsize : int, optional (default=10) For directed graphs, choose the size of the arrow head head's length and width. See :py:class: `matplotlib.patches.FancyArrowPatch` for attribute `mutation_scale` for more info. connectionstyle : str, optional (default=None) Pass the connectionstyle parameter to create curved arc of rounding radius rad. For example, connectionstyle='arc3,rad=0.2'. See :py:class: `matplotlib.patches.ConnectionStyle` and :py:class: `matplotlib.patches.FancyArrowPatch` for more info. label : [None| string] Label for legend Returns ------- matplotlib.collection.LineCollection `LineCollection` of the edges list of matplotlib.patches.FancyArrowPatch `FancyArrowPatch` instances of the directed edges Depending whether the drawing includes arrows or not. Notes ----- For directed graphs, arrows are drawn at the head end. Arrows can be turned off with keyword arrows=False. Be sure to include `node_size` as a keyword argument; arrows are drawn considering the size of nodes. Examples -------- >>> G = nx.dodecahedral_graph() >>> edges = nx.draw_networkx_edges(G, pos=nx.spring_layout(G)) >>> G = nx.DiGraph() >>> G.add_edges_from([(1, 2), (1, 3), (2, 3)]) >>> arcs = nx.draw_networkx_edges(G, pos=nx.spring_layout(G)) >>> alphas = [0.3, 0.4, 0.5] >>> for i, arc in enumerate(arcs): # change alpha values of arcs ... arc.set_alpha(alphas[i]) Also see the NetworkX drawing examples at https://networkx.github.io/documentation/latest/auto_examples/index.html See Also -------- draw() draw_networkx() draw_networkx_nodes() draw_networkx_labels() draw_networkx_edge_labels() """ try: import matplotlib import matplotlib.pyplot as plt import matplotlib.cbook as cb from matplotlib.colors import colorConverter, Colormap, Normalize from matplotlib.collections import LineCollection from matplotlib.patches import FancyArrowPatch import numpy as np except ImportError: raise ImportError("Matplotlib required for draw()") except RuntimeError: print("Matplotlib unable to open display") raise if ax is None: ax = plt.gca() if edgelist is None: edgelist = list(G.edges()) if not edgelist or len(edgelist) == 0: # no edges! return None if nodelist is None: nodelist = list(G.nodes()) # set edge positions edge_pos = np.asarray([(pos[e[0]], pos[e[1]]) for e in edgelist]) if not cb.iterable(width): lw = (width,) else: lw = width if not is_string_like(edge_color) \ and cb.iterable(edge_color) \ and len(edge_color) == len(edge_pos): if np.alltrue([is_string_like(c) for c in edge_color]): # (should check ALL elements) # list of color letters such as ['k','r','k',...] edge_colors = tuple([colorConverter.to_rgba(c, alpha) for c in edge_color]) elif np.alltrue([not is_string_like(c) for c in edge_color]): # If color specs are given as (rgb) or (rgba) tuples, we're OK if np.alltrue([cb.iterable(c) and len(c) in (3, 4) for c in edge_color]): edge_colors = tuple(edge_color) else: # numbers (which are going to be mapped with a colormap) edge_colors = None else: raise ValueError('edge_color must contain color names or numbers') else: if is_string_like(edge_color) or len(edge_color) == 1: edge_colors = (colorConverter.to_rgba(edge_color, alpha), ) else: msg = 'edge_color must be a color or list of one color per edge' raise ValueError(msg) if (not G.is_directed() or not arrows): edge_collection = LineCollection(edge_pos, colors=edge_colors, linewidths=lw, antialiaseds=(1,), linestyle=style, transOffset=ax.transData, ) edge_collection.set_zorder(1) # edges go behind nodes edge_collection.set_label(label) ax.add_collection(edge_collection) # Note: there was a bug in mpl regarding the handling of alpha values # for each line in a LineCollection. It was fixed in matplotlib by # r7184 and r7189 (June 6 2009). We should then not set the alpha # value globally, since the user can instead provide per-edge alphas # now. Only set it globally if provided as a scalar. if isinstance(alpha, Number): edge_collection.set_alpha(alpha) if edge_colors is None: if edge_cmap is not None: assert(isinstance(edge_cmap, Colormap)) edge_collection.set_array(np.asarray(edge_color)) edge_collection.set_cmap(edge_cmap) if edge_vmin is not None or edge_vmax is not None: edge_collection.set_clim(edge_vmin, edge_vmax) else: edge_collection.autoscale() return edge_collection arrow_collection = None if G.is_directed() and arrows: # Note: Waiting for someone to implement arrow to intersection with # marker. Meanwhile, this works well for polygons with more than 4 # sides and circle. def to_marker_edge(marker_size, marker): if marker in "s^>v<d": # `large` markers need extra space return np.sqrt(2 * marker_size) / 2 else: return np.sqrt(marker_size) / 2 # Draw arrows with `matplotlib.patches.FancyarrowPatch` arrow_collection = [] mutation_scale = arrowsize # scale factor of arrow head arrow_colors = edge_colors if arrow_colors is None: if edge_cmap is not None: assert(isinstance(edge_cmap, Colormap)) else: edge_cmap = plt.get_cmap() # default matplotlib colormap if edge_vmin is None: edge_vmin = min(edge_color) if edge_vmax is None: edge_vmax = max(edge_color) color_normal = Normalize(vmin=edge_vmin, vmax=edge_vmax) for i, (src, dst) in enumerate(edge_pos): x1, y1 = src x2, y2 = dst arrow_color = None line_width = None shrink_source = 0 # space from source to tail shrink_target = 0 # space from head to target if cb.iterable(node_size): # many node sizes src_node, dst_node = edgelist[i][:2] index_node = nodelist.index(dst_node) marker_size = node_size[index_node] shrink_target = to_marker_edge(marker_size, node_shape) else: shrink_target = to_marker_edge(node_size, node_shape) if arrow_colors is None: arrow_color = edge_cmap(color_normal(edge_color[i])) elif len(arrow_colors) > 1: arrow_color = arrow_colors[i] else: arrow_color = arrow_colors[0] if len(lw) > 1: line_width = lw[i] else: line_width = lw[0] arrow = FancyArrowPatch((x1, y1), (x2, y2), arrowstyle=arrowstyle, shrinkA=shrink_source, shrinkB=shrink_target, mutation_scale=mutation_scale, color=arrow_color, linewidth=line_width, connectionstyle=connectionstyle, zorder=1) # arrows go behind nodes # There seems to be a bug in matplotlib to make collections of # FancyArrowPatch instances. Until fixed, the patches are added # individually to the axes instance. arrow_collection.append(arrow) ax.add_patch(arrow) # update view minx = np.amin(np.ravel(edge_pos[:, :, 0])) maxx = np.amax(np.ravel(edge_pos[:, :, 0])) miny = np.amin(np.ravel(edge_pos[:, :, 1])) maxy = np.amax(np.ravel(edge_pos[:, :, 1])) w = maxx - minx h = maxy - miny padx, pady = 0.05 * w, 0.05 * h corners = (minx - padx, miny - pady), (maxx + padx, maxy + pady) ax.update_datalim(corners) ax.autoscale_view() plt.tick_params( axis='both', which='both', bottom=False, left=False, labelbottom=False, labelleft=False) return arrow_collection
def draw_networkx_edges(G, pos, edgelist=None, width=1.0, edge_color='k', style='solid', alpha=None, arrowstyle='-|>', arrowsize=10, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=True, label=None, node_size=300, nodelist=None, node_shape="o", connectionstyle=None, **kwds): """Draw the edges of the graph G. This draws only the edges of the graph G. Parameters ---------- G : graph A networkx graph pos : dictionary A dictionary with nodes as keys and positions as values. Positions should be sequences of length 2. edgelist : collection of edge tuples Draw only specified edges(default=G.edges()) width : float, or array of floats Line width of edges (default=1.0) edge_color : color or array of colors (default='k') Edge color. Can be a single color or a sequence of colors with the same length as edgelist. Color can be string, or rgb (or rgba) tuple of floats from 0-1. If numeric values are specified they will be mapped to colors using the edge_cmap and edge_vmin,edge_vmax parameters. style : string Edge line style (default='solid') (solid|dashed|dotted,dashdot) alpha : float The edge transparency (default=None) edge_ cmap : Matplotlib colormap Colormap for mapping intensities of edges (default=None) edge_vmin,edge_vmax : floats Minimum and maximum for edge colormap scaling (default=None) ax : Matplotlib Axes object, optional Draw the graph in the specified Matplotlib axes. arrows : bool, optional (default=True) For directed graphs, if True draw arrowheads. Note: Arrows will be the same color as edges. arrowstyle : str, optional (default='-|>') For directed graphs, choose the style of the arrow heads. See :py:class: `matplotlib.patches.ArrowStyle` for more options. arrowsize : int, optional (default=10) For directed graphs, choose the size of the arrow head head's length and width. See :py:class: `matplotlib.patches.FancyArrowPatch` for attribute `mutation_scale` for more info. connectionstyle : str, optional (default=None) Pass the connectionstyle parameter to create curved arc of rounding radius rad. For example, connectionstyle='arc3,rad=0.2'. See :py:class: `matplotlib.patches.ConnectionStyle` and :py:class: `matplotlib.patches.FancyArrowPatch` for more info. label : [None| string] Label for legend Returns ------- matplotlib.collection.LineCollection `LineCollection` of the edges list of matplotlib.patches.FancyArrowPatch `FancyArrowPatch` instances of the directed edges Depending whether the drawing includes arrows or not. Notes ----- For directed graphs, arrows are drawn at the head end. Arrows can be turned off with keyword arrows=False. Be sure to include `node_size` as a keyword argument; arrows are drawn considering the size of nodes. Examples -------- >>> G = nx.dodecahedral_graph() >>> edges = nx.draw_networkx_edges(G, pos=nx.spring_layout(G)) >>> G = nx.DiGraph() >>> G.add_edges_from([(1, 2), (1, 3), (2, 3)]) >>> arcs = nx.draw_networkx_edges(G, pos=nx.spring_layout(G)) >>> alphas = [0.3, 0.4, 0.5] >>> for i, arc in enumerate(arcs): # change alpha values of arcs ... arc.set_alpha(alphas[i]) Also see the NetworkX drawing examples at https://networkx.github.io/documentation/latest/auto_examples/index.html See Also -------- draw() draw_networkx() draw_networkx_nodes() draw_networkx_labels() draw_networkx_edge_labels() """ try: import matplotlib import matplotlib.pyplot as plt import matplotlib.cbook as cb from matplotlib.colors import colorConverter, Colormap, Normalize from matplotlib.collections import LineCollection from matplotlib.patches import FancyArrowPatch import numpy as np except ImportError: raise ImportError("Matplotlib required for draw()") except RuntimeError: print("Matplotlib unable to open display") raise if ax is None: ax = plt.gca() if edgelist is None: edgelist = list(G.edges()) if not edgelist or len(edgelist) == 0: # no edges! return None if nodelist is None: nodelist = list(G.nodes()) # FancyArrowPatch handles color=None different from LineCollection if edge_color is None: edge_color = 'k' # set edge positions edge_pos = np.asarray([(pos[e[0]], pos[e[1]]) for e in edgelist]) # Check if edge_color is an array of floats and map to edge_cmap. # This is the only case handled differently from matplotlib if cb.iterable(edge_color) and (len(edge_color) == len(edge_pos)) \ and np.alltrue([isinstance(c,Number) for c in edge_color]): if edge_cmap is not None: assert(isinstance(edge_cmap, Colormap)) else: edge_cmap = plt.get_cmap() if edge_vmin is None: edge_vmin = min(edge_color) if edge_vmax is None: edge_vmax = max(edge_color) color_normal = Normalize(vmin=edge_vmin, vmax=edge_vmax) edge_color = [edge_cmap(color_normal(e)) for e in edge_color] if (not G.is_directed() or not arrows): edge_collection = LineCollection(edge_pos, colors=edge_color, linewidths=width, antialiaseds=(1,), linestyle=style, transOffset=ax.transData, alpha=alpha ) edge_collection.set_zorder(1) # edges go behind nodes edge_collection.set_label(label) ax.add_collection(edge_collection) return edge_collection arrow_collection = None if G.is_directed() and arrows: # Note: Waiting for someone to implement arrow to intersection with # marker. Meanwhile, this works well for polygons with more than 4 # sides and circle. def to_marker_edge(marker_size, marker): if marker in "s^>v<d": # `large` markers need extra space return np.sqrt(2 * marker_size) / 2 else: return np.sqrt(marker_size) / 2 # Draw arrows with `matplotlib.patches.FancyarrowPatch` arrow_collection = [] mutation_scale = arrowsize # scale factor of arrow head # FancyArrowPatch doesn't handle color strings arrow_colors = colorConverter.to_rgba_array(edge_color,alpha) for i, (src, dst) in enumerate(edge_pos): x1, y1 = src x2, y2 = dst shrink_source = 0 # space from source to tail shrink_target = 0 # space from head to target if cb.iterable(node_size): # many node sizes src_node, dst_node = edgelist[i][:2] index_node = nodelist.index(dst_node) marker_size = node_size[index_node] shrink_target = to_marker_edge(marker_size, node_shape) else: shrink_target = to_marker_edge(node_size, node_shape) if cb.iterable(arrow_colors): if len(arrow_colors) == len(edge_pos): arrow_color = arrow_colors[i] elif len(arrow_colors)==1: arrow_color = arrow_colors[0] else: # Cycle through colors arrow_color = arrow_colors[i%len(arrow_colors)] else: arrow_color = edge_color if cb.iterable(width): if len(width) == len(edge_pos): line_width = width[i] else: line_width = width[i%len(width)] else: line_width = width arrow = FancyArrowPatch((x1, y1), (x2, y2), arrowstyle=arrowstyle, shrinkA=shrink_source, shrinkB=shrink_target, mutation_scale=mutation_scale, color=arrow_color, linewidth=line_width, connectionstyle=connectionstyle, zorder=1) # arrows go behind nodes # There seems to be a bug in matplotlib to make collections of # FancyArrowPatch instances. Until fixed, the patches are added # individually to the axes instance. arrow_collection.append(arrow) ax.add_patch(arrow) # update view minx = np.amin(np.ravel(edge_pos[:, :, 0])) maxx = np.amax(np.ravel(edge_pos[:, :, 0])) miny = np.amin(np.ravel(edge_pos[:, :, 1])) maxy = np.amax(np.ravel(edge_pos[:, :, 1])) w = maxx - minx h = maxy - miny padx, pady = 0.05 * w, 0.05 * h corners = (minx - padx, miny - pady), (maxx + padx, maxy + pady) ax.update_datalim(corners) ax.autoscale_view() plt.tick_params( axis='both', which='both', bottom=False, left=False, labelbottom=False, labelleft=False) return arrow_collection