Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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''' 

21import copy 

22import logging 

23 

24import numpy as np 

25 

26import matplotlib as mpl 

27import matplotlib.artist as martist 

28import matplotlib.cbook as cbook 

29import matplotlib.collections as collections 

30import matplotlib.colors as colors 

31import matplotlib.contour as contour 

32import matplotlib.cm as cm 

33import matplotlib.gridspec as gridspec 

34import matplotlib.patches as mpatches 

35import matplotlib.path as mpath 

36import matplotlib.ticker as ticker 

37import matplotlib.transforms as mtransforms 

38import matplotlib._layoutbox as layoutbox 

39import matplotlib._constrained_layout as constrained_layout 

40from matplotlib import docstring 

41 

42_log = logging.getLogger(__name__) 

43 

44make_axes_kw_doc = ''' 

45 

46 ============= ==================================================== 

47 Property Description 

48 ============= ==================================================== 

49 *orientation* vertical or horizontal 

50 *fraction* 0.15; fraction of original axes to use for colorbar 

51 *pad* 0.05 if vertical, 0.15 if horizontal; fraction 

52 of original axes between colorbar and new image axes 

53 *shrink* 1.0; fraction by which to multiply the size of the colorbar 

54 *aspect* 20; ratio of long to short dimensions 

55 *anchor* (0.0, 0.5) if vertical; (0.5, 1.0) if horizontal; 

56 the anchor point of the colorbar axes 

57 *panchor* (1.0, 0.5) if vertical; (0.5, 0.0) if horizontal; 

58 the anchor point of the colorbar parent axes. If 

59 False, the parent axes' anchor will be unchanged 

60 ============= ==================================================== 

61 

62''' 

63 

64colormap_kw_doc = ''' 

65 

66 ============ ==================================================== 

67 Property Description 

68 ============ ==================================================== 

69 *extend* {'neither', 'both', 'min', 'max'} 

70 If not 'neither', make pointed end(s) for out-of- 

71 range values. These are set for a given colormap 

72 using the colormap set_under and set_over methods. 

73 *extendfrac* {*None*, 'auto', length, lengths} 

74 If set to *None*, both the minimum and maximum 

75 triangular colorbar extensions with have a length of 

76 5% of the interior colorbar length (this is the 

77 default setting). If set to 'auto', makes the 

78 triangular colorbar extensions the same lengths as 

79 the interior boxes (when *spacing* is set to 

80 'uniform') or the same lengths as the respective 

81 adjacent interior boxes (when *spacing* is set to 

82 'proportional'). If a scalar, indicates the length 

83 of both the minimum and maximum triangular colorbar 

84 extensions as a fraction of the interior colorbar 

85 length. A two-element sequence of fractions may also 

86 be given, indicating the lengths of the minimum and 

87 maximum colorbar extensions respectively as a 

88 fraction of the interior colorbar length. 

89 *extendrect* bool 

90 If *False* the minimum and maximum colorbar extensions 

91 will be triangular (the default). If *True* the 

92 extensions will be rectangular. 

93 *spacing* {'uniform', 'proportional'} 

94 Uniform spacing gives each discrete color the same 

95 space; proportional makes the space proportional to 

96 the data interval. 

97 *ticks* *None* or list of ticks or Locator 

98 If None, ticks are determined automatically from the 

99 input. 

100 *format* None or str or Formatter 

101 If None, the 

102 :class:`~matplotlib.ticker.ScalarFormatter` is used. 

103 If a format string is given, e.g., '%.3f', that is 

104 used. An alternative 

105 :class:`~matplotlib.ticker.Formatter` object may be 

106 given instead. 

107 *drawedges* bool 

108 Whether to draw lines at color boundaries. 

109 *label* str 

110 The label on the colorbar's long axis. 

111 ============ ==================================================== 

112 

113 The following will probably be useful only in the context of 

114 indexed colors (that is, when the mappable has norm=NoNorm()), 

115 or other unusual circumstances. 

116 

117 ============ =================================================== 

118 Property Description 

119 ============ =================================================== 

120 *boundaries* None or a sequence 

121 *values* None or a sequence which must be of length 1 less 

122 than the sequence of *boundaries*. For each region 

123 delimited by adjacent entries in *boundaries*, the 

124 color mapped to the corresponding value in values 

125 will be used. 

126 ============ =================================================== 

127 

128''' 

129 

130colorbar_doc = ''' 

131 

132Add a colorbar to a plot. 

133 

134Function signatures for the :mod:`~matplotlib.pyplot` interface; all 

135but the first are also method signatures for the 

136:meth:`~matplotlib.figure.Figure.colorbar` method:: 

137 

138 colorbar(**kwargs) 

139 colorbar(mappable, **kwargs) 

140 colorbar(mappable, cax=cax, **kwargs) 

141 colorbar(mappable, ax=ax, **kwargs) 

142 

143Parameters 

144---------- 

145mappable 

146 The `matplotlib.cm.ScalarMappable` (i.e., `~matplotlib.image.Image`, 

147 `~matplotlib.contour.ContourSet`, etc.) described by this colorbar. 

148 This argument is mandatory for the `.Figure.colorbar` method but optional 

149 for the `.pyplot.colorbar` function, which sets the default to the current 

150 image. 

151 

152 Note that one can create a `ScalarMappable` "on-the-fly" to generate 

153 colorbars not attached to a previously drawn artist, e.g. :: 

154 

155 fig.colorbar(cm.ScalarMappable(norm=norm, cmap=cmap), ax=ax) 

156 

157cax : :class:`~matplotlib.axes.Axes` object, optional 

158 Axes into which the colorbar will be drawn. 

159 

160ax : :class:`~matplotlib.axes.Axes`, list of Axes, optional 

161 Parent axes from which space for a new colorbar axes will be stolen. 

162 If a list of axes is given they will all be resized to make room for the 

163 colorbar axes. 

164 

165use_gridspec : bool, optional 

166 If *cax* is ``None``, a new *cax* is created as an instance of Axes. If 

167 *ax* is an instance of Subplot and *use_gridspec* is ``True``, *cax* is 

168 created as an instance of Subplot using the :mod:`~.gridspec` module. 

169 

170Returns 

171------- 

172colorbar : `~matplotlib.colorbar.Colorbar` 

173 See also its base class, `~matplotlib.colorbar.ColorbarBase`. 

174 

175Notes 

176----- 

177Additional keyword arguments are of two kinds: 

178 

179 axes properties: 

180%s 

181 colorbar properties: 

182%s 

183 

184If *mappable* is a :class:`~matplotlib.contours.ContourSet`, its *extend* 

185kwarg is included automatically. 

186 

187The *shrink* kwarg provides a simple way to scale the colorbar with respect 

188to the axes. Note that if *cax* is specified, it determines the size of the 

189colorbar and *shrink* and *aspect* kwargs are ignored. 

190 

191For more precise control, you can manually specify the positions of 

192the axes objects in which the mappable and the colorbar are drawn. In 

193this case, do not use any of the axes properties kwargs. 

194 

195It is known that some vector graphics viewers (svg and pdf) renders white gaps 

196between segments of the colorbar. This is due to bugs in the viewers, not 

197Matplotlib. As a workaround, the colorbar can be rendered with overlapping 

198segments:: 

199 

200 cbar = colorbar() 

201 cbar.solids.set_edgecolor("face") 

202 draw() 

203 

204However this has negative consequences in other circumstances, e.g. with 

205semi-transparent images (alpha < 1) and colorbar extensions; therefore, this 

