From 0bb2659271c8719fa22d78b8bd06bcac8ec31c16 Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Wed, 28 Jun 2017 13:31:04 -0400 Subject: [PATCH 1/5] ENH: support extend kwarg with LogNorm in contourf --- lib/matplotlib/contour.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index 2b3c21b6d45..e72a10c8120 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -831,9 +831,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 +1210,19 @@ def _process_levels(self): # (Colorbar needs this even for line contours.) self._levels = list(self.levels) + if self.logscale: + raised = lambda x: x * 1.1 + lowered = lambda x: x / 1.1 + else: + raised = lambda x: x + 1 + lowered = lambda x: x - 1 + if self.extend in ('both', 'min'): - self._levels.insert(0, min(self.levels[0], self.zmin) - 1) + lower = lowered(min(self.levels[0], self.zmin)) + self._levels.insert(0, lower) if self.extend in ('both', 'max'): - self._levels.append(max(self.levels[-1], self.zmax) + 1) + upper = raised(max(self.levels[-1], self.zmax)) + self._levels.append(upper) self._levels = np.asarray(self._levels) if not self.filled: @@ -1228,7 +1234,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] = 1e-150 if self.logscale else -1e150 if self.extend in ('both', 'max'): self.layers[-1] = 1e150 From f640b7d3e9591da629e1a506a653af7fcb1fb3ae Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Mon, 3 Jul 2017 13:34:01 -0400 Subject: [PATCH 2/5] Contour level auto-selection: clip unused Locator output Locator.tick_values() returns levels beyond the data limits. In the case of LogLocator with a small data range, the overrun can be large because it is expanding to the decade points. In addition, no account was being taken of the "extend" kwarg. With this changeset, the outermost levels will be the miminum required to include the data interval in the default case, and will be reduced when "extend" is used so that some of the data range will fall in the extended sections. This is expected to be rare, however; normally the "extend" kwarg would be used only when levels are explicitly set, not auto-selected with a Locator. --- lib/matplotlib/contour.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index e72a10c8120..0c227d9073d 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -1131,8 +1131,16 @@ def _autolev(self, N): self.locator = ticker.MaxNLocator(N + 1, min_n_ticks=1) lev = self.locator.tick_values(self.zmin, self.zmax) + i0 = np.nonzero(lev < self.zmin)[0][-1] + i1 = np.nonzero(lev > self.zmax)[0][0] + 1 + if self.extend in ('min', 'both'): + i0 += 1 + if self.extend in ('max', 'both'): + i1 -= 1 + i1 = min(i1, len(lev)) + self._auto = True - return lev + return lev[i0:i1] def _contour_level_args(self, z, args): """ From d7576f5502b8c5ec6cb75525e09155c418bd8b98 Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Mon, 3 Jul 2017 15:48:18 -0400 Subject: [PATCH 3/5] pep8 checker requires def in place of lambda --- lib/matplotlib/contour.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index 0c227d9073d..2917dc421da 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -1219,11 +1219,14 @@ def _process_levels(self): self._levels = list(self.levels) if self.logscale: - raised = lambda x: x * 1.1 - lowered = lambda x: x / 1.1 + def raised(x): return x * 1.1 + + def lowered(x): return x / 1.1 + else: - raised = lambda x: x + 1 - lowered = lambda x: x - 1 + def raised(x): return x + 1 + + def lowered(x): return x - 1 if self.extend in ('both', 'min'): lower = lowered(min(self.levels[0], self.zmin)) From a41cb99c602683fb54980b1b00468546f5f84c8a Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Mon, 3 Jul 2017 20:31:53 -0400 Subject: [PATCH 4/5] in _autolev, handle the case where tick_values does not overshoot --- lib/matplotlib/contour.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index 2917dc421da..c3e9a360f6f 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -1124,6 +1124,7 @@ def _autolev(self, N): one contour line, but two filled regions, and therefore three levels to provide boundaries for both regions. """ + self._auto = True if self.locator is None: if self.logscale: self.locator = ticker.LogLocator() @@ -1131,15 +1132,25 @@ def _autolev(self, N): self.locator = ticker.MaxNLocator(N + 1, min_n_ticks=1) lev = self.locator.tick_values(self.zmin, self.zmax) - i0 = np.nonzero(lev < self.zmin)[0][-1] - i1 = np.nonzero(lev > self.zmax)[0][0] + 1 + + try: + if self.locator._symmetric: + return lev + except AttributeError: + pass + + under = np.nonzero(lev < self.zmin)[0] + i0 = under[-1] if len(under) else 0 + over = np.nonzero(lev > self.zmax)[0] + i1 = over[0] + 1 if len(over) else len(lev) if self.extend in ('min', 'both'): i0 += 1 if self.extend in ('max', 'both'): i1 -= 1 - i1 = min(i1, len(lev)) - self._auto = True + if i1 - i0 < 3: + i0, i1 = 0, len(lev) + return lev[i0:i1] def _contour_level_args(self, z, args): From 399d57a04cd9830e95e73e26b735b90e27bd85be Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Mon, 3 Jul 2017 20:52:52 -0400 Subject: [PATCH 5/5] partial fix for contour_hatching test failure This puts the hatches in the same order as they were in the reference figure, but the images still don't match because the gray shades are not the same as they were. Furthermore, looking at the original reference figures shows that the pdf and svg backends handle the alpha differently than the agg backend. Not sure what to do about that. --- lib/matplotlib/tests/test_axes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index f1cc67fde7a..cdfe2aa006f 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -1450,7 +1450,7 @@ def test_contour_hatching(): fig = plt.figure() ax = fig.add_subplot(111) - cs = ax.contourf(x, y, z, hatches=['-', '/', '\\', '//'], + cs = ax.contourf(x, y, z, hatches=['/', '\\', '//', '-'], cmap=plt.get_cmap('gray'), extend='both', alpha=0.5)