24
24
import six
25
25
from six .moves import xrange , zip
26
26
27
+ import logging
27
28
import warnings
28
29
29
30
import numpy as np
43
44
44
45
from matplotlib import docstring
45
46
47
+ _log = logging .getLogger (__name__ )
48
+
46
49
make_axes_kw_doc = '''
47
50
48
51
============= ====================================================
@@ -216,6 +219,22 @@ def _set_ticks_on_axis_warn(*args, **kw):
216
219
warnings .warn ("Use the colorbar set_ticks() method instead." )
217
220
218
221
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
+
219
238
class ColorbarBase (cm .ScalarMappable ):
220
239
'''
221
240
Draw a colorbar in an existing axes.
@@ -346,8 +365,15 @@ def draw_all(self):
346
365
and do all the drawing.
347
366
'''
348
367
368
+ # sets self._boundaries and self._values in real data units.
369
+ # takes into account extend values:
349
370
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:
350
374
self ._find_range ()
375
+ # returns the X and Y mesh, *but* this was/is in normalized
376
+ # units:
351
377
X , Y = self ._mesh ()
352
378
C = self ._values [:, np .newaxis ]
353
379
self ._config_axes (X , Y )
@@ -369,22 +395,76 @@ def config_axis(self):
369
395
370
396
self ._set_label ()
371
397
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
+
372
438
def update_ticks (self ):
373
439
"""
374
440
Force the update of the ticks and ticklabels. This must be
375
441
called whenever the tick locator and/or tick formatter changes.
376
442
"""
377
443
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 )
384
456
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 )
388
468
389
469
def set_ticks (self , ticks , update_ticks = True ):
390
470
"""
@@ -574,39 +654,12 @@ def add_lines(self, levels, colors, linewidths, erase=True):
574
654
self .ax .add_collection (col )
575
655
self .stale = True
576
656
577
- def _ticker (self ):
657
+ def _ticker (self , locator , formatter ):
578
658
'''
579
659
Return the sequence of ticks (colorbar data locations),
580
660
ticklabels (strings), and the corresponding offset string.
581
661
'''
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()
610
663
if isinstance (self .norm , colors .NoNorm ) and self .boundaries is None :
611
664
intv = self ._values [0 ], self ._values [- 1 ]
612
665
else :
@@ -851,12 +904,23 @@ def _mesh(self):
851
904
y = self ._uniform_y (self ._central_N ())
852
905
else :
853
906
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
854
917
self ._y = y
918
+
855
919
X , Y = np .meshgrid (x , y )
856
920
if self ._extend_lower () and not self .extendrect :
857
- X [0 , :] = 0.5
921
+ X [0 , :] = 0.5 * dy
858
922
if self ._extend_upper () and not self .extendrect :
859
- X [- 1 , :] = 0.5
923
+ X [- 1 , :] = 0.5 * dy
860
924
return X , Y
861
925
862
926
def _locate (self , x ):
0 commit comments