Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 1d4160c

Browse filesBrowse files
committed
ENH: colorbar ticks adjustable to colorbar size
1 parent c9f80ef commit 1d4160c
Copy full SHA for 1d4160c

File tree

Expand file treeCollapse file tree

7 files changed

+940
-839
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+940
-839
lines changed
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
The ticks for colorbar now adjust for the size of the colorbar
2+
--------------------------------------------------------------
3+
4+
Colorbar ticks now adjust for the size of the colorbar if the
5+
colorbar is made from a mappable that is not a contour or
6+
doesn't have a BoundaryNorm, or boundaries are not specified.
7+
If boundaries, etc are specified, the colorbar maintains the
8+
original behaviour.
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Colorbar ticks can now be automatic
2+
-----------------------------------
3+
4+
The number of ticks on colorbars was appropriate for a large colorbar, but
5+
looked bad if the colorbar was made smaller (i.e. via the ``shrink`` kwarg).
6+
This has been changed so that the number of ticks is now responsive to how
7+
large the colorbar is.

‎lib/matplotlib/colorbar.py

Copy file name to clipboardExpand all lines: lib/matplotlib/colorbar.py
+104-40Lines changed: 104 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import six
2525
from six.moves import xrange, zip
2626

27+
import logging
2728
import warnings
2829

2930
import numpy as np
@@ -43,6 +44,8 @@
4344

4445
from matplotlib import docstring
4546

