8000 Merge pull request #11412 from efiring/contour_colors · matplotlib/matplotlib@40cfdae · GitHub
[go: up one dir, main page]

Skip to content

Commit 40cfdae

Browse files
authored
Merge pull request #11412 from efiring/contour_colors
Make contour and contourf color assignments consistent.
2 parents 94f41d1 + 3abe218 commit 40cfdae

31 files changed

+42694
-43472
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Contour color autoscaling improvements
2+
--------------------------------------
3+
4+
Selection of contour levels is now the same for contour and
5+
contourf; previously, for contour, levels outside the data range were
6+
deleted. (Exception: if no contour levels are found within the
7+
data range, the `levels` attribute is replaced with a list holding
8+
only the minimum of the data range.)
9+
10+
When contour is called with levels specified as a target number rather
11+
than a list, and the 'extend' kwarg is used, the levels are now chosen
12+
such that some data typically will fall in the extended range.
13+
14+
When contour is called with a `LogNorm` or a `LogLocator`, it will now
15+
select colors using the geometric mean rather than the arithmetic mean
16+
of the contour levels.

lib/matplotlib/contour.py

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,22 +1174,45 @@ def _autolev(self, N):
11741174
"""
11751175
Select contour levels to span the data.
11761176
1177+
The target number of levels, *N*, is used only when the
1178+
scale is not log and default locator is used.
1179+
11771180
We need two more levels for filled contours than for
11781181
line contours, because for the latter we need to specify
11791182
the lower and upper boundary of each range. For example,
11801183
a single contour boundary, say at z = 0, requires only
11811184
one contour line, but two filled regions, and therefore
11821185
three levels to provide boundaries for both regions.
11831186
"""
1187+
self._auto = True
11841188
if self.locator is None:
11851189
if self.logscale:
11861190
self.locator = ticker.LogLocator()
11871191
else:
11881192
self.locator = ticker.MaxNLocator(N + 1, min_n_ticks=1)
11891193

11901194
lev = self.locator.tick_values(self.zmin, self.zmax)
1191-
self._auto = True
1192-
return lev
1195+
1196+
try:
1197+
if self.locator._symmetric:
1198+
return lev
1199+
except AttributeError:
1200+
pass
1201+
1202+
# Trim excess levels the locator may have supplied.
1203+
under = np.nonzero(lev < self.zmin)[0]
1204+
i0 = under[-1] if len(under) else 0
1205+
over = np.nonzero(lev > self.zmax)[0]
1206+
i1 = over[0] + 1 if len(over) else len(lev)
1207+
if self.extend in ('min', 'both'):
1208+
i0 += 1
1209+
if self.extend in ('max', 'both'):
1210+
i1 -= 1
1211+
1212+
if i1 - i0 < 3:
1213+
i0, i1 = 0, len(lev)
1214+
1215+
return lev[i0:i1]
11931216

11941217
def _contour_level_args(self, z, args):
11951218
"""
@@ -1220,8 +1243,8 @@ def _contour_level_args(self, z, args):
12201243

12211244
if not self.filled:
12221245
inside = (self.levels > self.zmin) & (self.levels < self.zmax)
1223-
self.levels = self.levels[inside]
1224-
if len(self.levels) == 0:
1246+
levels_in = self.levels[inside]
1247+
if len(levels_in) == 0:
12251248
self.levels = [self.zmin]
12261249
warnings.warn("No contour levels were found"
12271250
" within the data range.")
@@ -1246,27 +1269,28 @@ def _process_levels(self):
12461269
# (Colorbar needs this even for line contours.)
12471270
self._levels = list(self.levels)
12481271

1272+
if self.logscale:
1273+
lower, upper = 1e-250, 1e250
1274+
else:
1275+
lower, upper = -1e250, 1e250
1276+
12491277
if self.extend in ('both', 'min'):
1250-
self._levels.insert(0, min(self.levels[0], self.zmin) - 1)
1278+
self._levels.insert(0, lower)
12511279
if self.extend in ('both', 'max'):
1252-
self._levels.append(max(self.levels[-1], self.zmax) + 1)
1280+
self._levels.append(upper)
12531281
self._levels = np.asarray(self._levels)
12541282

12551283
if not self.filled:
12561284
self.layers = self.levels
12571285
return
12581286

1259-
# layer values are mid-way between levels
1260-
self.layers = 0.5 * (self._levels[:-1] + self._levels[1:])
1261-
# ...except that extended layers must be outside the
1262-
# normed range:
1263-
if self.extend in ('both', 'min'):
1264-
if self.logscale:
1265-
self.layers[0] = 1e-150
1266-
else:
1267-
self.layers[0] = -1e150
1268-
if self.extend in ('both', 'max'):
1269-
self.layers[-1] = 1e150
1287+
# Layer values are mid-way between levels in screen space.
1288+
if self.logscale:
1289+
# Avoid overflow by taking sqrt before multiplying.
1290+
self.layers = (np.sqrt(self._levels[:-1])
1291+
* np.sqrt(self._levels[1:]))
1292+
else:
1293+
self.layers = 0.5 * (self._levels[:-1] + self._levels[1:])
12701294

12711295
def _process_colors(self):
12721296
"""
Binary file not shown.
Loading

0 commit comments

Comments
 (0)
0