diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index f3b29ee429e5..821732dc318c 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -1094,6 +1094,8 @@ def _process_values(self): elif isinstance(self.norm, colors.NoNorm): # NoNorm has N blocks, so N+1 boundaries, centered on integers: b = np.arange(self.cmap.N + 1) - .5 + elif self.boundaries is not None: + b = self.boundaries else: # otherwise make the boundaries from the size of the cmap: N = self.cmap.N + 1 @@ -1110,7 +1112,8 @@ def _process_values(self): self.norm.vmax = 1 self.norm.vmin, self.norm.vmax = mtransforms.nonsingular( self.norm.vmin, self.norm.vmax, expander=0.1) - if not isinstance(self.norm, colors.BoundaryNorm): + if (not isinstance(self.norm, colors.BoundaryNorm) and + (self.boundaries is None)): b = self.norm.inverse(b) self._boundaries = np.asarray(b, dtype=float) @@ -1132,18 +1135,15 @@ def _mesh(self): norm = copy.deepcopy(self.norm) norm.vmin = self.vmin norm.vmax = self.vmax - x = np.array([0.0, 1.0]) y, extendlen = self._proportional_y() # invert: if (isinstance(norm, (colors.BoundaryNorm, colors.NoNorm)) or - (self.__scale == 'manual')): - # if a norm doesn't have a named scale, or we are not using a norm: - dv = self.vmax - self.vmin - y = y * dv + self.vmin + self.boundaries is not None): + y = y * (self.vmax - self.vmin) + self.vmin # not using a norm. else: y = norm.inverse(y) self._y = y - X, Y = np.meshgrid(x, y) + X, Y = np.meshgrid([0., 1.], y) if self.orientation == 'vertical': return (X, Y, extendlen) else: @@ -1183,8 +1183,8 @@ def _reset_locator_formatter_scale(self): self._set_scale('function', functions=funcs) elif self.spacing == 'proportional': self._set_scale('linear') - elif hasattr(self.norm, '_scale') and self.norm._scale is not None: - # use the norm's scale: + elif getattr(self.norm, '_scale', None): + # use the norm's scale (if it exists and is not None): self._set_scale(self.norm._scale) elif type(self.norm) is colors.Normalize: # plain Normalize: @@ -1234,7 +1234,8 @@ def _proportional_y(self): Return colorbar data coordinates for the boundaries of a proportional colorbar, plus extension lengths if required: """ - if isinstance(self.norm, colors.BoundaryNorm): + if (isinstance(self.norm, colors.BoundaryNorm) or + self.boundaries is not None): y = (self._boundaries - self._boundaries[self._inside][0]) y = y / (self._boundaries[self._inside][-1] - self._boundaries[self._inside][0]) diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/test_boundaries.png b/lib/matplotlib/tests/baseline_images/test_colorbar/test_boundaries.png new file mode 100644 index 000000000000..e2e05a375742 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_colorbar/test_boundaries.png differ diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index 17992a09e396..0fd049655a2c 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -927,3 +927,12 @@ def test_nonorm(): cmap = cm.get_cmap("viridis", len(data)) mappable = cm.ScalarMappable(norm=norm, cmap=cmap) cbar = fig.colorbar(mappable, cax=ax, orientation="horizontal") + + +@image_comparison(['test_boundaries.png'], remove_text=True, + style='mpl20') +def test_boundaries(): + np.random.seed(seed=19680808) + fig, ax = plt.subplots(figsize=(2, 2)) + pc = ax.pcolormesh(np.random.randn(10, 10), cmap='RdBu_r') + cb = fig.colorbar(pc, ax=ax, boundaries=np.linspace(-3, 3, 7))