3535import matplotlib .patches as mpatches
3636import matplotlib .path as mpath
3737import matplotlib .ticker as ticker
38+ import matplotlib .transforms as mtrans
3839
3940from matplotlib import docstring
4041
5253 *anchor* (0.0, 0.5) if vertical; (0.5, 1.0) if horizontal;
5354 the anchor point of the colorbar axes
5455 *panchor* (1.0, 0.5) if vertical; (0.5, 0.0) if horizontal;
55- the anchor point of the colorbar parent axes
56+ the anchor point of the colorbar parent axes. If
57+ False, the parent axes' anchor will be unchanged
5658 ============= ====================================================
5759
5860'''
149151 *cax*
150152 None | axes object into which the colorbar will be drawn
151153 *ax*
152- None | parent axes object from which space for a new
153- colorbar axes will be stolen
154+ None | parent axes object(s) from which space for a new
155+ colorbar axes will be stolen. If a list of axes is given
156+ they will all be resized to make room for the colorbar axes.
154157 *use_gridspec*
155158 False | If *cax* is None, a new *cax* is created as an instance of
156159 Axes. If *ax* is an instance of Subplot and *use_gridspec* is True,
@@ -255,6 +258,7 @@ def __init__(self, ax, cmap=None,
255258 values = None ,
256259 boundaries = None ,
257260 orientation = 'vertical' ,
261+ ticklocation = 'auto' ,
258262 extend = 'neither' ,
259263 spacing = 'uniform' , # uniform or proportional
260264 ticks = None ,
@@ -263,6 +267,7 @@ def __init__(self, ax, cmap=None,
263267 filled = True ,
264268 extendfrac = None ,
265269 extendrect = False ,
270+ label = '' ,
266271 ):
267272 self .ax = ax
268273 self ._patch_ax ()
@@ -287,7 +292,12 @@ def __init__(self, ax, cmap=None,
287292 self .outline = None
288293 self .patch = None
289294 self .dividers = None
290- self .set_label ('' )
295+
296+ if ticklocation == 'auto' :
297+ ticklocation = 'bottom' if orientation == 'horizontal' else 'right'
298+ self .ticklocation = ticklocation
299+
300+ self .set_label (label )
291301 if cbook .iterable (ticks ):
292302 self .locator = ticker .FixedLocator (ticks , nbins = len (ticks ))
293303 else :
@@ -336,11 +346,14 @@ def config_axis(self):
336346 ax = self .ax
337347 if self .orientation == 'vertical' :
338348 ax .xaxis .set_ticks ([])
339- ax .yaxis .set_label_position ('right' )
340- ax .yaxis .set_ticks_position ('right' )
349+ # location is either one of 'bottom' or 'top'
350+ ax .yaxis .set_label_position (self .ticklocation )
351+ ax .yaxis .set_ticks_position (self .ticklocation )
341352 else :
342353 ax .yaxis .set_ticks ([])
343- ax .xaxis .set_label_position ('bottom' )
354+ # location is either one of 'left' or 'right'
355+ ax .xaxis .set_label_position (self .ticklocation )
356+ ax .xaxis .set_ticks_position (self .ticklocation )
344357
345358 self ._set_label ()
346359
@@ -835,11 +848,10 @@ class Colorbar(ColorbarBase):
835848
836849 """
837850 def __init__ (self , ax , mappable , ** kw ):
838- mappable .autoscale_None () # Ensure mappable.norm.vmin, vmax
839- # are set when colorbar is called,
840- # even if mappable.draw has not yet
841- # been called. This will not change
842- # vmin, vmax if they are already set.
851+ # Ensure the given mappable's norm has appropriate vmin and vmax set
852+ # even if mappable.draw has not yet been called.
853+ mappable .autoscale_None ()
854+
843855 self .mappable = mappable
844856 kw ['cmap' ] = mappable .cmap
845857 kw ['norm' ] = mappable .norm
@@ -948,47 +960,118 @@ def update_bruteforce(self, mappable):
948960
949961
950962@docstring .Substitution (make_axes_kw_doc )
951- def make_axes (parent , ** kw ):
963+ def make_axes (parents , location = None , orientation = None , fraction = 0.15 ,
964+ shrink = 1.0 , aspect = 20 , ** kw ):
952965 '''
953- Resize and reposition a parent axes, and return a child
966+ Resize and reposition parent axes, and return a child
954967 axes suitable for a colorbar::
955968
956969 cax, kw = make_axes(parent, **kw)
957970
958971 Keyword arguments may include the following (with defaults):
959972
960- *orientation*
961- 'vertical' or 'horizontal'
973+ *location*: [**None**|'left'|'right'|'top'|'bottom']
974+ The position, relative to **parents**, where the colorbar axes
975+ should be created. If None, the value will either come from the
976+ given **orientation**, else it will default to 'right'.
962977
963- %s
978+ *orientation*: [**None**|'vertical'|'horizontal']
979+ The orientation of the colorbar. Typically, this keyword shouldn't
980+ be used, as it can be derived from the **location** keyword.
964981
965- All but the first of these are stripped from the input kw set.
982+ %s
966983
967- Returns (cax, kw), the child axes and the reduced kw dictionary.
984+ Returns (cax, kw), the child axes and the reduced kw dictionary to be
985+ passed when creating the colorbar instance.
968986 '''
969- orientation = kw .setdefault ('orientation' , 'vertical' )
970- fraction = kw .pop ('fraction' , 0.15 )
971- shrink = kw .pop ('shrink' , 1.0 )
972- aspect = kw .pop ('aspect' , 20 )
973- #pb = transforms.PBox(parent.get_position())
974- pb = parent .get_position (original = True ).frozen ()
975- if orientation == 'vertical' :
976- pad = kw .pop ('pad' , 0.05 )
977- x1 = 1.0 - fraction
978- pb1 , pbx , pbcb = pb .splitx (x1 - pad , x1 )
979- pbcb = pbcb .shrunk (1.0 , shrink ).anchored ('C' , pbcb )
980- anchor = kw .pop ('anchor' , (0.0 , 0.5 ))
981- panchor = kw .pop ('panchor' , (1.0 , 0.5 ))
987+ locations = ["left" , "right" , "top" , "bottom" ]
988+ if orientation is not None and location is not None :
989+ raise TypeError ('position and orientation are mutually exclusive. Consider ' \
990+ 'setting the position to any of %s' % ', ' .join (locations ))
991+
992+ # provide a default location
993+ if location is None and orientation is None :
994+ location = 'right'
995+
996+ # allow the user to not specify the location by specifying the orientation instead
997+ if location is None :
998+ location = 'right' if orientation == 'vertical' else 'bottom'
999+
1000+ if location not in locations :
1001+ raise ValueError ('Invalid colorbar location. Must be one of %s' % ', ' .join (locations ))
1002+
1003+ default_location_settings = {'left' : {'anchor' : (1.0 , 0.5 ),
1004+ 'panchor' : (0.0 , 0.5 ),
1005+ 'pad' : 0.10 ,
1006+ 'orientation' : 'vertical' },
1007+ 'right' : {'anchor' : (0.0 , 0.5 ),
1008+ 'panchor' : (1.0 , 0.5 ),
1009+ 'pad' : 0.05 ,
1010+ 'orientation' : 'vertical' },
1011+ 'top' : {'anchor' : (0.5 , 0.0 ),
1012+ 'panchor' : (0.5 , 1.0 ),
1013+ 'pad' : 0.05 ,
1014+ 'orientation' : 'horizontal' },
1015+ 'bottom' : {'anchor' : (0.5 , 1.0 ),
1016+ 'panchor' : (0.5 , 0.0 ),
1017+ 'pad' : 0.15 , # backwards compat
1018+ 'orientation' : 'horizontal' },
1019+ }
1020+
1021+ loc_settings = default_location_settings [location ]
1022+
1023+ # put appropriate values into the kw dict for passing back to
1024+ # the Colorbar class
1025+ kw ['orientation' ] = loc_settings ['orientation' ]
1026+ kw ['ticklocation' ] = location
1027+
1028+ anchor = kw .pop ('anchor' , loc_settings ['anchor' ])
1029+ parent_anchor = kw .pop ('panchor' , loc_settings ['panchor' ])
1030+ pad = kw .pop ('pad' , loc_settings ['pad' ])
1031+
1032+
1033+ # turn parents into a list if it is not already
1034+ if not isinstance (parents , (list , tuple )):
1035+ parents = [parents ]
1036+
1037+ fig = parents [0 ].get_figure ()
1038+ if not all (fig is ax .get_figure () for ax in parents ):
1039+ raise ValueError ('Unable to create a colorbar axes as not all ' + \
1040+ 'parents share the same figure.' )
1041+
1042+ # take a bounding box around all of the given axes
1043+ parents_bbox = mtrans .Bbox .union ([ax .get_position (original = True ).frozen () \
1044+ for ax in parents ])
1045+
1046+ pb = parents_bbox
1047+ if location in ('left' , 'right' ):
1048+ if location == 'left' :
1049+ pbcb , _ , pb1 = pb .splitx (fraction , fraction + pad )
1050+ else :
1051+ pb1 , _ , pbcb = pb .splitx (1 - fraction - pad , 1 - fraction )
1052+ pbcb = pbcb .shrunk (1.0 , shrink ).anchored (anchor , pbcb )
9821053 else :
983- pad = kw .pop ('pad' , 0.15 )
984- pbcb , pbx , pb1 = pb .splity (fraction , fraction + pad )
985- pbcb = pbcb .shrunk (shrink , 1.0 ).anchored ('C' , pbcb )
986- aspect = 1.0 / aspect
987- anchor = kw .pop ('anchor' , (0.5 , 1.0 ))
988- panchor = kw .pop ('panchor' , (0.5 , 0.0 ))
989- parent .set_position (pb1 )
990- parent .set_anchor (panchor )
991- fig = parent .get_figure ()
1054+ if location == 'bottom' :
1055+ pbcb , _ , pb1 = pb .splity (fraction , fraction + pad )
1056+ else :
1057+ pb1 , _ , pbcb = pb .splity (1 - fraction - pad , 1 - fraction )
1058+ pbcb = pbcb .shrunk (shrink , 1.0 ).anchored (anchor , pbcb )
1059+
1060+ # define the aspect ratio in terms of y's per x rather than x's per y
1061+ aspect = 1.0 / aspect
1062+
1063+ # define a transform which takes us from old axes coordinates to
1064+ # new axes coordinates
1065+ shrinking_trans = mtrans .BboxTransform (parents_bbox , pb1 )
1066+
1067+ # transform each of the axes in parents using the new transform
1068+ for ax in parents :
1069+ new_posn = shrinking_trans .transform (ax .get_position ())
1070+ new_posn = mtrans .Bbox (new_posn )
1071+ ax .set_position (new_posn )
1072+ if parent_anchor is not False :
1073+ ax .set_anchor (parent_anchor )
1074+
9921075 cax = fig .add_axes (pbcb )
9931076 cax .set_aspect (aspect , anchor = anchor , adjustable = 'box' )
9941077 return cax , kw
@@ -1001,6 +1084,9 @@ def make_axes_gridspec(parent, **kw):
10011084 suitable for a colorbar. This function is similar to
10021085 make_axes. Prmary differences are
10031086
1087+ * *make_axes_gridspec* only handles the *orientation* keyword
1088+ and cannot handle the "location" keyword.
1089+
10041090 * *make_axes_gridspec* should only be used with a subplot parent.
10051091
10061092 * *make_axes* creates an instance of Axes. *make_axes_gridspec*
@@ -1018,16 +1104,19 @@ def make_axes_gridspec(parent, **kw):
10181104 Keyword arguments may include the following (with defaults):
10191105
10201106 *orientation*
1021- 'vertical' or 'horizontal'
1107+ 'vertical' or 'horizontal'
10221108
10231109 %s
10241110
10251111 All but the first of these are stripped from the input kw set.
10261112
1027- Returns (cax, kw), the child axes and the reduced kw dictionary.
1113+ Returns (cax, kw), the child axes and the reduced kw dictionary to be
1114+ passed when creating the colorbar instance.
10281115 '''
10291116
10301117 orientation = kw .setdefault ('orientation' , 'vertical' )
1118+ kw ['ticklocation' ] = 'auto'
1119+
10311120 fraction = kw .pop ('fraction' , 0.15 )
10321121 shrink = kw .pop ('shrink' , 1.0 )
10331122 aspect = kw .pop ('aspect' , 20 )
@@ -1139,11 +1228,8 @@ def _add_solids(self, X, Y, C):
11391228
11401229 patch = mpatches .PathPatch (mpath .Path (xy ),
11411230 facecolor = self .cmap (self .norm (val )),
1142- hatch = hatch ,
1143- edgecolor = 'none' , linewidth = 0 ,
1144- antialiased = False , ** kw
1145- )
1146-
1231+ hatch = hatch , linewidth = 0 ,
1232+ antialiased = False , ** kw )
11471233 self .ax .add_patch (patch )
11481234 patches .append (patch )
11491235
@@ -1158,12 +1244,9 @@ def _add_solids(self, X, Y, C):
11581244 self .dividers = None
11591245
11601246 if self .drawedges :
1161- self .dividers = collections .LineCollection (
1162- self ._edges (X , Y ),
1163- colors = (mpl .rcParams ['axes.edgecolor' ],),
1164- linewidths = (
1165- 0.5 * mpl .rcParams ['axes.linewidth' ],)
1166- )
1247+ self .dividers = collections .LineCollection (self ._edges (X , Y ),
1248+ colors = (mpl .rcParams ['axes.edgecolor' ],),
1249+ linewidths = (0.5 * mpl .rcParams ['axes.linewidth' ],))
11671250 self .ax .add_collection (self .dividers )
11681251
11691252 self .ax .hold (_hold )
0 commit comments