def __init__(self, edgecolors=None, facecolors=None, linewidths=None, antialiaseds = None, offsets = None, transOffset = identity_transform(), norm = None, # optional for ScalarMappable cmap = None, # ditto ): Collection.__init__(self) ScalarMappable.__init__(self, norm, cmap) if edgecolors is None: edgecolors =\ self._get_color(rcParams['patch.edgecolor']) if facecolors is None: facecolors = \ self._get_color(rcParams['patch.facecolor']) if linewidths is None: linewidths = ( rcParams['patch.linewidth'],) if antialiaseds is None: antialiaseds = ( rcParams['patch.antialiased'],) self._edgecolors = edgecolors self._facecolors = facecolors self._linewidths = linewidths self._antialiaseds = antialiaseds self._offsets = offsets self._transOffset = transOffset
def changed(self): tcolors = [ (tuple(rgba),) for rgba in self.to_rgba(self.cvalues)] self.tcolors = tcolors contourNum = 0 for color, collection in zip(tcolors, self.collections): collection.set_color(color) for label, cv in zip(self.cl, self.cl_cvalues): label.set_color(self.label_mappable.to_rgba(cv)) # add label colors ScalarMappable.changed(self)
def __init__(self, levels, collections, norm=None, cmap=None, labeld=None): """ See comment on labeld in the ContourLabeler class """ ScalarMappable.__init__(self, norm, cmap) self.levels = levels self.collections = collections if labeld is None: labeld = {} self.labeld = labeld
def changed(self): colors = [ (tuple(rgba),) for rgba in self.to_rgba(self.levels)] contourNum = 0 for color, collection in zip(colors, self.collections): collection.set_color(color) Ncolor = len(color) # collections could have more than 1 in principle for segNum, segment in enumerate(collection._segments): key = contourNum, segNum t = self.labeld.get(key) if t is not None: t.set_color(color[segNum%Ncolor]) contourNum += 1 ScalarMappable.changed(self)
def __init__( self, edgecolors=None, facecolors=None, linewidths=None, antialiaseds=None, offsets=None, transOffset=identity_transform(), norm=None, # optional for ScalarMappable cmap=None, # ditto ): Collection.__init__(self) ScalarMappable.__init__(self, norm, cmap) if facecolors is None: facecolors = rcParams['patch.facecolor'] if edgecolors is None: edgecolors = rcParams['patch.edgecolor'] if linewidths is None: linewidths = (rcParams['patch.linewidth'], ) if antialiaseds is None: antialiaseds = (rcParams['patch.antialiased'], ) self._facecolors = colorConverter.to_rgba_list(facecolors) if edgecolors == 'None': self._edgecolors = self._facecolors linewidths = (0, ) else: self._edgecolors = colorConverter.to_rgba_list(edgecolors) self._linewidths = linewidths self._antialiaseds = antialiaseds #self._offsets = offsets self._original_offsets = offsets self._cached_offsets = offsets self._transOffset = transOffset self._xunits = self._yunits = None self._update_cache = { '_original_offsets': None, '_xunits': None, '_yunits': None }
def __init__(self, ax, *args, **kwargs): """ Draw contour lines or filled regions, depending on whether keyword arg 'filled' is False (default) or True. The first argument of the initializer must be an axes object. The remaining arguments and keyword arguments are described in ContourSet.contour_doc. """ self.ax = ax self.filled = kwargs.get('filled', False) self.linewidths = kwargs.get('linewidths', None) self.alpha = kwargs.get('alpha', 1.0) self.origin = kwargs.get('origin', None) self.extent = kwargs.get('extent', None) cmap = kwargs.get('cmap', None) self.colors = kwargs.get('colors', None) norm = kwargs.get('norm', None) self.clip_ends = kwargs.get('clip_ends', True) self.antialiased = kwargs.get('antialiased', True) self.nchunk = kwargs.get('nchunk', 0) if self.origin is not None: assert(self.origin in ['lower', 'upper', 'image']) if self.extent is not None: assert(len(self.extent) == 4) if cmap is not None: assert(isinstance(cmap, Colormap)) if self.colors is not None and cmap is not None: raise ValueError('Either colors or cmap must be None') if self.origin == 'image': self.origin = rcParams['image.origin'] x, y, z = self._contour_args(*args) # also sets self.levels, # self.layers if self.colors is not None: cmap = ListedColormap(self.colors, N=len(self.layers)) if self.filled: self.collections = silent_list('PolyCollection') else: self.collections = silent_list('LineCollection') # label lists must be initialized here self.cl = [] self.cl_cvalues = [] kw = {'cmap': cmap} if norm is not None: kw['norm'] = norm ScalarMappable.__init__(self, **kw) # sets self.cmap; self._process_colors() if self.filled: if self.linewidths is None: self.linewidths = 0.05 # Good default for Postscript. if iterable(self.linewidths): self.linewidths = self.linewidths[0] #C = _contour.Cntr(x, y, z.filled(), z.mask()) C = _contour.Cntr(x, y, z.filled(), ma.getmaskorNone(z)) lowers = self.levels[:-1] uppers = self.levels[1:] for level, level_upper, color in zip(lowers, uppers, self.tcolors): nlist = C.trace(level, level_upper, points = 1, nchunk = self.nchunk) col = PolyCollection(nlist, linewidths = (self.linewidths,), antialiaseds = (self.antialiased,)) col.set_color(color) # sets both facecolor and edgecolor self.ax.add_collection(col) self.collections.append(col) else: tlinewidths = self._process_linewidths() #C = _contour.Cntr(x, y, z.filled(), z.mask()) C = _contour.Cntr(x, y, z.filled(), ma.getmaskorNone(z)) for level, color, width in zip(self.levels, self.tcolors, tlinewidths): nlist = C.trace(level, points = 1) col = LineCollection(nlist) col.set_color(color) col.set_linewidth(width) if level < 0.0 and self.monochrome: col.set_linestyle((0, (6.,6.)),) col.set_label(str(level)) # only for self-documentation self.ax.add_collection(col) self.collections.append(col) ## check: seems like set_xlim should also be inside if not self.ax.ishold(): self.ax.cla() self.ax.set_xlim((ma.minimum(x), ma.maximum(x))) self.ax.set_ylim((ma.minimum(y), ma.maximum(y)))
def clabel(self, *args, **kwargs): """ clabel(CS, **kwargs) - add labels to line contours in CS, where CS is a ContourSet object returned by contour. clabel(CS, V, **kwargs) - only label contours listed in V keyword arguments: * fontsize = None: as described in http://matplotlib.sf.net/fonts.html * colors = None: - a tuple of matplotlib color args (string, float, rgb, etc), different labels will be plotted in different colors in the order specified - one string color, e.g. colors = 'r' or colors = 'red', all labels will be plotted in this color - if colors == None, the color of each label matches the color of the corresponding contour * inline = True: controls whether the underlying contour is removed (inline = True) or not (False) * fmt = '%1.3f': a format string for the label """ fontsize = kwargs.get('fontsize', None) inline = kwargs.get('inline', 1) self.fmt = kwargs.get('fmt', '%1.3f') colors = kwargs.get('colors', None) if len(args) == 0: levels = self.levels indices = range(len(self.levels)) elif len(args) == 1: levlabs = list(args[0]) indices, levels = [], [] for i, lev in enumerate(self.levels): if lev in levlabs: indices.append(i) levels.append(lev) if len(levels) < len(levlabs): msg = "Specified levels " + str(levlabs) msg += "\n don't match available levels " msg += str(self.levels) raise ValueError(msg) else: raise TypeError("Illegal arguments to clabel, see help(clabel)") self.label_levels = levels self.label_indices = indices self.fp = FontProperties() if fontsize == None: font_size = int(self.fp.get_size_in_points()) else: if type(fontsize) not in [int, float, str]: raise TypeError("Font size must be an integer number.") # Can't it be floating point, as indicated in line above? else: if type(fontsize) == str: font_size = int(self.fp.get_size_in_points()) else: self.fp.set_size(fontsize) font_size = fontsize self.fslist = [font_size] * len(levels) if colors == None: self.label_mappable = self self.label_cvalues = take(self.cvalues, self.label_indices) else: cmap = ListedColormap(colors, N=len(self.label_levels)) self.label_cvalues = range(len(self.label_levels)) self.label_mappable = ScalarMappable(cmap = cmap, norm = no_norm()) #self.cl = [] # Initialized in ContourSet.__init__ #self.cl_cvalues = [] # same self.cl_xy = [] self.labels(inline) for label in self.cl: self.ax.add_artist(label) self.label_list = silent_list('Text', self.cl) return self.label_list
class ContourLabeler: '''Mixin to provide labelling capability to ContourSet''' def clabel(self, *args, **kwargs): """ clabel(CS, **kwargs) - add labels to line contours in CS, where CS is a ContourSet object returned by contour. clabel(CS, V, **kwargs) - only label contours listed in V keyword arguments: * fontsize = None: as described in http://matplotlib.sf.net/fonts.html * colors = None: - a tuple of matplotlib color args (string, float, rgb, etc), different labels will be plotted in different colors in the order specified - one string color, e.g. colors = 'r' or colors = 'red', all labels will be plotted in this color - if colors == None, the color of each label matches the color of the corresponding contour * inline = True: controls whether the underlying contour is removed (inline = True) or not (False) * fmt = '%1.3f': a format string for the label """ fontsize = kwargs.get('fontsize', None) inline = kwargs.get('inline', 1) self.fmt = kwargs.get('fmt', '%1.3f') colors = kwargs.get('colors', None) if len(args) == 0: levels = self.levels indices = range(len(self.levels)) elif len(args) == 1: levlabs = list(args[0]) indices, levels = [], [] for i, lev in enumerate(self.levels): if lev in levlabs: indices.append(i) levels.append(lev) if len(levels) < len(levlabs): msg = "Specified levels " + str(levlabs) msg += "\n don't match available levels " msg += str(self.levels) raise ValueError(msg) else: raise TypeError("Illegal arguments to clabel, see help(clabel)") self.label_levels = levels self.label_indices = indices self.fp = FontProperties() if fontsize == None: font_size = int(self.fp.get_size_in_points()) else: if type(fontsize) not in [int, float, str]: raise TypeError("Font size must be an integer number.") # Can't it be floating point, as indicated in line above? else: if type(fontsize) == str: font_size = int(self.fp.get_size_in_points()) else: self.fp.set_size(fontsize) font_size = fontsize self.fslist = [font_size] * len(levels) if colors == None: self.label_mappable = self self.label_cvalues = take(self.cvalues, self.label_indices) else: cmap = ListedColormap(colors, N=len(self.label_levels)) self.label_cvalues = range(len(self.label_levels)) self.label_mappable = ScalarMappable(cmap = cmap, norm = no_norm()) #self.cl = [] # Initialized in ContourSet.__init__ #self.cl_cvalues = [] # same self.cl_xy = [] self.labels(inline) for label in self.cl: self.ax.add_artist(label) self.label_list = silent_list('Text', self.cl) return self.label_list def print_label(self, linecontour,labelwidth): "if contours are too short, don't plot a label" lcsize = len(linecontour) if lcsize > 10 * labelwidth: return 1 xmax = amax(array(linecontour)[:,0]) xmin = amin(array(linecontour)[:,0]) ymax = amax(array(linecontour)[:,1]) ymin = amin(array(linecontour)[:,1]) lw = labelwidth if (xmax - xmin) > 1.2* lw or (ymax - ymin) > 1.2 * lw: return 1 else: return 0 def too_close(self, x,y, lw): "if there's a label already nearby, find a better place" if self.cl_xy != []: dist = [sqrt((x-loc[0]) ** 2 + (y-loc[1]) ** 2) for loc in self.cl_xy] for d in dist: if d < 1.2*lw: return 1 else: return 0 else: return 0 def get_label_coords(self, distances, XX, YY, ysize, lw): """ labels are ploted at a location with the smallest dispersion of the contour from a straight line unless there's another label nearby, in which case the second best place on the contour is picked up if there's no good place a label isplotted at the beginning of the contour """ hysize = int(ysize/2) adist = argsort(distances) for ind in adist: x, y = XX[ind][hysize], YY[ind][hysize] if self.too_close(x,y, lw): continue else: self.cl_xy.append((x,y)) return x,y, ind ind = adist[0] x, y = XX[ind][hysize], YY[ind][hysize] self.cl_xy.append((x,y)) return x,y, ind def get_label_width(self, lev, fmt, fsize): "get the width of the label in points" if is_string_like(lev): lw = (len(lev)) * fsize else: lw = (len(fmt%lev)) * fsize return lw def set_label_props(self, label, text, color): "set the label properties - color, fontsize, text" label.set_text(text) label.set_color(color) label.set_fontproperties(self.fp) label.set_clip_box(self.ax.bbox) def get_text(self, lev, fmt): "get the text of the label" if is_string_like(lev): return lev else: return fmt%lev def break_linecontour(self, linecontour, rot, labelwidth, ind): "break a contour in two contours at the location of the label" lcsize = len(linecontour) hlw = int(labelwidth/2) #length of label in screen coords ylabel = abs(hlw * sin(rot*pi/180)) xlabel = abs(hlw * cos(rot*pi/180)) trans = self.ax.transData slc = trans.seq_xy_tups(linecontour) x,y = slc[ind] xx= array(slc)[:,0].copy() yy=array(slc)[:,1].copy() #indices which are under the label inds=nonzero(((xx < x+xlabel) & (xx > x-xlabel)) & ((yy < y+ylabel) & (yy > y-ylabel))) if len(inds) >0: #if the label happens to be over the beginning of the #contour, the entire contour is removed, i.e. #indices to be removed are #inds= [0,1,2,3,305,306,307] #should rewrite this in a better way linds = nonzero(inds[1:]- inds[:-1] != 1) if inds[0] == 0 and len(linds) != 0: ii = inds[linds[0]] lc1 =linecontour[ii+1:inds[ii+1]] lc2 = [] else: lc1=linecontour[:inds[0]] lc2= linecontour[inds[-1]+1:] else: lc1=linecontour[:ind] lc2 = linecontour[ind+1:] if rot <0: new_x1, new_y1 = x-xlabel, y+ylabel new_x2, new_y2 = x+xlabel, y-ylabel else: new_x1, new_y1 = x-xlabel, y-ylabel new_x2, new_y2 = x+xlabel, y+ylabel new_x1d, new_y1d = trans.inverse_xy_tup((new_x1, new_y1)) new_x2d, new_y2d = trans.inverse_xy_tup((new_x2, new_y2)) if rot > 0: if len(lc1) > 0 and (lc1[-1][0] <= new_x1d) and (lc1[-1][1] <= new_y1d): lc1.append((new_x1d, new_y1d)) if len(lc2) > 0 and (lc2[0][0] >= new_x2d) and (lc2[0][1] >= new_y2d): lc2.insert(0, (new_x2d, new_y2d)) else: if len(lc1) > 0 and ((lc1[-1][0] <= new_x1d) and (lc1[-1][1] >= new_y1d)): lc1.append((new_x1d, new_y1d)) if len(lc2) > 0 and ((lc2[0][0] >= new_x2d) and (lc2[0][1] <= new_y2d)): lc2.insert(0, (new_x2d, new_y2d)) return [lc1,lc2] def locate_label(self, linecontour, labelwidth): """find a good place to plot a label (relatively flat part of the contour) and the angle of rotation for the text object """ nsize= len(linecontour) if labelwidth > 1: xsize = int(ceil(nsize/labelwidth)) else: xsize = 1 if xsize == 1: ysize = nsize else: ysize = labelwidth XX = resize(array(linecontour)[:,0],(xsize, ysize)) YY = resize(array(linecontour)[:,1],(xsize,ysize)) yfirst = YY[:,0] ylast = YY[:,-1] xfirst = XX[:,0] xlast = XX[:,-1] s = ( (reshape(yfirst, (xsize,1))-YY) * (reshape(xlast,(xsize,1)) - reshape(xfirst,(xsize,1))) - (reshape(xfirst,(xsize,1))-XX) * (reshape(ylast,(xsize,1)) - reshape(yfirst,(xsize,1))) ) L=sqrt((xlast-xfirst)**2+(ylast-yfirst)**2) dist = add.reduce(([(abs(s)[i]/L[i]) for i in range(xsize)]),-1) x,y,ind = self.get_label_coords(dist, XX, YY, ysize, labelwidth) angle = arctan2(ylast - yfirst, xlast - xfirst) rotation = angle[ind]*180/pi if rotation > 90: rotation = rotation -180 if rotation < -90: rotation = 180 + rotation dind = list(linecontour).index((x,y)) return x,y, rotation, dind def labels(self, inline): levels = self.label_levels fslist = self.fslist trans = self.ax.transData colors = self.label_mappable.to_rgba(self.label_cvalues) fmt = self.fmt for icon, lev, color, cvalue, fsize in zip(self.label_indices, self.label_levels, colors, self.label_cvalues, fslist): con = self.collections[icon] toremove = [] toadd = [] lw = self.get_label_width(lev, fmt, fsize) for segNum, linecontour in enumerate(con._segments): # for closed contours add one more point to # avoid division by zero if linecontour[0] == linecontour[-1]: linecontour.append(linecontour[1]) # transfer all data points to screen coordinates slc = trans.seq_xy_tups(linecontour) if self.print_label(slc,lw): x,y, rotation, ind = self.locate_label(slc, lw) # transfer the location of the label back to # data coordinates dx,dy = trans.inverse_xy_tup((x,y)) t = Text(dx, dy, rotation = rotation, horizontalalignment='center', verticalalignment='center') text = self.get_text(lev,fmt) self.set_label_props(t, text, color) self.cl.append(t) self.cl_cvalues.append(cvalue) if inline: new = self.break_linecontour(linecontour, rotation, lw, ind) toadd.extend(new) #for c in new: toadd.append(c) toremove.append(linecontour) for c in toremove: con._segments.remove(c) for c in toadd: con._segments.append(c)
def __init__(self, ax, *args, **kwargs): """ Draw contour lines or filled regions, depending on whether keyword arg 'filled' is False (default) or True. The first argument of the initializer must be an axes object. The remaining arguments and keyword arguments are described in ContourSet.contour_doc. """ self.ax = ax self.filled = kwargs.get('filled', False) self.linewidths = kwargs.get('linewidths', None) self.alpha = kwargs.get('alpha', 1.0) self.origin = kwargs.get('origin', None) self.extent = kwargs.get('extent', None) cmap = kwargs.get('cmap', None) self.colors = kwargs.get('colors', None) self.clip_ends = kwargs.get('clip_ends', True) self.antialiased = kwargs.get('antialiased', True) self.nchunk = kwargs.get('nchunk', 0) if self.origin is not None: assert(self.origin in ['lower', 'upper', 'image']) if self.extent is not None: assert(len(self.extent) == 4) if cmap is not None: assert(isinstance(cmap, Colormap)) if self.colors is not None and cmap is not None: raise ValueError('Either colors or cmap must be None') if self.origin == 'image': self.origin = rcParams['image.origin'] x, y, z = self._contour_args(*args) # also sets self.levels, # self.layers if self.colors is not None: cmap = ListedColormap(self.colors, N=len(self.layers)) if self.filled: self.collections = silent_list('PolyCollection') else: self.collections = silent_list('LineCollection') # label lists must be initialized here self.cl = [] self.cl_cvalues = [] ScalarMappable.__init__(self, cmap = cmap) # sets self.cmap; # default norm for now self._process_colors() if self.filled: if self.linewidths is None: self.linewidths = 0.05 # Good default for Postscript. if iterable(self.linewidths): self.linewidths = self.linewidths[0] #C = _contour.Cntr(x, y, z.filled(), z.mask()) C = _contour.Cntr(x, y, z.filled(), ma.getmask(z)) lowers = self.levels[:-1] uppers = self.levels[1:] for level, level_upper, color in zip(lowers, uppers, self.tcolors): nlist = C.trace(level, level_upper, points = 1, nchunk = self.nchunk) col = PolyCollection(nlist, linewidths = (self.linewidths,), antialiaseds = (self.antialiased,)) col.set_color(color) # sets both facecolor and edgecolor self.ax.add_collection(col) self.collections.append(col) else: tlinewidths = self._process_linewidths() #C = _contour.Cntr(x, y, z.filled(), z.mask()) C = _contour.Cntr(x, y, z.filled(), ma.getmask(z)) for level, color, width in zip(self.levels, self.tcolors, tlinewidths): nlist = C.trace(level, points = 1) col = LineCollection(nlist) col.set_color(color) col.set_linewidth(width) if level < 0.0 and self.monochrome: col.set_linestyle((0, (6.,6.)),) #print "setting dashed" col.set_label(str(level)) # only for self-documentation self.ax.add_collection(col) self.collections.append(col) ## check: seems like set_xlim should also be inside if not self.ax.ishold(): self.ax.cla() self.ax.set_xlim((ma.minimum(x), ma.maximum(x))) self.ax.set_ylim((ma.minimum(y), ma.maximum(y)))
def __init__(self, ax, *args, **kwargs): """ Draw contour lines or filled regions, depending on whether keyword arg 'filled' is False (default) or True. The first argument of the initializer must be an axes object. The remaining arguments and keyword arguments are described in ContourSet.contour_doc. """ self.ax = ax self.levels = kwargs.get('levels', None) self.filled = kwargs.get('filled', False) self.linewidths = kwargs.get('linewidths', None) self.alpha = kwargs.get('alpha', 1.0) self.origin = kwargs.get('origin', None) self.extent = kwargs.get('extent', None) cmap = kwargs.get('cmap', None) self.colors = kwargs.get('colors', None) norm = kwargs.get('norm', None) self.clip_ends = kwargs.get('clip_ends', None) ######## self.extend = kwargs.get('extend', 'neither') if self.clip_ends is not None: warnings.warn("'clip_ends' has been replaced by 'extend'") self.levels = self.levels[1:-1] # discard specified end levels self.extend = 'both' # regenerate end levels self.antialiased = kwargs.get('antialiased', True) self.nchunk = kwargs.get('nchunk', 0) self.locator = kwargs.get('locator', None) if self.origin is not None: assert (self.origin in ['lower', 'upper', 'image']) if self.extent is not None: assert (len(self.extent) == 4) if cmap is not None: assert (isinstance(cmap, Colormap)) if self.colors is not None and cmap is not None: raise ValueError('Either colors or cmap must be None') if self.origin == 'image': self.origin = rcParams['image.origin'] x, y, z = self._contour_args(*args) # also sets self.levels, # self.layers if self.colors is not None: cmap = ListedColormap(self.colors, N=len(self.layers)) if self.filled: self.collections = silent_list('PolyCollection') else: self.collections = silent_list('LineCollection') # label lists must be initialized here self.cl = [] self.cl_cvalues = [] kw = {'cmap': cmap} if norm is not None: kw['norm'] = norm ScalarMappable.__init__(self, **kw) # sets self.cmap; self._process_colors() if self.filled: if self.linewidths is None: self.linewidths = 0.05 # Good default for Postscript. if iterable(self.linewidths): self.linewidths = self.linewidths[0] #C = _contour.Cntr(x, y, z.filled(), z.mask()) C = _contour.Cntr(x, y, z.filled(), ma.getmaskorNone(z)) lowers = self._levels[:-1] uppers = self._levels[1:] for level, level_upper, color in zip(lowers, uppers, self.tcolors): nlist = C.trace(level, level_upper, points=0, nchunk=self.nchunk) col = PolyCollection(nlist, linewidths=(self.linewidths, ), antialiaseds=(self.antialiased, ), facecolors=color, edgecolors='None') self.ax.add_collection(col) self.collections.append(col) else: tlinewidths = self._process_linewidths() self.tlinewidths = tlinewidths #C = _contour.Cntr(x, y, z.filled(), z.mask()) C = _contour.Cntr(x, y, z.filled(), ma.getmaskorNone(z)) for level, color, width in zip(self.levels, self.tcolors, tlinewidths): nlist = C.trace(level, points=0) col = LineCollection(nlist, colors=color, linewidths=width) if level < 0.0 and self.monochrome: col.set_linestyle( (0, rcParams['contour.negative_linestyle'])) col.set_label(str(level)) # only for self-documentation self.ax.add_collection(col) self.collections.append(col) x0 = ma.minimum(x) x1 = ma.maximum(x) y0 = ma.minimum(y) y1 = ma.maximum(y) self.ax.update_datalim([(x0, y0), (x1, y1)]) self.ax.set_xlim((x0, x1)) self.ax.set_ylim((y0, y1))
def clabel(self, *args, **kwargs): """ clabel(CS, **kwargs) - add labels to line contours in CS, where CS is a ContourSet object returned by contour. clabel(CS, V, **kwargs) - only label contours listed in V keyword arguments: * fontsize = None: as described in http://matplotlib.sf.net/fonts.html * colors = None: - a tuple of matplotlib color args (string, float, rgb, etc), different labels will be plotted in different colors in the order specified - one string color, e.g. colors = 'r' or colors = 'red', all labels will be plotted in this color - if colors == None, the color of each label matches the color of the corresponding contour * inline = True: controls whether the underlying contour is removed (inline = True) or not (False) * fmt = '%1.3f': a format string for the label """ fontsize = kwargs.get('fontsize', None) inline = kwargs.get('inline', 1) self.fmt = kwargs.get('fmt', '%1.3f') colors = kwargs.get('colors', None) if len(args) == 0: levels = self.levels indices = range(len(self.levels)) elif len(args) == 1: levlabs = list(args[0]) indices, levels = [], [] for i, lev in enumerate(self.levels): if lev in levlabs: indices.append(i) levels.append(lev) if len(levels) < len(levlabs): msg = "Specified levels " + str(levlabs) msg += "\n don't match available levels " msg += str(self.levels) raise ValueError(msg) else: raise TypeError("Illegal arguments to clabel, see help(clabel)") self.label_levels = levels self.label_indices = indices self.fp = FontProperties() if fontsize == None: font_size = int(self.fp.get_size_in_points()) else: if type(fontsize) not in [int, float, str]: raise TypeError("Font size must be an integer number.") # Can't it be floating point, as indicated in line above? else: if type(fontsize) == str: font_size = int(self.fp.get_size_in_points()) else: self.fp.set_size(fontsize) font_size = fontsize self.fslist = [font_size] * len(levels) if colors == None: self.label_mappable = self self.label_cvalues = take(self.cvalues, self.label_indices) else: cmap = ListedColormap(colors, N=len(self.label_levels)) self.label_cvalues = range(len(self.label_levels)) self.label_mappable = ScalarMappable(cmap=cmap, norm=no_norm()) #self.cl = [] # Initialized in ContourSet.__init__ #self.cl_cvalues = [] # same self.cl_xy = [] self.labels(inline) for label in self.cl: self.ax.add_artist(label) self.label_list = silent_list('Text', self.cl) return self.label_list
class ContourLabeler: '''Mixin to provide labelling capability to ContourSet''' def clabel(self, *args, **kwargs): """ clabel(CS, **kwargs) - add labels to line contours in CS, where CS is a ContourSet object returned by contour. clabel(CS, V, **kwargs) - only label contours listed in V keyword arguments: * fontsize = None: as described in http://matplotlib.sf.net/fonts.html * colors = None: - a tuple of matplotlib color args (string, float, rgb, etc), different labels will be plotted in different colors in the order specified - one string color, e.g. colors = 'r' or colors = 'red', all labels will be plotted in this color - if colors == None, the color of each label matches the color of the corresponding contour * inline = True: controls whether the underlying contour is removed (inline = True) or not (False) * fmt = '%1.3f': a format string for the label """ fontsize = kwargs.get('fontsize', None) inline = kwargs.get('inline', 1) self.fmt = kwargs.get('fmt', '%1.3f') colors = kwargs.get('colors', None) if len(args) == 0: levels = self.levels indices = range(len(self.levels)) elif len(args) == 1: levlabs = list(args[0]) indices, levels = [], [] for i, lev in enumerate(self.levels): if lev in levlabs: indices.append(i) levels.append(lev) if len(levels) < len(levlabs): msg = "Specified levels " + str(levlabs) msg += "\n don't match available levels " msg += str(self.levels) raise ValueError(msg) else: raise TypeError("Illegal arguments to clabel, see help(clabel)") self.label_levels = levels self.label_indices = indices self.fp = FontProperties() if fontsize == None: font_size = int(self.fp.get_size_in_points()) else: if type(fontsize) not in [int, float, str]: raise TypeError("Font size must be an integer number.") # Can't it be floating point, as indicated in line above? else: if type(fontsize) == str: font_size = int(self.fp.get_size_in_points()) else: self.fp.set_size(fontsize) font_size = fontsize self.fslist = [font_size] * len(levels) if colors == None: self.label_mappable = self self.label_cvalues = take(self.cvalues, self.label_indices) else: cmap = ListedColormap(colors, N=len(self.label_levels)) self.label_cvalues = range(len(self.label_levels)) self.label_mappable = ScalarMappable(cmap=cmap, norm=no_norm()) #self.cl = [] # Initialized in ContourSet.__init__ #self.cl_cvalues = [] # same self.cl_xy = [] self.labels(inline) for label in self.cl: self.ax.add_artist(label) self.label_list = silent_list('Text', self.cl) return self.label_list def print_label(self, linecontour, labelwidth): "if contours are too short, don't plot a label" lcsize = len(linecontour) if lcsize > 10 * labelwidth: return 1 xmax = amax(array(linecontour)[:, 0]) xmin = amin(array(linecontour)[:, 0]) ymax = amax(array(linecontour)[:, 1]) ymin = amin(array(linecontour)[:, 1]) lw = labelwidth if (xmax - xmin) > 1.2 * lw or (ymax - ymin) > 1.2 * lw: return 1 else: return 0 def too_close(self, x, y, lw): "if there's a label already nearby, find a better place" if self.cl_xy != []: dist = [ sqrt((x - loc[0])**2 + (y - loc[1])**2) for loc in self.cl_xy ] for d in dist: if d < 1.2 * lw: return 1 else: return 0 else: return 0 def get_label_coords(self, distances, XX, YY, ysize, lw): """ labels are ploted at a location with the smallest dispersion of the contour from a straight line unless there's another label nearby, in which case the second best place on the contour is picked up if there's no good place a label isplotted at the beginning of the contour """ hysize = int(ysize / 2) adist = argsort(distances) for ind in adist: x, y = XX[ind][hysize], YY[ind][hysize] if self.too_close(x, y, lw): continue else: self.cl_xy.append((x, y)) return x, y, ind ind = adist[0] x, y = XX[ind][hysize], YY[ind][hysize] self.cl_xy.append((x, y)) return x, y, ind def get_label_width(self, lev, fmt, fsize): "get the width of the label in points" if is_string_like(lev): lw = (len(lev)) * fsize else: lw = (len(fmt % lev)) * fsize return lw def set_label_props(self, label, text, color): "set the label properties - color, fontsize, text" label.set_text(text) label.set_color(color) label.set_fontproperties(self.fp) label.set_clip_box(self.ax.bbox) def get_text(self, lev, fmt): "get the text of the label" if is_string_like(lev): return lev else: return fmt % lev def break_linecontour(self, linecontour, rot, labelwidth, ind): "break a contour in two contours at the location of the label" lcsize = len(linecontour) hlw = int(labelwidth / 2) #length of label in screen coords ylabel = abs(hlw * sin(rot * pi / 180)) xlabel = abs(hlw * cos(rot * pi / 180)) trans = self.ax.transData slc = trans.seq_xy_tups(linecontour) x, y = slc[ind] xx = asarray(slc)[:, 0].copy() yy = asarray(slc)[:, 1].copy() #indices which are under the label inds = nonzero(((xx < x + xlabel) & (xx > x - xlabel)) & ((yy < y + ylabel) & (yy > y - ylabel))) if len(inds) > 0: #if the label happens to be over the beginning of the #contour, the entire contour is removed, i.e. #indices to be removed are #inds= [0,1,2,3,305,306,307] #should rewrite this in a better way linds = nonzero(inds[1:] - inds[:-1] != 1) if inds[0] == 0 and len(linds) != 0: ii = inds[linds[0]] lc1 = linecontour[ii + 1:inds[ii + 1]] lc2 = [] else: lc1 = linecontour[:inds[0]] lc2 = linecontour[inds[-1] + 1:] else: lc1 = linecontour[:ind] lc2 = linecontour[ind + 1:] if rot < 0: new_x1, new_y1 = x - xlabel, y + ylabel new_x2, new_y2 = x + xlabel, y - ylabel else: new_x1, new_y1 = x - xlabel, y - ylabel new_x2, new_y2 = x + xlabel, y + ylabel new_x1d, new_y1d = trans.inverse_xy_tup((new_x1, new_y1)) new_x2d, new_y2d = trans.inverse_xy_tup((new_x2, new_y2)) new_xy1 = array(((new_x1d, new_y1d), )) new_xy2 = array(((new_x2d, new_y2d), )) if rot > 0: if (len(lc1) > 0 and (lc1[-1][0] <= new_x1d) and (lc1[-1][1] <= new_y1d)): lc1 = concatenate((lc1, new_xy1)) #lc1.append((new_x1d, new_y1d)) if (len(lc2) > 0 and (lc2[0][0] >= new_x2d) and (lc2[0][1] >= new_y2d)): lc2 = concatenate((new_xy2, lc2)) #lc2.insert(0, (new_x2d, new_y2d)) else: if (len(lc1) > 0 and ((lc1[-1][0] <= new_x1d) and (lc1[-1][1] >= new_y1d))): lc1 = concatenate((lc1, new_xy1)) #lc1.append((new_x1d, new_y1d)) if (len(lc2) > 0 and ((lc2[0][0] >= new_x2d) and (lc2[0][1] <= new_y2d))): lc2 = concatenate((new_xy2, lc2)) #lc2.insert(0, (new_x2d, new_y2d)) return [lc1, lc2] def locate_label(self, linecontour, labelwidth): """find a good place to plot a label (relatively flat part of the contour) and the angle of rotation for the text object """ nsize = len(linecontour) if labelwidth > 1: xsize = int(ceil(nsize / labelwidth)) else: xsize = 1 if xsize == 1: ysize = nsize else: ysize = labelwidth XX = resize(asarray(linecontour)[:, 0], (xsize, ysize)) YY = resize(asarray(linecontour)[:, 1], (xsize, ysize)) yfirst = YY[:, 0] ylast = YY[:, -1] xfirst = XX[:, 0] xlast = XX[:, -1] s = ((reshape(yfirst, (xsize, 1)) - YY) * (reshape(xlast, (xsize, 1)) - reshape(xfirst, (xsize, 1))) - (reshape(xfirst, (xsize, 1)) - XX) * (reshape(ylast, (xsize, 1)) - reshape(yfirst, (xsize, 1)))) L = sqrt((xlast - xfirst)**2 + (ylast - yfirst)**2) dist = add.reduce(([(abs(s)[i] / L[i]) for i in range(xsize)]), -1) x, y, ind = self.get_label_coords(dist, XX, YY, ysize, labelwidth) #print 'ind, x, y', ind, x, y angle = arctan2(ylast - yfirst, xlast - xfirst) rotation = angle[ind] * 180 / pi if rotation > 90: rotation = rotation - 180 if rotation < -90: rotation = 180 + rotation # There must be a more efficient way... lc = [tuple(l) for l in linecontour] dind = lc.index((x, y)) #print 'dind', dind #dind = list(linecontour).index((x,y)) return x, y, rotation, dind def labels(self, inline): levels = self.label_levels fslist = self.fslist trans = self.ax.transData colors = self.label_mappable.to_rgba(self.label_cvalues) fmt = self.fmt for icon, lev, color, cvalue, fsize in zip(self.label_indices, self.label_levels, colors, self.label_cvalues, fslist): con = self.collections[icon] lw = self.get_label_width(lev, fmt, fsize) additions = [] for segNum, linecontour in enumerate(con._segments): # for closed contours add one more point to # avoid division by zero if all(linecontour[0] == linecontour[-1]): linecontour = concatenate( (linecontour, linecontour[1][newaxis, :])) #linecontour.append(linecontour[1]) # transfer all data points to screen coordinates slc = trans.seq_xy_tups(linecontour) if self.print_label(slc, lw): x, y, rotation, ind = self.locate_label(slc, lw) # transfer the location of the label back to # data coordinates dx, dy = trans.inverse_xy_tup((x, y)) t = Text(dx, dy, rotation=rotation, horizontalalignment='center', verticalalignment='center') text = self.get_text(lev, fmt) self.set_label_props(t, text, color) self.cl.append(t) self.cl_cvalues.append(cvalue) if inline: new = self.break_linecontour(linecontour, rotation, lw, ind) con._segments[segNum] = new[0] additions.append(new[1]) con._segments.extend(additions)
def __init__( self, segments, # Can be None. linewidths=None, colors=None, antialiaseds=None, linestyle='solid', offsets=None, transOffset=None, #identity_transform(), norm=None, cmap=None, ): """ segments is a sequence of ( line0, line1, line2), where linen = (x0, y0), (x1, y1), ... (xm, ym), or the equivalent numerix array with two columns. Each line can be a different length. colors must be a tuple of RGBA tuples (eg arbitrary color strings, etc, not allowed). antialiaseds must be a sequence of ones or zeros linestyles is a string or dash tuple. Legal string values are solid|dashed|dashdot|dotted. The dash tuple is (offset, onoffseq) where onoffseq is an even length tuple of on and off ink in points. If linewidths, colors, or antialiaseds is None, they default to their rc params setting, in sequence form. If offsets and transOffset are not None, then offsets are transformed by transOffset and applied after the segments have been transformed to display coordinates. If offsets is not None but transOffset is None, then the offsets are added to the segments before any transformation. In this case, a single offset can be specified as offsets=(xo,yo), and this value will be added cumulatively to each successive segment, so as to produce a set of successively offset curves. norm = None, # optional for ScalarMappable cmap = None, # ditto The use of ScalarMappable is optional. If the ScalarMappable matrix _A is not None (ie a call to set_array has been made), at draw time a call to scalar mappable will be made to set the colors. """ Collection.__init__(self) ScalarMappable.__init__(self, norm, cmap) if linewidths is None: linewidths = (rcParams['lines.linewidth'], ) if colors is None: colors = (rcParams['lines.color'], ) if antialiaseds is None: antialiaseds = (rcParams['lines.antialiased'], ) self._colors = colorConverter.to_rgba_list(colors) self._aa = antialiaseds self._lw = linewidths self.set_linestyle(linestyle) self._uniform_offsets = None if offsets is not None: offsets = asarray(offsets) if len(offsets.shape) == 1: offsets = offsets[newaxis, :] # Make it Nx2. if transOffset is None: if offsets is not None: self._uniform_offsets = offsets offsets = None transOffset = identity_transform() self._offsets = offsets self._transOffset = transOffset self.set_segments(segments)
def __init__(self, ax, *args, **kwargs): """ Draw contour lines or filled regions, depending on whether keyword arg 'filled' is False (default) or True. The first argument of the initializer must be an axes object. The remaining arguments and keyword arguments are described in ContourSet.contour_doc. """ self.ax = ax self.levels = kwargs.get('levels', None) self.filled = kwargs.get('filled', False) self.linewidths = kwargs.get('linewidths', None) self.alpha = kwargs.get('alpha', 1.0) self.origin = kwargs.get('origin', None) self.extent = kwargs.get('extent', None) cmap = kwargs.get('cmap', None) self.colors = kwargs.get('colors', None) norm = kwargs.get('norm', None) self.clip_ends = kwargs.get('clip_ends', None) ######## self.extend = kwargs.get('extend', 'neither') if self.clip_ends is not None: warnings.warn("'clip_ends' has been replaced by 'extend'") self.levels = self.levels[1:-1] # discard specified end levels self.extend = 'both' # regenerate end levels self.antialiased = kwargs.get('antialiased', True) self.nchunk = kwargs.get('nchunk', 0) self.locator = kwargs.get('locator', None) if self.origin is not None: assert(self.origin in ['lower', 'upper', 'image']) if self.extent is not None: assert(len(self.extent) == 4) if cmap is not None: assert(isinstance(cmap, Colormap)) if self.colors is not None and cmap is not None: raise ValueError('Either colors or cmap must be None') if self.origin == 'image': self.origin = rcParams['image.origin'] x, y, z = self._contour_args(*args) # also sets self.levels, # self.layers if self.colors is not None: cmap = ListedColormap(self.colors, N=len(self.layers)) if self.filled: self.collections = silent_list('PolyCollection') else: self.collections = silent_list('LineCollection') # label lists must be initialized here self.cl = [] self.cl_cvalues = [] kw = {'cmap': cmap} if norm is not None: kw['norm'] = norm ScalarMappable.__init__(self, **kw) # sets self.cmap; self._process_colors() if self.filled: if self.linewidths is None: self.linewidths = 0.05 # Good default for Postscript. if iterable(self.linewidths): self.linewidths = self.linewidths[0] #C = _contour.Cntr(x, y, z.filled(), z.mask()) C = _contour.Cntr(x, y, z.filled(), ma.getmaskorNone(z)) lowers = self._levels[:-1] uppers = self._levels[1:] for level, level_upper, color in zip(lowers, uppers, self.tcolors): nlist = C.trace(level, level_upper, points = 0, nchunk = self.nchunk) col = PolyCollection(nlist, linewidths = (self.linewidths,), antialiaseds = (self.antialiased,), facecolors= color, edgecolors= 'None') self.ax.add_collection(col) self.collections.append(col) else: tlinewidths = self._process_linewidths() self.tlinewidths = tlinewidths #C = _contour.Cntr(x, y, z.filled(), z.mask()) C = _contour.Cntr(x, y, z.filled(), ma.getmaskorNone(z)) for level, color, width in zip(self.levels, self.tcolors, tlinewidths): nlist = C.trace(level, points = 0) col = LineCollection(nlist, colors = color, linewidths = width) if level < 0.0 and self.monochrome: col.set_linestyle((0, rcParams['contour.negative_linestyle'])) col.set_label(str(level)) # only for self-documentation self.ax.add_collection(col) self.collections.append(col) x0 = ma.minimum(x) x1 = ma.maximum(x) y0 = ma.minimum(y) y1 = ma.maximum(y) self.ax.update_datalim([(x0,y0), (x1,y1)]) self.ax.set_xlim((x0, x1)) self.ax.set_ylim((y0, y1))