Description
Bug report
Bug summary
In MPL v3, when using Colorbar with a linear norm, then changing norm to PowerNorm, breaks the Colorbar under some circumstances. Worked fine in MPL v1 and v2.
Code for reproduction
Minimum necessary test case:
fig, ax = plt.subplots()
data = np.clip(randn(250, 250)*10, 0, 10)
cax = ax.imshow(data)
cbar = fig.colorbar(cax)
cax.norm = mpl.colors.PowerNorm(gamma=2, vmin=0, vmax=10)
cbar.set_norm(cax.norm)
cbar.update_normal(cax)
plt.show()
On matplotlib 3.0.2 the (bug?) result is:
On matplotlib 2.2.3 the (expected) result is:
Result when adding LogNorm to ColorbarBase._use_auto_colorbar_locator
(see discussion below) on matplotlib 3.0.2
Discussion
Few points:
- If the image's norm is set prior to
fig.colorbar
, then the expected result is achieved on 3.0.2 as well. - If vmin/vmax are not set, the result seems correct and matches between 3.0.2 and 2.2.3
cbar.update_bruteforce
instead of update_normal does not fix the issue
I looked into the colorbar code, and can see the following:
In ColorbarBase.update_ticks()
, if the auto-colorbar is first used (i.e. first branch), then setting PowerNorm calls the second branch (i.e. what it calls the fixed locator branch) once Colorbar.update_normal
runs. That exact sequence triggers the issue.
If things are forced to the first branch of ColorbarBase.update_ticks()
(e.g. via adding to PowerNorm to ColorbarBase._use_auto_colorbar_locator()
), then a similar result to what one gets from MPL 3.0.2 LogNorm is achieved. If things are forced to the second branch, then the result from MPL 2.2.3 is achieved. However, when first one branch is hit and then PowerNorm hits the second branch, this triggers the issue.
Relevant code from ColorbarBase,
def _use_auto_colorbar_locator(self):
return (self.boundaries is None
and self.values is None
and ((type(self.norm) == colors.Normalize)
or (type(self.norm) == colors.LogNorm)))
def update_ticks():
# ...
if self._use_auto_colorbar_locator():
_log.debug('Using auto colorbar locator on colorbar')
_log.debug('locator: %r', locator)
long_axis.set_major_locator(locator)
long_axis.set_major_formatter(formatter)
else:
_log.debug('Using fixed locator on colorbar')
ticks, ticklabels, offset_string = self._ticker(locator, formatter)
long_axis.set_ticks(ticks)
long_axis.set_ticklabels(ticklabels)
long_axis.get_major_formatter().set_offset_string(offset_string)
Expected outcome
Either:
- functionality of MPL 2.2.3
- functionality of adding PowerNorm to
ColorbarBase._use_auto_colorbar_locator
.