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,65 @@ 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
+ """
224
+ AutoLocator for Colorbar
225
+
226
+ This locator is just a `.MaxNLocator` except the min and max are
227
+ clipped by the norm's min and max (i.e. vmin/vmax from the
228
+ image/pcolor/contour object). This is necessary so ticks don't
229
+ extrude into the "extend regions".
230
+ """
231
+
232
+ def __init__ (self , colorbar ):
233
+ """
234
+ ColorbarAutoLocator(colorbar)
235
+
236
+ This ticker needs to know the *colorbar* so that it can access
237
+ its *vmin* and *vmax*. Otherwise it is the same as
238
+ `~.ticker.AutoLocator`.
239
+ """
240
+
241
+ self ._colorbar = colorbar
242
+ nbins = 'auto'
243
+ steps = [1 , 2 , 2.5 , 5 , 10 ]
244
+ ticker .MaxNLocator .__init__ (self , nbins = nbins , steps = steps )
245
+
246
+ def tick_values (self , vmin , vmax ):
247
+ vmin = max (vmin , self ._colorbar .norm .vmin )
248
+ vmax = min (vmax , self ._colorbar .norm .vmax )
249
+ return ticker .MaxNLocator .tick_values (self , vmin , vmax )
250
+
251
+
252
+ class ColorbarLogLocator (ticker .LogLocator ):
253
+ """
254
+ LogLocator for Colorbarbar
255
+
256
+ This locator is just a `.LogLocator` except the min and max are
257
+ clipped by the norm's min and max (i.e. vmin/vmax from the
258
+ image/pcolor/contour object). This is necessary so ticks don't
259
+ extrude into the "extend regions".
260
+
261
+ """
262
+ def __init__ (self , colorbar , * args , ** kwargs ):
263
+ """
264
+ ColorbarLogLocator(colorbar, *args, **kwargs)
265
+
266
+ This ticker needs to know the *colorbar* so that it can access
267
+ its *vmin* and *vmax*. Otherwise it is the same as
268
+ `~.ticker.LogLocator`. The ``*args`` and ``**kwargs`` are the
269
+ same as `~.ticker.LogLocator`.
270
+ """
271
+ self ._colorbar = colorbar
272
+ ticker .LogLocator .__init__ (self , * args , ** kwargs )
273
+
274
+ def tick_values (self , vmin , vmax ):
275
+ vmin = self ._colorbar .norm .vmin
276
+ vmax = self ._colorbar .norm .vmax
277
+ ticks = ticker .LogLocator .tick_values (self , vmin , vmax )
278
+ return ticks [(ticks >= vmin ) & (ticks <= vmax )]
279
+
280
+
219
281
class ColorbarBase (cm .ScalarMappable ):
220
282
'''
221
283
Draw a colorbar in an existing axes.
@@ -346,8 +408,15 @@ def draw_all(self):
346
408
and do all the drawing.
347
409
'''
348
410
411
+ # sets self._boundaries and self._values in real data units.
412
+ # takes into account extend values:
349
413
self ._process_values ()
414
+ # sets self.vmin and vmax in data units, but just for
415
+ # the part of the colorbar that is not part of the extend
416
+ # patch:
350
417
self ._find_range ()
418
+ # returns the X and Y mesh, *but* this was/is in normalized
419
+ # units:
351
420
X , Y = self ._mesh ()
352
421
C = self ._values [:, np .newaxis ]
353
422
self ._config_axes (X , Y )
@@ -357,34 +426,119 @@ def draw_all(self):
357
426
def config_axis (self ):
358
427
ax = self .ax
359
428
if self .orientation == 'vertical' :
360
- ax .xaxis .set_ticks ([])
361
429
# location is either one of 'bottom' or 'top'
362
430
ax .yaxis .set_label_position (self .ticklocation )
363
431
ax .yaxis .set_ticks_position (self .ticklocation )
432
+ if (isinstance (self .norm , colors .LogNorm )
433
+ and self ._use_adjustable ()):
434
+ ax .set_xscale ('log' )
435
+ ax .set_yscale ('log' )
436
+ ax .xaxis .set_ticks ([])
437
+ ax .xaxis .set_ticks ([], minor = True )
438
+
364
439
else :
365
- ax .yaxis .set_ticks ([])
366
440
# location is either one of 'left' or 'right'
367
441
ax .xaxis .set_label_position (self .ticklocation )
368
442
ax .xaxis .set_ticks_position (self .ticklocation )
443
+ if (isinstance (self .norm , colors .LogNorm )
444
+ and self ._use_adjustable ()):
445
+ ax .set_xscale ('log' )
446
+ ax .set_yscale ('log' )
447
+ ax .yaxis .set_ticks ([])
448
+ ax .yaxis .set_ticks ([], minor = True )
369
449
370
450
self ._set_label ()
371
451
452
+ def _get_ticker_locator_formatter (self ):
453
+ """
454
+ This code looks at the norm being used by the colorbar
455
+ and decides what locator and formatter to use. If ``locator`` has
456
+ already been set by hand, it just returns
457
+ ``self.locator, self.formatter``.
458
+ """
459
+ locator = self .locator
460
+ formatter = self .formatter
461
+ if locator is None :
462
+ if self .boundaries is None :
463
+ if isinstance (self .norm , colors .NoNorm ):
464
+ nv = len (self ._values )
465
+ base = 1 + int (nv / 10 )
466
+ locator = ticker .IndexLocator (base = base , offset = 0 )
467
+ elif isinstance (self .norm , colors .BoundaryNorm ):
468
+ b = self .norm .boundaries
469
+ locator = ticker .FixedLocator (b , nbins = 10 )
470
+ elif isinstance (self .norm , colors .LogNorm ):
471
+ locator = ColorbarLogLocator (self )
472
+ elif isinstance (self .norm , colors .SymLogNorm ):
473
+ # The subs setting here should be replaced
474
+ # by logic in the locator.
475
+ locator = ticker .SymmetricalLogLocator (
476
+ subs = np .arange (1 , 10 ),
477
+ linthresh = self .norm .linthresh ,
478
+ base = 10 )
479
+ else :
480
+ if mpl .rcParams ['_internal.classic_mode' ]:
481
+ locator = ticker .MaxNLocator ()
482
+ else :
483
+ locator = ColorbarAutoLocator (self )
484
+ else :
485
+ b = self ._boundaries [self ._inside ]
486
+ locator = ticker .FixedLocator (b , nbins = 10 )
487
+ _log .debug ('locator: %r' , locator )
488
+ return locator , formatter
489
+
490
+ def _use_adjustable (self ):
491
+ """
492
+ Return if we should use an adjustable tick locator or a fixed
493
+ one. (check is used twice so factored out here...)
494
+ """
495
+ return (self .boundaries is None
496
+ and self .values is None
497
+ and ((type (self .norm ) == colors .Normalize )
498
+ or (type (self .norm ) == colors .LogNorm )))
499
+
372
500
def update_ticks (self ):
373
501
"""
374
502
Force the update of the ticks and ticklabels. This must be
375
503
called whenever the tick locator and/or tick formatter changes.
376
504
"""
377
505
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 )
506
+ # get the locator and formatter. Defaults to
507
+ # self.locator if not None..
508
+ locator , formatter = self ._get_ticker_locator_formatter ()
509
+
510
+ if self ._use_adjustable ():
511
+ _log .debug ('Using adjustable locator on colorbar' )
512
+ _log .debug ('locator: %r' , locator )
513
+ #self._find_range()
514
+ if self .orientation == 'vertical' :
515
+ ax .yaxis .set_major_locator (locator )
516
+ ax .yaxis .set_major_formatter (formatter )
517
+ if type (self .norm ) == colors .LogNorm :
518
+ ax .yaxis .set_minor_locator (ColorbarLogLocator (self ,
519
+ base = 10. , subs = 'auto' ))
520
+ ax .yaxis .set_minor_formatter (ticker .NullFormatter ())
521
+
522
+ else :
523
+ ax .xaxis .set_major_locator (locator )
524
+ ax .xaxis .set_major_formatter (formatter )
525
+ if type (self .norm ) == colors .LogNorm :
526
+ ax .xaxis .set_minor_locator (ColorbarLogLocator (self ,
527
+ base = 10. , subs = 'auto' ))
528
+ ax .xaxis .set_minor_formatter (ticker .NullFormatter ())
383
529
384
530
else :
385
- ax .xaxis .set_ticks (ticks )
386
- ax .set_xticklabels (ticklabels )
387
- ax .xaxis .get_major_formatter ().set_offset_string (offset_string )
531
+ _log .debug ('Using fixed locator on colorbar' )
532
+ ticks , ticklabels , offset_string = self ._ticker (locator , formatter )
533
+ if self .orientation == 'vertical' :
534
+ ax .yaxis .set_ticks (ticks )
535
+ ax .set_yticklabels (ticklabels )
536
+ ax .yaxis .get_major_formatter ().set_offset_string (offset_string )
537
+
538
+ else :
539
+ ax .xaxis .set_ticks (ticks )
540
+ ax .set_xticklabels (ticklabels )
541
+ ax .xaxis .get_major_formatter ().set_offset_string (offset_string )
388
542
389
543
def set_ticks (self , ticks , update_ticks = True ):
390
544
"""
@@ -520,6 +674,7 @@ def _add_solids(self, X, Y, C):
520
674
# since the axes object should already have hold set.
521
675
_hold = self .ax ._hold
522
676
self .ax ._hold = True
677
+ _log .debug ('Setting pcolormesh' )
523
678
col = self .ax .pcolormesh (* args , ** kw )
524
679
self .ax ._hold = _hold
525
680
#self.add_observer(col) # We should observe, not be observed...
@@ -574,39 +729,11 @@ def add_lines(self, levels, colors, linewidths, erase=True):
574
729
self .ax .add_collection (col )
575
730
self .stale = True
576
731
577
- def _ticker (self ):
732
+ def _ticker (self , locator , formatter ):
578
733
'''
579
734
Return the sequence of ticks (colorbar data locations),
580
735
ticklabels (strings), and the corresponding offset string.
581
736
'''
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 )
610
737
if isinstance (self .norm , colors .NoNorm ) and self .boundaries is None :
611
738
intv = self ._values [0 ], self ._values [- 1 ]
612
739
else :
@@ -851,12 +978,28 @@ def _mesh(self):
851
978
y = self ._uniform_y (self ._central_N ())
852
979
else :
853
980
y = self ._proportional_y ()
981
+ # if boundaries and values are None, then we can go ahead and
982
+ # scale this up for Auto tick location. Otherwise we
983
+ # want to keep normalized between 0 and 1 and use manual tick
984
+ # locations.
985
+ if self ._use_adjustable ():
986
+ y = self .norm .inverse (y )
987
+ x = self .norm .inverse (x )
988
+ else :
989
+ dy = 1.0
854
990
self ._y = y
991
+
855
992
X , Y = np .meshgrid (x , y )
856
993
if self ._extend_lower () and not self .extendrect :
857
- X [0 , :] = 0.5
994
+ if self ._use_adjustable ():
995
+ X [0 , :] = self .norm .inverse (0.5 )
996
+ else :
997
+ X [0 , :] = 0.5
858
998
if self ._extend_upper () and not self .extendrect :
859
- X [- 1 , :] = 0.5
999
+ if self ._use_adjustable ():
1000
+ X [- 1 , :] = self .norm .inverse (0.5 )
1001
+ else :
1002
+ X [- 1 , :] = 0.5
860
1003
return X , Y
861
1004
862
1005
def _locate (self , x ):
0 commit comments