47+
_log = logging.getLogger(__name__)
48+
4649
make_axes_kw_doc = '''
4750
4851
============= ====================================================
@@ -216,6 +219,22 @@ def _set_ticks_on_axis_warn(*args, **kw):
216219
warnings.warn("Use the colorbar set_ticks() method instead.")
217220

218221

222+
class ColorbarAutoLocator(ticker.MaxNLocator):
223+
""" AutoLocator for Colorbar
224+
"""
225+
226+
def __init__(self, colorbar, *args, **kwargs):
227+
self._colorbar = colorbar
228+
nbins = 'auto'
229+
steps = [1, 2, 2.5, 5, 10]
230+
ticker.MaxNLocator.__init__(self, nbins=nbins, steps=steps)
231+
232+
def tick_values(self, vmin, vmax):
233+
vmin = max(vmin, self._colorbar.norm.vmin)
234+
vmax = min(vmax, self._colorbar.norm.vmax)
235+
return ticker.MaxNLocator.tick_values(self, vmin, vmax)
236+
237+
219238
class ColorbarBase(cm.ScalarMappable):
220239
'''
221240
Draw a colorbar in an existing axes.
@@ -346,8 +365,15 @@ def draw_all(self):
346365
and do all the drawing.
347366
'''
348367

368+
# sets self._boundaries and self._values in real data units.
369+
# takes into account extend values:
349370
self._process_values()
371+
# sets self.vmin and vmax in data units, but just for
372+
# the part of the colorbar that is not part of the extend
373+
# patch:
350374
self._find_range()
375+
# returns the X and Y mesh, *but* this was/is in normalized
376+
# units:
351377
X, Y = self._mesh()
352378
C = self._values[:, np.newaxis]
353379
self._config_axes(X, Y)
@@ -369,22 +395,76 @@ def config_axis(self):
369395

370396
self._set_label()
371397

398+
def _get_ticker_locator_formatter(self):
399+
locator = self.locator
400+
formatter = self.formatter
401+
if locator is None:
402+
if self.boundaries is None:
403+
if isinstance(self.norm, colors.NoNorm):
404+
nv = len(self._values)
405+
base = 1 + int(nv / 10)
406+
locator = ticker.IndexLocator(base=base, offset=0)
407+
elif isinstance(self.norm, colors.BoundaryNorm):
408+
b = self.norm.boundaries
409+
locator = ticker.FixedLocator(b, nbins=10)
410+
elif isinstance(self.norm, colors.LogNorm):
411+
locator = ticker.LogLocator(subs='all')
412+
elif isinstance(self.norm, colors.SymLogNorm):
413+
# The subs setting here should be replaced
414+
# by logic in the locator.
415+
locator = ticker.SymmetricalLogLocator(
416+
subs=np.arange(1, 10),
417+
linthresh=self.norm.linthresh,
418+
base=10)
419+
else:
420+
if mpl.rcParams['_internal.classic_mode']:
421+
locator = ticker.MaxNLocator()
422+
else:
423+
locator = ColorbarAutoLocator(self)
424+
else:
425+
b = self._boundaries[self._inside]
426+
locator = ticker.FixedLocator(b, nbins=10)
427+
return locator, formatter
428+
429+
def _use_adjustable(self):
430+
"""
431+
Return if we should use an adjustable tick locator or a fixed
432+
one. (check is used twice so factored out here...)
433+
"""
434+
return (self.boundaries is None
435+
and self.values is None
436+
and not isinstance(self.norm, colors.BoundaryNorm))
437+
372438
def update_ticks(self):
373439
"""
374440
Force the update of the ticks and ticklabels. This must be
375441
called whenever the tick locator and/or tick formatter changes.
376442
"""
377443
ax = self.ax
378-
ticks, ticklabels, offset_string = self._ticker()
379-
if self.orientation == 'vertical':
380-
ax.yaxis.set_ticks(ticks)
381-
ax.set_yticklabels(ticklabels)
382-
ax.yaxis.get_major_formatter().set_offset_string(offset_string)
383-
444+
# get the locator and formatter. Defaults to
445+
# self.locator if not None..
446+
locator, formatter = self._get_ticker_locator_formatter()
447+
448+
if self._use_adjustable():
449+
_log.debug('Using adjustable locator on colorbar')
450+
if self.orientation == 'vertical':
451+
ax.yaxis.set_major_locator(locator)
452+
ax.yaxis.set_major_formatter(formatter)
453+
else:
454+
ax.xaxis.set_major_locator(locator)
455+
ax.xaxis.set_major_formatter(formatter)
384456
else:
385-
ax.xaxis.set_ticks(ticks)
386-
ax.set_xticklabels(ticklabels)
387-
ax.xaxis.get_major_formatter().set_offset_string(offset_string)
457+
_log.debug('Using fixed locator on colorbar')
458+
ticks, ticklabels, offset_string = self._ticker(locator, formatter)
459+
if self.orientation == 'vertical':
460+
ax.yaxis.set_ticks(ticks)
461+
ax.set_yticklabels(ticklabels)
462+
ax.yaxis.get_major_formatter().set_offset_string(offset_string)
463+
464+
else:
465+
ax.xaxis.set_ticks(ticks)
466+
ax.set_xticklabels(ticklabels)
467+
ax.xaxis.get_major_formatter().set_offset_string(offset_string)
388468

389469
def set_ticks(self, ticks, update_ticks=True):
390470
"""
@@ -574,39 +654,12 @@ def add_lines(self, levels, colors, linewidths, erase=True):
574654
self.ax.add_collection(col)
575655
self.stale = True
576656

577-
def _ticker(self):
657+
def _ticker(self, locator, formatter):
578658
'''
579659
Return the sequence of ticks (colorbar data locations),
580660
ticklabels (strings), and the corresponding offset string.
581661
'''
582-
locator = self.locator
583-
formatter = self.formatter
584-
if locator is None:
585-
if self.boundaries is None:
586-
if isinstance(self.norm, colors.NoNorm):
587-
nv = len(self._values)
588-
base = 1 + int(nv / 10)
589-
locator = ticker.IndexLocator(base=base, offset=0)
590-
elif isinstance(self.norm, colors.BoundaryNorm):
591-
b = self.norm.boundaries
592-
locator = ticker.FixedLocator(b, nbins=10)
593-
elif isinstance(self.norm, colors.LogNorm):
594-
locator = ticker.LogLocator(subs='all')
595-
elif isinstance(self.norm, colors.SymLogNorm):
596-
# The subs setting here should be replaced
597-
# by logic in the locator.
598-
locator = ticker.SymmetricalLogLocator(
599-
subs=np.arange(1, 10),
600-
linthresh=self.norm.linthresh,
601-
base=10)
602-
else:
603-
if mpl.rcParams['_internal.classic_mode']:
604-
locator = ticker.MaxNLocator()
605-
else:
606-
locator = ticker.AutoLocator()
607-
else:
608-
b = self._boundaries[self._inside]
609-
locator = ticker.FixedLocator(b, nbins=10)
662+
# locator, formatter = _get_ticker_locator_formatter()
610663
if isinstance(self.norm, colors.NoNorm) and self.boundaries is None:
611664
intv = self._values[0], self._values[-1]
612665
else:
@@ -851,12 +904,23 @@ def _mesh(self):
851904
y = self._uniform_y(self._central_N())
852905
else:
853906
y = self._proportional_y()
907+
# if boundaries and values are None, then we can go ahead and
908+
# scale this up for Auto tick location. Otherwise we
909+
# want to keep normalized between 0 and 1 and use manual tick
910+
# locations.
911+
if self._use_adjustable():
912+
dy = self.vmax - self.vmin
913+
y = y * dy + self.vmin
914+
x = x * dy
915+
else:
916+
dy = 1.0
854917
self._y = y
918+
855919
X, Y = np.meshgrid(x, y)
856920
if self._extend_lower() and not self.extendrect:
857-
X[0, :] = 0.5
921+
X[0, :] = 0.5 * dy
858922
if self._extend_upper() and not self.extendrect:
859-
X[-1, :] = 0.5
923+
X[-1, :] = 0.5 * dy
860924
return X, Y
861925

862926
def _locate(self, x):
Loading
Binary file not shown.
Loading

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.