From 8d3d03ad03183dc2f85f6ca222e432e90d232a45 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 6 Dec 2021 00:10:36 -0500 Subject: [PATCH 1/2] FIX: colorbars with NoNorm Closes #21870 This adds another special-case path for NoNorm and tweaks the contents of another. These special-cases are required because NoNorm (like BoundaryNorm) has a different return value than ever other Norm (it directly returns integers to index into the LUT rather than [0, 1] that is later mapped into the LUT. Co-authored-by: Jody Klymak --- lib/matplotlib/colorbar.py | 6 +- .../test_colorbar/nonorm_colorbars.svg | 151 ++++++++++++++++++ lib/matplotlib/tests/test_colorbar.py | 20 ++- 3 files changed, 174 insertions(+), 3 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_colorbar/nonorm_colorbars.svg diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 68530e0695aa..8aeea20c918b 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -839,7 +839,8 @@ def _get_ticker_locator_formatter(self): # default locator: nv = len(self._values) base = 1 + int(nv / 10) - locator = ticker.IndexLocator(base=base, offset=0) + # put ticks on integers between the boundaries of NoNorm... + locator = ticker.IndexLocator(base=base, offset=.5) if minorlocator is None: minorlocator = ticker.NullLocator() @@ -1097,6 +1098,9 @@ def _process_values(self): # otherwise values are set from the boundaries if isinstance(self.norm, colors.BoundaryNorm): b = self.norm.boundaries + 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 else: # otherwise make the boundaries from the size of the cmap: N = self.cmap.N + 1 diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/nonorm_colorbars.svg b/lib/matplotlib/tests/baseline_images/test_colorbar/nonorm_colorbars.svg new file mode 100644 index 000000000000..85f92c3c8d64 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_colorbar/nonorm_colorbars.svg @@ -0,0 +1,151 @@ + + + + + + + + 2021-12-06T14:23:33.155376 + image/svg+xml + + + Matplotlib v3.6.0.dev954+ged9e9c2ef2.d20211206, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + 1 + + + + + + + + + + 2 + + + + + + + + + + 3 + + + + + + + + + + 4 + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index 1521e0c5ed74..c6d021a1e8c9 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -7,10 +7,11 @@ from matplotlib import rc_context from matplotlib.testing.decorators import image_comparison import matplotlib.pyplot as plt -from matplotlib.colors import BoundaryNorm, LogNorm, PowerNorm, Normalize +from matplotlib.colors import ( + BoundaryNorm, LogNorm, PowerNorm, Normalize, NoNorm +) from matplotlib.colorbar import Colorbar from matplotlib.ticker import FixedLocator - from matplotlib.testing.decorators import check_figures_equal @@ -913,3 +914,18 @@ def test_negative_boundarynorm(): cb = fig.colorbar(cm.ScalarMappable(cmap=cmap, norm=norm), cax=ax) np.testing.assert_allclose(cb.ax.get_ylim(), [clevs[0], clevs[-1]]) np.testing.assert_allclose(cb.ax.get_yticks(), clevs) + + +@image_comparison(['nonorm_colorbars.svg'], remove_text=False, + style='mpl20') +def test_nonorm(): + plt.rcParams['svg.fonttype'] = 'none' + data = [1, 2, 3, 4, 5] + + fig, ax = plt.subplots(figsize=(6, 1)) + fig.subplots_adjust(bottom=0.5) + + norm = NoNorm(vmin=min(data), vmax=max(data)) + cmap = cm.get_cmap("viridis", len(data)) + mappable = cm.ScalarMappable(norm=norm, cmap=cmap) + cbar = fig.colorbar(mappable, cax=ax, orientation="horizontal") From 8266e76e419e5b313bebba290a79a60c6f778598 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 6 Dec 2021 14:27:05 -0500 Subject: [PATCH 2/2] MNT: move NoNorm logic up to be on same footing on BoundryNorm --- lib/matplotlib/colorbar.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 8aeea20c918b..cf31178b7696 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -824,6 +824,12 @@ def _get_ticker_locator_formatter(self): b = self.norm.boundaries if locator is None: locator = ticker.FixedLocator(b, nbins=10) + elif isinstance(self.norm, colors.NoNorm): + if locator is None: + # put ticks on integers between the boundaries of NoNorm + nv = len(self._values) + base = 1 + int(nv / 10) + locator = ticker.IndexLocator(base=base, offset=.5) elif self.boundaries is not None: b = self._boundaries[self._inside] if locator is None: @@ -835,12 +841,6 @@ def _get_ticker_locator_formatter(self): locator = self._long_axis().get_major_locator() if minorlocator is None: minorlocator = self._long_axis().get_minor_locator() - if isinstance(self.norm, colors.NoNorm): - # default locator: - nv = len(self._values) - base = 1 + int(nv / 10) - # put ticks on integers between the boundaries of NoNorm... - locator = ticker.IndexLocator(base=base, offset=.5) if minorlocator is None: minorlocator = ticker.NullLocator()