5959
6060colormap_kw_doc = '''
6161
62- =========== ====================================================
62+ ============ ====================================================
6363 Property Description
64- =========== ====================================================
64+ ============ ====================================================
6565 *extend* [ 'neither' | 'both' | 'min' | 'max' ]
6666 If not 'neither', make pointed end(s) for out-of-
6767 range values. These are set for a given colormap
6868 using the colormap set_under and set_over methods.
69+ *extendfrac* [ *None* | 'auto' | length | lengths ]
70+ If set to *None*, both the minimum and maximum
71+ triangular colorbar extensions with have a length of
72+ 5% of the interior colorbar length (this is the
73+ default setting). If set to 'auto', makes the
74+ triangular colorbar extensions the same lengths as
75+ the interior boxes (when *spacing* is set to
76+ 'uniform') or the same lengths as the respective
77+ adjacent interior boxes (when *spacing* is set to
78+ 'proportional'). If a scalar, indicates the length
79+ of both the minimum and maximum triangular colorbar
80+ extensions as a fraction of the interior colorbar
81+ length. A two-element sequence of fractions may also
82+ be given, indicating the lengths of the minimum and
83+ maximum colorbar extensions respectively as a
84+ fraction of the interior colorbar length.
6985 *spacing* [ 'uniform' | 'proportional' ]
7086 Uniform spacing gives each discrete color the same
7187 space; proportional makes the space proportional to
8298 given instead.
8399 *drawedges* [ False | True ] If true, draw lines at color
84100 boundaries.
85- =========== ====================================================
101+ ============ ====================================================
86102
87103 The following will probably be useful only in the context of
88104 indexed colors (that is, when the mappable has norm=NoNorm()),
@@ -221,6 +237,7 @@ def __init__(self, ax, cmap=None,
221237 format = None ,
222238 drawedges = False ,
223239 filled = True ,
240+ extendfrac = None ,
224241 ):
225242 self .ax = ax
226243 self ._patch_ax ()
@@ -236,6 +253,7 @@ def __init__(self, ax, cmap=None,
236253 self .orientation = orientation
237254 self .drawedges = drawedges
238255 self .filled = filled
256+ self .extendfrac = extendfrac
239257 self .solids = None
240258 self .lines = None
241259 self .outline = None
@@ -616,6 +634,35 @@ def _extended_N(self):
616634 N += 1
617635 return N
618636
637+ def _get_extension_lengths (self , frac , automin , automax , default = 0.05 ):
638+ '''
639+ Get the lengths of colorbar extensions.
640+
641+ A helper method for _uniform_y and _proportional_y.
642+ '''
643+ # Set the default value.
644+ extendlength = np .array ([default , default ])
645+ if isinstance (frac , str ):
646+ if frac .lower () == 'auto' :
647+ # Use the provided values when 'auto' is required.
648+ extendlength [0 ] = automin
649+ extendlength [1 ] = automax
650+ else :
651+ # Any other string is invalid.
652+ raise ValueError ('invalid value for extendfrac' )
653+ elif frac is not None :
654+ try :
655+ # Try to set min and max extension fractions directly.
656+ extendlength [:] = frac
657+ # If frac is a sequence contaning None then NaN may
658+ # be encountered. This is an error.
659+ if np .isnan (extendlength ).any ():
660+ raise ValueError ()
661+ except (TypeError , ValueError ):
662+ # Raise an error on encountering an invalid value for frac.
663+ raise ValueError ('invalid value for extendfrac' )
664+ return extendlength
665+
619666 def _uniform_y (self , N ):
620667 '''
621668 Return colorbar data coordinates for *N* uniformly
@@ -624,16 +671,19 @@ def _uniform_y(self, N):
624671 if self .extend == 'neither' :
625672 y = np .linspace (0 , 1 , N )
626673 else :
674+ automin = automax = 1. / (N - 1. )
675+ extendlength = self ._get_extension_lengths (self .extendfrac ,
676+ automin , automax , default = 0.05 )
627677 if self .extend == 'both' :
628678 y = np .zeros (N + 2 , 'd' )
629- y [0 ] = - 0.05
630- y [- 1 ] = 1.05
679+ y [0 ] = 0. - extendlength [ 0 ]
680+ y [- 1 ] = 1. + extendlength [ 1 ]
631681 elif self .extend == 'min' :
632682 y = np .zeros (N + 1 , 'd' )
633- y [0 ] = - 0.05
683+ y [0 ] = 0. - extendlength [ 0 ]
634684 else :
635685 y = np .zeros (N + 1 , 'd' )
636- y [- 1 ] = 1.05
686+ y [- 1 ] = 1. + extendlength [ 1 ]
637687 y [self ._inside ] = np .linspace (0 , 1 , N )
638688 return y
639689
@@ -648,10 +698,27 @@ def _proportional_y(self):
648698 y = y / (self ._boundaries [- 1 ] - self ._boundaries [0 ])
649699 else :
650700 y = self .norm (self ._boundaries .copy ())
651- if self ._extend_lower ():
652- y [0 ] = - 0.05
653- if self ._extend_upper ():
654- y [- 1 ] = 1.05
701+ if self .extend == 'min' :
702+ # Exclude leftmost interval of y.
703+ clen = y [- 1 ] - y [1 ]
704+ automin = (y [2 ] - y [1 ]) / clen
705+ automax = (y [- 1 ] - y [- 2 ]) / clen
706+ elif self .extend == 'max' :
707+ # Exclude rightmost interval in y.
708+ clen = y [- 2 ] - y [0 ]
709+ automin = (y [1 ] - y [0 ]) / clen
710+ automax = (y [- 2 ] - y [- 3 ]) / clen
711+ else :
712+ # Exclude leftmost and rightmost intervals in y.
713+ clen = y [- 2 ] - y [1 ]
714+ automin = (y [2 ] - y [1 ]) / clen
715+ automax = (y [- 2 ] - y [- 3 ]) / clen
716+ extendlength = self ._get_extension_lengths (self .extendfrac ,
717+ automin , automax , default = 0.05 )
718+ if self .extend in ('both' , 'min' ):
719+ y [0 ] = 0. - extendlength [0 ]
720+ if self .extend in ('both' , 'max' ):
721+ y [- 1 ] = 1. + extendlength [1 ]
655722 yi = y [self ._inside ]
656723 norm = colors .Normalize (yi [0 ], yi [- 1 ])
657724 y [self ._inside ] = norm (yi )
0 commit comments