8000 Merge pull request #13506 from jklymak/fix-colorbar-on-contour · matplotlib/matplotlib@a7b8e3b · GitHub
[go: up one dir, main page]

Skip to content

Commit a7b8e3b

Browse files
authored
Merge pull request #13506 from jklymak/fix-colorbar-on-contour
Change colorbar for contour to have the proper axes limits...
2 parents 1fd861b + 6d7c885 commit a7b8e3b

File tree

6 files changed

+67
-24
lines changed

6 files changed

+67
-24
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
`matplotlib.colorbar.Colorbar` uses un-normalized axes for all mappables
2+
------------------------------------------------------------------------
3+
4+
Before 3.0, `matplotlib.colorbar.Colorbar` (`~.Figure.colorbar`) normalized
5+
all axes limits between 0 and 1 and had custom tickers to handle the
6+
labelling of the colorbar ticks. After 3.0, colorbars constructed from
7+
mappables that were *not* contours were constructed with axes that had
8+
limits between ``vmin`` and ``vmax`` of the mappable's norm, and the tickers
9+
were made children of the normal axes tickers.
10+
11+
This version of Matplotlib extends that to mappables made by contours, and
12+
allows the axes to run between the lowest boundary in the contour and the
13+
highest.
14+
15+
Code that worked around the normalization between 0 and 1 will need to be
16+
modified.

lib/matplotlib/colorbar.py

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
is a thin wrapper over :meth:`~matplotlib.figure.Figure.colorbar`.
1919
2020
'''
21-
21+
import copy
2222
import logging
2323

2424
import numpy as np
@@ -489,6 +489,7 @@ def draw_all(self):
489489
# units:
490490
X, Y = self._mesh()
491491
C = self._values[:, np.newaxis]
492+
# decide minor/major axis
492493
self.config_axis()
493494
self._config_axes(X, Y)
494495
if self.filled:
@@ -565,10 +566,11 @@ def _use_auto_colorbar_locator(self):
565566
Return if we should use an adjustable tick locator or a fixed
566567
one. (check is used twice so factored out here...)
567568
"""
568-
return (self.boundaries is None
569-
and self.values is None
570-
and ((type(self.norm) == colors.Normalize)
571-
or (type(self.norm) == colors.LogNorm)))
569+
contouring = ((self.boundaries is not None) and
570+
(self.spacing == 'uniform'))
571+
return (((type(self.norm) == colors.Normalize)
572+
or (type(self.norm) == colors.LogNorm))
573+
and not contouring)
572574

573575
def _reset_locator_formatter_scale(self):
574576
"""
@@ -578,13 +580,11 @@ def _reset_locator_formatter_scale(self):
578580
"""
579581
self.locator = None
580582
self.formatter = None
581-
if (isinstance(self.norm, colors.LogNorm)
582-
and self._use_auto_colorbar_locator()):
583+ 8000
if (isinstance(self.norm, colors.LogNorm)):
583584
# *both* axes are made log so that determining the
584585
# mid point is easier.
585586
self.ax.set_xscale('log')
586587
self.ax.set_yscale('log')
587-
588588
self.minorticks_on()
589589
else:
590590
self.ax.set_xscale('linear')
@@ -1066,26 +1066,35 @@ def _mesh(self):
10661066
These are suitable for a vertical colorbar; swapping and
10671067
transposition for a horizontal colorbar are done outside
10681068
this function.
1069-
'''
1070-
# if boundaries and values are None, then we can go ahead and
1071-
# scale this up for Auto tick location. Otherwise we
1072-
# want to keep normalized between 0 and 1 and use manual tick
1073-
# locations.
10741069
1070+
These are scaled between vmin and vmax
1071+
'''
1072+
# copy the norm and change the vmin and vmax to the vmin and
1073+
# vmax of the colorbar, not the norm. This allows the situation
1074+
# where the colormap has a narrower range than the colorbar, to
1075+
# accomodate extra contours:
1076+
norm = copy.copy(self.norm)
1077+
norm.vmin = self.vmin
1078+
norm.vmax = self.vmax
10751079
x = np.array([0.0, 1.0])
10761080
if self.spacing == 'uniform':
10771081
y = self._uniform_y(self._central_N())
10781082
else:
10791083
y = self._proportional_y()
1080-
if self._use_auto_colorbar_locator():
1081-
y = self.norm.inverse(y)
1082-
x = self.norm.inverse(x)
1084+
xmid = np.array([0.5])
1085+
try:
1086+
y = norm.inverse(y)
1087+
x = norm.inverse(x)
1088+
xmid = norm.inverse(xmid)
1089+
except ValueError:
1090+
# occurs for norms that don't have an inverse, in
1091+
# which case manually scale:
1092+
dv = self.vmax - self.vmin
1093+
x = x * dv + self.vmin
1094+
y = y * dv + self.vmin
1095+
xmid = xmid * dv + self.vmin
10831096
self._y = y
10841097
X, Y = np.meshgrid(x, y)
1085-
if self._use_auto_colorbar_locator():
1086-
xmid = self.norm.inverse(0.5)
1087-
else:
1088-
xmid = 0.5
10891098
if self._extend_lower() and not self.extendrect:
10901099
X[0, :] = xmid
10911100
if self._extend_upper() and not self.extendrect:

lib/matplotlib/colors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1373,7 +1373,7 @@ def inverse(self, value):
13731373
BoundaryNorm is not invertible, so calling this method will always
13741374
raise an error
13751375
"""
1376-
return ValueError("BoundaryNorm is not invertible")
1376+
raise ValueError("BoundaryNorm is not invertible")
13771377

13781378

13791379
class NoNorm(Normalize):
Loading
Loading

lib/matplotlib/tests/test_contour.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -397,9 +397,13 @@ def test_contourf_log_extension():
397397
c3 = ax3.contourf(data, levels=levels,
398398
norm=LogNorm(vmin=levels.min(), vmax=levels.max()),
399399
extend='both')
400-
plt.colorbar(c1, ax=ax1)
401-
plt.colorbar(c2, ax=ax2)
402-
plt.colorbar(c3, ax=ax3)
400+
cb = plt.colorbar(c1, ax=ax1)
401+
assert cb.ax.get_ylim() == (1e-8, 1e10)
402+
cb = plt.colorbar(c2, ax=ax2)
403+
assert cb.ax.get_ylim() == (1e-4, 1e6)
404+
cb = plt.colorbar(c3, ax=ax3)
405+
assert_array_almost_equal(cb.ax.get_ylim(),
406+
[3.162277660168379e-05, 3162277.660168383], 2)
403407

404408

405409
@image_comparison(['contour_addlines.png'],
@@ -415,3 +419,17 @@ def test_contour_addlines():
415419
cont = ax.contour(X+1000)
416420
cb = fig.colorbar(pcm)
417421
cb.add_lines(cont)
422+
assert_array_almost_equal(cb.ax.get_ylim(), [114.3091, 9972.30735], 3)
423+
424+
425+
@image_comparison(baseline_images=['contour_uneven'],
426+
extensions=['png'], remove_text=True, style='mpl20')
427+
def test_contour_uneven():
428+
z = np.arange(24).reshape(4, 6)
429+
fig, axs = plt.subplots(1, 2)
430+
ax = axs[0]
431+
cs = ax.contourf(z, levels=[2, 4, 6, 10, 20])
432+
fig.colorbar(cs, ax=ax, spacing='proportional')
433+
ax = axs[1]
434+
cs = ax.contourf(z, levels=[2, 4, 6, 10, 20])
435+
fig.colorbar(cs, ax=ax, spacing='uniform')

0 commit comments

Comments
 (0)
0