diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index e0c42c5b69d5..01005aabf282 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -1300,9 +1300,11 @@ def __call__(self, value, clip=None): if not self.vmin <= self.vcenter <= self.vmax: raise ValueError("vmin, vcenter, vmax must increase monotonically") + # note that we must extrapolate for tick locators: result = np.ma.masked_array( np.interp(result, [self.vmin, self.vcenter, self.vmax], - [0, 0.5, 1.]), mask=np.ma.getmask(result)) + [0, 0.5, 1], left=-np.inf, right=np.inf), + mask=np.ma.getmask(result)) if is_scalar: result = np.atleast_1d(result)[0] return result @@ -1313,8 +1315,8 @@ def inverse(self, value): (vmin,), _ = self.process_value(self.vmin) (vmax,), _ = self.process_value(self.vmax) (vcenter,), _ = self.process_value(self.vcenter) - - result = np.interp(value, [0, 0.5, 1.], [vmin, vcenter, vmax]) + result = np.interp(value, [0, 0.5, 1], [vmin, vcenter, vmax], + left=-np.inf, right=np.inf) return result diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_twoslope.png b/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_twoslope.png index 27d9227dcfe0..b92103ae1347 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_twoslope.png and b/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_twoslope.png differ diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index bb4781f131ed..75e12d3b44cc 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -776,11 +776,12 @@ def test_inset_colorbar_layout(): @image_comparison(['colorbar_twoslope.png'], remove_text=True, style='mpl20') def test_twoslope_colorbar(): - # Note that the first tick = 20, and should be in the middle + # Note that the second tick = 20, and should be in the middle # of the colorbar (white) + # There should be no tick right at the bottom, nor at the top. fig, ax = plt.subplots() - norm = mcolors.TwoSlopeNorm(20, 0, 100) + norm = mcolors.TwoSlopeNorm(20, 5, 95) pc = ax.pcolormesh(np.arange(1, 11), np.arange(1, 11), np.arange(100).reshape(10, 10), norm=norm, cmap='RdBu_r') diff --git a/tutorials/colors/colormapnorms.py b/tutorials/colors/colormapnorms.py index 41aa6310949a..ae3ac8825602 100644 --- a/tutorials/colors/colormapnorms.py +++ b/tutorials/colors/colormapnorms.py @@ -277,7 +277,8 @@ # longitude depends on latitude. ax.set_aspect(1 / np.cos(np.deg2rad(49))) ax.set_title('TwoSlopeNorm(x)') -fig.colorbar(pcm, shrink=0.6) +cb = fig.colorbar(pcm, shrink=0.6) +cb.set_ticks([-500, 0, 1000, 2000, 3000, 4000]) plt.show() @@ -312,7 +313,8 @@ def _inverse(x): # ---------------------------------------------------------- # # The `.TwoSlopeNorm` described above makes a useful example for -# defining your own norm. +# defining your own norm. Note for the colorbar to work, you must +# define an inverse for your norm: class MidpointNormalize(colors.Normalize): @@ -323,8 +325,14 @@ def __init__(self, vmin=None, vmax=None, vcenter=None, clip=False): def __call__(self, value, clip=None): # I'm ignoring masked values and all kinds of edge cases to make a # simple example... - x, y = [self.vmin, self.vcenter, self.vmax], [0, 0.5, 1] - return np.ma.masked_array(np.interp(value, x, y)) + # Note also that we must extrapolate beyond vmin/vmax + x, y = [self.vmin, self.vcenter, self.vmax], [0, 0.5, 1.] + return np.ma.masked_array(np.interp(value, x, y, + left=-np.inf, right=np.inf)) + + def inverse(self, value): + y, x = [self.vmin, self.vcenter, self.vmax], [0, 0.5, 1] + return np.interp(value, x, y, left=-np.inf, right=np.inf) fig, ax = plt.subplots() @@ -334,5 +342,7 @@ def __call__(self, value, clip=None): cmap=terrain_map, shading='auto') ax.set_aspect(1 / np.cos(np.deg2rad(49))) ax.set_title('Custom norm') -fig.colorbar(pcm, shrink=0.6, extend='both') +cb = fig.colorbar(pcm, shrink=0.6, extend='both') +cb.set_ticks([-500, 0, 1000, 2000, 3000, 4000]) + plt.show()