1''' 2Colorbar toolkit with two classes and a function: 3 4 :class:`ColorbarBase` 5 the base class with full colorbar drawing functionality. 6 It can be used as-is to make a colorbar for a given colormap; 7 a mappable object (e.g., image) is not needed. 8 9 :class:`Colorbar` 10 the derived class for use with images or contour plots. 11 12 :func:`make_axes` 13 a function for resizing an axes and adding a second axes 14 suitable for a colorbar 15 16The :meth:`~matplotlib.figure.Figure.colorbar` method uses :func:`make_axes` 17and :class:`Colorbar`; the :func:`~matplotlib.pyplot.colorbar` function 18is a thin wrapper over :meth:`~matplotlib.figure.Figure.colorbar`. 19 20''' 21from __future__ import (absolute_import, division, print_function, 22 unicode_literals) 23 24import six 25from six.moves import xrange, zip 26 27import warnings 28 29import numpy as np 30 31import matplotlib as mpl 32import matplotlib.artist as martist 33import matplotlib.cbook as cbook 34import matplotlib.collections as collections 35import matplotlib.colors as colors 36import matplotlib.contour as contour 37import matplotlib.cm as cm 38import matplotlib.gridspec as gridspec 39import matplotlib.patches as mpatches 40import matplotlib.path as mpath 41import matplotlib.ticker as ticker 42import matplotlib.transforms as mtransforms 43import matplotlib._layoutbox as layoutbox 44import matplotlib._constrained_layout as constrained_layout 45from matplotlib import docstring 46 47make_axes_kw_doc = ''' 48 49 ============= ==================================================== 50 Property Description 51 ============= ==================================================== 52 *orientation* vertical or horizontal 53 *fraction* 0.15; fraction of original axes to use for colorbar 54 *pad* 0.05 if vertical, 0.15 if horizontal; fraction 55 of original axes between colorbar and new image axes 56 *shrink* 1.0; fraction by which to multiply the size of the colorbar 57 *aspect* 20; ratio of long to short dimensions 58 *anchor* (0.0, 0.5) if vertical; (0.5, 1.0) if horizontal; 59 the anchor point of the colorbar axes 60 *panchor* (1.0, 0.5) if vertical; (0.5, 0.0) if horizontal; 61 the anchor point of the colorbar parent axes. If 62 False, the parent axes' anchor will be unchanged 63 ============= ==================================================== 64 65''' 66 67colormap_kw_doc = ''' 68 69 ============ ==================================================== 70 Property Description 71 ============ ==================================================== 72 *extend* [ 'neither' | 'both' | 'min' | 'max' ] 73 If not 'neither', make pointed end(s) for out-of- 74 range values. These are set for a given colormap 75 using the colormap set_under and set_over methods. 76 *extendfrac* [ *None* | 'auto' | length | lengths ] 77 If set to *None*, both the minimum and maximum 78 triangular colorbar extensions with have a length of 79 5% of the interior colorbar length (this is the 80 default setting). If set to 'auto', makes the 81 triangular colorbar extensions the same lengths as 82 the interior boxes (when *spacing* is set to 83 'uniform') or the same lengths as the respective 84 adjacent interior boxes (when *spacing* is set to 85 'proportional'). If a scalar, indicates the length 86 of both the minimum and maximum triangular colorbar 87 extensions as a fraction of the interior colorbar 88 length. A two-element sequence of fractions may also 89 be given, indicating the lengths of the minimum and 90 maximum colorbar extensions respectively as a 91 fraction of the interior colorbar length. 92 *extendrect* bool 93 If *False* the minimum and maximum colorbar extensions 94 will be triangular (the default). If *True* the 95 extensions will be rectangular. 96 *spacing* [ 'uniform' | 'proportional' ] 97 Uniform spacing gives each discrete color the same 98 space; proportional makes the space proportional to 99 the data interval. 100 *ticks* [ None | list of ticks | Locator object ] 101 If None, ticks are determined automatically from the 102 input. 103 *format* [ None | format string | Formatter object ] 104 If None, the 105 :class:`~matplotlib.ticker.ScalarFormatter` is used. 106 If a format string is given, e.g., '%.3f', that is 107 used. An alternative 108 :class:`~matplotlib.ticker.Formatter` object may be 109 given instead. 110 *drawedges* bool 111 Whether to draw lines at color boundaries. 112 ============ ==================================================== 113 114 The following will probably be useful only in the context of 115 indexed colors (that is, when the mappable has norm=NoNorm()), 116 or other unusual circumstances. 117 118 ============ =================================================== 119 Property Description 120 ============ =================================================== 121 *boundaries* None or a sequence 122 *values* None or a sequence which must be of length 1 less 123 than the sequence of *boundaries*. For each region 124 delimited by adjacent entries in *boundaries*, the 125 color mapped to the corresponding value in values 126 will be used. 127 ============ =================================================== 128 129''' 130 131colorbar_doc = ''' 132 133Add a colorbar to a plot. 134 135Function signatures for the :mod:`~matplotlib.pyplot` interface; all 136but the first are also method signatures for the 137:meth:`~matplotlib.figure.Figure.colorbar` method:: 138 139 colorbar(**kwargs) 140 colorbar(mappable, **kwargs) 141 colorbar(mappable, cax=cax, **kwargs) 142 colorbar(mappable, ax=ax, **kwargs) 143 144Parameters 145---------- 146mappable : 147 The :class:`~matplotlib.image.Image`, 148 :class:`~matplotlib.contour.ContourSet`, etc. to 149 which the colorbar applies; this argument is mandatory for the Figure 150 :meth:`~matplotlib.figure.Figure.colorbar` method but optional for the 151 pyplot :func:`~matplotlib.pyplot.colorbar` function, which sets the 152 default to the current image. 153 154cax : :class:`~matplotlib.axes.Axes` object, optional 155 Axes into which the colorbar will be drawn. 156 157ax : :class:`~matplotlib.axes.Axes`, list of Axes, optional 158 Parent axes from which space for a new colorbar axes will be stolen. 159 If a list of axes is given they will all be resized to make room for the 160 colorbar axes. 161 162use_gridspec : bool, optional 163 If *cax* is ``None``, a new *cax* is created as an instance of 164 Axes. If *ax* is an instance of Subplot and *use_gridspec* is ``True``, 165 *cax* is created as an instance of Subplot using the 166 grid_spec module. 167 168 169Returns 170------- 171:class:`~matplotlib.colorbar.Colorbar` instance 172 See also its base class, :class:`~matplotlib.colorbar.ColorbarBase`. 173 Call the :meth:`~matplotlib.colorbar.ColorbarBase.set_label` method 174 to label the colorbar. 175 176Notes 177----- 178Additional keyword arguments are of two kinds: 179 180 axes properties: 181%s 182 colorbar properties: 183%s 184 185If *mappable* is a :class:`~matplotlib.contours.ContourSet`, its *extend* 186kwarg is included automatically. 187 188The *shrink* kwarg provides a simple way to scale the colorbar with respect 189to the axes. Note that if *cax* is specified it determines the size of the 190colorbar and *shrink* and *aspect* kwargs are ignored. 191 192For more precise control, you can manually specify the positions of 193the axes objects in which the mappable and the colorbar are drawn. In 194this case, do not use any of the axes properties kwargs. 195 196It is known that some vector graphics viewer (svg and pdf) renders white gaps 197between segments of the colorbar. This is due to bugs in the viewers not 198matplotlib. As a workaround the colorbar can be rendered with overlapping 199segments:: 200 201 cbar = colorbar() 202 cbar.solids.set_edgecolor("face") 203 draw() 204 205However this has negative consequences in other circumstances. Particularly 206with semi transparent images (alpha < 1) and colorbar extensions and is not 207enabled by default see (issue #1188). 208 209''' % (make_axes_kw_doc, colormap_kw_doc) 210 211docstring.interpd.update(colorbar_doc=colorbar_doc) 212 213 214def _set_ticks_on_axis_warn(*args, **kw): 215 # a top level function which gets put in at the axes' 216 # set_xticks set_yticks by _patch_ax 217 warnings.warn("Use the colorbar set_ticks() method instead.") 218 219 220class ColorbarBase(cm.ScalarMappable): 221 ''' 222 Draw a colorbar in an existing axes. 223 224 This is a base class for the :class:`Colorbar` class, which is the 225 basis for the :func:`~matplotlib.pyplot.colorbar` function and the 226 :meth:`~matplotlib.figure.Figure.colorbar` method, which are the 227 usual ways of creating a colorbar. 228 229 It is also useful by itself for showing a colormap. If the *cmap* 230 kwarg is given but *boundaries* and *values* are left as None, 231 then the colormap will be displayed on a 0-1 scale. To show the 232 under- and over-value colors, specify the *norm* as:: 233 234 colors.Normalize(clip=False) 235 236 To show the colors versus index instead of on the 0-1 scale, 237 use:: 238 239 norm=colors.NoNorm. 240 241 Useful public methods are :meth:`set_label` and :meth:`add_lines`. 242 243 Attributes 244 ---------- 245 ax : Axes 246 The `Axes` instance in which the colorbar is drawn. 247 248 lines : list 249 A list of `LineCollection` if lines were drawn, otherwise 250 an empty list. 251 252 dividers : LineCollection 253 A LineCollection if *drawedges* is ``True``, otherwise ``None``. 254 ''' 255 _slice_dict = {'neither': slice(0, None), 256 'both': slice(1, -1), 257 'min': slice(1, None), 258 'max': slice(0, -1)} 259 260 n_rasterize = 50 # rasterize solids if number of colors >= n_rasterize 261 262 def __init__(self, ax, cmap=None, 263 norm=None, 264 alpha=None, 265 values=None, 266 boundaries=None, 267 orientation='vertical', 268 ticklocation='auto', 269 extend='neither', 270 spacing='uniform', # uniform or proportional 271 ticks=None, 272 format=None, 273 drawedges=False, 274 filled=True, 275 extendfrac=None, 276 extendrect=False, 277 label='', 278 ): 279 #: The axes that this colorbar lives in. 280 self.ax = ax 281 self._patch_ax() 282 if cmap is None: 283 cmap = cm.get_cmap() 284 if norm is None: 285 norm = colors.Normalize() 286 self.alpha = alpha 287 cm.ScalarMappable.__init__(self, cmap=cmap, norm=norm) 288 self.values = values 289 self.boundaries = boundaries 290 self.extend = extend 291 self._inside = self._slice_dict[extend] 292 self.spacing = spacing 293 self.orientation = orientation 294 self.drawedges = drawedges 295 self.filled = filled 296 self.extendfrac = extendfrac 297 self.extendrect = extendrect 298 self.solids = None 299 self.lines = list() 300 self.outline = None 301 self.patch = None 302 self.dividers = None 303 304 if ticklocation == 'auto': 305 ticklocation = 'bottom' if orientation == 'horizontal' else 'right' 306 self.ticklocation = ticklocation 307 308 self.set_label(label) 309 if cbook.iterable(ticks): 310 self.locator = ticker.FixedLocator(ticks, nbins=len(ticks)) 311 else: 312 self.locator = ticks # Handle default in _ticker() 313 if format is None: 314 if isinstance(self.norm, colors.LogNorm): 315 self.formatter = ticker.LogFormatterSciNotation() 316 elif isinstance(self.norm, colors.SymLogNorm): 317 self.formatter = ticker.LogFormatterSciNotation( 318 linthresh=self.norm.linthresh) 319 else: 320 self.formatter = ticker.ScalarFormatter() 321 elif isinstance(format, six.string_types): 322 self.formatter = ticker.FormatStrFormatter(format) 323 else: 324 self.formatter = format # Assume it is a Formatter 325 # The rest is in a method so we can recalculate when clim changes. 326 self.config_axis() 327 self.draw_all() 328 329 def _extend_lower(self): 330 """Returns whether the lower limit is open ended.""" 331 return self.extend in ('both', 'min') 332 333 def _extend_upper(self): 334 """Returns whether the uper limit is open ended.""" 335 return self.extend in ('both', 'max') 336 337 def _patch_ax(self): 338 # bind some methods to the axes to warn users 339 # against using those methods. 340 self.ax.set_xticks = _set_ticks_on_axis_warn 341 self.ax.set_yticks = _set_ticks_on_axis_warn 342 343 def draw_all(self): 344 ''' 345 Calculate any free parameters based on the current cmap and norm, 346 and do all the drawing. 347 ''' 348 349 self._process_values() 350 self._find_range() 351 X, Y = self._mesh() 352 C = self._values[:, np.newaxis] 353 self._config_axes(X, Y) 354 if self.filled: 355 self._add_solids(X, Y, C) 356 357 def config_axis(self): 358 ax = self.ax 359 if self.orientation == 'vertical': 360 ax.xaxis.set_ticks([]) 361 # location is either one of 'bottom' or 'top' 362 ax.yaxis.set_label_position(self.ticklocation) 363 ax.yaxis.set_ticks_position(self.ticklocation) 364 else: 365 ax.yaxis.set_ticks([]) 366 # location is either one of 'left' or 'right' 367 ax.xaxis.set_label_position(self.ticklocation) 368 ax.xaxis.set_ticks_position(self.ticklocation) 369 370 self._set_label() 371 372 def update_ticks(self): 373 """ 374 Force the update of the ticks and ticklabels. This must be 375 called whenever the tick locator and/or tick formatter changes. 376 """ 377 ax = self.ax 378 ticks, ticklabels, offset_string = self._ticker() 379 if self.orientation == 'vertical': 380 ax.yaxis.set_ticks(ticks) 381 ax.set_yticklabels(ticklabels) 382 ax.yaxis.get_major_formatter().set_offset_string(offset_string) 383 384 else: 385 ax.xaxis.set_ticks(ticks) 386 ax.set_xticklabels(ticklabels) 387 ax.xaxis.get_major_formatter().set_offset_string(offset_string) 388 389 def set_ticks(self, ticks, update_ticks=True): 390 """ 391 Set tick locations. 392 393 Parameters 394 ---------- 395 ticks : {None, sequence, :class:`~matplotlib.ticker.Locator` instance} 396 If None, a default Locator will be used. 397 398 update_ticks : {True, False}, optional 399 If True, tick locations are updated immediately. If False, 400 use :meth:`update_ticks` to manually update the ticks. 401 402 """ 403 if cbook.iterable(ticks): 404 self.locator = ticker.FixedLocator(ticks, nbins=len(ticks)) 405 else: 406 self.locator = ticks 407 408 if update_ticks: 409 self.update_ticks() 410 self.stale = True 411 412 def get_ticks(self, minor=False): 413 """Return the x ticks as a list of locations""" 414 return self._tick_data_values 415 416 def set_ticklabels(self, ticklabels, update_ticks=True): 417 """ 418 set tick labels. Tick labels are updated immediately unless 419 update_ticks is *False*. To manually update the ticks, call 420 *update_ticks* method explicitly. 421 """ 422 if isinstance(self.locator, ticker.FixedLocator): 423 self.formatter = ticker.FixedFormatter(ticklabels) 424 if update_ticks: 425 self.update_ticks() 426 else: 427 warnings.warn("set_ticks() must have been called.") 428 self.stale = True 429 430 def _config_axes(self, X, Y): 431 ''' 432 Make an axes patch and outline. 433 ''' 434 ax = self.ax 435 ax.set_frame_on(False) 436 ax.set_navigate(False) 437 xy = self._outline(X, Y) 438 ax.update_datalim(xy) 439 ax.set_xlim(*ax.dataLim.intervalx) 440 ax.set_ylim(*ax.dataLim.intervaly) 441 if self.outline is not None: 442 self.outline.remove() 443 self.outline = mpatches.Polygon( 444 xy, edgecolor=mpl.rcParams['axes.edgecolor'], 445 facecolor='none', 446 linewidth=mpl.rcParams['axes.linewidth'], 447 closed=True, 448 zorder=2) 449 ax.add_artist(self.outline) 450 self.outline.set_clip_box(None) 451 self.outline.set_clip_path(None) 452 c = mpl.rcParams['axes.facecolor'] 453 if self.patch is not None: 454 self.patch.remove() 455 self.patch = mpatches.Polygon(xy, edgecolor=c, 456 facecolor=c, 457 linewidth=0.01, 458 zorder=-1) 459 ax.add_artist(self.patch) 460 461 self.update_ticks() 462 463 def _set_label(self): 464 if self.orientation == 'vertical': 465 self.ax.set_ylabel(self._label, **self._labelkw) 466 else: 467 self.ax.set_xlabel(self._label, **self._labelkw) 468 self.stale = True 469 470 def set_label(self, label, **kw): 471 ''' 472 Label the long axis of the colorbar 473 ''' 474 self._label = '%s' % (label, ) 475 self._labelkw = kw 476 self._set_label() 477 478 def _outline(self, X, Y): 479 ''' 480 Return *x*, *y* arrays of colorbar bounding polygon, 481 taking orientation into account. 482 ''' 483 N = X.shape[0] 484 ii = [0, 1, N - 2, N - 1, 2 * N - 1, 2 * N - 2, N + 1, N, 0] 485 x = np.take(np.ravel(np.transpose(X)), ii) 486 y = np.take(np.ravel(np.transpose(Y)), ii) 487 x = x.reshape((len(x), 1)) 488 y = y.reshape((len(y), 1)) 489 if self.orientation == 'horizontal': 490 return np.hstack((y, x)) 491 return np.hstack((x, y)) 492 493 def _edges(self, X, Y): 494 ''' 495 Return the separator line segments; helper for _add_solids. 496 ''' 497 N = X.shape[0] 498 # Using the non-array form of these line segments is much 499 # simpler than making them into arrays. 500 if self.orientation == 'vertical': 501 return [list(zip(X[i], Y[i])) for i in xrange(1, N - 1)] 502 else: 503 return [list(zip(Y[i], X[i])) for i in xrange(1, N - 1)] 504 505 def _add_solids(self, X, Y, C): 506 ''' 507 Draw the colors using :meth:`~matplotlib.axes.Axes.pcolormesh`; 508 optionally add separators. 509 ''' 510 if self.orientation == 'vertical': 511 args = (X, Y, C) 512 else: 513 args = (np.transpose(Y), np.transpose(X), np.transpose(C)) 514 kw = dict(cmap=self.cmap, 515 norm=self.norm, 516 alpha=self.alpha, 517 edgecolors='None') 518 # Save, set, and restore hold state to keep pcolor from 519 # clearing the axes. Ordinarily this will not be needed, 520 # since the axes object should already have hold set. 521 _hold = self.ax._hold 522 self.ax._hold = True 523 col = self.ax.pcolormesh(*args, **kw) 524 self.ax._hold = _hold 525 #self.add_observer(col) # We should observe, not be observed... 526 527 if self.solids is not None: 528 self.solids.remove() 529 self.solids = col 530 if self.dividers is not None: 531 self.dividers.remove() 532 self.dividers = None 533 if self.drawedges: 534 linewidths = (0.5 * mpl.rcParams['axes.linewidth'],) 535 self.dividers = collections.LineCollection( 536 self._edges(X, Y), 537 colors=(mpl.rcParams['axes.edgecolor'],), 538 linewidths=linewidths) 539 self.ax.add_collection(self.dividers) 540 elif len(self._y) >= self.n_rasterize: 541 self.solids.set_rasterized(True) 542 543 def add_lines(self, levels, colors, linewidths, erase=True): 544 ''' 545 Draw lines on the colorbar. 546 547 *colors* and *linewidths* must be scalars or 548 sequences the same length as *levels*. 549 550 Set *erase* to False to add lines without first 551 removing any previously added lines. 552 ''' 553 y = self._locate(levels) 554 igood = (y < 1.001) & (y > -0.001) 555 y = y[igood] 556 if cbook.iterable(colors): 557 colors = np.asarray(colors)[igood] 558 if cbook.iterable(linewidths): 559 linewidths = np.asarray(linewidths)[igood] 560 N = len(y) 561 x = np.array([0.0, 1.0]) 562 X, Y = np.meshgrid(x, y) 563 if self.orientation == 'vertical': 564 xy = [list(zip(X[i], Y[i])) for i in xrange(N)] 565 else: 566 xy = [list(zip(Y[i], X[i])) for i in xrange(N)] 567 col = collections.LineCollection(xy, linewidths=linewidths) 568 569 if erase and self.lines: 570 for lc in self.lines: 571 lc.remove() 572 self.lines = [] 573 self.lines.append(col) 574 col.set_color(colors) 575 self.ax.add_collection(col) 576 self.stale = True 577 578 def _ticker(self): 579 ''' 580 Return the sequence of ticks (colorbar data locations), 581 ticklabels (strings), and the corresponding offset string. 582 ''' 583 locator = self.locator 584 formatter = self.formatter 585 if locator is None: 586 if self.boundaries is None: 587 if isinstance(self.norm, colors.NoNorm): 588 nv = len(self._values) 589 base = 1 + int(nv / 10) 590 locator = ticker.IndexLocator(base=base, offset=0) 591 elif isinstance(self.norm, colors.BoundaryNorm): 592 b = self.norm.boundaries 593 locator = ticker.FixedLocator(b, nbins=10) 594 elif isinstance(self.norm, colors.LogNorm): 595 locator = ticker.LogLocator(subs='all') 596 elif isinstance(self.norm, colors.SymLogNorm): 597 # The subs setting here should be replaced 598 # by logic in the locator. 599 locator = ticker.SymmetricalLogLocator( 600 subs=np.arange(1, 10), 601 linthresh=self.norm.linthresh, 602 base=10) 603 else: 604 if mpl.rcParams['_internal.classic_mode']: 605 locator = ticker.MaxNLocator() 606 else: 607 locator = ticker.AutoLocator() 608 else: 609 b = self._boundaries[self._inside] 610 locator = ticker.FixedLocator(b, nbins=10) 611 if isinstance(self.norm, colors.NoNorm) and self.boundaries is None: 612 intv = self._values[0], self._values[-1] 613 else: 614 intv = self.vmin, self.vmax 615 locator.create_dummy_axis(minpos=intv[0]) 616 formatter.create_dummy_axis(minpos=intv[0]) 617 locator.set_view_interval(*intv) 618 locator.set_data_interval(*intv) 619 formatter.set_view_interval(*intv) 620 formatter.set_data_interval(*intv) 621 622 b = np.array(locator()) 623 if isinstance(locator, ticker.LogLocator): 624 eps = 1e-10 625 b = b[(b <= intv[1] * (1 + eps)) & (b >= intv[0] * (1 - eps))] 626 else: 627 eps = (intv[1] - intv[0]) * 1e-10 628 b = b[(b <= intv[1] + eps) & (b >= intv[0] - eps)] 629 self._tick_data_values = b 630 ticks = self._locate(b) 631 formatter.set_locs(b) 632 ticklabels = [formatter(t, i) for i, t in enumerate(b)] 633 offset_string = formatter.get_offset() 634 return ticks, ticklabels, offset_string 635 636 def _process_values(self, b=None): 637 ''' 638 Set the :attr:`_boundaries` and :attr:`_values` attributes 639 based on the input boundaries and values. Input boundaries 640 can be *self.boundaries* or the argument *b*. 641 ''' 642 if b is None: 643 b = self.boundaries 644 if b is not None: 645 self._boundaries = np.asarray(b, dtype=float) 646 if self.values is None: 647 self._values = 0.5 * (self._boundaries[:-1] 648 + self._boundaries[1:]) 649 if isinstance(self.norm, colors.NoNorm): 650 self._values = (self._values + 0.00001).astype(np.int16) 651 return 652 self._values = np.array(self.values) 653 return 654 if self.values is not None: 655 self._values = np.array(self.values) 656 if self.boundaries is None: 657 b = np.zeros(len(self.values) + 1, 'd') 658 b[1:-1] = 0.5 * (self._values[:-1] - self._values[1:]) 659 b[0] = 2.0 * b[1] - b[2] 660 b[-1] = 2.0 * b[-2] - b[-3] 661 self._boundaries = b 662 return 663 self._boundaries = np.array(self.boundaries) 664 return 665 # Neither boundaries nor values are specified; 666 # make reasonable ones based on cmap and norm. 667 if isinstance(self.norm, colors.NoNorm): 668 b = self._uniform_y(self.cmap.N + 1) * self.cmap.N - 0.5 669 v = np.zeros((len(b) - 1,), dtype=np.int16) 670 v[self._inside] = np.arange(self.cmap.N, dtype=np.int16) 671 if self._extend_lower(): 672 v[0] = -1 673 if self._extend_upper(): 674 v[-1] = self.cmap.N 675 self._boundaries = b 676 self._values = v 677 return 678 elif isinstance(self.norm, colors.BoundaryNorm): 679 b = list(self.norm.boundaries) 680 if self._extend_lower(): 681 b = [b[0] - 1] + b 682 if self._extend_upper(): 683 b = b + [b[-1] + 1] 684 b = np.array(b) 685 v = np.zeros((len(b) - 1,), dtype=float) 686 bi = self.norm.boundaries 687 v[self._inside] = 0.5 * (bi[:-1] + bi[1:]) 688 if self._extend_lower(): 689 v[0] = b[0] - 1 690 if self._extend_upper(): 691 v[-1] = b[-1] + 1 692 self._boundaries = b 693 self._values = v 694 return 695 else: 696 if not self.norm.scaled(): 697 self.norm.vmin = 0 698 self.norm.vmax = 1 699 700 self.norm.vmin, self.norm.vmax = mtransforms.nonsingular( 701 self.norm.vmin, 702 self.norm.vmax, 703 expander=0.1) 704 705 b = self.norm.inverse(self._uniform_y(self.cmap.N + 1)) 706 707 if isinstance(self.norm, colors.LogNorm): 708 # If using a lognorm, ensure extensions don't go negative 709 if self._extend_lower(): 710 b[0] = 0.9 * b[0] 711 if self._extend_upper(): 712 b[-1] = 1.1 * b[-1] 713 else: 714 if self._extend_lower(): 715 b[0] = b[0] - 1 716 if self._extend_upper(): 717 b[-1] = b[-1] + 1 718 self._process_values(b) 719 720 def _find_range(self): 721 ''' 722 Set :attr:`vmin` and :attr:`vmax` attributes to the first and 723 last boundary excluding extended end boundaries. 724 ''' 725 b = self._boundaries[self._inside] 726 self.vmin = b[0] 727 self.vmax = b[-1] 728 729 def _central_N(self): 730 '''number of boundaries **before** extension of ends''' 731 nb = len(self._boundaries) 732 if self.extend == 'both': 733 nb -= 2 734 elif self.extend in ('min', 'max'): 735 nb -= 1 736 return nb 737 738 def _extended_N(self): 739 ''' 740 Based on the colormap and extend variable, return the 741 number of boundaries. 742 ''' 743 N = self.cmap.N + 1 744 if self.extend == 'both': 745 N += 2 746 elif self.extend in ('min', 'max'): 747 N += 1 748 return N 749 750 def _get_extension_lengths(self, frac, automin, automax, default=0.05): 751 ''' 752 Get the lengths of colorbar extensions. 753 754 A helper method for _uniform_y and _proportional_y. 755 ''' 756 # Set the default value. 757 extendlength = np.array([default, default]) 758 if isinstance(frac, six.string_types): 759 if frac.lower() == 'auto': 760 # Use the provided values when 'auto' is required. 761 extendlength[0] = automin 762 extendlength[1] = automax 763 else: 764 # Any other string is invalid. 765 raise ValueError('invalid value for extendfrac') 766 elif frac is not None: 767 try: 768 # Try to set min and max extension fractions directly. 769 extendlength[:] = frac 770 # If frac is a sequence containing None then NaN may 771 # be encountered. This is an error. 772 if np.isnan(extendlength).any(): 773 raise ValueError() 774 except (TypeError, ValueError): 775 # Raise an error on encountering an invalid value for frac. 776 raise ValueError('invalid value for extendfrac') 777 return extendlength 778 779 def _uniform_y(self, N): 780 ''' 781 Return colorbar data coordinates for *N* uniformly 782 spaced boundaries, plus ends if required. 783 ''' 784 if self.extend == 'neither': 785 y = np.linspace(0, 1, N) 786 else: 787 automin = automax = 1. / (N - 1.) 788 extendlength = self._get_extension_lengths(self.extendfrac, 789 automin, automax, 790 default=0.05) 791 if self.extend == 'both': 792 y = np.zeros(N + 2, 'd') 793 y[0] = 0. - extendlength[0] 794 y[-1] = 1. + extendlength[1] 795 elif self.extend == 'min': 796 y = np.zeros(N + 1, 'd') 797 y[0] = 0. - extendlength[0] 798 else: 799 y = np.zeros(N + 1, 'd') 800 y[-1] = 1. + extendlength[1] 801 y[self._inside] = np.linspace(0, 1, N) 802 return y 803 804 def _proportional_y(self): 805 ''' 806 Return colorbar data coordinates for the boundaries of 807 a proportional colorbar. 808 ''' 809 if isinstance(self.norm, colors.BoundaryNorm): 810 y = (self._boundaries - self._boundaries[0]) 811 y = y / (self._boundaries[-1] - self._boundaries[0]) 812 else: 813 y = self.norm(self._boundaries.copy()) 814 y = np.ma.filled(y, np.nan) 815 if self.extend == 'min': 816 # Exclude leftmost interval of y. 817 clen = y[-1] - y[1] 818 automin = (y[2] - y[1]) / clen 819 automax = (y[-1] - y[-2]) / clen 820 elif self.extend == 'max': 821 # Exclude rightmost interval in y. 822 clen = y[-2] - y[0] 823 automin = (y[1] - y[0]) / clen 824 automax = (y[-2] - y[-3]) / clen 825 elif self.extend == 'both': 826 # Exclude leftmost and rightmost intervals in y. 827 clen = y[-2] - y[1] 828 automin = (y[2] - y[1]) / clen 829 automax = (y[-2] - y[-3]) / clen 830 if self.extend in ('both', 'min', 'max'): 831 extendlength = self._get_extension_lengths(self.extendfrac, 832 automin, automax, 833 default=0.05) 834 if self.extend in ('both', 'min'): 835 y[0] = 0. - extendlength[0] 836 if self.extend in ('both', 'max'): 837 y[-1] = 1. + extendlength[1] 838 yi = y[self._inside] 839 norm = colors.Normalize(yi[0], yi[-1]) 840 y[self._inside] = np.ma.filled(norm(yi), np.nan) 841 return y 842 843 def _mesh(self): 844 ''' 845 Return X,Y, the coordinate arrays for the colorbar pcolormesh. 846 These are suitable for a vertical colorbar; swapping and 847 transposition for a horizontal colorbar are done outside 848 this function. 849 ''' 850 x = np.array([0.0, 1.0]) 851 if self.spacing == 'uniform': 852 y = self._uniform_y(self._central_N()) 853 else: 854 y = self._proportional_y() 855 self._y = y 856 X, Y = np.meshgrid(x, y) 857 if self._extend_lower() and not self.extendrect: 858 X[0, :] = 0.5 859 if self._extend_upper() and not self.extendrect: 860 X[-1, :] = 0.5 861 return X, Y 862 863 def _locate(self, x): 864 ''' 865 Given a set of color data values, return their 866 corresponding colorbar data coordinates. 867 ''' 868 if isinstance(self.norm, (colors.NoNorm, colors.BoundaryNorm)): 869 b = self._boundaries 870 xn = x 871 else: 872 # Do calculations using normalized coordinates so 873 # as to make the interpolation more accurate. 874 b = self.norm(self._boundaries, clip=False).filled() 875 xn = self.norm(x, clip=False).filled() 876 877 # The rest is linear interpolation with extrapolation at ends. 878 ii = np.searchsorted(b, xn) 879 i0 = ii - 1 880 itop = (ii == len(b)) 881 ibot = (ii == 0) 882 i0[itop] -= 1 883 ii[itop] -= 1 884 i0[ibot] += 1 885 ii[ibot] += 1 886 887 db = np.take(b, ii) - np.take(b, i0) 888 y = self._y 889 dy = np.take(y, ii) - np.take(y, i0) 890 z = np.take(y, i0) + (xn - np.take(b, i0)) * dy / db 891 return z 892 893 def set_alpha(self, alpha): 894 self.alpha = alpha 895 896 def remove(self): 897 """ 898 Remove this colorbar from the figure 899 """ 900 901 fig = self.ax.figure 902 fig.delaxes(self.ax) 903 904 905class Colorbar(ColorbarBase): 906 """ 907 This class connects a :class:`ColorbarBase` to a 908 :class:`~matplotlib.cm.ScalarMappable` such as a 909 :class:`~matplotlib.image.AxesImage` generated via 910 :meth:`~matplotlib.axes.Axes.imshow`. 911 912 It is not intended to be instantiated directly; instead, 913 use :meth:`~matplotlib.figure.Figure.colorbar` or 914 :func:`~matplotlib.pyplot.colorbar` to make your colorbar. 915 916 """ 917 def __init__(self, ax, mappable, **kw): 918 # Ensure the given mappable's norm has appropriate vmin and vmax set 919 # even if mappable.draw has not yet been called. 920 mappable.autoscale_None() 921 922 self.mappable = mappable 923 kw['cmap'] = cmap = mappable.cmap 924 kw['norm'] = norm = mappable.norm 925 926 if isinstance(mappable, contour.ContourSet): 927 CS = mappable 928 kw['alpha'] = mappable.get_alpha() 929 kw['boundaries'] = CS._levels 930 kw['values'] = CS.cvalues 931 kw['extend'] = CS.extend 932 #kw['ticks'] = CS._levels 933 kw.setdefault('ticks', ticker.FixedLocator(CS.levels, nbins=10)) 934 kw['filled'] = CS.filled 935 ColorbarBase.__init__(self, ax, **kw) 936 if not CS.filled: 937 self.add_lines(CS) 938 else: 939 if getattr(cmap, 'colorbar_extend', False) is not False: 940 kw.setdefault('extend', cmap.colorbar_extend) 941 942 if isinstance(mappable, martist.Artist): 943 kw['alpha'] = mappable.get_alpha() 944 945 ColorbarBase.__init__(self, ax, **kw) 946 947 def on_mappable_changed(self, mappable): 948 """ 949 Updates this colorbar to match the mappable's properties. 950 951 Typically this is automatically registered as an event handler 952 by :func:`colorbar_factory` and should not be called manually. 953 954 """ 955 self.set_cmap(mappable.get_cmap()) 956 self.set_clim(mappable.get_clim()) 957 self.update_normal(mappable) 958 959 def add_lines(self, CS, erase=True): 960 ''' 961 Add the lines from a non-filled 962 :class:`~matplotlib.contour.ContourSet` to the colorbar. 963 964 Set *erase* to False if these lines should be added to 965 any pre-existing lines. 966 ''' 967 if not isinstance(CS, contour.ContourSet) or CS.filled: 968 raise ValueError('add_lines is only for a ContourSet of lines') 969 tcolors = [c[0] for c in CS.tcolors] 970 tlinewidths = [t[0] for t in CS.tlinewidths] 971 # The following was an attempt to get the colorbar lines 972 # to follow subsequent changes in the contour lines, 973 # but more work is needed: specifically, a careful 974 # look at event sequences, and at how 975 # to make one object track another automatically. 976 #tcolors = [col.get_colors()[0] for col in CS.collections] 977 #tlinewidths = [col.get_linewidth()[0] for lw in CS.collections] 978 ColorbarBase.add_lines(self, CS.levels, tcolors, tlinewidths, 979 erase=erase) 980 981 def update_normal(self, mappable): 982 ''' 983 update solid, lines, etc. Unlike update_bruteforce, it does 984 not clear the axes. This is meant to be called when the image 985 or contour plot to which this colorbar belongs is changed. 986 ''' 987 self.draw_all() 988 if isinstance(self.mappable, contour.ContourSet): 989 CS = self.mappable 990 if not CS.filled: 991 self.add_lines(CS) 992 self.stale = True 993 994 def update_bruteforce(self, mappable): 995 ''' 996 Destroy and rebuild the colorbar. This is 997 intended to become obsolete, and will probably be 998 deprecated and then removed. It is not called when 999 the pyplot.colorbar function or the Figure.colorbar 1000 method are used to create the colorbar. 1001 1002 ''' 1003 # We are using an ugly brute-force method: clearing and 1004 # redrawing the whole thing. The problem is that if any 1005 # properties have been changed by methods other than the 1006 # colorbar methods, those changes will be lost. 1007 self.ax.cla() 1008 # clearing the axes will delete outline, patch, solids, and lines: 1009 self.outline = None 1010 self.patch = None 1011 self.solids = None 1012 self.lines = list() 1013 self.dividers = None 1014 self.set_alpha(mappable.get_alpha()) 1015 self.cmap = mappable.cmap 1016 self.norm = mappable.norm 1017 self.config_axis() 1018 self.draw_all() 1019 if isinstance(self.mappable, contour.ContourSet): 1020 CS = self.mappable 1021 if not CS.filled: 1022 self.add_lines(CS) 1023 #if self.lines is not None: 1024 # tcolors = [c[0] for c in CS.tcolors] 1025 # self.lines.set_color(tcolors) 1026 #Fixme? Recalculate boundaries, ticks if vmin, vmax have changed. 1027 #Fixme: Some refactoring may be needed; we should not 1028 # be recalculating everything if there was a simple alpha 1029 # change. 1030 1031 def remove(self): 1032 """ 1033 Remove this colorbar from the figure. If the colorbar was created with 1034 ``use_gridspec=True`` then restore the gridspec to its previous value. 1035 """ 1036 1037 ColorbarBase.remove(self) 1038 self.mappable.callbacksSM.disconnect(self.mappable.colorbar_cid) 1039 self.mappable.colorbar = None 1040 self.mappable.colorbar_cid = None 1041 1042 try: 1043 ax = self.mappable.axes 1044 except AttributeError: 1045 return 1046 1047 try: 1048 gs = ax.get_subplotspec().get_gridspec() 1049 subplotspec = gs.get_topmost_subplotspec() 1050 except AttributeError: 1051 # use_gridspec was False 1052 pos = ax.get_position(original=True) 1053 ax._set_position(pos) 1054 else: 1055 # use_gridspec was True 1056 ax.set_subplotspec(subplotspec) 1057 1058 1059@docstring.Substitution(make_axes_kw_doc) 1060def make_axes(parents, location=None, orientation=None, fraction=0.15, 1061 shrink=1.0, aspect=20, **kw): 1062 ''' 1063 Resize and reposition parent axes, and return a child 1064 axes suitable for a colorbar. 1065 1066 Keyword arguments may include the following (with defaults): 1067 1068 location : [None|'left'|'right'|'top'|'bottom'] 1069 The position, relative to **parents**, where the colorbar axes 1070 should be created. If None, the value will either come from the 1071 given ``orientation``, else it will default to 'right'. 1072 1073 orientation : [None|'vertical'|'horizontal'] 1074 The orientation of the colorbar. Typically, this keyword shouldn't 1075 be used, as it can be derived from the ``location`` keyword. 1076 1077 %s 1078 1079 Returns (cax, kw), the child axes and the reduced kw dictionary to be 1080 passed when creating the colorbar instance. 1081 ''' 1082 locations = ["left", "right", "top", "bottom"] 1083 if orientation is not None and location is not None: 1084 raise TypeError('position and orientation are mutually exclusive. ' 1085 'Consider setting the position to any of {}' 1086 .format(', '.join(locations))) 1087 1088 # provide a default location 1089 if location is None and orientation is None: 1090 location = 'right' 1091 1092 # allow the user to not specify the location by specifying the 1093 # orientation instead 1094 if location is None: 1095 location = 'right' if orientation == 'vertical' else 'bottom' 1096 1097 if location not in locations: 1098 raise ValueError('Invalid colorbar location. Must be one ' 1099 'of %s' % ', '.join(locations)) 1100 1101 default_location_settings = {'left': {'anchor': (1.0, 0.5), 1102 'panchor': (0.0, 0.5), 1103 'pad': 0.10, 1104 'orientation': 'vertical'}, 1105 'right': {'anchor': (0.0, 0.5), 1106 'panchor': (1.0, 0.5), 1107 'pad': 0.05, 1108 'orientation': 'vertical'}, 1109 'top': {'anchor': (0.5, 0.0), 1110 'panchor': (0.5, 1.0), 1111 'pad': 0.05, 1112 'orientation': 'horizontal'}, 1113 'bottom': {'anchor': (0.5, 1.0), 1114 'panchor': (0.5, 0.0), 1115 'pad': 0.15, # backwards compat 1116 'orientation': 'horizontal'}, 1117 } 1118 1119 loc_settings = default_location_settings[location] 1120 1121 # put appropriate values into the kw dict for passing back to 1122 # the Colorbar class 1123 kw['orientation'] = loc_settings['orientation'] 1124 kw['ticklocation'] = location 1125 1126 anchor = kw.pop('anchor', loc_settings['anchor']) 1127 parent_anchor = kw.pop('panchor', loc_settings['panchor']) 1128 1129 parents_iterable = cbook.iterable(parents) 1130 # turn parents into a list if it is not already. We do this w/ np 1131 # because `plt.subplots` can return an ndarray and is natural to 1132 # pass to `colorbar`. 1133 parents = np.atleast_1d(parents).ravel() 1134 1135 # check if using constrained_layout: 1136 try: 1137 gs = parents[0].get_subplotspec().get_gridspec() 1138 using_constrained_layout = (gs._layoutbox is not None) 1139 except AttributeError: 1140 using_constrained_layout = False 1141 1142 # defaults are not appropriate for constrained_layout: 1143 pad0 = loc_settings['pad'] 1144 if using_constrained_layout: 1145 pad0 = 0.02 1146 pad = kw.pop('pad', pad0) 1147 1148 fig = parents[0].get_figure() 1149 if not all(fig is ax.get_figure() for ax in parents): 1150 raise ValueError('Unable to create a colorbar axes as not all ' 1151 'parents share the same figure.') 1152 1153 # take a bounding box around all of the given axes 1154 parents_bbox = mtransforms.Bbox.union( 1155 [ax.get_position(original=True).frozen() for ax in parents]) 1156 1157 pb = parents_bbox 1158 if location in ('left', 'right'): 1159 if location == 'left': 1160 pbcb, _, pb1 = pb.splitx(fraction, fraction + pad) 1161 else: 1162 pb1, _, pbcb = pb.splitx(1 - fraction - pad, 1 - fraction) 1163 pbcb = pbcb.shrunk(1.0, shrink).anchored(anchor, pbcb) 1164 else: 1165 if location == 'bottom': 1166 pbcb, _, pb1 = pb.splity(fraction, fraction + pad) 1167 else: 1168 pb1, _, pbcb = pb.splity(1 - fraction - pad, 1 - fraction) 1169 pbcb = pbcb.shrunk(shrink, 1.0).anchored(anchor, pbcb) 1170 1171 # define the aspect ratio in terms of y's per x rather than x's per y 1172 aspect = 1.0 / aspect 1173 1174 # define a transform which takes us from old axes coordinates to 1175 # new axes coordinates 1176 shrinking_trans = mtransforms.BboxTransform(parents_bbox, pb1) 1177 1178 # transform each of the axes in parents using the new transform 1179 for ax in parents: 1180 new_posn = shrinking_trans.transform(ax.get_position()) 1181 new_posn = mtransforms.Bbox(new_posn) 1182 ax._set_position(new_posn) 1183 if parent_anchor is not False: 1184 ax.set_anchor(parent_anchor) 1185 1186 cax = fig.add_axes(pbcb) 1187 1188 # OK, now make a layoutbox for the cb axis. Later, we will use this 1189 # to make the colorbar fit nicely. 1190 if not using_constrained_layout: 1191 # no layout boxes: 1192 lb = None 1193 lbpos = None 1194 # and we need to set the aspect ratio by hand... 1195 cax.set_aspect(aspect, anchor=anchor, adjustable='box') 1196 else: 1197 if not parents_iterable: 1198 # this is a single axis... 1199 ax = parents[0] 1200 lb, lbpos = constrained_layout.layoutcolorbarsingle( 1201 ax, cax, shrink, aspect, location, pad=pad) 1202 else: # there is more than one parent, so lets use gridspec 1203 # the colorbar will be a sibling of this gridspec, so the 1204 # parent is the same parent as the gridspec. Either the figure, 1205 # or a subplotspec. 1206 1207 lb, lbpos = constrained_layout.layoutcolorbargridspec( 1208 parents, cax, shrink, aspect, location, pad) 1209 1210 cax._layoutbox = lb 1211 cax._poslayoutbox = lbpos 1212 1213 return cax, kw 1214 1215 1216@docstring.Substitution(make_axes_kw_doc) 1217def make_axes_gridspec(parent, **kw): 1218 ''' 1219 Resize and reposition a parent axes, and return a child axes 1220 suitable for a colorbar. This function is similar to 1221 make_axes. Prmary differences are 1222 1223 * *make_axes_gridspec* only handles the *orientation* keyword 1224 and cannot handle the "location" keyword. 1225 1226 * *make_axes_gridspec* should only be used with a subplot parent. 1227 1228 * *make_axes* creates an instance of Axes. *make_axes_gridspec* 1229 creates an instance of Subplot. 1230 1231 * *make_axes* updates the position of the 1232 parent. *make_axes_gridspec* replaces the grid_spec attribute 1233 of the parent with a new one. 1234 1235 While this function is meant to be compatible with *make_axes*, 1236 there could be some minor differences. 1237 1238 Keyword arguments may include the following (with defaults): 1239 1240 *orientation* 1241 'vertical' or 'horizontal' 1242 1243 %s 1244 1245 All but the first of these are stripped from the input kw set. 1246 1247 Returns (cax, kw), the child axes and the reduced kw dictionary to be 1248 passed when creating the colorbar instance. 1249 ''' 1250 1251 orientation = kw.setdefault('orientation', 'vertical') 1252 kw['ticklocation'] = 'auto' 1253 1254 fraction = kw.pop('fraction', 0.15) 1255 shrink = kw.pop('shrink', 1.0) 1256 aspect = kw.pop('aspect', 20) 1257 1258 x1 = 1 - fraction 1259 1260 # for shrinking 1261 pad_s = (1 - shrink) * 0.5 1262 wh_ratios = [pad_s, shrink, pad_s] 1263 1264 # we need to none the tree of layoutboxes because 1265 # constrained_layout can't remove and replace the tree 1266 # hierarchy w/o a seg fault. 1267 gs = parent.get_subplotspec().get_gridspec() 1268 layoutbox.nonetree(gs._layoutbox) 1269 gs_from_subplotspec = gridspec.GridSpecFromSubplotSpec 1270 if orientation == 'vertical': 1271 pad = kw.pop('pad', 0.05) 1272 wh_space = 2 * pad / (1 - pad) 1273 gs = gs_from_subplotspec(1, 2, 1274 subplot_spec=parent.get_subplotspec(), 1275 wspace=wh_space, 1276 width_ratios=[x1 - pad, fraction]) 1277 gs2 = gs_from_subplotspec(3, 1, 1278 subplot_spec=gs[1], 1279 hspace=0., 1280 height_ratios=wh_ratios) 1281 anchor = (0.0, 0.5) 1282 panchor = (1.0, 0.5) 1283 else: 1284 pad = kw.pop('pad', 0.15) 1285 wh_space = 2 * pad / (1 - pad) 1286 gs = gs_from_subplotspec(2, 1, 1287 subplot_spec=parent.get_subplotspec(), 1288 hspace=wh_space, 1289 height_ratios=[x1 - pad, fraction]) 1290 gs2 = gs_from_subplotspec(1, 3, 1291 subplot_spec=gs[1], 1292 wspace=0., 1293 width_ratios=wh_ratios) 1294 aspect = 1 / aspect 1295 anchor = (0.5, 1.0) 1296 panchor = (0.5, 0.0) 1297 1298 parent.set_subplotspec(gs[0]) 1299 parent.update_params() 1300 parent._set_position(parent.figbox) 1301 parent.set_anchor(panchor) 1302 1303 fig = parent.get_figure() 1304 cax = fig.add_subplot(gs2[1]) 1305 cax.set_aspect(aspect, anchor=anchor, adjustable='box') 1306 return cax, kw 1307 1308 1309class ColorbarPatch(Colorbar): 1310 """ 1311 A Colorbar which is created using :class:`~matplotlib.patches.Patch` 1312 rather than the default :func:`~matplotlib.axes.pcolor`. 1313 1314 It uses a list of Patch instances instead of a 1315 :class:`~matplotlib.collections.PatchCollection` because the 1316 latter does not allow the hatch pattern to vary among the 1317 members of the collection. 1318 """ 1319 def __init__(self, ax, mappable, **kw): 1320 # we do not want to override the behaviour of solids 1321 # so add a new attribute which will be a list of the 1322 # colored patches in the colorbar 1323 self.solids_patches = [] 1324 Colorbar.__init__(self, ax, mappable, **kw) 1325 1326 def _add_solids(self, X, Y, C): 1327 """ 1328 Draw the colors using :class:`~matplotlib.patches.Patch`; 1329 optionally add separators. 1330 """ 1331 # Save, set, and restore hold state to keep pcolor from 1332 # clearing the axes. Ordinarily this will not be needed, 1333 # since the axes object should already have hold set. 1334 _hold = self.ax._hold 1335 self.ax._hold = True 1336 1337 kw = {'alpha': self.alpha, } 1338 1339 n_segments = len(C) 1340 1341 # ensure there are sufficient hatches 1342 hatches = self.mappable.hatches * n_segments 1343 1344 patches = [] 1345 for i in xrange(len(X) - 1): 1346 val = C[i][0] 1347 hatch = hatches[i] 1348 1349 xy = np.array([[X[i][0], Y[i][0]], 1350 [X[i][1], Y[i][0]], 1351 [X[i + 1][1], Y[i + 1][0]], 1352 [X[i + 1][0], Y[i + 1][1]]]) 1353 1354 if self.orientation == 'horizontal': 1355 # if horizontal swap the xs and ys 1356 xy = xy[..., ::-1] 1357 1358 patch = mpatches.PathPatch(mpath.Path(xy), 1359 facecolor=self.cmap(self.norm(val)), 1360 hatch=hatch, linewidth=0, 1361 antialiased=False, **kw) 1362 self.ax.add_patch(patch) 1363 patches.append(patch) 1364 1365 if self.solids_patches: 1366 for solid in self.solids_patches: 1367 solid.remove() 1368 1369 self.solids_patches = patches 1370 1371 if self.dividers is not None: 1372 self.dividers.remove() 1373 self.dividers = None 1374 1375 if self.drawedges: 1376 self.dividers = collections.LineCollection( 1377 self._edges(X, Y), 1378 colors=(mpl.rcParams['axes.edgecolor'],), 1379 linewidths=(0.5 * mpl.rcParams['axes.linewidth'],)) 1380 self.ax.add_collection(self.dividers) 1381 1382 self.ax._hold = _hold 1383 1384 1385def colorbar_factory(cax, mappable, **kwargs): 1386 """ 1387 Creates a colorbar on the given axes for the given mappable. 1388 1389 Typically, for automatic colorbar placement given only a mappable use 1390 :meth:`~matplotlib.figure.Figure.colorbar`. 1391 1392 """ 1393 # if the given mappable is a contourset with any hatching, use 1394 # ColorbarPatch else use Colorbar 1395 if (isinstance(mappable, contour.ContourSet) 1396 and any([hatch is not None for hatch in mappable.hatches])): 1397 cb = ColorbarPatch(cax, mappable, **kwargs) 1398 else: 1399 cb = Colorbar(cax, mappable, **kwargs) 1400 1401 cid = mappable.callbacksSM.connect('changed', cb.on_mappable_changed) 1402 mappable.colorbar = cb 1403 mappable.colorbar_cid = cid 1404 1405 return cb 1406