206workaround is not used by default (see issue #1188). 

207 

208''' % (make_axes_kw_doc, colormap_kw_doc) 

209 

210docstring.interpd.update(colorbar_doc=colorbar_doc) 

211 

212 

213def _set_ticks_on_axis_warn(*args, **kw): 

214 # a top level function which gets put in at the axes' 

215 # set_xticks set_yticks by _patch_ax 

216 cbook._warn_external("Use the colorbar set_ticks() method instead.") 

217 

218 

219class _ColorbarAutoLocator(ticker.MaxNLocator): 

220 """ 

221 AutoLocator for Colorbar 

222 

223 This locator is just a `.MaxNLocator` except the min and max are 

224 clipped by the norm's min and max (i.e. vmin/vmax from the 

225 image/pcolor/contour object). This is necessary so ticks don't 

226 extrude into the "extend regions". 

227 """ 

228 

229 def __init__(self, colorbar): 

230 """ 

231 This ticker needs to know the *colorbar* so that it can access 

232 its *vmin* and *vmax*. Otherwise it is the same as 

233 `~.ticker.AutoLocator`. 

234 """ 

235 

236 self._colorbar = colorbar 

237 nbins = 'auto' 

238 steps = [1, 2, 2.5, 5, 10] 

239 super().__init__(nbins=nbins, steps=steps) 

240 

241 def tick_values(self, vmin, vmax): 

242 # flip if needed: 

243 if vmin > vmax: 

244 vmin, vmax = vmax, vmin 

245 vmin = max(vmin, self._colorbar.norm.vmin) 

246 vmax = min(vmax, self._colorbar.norm.vmax) 

247 ticks = super().tick_values(vmin, vmax) 

248 rtol = (vmax - vmin) * 1e-10 

249 return ticks[(ticks >= vmin - rtol) & (ticks <= vmax + rtol)] 

250 

251 

252class _ColorbarAutoMinorLocator(ticker.AutoMinorLocator): 

253 """ 

254 AutoMinorLocator for Colorbar 

255 

256 This locator is just a `.AutoMinorLocator` except the min and max are 

257 clipped by the norm's min and max (i.e. vmin/vmax from the 

258 image/pcolor/contour object). This is necessary so that the minorticks 

259 don't extrude into the "extend regions". 

260 """ 

261 

262 def __init__(self, colorbar, n=None): 

263 """ 

264 This ticker needs to know the *colorbar* so that it can access 

265 its *vmin* and *vmax*. 

266 """ 

267 self._colorbar = colorbar 

268 self.ndivs = n 

269 super().__init__(n=None) 

270 

271 def __call__(self): 

272 vmin = self._colorbar.norm.vmin 

273 vmax = self._colorbar.norm.vmax 

274 ticks = super().__call__() 

275 rtol = (vmax - vmin) * 1e-10 

276 return ticks[(ticks >= vmin - rtol) & (ticks <= vmax + rtol)] 

277 

278 

279class _ColorbarLogLocator(ticker.LogLocator): 

280 """ 

281 LogLocator for Colorbarbar 

282 

283 This locator is just a `.LogLocator` except the min and max are 

284 clipped by the norm's min and max (i.e. vmin/vmax from the 

285 image/pcolor/contour object). This is necessary so ticks don't 

286 extrude into the "extend regions". 

287 

288 """ 

289 def __init__(self, colorbar, *args, **kwargs): 

290 """ 

291 _ColorbarLogLocator(colorbar, *args, **kwargs) 

292 

293 This ticker needs to know the *colorbar* so that it can access 

294 its *vmin* and *vmax*. Otherwise it is the same as 

295 `~.ticker.LogLocator`. The ``*args`` and ``**kwargs`` are the 

296 same as `~.ticker.LogLocator`. 

297 """ 

298 self._colorbar = colorbar 

299 super().__init__(*args, **kwargs) 

300 

301 def tick_values(self, vmin, vmax): 

302 if vmin > vmax: 

303 vmin, vmax = vmax, vmin 

304 vmin = max(vmin, self._colorbar.norm.vmin) 

305 vmax = min(vmax, self._colorbar.norm.vmax) 

306 ticks = super().tick_values(vmin, vmax) 

307 rtol = (np.log10(vmax) - np.log10(vmin)) * 1e-10 

308 ticks = ticks[(np.log10(ticks) >= np.log10(vmin) - rtol) & 

309 (np.log10(ticks) <= np.log10(vmax) + rtol)] 

310 return ticks 

311 

312 

313class _ColorbarMappableDummy: 

314 """ 

315 Private class to hold deprecated ColorbarBase methods that used to be 

316 inhereted from ScalarMappable. 

317 """ 

318 @cbook.deprecated("3.1", alternative="ScalarMappable.set_norm") 

319 def set_norm(self, norm): 

320 """ 

321 `.colorbar.Colorbar.set_norm` does nothing; set the norm on 

322 the mappable associated with this colorbar. 

323 """ 

324 pass 

325 

326 @cbook.deprecated("3.1", alternative="ScalarMappable.set_cmap") 

327 def set_cmap(self, cmap): 

328 """ 

329 `.colorbar.Colorbar.set_cmap` does nothing; set the norm on 

330 the mappable associated with this colorbar. 

331 """ 

332 pass 

333 

334 @cbook.deprecated("3.1", alternative="ScalarMappable.set_clim") 

335 def set_clim(self, vmin=None, vmax=None): 

336 """ 

337 `.colorbar.Colorbar.set_clim` does nothing; set the limits on 

338 the mappable associated with this colorbar. 

339 """ 

340 pass 

341 

342 @cbook.deprecated("3.1", alternative="ScalarMappable.get_cmap") 

343 def get_cmap(self): 

344 """Return the colormap.""" 

345 return self.cmap 

346 

347 @cbook.deprecated("3.1", alternative="ScalarMappable.get_clim") 

348 def get_clim(self): 

349 """Return the min, max of the color limits for image scaling.""" 

350 return self.norm.vmin, self.norm.vmax 

351 

352 

353class ColorbarBase(_ColorbarMappableDummy): 

354 r""" 

355 Draw a colorbar in an existing axes. 

356 

357 There are only some rare cases in which you would work directly with a 

358 `.ColorbarBase` as an end-user. Typically, colorbars are used 

359 with `.ScalarMappable`\s such as an `.AxesImage` generated via 

360 `~.axes.Axes.imshow`. For these cases you will use `.Colorbar` and 

361 likely create it via `.pyplot.colorbar` or `.Figure.colorbar`. 

362 

363 The main application of using a `.ColorbarBase` explicitly is drawing 

364 colorbars that are not associated with other elements in the figure, e.g. 

365 when showing a colormap by itself. 

366 

367 If the *cmap* kwarg is given but *boundaries* and *values* are left as 

368 None, then the colormap will be displayed on a 0-1 scale. To show the 

369 under- and over-value colors, specify the *norm* as:: 

370 

371 norm=colors.Normalize(clip=False) 

372 

373 To show the colors versus index instead of on the 0-1 scale, 

374 use:: 

375 

376 norm=colors.NoNorm() 

377 

378 Useful public methods are :meth:`set_label` and :meth:`add_lines`. 

379 

380 Attributes 

381 ---------- 

382 ax : `~matplotlib.axes.Axes` 

383 The `~.axes.Axes` instance in which the colorbar is drawn. 

384 lines : list 

385 A list of `.LineCollection` if lines were drawn, otherwise 

386 an empty list. 

387 dividers : `.LineCollection` 

388 A LineCollection if *drawedges* is ``True``, otherwise ``None``. 

389 

390 Parameters 

391 ---------- 

392 ax : `~matplotlib.axes.Axes` 

393 The `~.axes.Axes` instance in which the colorbar is drawn. 

394 cmap : `~matplotlib.colors.Colormap` or None 

395 The colormap to use. If *None*, use :rc:`image.cmap`. 

396 norm : `~matplotlib.colors.Normalize` 

397 

398 alpha : float 

399 

400 values 

401 

402 boundaries 

403 

404 orientation : {'vertical', 'horizontal'} 

405 

406 ticklocation : {'auto', 'left', 'right', 'top', 'bottom'} 

407 

408 extend : {'neither', 'both', 'min', 'max'} 

409 

410 spacing : {'uniform', 'proportional'} 

411 

412 ticks : `~matplotlib.ticker.Locator` or array-like of float 

413 

414 format : str or `~matplotlib.ticker.Formatter` 

415 

416 drawedges : bool 

417 

418 filled : bool 

419 

420 extendfrac 

421 

422 extendrec 

423 

424 label : str 

425 """ 

426 _slice_dict = {'neither': slice(0, None), 

427 'both': slice(1, -1), 

428 'min': slice(1, None), 

429 'max': slice(0, -1)} 

430 

431 n_rasterize = 50 # rasterize solids if number of colors >= n_rasterize 

432 

433 def __init__(self, ax, cmap=None, 

434 norm=None, 

435 alpha=None, 

436 values=None, 

437 boundaries=None, 

438 orientation='vertical', 

439 ticklocation='auto', 

440 extend='neither', 

441 spacing='uniform', # uniform or proportional 

442 ticks=None, 

443 format=None, 

444 drawedges=False, 

445 filled=True, 

446 extendfrac=None, 

447 extendrect=False, 

448 label='', 

449 ): 

450 cbook._check_isinstance([colors.Colormap, None], cmap=cmap) 

451 cbook._check_in_list( 

452 ['vertical', 'horizontal'], orientation=orientation) 

453 cbook._check_in_list( 

454 ['auto', 'left', 'right', 'top', 'bottom'], 

455 ticklocation=ticklocation) 

456 cbook._check_in_list( 

457 self._slice_dict, extend=extend) 

458 cbook._check_in_list( 

459 ['uniform', 'proportional'], spacing=spacing) 

460 

461 self.ax = ax 

462 self._patch_ax() 

463 if cmap is None: 

464 cmap = cm.get_cmap() 

465 if norm is None: 

466 norm = colors.Normalize() 

467 self.alpha = alpha 

468 self.cmap = cmap 

469 self.norm = norm 

470 self.values = values 

471 self.boundaries = boundaries 

472 self.extend = extend 

473 self._inside = self._slice_dict[extend] 

474 self.spacing = spacing 

475 self.orientation = orientation 

476 self.drawedges = drawedges 

477 self.filled = filled 

478 self.extendfrac = extendfrac 

479 self.extendrect = extendrect 

480 self.solids = None 

481 self.lines = list() 

482 self.outline = None 

483 self.patch = None 

484 self.dividers = None 

485 self.locator = None 

486 self.formatter = None 

487 self._manual_tick_data_values = None 

488 self.__scale = None # linear, log10 for now. Hopefully more? 

489 

490 if ticklocation == 'auto': 

491 ticklocation = 'bottom' if orientation == 'horizontal' else 'right' 

492 self.ticklocation = ticklocation 

493 

494 self.set_label(label) 

495 self._reset_locator_formatter_scale() 

496 

497 if np.iterable(ticks): 

498 self.locator = ticker.FixedLocator(ticks, nbins=len(ticks)) 

499 else: 

500 self.locator = ticks # Handle default in _ticker() 

501 

502 if isinstance(format, str): 

503 self.formatter = ticker.FormatStrFormatter(format) 

504 else: 

505 self.formatter = format # Assume it is a Formatter or None 

506 self.draw_all() 

507 

508 def _extend_lower(self): 

509 """Return whether the lower limit is open ended.""" 

510 return self.extend in ('both', 'min') 

511 

512 def _extend_upper(self): 

513 """Return whether the uper limit is open ended.""" 

514 return self.extend in ('both', 'max') 

515 

516 def _patch_ax(self): 

517 # bind some methods to the axes to warn users 

518 # against using those methods. 

519 self.ax.set_xticks = _set_ticks_on_axis_warn 

520 self.ax.set_yticks = _set_ticks_on_axis_warn 

521 

522 def draw_all(self): 

523 ''' 

524 Calculate any free parameters based on the current cmap and norm, 

525 and do all the drawing. 

526 ''' 

527 # sets self._boundaries and self._values in real data units. 

528 # takes into account extend values: 

529 self._process_values() 

530 # sets self.vmin and vmax in data units, but just for 

531 # the part of the colorbar that is not part of the extend 

532 # patch: 

533 self._find_range() 

534 # returns the X and Y mesh, *but* this was/is in normalized 

535 # units: 

536 X, Y = self._mesh() 

537 C = self._values[:, np.newaxis] 

538 # decide minor/major axis 

539 self.config_axis() 

540 self._config_axes(X, Y) 

541 if self.filled: 

542 self._add_solids(X, Y, C) 

543 

544 def config_axis(self): 

545 ax = self.ax 

546 

547 if self.orientation == 'vertical': 

548 long_axis, short_axis = ax.yaxis, ax.xaxis 

549 else: 

550 long_axis, short_axis = ax.xaxis, ax.yaxis 

551 

552 long_axis.set_label_position(self.ticklocation) 

553 long_axis.set_ticks_position(self.ticklocation) 

554 short_axis.set_ticks([]) 

555 short_axis.set_ticks([], minor=True) 

556 self._set_label() 

557 

558 def _get_ticker_locator_formatter(self): 

559 """ 

560 This code looks at the norm being used by the colorbar 

561 and decides what locator and formatter to use. If ``locator`` has 

562 already been set by hand, it just returns 

563 ``self.locator, self.formatter``. 

564 """ 

565 locator = self.locator 

566 formatter = self.formatter 

567 if locator is None: 

568 if self.boundaries is None: 

569 if isinstance(self.norm, colors.NoNorm): 

570 nv = len(self._values) 

571 base = 1 + int(nv / 10) 

572 locator = ticker.IndexLocator(base=base, offset=0) 

573 elif isinstance(self.norm, colors.BoundaryNorm): 

574 b = self.norm.boundaries 

575 locator = ticker.FixedLocator(b, nbins=10) 

576 elif isinstance(self.norm, colors.LogNorm): 

577 locator = _ColorbarLogLocator(self) 

578 elif isinstance(self.norm, colors.SymLogNorm): 

579 # The subs setting here should be replaced 

580 # by logic in the locator. 

581 locator = ticker.SymmetricalLogLocator( 

582 subs=np.arange(1, 10), 

583 linthresh=self.norm.linthresh, 

584 base=10) 

585 else: 

586 if mpl.rcParams['_internal.classic_mode']: 

587 locator = ticker.MaxNLocator() 

588 else: 

589 locator = _ColorbarAutoLocator(self) 

590 else: 

591 b = self._boundaries[self._inside] 

592 locator = ticker.FixedLocator(b, nbins=10) 

593 

594 if formatter is None: 

595 if isinstance(self.norm, colors.LogNorm): 

596 formatter = ticker.LogFormatterSciNotation() 

597 elif isinstance(self.norm, colors.SymLogNorm): 

598 formatter = ticker.LogFormatterSciNotation( 

599 linthresh=self.norm.linthresh) 

600 else: 

601 formatter = ticker.ScalarFormatter() 

602 else: 

603 formatter = self.formatter 

604 

605 self.locator = locator 

606 self.formatter = formatter 

607 _log.debug('locator: %r', locator) 

608 return locator, formatter 

609 

610 def _use_auto_colorbar_locator(self): 

611 """ 

612 Return if we should use an adjustable tick locator or a fixed 

613 one. (check is used twice so factored out here...) 

614 """ 

615 contouring = self.boundaries is not None and self.spacing == 'uniform' 

616 return (type(self.norm) in [colors.Normalize, colors.LogNorm] and 

617 not contouring) 

618 

619 def _reset_locator_formatter_scale(self): 

620 """ 

621 Reset the locator et al to defaults. Any user-hardcoded changes 

622 need to be re-entered if this gets called (either at init, or when 

623 the mappable normal gets changed: Colorbar.update_normal) 

624 """ 

625 self.locator = None 

626 self.formatter = None 

627 if (isinstance(self.norm, colors.LogNorm)): 

628 # *both* axes are made log so that determining the 

629 # mid point is easier. 

630 self.ax.set_xscale('log') 

631 self.ax.set_yscale('log') 

632 self.minorticks_on() 

633 self.__scale = 'log' 

634 else: 

635 self.ax.set_xscale('linear') 

636 self.ax.set_yscale('linear') 

637 if type(self.norm) is colors.Normalize: 

638 self.__scale = 'linear' 

639 else: 

640 self.__scale = 'manual' 

641 

642 def update_ticks(self): 

643 """ 

644 Force the update of the ticks and ticklabels. This must be 

645 called whenever the tick locator and/or tick formatter changes. 

646 """ 

647 ax = self.ax 

648 # Get the locator and formatter; defaults to self.locator if not None. 

649 locator, formatter = self._get_ticker_locator_formatter() 

650 long_axis = ax.yaxis if self.orientation == 'vertical' else ax.xaxis 

651 if self._use_auto_colorbar_locator(): 

652 _log.debug('Using auto colorbar locator on colorbar') 

653 _log.debug('locator: %r', locator) 

654 long_axis.set_major_locator(locator) 

655 long_axis.set_major_formatter(formatter) 

656 else: 

657 _log.debug('Using fixed locator on colorbar') 

658 ticks, ticklabels, offset_string = self._ticker(locator, formatter) 

659 long_axis.set_ticks(ticks) 

660 long_axis.set_ticklabels(ticklabels) 

661 long_axis.get_major_formatter().set_offset_string(offset_string) 

662 

663 def set_ticks(self, ticks, update_ticks=True): 

664 """ 

665 Set tick locations. 

666 

667 Parameters 

668 ---------- 

669 ticks : {None, sequence, :class:`~matplotlib.ticker.Locator` instance} 

670 If None, a default Locator will be used. 

671 

672 update_ticks : {True, False}, optional 

673 If True, tick locations are updated immediately. If False, 

674 use :meth:`update_ticks` to manually update the ticks. 

675 

676 """ 

677 if np.iterable(ticks): 

678 self.locator = ticker.FixedLocator(ticks, nbins=len(ticks)) 

679 else: 

680 self.locator = ticks 

681 

682 if update_ticks: 

683 self.update_ticks() 

684 self.stale = True 

685 

686 def get_ticks(self, minor=False): 

687 """Return the x ticks as a list of locations.""" 

688 if self._manual_tick_data_values is None: 

689 ax = self.ax 

690 long_axis = ( 

691 ax.yaxis if self.orientation == 'vertical' else ax.xaxis) 

692 return long_axis.get_majorticklocs() 

693 else: 

694 # We made the axes manually, the old way, and the ylim is 0-1, 

695 # so the majorticklocs are in those units, not data units. 

696 return self._manual_tick_data_values 

697 

698 def set_ticklabels(self, ticklabels, update_ticks=True): 

699 """ 

700 Set tick labels. 

701 

702 Tick labels are updated immediately unless *update_ticks* is *False*, 

703 in which case one should call `.update_ticks` explicitly. 

704 """ 

705 if isinstance(self.locator, ticker.FixedLocator): 

706 self.formatter = ticker.FixedFormatter(ticklabels) 

707 if update_ticks: 

708 self.update_ticks() 

709 else: 

710 cbook._warn_external("set_ticks() must have been called.") 

711 self.stale = True 

712 

713 def minorticks_on(self): 

714 """ 

715 Turns on the minor ticks on the colorbar without extruding 

716 into the "extend regions". 

717 """ 

718 ax = self.ax 

719 long_axis = ax.yaxis if self.orientation == 'vertical' else ax.xaxis 

720 

721 if long_axis.get_scale() == 'log': 

722 long_axis.set_minor_locator(_ColorbarLogLocator(self, base=10., 

723 subs='auto')) 

724 long_axis.set_minor_formatter(ticker.LogFormatterSciNotation()) 

725 else: 

726 long_axis.set_minor_locator(_ColorbarAutoMinorLocator(self)) 

727 

728 def minorticks_off(self): 

729 """ 

730 Turns off the minor ticks on the colorbar. 

731 """ 

732 ax = self.ax 

733 long_axis = ax.yaxis if self.orientation == 'vertical' else ax.xaxis 

734 

735 long_axis.set_minor_locator(ticker.NullLocator()) 

736 

737 def _config_axes(self, X, Y): 

738 ''' 

739 Make an axes patch and outline. 

740 ''' 

741 ax = self.ax 

742 ax.set_frame_on(False) 

743 ax.set_navigate(False) 

744 xy = self._outline(X, Y) 

745 ax.ignore_existing_data_limits = True 

746 ax.update_datalim(xy) 

747 ax.set_xlim(*ax.dataLim.intervalx) 

748 ax.set_ylim(*ax.dataLim.intervaly) 

749 if self.outline is not None: 

750 self.outline.remove() 

751 self.outline = mpatches.Polygon( 

752 xy, edgecolor=mpl.rcParams['axes.edgecolor'], 

753 facecolor='none', 

754 linewidth=mpl.rcParams['axes.linewidth'], 

755 closed=True, 

756 zorder=2) 

757 ax.add_artist(self.outline) 

758 self.outline.set_clip_box(None) 

759 self.outline.set_clip_path(None) 

760 c = mpl.rcParams['axes.facecolor'] 

761 if self.patch is not None: 

762 self.patch.remove() 

763 self.patch = mpatches.Polygon(xy, edgecolor=c, 

764 facecolor=c, 

765 linewidth=0.01, 

766 zorder=-1) 

767 ax.add_artist(self.patch) 

768 

769 self.update_ticks() 

770 

771 def _set_label(self): 

772 if self.orientation == 'vertical': 

773 self.ax.set_ylabel(self._label, **self._labelkw) 

774 else: 

775 self.ax.set_xlabel(self._label, **self._labelkw) 

776 self.stale = True 

777 

778 def set_label(self, label, **kw): 

779 """Label the long axis of the colorbar.""" 

780 self._label = label 

781 self._labelkw = kw 

782 self._set_label() 

783 

784 def _outline(self, X, Y): 

785 ''' 

786 Return *x*, *y* arrays of colorbar bounding polygon, 

787 taking orientation into account. 

788 ''' 

789 N = X.shape[0] 

790 ii = [0, 1, N - 2, N - 1, 2 * N - 1, 2 * N - 2, N + 1, N, 0] 

791 x = X.T.reshape(-1)[ii] 

792 y = Y.T.reshape(-1)[ii] 

793 return (np.column_stack([y, x]) 

794 if self.orientation == 'horizontal' else 

795 np.column_stack([x, y])) 

796 

797 def _edges(self, X, Y): 

798 ''' 

799 Return the separator line segments; helper for _add_solids. 

800 ''' 

801 N = X.shape[0] 

802 # Using the non-array form of these line segments is much 

803 # simpler than making them into arrays. 

804 if self.orientation == 'vertical': 

805 return [list(zip(X[i], Y[i])) for i in range(1, N - 1)] 

806 else: 

807 return [list(zip(Y[i], X[i])) for i in range(1, N - 1)] 

808 

809 def _add_solids(self, X, Y, C): 

810 ''' 

811 Draw the colors using :meth:`~matplotlib.axes.Axes.pcolormesh`; 

812 optionally add separators. 

813 ''' 

814 if self.orientation == 'vertical': 

815 args = (X, Y, C) 

816 else: 

817 args = (np.transpose(Y), np.transpose(X), np.transpose(C)) 

818 kw = dict(cmap=self.cmap, 

819 norm=self.norm, 

820 alpha=self.alpha, 

821 edgecolors='None') 

822 _log.debug('Setting pcolormesh') 

823 col = self.ax.pcolormesh(*args, **kw) 

824 # self.add_observer(col) # We should observe, not be observed... 

825 

826 if self.solids is not None: 

827 self.solids.remove() 

828 self.solids = col 

829 if self.dividers is not None: 

830 self.dividers.remove() 

831 self.dividers = None 

832 if self.drawedges: 

833 linewidths = (0.5 * mpl.rcParams['axes.linewidth'],) 

834 self.dividers = collections.LineCollection( 

835 self._edges(X, Y), 

836 colors=(mpl.rcParams['axes.edgecolor'],), 

837 linewidths=linewidths) 

838 self.ax.add_collection(self.dividers) 

839 elif len(self._y) >= self.n_rasterize: 

840 self.solids.set_rasterized(True) 

841 

842 def add_lines(self, levels, colors, linewidths, erase=True): 

843 ''' 

844 Draw lines on the colorbar. 

845 

846 *colors* and *linewidths* must be scalars or 

847 sequences the same length as *levels*. 

848 

849 Set *erase* to False to add lines without first 

850 removing any previously added lines. 

851 ''' 

852 y = self._locate(levels) 

853 rtol = (self._y[-1] - self._y[0]) * 1e-10 

854 igood = (y < self._y[-1] + rtol) & (y > self._y[0] - rtol) 

855 y = y[igood] 

856 if np.iterable(colors): 

857 colors = np.asarray(colors)[igood] 

858 if np.iterable(linewidths): 

859 linewidths = np.asarray(linewidths)[igood] 

860 X, Y = np.meshgrid([self._y[0], self._y[-1]], y) 

861 if self.orientation == 'vertical': 

862 xy = np.stack([X, Y], axis=-1) 

863 else: 

864 xy = np.stack([Y, X], axis=-1) 

865 col = collections.LineCollection(xy, linewidths=linewidths) 

866 

867 if erase and self.lines: 

868 for lc in self.lines: 

869 lc.remove() 

870 self.lines = [] 

871 self.lines.append(col) 

872 col.set_color(colors) 

873 self.ax.add_collection(col) 

874 self.stale = True 

875 

876 def _ticker(self, locator, formatter): 

877 ''' 

878 Return the sequence of ticks (colorbar data locations), 

879 ticklabels (strings), and the corresponding offset string. 

880 ''' 

881 if isinstance(self.norm, colors.NoNorm) and self.boundaries is None: 

882 intv = self._values[0], self._values[-1] 

883 else: 

884 intv = self.vmin, self.vmax 

885 locator.create_dummy_axis(minpos=intv[0]) 

886 formatter.create_dummy_axis(minpos=intv[0]) 

887 locator.set_view_interval(*intv) 

888 locator.set_data_interval(*intv) 

889 formatter.set_view_interval(*intv) 

890 formatter.set_data_interval(*intv) 

891 

892 b = np.array(locator()) 

893 if isinstance(locator, ticker.LogLocator): 

894 eps = 1e-10 

895 b = b[(b <= intv[1] * (1 + eps)) & (b >= intv[0] * (1 - eps))] 

896 else: 

897 eps = (intv[1] - intv[0]) * 1e-10 

898 b = b[(b <= intv[1] + eps) & (b >= intv[0] - eps)] 

899 self._manual_tick_data_values = b 

900 ticks = self._locate(b) 

901 ticklabels = formatter.format_ticks(b) 

902 offset_string = formatter.get_offset() 

903 return ticks, ticklabels, offset_string 

904 

905 def _process_values(self, b=None): 

906 ''' 

907 Set the :attr:`_boundaries` and :attr:`_values` attributes 

908 based on the input boundaries and values. Input boundaries 

909 can be *self.boundaries* or the argument *b*. 

910 ''' 

911 if b is None: 

912 b = self.boundaries 

913 if b is not None: 

914 self._boundaries = np.asarray(b, dtype=float) 

915 if self.values is None: 

916 self._values = 0.5 * (self._boundaries[:-1] 

917 + self._boundaries[1:]) 

918 if isinstance(self.norm, colors.NoNorm): 

919 self._values = (self._values + 0.00001).astype(np.int16) 

920 else: 

921 self._values = np.array(self.values) 

922 return 

923 if self.values is not None: 

924 self._values = np.array(self.values) 

925 if self.boundaries is None: 

926 b = np.zeros(len(self.values) + 1) 

927 b[1:-1] = 0.5 * (self._values[:-1] + self._values[1:]) 

928 b[0] = 2.0 * b[1] - b[2] 

929 b[-1] = 2.0 * b[-2] - b[-3] 

930 self._boundaries = b 

931 return 

932 self._boundaries = np.array(self.boundaries) 

933 return 

934 # Neither boundaries nor values are specified; 

935 # make reasonable ones based on cmap and norm. 

936 if isinstance(self.norm, colors.NoNorm): 

937 b = self._uniform_y(self.cmap.N + 1) * self.cmap.N - 0.5 

938 v = np.zeros(len(b) - 1, dtype=np.int16) 

939 v[self._inside] = np.arange(self.cmap.N, dtype=np.int16) 

940 if self._extend_lower(): 

941 v[0] = -1 

942 if self._extend_upper(): 

943 v[-1] = self.cmap.N 

944 self._boundaries = b 

945 self._values = v 

946 return 

947 elif isinstance(self.norm, colors.BoundaryNorm): 

948 b = list(self.norm.boundaries) 

949 if self._extend_lower(): 

950 b = [b[0] - 1] + b 

951 if self._extend_upper(): 

952 b = b + [b[-1] + 1] 

953 b = np.array(b) 

954 v = np.zeros(len(b) - 1) 

955 bi = self.norm.boundaries 

956 v[self._inside] = 0.5 * (bi[:-1] + bi[1:]) 

957 if self._extend_lower(): 

958 v[0] = b[0] - 1 

959 if self._extend_upper(): 

960 v[-1] = b[-1] + 1 

961 self._boundaries = b 

962 self._values = v 

963 return 

964 else: 

965 if not self.norm.scaled(): 

966 self.norm.vmin = 0 

967 self.norm.vmax = 1 

968 

969 self.norm.vmin, self.norm.vmax = mtransforms.nonsingular( 

970 self.norm.vmin, 

971 self.norm.vmax, 

972 expander=0.1) 

973 

974 b = self.norm.inverse(self._uniform_y(self.cmap.N + 1)) 

975 

976 if isinstance(self.norm, (colors.PowerNorm, colors.LogNorm)): 

977 # If using a lognorm or powernorm, ensure extensions don't 

978 # go negative 

979 if self._extend_lower(): 

980 b[0] = 0.9 * b[0] 

981 if self._extend_upper(): 

982 b[-1] = 1.1 * b[-1] 

983 else: 

984 if self._extend_lower(): 

985 b[0] = b[0] - 1 

986 if self._extend_upper(): 

987 b[-1] = b[-1] + 1 

988 self._process_values(b) 

989 

990 def _find_range(self): 

991 ''' 

992 Set :attr:`vmin` and :attr:`vmax` attributes to the first and 

993 last boundary excluding extended end boundaries. 

994 ''' 

995 b = self._boundaries[self._inside] 

996 self.vmin = b[0] 

997 self.vmax = b[-1] 

998 

999 def _central_N(self): 

1000 """Return the number of boundaries excluding end extensions.""" 

1001 nb = len(self._boundaries) 

1002 if self.extend == 'both': 

1003 nb -= 2 

1004 elif self.extend in ('min', 'max'): 

1005 nb -= 1 

1006 return nb 

1007 

1008 def _extended_N(self): 

1009 ''' 

1010 Based on the colormap and extend variable, return the 

1011 number of boundaries. 

1012 ''' 

1013 N = self.cmap.N + 1 

1014 if self.extend == 'both': 

1015 N += 2 

1016 elif self.extend in ('min', 'max'): 

1017 N += 1 

1018 return N 

1019 

1020 def _get_extension_lengths(self, frac, automin, automax, default=0.05): 

1021 ''' 

1022 Get the lengths of colorbar extensions. 

1023 

1024 A helper method for _uniform_y and _proportional_y. 

1025 ''' 

1026 # Set the default value. 

1027 extendlength = np.array([default, default]) 

1028 if isinstance(frac, str): 

1029 cbook._check_in_list(['auto'], extendfrac=frac.lower()) 

1030 # Use the provided values when 'auto' is required. 

1031 extendlength[:] = [automin, automax] 

1032 elif frac is not None: 

1033 try: 

1034 # Try to set min and max extension fractions directly. 

1035 extendlength[:] = frac 

1036 # If frac is a sequence containing None then NaN may 

1037 # be encountered. This is an error. 

1038 if np.isnan(extendlength).any(): 

1039 raise ValueError() 

1040 except (TypeError, ValueError): 

1041 # Raise an error on encountering an invalid value for frac. 

1042 raise ValueError('invalid value for extendfrac') 

1043 return extendlength 

1044 

1045 def _uniform_y(self, N): 

1046 ''' 

1047 Return colorbar data coordinates for *N* uniformly 

1048 spaced boundaries, plus ends if required. 

1049 ''' 

1050 if self.extend == 'neither': 

1051 y = np.linspace(0, 1, N) 

1052 else: 

1053 automin = automax = 1. / (N - 1.) 

1054 extendlength = self._get_extension_lengths(self.extendfrac, 

1055 automin, automax, 

1056 default=0.05) 

1057 if self.extend == 'both': 

1058 y = np.zeros(N + 2, 'd') 

1059 y[0] = 0. - extendlength[0] 

1060 y[-1] = 1. + extendlength[1] 

1061 elif self.extend == 'min': 

1062 y = np.zeros(N + 1, 'd') 

1063 y[0] = 0. - extendlength[0] 

1064 else: 

1065 y = np.zeros(N + 1, 'd') 

1066 y[-1] = 1. + extendlength[1] 

1067 y[self._inside] = np.linspace(0, 1, N) 

1068 return y 

1069 

1070 def _proportional_y(self): 

1071 ''' 

1072 Return colorbar data coordinates for the boundaries of 

1073 a proportional colorbar. 

1074 ''' 

1075 if isinstance(self.norm, colors.BoundaryNorm): 

1076 y = (self._boundaries - self._boundaries[0]) 

1077 y = y / (self._boundaries[-1] - self._boundaries[0]) 

1078 else: 

1079 y = self.norm(self._boundaries.copy()) 

1080 y = np.ma.filled(y, np.nan) 

1081 if self.extend == 'min': 

1082 # Exclude leftmost interval of y. 

1083 clen = y[-1] - y[1] 

1084 automin = (y[2] - y[1]) / clen 

1085 automax = (y[-1] - y[-2]) / clen 

1086 elif self.extend == 'max': 

1087 # Exclude rightmost interval in y. 

1088 clen = y[-2] - y[0] 

1089 automin = (y[1] - y[0]) / clen 

1090 automax = (y[-2] - y[-3]) / clen 

1091 elif self.extend == 'both': 

1092 # Exclude leftmost and rightmost intervals in y. 

1093 clen = y[-2] - y[1] 

1094 automin = (y[2] - y[1]) / clen 

1095 automax = (y[-2] - y[-3]) / clen 

1096 if self.extend in ('both', 'min', 'max'): 

1097 extendlength = self._get_extension_lengths(self.extendfrac, 

1098 automin, automax, 

1099 default=0.05) 

1100 if self.extend in ('both', 'min'): 

1101 y[0] = 0. - extendlength[0] 

1102 if self.extend in ('both', 'max'): 

1103 y[-1] = 1. + extendlength[1] 

1104 yi = y[self._inside] 

1105 norm = colors.Normalize(yi[0], yi[-1]) 

1106 y[self._inside] = np.ma.filled(norm(yi), np.nan) 

1107 return y 

1108 

1109 def _mesh(self): 

1110 ''' 

1111 Return ``(X, Y)``, the coordinate arrays for the colorbar pcolormesh. 

1112 These are suitable for a vertical colorbar; swapping and transposition 

1113 for a horizontal colorbar are done outside this function. 

1114 

1115 These are scaled between vmin and vmax. 

1116 ''' 

1117 # copy the norm and change the vmin and vmax to the vmin and 

1118 # vmax of the colorbar, not the norm. This allows the situation 

1119 # where the colormap has a narrower range than the colorbar, to 

1120 # accomodate extra contours: 

1121 norm = copy.copy(self.norm) 

1122 norm.vmin = self.vmin 

1123 norm.vmax = self.vmax 

1124 x = np.array([0.0, 1.0]) 

1125 if self.spacing == 'uniform': 

1126 y = self._uniform_y(self._central_N()) 

1127 else: 

1128 y = self._proportional_y() 

1129 xmid = np.array([0.5]) 

1130 if self.__scale != 'manual': 

1131 y = norm.inverse(y) 

1132 x = norm.inverse(x) 

1133 xmid = norm.inverse(xmid) 

1134 else: 

1135 # if a norm doesn't have a named scale, or 

1136 # we are not using a norm 

1137 dv = self.vmax - self.vmin 

1138 x = x * dv + self.vmin 

1139 y = y * dv + self.vmin 

1140 xmid = xmid * dv + self.vmin 

1141 self._y = y 

1142 X, Y = np.meshgrid(x, y) 

1143 if self._extend_lower() and not self.extendrect: 

1144 X[0, :] = xmid 

1145 if self._extend_upper() and not self.extendrect: 

1146 X[-1, :] = xmid 

1147 return X, Y 

1148 

1149 def _locate(self, x): 

1150 ''' 

1151 Given a set of color data values, return their 

1152 corresponding colorbar data coordinates. 

1153 ''' 

1154 if isinstance(self.norm, (colors.NoNorm, colors.BoundaryNorm)): 

1155 b = self._boundaries 

1156 xn = x 

1157 else: 

1158 # Do calculations using normalized coordinates so 

1159 # as to make the interpolation more accurate. 

1160 b = self.norm(self._boundaries, clip=False).filled() 

1161 xn = self.norm(x, clip=False).filled() 

1162 

1163 bunique = b 

1164 yunique = self._y 

1165 # trim extra b values at beginning and end if they are 

1166 # not unique. These are here for extended colorbars, and are not 

1167 # wanted for the interpolation. 

1168 if b[0] == b[1]: 

1169 bunique = bunique[1:] 

1170 yunique = yunique[1:] 

1171 if b[-1] == b[-2]: 

1172 bunique = bunique[:-1] 

1173 yunique = yunique[:-1] 

1174 

1175 z = np.interp(xn, bunique, yunique) 

1176 return z 

1177 

1178 def set_alpha(self, alpha): 

1179 self.alpha = alpha 

1180 

1181 def remove(self): 

1182 """ 

1183 Remove this colorbar from the figure 

1184 """ 

1185 

1186 fig = self.ax.figure 

1187 fig.delaxes(self.ax) 

1188 

1189 

1190class Colorbar(ColorbarBase): 

1191 """ 

1192 This class connects a :class:`ColorbarBase` to a 

1193 :class:`~matplotlib.cm.ScalarMappable` such as a 

1194 :class:`~matplotlib.image.AxesImage` generated via 

1195 :meth:`~matplotlib.axes.Axes.imshow`. 

1196 

1197 It is not intended to be instantiated directly; instead, 

1198 use :meth:`~matplotlib.figure.Figure.colorbar` or 

1199 :func:`~matplotlib.pyplot.colorbar` to make your colorbar. 

1200 

1201 """ 

1202 def __init__(self, ax, mappable, **kw): 

1203 # Ensure the given mappable's norm has appropriate vmin and vmax set 

1204 # even if mappable.draw has not yet been called. 

1205 if mappable.get_array() is not None: 

1206 mappable.autoscale_None() 

1207 

1208 self.mappable = mappable 

1209 kw['cmap'] = cmap = mappable.cmap 

1210 kw['norm'] = mappable.norm 

1211 

1212 if isinstance(mappable, contour.ContourSet): 

1213 CS = mappable 

1214 kw['alpha'] = mappable.get_alpha() 

1215 kw['boundaries'] = CS._levels 

1216 kw['values'] = CS.cvalues 

1217 kw['extend'] = CS.extend 

1218 kw.setdefault('ticks', ticker.FixedLocator(CS.levels, nbins=10)) 

1219 kw['filled'] = CS.filled 

1220 ColorbarBase.__init__(self, ax, **kw) 

1221 if not CS.filled: 

1222 self.add_lines(CS) 

1223 else: 

1224 if getattr(cmap, 'colorbar_extend', False) is not False: 

1225 kw.setdefault('extend', cmap.colorbar_extend) 

1226 

1227 if isinstance(mappable, martist.Artist): 

1228 kw['alpha'] = mappable.get_alpha() 

1229 

1230 ColorbarBase.__init__(self, ax, **kw) 

1231 

1232 def on_mappable_changed(self, mappable): 

1233 """ 

1234 Updates this colorbar to match the mappable's properties. 

1235 

1236 Typically this is automatically registered as an event handler 

1237 by :func:`colorbar_factory` and should not be called manually. 

1238 

1239 """ 

1240 _log.debug('colorbar mappable changed') 

1241 self.update_normal(mappable) 

1242 

1243 def add_lines(self, CS, erase=True): 

1244 ''' 

1245 Add the lines from a non-filled 

1246 :class:`~matplotlib.contour.ContourSet` to the colorbar. 

1247 

1248 Set *erase* to False if these lines should be added to 

1249 any pre-existing lines. 

1250 ''' 

1251 if not isinstance(CS, contour.ContourSet) or CS.filled: 

1252 raise ValueError('add_lines is only for a ContourSet of lines') 

1253 tcolors = [c[0] for c in CS.tcolors] 

1254 tlinewidths = [t[0] for t in CS.tlinewidths] 

1255 # The following was an attempt to get the colorbar lines 

1256 # to follow subsequent changes in the contour lines, 

1257 # but more work is needed: specifically, a careful 

1258 # look at event sequences, and at how 

1259 # to make one object track another automatically. 

1260 #tcolors = [col.get_colors()[0] for col in CS.collections] 

1261 #tlinewidths = [col.get_linewidth()[0] for lw in CS.collections] 

1262 ColorbarBase.add_lines(self, CS.levels, tcolors, tlinewidths, 

1263 erase=erase) 

1264 

1265 def update_normal(self, mappable): 

1266 """ 

1267 Update solid patches, lines, etc. 

1268 

1269 Unlike `.update_bruteforce`, this does not clear the axes. This is 

1270 meant to be called when the norm of the image or contour plot to which 

1271 this colorbar belongs changes. 

1272 

1273 If the norm on the mappable is different than before, this resets the 

1274 locator and formatter for the axis, so if these have been customized, 

1275 they will need to be customized again. However, if the norm only 

1276 changes values of *vmin*, *vmax* or *cmap* then the old formatter 

1277 and locator will be preserved. 

1278 """ 

1279 

1280 _log.debug('colorbar update normal %r %r', mappable.norm, self.norm) 

1281 self.mappable = mappable 

1282 self.set_alpha(mappable.get_alpha()) 

1283 self.cmap = mappable.cmap 

1284 if mappable.norm != self.norm: 

1285 self.norm = mappable.norm 

1286 self._reset_locator_formatter_scale() 

1287 

1288 self.draw_all() 

1289 if isinstance(self.mappable, contour.ContourSet): 

1290 CS = self.mappable 

1291 if not CS.filled: 

1292 self.add_lines(CS) 

1293 self.stale = True 

1294 

1295 def update_bruteforce(self, mappable): 

1296 ''' 

1297 Destroy and rebuild the colorbar. This is 

1298 intended to become obsolete, and will probably be 

1299 deprecated and then removed. It is not called when 

1300 the pyplot.colorbar function or the Figure.colorbar 

1301 method are used to create the colorbar. 

1302 

1303 ''' 

1304 # We are using an ugly brute-force method: clearing and 

1305 # redrawing the whole thing. The problem is that if any 

1306 # properties have been changed by methods other than the 

1307 # colorbar methods, those changes will be lost. 

1308 self.ax.cla() 

1309 self.locator = None 

1310 self.formatter = None 

1311 

1312 # clearing the axes will delete outline, patch, solids, and lines: 

1313 self.outline = None 

1314 self.patch = None 

1315 self.solids = None 

1316 self.lines = list() 

1317 self.dividers = None 

1318 self.update_normal(mappable) 

1319 self.draw_all() 

1320 if isinstance(self.mappable, contour.ContourSet): 

1321 CS = self.mappable 

1322 if not CS.filled: 

1323 self.add_lines(CS) 

1324 #if self.lines is not None: 

1325 # tcolors = [c[0] for c in CS.tcolors] 

1326 # self.lines.set_color(tcolors) 

1327 #Fixme? Recalculate boundaries, ticks if vmin, vmax have changed. 

1328 #Fixme: Some refactoring may be needed; we should not 

1329 # be recalculating everything if there was a simple alpha 

1330 # change. 

1331 

1332 def remove(self): 

1333 """ 

1334 Remove this colorbar from the figure. If the colorbar was created with 

1335 ``use_gridspec=True`` then restore the gridspec to its previous value. 

1336 """ 

1337 

1338 ColorbarBase.remove(self) 

1339 self.mappable.callbacksSM.disconnect(self.mappable.colorbar_cid) 

1340 self.mappable.colorbar = None 

1341 self.mappable.colorbar_cid = None 

1342 

1343 try: 

1344 ax = self.mappable.axes 

1345 except AttributeError: 

1346 return 

1347 

1348 try: 

1349 gs = ax.get_subplotspec().get_gridspec() 

1350 subplotspec = gs.get_topmost_subplotspec() 

1351 except AttributeError: 

1352 # use_gridspec was False 

1353 pos = ax.get_position(original=True) 

1354 ax._set_position(pos) 

1355 else: 

1356 # use_gridspec was True 

1357 ax.set_subplotspec(subplotspec) 

1358 

1359 

1360@docstring.Substitution(make_axes_kw_doc) 

1361def make_axes(parents, location=None, orientation=None, fraction=0.15, 

1362 shrink=1.0, aspect=20, **kw): 

1363 ''' 

1364 Resize and reposition parent axes, and return a child 

1365 axes suitable for a colorbar. 

1366 

1367 Keyword arguments may include the following (with defaults): 

1368 

1369 location : [None|'left'|'right'|'top'|'bottom'] 

1370 The position, relative to **parents**, where the colorbar axes 

1371 should be created. If None, the value will either come from the 

1372 given ``orientation``, else it will default to 'right'. 

1373 

1374 orientation : [None|'vertical'|'horizontal'] 

1375 The orientation of the colorbar. Typically, this keyword shouldn't 

1376 be used, as it can be derived from the ``location`` keyword. 

1377 

1378 %s 

1379 

1380 Returns (cax, kw), the child axes and the reduced kw dictionary to be 

1381 passed when creating the colorbar instance. 

1382 ''' 

1383 

1384 locations = ["left", "right", "top", "bottom"] 

1385 if orientation is not None and location is not None: 

1386 raise TypeError('position and orientation are mutually exclusive. ' 

1387 'Consider setting the position to any of {}' 

1388 .format(', '.join(locations))) 

1389 

1390 # provide a default location 

1391 if location is None and orientation is None: 

1392 location = 'right' 

1393 

1394 # allow the user to not specify the location by specifying the 

1395 # orientation instead 

1396 if location is None: 

1397 location = 'right' if orientation == 'vertical' else 'bottom' 

1398 

1399 cbook._check_in_list(locations, location=location) 

1400 

1401 default_location_settings = {'left': {'anchor': (1.0, 0.5), 

1402 'panchor': (0.0, 0.5), 

1403 'pad': 0.10, 

1404 'orientation': 'vertical'}, 

1405 'right': {'anchor': (0.0, 0.5), 

1406 'panchor': (1.0, 0.5), 

1407 'pad': 0.05, 

1408 'orientation': 'vertical'}, 

1409 'top': {'anchor': (0.5, 0.0), 

1410 'panchor': (0.5, 1.0), 

1411 'pad': 0.05, 

1412 'orientation': 'horizontal'}, 

1413 'bottom': {'anchor': (0.5, 1.0), 

1414 'panchor': (0.5, 0.0), 

1415 'pad': 0.15, # backwards compat 

1416 'orientation': 'horizontal'}, 

1417 } 

1418 

1419 loc_settings = default_location_settings[location] 

1420 

1421 # put appropriate values into the kw dict for passing back to 

1422 # the Colorbar class 

1423 kw['orientation'] = loc_settings['orientation'] 

1424 kw['ticklocation'] = location 

1425 

1426 anchor = kw.pop('anchor', loc_settings['anchor']) 

1427 parent_anchor = kw.pop('panchor', loc_settings['panchor']) 

1428 

1429 parents_iterable = np.iterable(parents) 

1430 # turn parents into a list if it is not already. We do this w/ np 

1431 # because `plt.subplots` can return an ndarray and is natural to 

1432 # pass to `colorbar`. 

1433 parents = np.atleast_1d(parents).ravel() 

1434 

1435 # check if using constrained_layout: 

1436 try: 

1437 gs = parents[0].get_subplotspec().get_gridspec() 

1438 using_constrained_layout = (gs._layoutbox is not None) 

1439 except AttributeError: 

1440 using_constrained_layout = False 

1441 

1442 # defaults are not appropriate for constrained_layout: 

1443 pad0 = loc_settings['pad'] 

1444 if using_constrained_layout: 

1445 pad0 = 0.02 

1446 pad = kw.pop('pad', pad0) 

1447 

1448 fig = parents[0].get_figure() 

1449 if not all(fig is ax.get_figure() for ax in parents): 

1450 raise ValueError('Unable to create a colorbar axes as not all ' 

1451 'parents share the same figure.') 

1452 

1453 # take a bounding box around all of the given axes 

1454 parents_bbox = mtransforms.Bbox.union( 

1455 [ax.get_position(original=True).frozen() for ax in parents]) 

1456 

1457 pb = parents_bbox 

1458 if location in ('left', 'right'): 

1459 if location == 'left': 

1460 pbcb, _, pb1 = pb.splitx(fraction, fraction + pad) 

1461 else: 

1462 pb1, _, pbcb = pb.splitx(1 - fraction - pad, 1 - fraction) 

1463 pbcb = pbcb.shrunk(1.0, shrink).anchored(anchor, pbcb) 

1464 else: 

1465 if location == 'bottom': 

1466 pbcb, _, pb1 = pb.splity(fraction, fraction + pad) 

1467 else: 

1468 pb1, _, pbcb = pb.splity(1 - fraction - pad, 1 - fraction) 

1469 pbcb = pbcb.shrunk(shrink, 1.0).anchored(anchor, pbcb) 

1470 

1471 # define the aspect ratio in terms of y's per x rather than x's per y 

1472 aspect = 1.0 / aspect 

1473 

1474 # define a transform which takes us from old axes coordinates to 

1475 # new axes coordinates 

1476 shrinking_trans = mtransforms.BboxTransform(parents_bbox, pb1) 

1477 

1478 # transform each of the axes in parents using the new transform 

1479 for ax in parents: 

1480 new_posn = shrinking_trans.transform(ax.get_position(original=True)) 

1481 new_posn = mtransforms.Bbox(new_posn) 

1482 ax._set_position(new_posn) 

1483 if parent_anchor is not False: 

1484 ax.set_anchor(parent_anchor) 

1485 

1486 cax = fig.add_axes(pbcb, label="<colorbar>") 

1487 

1488 # OK, now make a layoutbox for the cb axis. Later, we will use this 

1489 # to make the colorbar fit nicely. 

1490 if not using_constrained_layout: 

1491 # no layout boxes: 

1492 lb = None 

1493 lbpos = None 

1494 # and we need to set the aspect ratio by hand... 

1495 cax.set_aspect(aspect, anchor=anchor, adjustable='box') 

1496 else: 

1497 if not parents_iterable: 

1498 # this is a single axis... 

1499 ax = parents[0] 

1500 lb, lbpos = constrained_layout.layoutcolorbarsingle( 

1501 ax, cax, shrink, aspect, location, pad=pad) 

1502 else: # there is more than one parent, so lets use gridspec 

1503 # the colorbar will be a sibling of this gridspec, so the 

1504 # parent is the same parent as the gridspec. Either the figure, 

1505 # or a subplotspec. 

1506 

1507 lb, lbpos = constrained_layout.layoutcolorbargridspec( 

1508 parents, cax, shrink, aspect, location, pad) 

1509 

1510 cax._layoutbox = lb 

1511 cax._poslayoutbox = lbpos 

1512 

1513 return cax, kw 

1514 

1515 

1516@docstring.Substitution(make_axes_kw_doc) 

1517def make_axes_gridspec(parent, *, fraction=0.15, shrink=1.0, aspect=20, **kw): 

1518 ''' 

1519 Resize and reposition a parent axes, and return a child axes 

1520 suitable for a colorbar. This function is similar to 

1521 make_axes. Prmary differences are 

1522 

1523 * *make_axes_gridspec* only handles the *orientation* keyword 

1524 and cannot handle the "location" keyword. 

1525 

1526 * *make_axes_gridspec* should only be used with a subplot parent. 

1527 

1528 * *make_axes* creates an instance of Axes. *make_axes_gridspec* 

1529 creates an instance of Subplot. 

1530 

1531 * *make_axes* updates the position of the 

1532 parent. *make_axes_gridspec* replaces the grid_spec attribute 

1533 of the parent with a new one. 

1534 

1535 While this function is meant to be compatible with *make_axes*, 

1536 there could be some minor differences. 

1537 

1538 Keyword arguments may include the following (with defaults): 

1539 

1540 *orientation* 

1541 'vertical' or 'horizontal' 

1542 

1543 %s 

1544 

1545 All but the first of these are stripped from the input kw set. 

1546 

1547 Returns (cax, kw), the child axes and the reduced kw dictionary to be 

1548 passed when creating the colorbar instance. 

1549 ''' 

1550 

1551 orientation = kw.setdefault('orientation', 'vertical') 

1552 kw['ticklocation'] = 'auto' 

1553 

1554 x1 = 1 - fraction 

1555 

1556 # for shrinking 

1557 pad_s = (1 - shrink) * 0.5 

1558 wh_ratios = [pad_s, shrink, pad_s] 

1559 

1560 # we need to none the tree of layoutboxes because 

1561 # constrained_layout can't remove and replace the tree 

1562 # hierarchy w/o a seg fault. 

1563 gs = parent.get_subplotspec().get_gridspec() 

1564 layoutbox.nonetree(gs._layoutbox) 

1565 gs_from_subplotspec = gridspec.GridSpecFromSubplotSpec 

1566 if orientation == 'vertical': 

1567 pad = kw.pop('pad', 0.05) 

1568 wh_space = 2 * pad / (1 - pad) 

1569 gs = gs_from_subplotspec(1, 2, 

1570 subplot_spec=parent.get_subplotspec(), 

1571 wspace=wh_space, 

1572 width_ratios=[x1 - pad, fraction]) 

1573 gs2 = gs_from_subplotspec(3, 1, 

1574 subplot_spec=gs[1], 

1575 hspace=0., 

1576 height_ratios=wh_ratios) 

1577 anchor = (0.0, 0.5) 

1578 panchor = (1.0, 0.5) 

1579 else: 

1580 pad = kw.pop('pad', 0.15) 

1581 wh_space = 2 * pad / (1 - pad) 

1582 gs = gs_from_subplotspec(2, 1, 

1583 subplot_spec=parent.get_subplotspec(), 

1584 hspace=wh_space, 

1585 height_ratios=[x1 - pad, fraction]) 

1586 gs2 = gs_from_subplotspec(1, 3, 

1587 subplot_spec=gs[1], 

1588 wspace=0., 

1589 width_ratios=wh_ratios) 

1590 aspect = 1 / aspect 

1591 anchor = (0.5, 1.0) 

1592 panchor = (0.5, 0.0) 

1593 

1594 parent.set_subplotspec(gs[0]) 

1595 parent.update_params() 

1596 parent._set_position(parent.figbox) 

1597 parent.set_anchor(panchor) 

1598 

1599 fig = parent.get_figure() 

1600 cax = fig.add_subplot(gs2[1], label="<colorbar>") 

1601 cax.set_aspect(aspect, anchor=anchor, adjustable='box') 

1602 return cax, kw 

1603 

1604 

1605class ColorbarPatch(Colorbar): 

1606 """ 

1607 A Colorbar which is created using :class:`~matplotlib.patches.Patch` 

1608 rather than the default :func:`~matplotlib.axes.pcolor`. 

1609 

1610 It uses a list of Patch instances instead of a 

1611 :class:`~matplotlib.collections.PatchCollection` because the 

1612 latter does not allow the hatch pattern to vary among the 

1613 members of the collection. 

1614 """ 

1615 def __init__(self, ax, mappable, **kw): 

1616 # we do not want to override the behaviour of solids 

1617 # so add a new attribute which will be a list of the 

1618 # colored patches in the colorbar 

1619 self.solids_patches = [] 

1620 Colorbar.__init__(self, ax, mappable, **kw) 

1621 

1622 def _add_solids(self, X, Y, C): 

1623 """ 

1624 Draw the colors using :class:`~matplotlib.patches.Patch`; 

1625 optionally add separators. 

1626 """ 

1627 n_segments = len(C) 

1628 

1629 # ensure there are sufficient hatches 

1630 hatches = self.mappable.hatches * n_segments 

1631 

1632 patches = [] 

1633 for i in range(len(X) - 1): 

1634 val = C[i][0] 

1635 hatch = hatches[i] 

1636 

1637 xy = np.array([[X[i][0], Y[i][0]], 

1638 [X[i][1], Y[i][0]], 

1639 [X[i + 1][1], Y[i + 1][0]], 

1640 [X[i + 1][0], Y[i + 1][1]]]) 

1641 

1642 if self.orientation == 'horizontal': 

1643 # if horizontal swap the xs and ys 

1644 xy = xy[..., ::-1] 

1645 

1646 patch = mpatches.PathPatch(mpath.Path(xy), 

1647 facecolor=self.cmap(self.norm(val)), 

1648 hatch=hatch, linewidth=0, 

1649 antialiased=False, alpha=self.alpha) 

1650 self.ax.add_patch(patch) 

1651 patches.append(patch) 

1652 

1653 if self.solids_patches: 

1654 for solid in self.solids_patches: 

1655 solid.remove() 

1656 

1657 self.solids_patches = patches 

1658 

1659 if self.dividers is not None: 

1660 self.dividers.remove() 

1661 self.dividers = None 

1662 

1663 if self.drawedges: 

1664 self.dividers = collections.LineCollection( 

1665 self._edges(X, Y), 

1666 colors=(mpl.rcParams['axes.edgecolor'],), 

1667 linewidths=(0.5 * mpl.rcParams['axes.linewidth'],)) 

1668 self.ax.add_collection(self.dividers) 

1669 

1670 

1671def colorbar_factory(cax, mappable, **kwargs): 

1672 """ 

1673 Creates a colorbar on the given axes for the given mappable. 

1674 

1675 Typically, for automatic colorbar placement given only a mappable use 

1676 :meth:`~matplotlib.figure.Figure.colorbar`. 

1677 

1678 """ 

1679 # if the given mappable is a contourset with any hatching, use 

1680 # ColorbarPatch else use Colorbar 

1681 if (isinstance(mappable, contour.ContourSet) 

1682 and any(hatch is not None for hatch in mappable.hatches)): 

1683 cb = ColorbarPatch(cax, mappable, **kwargs) 

1684 else: 

1685 cb = Colorbar(cax, mappable, **kwargs) 

1686 

1687 cid = mappable.callbacksSM.connect('changed', cb.on_mappable_changed) 

1688 mappable.colorbar = cb 

1689 mappable.colorbar_cid = cid 

1690 

1691 return cb