def plot_polygon_collection(ax, geoms, values=None, colormap='gist_rainbow', facecolor=None, edgecolor=None, alpha=0.5, linewidth=1.0, **kwargs): """ Plot a collection of Polygon geometries """ patches = [] for poly in geoms: a = np.asarray(poly.exterior) if poly.has_z: poly = shapely.geometry.Polygon(zip(*poly.exterior.xy)) patches.append(Polygon(a)) patches = PatchCollection(patches, facecolor=facecolor, linewidth=linewidth, edgecolor=edgecolor, alpha=alpha, **kwargs) if values is not None: patches.set_array(values) patches.set_cmap(colormap) ax.add_collection(patches, autolim=True) ax.autoscale_view() return patches
def plot_polygon_collection(ax, geoms, values=None, colormap='Greens', facecolor=None, edgecolor=None, alpha=0.7, linewidth=1.0, **kwargs): """ Plot a collection of Polygon geometries """ patches = [] for poly in geoms: a = np.asarray(poly.exterior) patches.append(Polygon(a)) patches = PatchCollection(patches, facecolor=facecolor, linewidth=linewidth, edgecolor=edgecolor, alpha=alpha, **kwargs) if values is not None: patches.set_array(values) patches.set_cmap(colormap) ax.add_collection(patches, autolim=True) # ax.autoscale_view() return patches
def plot_distribution(ax, cmap, probabilitydist, fig, time_from, reference_data=None): from matplotlib.patches import Rectangle from matplotlib.collections import PatchCollection patches = [] colors = [] for ct, dt in enumerate(probabilitydist): disp = 0.0 if reference_data is not None: disp = reference_data[time_from + ct] for y in dt.bins: s = Rectangle((time_from + ct, y + disp), 1, dt.resolution, fill=True, lw=0) patches.append(s) colors.append(dt.density(y)) scale = Transformations.Scale() colors = scale.apply(colors) pc = PatchCollection(patches=patches, match_original=True) pc.set_clim([0, 1]) pc.set_cmap(cmap) pc.set_array(np.array(colors)) ax.add_collection(pc) cb = fig.colorbar(pc, ax=ax) cb.set_label('Density')
def plotCustomColors(ax, df, column, custom_cmap, linewidth=1.0, alpha=1.0, edgecolor='black'): # Get Min and max vmin = None vmax = None # Flatten possible MultiPlygons components, component_colors_or_values = _flatten_multi_geoms( df['geometry'], df[column]) collection = PatchCollection([PolygonPatch(poly) for poly in components], linewidth=linewidth, edgecolor=edgecolor, alpha=alpha) collection.set_array(np.array(df[column])) collection.set_cmap(custom_cmap) collection.set_clim(vmin, vmax) ax.add_collection(collection, autolim=True) return ax
def draw_disc(cpx, cpy, area, size): # input arguments: ## cpx, cpy: x,y/positions of the vertices of all cells # format: list (1 element per cell) of sublists (1 number per vertex, eg 3 numbers for a triangle). ## area: cell area # format: 1-dimentsional numpy array (1 number per cell) ## size: 'large' for the large disc and 'small' for the small disc polygs = [] for i in range(len(cpx)): polyg = [] for j in range(len(cpx[i])): polyg.append([cpx[i][j], cpy[i][j]]) polygs.append(Polygon(polyg)) patches = PatchCollection(polygs) patches.set_cmap('jet') colors = 1 * area colors[ colors > 14] = 14 # color value for all the mitotic cells (area>14) is set to 14 patches.set_array(np.array(colors)) # for colors fig = plt.figure() panel = fig.add_subplot(1, 1, 1) panel.add_collection(patches) fig.colorbar(patches) panel.set_xlim(min(min(cpx)) - 5, max(max(cpx)) + 5) panel.set_ylim(min(min(cpy)) - 5, max(max(cpy)) + 5) panel.set_aspect('equal') plt.title(size + ' wing disc')
def plot_distribution(ax, cmap, probabilitydist, fig, time_from, reference_data=None): """ Plot forecasted ProbabilityDistribution objects on a matplotlib axis :param ax: matplotlib axis :param cmap: matplotlib colormap name :param probabilitydist: list of ProbabilityDistribution objects :param fig: matplotlib figure :param time_from: starting time (on x axis) to begin the plots :param reference_data: :return: """ from matplotlib.patches import Rectangle from matplotlib.collections import PatchCollection patches = [] colors = [] for ct, dt in enumerate(probabilitydist): disp = 0.0 if reference_data is not None: disp = reference_data[time_from+ct] for y in dt.bins: s = Rectangle((time_from+ct, y+disp), 1, dt.resolution, fill=True, lw = 0) patches.append(s) colors.append(dt.density(y)) scale = Transformations.Scale() colors = scale.apply(colors) pc = PatchCollection(patches=patches, match_original=True) pc.set_clim([0, 1]) pc.set_cmap(cmap) pc.set_array(np.array(colors)) ax.add_collection(pc) cb = fig.colorbar(pc, ax=ax) cb.set_label('Density')
def plot_polygon_collection(ax, geoms, values=None, color=None, cmap=None, vmin=None, vmax=None, **kwargs): """ Plots a collection of Polygon and MultiPolygon geometries to `ax` Parameters ---------- ax : matplotlib.axes.Axes where shapes will be plotted geoms : a sequence of `N` Polygons and/or MultiPolygons (can be mixed) values : a sequence of `N` values, optional Values will be mapped to colors using vmin/vmax/cmap. They should have 1:1 correspondence with the geometries (not their components). Otherwise follows `color` / `facecolor` kwargs. edgecolor : single color or sequence of `N` colors Color for the edge of the polygons facecolor : single color or sequence of `N` colors Color to fill the polygons. Cannot be used together with `values`. color : single color or sequence of `N` colors Sets both `edgecolor` and `facecolor` **kwargs Additional keyword arguments passed to the collection Returns ------- collection : matplotlib.collections.Collection that was plotted """ from descartes.patch import PolygonPatch from matplotlib.collections import PatchCollection geoms, values = _flatten_multi_geoms(geoms, values) if None in values: values = None # PatchCollection does not accept some kwargs. if 'markersize' in kwargs: del kwargs['markersize'] # color=None overwrites specified facecolor/edgecolor with default color if color is not None: kwargs['color'] = color collection = PatchCollection([PolygonPatch(poly) for poly in geoms], **kwargs) if values is not None: collection.set_array(np.asarray(values)) collection.set_cmap(cmap) collection.set_clim(vmin, vmax) ax.add_collection(collection, autolim=True) ax.autoscale_view() return collection
def draw_voronoi(box, cells, ax=None): """Helper function to draw 2D Voronoi diagram. Args: box (:class:`freud.box.Box`): Simulation box. cells (:class:`numpy.ndarray`): Array containing Voronoi polytope vertices. color_by_sides (bool): If :code:`True`, color cells by the number of sides. (Default value = :code:`False`) ax (:class:`matplotlib.axes.Axes`): axes object to plot. If :code:`None`, make a new axes and figure object. (Default value = :code:`None`). Returns: :class:`matplotlib.axes.Axes`: axes object with the diagram. """ if not MATPLOTLIB: return None try: from matplotlib import cm from matplotlib.collections import PatchCollection from matplotlib.patches import Polygon, Rectangle except ImportError: return None if ax is None: fig = Figure() ax = fig.subplots() # Draw Voronoi cells patches = [Polygon(cell[:, :2]) for cell in cells] patch_collection = PatchCollection(patches, edgecolors='black', alpha=0.4) cmap = cm.Set1 colors = np.random.permutation(np.arange(len(patches))) cmap = cm.get_cmap('Set1', np.unique(colors).size) bounds = np.array(range(min(colors), max(colors)+2)) patch_collection.set_array(np.array(colors)) patch_collection.set_cmap(cmap) patch_collection.set_clim(bounds[0], bounds[-1]) ax.add_collection(patch_collection) ax.set_title('Voronoi Diagram') ax.set_xlim((-box.Lx/2, box.Lx/2)) ax.set_ylim((-box.Ly/2, box.Ly/2)) # Set equal aspect and draw box ax.set_aspect('equal', 'datalim') box_patch = Rectangle([-box.Lx/2, -box.Ly/2], box.Lx, box.Ly, alpha=1, fill=None) ax.add_patch(box_patch) return ax
def paw_plot(sdata=None, sdata_bg=None, cdata=None, cmap=None, clim=None, edgecolor='#555555', bgcolor='#cccccc', legend=True, ax=None): ax = ax if ax else plt.gca() r = np.array([ 0, 1, 1, 1, 1, 1 ]) theta = np.array([ 0, 30, 75, 120, 165, 210 ]) * np.pi / 180.0 theta = np.array([ -5, 25, 70, 115, 160, 205 ]) * np.pi / 180.0 # theta = np.array([ 0, 30, 72, 114, 156, 198 ]) * np.pi / 180.0 if sdata is None: sdata = np.array([4,1,1,1,1,1])*.08 if cdata is None: cdata = np.ones(len(r)) if cmap is None: cmap = 'magma' if clim is None: clim = [0,1] xx = r * np.cos(theta) yy = r * np.sin(theta) patches = [ mpatch.Circle((x,y),rad) for (x,y,rad) in zip(xx,yy,np.sqrt(sdata)) ] col = PatchCollection(patches, zorder=2) col.set_array(cdata) col.set_cmap(cmap) col.set_edgecolor(edgecolor) col.set_clim(vmin=clim[0], vmax=clim[1]) ax.add_patch(mpatch.Arc((0.0, 0.0), 2.0, 2.0, angle=0.0, theta1=theta[1]*180.0/np.pi, theta2=theta[-1]*180.0/np.pi, edgecolor=edgecolor,facecolor='none', zorder=0, linestyle='--')) ax.add_collection(col) if legend: plt.colorbar(col) if sdata_bg is not None: patches = [ mpatch.Circle((x,y),rad) for (x,y,rad) in zip(xx,yy,np.sqrt(sdata_bg)) ] col = PatchCollection(patches, zorder=1) col.set_edgecolor(edgecolor) col.set_facecolor(bgcolor) ax.add_collection(col) ax.axis('equal') ax.axis('off') ax.set_xlim([-1.2,1.2]) ax.set_ylim([-.8,1.35])
def draw1DColumn(ax, x, val, thk, width=30, ztopo=0, cmin=1, cmax=1000, cmap=None, name=None, textoffset=0.0): """Draw a 1D column (e.g., from a 1D inversion) on a given ax. Examples -------- >>> import numpy as np >>> import matplotlib.pyplot as plt >>> from pygimli.mplviewer import draw1DColumn >>> thk = [1, 2, 3, 4] >>> val = thk >>> fig, ax = plt.subplots() >>> draw1DColumn(ax, 0.5, val, thk, width=0.1, cmin=1, cmax=4, name="VES") <matplotlib.collections.PatchCollection object at ...> >>> ax.set_ylim(-np.sum(thk), 0) (-10, 0) """ z = -np.hstack((0., np.cumsum(thk), np.sum(thk) * 1.5)) + ztopo recs = [] for i in range(len(val)): recs.append(Rectangle((x - width / 2., z[i]), width, z[i + 1] - z[i])) pp = PatchCollection(recs) col = ax.add_collection(pp) pp.set_edgecolor(None) pp.set_linewidths(0.0) if cmap is not None: if isinstance(cmap, str): pp.set_cmap(pg.mplviewer.cmapFromName(cmap)) else: pp.set_cmap(cmap) pp.set_norm(colors.LogNorm(cmin, cmax)) pp.set_array(np.array(val)) pp.set_clim(cmin, cmax) if name: ax.text(x + textoffset, ztopo, name, ha='center', va='bottom') updateAxes_(ax) return col
def create_warming_strips_plot(df): FIRST = 2020 LAST = 2021 # inclusive # Reference period for the center of the color scale FIRST_REFERENCE = 1971 LAST_REFERENCE = 2000 LIM = 0.7 # degrees # the colors in this colormap come from http://colorbrewer2.org # the 8 more saturated colors from the 9 blues / 9 reds cmap = ListedColormap([ '#08306b', '#08519c', '#2171b5', '#4292c6', '#6baed6', '#9ecae1', '#c6dbef', '#deebf7', '#fee0d2', '#fcbba1', '#fc9272', '#fb6a4a', '#ef3b2c', '#cb181d', '#a50f15', '#67000d', ]) fig = plt.figure(figsize=(10, 1)) ax = fig.add_axes([0, 0, 1, 1]) ax.set_axis_off() # create a collection with a rectangle for each year col = PatchCollection( [Rectangle((y, 0), 1, 1) for y in range(FIRST, LAST + 1)]) # set data, colormap and color limits col.set_array(anomaly) col.set_cmap(cmap) col.set_clim(reference - LIM, reference + LIM) ax.add_collection(col) ax.set_ylim(0, 1) ax.set_xlim(FIRST, LAST + 1) fig.savefig('{}_warming_stripes.png'.format(CMIP6_name))
class ArrayDisplay: """ Display a top-town view of a telescope array """ def __init__(self, telx, tely, mirrorarea, axes=None, title="Array", autoupdate=True): patches = [ Circle(xy=(x, y), radius=np.sqrt(a)) for x, y, a in zip(telx, tely, mirrorarea) ] self.autoupdate = autoupdate self.telescopes = PatchCollection(patches) self.telescopes.set_clim(0, 100) self.telescopes.set_array(np.zeros(len(telx))) self.telescopes.set_cmap('spectral_r') self.telescopes.set_edgecolor('none') self.axes = axes if axes is not None else plt.gca() self.axes.add_collection(self.telescopes) self.axes.set_aspect(1.0) self.axes.set_title(title) self.axes.set_xlim(-1000, 1000) self.axes.set_ylim(-1000, 1000) self.bar = plt.colorbar(self.telescopes) self.bar.set_label("Value") @property def values(self): """An array containing a value per telescope""" return self.telescopes.get_array() @values.setter def values(self, values): """ set the telescope colors to display """ self.telescopes.set_array(values) self._update() def _update(self): """ signal a redraw if necessary """ if self.autoupdate: plt.draw()
def plot_polygons(geoms, ax, values=None, colormap='Set1', facecolor=None, edgecolor=None, alpha=1.0, linewidth=1.0, **kwargs): """Makes a MatPlotLib PatchCollection out of Polygon and/or MultiPolygon geometries Thanks to http://stackoverflow.com/a/33753927 and David Sullivan""" # init list to store patches = [] newvals = [] for polynum in range(len(geoms)): # for polygon # i poly = geoms.iloc[polynum] # find data.geometry[i] if type( poly ) != shapely.geometry.polygon.Polygon: # if that is not a shapely Polygon object for currpoly in poly.geoms: # then for data.geometry[i].geoms a = np.asarray( currpoly.exterior ) # make a an array of those exterior values and patches.append(Polygon(a)) # append ato patches if values is not None: # if values, add value to newvals newvals.append(values.iloc[polynum]) else: a = np.asarray(poly.exterior) patches.append(Polygon(a)) if values is not None: newvals.append(values.iloc[polynum]) patches = PatchCollection(patches, facecolor=facecolor, linewidth=linewidth, edgecolor=edgecolor, alpha=alpha, **kwargs) if values is not None: patches.set_array(np.asarray(newvals)) patches.set_cmap(colormap) norm = matplotlib.colors.Normalize() norm.autoscale(newvals) patches.set_norm(norm) ax.add_collection(patches, autolim=True) ax.autoscale_view() return patches
def plot_density_rectange(ax, cmap, density, fig, resolution, time_from, time_to): from matplotlib.patches import Rectangle from matplotlib.collections import PatchCollection patches = [] colors = [] for x in density.index: for y in density.columns: s = Rectangle((time_from + x, y), 1, resolution, fill=True, lw=0) patches.append(s) colors.append(density[y][x] * 5) pc = PatchCollection(patches=patches, match_original=True) pc.set_clim([0, 1]) pc.set_cmap(cmap) pc.set_array(np.array(colors)) ax.add_collection(pc) cb = fig.colorbar(pc, ax=ax) cb.set_label('Density')
class ArrayDisplay: """ Display a top-town view of a telescope array """ def __init__(self, telx, tely, mirrorarea, axes=None, title="Array", autoupdate=True): patches = [Circle(xy=(x, y), radius=np.sqrt(a)) for x, y, a in zip(telx, tely, mirrorarea)] self.autoupdate = autoupdate self.telescopes = PatchCollection(patches) self.telescopes.set_clim(0, 100) self.telescopes.set_array(np.zeros(len(telx))) self.telescopes.set_cmap('spectral_r') self.telescopes.set_edgecolor('none') self.axes = axes if axes is not None else plt.gca() self.axes.add_collection(self.telescopes) self.axes.set_aspect(1.0) self.axes.set_title(title) self.axes.set_xlim(-1000, 1000) self.axes.set_ylim(-1000, 1000) self.bar = plt.colorbar(self.telescopes) self.bar.set_label("Value") @property def values(self): """An array containing a value per telescope""" return self.telescopes.get_array() @values.setter def values(self, values): """ set the telescope colors to display """ self.telescopes.set_array(values) self._update() def _update(self): """ signal a redraw if necessary """ if self.autoupdate: plt.draw()
def draw1DColumn(ax, x, val, thk, width=30, ztopo=0, cmin=1, cmax=1000, cmap=None, name=None, textoffset=0.0): """Draw a 1D column (e.g., from a 1D inversion) on a given ax. Examples -------- >>> import numpy as np >>> import matplotlib.pyplot as plt >>> from pygimli.mplviewer import draw1DColumn >>> thk = [1, 2, 3, 4] >>> val = thk >>> fig, ax = plt.subplots() >>> draw1DColumn(ax, 0.5, val, thk, width=0.1, cmin=1, cmax=4, name="VES") <matplotlib.collections.PatchCollection object at ...> >>> ax.set_ylim(-np.sum(thk), 0) (-10, 0) """ z = -np.hstack((0., np.cumsum(thk), np.sum(thk) * 1.5)) + ztopo recs = [] for i in range(len(val)): recs.append(Rectangle((x - width / 2., z[i]), width, z[i + 1] - z[i])) pp = PatchCollection(recs) col = ax.add_collection(pp) pp.set_edgecolor(None) pp.set_linewidths(0.0) if cmap is not None: if isinstance(cmap, str): pp.set_cmap(pg.mplviewer.cmapFromName(cmap)) else: pp.set_cmap(cmap) pp.set_norm(colors.LogNorm(cmin, cmax)) pp.set_array(np.array(val)) pp.set_clim(cmin, cmax) if name: ax.text(x+textoffset, ztopo, name, ha='center', va='bottom') updateAxes_(ax) return col
def draw_patches(list_of_polygons, intensity_list=None, cm='inferno', empty_face=False, norm=None, alpha=None): patches = [Polygon(polygon, lw=0.1) for polygon in list_of_polygons] pc = PatchCollection(patches) if not empty_face: if intensity_list is None: raise ValueError('values not provided for Voronoi plot.') pc.set_array(np.array(intensity_list)) pc.set_edgecolor('face') pc.set_cmap(cm) pc.set_norm(norm) else: pc.set_edgecolor([0.3, 0.3, 0.5]) pc.set_facecolor([0, 0, 0, 0]) return pc
def make_collection(map, df_plotvar_t, cmap, tick_values, variable_scale, plotvar_bounds, groupvar): patches = [] colors = [] if variable_scale=="logodds": df_plotvar_t = logit(df_plotvar_t) else: pass if groupvar == "country_id": for info, shape in zip(map.ID_info, map.ID): gid = info['ID'] if gid in df_plotvar_t.index.values: prob = df_plotvar_t.loc[gid] patch = Polygon(np.array(shape), True) patches.append(patch) colors.append(prob) elif groupvar == "pg_id": for info, shape in zip(map.GID_info, map.GID): gid = info['GID'] if gid in df_plotvar_t.index.values: prob = df_plotvar_t.loc[gid] patch = Polygon(np.array(shape), True) patches.append(patch) colors.append(prob) else: raise NotImplementedError collection = PatchCollection(patches, zorder=2) collection.set_array(np.array(colors).flatten()) collection.set_cmap(cmap) # If we have custom tick values, set the ticks as the colorlimit if tick_values: collection.set_clim(np.min(tick_values), np.max(tick_values)) else: collection.set_clim(plotvar_bounds[0], plotvar_bounds[1]) return collection
def visualise(sim): X = np.arange(sim.A.size).reshape(sim.A.shape) % sim.A.shape[0] Y = (np.arange(sim.A.size).reshape(sim.A.shape) % sim.A.shape[1]).T U = cos(sim.A) V = sin(sim.A) fig, ax = plt.subplots(1, 1, figsize=(15, 15)) rects = [] # create rectangles for vortex/antivortex determination for i in range(sim.V.shape[0]): for j in range(sim.V.shape[1]): rect = patches.Rectangle(xy=(i, j), height=1, width=1) rects.append(rect) rects = PatchCollection(rects) # Set colors for the rectangles col = 'RdBu' r_cmap = plt.get_cmap(col) r_cmap_r = plt.get_cmap(col + "_r") #eto kostil' =) rects.set_cmap(r_cmap) rects.set_clim(vmin=-1, vmax=1) rects.set_animated(True) rects.set_array(sim.V.flatten('F') / 2) ax.add_collection(rects) # create legend legend_boxes = [patches.Patch(facecolor=r_cmap(0.7), label='Antiortex'), patches.Patch(facecolor=r_cmap_r(0.7), label='Vortex')] ax.legend(handles=legend_boxes) # build an initial quiver plot q = ax.quiver(X, Y, U, V, pivot='tail', cmap=plt.cm.get_cmap('hsv'), units='inches', scale=4) fig.colorbar(q, label='Angles (2 pi)') ax.set_xlim(-1, sim.A.shape[0]) ax.set_ylim(-1, sim.A.shape[1]) q.set_UVC(U, V, C=sim.A) return q, fig, rects
def draw_disc(cpx, cpy, area, size): polygs = [] for i in range(len(cpx)): polyg = [] for j in range(len(cpx[i])): polyg.append([cpx[i][j], cpy[i][j]]) polygs.append(Polygon(polyg)) patches = PatchCollection(polygs) patches.set_cmap('jet') colors = 1 * area colors[colors > 14] = 14 patches.set_array(np.array(colors)) #for colors fig = plt.figure() panel = plt.gca() panel.add_collection(patches) fig.colorbar(patches) panel.set_xlim(min(min(cpx)) - 5, max(max(cpx)) + 5) panel.set_ylim(min(min(cpy)) - 5, max(max(cpy)) + 5) panel.set_aspect('equal') plt.title(size + ' wing disc')
def draw_map(map: GeographicalMap, mode='random', data=None): fig, ax = plt.subplots(figsize=(18, 10)) ax.set_xlim(map.xmin, map.xmax) ax.set_ylim(map.ymin, map.ymax) patches = PatchCollection( [matplotlib.patches.Polygon(p, fill=False) for p in map.polygons]) to_line = lambda e: map.centroids[(e.v1, e.v2), :] land_edges = [to_line(e) for e in map.edges.values() if e.type == 'land'] air_edges = [to_line(e) for e in map.edges.values() if e.type == 'air'] land_edges = LineCollection(land_edges, linewidths=0.3, colors='red') air_edges = LineCollection(air_edges, linewidths=0.1, colors='green') ax.add_collection(patches) if mode == 'random': patches.set_array(np.random.randint(0, 20, size=map.N)) patches.set_cmap(matplotlib.cm.jet) elif mode == 'population': patches.set_array(map.population) patches.set_cmap(matplotlib.cm.jet) patches.set_norm(matplotlib.colors.LogNorm()) plt.colorbar(patches, ax=ax) elif mode == 'graph': patches.set_color('black') patches.set_facecolor('white') patches.set_linewidth(0.1) ax.scatter(map.centroids[:, 0], map.centroids[:, 1], s=5) # Plot edges. ax.add_collection(land_edges) ax.add_collection(air_edges) elif mode == 'data': patches.set_array(data) patches.set_cmap(matplotlib.cm.jet) plt.colorbar(patches, ax=ax) plt.show()
def patchValMap(vals, xvec=None, yvec=None, ax=None, cMin=None, cMax=None, logScale=None, label=None, dx=1, dy=None, **kwargs): """Plot previously generated (generateVecMatrix) y map (category). Parameters ---------- vals : iterable to show xvec : dict {i:num} dict (must match vals.shape[0]) ymap : iterable vector for x axis (must match vals.shape[0]) ax : mpl.axis axis to plot, if not given a new figure is created cMin/cMax : float minimum/maximum color values logScale : bool logarithmic colour scale [min(vals)>0] label : string colorbar label """ if cMin is None: cMin = np.min(vals) if cMax is None: cMax = np.max(vals) if logScale is None: logScale = (cMin > 0.0) norm = None if logScale and cMin > 0: norm = LogNorm(vmin=cMin, vmax=cMax) else: norm = Normalize(vmin=cMin, vmax=cMax) if 'ax' is None: ax = plt.subplots()[1] recs = [] if dy is None: # map y values to unique ymap = {xy: ii for ii, xy in enumerate(np.unique(yvec))} for i in range(len(vals)): recs.append(Rectangle((xvec[i] - dx / 2, ymap[yvec[i]] - 0.5), dx, 1)) else: for i in range(len(vals)): recs.append(Rectangle((xvec[i] - dx / 2, yvec[i] - dy / 2), dx, dy)) pp = PatchCollection(recs) # ax.clear() col = ax.add_collection(pp) pp.set_edgecolor(None) pp.set_linewidths(0.0) cmap = pg.mplviewer.cmapFromName(**kwargs) cmap.set_bad('grey') if kwargs.pop('markOutside', True): cmap.set_under('darkgrey') cmap.set_bad('lightgrey') pp.set_cmap(cmap) pp.set_norm(norm) pp.set_array(np.array(vals)) pp.set_clim(cMin, cMax) ax.set_xlim(min(xvec) - dx / 2, max(xvec) + dx / 2) ax.set_ylim(len(ymap) - 0.5, -0.5) updateAxes_(ax) cbar = kwargs.pop('colorBar', True) if cbar is True: # not for cbar=1, which is really confusing! cbar = pg.mplviewer.createColorBar(col, cMin=cMin, cMax=cMax, nLevs=5, label=label) elif cbar is not False: # what the hell is this? pg.mplviewer.updateColorBar(cbar, cMin=cMin, cMax=cMax, nLevs=5, label=label) return ax, cbar, ymap
# but each (x, y) grid point in the DataFrame is defined at its center, # so generate xy pairs by shifting down and left by (dx/2, dy/2) xy_pairs = zip(df['x'].values - p['dx']/2., df['y'].values - p['dy']/2.) patches = [Rectangle(xy, width=p['dx'], height=p['dy']) for xy in xy_pairs] # build PatchCollection from the Patches from matplotlib.collections import PatchCollection pc = PatchCollection(patches, edgecolor='None') patches = None # patches is huge (~2 GB!), so clear it from RAM ASAP pickle.dump(pc, open(pc_fname, 'w')) # cache PatchCollection so it can be reused # give soil moisture data to the PatchCollection pc.set_array(df[map_var].values) # color the PatchCollection based on soil moisture pc.set_cmap(cmap) # set color map if 'ticks' in map_vars[map_var].keys(): # set color map limits based on min/max tick values pc.set_clim([min(map_vars[map_var]['ticks']), max(map_vars[map_var]['ticks'])]) ## Plot the map from matplotlib import use use('agg') # prevent matplotlib from using interactive mode import matplotlib.pyplot as plt # create the figure size = (w/float(dpi), h/float(dpi)) # convert to from pixels to inches fig = plt.figure(1, size, dpi=dpi) # create axes and add the PatchCollection
def Plot2D(self, xy=None, elecon=None, u=None, color=None, ax=None, show=0, weight=None, colorby=None, linestyle='-', label=None, xlim=None, ylim=None, filename=None, **kwds): assert self.dimensions == 2 from matplotlib.patches import Polygon import matplotlib.lines as mlines from matplotlib.collections import PatchCollection from matplotlib.cm import coolwarm, Spectral import matplotlib.pyplot as plt if xy is None: xy = array(self.coord) if elecon is None: elecon = [] for blk in self.eleblx: elecon.extend(blk.elecon.tolist()) elecon = asarray(elecon) if u is not None: xy += u.reshape(xy.shape) patches = [] for points in xy[elecon[:]]: quad = Polygon(points, True) patches.append(quad) if ax is None: fig, ax = plt.subplots() #colors = 100 * random.rand(len(patches)) p = PatchCollection(patches, linewidth=weight, **kwds) if colorby is not None: colorby = asarray(colorby).flatten() if len(colorby) == len(xy): # average value in element colorby = array([average(colorby[points]) for points in elecon]) p.set_cmap(Spectral) #coolwarm) p.set_array(colorby) p.set_clim(vmin=colorby.min(), vmax=colorby.max()) fig.colorbar(p) else: p.set_edgecolor(color) p.set_facecolor('None') p.set_linewidth(weight) p.set_linestyle(linestyle) if label: ax.plot([], [], color=color, linestyle=linestyle, label=label) ax.add_collection(p) if not ylim: ymin, ymax = amin(xy[:,1]), amax(xy[:,1]) dy = max(abs(ymin*.05), abs(ymax*.05)) ax.set_ylim([ymin-dy, ymax+dy]) else: ax.set_ylim(ylim) if not xlim: xmin, xmax = amin(xy[:,0]), amax(xy[:,0]) dx = max(abs(xmin*.05), abs(xmax*.05)) ax.set_xlim([xmin-dx, xmax+dx]) else: ax.set_xlim(xlim) ax.set_aspect('equal') if show: plt.show() if filename is not None: plt.legend() plt.savefig(filename, transparent=True, bbox_inches="tight", pad_inches=0) return ax
def voronoi_plot(box, polytopes, ax=None, color_by_sides=True, cmap=None): """Helper function to draw 2D Voronoi diagram. Args: box (:class:`freud.box.Box`): Simulation box. polytopes (:class:`numpy.ndarray`): Array containing Voronoi polytope vertices. ax (:class:`matplotlib.axes.Axes`): Axes object to plot. If :code:`None`, make a new axes and figure object. (Default value = :code:`None`). color_by_sides (bool): If :code:`True`, color cells by the number of sides. If :code:`False`, random colors are used for each cell. (Default value = :code:`True`). cmap (str): Colormap name to use (Default value = :code:`None`). Returns: :class:`matplotlib.axes.Axes`: Axes object with the diagram. """ from matplotlib import cm from matplotlib.collections import PatchCollection from matplotlib.patches import Polygon from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable from matplotlib.colorbar import Colorbar if ax is None: fig = Figure() ax = fig.subplots() # Draw Voronoi polytopes patches = [Polygon(poly[:, :2]) for poly in polytopes] patch_collection = PatchCollection(patches, edgecolors='black', alpha=0.4) if color_by_sides: colors = np.array([len(poly) for poly in polytopes]) num_colors = np.ptp(colors) + 1 else: colors = np.random.RandomState().permutation(np.arange(len(patches))) num_colors = np.unique(colors).size # Ensure we have enough colors to uniquely identify the cells if cmap is None: if color_by_sides and num_colors <= 10: cmap = 'tab10' else: if num_colors > 20: warnings.warn('More than 20 unique colors were requested. ' 'Consider providing a colormap to the cmap ' 'argument.', UserWarning) cmap = 'tab20' cmap = cm.get_cmap(cmap, num_colors) bounds = np.arange(np.min(colors), np.max(colors)+1) patch_collection.set_array(np.array(colors)-0.5) patch_collection.set_cmap(cmap) patch_collection.set_clim(bounds[0]-0.5, bounds[-1]+0.5) ax.add_collection(patch_collection) # Draw box corners = [[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0]] # Need to copy the last point so that the box is closed. corners.append(corners[0]) corners = box.make_absolute(corners)[:, :2] ax.plot(corners[:, 0], corners[:, 1], color='k') # Set title, limits, aspect ax.set_title('Voronoi Diagram') ax.set_xlim((np.min(corners[:, 0]), np.max(corners[:, 0]))) ax.set_ylim((np.min(corners[:, 1]), np.max(corners[:, 1]))) ax.set_aspect('equal', 'datalim') # Add colorbar for number of sides if color_by_sides: ax_divider = make_axes_locatable(ax) cax = ax_divider.append_axes("right", size="7%", pad="10%") cb = Colorbar(cax, patch_collection) cb.set_label("Number of sides") cb.set_ticks(bounds) return ax
max_abs = np.max(np.abs(year_mean)) fig = plt.figure(figsize=(12, 6)) ax1 = fig.add_subplot(2, 1, 1) ax2 = fig.add_subplot(2, 1, 2) cmap = plt.get_cmap('RdBu_r') rectangles = [Rectangle((year, 0), 1, 1) for year in year_mean.index] col1 = PatchCollection(rectangles) col1.set_array(year_mean) col1.set_cmap(cmap) col2 = deepcopy(col1) col2.set_clim(-max_abs, max_abs) ax1.add_collection(col1) ax2.add_collection(col2) for ax in (ax1, ax2): ax.set_ylim(0, 1) ax.set_yticks([]) ax.set_xlim(1880, 2019) ax1.set_title('Color scale over full range') ax2.set_title('Color scale centered at 0°C') fig.colorbar(col1, ax=ax1, label='GTA / °C') fig.colorbar(col2, ax=ax2, label='GTA / °C')
class CameraDisplay: """Camera Display using matplotlib. Parameters ---------- geometry : `~ctapipe.io.CameraGeometry` Definition of the Camera/Image axis : `matplotlib.axes.Axes` A matplotlib axes object to plot on, or None to create a new one title : str Title to put on camera plot allow_pick : bool (default False) if True, allow user to click and select a pixel autoupdate : bool (default True) redraw automatically (otherwise need to call plt.draw()) antialiased : bool (default True) whether to draw in antialiased mode or not. Notes ----- Speed: CameraDisplay is not intended to be very fast (matplotlib is not a very speed performant graphics library, it is intended for nice output plots). However, most of the slowness of CameraDisplay is in the constructor. Once one is displayed, changing the image that is displayed is relatively fast and efficient. Therefore it is best to initialize an instance, and change the data, rather than generating new CameraDisplays. Pixel Implementation: Pixels are rendered as a `matplotlib.collections.PatchCollection` of Polygons (either 6 or 4 sided). You can access the PatchCollection directly (to e.g. change low-level style parameters) via `CameraDisplay.pixels` Output: Since CameraDisplay uses matplotlib, any display can be saved to any output file supported via plt.savefig(filename). This includes `.pdf` and `.png`. """ def __init__(self, geometry, axes=None, title="Camera", allow_pick=False, autoupdate=True, antialiased=True): self.axes = axes if axes is not None else plt.gca() self.geom = geometry self.pixels = None self.cmap = plt.cm.jet self.autoupdate = autoupdate self._active_pixel = None self._active_pixel_label = None # initialize the plot and generate the pixels as a # RegularPolyCollection patches = [] for xx, yy, aa in zip(u.Quantity(self.geom.pix_x).value, u.Quantity(self.geom.pix_y).value, u.Quantity(np.array(self.geom.pix_area))): if self.geom.pix_type.startswith("hex"): rr = sqrt(aa * 2 / 3 / sqrt(3)) poly = RegularPolygon((xx, yy), 6, radius=rr, orientation=np.radians(0), fill=True) else: rr = sqrt(aa) * sqrt(2) poly = Rectangle((xx, yy), width=rr, height=rr, angle=np.radians(0), fill=True) patches.append(poly) self.pixels = PatchCollection(patches, cmap=self.cmap, linewidth=0) self.axes.add_collection(self.pixels) # Set up some nice plot defaults self.axes.set_aspect('equal', 'datalim') self.axes.set_title(title) self.axes.set_xlabel("X position ({})".format(self.geom.pix_x.unit)) self.axes.set_ylabel("Y position ({})".format(self.geom.pix_y.unit)) self.axes.autoscale_view() # set up a patch to display when a pixel is clicked (and # pixel_picker is enabled): self._active_pixel = copy.copy(patches[0]) self._active_pixel.set_facecolor('r') self._active_pixel.set_alpha(0.5) self._active_pixel.set_linewidth(2.0) self._active_pixel.set_visible(False) self.axes.add_patch(self._active_pixel) self._active_pixel_label = plt.text(self._active_pixel.xy[0], self._active_pixel.xy[1], "0", horizontalalignment='center', verticalalignment='center') self._active_pixel_label.set_visible(False) # enable ability to click on pixel and do something (can be # enabled on-the-fly later as well: if allow_pick: self.enable_pixel_picker() def enable_pixel_picker(self): """ enable ability to click on pixels """ self.pixels.set_picker(True) # enable click self.pixels.set_pickradius(sqrt(u.Quantity(self.geom.pix_area[0]) .value) / np.pi) self.pixels.set_snap(True) # snap cursor to pixel center self.axes.figure.canvas.mpl_connect('pick_event', self._on_pick) def set_cmap(self, cmap): """ Change the color map Parameters ---------- self: type description cmap: `matplotlib.colors.ColorMap` a color map, e.g. from `matplotlib.pyplot.cm.*` """ self.pixels.set_cmap(cmap) def set_image(self, image): """ Change the image displayed on the Camera. Parameters ---------- image: array_like array of values corresponding to the pixels in the CameraGeometry. """ image = np.asanyarray(image) if image.shape != self.geom.pix_x.shape: raise ValueError("Image has a different shape {} than the" "given CameraGeometry {}" .format(image.shape, self.geom.pix_x.shape)) self.pixels.set_array(image) self.update() def update(self): """ signal a redraw if necessary """ if self.autoupdate: plt.draw() def add_colorbar(self): """ add a colobar to the camera plot """ self.axes.figure.colorbar(self.pixels) def add_ellipse(self, centroid, length, width, angle, asymmetry=0.0, **kwargs): """ plot an ellipse on top of the camera Parameters ---------- centroid: (float,float) position of centroid length: float major axis width: float minor axis angle: float rotation angle wrt "up" about the centroid, clockwise, in radians asymmetry: float 3rd-order moment for directionality if known kwargs: any MatPlotLib style arguments to pass to the Ellipse patch """ ellipse = Ellipse(xy=centroid, width=width, height=length, angle=np.degrees(angle), fill=False, **kwargs) self.axes.add_patch(ellipse) self.update() return ellipse def overlay_moments(self, momparams, **kwargs): """helper to overlay ellipse from a `reco.MomentParameters` structure Parameters ---------- momparams: `reco.MomentParameters` structuring containing Hillas-style parameterization kwargs: key=value any style keywords to pass to matplotlib (e.g. color='red' or linewidth=6) """ el = self.add_ellipse(centroid=(momparams.cen_x, momparams.cen_y), length=momparams.length, width=momparams.width, angle=momparams.psi, **kwargs) self.axes.text(momparams.cen_x, momparams.cen_y, ("({:.02f},{:.02f})\n" "[w={:.02f},l={:.02f}]") .format(momparams.cen_x, momparams.cen_y, momparams.width, momparams.length), color=el.get_edgecolor()) def _on_pick(self, event): """ handler for when a pixel is clicked """ pix_id = event.ind.pop() xx, yy = u.Quantity(self.geom.pix_x[pix_id]).value,\ u.Quantity(self.geom.pix_y[pix_id]).value self._active_pixel.xy = (xx, yy) self._active_pixel.set_visible(True) self._active_pixel_label.set_x(xx) self._active_pixel_label.set_y(yy) self._active_pixel_label.set_text("{:003d}".format(pix_id)) self._active_pixel_label.set_visible(True) self.update() self.on_pixel_clicked(pix_id) # call user-function def on_pixel_clicked(self, pix_id): """virtual function to overide in sub-classes to do something special when a pixel is clicked """ print("Clicked pixel_id {}".format(pix_id))
def patchMatrix(mat, xmap=None, ymap=None, ax=None, cMin=None, cMax=None, logScale=None, label=None, dx=1, **kwargs): """Plot previously generated (generateVecMatrix) matrix. Parameters ---------- mat : numpy.array2d matrix to show xmap : dict {i:num} dict (must match A.shape[0]) ymap : iterable vector for x axis (must match A.shape[0]) ax : mpl.axis axis to plot, if not given a new figure is created cMin/cMax : float minimum/maximum color values logScale : bool logarithmic colour scale [min(A)>0] label : string colorbar label dx : float width of the matrix elements (by default 1) """ mat = np.ma.masked_where(mat == 0.0, mat, False) if cMin is None: cMin = np.min(mat) if cMax is None: cMax = np.max(mat) if logScale is None: logScale = (cMin > 0.0) if logScale: norm = LogNorm(vmin=cMin, vmax=cMax) else: norm = Normalize(vmin=cMin, vmax=cMax) if 'ax' is None: ax = plt.subplots()[1] iy, ix = np.nonzero(mat) # != 0) recs = [] vals = [] for i, _ in enumerate(ix): recs.append(Rectangle((ix[i] - dx / 2, iy[i] - 0.5), dx, 1)) vals.append(mat[iy[i], ix[i]]) pp = PatchCollection(recs) col = ax.add_collection(pp) pp.set_edgecolor(None) pp.set_linewidths(0.0) if 'cmap' in kwargs: pp.set_cmap(kwargs.pop('cmap')) if 'cMap' in kwargs: pp.set_cmap(kwargs.pop('cMap')) pp.set_norm(norm) pp.set_array(np.array(vals)) pp.set_clim(cMin, cMax) xval = [k for k in xmap.keys()] ax.set_xlim(min(xval) - dx / 2, max(xval) + dx / 2) ax.set_ylim(len(ymap) + 0.5, -0.5) updateAxes_(ax) cbar = None if kwargs.pop('colorBar', True): ori = kwargs.pop('orientation', 'horizontal') cbar = pg.mplviewer.createColorBar(col, cMin=cMin, cMax=cMax, nLevs=5, label=label, orientation=ori) return ax, cbar
def patchMatrix(A, xmap=None, ymap=None, ax=None, cMin=None, cMax=None, logScale=None, label=None, dx=1, **kwargs): """ plot previously generated (generateVecMatrix) matrix Parameters ---------- A : numpy.array2d matrix to show xmap : dict {i:num} dict (must match A.shape[0]) ymap : iterable vector for x axis (must match A.shape[0]) ax : mpl.axis axis to plot, if not given a new figure is created cMin/cMax : float minimum/maximum color values logScale : bool logarithmic colour scale [min(A)>0] label : string colorbar label """ mat = np.ma.masked_where(A == 0.0, A, False) if cMin is None: cMin = np.min(mat) if cMax is None: cMax = np.max(mat) if logScale is None: logScale = (cMin > 0.0) if logScale: norm = LogNorm(vmin=cMin, vmax=cMax) else: norm = Normalize(vmin=cMin, vmax=cMax) if 'ax' is None: fig, ax = plt.subplots() iy, ix = np.nonzero(A) # != 0) recs = [] vals = [] for i in range(len(ix)): recs.append(Rectangle((ix[i]-dx/2, iy[i]-0.5), dx, 1)) vals.append(A[iy[i], ix[i]]) pp = PatchCollection(recs) col = ax.add_collection(pp) pp.set_edgecolor(None) pp.set_linewidths(0.0) if 'cmap' in kwargs: pp.set_cmap(kwargs.pop('cmap')) pp.set_norm(norm) pp.set_array(np.array(vals)) pp.set_clim(cMin, cMax) xval = [k for k in xmap.keys()] ax.set_xlim(min(xval)-dx/2, max(xval)+dx/2) ax.set_ylim(len(ymap)+0.5, -0.5) updateAxes_(ax) cbar = None if kwargs.pop('colorBar', True): cbar = pg.mplviewer.createColorbar(col, cMin=cMin, cMax=cMax, nLevs=5, label=label) return ax, cbar
def patchValMap(vals, xvec=None, yvec=None, ax=None, cMin=None, cMax=None, logScale=None, label=None, dx=1, dy=None, **kwargs): """ plot previously generated (generateVecMatrix) y map (category) Parameters ---------- A : iterable to show xvec : dict {i:num} dict (must match A.shape[0]) ymap : iterable vector for x axis (must match A.shape[0]) ax : mpl.axis axis to plot, if not given a new figure is created cMin/cMax : float minimum/maximum color values logScale : bool logarithmic colour scale [min(A)>0] label : string colorbar label """ if cMin is None: cMin = np.min(vals) if cMax is None: cMax = np.max(vals) if logScale is None: logScale = (cMin > 0.0) if logScale: norm = LogNorm(vmin=cMin, vmax=cMax) else: norm = Normalize(vmin=cMin, vmax=cMax) if 'ax' is None: fig, ax = plt.subplots() recs = [] if dy is None: # map y values to unique ymap = {xy: ii for ii, xy in enumerate(np.unique(yvec))} for i in range(len(vals)): recs.append(Rectangle((xvec[i]-dx/2, ymap[yvec[i]]-0.5), dx, 1)) else: for i in range(len(vals)): recs.append(Rectangle((xvec[i]-dx/2, yvec[i]-dy/2), dx, dy)) pp = PatchCollection(recs) col = ax.add_collection(pp) pp.set_edgecolor(None) pp.set_linewidths(0.0) if 'cmap' in kwargs: pp.set_cmap(kwargs.pop('cmap')) pp.set_norm(norm) pp.set_array(np.array(vals)) pp.set_clim(cMin, cMax) ax.set_xlim(min(xvec)-dx/2, max(xvec)+dx/2) ax.set_ylim(len(ymap)-0.5, -0.5) updateAxes_(ax) cbar = None if kwargs.pop('colorBar', True): cbar = pg.mplviewer.createColorbar(col, cMin=cMin, cMax=cMax, nLevs=5, label=label) return ax, cbar, ymap
class CameraPlot(object): '''A Class for a camera pixel''' def __init__( self, telescope, ax, data=None, cmap='gray', vmin=None, vmax=None, ): ''' :telescope: the telescope class for the pixel :data: array-like with one value for each pixel :cmap: a matpixellib colormap string or instance :vmin: minimum value of the colormap :vmax: maximum value of the colormap ''' self.telescope = telescope if data is None: data = np.zeros(telescope.n_pixel) patches = [] if telescope.pixel_shape == 'hexagon': for xy in zip(telescope.pixel_x, telescope.pixel_y): patches.append( RegularPolygon( xy=xy, numVertices=6, radius=telescope.pixel_size, orientation=telescope.pixel_orientation, ) ) self.pixel = PatchCollection(patches) self.pixel.set_linewidth(0) self.pixel.set_cmap(cmap) self.pixel.set_array(data) self.pixel.set_clim(vmin, vmax) self.vmin = vmin self.vmax = vmax self.ax = ax self.ax.add_collection(self.pixel) self.ax.set_xlim( self.telescope.pixel_x.min() - 2 * self.telescope.pixel_size, self.telescope.pixel_x.max() + 2 * self.telescope.pixel_size, ) self.ax.set_ylim( self.telescope.pixel_y.min() - 2 * self.telescope.pixel_size, self.telescope.pixel_y.max() + 2 * self.telescope.pixel_size, ) @property def data(self): return self.pixel.get_array() @data.setter def data(self, data): self.pixel.set_array(data) if not self.vmin or not self.vmax: self.pixel.autoscale() self.pixel.changed()
def patchValMap(vals, xvec=None, yvec=None, ax=None, cMin=None, cMax=None, logScale=None, label=None, dx=1, dy=None, **kwargs): """Plot previously generated (generateVecMatrix) y map (category). Parameters ---------- vals : iterable Data values to show. xvec : dict {i:num} dict (must match vals.shape[0]) ymap : iterable vector for x axis (must match vals.shape[0]) ax : mpl.axis axis to plot, if not given a new figure is created cMin/cMax : float minimum/maximum color values logScale : bool logarithmic colour scale [min(vals)>0] label : string colorbar label ** kwargs: * circular : bool Plot in polar coordinates. """ if cMin is None: cMin = np.min(vals) if cMax is None: cMax = np.max(vals) if logScale is None: logScale = (cMin > 0.0) norm = None if logScale and cMin > 0: norm = LogNorm(vmin=cMin, vmax=cMax) else: norm = Normalize(vmin=cMin, vmax=cMax) if ax is None: ax = plt.subplots()[1] recs = [] circular = kwargs.pop('circular', False) if circular: recs = [None] * len(xvec) if dy is None: # map y values to unique ymap = {xy: ii for ii, xy in enumerate(np.unique(yvec))} xyMap = {} for i, y in enumerate(yvec): if y not in xyMap: xyMap[y] = [] xyMap[y].append(i) # maxR = max(ymap.values()) # what's that for? not used dR = 1 / (len(ymap.values())+1) # dOff = np.pi / 2 # what's that for? not used for y, xIds in xyMap.items(): r = 1. - dR*(ymap[y]+1) # ax.plot(r * np.cos(xvec[xIds]), # r * np.sin(xvec[xIds]), 'o') # print(y, ymap[y]) for i in xIds: phi = xvec[i] # x = r * np.cos(phi) # what's that for? not used y = r * np.sin(phi) dPhi = (xvec[1] - xvec[0]) recs[i] = Wedge((0., 0.), r + dR/1.5, (phi - dPhi)*360/(2*np.pi), (phi + dPhi)*360/(2*np.pi), width=dR, zorder=1+r) # if i < 5: # ax.text(x, y, str(i)) # pg.wait() else: raise("Implementme") else: if dy is None: # map y values to unique ymap = {xy: ii for ii, xy in enumerate(np.unique(yvec))} for i in range(len(vals)): recs.append(Rectangle((xvec[i] - dx / 2, ymap[yvec[i]] - 0.5), dx, 1)) else: for i in range(len(vals)): recs.append(Rectangle((xvec[i] - dx / 2, yvec[i] - dy / 2), dx, dy)) ax.set_xlim(min(xvec) - dx / 2, max(xvec) + dx / 2) ax.set_ylim(len(ymap) - 0.5, -0.5) pp = PatchCollection(recs) # ax.clear() col = ax.add_collection(pp) pp.set_edgecolor(None) pp.set_linewidths(0.0) if circular: pp.set_edgecolor('black') pp.set_linewidths(0.1) cmap = pg.mplviewer.cmapFromName(**kwargs) if kwargs.pop('markOutside', False): cmap.set_bad('grey') cmap.set_under('darkgrey') cmap.set_over('lightgrey') cmap.set_bad('black') pp.set_cmap(cmap) pp.set_norm(norm) pp.set_array(vals) pp.set_clim(cMin, cMax) updateAxes_(ax) cbar = kwargs.pop('colorBar', True) ori = kwargs.pop('orientation', 'horizontal') if cbar in ['horizontal', 'vertical']: ori = cbar cbar = True if cbar is True: # not for cbar=1, which is really confusing! cbar = pg.mplviewer.createColorBar(col, cMin=cMin, cMax=cMax, nLevs=5, label=label, orientation=ori) elif cbar is not False: # .. cbar is an already existing cbar .. so we update its values pg.mplviewer.updateColorBar(cbar, cMin=cMin, cMax=cMax, nLevs=5, label=label) updateAxes_(ax) return ax, cbar, ymap
class CameraDisplay: """ Camera Display using matplotlib. Parameters ---------- geometry : `~ctapipe.io.CameraGeometry` Definition of the Camera/Image image: array_like array of values corresponding to the pixels in the CameraGeometry. ax : `matplotlib.axes.Axes` A matplotlib axes object to plot on, or None to create a new one title : str (default "Camera") Title to put on camera plot norm : str or `matplotlib.color.Normalize` instance (default 'lin') Normalization for the color scale. Supported str arguments are - 'lin': linear scale - 'log': logarithmic scale (base 10) cmap : str or `matplotlib.colors.Colormap` (default 'hot') Color map to use (see `matplotlib.cm`) allow_pick : bool (default False) if True, allow user to click and select a pixel autoupdate : bool (default True) redraw automatically (otherwise need to call plt.draw()) autoscale : bool (default True) rescale the vmin/vmax values when the image changes. This is set to False if `set_limits_*` is called to explicity set data limits. antialiased : bool (default True) whether to draw in antialiased mode or not. Notes ----- Speed: CameraDisplay is not intended to be very fast (matplotlib is not a very speed performant graphics library, it is intended for nice output plots). However, most of the slowness of CameraDisplay is in the constructor. Once one is displayed, changing the image that is displayed is relatively fast and efficient. Therefore it is best to initialize an instance, and change the data, rather than generating new CameraDisplays. Pixel Implementation: Pixels are rendered as a `matplotlib.collections.PatchCollection` of Polygons (either 6 or 4 sided). You can access the PatchCollection directly (to e.g. change low-level style parameters) via `CameraDisplay.pixels` Output: Since CameraDisplay uses matplotlib, any display can be saved to any output file supported via plt.savefig(filename). This includes `.pdf` and `.png`. """ def __init__( self, geometry, image=None, ax=None, title="Camera", norm="lin", cmap="hot", allow_pick=False, autoupdate=True, autoscale=True, antialiased=True, ): self.axes = ax if ax is not None else plt.gca() self.geom = geometry self.pixels = None self.colorbar = None self.autoupdate = autoupdate self.autoscale = autoscale self._active_pixel = None self._active_pixel_label = None # initialize the plot and generate the pixels as a # RegularPolyCollection patches = [] for xx, yy, aa in zip( u.Quantity(self.geom.pix_x).value, u.Quantity(self.geom.pix_y).value, u.Quantity(np.array(self.geom.pix_area))): if self.geom.pix_type.startswith("hex"): rr = sqrt(aa * 2 / 3 / sqrt(3)) poly = RegularPolygon( (xx, yy), 6, radius=rr, orientation=self.geom.pix_rotation.rad, fill=True, ) else: rr = sqrt(aa) poly = Rectangle( (xx - rr / 2., yy - rr / 2.), width=rr, height=rr, angle=self.geom.pix_rotation.deg, fill=True, ) patches.append(poly) self.pixels = PatchCollection(patches, cmap=cmap, linewidth=0) self.axes.add_collection(self.pixels) self.pixel_highlighting = copy.copy(self.pixels) self.pixel_highlighting.set_facecolor('none') self.pixel_highlighting.set_linewidth(0) self.axes.add_collection(self.pixel_highlighting) # Set up some nice plot defaults self.axes.set_aspect('equal', 'datalim') self.axes.set_title(title) self.axes.set_xlabel("X position ({})".format(self.geom.pix_x.unit)) self.axes.set_ylabel("Y position ({})".format(self.geom.pix_y.unit)) self.axes.autoscale_view() # set up a patch to display when a pixel is clicked (and # pixel_picker is enabled): self._active_pixel = copy.copy(patches[0]) self._active_pixel.set_facecolor('r') self._active_pixel.set_alpha(0.5) self._active_pixel.set_linewidth(2.0) self._active_pixel.set_visible(False) self.axes.add_patch(self._active_pixel) self._active_pixel_label = self.axes.text(self._active_pixel.xy[0], self._active_pixel.xy[1], "0", horizontalalignment='center', verticalalignment='center') self._active_pixel_label.set_visible(False) # enable ability to click on pixel and do something (can be # enabled on-the-fly later as well: if allow_pick: self.enable_pixel_picker() if image is not None: self.image = image else: self.image = np.zeros_like(self.geom.pix_id, dtype=np.float) self.norm = norm def highlight_pixels(self, pixels, color='g', linewidth=1, alpha=0.75): ''' Highlight the given pixels with a colored line around them Parameters ---------- pixels : index-like The pixels to highlight. Can either be a list or array of integers or a boolean mask of length number of pixels color: a matplotlib conform color the color for the pixel highlighting linewidth: float linewidth of the highlighting in points alpha: 0 <= alpha <= 1 The transparency ''' l = np.zeros_like(self.image) l[pixels] = linewidth self.pixel_highlighting.set_linewidth(l) self.pixel_highlighting.set_alpha(alpha) self.pixel_highlighting.set_edgecolor(color) self.update() def enable_pixel_picker(self): """ enable ability to click on pixels """ self.pixels.set_picker(True) # enable click self.pixels.set_pickradius( sqrt(u.Quantity(self.geom.pix_area[0]).value) / np.pi) self.pixels.set_snap(True) # snap cursor to pixel center self.axes.figure.canvas.mpl_connect('pick_event', self._on_pick) def set_limits_minmax(self, zmin, zmax): """ set the color scale limits from min to max """ self.pixels.set_clim(zmin, zmax) self.autoscale = False self.update() def set_limits_percent(self, percent=95): """ auto-scale the color range to percent of maximum """ zmin = self.pixels.get_array().min() zmax = self.pixels.get_array().max() dz = zmax - zmin frac = percent / 100.0 self.autoscale = False self.set_limits_minmax(zmin, zmax - (1.0 - frac) * dz) @property def norm(self): ''' The norm instance of the Display Possible values: - "lin": linear scale - "log": log scale - any matplotlib.colors.Normalize instance, e. g. PowerNorm(gamma=-2) ''' return self.pixels.norm @norm.setter def norm(self, norm): if norm == 'lin': self.pixels.norm = Normalize() elif norm == 'log': self.pixels.norm = LogNorm() self.pixels.autoscale() # this is to handle matplotlib bug #5424 elif isinstance(norm, Normalize): self.pixels.norm = norm else: raise ValueError('Unsupported norm: {}'.format(norm)) self.update(force=True) self.pixels.autoscale() @property def cmap(self): """ Color map to use. Either a name or `matplotlib.colors.ColorMap` instance, e.g. from `matplotlib.pyplot.cm` """ return self.pixels.get_cmap() @cmap.setter def cmap(self, cmap): self.pixels.set_cmap(cmap) self.update() @property def image(self): """The image displayed on the camera (1D array of pixel values)""" return self.pixels.get_array() @image.setter def image(self, image): """ Change the image displayed on the Camera. Parameters ---------- image: array_like array of values corresponding to the pixels in the CameraGeometry. """ image = np.asanyarray(image) if image.shape != self.geom.pix_x.shape: raise ValueError("Image has a different shape {} than the" "given CameraGeometry {}".format( image.shape, self.geom.pix_x.shape)) self.pixels.set_array(image) self.pixels.changed() if self.autoscale: self.pixels.autoscale() self.update() def update(self, force=False): """ signal a redraw if necessary """ if self.autoupdate: if self.colorbar is not None: if force is True: self.colorbar.update_bruteforce(self.pixels) else: self.colorbar.update_normal(self.pixels) self.colorbar.draw_all() self.axes.figure.canvas.draw() def add_colorbar(self, **kwargs): """ add a colobar to the camera plot kwargs are passed to `figure.colorbar(self.pixels, **kwargs)` See matplotlib documentation for the supported kwargs: http://matplotlib.org/api/figure_api.html#matplotlib.figure.Figure.colorbar """ if self.colorbar is not None: raise ValueError( 'There is already a colorbar attached to this CameraDisplay') else: self.colorbar = self.axes.figure.colorbar(self.pixels, **kwargs) self.update() def add_ellipse(self, centroid, length, width, angle, asymmetry=0.0, **kwargs): """ plot an ellipse on top of the camera Parameters ---------- centroid: (float, float) position of centroid length: float major axis width: float minor axis angle: float rotation angle wrt "up" about the centroid, clockwise, in radians asymmetry: float 3rd-order moment for directionality if known kwargs: any MatPlotLib style arguments to pass to the Ellipse patch """ ellipse = Ellipse(xy=centroid, width=width, height=length, angle=np.degrees(angle), fill=False, **kwargs) self.axes.add_patch(ellipse) self.update() return ellipse def overlay_moments(self, momparams, **kwargs): """helper to overlay ellipse from a `reco.MomentParameters` structure Parameters ---------- momparams: `reco.MomentParameters` structuring containing Hillas-style parameterization kwargs: key=value any style keywords to pass to matplotlib (e.g. color='red' or linewidth=6) """ el = self.add_ellipse(centroid=(momparams.cen_x.value, momparams.cen_y.value), length=momparams.length.value, width=momparams.width.value, angle=momparams.psi.to(u.rad).value, **kwargs) self.axes.text(momparams.cen_x.value, momparams.cen_y.value, ("({:.02f},{:.02f})\n" "[w={:.02f},l={:.02f}]").format( momparams.cen_x, momparams.cen_y, momparams.width, momparams.length), color=el.get_edgecolor()) def _on_pick(self, event): """ handler for when a pixel is clicked """ pix_id = event.ind[-1] xx, yy, aa = u.Quantity(self.geom.pix_x[pix_id]).value, \ u.Quantity(self.geom.pix_y[pix_id]).value, \ u.Quantity(np.array(self.geom.pix_area)[pix_id]) if self.geom.pix_type.startswith("hex"): self._active_pixel.xy = (xx, yy) else: rr = sqrt(aa) self._active_pixel.xy = (xx - rr / 2., yy - rr / 2.) self._active_pixel.set_visible(True) self._active_pixel_label.set_x(xx) self._active_pixel_label.set_y(yy) self._active_pixel_label.set_text("{:003d}".format(pix_id)) self._active_pixel_label.set_visible(True) self.update() self.on_pixel_clicked(pix_id) # call user-function def on_pixel_clicked(self, pix_id): """virtual function to overide in sub-classes to do something special when a pixel is clicked """ print("Clicked pixel_id {}".format(pix_id)) def show(self): self.axes.figure.show()
def showStitchedModels_Redundant(mods, ax=None, cmin=None, cmax=None, **kwargs): """Show several 1d block models as (stitched) section.""" x = kwargs.pop('x', np.arange(len(mods))) topo = kwargs.pop('topo', x * 0) nlay = int(np.floor((len(mods[0]) - 1) / 2.)) + 1 if cmin is None or cmax is None: cmin = 1e9 cmax = 1e-9 for model in mods: res = np.asarray(model)[nlay - 1:nlay * 2 - 1] cmin = min(cmin, min(res)) cmax = max(cmax, max(res)) if kwargs.pop('sameSize', True): # all having the same width dx = np.ones_like(x) * np.median(np.diff(x)) else: dx = np.diff(x) * 1.05 dx = np.hstack((dx, dx[-1])) x1 = x - dx / 2 if ax is None: fig, ax = plt.subplots() else: ax = ax fig = ax.figure # ax.plot(x, x * 0., 'k.') zm = kwargs.pop('zm', None) maxz = 0. if zm is not None: maxz = zm recs = [] RES = [] for i, mod in enumerate(mods): mod1 = np.asarray(mod) res = mod1[nlay - 1:] RES.extend(res) thk = mod1[:nlay - 1] thk = np.hstack((thk, thk[-1])) z = np.hstack((0., np.cumsum(thk))) if zm is not None: thk[-1] = zm - z[-2] z[-1] = zm else: maxz = max(maxz, z[-1]) for j, _ in enumerate(thk): recs.append(Rectangle((x1[i], topo[i] - z[j]), dx[i], -thk[j])) pp = PatchCollection(recs, edgecolors=kwargs.pop('edgecolors', 'none')) pp.set_edgecolor(kwargs.pop('edgecolors', 'none')) pp.set_linewidths(0.0) ax.add_collection(pp) if 'cmap' in kwargs: pp.set_cmap(kwargs['cmap']) print(cmin, cmax) norm = colors.LogNorm(cmin, cmax) pp.set_norm(norm) pp.set_array(np.array(RES)) # pp.set_clim(cmin, cmax) ax.set_ylim((-maxz, max(topo))) ax.set_xlim((x1[0], x1[-1] + dx[-1])) cbar = None if kwargs.pop('colorBar', True): cbar = plt.colorbar(pp, ax=ax, norm=norm, orientation='horizontal', aspect=60) # , ticks=[1, 3, 10, 30, 100, 300]) if 'ticks' in kwargs: cbar.set_ticks(kwargs['ticks']) # cbar.autoscale_None() if ax is None: # newly created fig+ax return fig, ax else: # already given, better give back color bar return cbar
def showStitchedModels(mods, axes=None, cmin=None, cmax=None, **kwargs): """ Show several 1d block models as (stitched) section. """ x = kwargs.pop('x', np.arange(len(mods))) topo = kwargs.pop('topo', x*0) nlay = int(np.floor((len(mods[0]) - 1) / 2.)) + 1 if cmin is None or cmax is None: cmin = 1e9 cmax = 1e-9 for model in mods: res = np.asarray(model)[nlay - 1:nlay * 2 - 1] cmin = min(cmin, min(res)) cmax = max(cmax, max(res)) if kwargs.pop('sameSize', True): # all having the same width dx = np.ones_like(x)*np.median(np.diff(x)) else: dx = np.diff(x) * 1.05 dx = np.hstack((dx, dx[-1])) x1 = x - dx / 2 if axes is None: fig, ax = plt.subplots() else: ax = axes fig = ax.figure # ax.plot(x, x * 0., 'k.') zm = kwargs.pop('zm', None) maxz = 0. if zm is not None: maxz = zm recs = [] RES = [] for i, mod in enumerate(mods): mod1 = np.asarray(mod) res = mod1[nlay - 1:] RES.extend(res) thk = mod1[:nlay - 1] thk = np.hstack((thk, thk[-1])) z = np.hstack((0., np.cumsum(thk))) if zm is not None: thk[-1] = zm - z[-2] z[-1] = zm else: maxz = max(maxz, z[-1]) for j in range(len(thk)): recs.append(Rectangle((x1[i], topo[i]-z[j]), dx[i], -thk[j])) pp = PatchCollection(recs, edgecolors=kwargs.pop('edgecolors', 'none')) pp.set_edgecolor(kwargs.pop('edgecolors', 'none')) pp.set_linewidths(0.0) ax.add_collection(pp) if 'cmap' in kwargs: pp.set_cmap(kwargs['cmap']) print(cmin, cmax) norm = LogNorm(cmin, cmax) pp.set_norm(norm) pp.set_array(np.array(RES)) # pp.set_clim(cmin, cmax) ax.set_ylim((-maxz, max(topo))) ax.set_xlim((x1[0], x1[-1] + dx[-1])) cbar = None if kwargs.pop('colorBar', True): cbar = plt.colorbar(pp, ax=ax, norm=norm, orientation='horizontal', aspect=60) # , ticks=[1, 3, 10, 30, 100, 300]) if 'ticks' in kwargs: cbar.set_ticks(kwargs['ticks']) # cbar.autoscale_None() if axes is None: # newly created fig+ax return fig, ax else: # already given, better give back color bar return cbar
class CameraDisplay: """ Camera Display using matplotlib. Parameters ---------- geometry : `~ctapipe.instrument.CameraGeometry` Definition of the Camera/Image image: array_like array of values corresponding to the pixels in the CameraGeometry. ax : `matplotlib.axes.Axes` A matplotlib axes object to plot on, or None to create a new one title : str (default "Camera") Title to put on camera plot norm : str or `matplotlib.color.Normalize` instance (default 'lin') Normalization for the color scale. Supported str arguments are - 'lin': linear scale - 'log': logarithmic scale (base 10) cmap : str or `matplotlib.colors.Colormap` (default 'hot') Color map to use (see `matplotlib.cm`) allow_pick : bool (default False) if True, allow user to click and select a pixel autoupdate : bool (default True) redraw automatically (otherwise need to call plt.draw()) autoscale : bool (default True) rescale the vmin/vmax values when the image changes. This is set to False if `set_limits_*` is called to explicity set data limits. antialiased : bool (default True) whether to draw in antialiased mode or not. Notes ----- Speed: CameraDisplay is not intended to be very fast (matplotlib is not a very speed performant graphics library, it is intended for nice output plots). However, most of the slowness of CameraDisplay is in the constructor. Once one is displayed, changing the image that is displayed is relatively fast and efficient. Therefore it is best to initialize an instance, and change the data, rather than generating new CameraDisplays. Pixel Implementation: Pixels are rendered as a `matplotlib.collections.PatchCollection` of Polygons (either 6 or 4 sided). You can access the PatchCollection directly (to e.g. change low-level style parameters) via `CameraDisplay.pixels` Output: Since CameraDisplay uses matplotlib, any display can be saved to any output file supported via plt.savefig(filename). This includes `.pdf` and `.png`. """ def __init__( self, geometry, image=None, ax=None, title=None, norm="lin", cmap=None, allow_pick=False, autoupdate=True, autoscale=True, antialiased=True, ): self.axes = ax if ax is not None else plt.gca() self.geom = geometry self.pixels = None self.colorbar = None self.autoupdate = autoupdate self.autoscale = autoscale self._active_pixel = None self._active_pixel_label = None if title is None: title = geometry.cam_id # initialize the plot and generate the pixels as a # RegularPolyCollection patches = [] if not hasattr(self.geom, "mask"): self.geom.mask = np.ones_like(self.geom.pix_x.value, dtype=bool) for xx, yy, aa in zip( u.Quantity(self.geom.pix_x[self.geom.mask]).value, u.Quantity(self.geom.pix_y[self.geom.mask]).value, u.Quantity(np.array(self.geom.pix_area)[self.geom.mask]).value): if self.geom.pix_type.startswith("hex"): rr = sqrt(aa * 2 / 3 / sqrt(3)) + 2*PIXEL_EPSILON poly = RegularPolygon( (xx, yy), 6, radius=rr, orientation=self.geom.pix_rotation.rad, fill=True, ) else: rr = sqrt(aa) + PIXEL_EPSILON poly = Rectangle( (xx-rr/2., yy-rr/2.), width=rr, height=rr, angle=self.geom.pix_rotation.deg, fill=True, ) patches.append(poly) self.pixels = PatchCollection(patches, cmap=cmap, linewidth=0) self.axes.add_collection(self.pixels) self.pixel_highlighting = copy.copy(self.pixels) self.pixel_highlighting.set_facecolor('none') self.pixel_highlighting.set_linewidth(0) self.axes.add_collection(self.pixel_highlighting) # Set up some nice plot defaults self.axes.set_aspect('equal', 'datalim') self.axes.set_title(title) self.axes.set_xlabel("X position ({})".format(self.geom.pix_x.unit)) self.axes.set_ylabel("Y position ({})".format(self.geom.pix_y.unit)) self.axes.autoscale_view() # set up a patch to display when a pixel is clicked (and # pixel_picker is enabled): self._active_pixel = copy.copy(patches[0]) self._active_pixel.set_facecolor('r') self._active_pixel.set_alpha(0.5) self._active_pixel.set_linewidth(2.0) self._active_pixel.set_visible(False) self.axes.add_patch(self._active_pixel) self._active_pixel_label = self.axes.text(self._active_pixel.xy[0], self._active_pixel.xy[1], "0", horizontalalignment='center', verticalalignment='center') self._active_pixel_label.set_visible(False) # enable ability to click on pixel and do something (can be # enabled on-the-fly later as well: if allow_pick: self.enable_pixel_picker() if image is not None: self.image = image else: self.image = np.zeros_like(self.geom.pix_id, dtype=np.float) self.norm = norm def highlight_pixels(self, pixels, color='g', linewidth=1, alpha=0.75): ''' Highlight the given pixels with a colored line around them Parameters ---------- pixels : index-like The pixels to highlight. Can either be a list or array of integers or a boolean mask of length number of pixels color: a matplotlib conform color the color for the pixel highlighting linewidth: float linewidth of the highlighting in points alpha: 0 <= alpha <= 1 The transparency ''' l = np.zeros_like(self.image) l[pixels] = linewidth self.pixel_highlighting.set_linewidth(l) self.pixel_highlighting.set_alpha(alpha) self.pixel_highlighting.set_edgecolor(color) self._update() def enable_pixel_picker(self): """ enable ability to click on pixels """ self.pixels.set_picker(True) # enable click self.pixels.set_pickradius(sqrt(u.Quantity(self.geom.pix_area[0]) .value) / np.pi) self.pixels.set_snap(True) # snap cursor to pixel center self.axes.figure.canvas.mpl_connect('pick_event', self._on_pick) def set_limits_minmax(self, zmin, zmax): """ set the color scale limits from min to max """ self.pixels.set_clim(zmin, zmax) self.autoscale = False self._update() def set_limits_percent(self, percent=95): """ auto-scale the color range to percent of maximum """ zmin = self.pixels.get_array().min() zmax = self.pixels.get_array().max() dz = zmax - zmin frac = percent / 100.0 self.autoscale = False self.set_limits_minmax(zmin, zmax - (1.0 - frac) * dz) @property def norm(self): ''' The norm instance of the Display Possible values: - "lin": linear scale - "log": log scale (cannot have negative values) - "symlog": symmetric log scale (negative values are ok) - any matplotlib.colors.Normalize instance, e. g. PowerNorm(gamma=-2) ''' return self.pixels.norm @norm.setter def norm(self, norm): if norm == 'lin': self.pixels.norm = Normalize() elif norm == 'log': self.pixels.norm = LogNorm() self.pixels.autoscale() # this is to handle matplotlib bug #5424 elif norm == 'symlog': self.pixels.norm = SymLogNorm(linthresh=1.0) self.pixels.autoscale() elif isinstance(norm, Normalize): self.pixels.norm = norm else: raise ValueError("Unsupported norm: '{}', options are 'lin'," "'log','symlog', or a matplotlib Normalize object" .format(norm)) self.update(force=True) self.pixels.autoscale() @property def cmap(self): """ Color map to use. Either a name or `matplotlib.colors.ColorMap` instance, e.g. from `matplotlib.pyplot.cm` """ return self.pixels.get_cmap() @cmap.setter def cmap(self, cmap): self.pixels.set_cmap(cmap) self._update() @property def image(self): """The image displayed on the camera (1D array of pixel values)""" return self.pixels.get_array() @image.setter def image(self, image): """ Change the image displayed on the Camera. Parameters ---------- image: array_like array of values corresponding to the pixels in the CameraGeometry. """ image = np.asanyarray(image) if image.shape != self.geom.pix_x.shape: raise ValueError( "Image has a different shape {} than the " "given CameraGeometry {}" .format(image.shape, self.geom.pix_x.shape) ) self.pixels.set_array(image[self.geom.mask]) self.pixels.changed() if self.autoscale: self.pixels.autoscale() self._update() def _update(self, force=False): """ signal a redraw if autoupdate is turned on """ if self.autoupdate: self.update(force) def update(self, force=False): """ redraw the display now """ self.axes.figure.canvas.draw() if self.colorbar is not None: if force is True: self.colorbar.update_bruteforce(self.pixels) else: self.colorbar.update_normal(self.pixels) self.colorbar.draw_all() def add_colorbar(self, **kwargs): """ add a colobar to the camera plot kwargs are passed to `figure.colorbar(self.pixels, **kwargs)` See matplotlib documentation for the supported kwargs: http://matplotlib.org/api/figure_api.html#matplotlib.figure.Figure.colorbar """ if self.colorbar is not None: raise ValueError( 'There is already a colorbar attached to this CameraDisplay' ) else: self.colorbar = self.axes.figure.colorbar(self.pixels, **kwargs) self.update() def add_ellipse(self, centroid, length, width, angle, asymmetry=0.0, **kwargs): """ plot an ellipse on top of the camera Parameters ---------- centroid: (float, float) position of centroid length: float major axis width: float minor axis angle: float rotation angle wrt x-axis about the centroid, anticlockwise, in radians asymmetry: float 3rd-order moment for directionality if known kwargs: any MatPlotLib style arguments to pass to the Ellipse patch """ ellipse = Ellipse(xy=centroid, width=length, height=width, angle=np.degrees(angle), fill=False, **kwargs) self.axes.add_patch(ellipse) self.update() return ellipse def overlay_moments(self, momparams, with_label=True, **kwargs): """helper to overlay ellipse from a `reco.MomentParameters` structure Parameters ---------- momparams: `reco.MomentParameters` structuring containing Hillas-style parameterization kwargs: key=value any style keywords to pass to matplotlib (e.g. color='red' or linewidth=6) """ # strip off any units cen_x = u.Quantity(momparams.cen_x).value cen_y = u.Quantity(momparams.cen_y).value length = u.Quantity(momparams.length).value width = u.Quantity(momparams.width).value el = self.add_ellipse(centroid=(cen_x, cen_y), length=length*2, width=width*2, angle=momparams.psi.rad, **kwargs) if with_label: self.axes.text(cen_x, cen_y, ("({:.02f},{:.02f})\n" "[w={:.02f},l={:.02f}]") .format(momparams.cen_x, momparams.cen_y, momparams.width, momparams.length), color=el.get_edgecolor()) def _on_pick(self, event): """ handler for when a pixel is clicked """ pix_id = event.ind[-1] xx, yy, aa = u.Quantity(self.geom.pix_x[pix_id]).value, \ u.Quantity(self.geom.pix_y[pix_id]).value, \ u.Quantity(np.array(self.geom.pix_area)[pix_id]) if self.geom.pix_type.startswith("hex"): self._active_pixel.xy = (xx, yy) else: rr = sqrt(aa) self._active_pixel.xy = (xx - rr / 2., yy - rr / 2.) self._active_pixel.set_visible(True) self._active_pixel_label.set_x(xx) self._active_pixel_label.set_y(yy) self._active_pixel_label.set_text("{:003d}".format(pix_id)) self._active_pixel_label.set_visible(True) self._update() self.on_pixel_clicked(pix_id) # call user-function def on_pixel_clicked(self, pix_id): """virtual function to overide in sub-classes to do something special when a pixel is clicked """ print("Clicked pixel_id {}".format(pix_id)) def show(self): self.axes.figure.show()
def GridStrain(pos, disp, k, par, plotpar, plotst): ''' GridStrain computes the infinitesimal strain of a network of stations with displacements in x (east) and y (north). Strain in z is assumed to be zero (plane strain) USE: cent,eps,ome,pstrain,rotc = GridStrain(pos,disp,k,par,plotpar,plotst) pos = nstations x 2 matrix with x (east) and y (north) positions of stations in meters disp = nstations x 2 matrix with x (east) and y (north) displacements of stations in meters k = Type of computation: Delaunay (k = 0), nearest neighbor (k = 1), or distance weighted (k = 2) par = Parameters for nearest neighbor or distance weighted computation. If Delaunay (k = 0), enter a scalar corresponding to the minimum internal angle of a triangle valid for computation. If nearest neighbor (k = 1), input a 1 x 3 vector with grid spacing, number of nearest neighbors, and maximum distance to neighbors. If distance weighted (k = 2), input a 1 x 2 vector with grid spacing and distance weighting factor alpha plotpar = Parameter to color the cells: Max elongation (plotpar = 0), minimum elongation (plotpar = 1), rotation (plotpar = 2), or dilatation (plotpar = 3) plotst = A flag to plot the stations (1) or not (0) cent = ncells x 2 matrix with x and y positions of cells centroids eps = 3 x 3 x ncells array with strain tensors of the cells ome = 3 x 3 x ncells array with rotation tensors of the cells pstrain = 3 x 3 x ncells array with magnitude and orientation of principal strains of the cells rotc = ncells x 3 matrix with rotation components of cells NOTE: Input/Output angles are in radians. Output azimuths are given with respect to North pos, disp, grid spacing, max. distance to neighbors, and alpha should be in meters GridStrain uses functions lscov and InfStrain Python function translated from the Matlab function GridStrain in Allmendinger et al. (2012) ''' pi = np.pi # If Delaunay if k == 0: # Indexes of triangles vertices # Use function Delaunay tri = Delaunay(pos) inds = tri.simplices # Number of cells ncells = np.size(inds, 0) # Number of stations per cell = 3 nstat = 3 # Centers of cells cent = np.zeros((ncells, 2)) for i in range(0, ncells): # Triangle vertices v1x = pos[inds[i, 0], 0] v2x = pos[inds[i, 1], 0] v3x = pos[inds[i, 2], 0] v1y = pos[inds[i, 0], 1] v2y = pos[inds[i, 1], 1] v3y = pos[inds[i, 2], 1] # Center of cell cent[i, 0] = (v1x + v2x + v3x) / 3.0 cent[i, 1] = (v1y + v2y + v3y) / 3.0 # Triangle internal angles s1 = np.sqrt((v3x - v2x)**2 + (v3y - v2y)**2) s2 = np.sqrt((v1x - v3x)**2 + (v1y - v3y)**2) s3 = np.sqrt((v2x - v1x)**2 + (v2y - v1y)**2) a1 = np.arccos((v2x-v1x)*(v3x-v1x)/(s3*s2)+\ (v2y-v1y)*(v3y-v1y)/(s3*s2)) a2 = np.arccos((v3x-v2x)*(v1x-v2x)/(s1*s3)+\ (v3y-v2y)*(v1y-v2y)/(s1*s3)) a3 = np.arccos((v2x-v3x)*(v1x-v3x)/(s1*s2)+\ (v2y-v3y)*(v1y-v3y)/(s1*s2)) # If any of the internal angles is less than # specified minimum, invalidate triangle if a1 < par or a2 < par or a3 < par: inds[i, :] = np.zeros(3) # If nearest neighbor or distance weighted else: # Construct grid xmin = min(pos[:, 0]) xmax = max(pos[:, 0]) ymin = min(pos[:, 1]) ymax = max(pos[:, 1]) cellsx = int(np.ceil((xmax - xmin) / par[0])) cellsy = int(np.ceil((ymax - ymin) / par[0])) xgrid = np.arange(xmin, (xmin + (cellsx + 1) * par[0]), par[0]) ygrid = np.arange(ymin, (ymin + (cellsy + 1) * par[0]), par[0]) XX, YY = np.meshgrid(xgrid, ygrid) # Number of cells ncells = cellsx * cellsy # Number of stations per cell (nstat) and # other parameters # If nearest neighbor if k == 1: nstat = par[1] # max neighbors sqmd = par[2]**2 # max squared distance # If distance weighted elif k == 2: nstat = np.size(pos, 0) # all stations dalpha = 2.0 * par[1] * par[1] # 2*alpha*alpha # Cells' centers cent = np.zeros((ncells, 2)) count = 0 for i in range(0, cellsy): for j in range(0, cellsx): cent[count, 0] = (XX[i, j] + XX[i, j + 1]) / 2.0 cent[count, 1] = (YY[i, j] + YY[i + 1, j]) / 2.0 count += 1 # Initialize stations indexes for cells to -1 inds = np.ones((ncells, nstat), dtype=int) * -1 # Initialize weight matrix for distance weighted wv = np.zeros((ncells, nstat * 2)) # For all cells set stations indexes for i in range(0, ncells): # Initialize sq distances to -1.0 sds = np.ones(nstat) * -1.0 # For all stations for j in range(0, np.size(pos, 0)): # Sq distance from cell center to station dx = cent[i, 0] - pos[j, 0] dy = cent[i, 1] - pos[j, 1] sd = dx**2 + dy**2 # If nearest neighbor if k == 1: # If within the max sq distance if sd <= sqmd: minsd = min(sds) mini = np.argmin(sds) # If less than max neighbors if minsd == -1.0: sds[mini] = sd inds[i, mini] = j # If max neighbors else: # If sq distance is less # than neighbors max sq distance maxsd = max(sds) maxi = np.argmax(sds) if sd < maxsd: sds[maxi] = sd inds[i, maxi] = j # If distance weighted elif k == 2: # All stations indexes inds[i, :] = np.arange(nstat) # Eq. 8.27: Weight factor weight = np.exp(-sd / dalpha) wv[i, j * 2] = weight wv[i, j * 2 + 1] = weight # Initialize arrays y = np.zeros(nstat * 2) M = np.zeros((nstat * 2, 6)) e = np.zeros((3, 3)) eps = np.zeros((3, 3, ncells)) ome = np.zeros((3, 3, ncells)) pstrain = np.zeros((3, 3, ncells)) rotc = np.zeros((ncells, 3)) # For each cell for i in range(0, ncells): # If required minimum number of stations if min(inds[i, :]) >= 0: # Eq. 8.24: Displacements column vector y # and design matrix M. X1 = North, X2 = East for j in range(0, nstat): ic = inds[i, j] y[j * 2] = disp[ic, 1] y[j * 2 + 1] = disp[ic, 0] M[j * 2, :] = [1., 0., pos[ic, 1], pos[ic, 0], 0., 0.] M[j * 2 + 1, :] = [0., 1., 0., 0., pos[ic, 1], pos[ic, 0]] # Eqs. 8.25-8.26: Find x using function lscov # If Delaunay or nearest neighbor if k == 0 or k == 1: x = lscov(M, y) # If distance weighted elif k == 2: x = lscov(M, y, wv[i, :]) # Displacement gradient tensor for j in range(0, 2): e[j, 0] = x[j * 2 + 2] e[j, 1] = x[j * 2 + 3] # Compute strain eps[:,:,i],ome[:,:,i],pstrain[:,:,i],\ rotc[i,:],_ = InfStrain(e) # Variable to plot # If maximum principal strain if plotpar == 0: vp = pstrain[0, 0, :] lcb = "emax" # If minimum principal strain elif plotpar == 1: vp = pstrain[2, 0, :] lcb = "emin" # If rotation: # For plane strain, rotation = rotc(3) elif plotpar == 2: vp = rotc[:, 2] * 180 / pi lcb = "Rotation (deg)" # If dilatation elif plotpar == 3: vp = pstrain[0, 0, :] + pstrain[1, 0, :] + pstrain[2, 0, :] lcb = "dilatation" # Make a figure fig, ax = plt.subplots() fig.set_size_inches(15.0, 7.5) # Patches and colors for cells patches = [] colors = [] # Fill cells patches and colors # If Delaunay if k == 0: for i in range(0, ncells): # If minimum number of stations if min(inds[i, :]) >= 0: xpyp = [[pos[inds[i,0],0],pos[inds[i,0],1]],\ [pos[inds[i,1],0],pos[inds[i,1],1]],\ [pos[inds[i,2],0],pos[inds[i,2],1]]] # length in km xpyp = np.divide(xpyp, 1e3) polygon = Polygon(xpyp, True) patches.append(polygon) colors.append(vp[i]) # If nearest neighbor or distance weighted if k == 1 or k == 2: count = 0 for i in range(0, cellsy): for j in range(0, cellsx): # If minimum number of stations if min(inds[count, :]) >= 0: xpyp = [[XX[i,j],YY[i,j]],[XX[i,j+1],YY[i,j+1]],\ [XX[i+1,j+1],YY[i+1,j+1]],[XX[i+1,j],YY[i+1,j]]] # length in km xpyp = np.divide(xpyp, 1e3) polygon = Polygon(xpyp, True) patches.append(polygon) colors.append(vp[count]) count += 1 # Collect cells patches pcoll = PatchCollection(patches) # Cells colors pcoll.set_array(np.array(colors)) # Color map is blue to red pcoll.set_cmap('bwr') # Positive values are red, negative are # blue and zero is white vmin = min(vp) vmax = max(vp) norm = mcolors.TwoSlopeNorm(vmin=vmin, vcenter=0.0, vmax=vmax) pcoll.set_norm(norm) # Draw cells ax.add_collection(pcoll) # Plot stations if plotst == 1: plt.plot(pos[:, 0] * 1e-3, pos[:, 1] * 1e-3, 'k.', markersize=2) # Axes plt.axis('equal') plt.xlabel('x (km)') plt.ylabel('y (km)') # Color bar with nice ticks intv = (vmax - vmin) * 0.25 ticks = [vmin, vmin + intv, vmin + 2 * intv, vmin + 3 * intv, vmax] lticks = ['{:.2e}'.format(ticks[0]),\ '{:.2e}'.format(ticks[1]),'{:.2e}'.format(ticks[2]),\ '{:.2e}'.format(ticks[3]),'{:.2e}'.format(ticks[4])] cbar = fig.colorbar(pcoll, label=lcb, ticks=ticks) cbar.ax.set_yticklabels(lticks) # Show plot plt.show() return cent, eps, ome, pstrain, rotc
def create_bus_symbol_collection(coords, buses=None, size=5, marker="o", patch_type="circle", colors=None, z=None, cmap=None, norm=None, infofunc=None, picker=False, net=None, **kwargs): infos = [] if 'height' in kwargs and 'width' in kwargs: height, width = kwargs['height'], kwargs['width'] else: height, width = size, size def figmaker(x, y, i): if patch_type == "circle": if colors: fig = Circle((x, y), size, color=colors[i], **kwargs) else: fig = Circle((x, y), size, **kwargs) elif patch_type == 'ellipse': angle = kwargs['angle'] if 'angle' in kwargs else 0 if colors: fig = Ellipse((x, y), width=width, height=height, color=colors[i], **kwargs) else: fig = Ellipse((x, y), width=width, height=height, angle=angle, **kwargs) elif patch_type == "rect": if colors: fig = Rectangle([x - width, y - height], 2 * width, 2 * height, color=colors[i], **kwargs) else: fig = Rectangle([x - width, y - height], 2 * width, 2 * height, **kwargs) elif patch_type.startswith("poly"): edges = int(patch_type[4:]) if colors: fig = RegularPolygon([x, y], numVertices=edges, radius=size, color=colors[i], **kwargs) else: fig = RegularPolygon([x, y], numVertices=edges, radius=size, **kwargs) else: logger.error( "Wrong patchtype. Please choose a correct patch type.") if infofunc: infos.append(infofunc(buses[i])) return fig patches = [ figmaker(x, y, i) for i, (x, y) in enumerate(coords) if x != np.nan ] pc = PatchCollection(patches, match_original=True, picker=picker) pc.bus_indices = np.array(buses) if cmap: pc.set_cmap(cmap) pc.set_norm(norm) if z is None and net: z = net.res_bus.vm_pu.loc[buses] else: logger.warning("z is None and no net is provided") pc.set_array(np.array(z)) pc.has_colormap = True pc.cbar_title = "Bus Voltage [pu]" pc.patch_type = patch_type pc.size = size if 'orientation' in kwargs: pc.orientation = kwargs['orientation'] if "zorder" in kwargs: pc.set_zorder(kwargs["zorder"]) pc.info = infos return pc
def plot_polygon_collection(ax, geoms, values=None, color=None, cmap=None, vmin=None, vmax=None, **kwargs): """ Plots a collection of Polygon and MultiPolygon geometries to `ax` Parameters ---------- ax : matplotlib.axes.Axes where shapes will be plotted geoms : a sequence of `N` Polygons and/or MultiPolygons (can be mixed) values : a sequence of `N` values, optional Values will be mapped to colors using vmin/vmax/cmap. They should have 1:1 correspondence with the geometries (not their components). Otherwise follows `color` / `facecolor` kwargs. edgecolor : single color or sequence of `N` colors Color for the edge of the polygons facecolor : single color or sequence of `N` colors Color to fill the polygons. Cannot be used together with `values`. color : single color or sequence of `N` colors Sets both `edgecolor` and `facecolor` **kwargs Additional keyword arguments passed to the collection Returns ------- collection : matplotlib.collections.Collection that was plotted """ from descartes.patch import PolygonPatch from matplotlib.collections import PatchCollection geoms, values = _flatten_multi_geoms(geoms, values) if None in values: values = None # PatchCollection does not accept some kwargs. if 'markersize' in kwargs: del kwargs['markersize'] # color=None overwrites specified facecolor/edgecolor with default color if color is not None: kwargs['color'] = color collection = PatchCollection([PolygonPatch(poly) for poly in geoms], **kwargs) if values is not None: collection.set_array(np.asarray(values)) collection.set_cmap(cmap) collection.set_clim(vmin, vmax) ax.add_collection(collection, autolim=True) ax.autoscale_view() return collection
def patchValMap(vals, xvec=None, yvec=None, ax=None, cMin=None, cMax=None, logScale=None, label=None, dx=1, dy=None, **kwargs): """Plot previously generated (generateVecMatrix) y map (category). Parameters ---------- vals : iterable Data values to show. xvec : dict {i:num} dict (must match vals.shape[0]) ymap : iterable vector for x axis (must match vals.shape[0]) ax : mpl.axis axis to plot, if not given a new figure is created cMin/cMax : float minimum/maximum color values logScale : bool logarithmic colour scale [min(vals)>0] label : string colorbar label ** kwargs: * circular : bool Plot in polar coordinates. """ if cMin is None: cMin = np.min(vals) if cMax is None: cMax = np.max(vals) if logScale is None: logScale = (cMin > 0.0) norm = None if logScale and cMin > 0: norm = LogNorm(vmin=cMin, vmax=cMax) else: norm = Normalize(vmin=cMin, vmax=cMax) if ax is None: ax = plt.subplots()[1] recs = [] circular = kwargs.pop('circular', False) if circular: recs = [None] * len(xvec) if dy is None: # map y values to unique ymap = {xy: ii for ii, xy in enumerate(np.unique(yvec))} xyMap = {} for i, y in enumerate(yvec): if y not in xyMap: xyMap[y] = [] xyMap[y].append(i) maxR = max(ymap.values()) # what's that for? not used dR = 1 / (len(ymap.values()) + 1) dOff = np.pi / 2 # what's that for? not used for y, xIds in xyMap.items(): r = 1. - dR * (ymap[y] + 1) # ax.plot(r * np.cos(xvec[xIds]), # r * np.sin(xvec[xIds]), 'o') # print(y, ymap[y]) for i in xIds: phi = xvec[i] x = r * np.cos(phi) # what's that for? not used y = r * np.sin(phi) dPhi = (xvec[1] - xvec[0]) recs[i] = Wedge((0., 0.), r + dR / 1.5, (phi - dPhi) * 360 / (2 * np.pi), (phi + dPhi) * 360 / (2 * np.pi), width=dR, zorder=1 + r) # if i < 5: # ax.text(x, y, str(i)) # pg.wait() else: raise ("Implementme") else: if dy is None: # map y values to unique ymap = {xy: ii for ii, xy in enumerate(np.unique(yvec))} for i in range(len(vals)): recs.append( Rectangle((xvec[i] - dx / 2, ymap[yvec[i]] - 0.5), dx, 1)) else: for i in range(len(vals)): recs.append( Rectangle((xvec[i] - dx / 2, yvec[i] - dy / 2), dx, dy)) ax.set_xlim(min(xvec) - dx / 2, max(xvec) + dx / 2) ax.set_ylim(len(ymap) - 0.5, -0.5) pp = PatchCollection(recs) # ax.clear() col = ax.add_collection(pp) pp.set_edgecolor(None) pp.set_linewidths(0.0) if circular: pp.set_edgecolor('black') pp.set_linewidths(0.1) cmap = pg.mplviewer.cmapFromName(**kwargs) if kwargs.pop('markOutside', False): cmap.set_bad('grey') cmap.set_under('darkgrey') cmap.set_over('lightgrey') cmap.set_bad('black') pp.set_cmap(cmap) pp.set_norm(norm) pp.set_array(vals) pp.set_clim(cMin, cMax) updateAxes_(ax) cbar = kwargs.pop('colorBar', True) ori = kwargs.pop('orientation', 'horizontal') if cbar in ['horizontal', 'vertical']: ori = cbar cbar = True if cbar is True: # not for cbar=1, which is really confusing! cbar = pg.mplviewer.createColorBar(col, cMin=cMin, cMax=cMax, nLevs=5, label=label, orientation=ori) elif cbar is not False: # .. cbar is an already existing cbar .. so we update its values pg.mplviewer.updateColorBar(cbar, cMin=cMin, cMax=cMax, nLevs=5, label=label) updateAxes_(ax) return ax, cbar, ymap
def factcamera(self, data, pixelcoords=None, cmap='gray', vmin=None, vmax=None, pixelset=None, pixelsetcolour='g', linewidth=None, intersectcolour='b', picker=True, ): """ Attributes ---------- data : array like with shape 1440 the data you want to plot into the pixels pixelset : boolean array with shape 1440 the pixels where pixelset is True are marked with 'pixelsetcolour' [default: None] pixelsetcolour : a matplotlib conform colour representation the colour for the pixels in 'pixelset', [default: green] pixelcoords : the coordinates for the pixels in form [x-values, y-values] if None, the package resource is used [default: None] cmap : str or matplotlib colormap instance the colormap to use for plotting the 'dataset' [default: gray] vmin : float the minimum for the colorbar, if None min(dataset[event]) is used [default: None] vmax : float the maximum for the colorbar, if None max(dataset[event]) is used [default: None] picker: bool if True then the the pixel are made clickable to show information """ self.set_aspect('equal') if picker is True: fig = self.get_figure() fig.canvas.mpl_connect("pick_event", onpick) # if the axes limit is still (0,1) assume new axes if self.get_xlim() == (0, 1) and self.get_ylim() == (0, 1): self.set_xlim(-200, 200) self.set_ylim(-200, 200) if pixelcoords is None: pixel_x, pixel_y = get_pixel_coords() else: pixel_x, pixel_y = pixelcoords if vmin is None: vmin = np.min(data) if vmax is None: vmax = np.max(data) edgecolors = np.array(1440*["k"]) if pixelset is None: pixelset = np.zeros(1440, dtype=np.bool) _pixelset = np.array(pixelset) if _pixelset.ndim == 1: if _pixelset.shape != (1440,): pixelset = np.zeros(1440, dtype=np.bool) pixelset[_pixelset] = True else: pixelset = np.array(_pixelset, dtype=np.bool) edgecolors[pixelset] = pixelsetcolour elif _pixelset.ndim == 2: for pixelset, colour in zip(_pixelset, pixelsetcolour): edgecolors[pixelset] = colour intersect = np.logical_and(_pixelset[0], _pixelset[1]) edgecolors[intersect] = intersectcolour else: raise ValueError( """pixelset needs to be one of: 1. list of pixelids 2. 1d bool array with shape (1440,) 3. 2d bool array with shape (2, 1440) """ ) patches = [] for x, y, ec in zip(pixel_x, pixel_y, edgecolors): patches.append( RegularPolygon( xy=(x, y), numVertices=6, radius=0.95*9.5/np.sqrt(3), orientation=0., # in radians ) ) if linewidth is None: linewidth = calc_linewidth(self) collection = PatchCollection(patches, picker=0) collection.set_linewidth(linewidth) collection.set_edgecolors(edgecolors) collection.set_cmap(cmap) collection.set_array(data) collection.set_clim(vmin, vmax) self.add_collection(collection) return collection
def _plot_polygon_collection(ax, geoms, values=None, color=None, cmap=None, vmin=None, vmax=None, **kwargs): """ Plots a collection of Polygon and MultiPolygon geometries to `ax` Parameters ---------- ax : matplotlib.axes.Axes where shapes will be plotted geoms : a sequence of `N` Polygons and/or MultiPolygons (can be mixed) values : a sequence of `N` values, optional Values will be mapped to colors using vmin/vmax/cmap. They should have 1:1 correspondence with the geometries (not their components). Otherwise follows `color` / `facecolor` kwargs. edgecolor : single color or sequence of `N` colors Color for the edge of the polygons facecolor : single color or sequence of `N` colors Color to fill the polygons. Cannot be used together with `values`. color : single color or sequence of `N` colors Sets both `edgecolor` and `facecolor` **kwargs Additional keyword arguments passed to the collection Returns ------- collection : matplotlib.collections.Collection that was plotted """ from matplotlib.collections import PatchCollection geoms, multiindex = _flatten_multi_geoms(geoms) if values is not None: values = np.take(values, multiindex, axis=0) # PatchCollection does not accept some kwargs. kwargs = { att: value for att, value in kwargs.items() if att not in ["markersize", "marker"] } # Add to kwargs for easier checking below. if color is not None: kwargs["color"] = color _expand_kwargs(kwargs, multiindex) collection = PatchCollection( [_PolygonPatch(poly) for poly in geoms if not poly.is_empty], **kwargs) if values is not None: collection.set_array(np.asarray(values)) collection.set_cmap(cmap) if "norm" not in kwargs: collection.set_clim(vmin, vmax) ax.add_collection(collection, autolim=True) ax.autoscale_view() return collection
def plot_polygon_collection(ax, geoms, colors_or_values, plot_values, vmin=None, vmax=None, cmap=None, edgecolor='black', alpha=0.5, linewidth=1.0, **kwargs): """ Plots a collection of Polygon and MultiPolygon geometries to `ax` Parameters ---------- ax : matplotlib.axes.Axes where shapes will be plotted geoms : a sequence of `N` Polygons and/or MultiPolygons (can be mixed) colors_or_values : a sequence of `N` values or RGBA tuples It should have 1:1 correspondence with the geometries (not their components). plot_values : bool If True, `colors_or_values` is interpreted as a list of values, and will be mapped to colors using vmin/vmax/cmap (which become required). Otherwise `colors_or_values` is interpreted as a list of colors. Returns ------- collection : matplotlib.collections.Collection that was plotted """ from descartes.patch import PolygonPatch from matplotlib.collections import PatchCollection components, component_colors_or_values = _flatten_multi_geoms( geoms, colors_or_values) # PatchCollection does not accept some kwargs. if 'markersize' in kwargs: del kwargs['markersize'] collection = PatchCollection([PolygonPatch(poly) for poly in components], linewidth=linewidth, edgecolor=edgecolor, alpha=alpha, **kwargs) if plot_values: collection.set_array(np.array(component_colors_or_values)) collection.set_cmap(cmap) collection.set_clim(vmin, vmax) else: # set_color magically sets the correct combination of facecolor and # edgecolor, based on collection type. collection.set_color(component_colors_or_values) # If the user set facecolor and/or edgecolor explicitly, the previous # call to set_color might have overridden it (remember, the 'color' may # have come from plot_series, not from the user). The user should be # able to override matplotlib's default behavior, by setting them again # after set_color. if 'facecolor' in kwargs: collection.set_facecolor(kwargs['facecolor']) if edgecolor: collection.set_edgecolor(edgecolor) ax.add_collection(collection, autolim=True) ax.autoscale_view() return collection