diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index 2b3c21b6d45a..af4bf25b78b2 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -27,6 +27,7 @@ import matplotlib.texmanager as texmanager import matplotlib.transforms as mtransforms from matplotlib.cbook import mplDeprecation +from matplotlib.ticker import Locator, LogLocator # Import needed for adding manual selection capability to clabel from matplotlib.blocking_input import BlockingContourLabeler @@ -831,9 +832,6 @@ def __init__(self, ax, *args, **kwargs): self.logscale = True if norm is None: norm = colors.LogNorm() - if self.extend is not 'neither': - raise ValueError('extend kwarg does not work yet with log ' - ' scale') else: self.logscale = False @@ -1213,10 +1211,17 @@ def _process_levels(self): # (Colorbar needs this even for line contours.) self._levels = list(self.levels) + tempLocator = Locator() + if self.logscale: + tempLocator = LogLocator() + if self.locator is not None: + tempLocator = self.locator if self.extend in ('both', 'min'): - self._levels.insert(0, min(self.levels[0], self.zmin) - 1) + lowerbound = min(self.levels[0], self.zmin) + tempLocator._decrease_lower_bound(self._levels, lowerbound) if self.extend in ('both', 'max'): - self._levels.append(max(self.levels[-1], self.zmax) + 1) + upperbound = max(self.levels[-1], self.zmax) + tempLocator._increase_upper_bound(self._levels, upperbound) self._levels = np.asarray(self._levels) if not self.filled: @@ -1228,7 +1233,7 @@ def _process_levels(self): # ...except that extended layers must be outside the # normed range: if self.extend in ('both', 'min'): - self.layers[0] = -1e150 + self.layers[0] = tempLocator._get_lower_extend_layer_value() if self.extend in ('both', 'max'): self.layers[-1] = 1e150 diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/extended_cbar_with_contourf_both.png b/lib/matplotlib/tests/baseline_images/test_colorbar/extended_cbar_with_contourf_both.png new file mode 100644 index 000000000000..fa61544eed5e Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_colorbar/extended_cbar_with_contourf_both.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/extended_cbar_with_contourf_max.png b/lib/matplotlib/tests/baseline_images/test_colorbar/extended_cbar_with_contourf_max.png new file mode 100644 index 000000000000..65fc1b3b8b84 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_colorbar/extended_cbar_with_contourf_max.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/extended_cbar_with_contourf_min.png b/lib/matplotlib/tests/baseline_images/test_colorbar/extended_cbar_with_contourf_min.png new file mode 100644 index 000000000000..ed7c7ef3e362 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_colorbar/extended_cbar_with_contourf_min.png differ diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index ea5ed98cab6a..ee8ed6b11282 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -10,6 +10,8 @@ from matplotlib.colors import BoundaryNorm, LogNorm from matplotlib.cm import get_cmap from matplotlib.colorbar import ColorbarBase +from matplotlib import ticker, cm +from matplotlib.mlab import bivariate_normal def _get_cmap_norms(): @@ -308,3 +310,34 @@ def test_colorbar_lognorm_extension(): cb = ColorbarBase(ax, norm=LogNorm(vmin=0.1, vmax=1000.0), orientation='vertical', extend='both') assert cb._values[0] >= 0.0 + + +@image_comparison(baseline_images=['extended_cbar_with_contourf_min', + 'extended_cbar_with_contourf_max', + 'extended_cbar_with_contourf_both'], + extensions=['png']) +def test_extended_colorbar_on_contourf(): + np.random.seed(1) + N = 100 + x = np.linspace(-3.0, 3.0, N) + y = np.linspace(-2.0, 2.0, N) + X, Y = np.meshgrid(x, y) + z = (bivariate_normal(X, Y, 0.1, 0.2, 1.0, 1.0) + + 0.1 * bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)) + plt.figure() + plt.subplot(111) + plt.contourf(X, Y, z, cmap=cm.PuBu_r, locator=ticker.LogLocator(), + extend='min') + plt.colorbar() + + plt.figure() + plt.subplot(111) + plt.contourf(X, Y, z, cmap=cm.PuBu_r, locator=ticker.LogLocator(), + extend='max') + plt.colorbar() + + plt.figure() + plt.subplot(111) + plt.contourf(X, Y, z, cmap=cm.PuBu_r, locator=ticker.LogLocator(), + extend='both') + plt.colorbar() diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index f2fb69194849..9ad69dcae2b4 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -1480,6 +1480,15 @@ def zoom(self, direction): step = 0.1 * interval * direction self.axis.set_view_interval(vmin + step, vmax - step, ignore=True) + def _decrease_lower_bound(self, levels, lower_bound): + levels.insert(0, lower_bound - 1.0) + + def _increase_upper_bound(self, levels, upper_bound): + levels.append(upper_bound + 1.0) + + def _get_lower_extend_layer_value(self): + return -1e150 + def refresh(self): """refresh internal information based on current lim""" pass @@ -2192,6 +2201,15 @@ def nonsingular(self, vmin, vmax): vmax = decade_up(vmax, self._base) return vmin, vmax + def _decrease_lower_bound(self, levels, lower_bound): + levels.insert(0, lower_bound / self._base) + + def _increase_upper_bound(self, levels, upper_bound): + levels.append(upper_bound * self._base) + + def _get_lower_extend_layer_value(self): + return np.nextafter(0, 1) + class SymmetricalLogLocator(Locator): """