2626
2727import numpy as np
2828
29+
2930from matplotlib import rcParams
3031from matplotlib import cbook , docstring
3132from matplotlib .artist import Artist , allow_rasterization
32- from matplotlib .cbook import silent_list , is_hashable , warn_deprecated
33+ from matplotlib .cbook import (silent_list , is_hashable , warn_deprecated ,
34+ _valid_compass , _map_loc_to_compass )
3335from matplotlib .font_manager import FontProperties
3436from matplotlib .lines import Line2D
3537from matplotlib .patches import Patch , Rectangle , Shadow , FancyBboxPatch
@@ -113,23 +115,23 @@ def _update_bbox_to_anchor(self, loc_in_canvas):
113115loc : int or string or pair of floats, default: :rc:`legend.loc` ('best' for \
114116 axes, 'upper right' for figures)
115117 The location of the legend.
116- Possible (case-insensitive ) strings and codes are:
117-
118- =============== ============== =============
119- Location String Compass String Location Code
120- =============== ============== =============
121- 'best' 0
122- 'upper right ' 'NE' 1
123- 'upper left ' 'NW ' 2
124- 'lower left ' 'SW ' 3
125- 'lower right ' 'SE' 4
126- 'right' 5
127- 'center left ' 'W ' 6
128- 'center right ' 'E ' 7
129- 'lower center ' 'S ' 8
130- 'upper center ' 'N ' 9
131- 'center ' 'C ' 10
132- =============== ============== =============
118+ Possible (case-sensitive ) strings and codes are:
119+
120+ ============ ============== = ============== =============
121+ Compass Code Compass String Location String Location Code
122+ ============ ============== = ============== =============
123+ .. 'best' 0
124+ 'NE ' 'northeast' 'upper right' 1
125+ 'NW ' 'northwest ' 'upper left' 2
126+ 'SW ' 'southwest ' 'lower left' 3
127+ 'SE ' 'southeast' 'lower right' 4
128+ .. 'right' 5
129+ 'W ' 'west ' 'center left' 6
130+ 'E ' 'east ' 'center right' 7
131+ 'S ' 'south ' 'lower center' 8
132+ 'N ' 'north ' 'upper center' 9
133+ 'C ' 'center ' 'center' 10
134+ ============ ============== = ============== =============
133135
134136
135137 Alternatively can be a 2-tuple giving ``x, y`` of the lower-left
@@ -309,21 +311,7 @@ class Legend(Artist):
309311 Place a legend on the axes at location loc.
310312
311313 """
312- codes = {'best' : 0 , # only implemented for axes legends
313- 'upper right' : 1 ,
314- 'upper left' : 2 ,
315- 'lower left' : 3 ,
316- 'lower right' : 4 ,
317- 'right' : 5 ,
318- 'center left' : 6 ,
319- 'center right' : 7 ,
320- 'lower center' : 8 ,
321- 'upper center' : 9 ,
322- 'center' : 10 ,
323- }
324- compasscodes = {'nw' : 2 , 'n' : 9 , 'ne' : 1 , 'w' : 6 , 'c' : 10 , 'e' : 7 ,
325- 'sw' : 3 , 's' : 8 , 'se' : 4 }
326- allcodes = {** codes , ** compasscodes }
314+
327315 zorder = 5
328316
329317 def __str__ (self ):
@@ -487,35 +475,6 @@ def __init__(self, parent, handles, labels,
487475 raise TypeError ("Legend needs either Axes or Figure as parent" )
488476 self .parent = parent
489477
490- if loc is None :
491- loc = rcParams ["legend.loc" ]
492- if not self .isaxes and loc in [0 , 'best' ]:
493- loc = 'upper right'
494- if isinstance (loc , str ):
495- if loc .lower () not in self .allcodes :
496- if self .isaxes :
497- cbook .warn_deprecated (
498- "3.1" , message = "Unrecognized location {!r}. Falling "
499- "back on 'best'; valid locations are\n \t {}\n "
500- "This will raise an exception %(removal)s."
501- .format (loc , '\n \t ' .join (self .allcodes )))
502- loc = 0
503- else :
504- cbook .warn_deprecated (
505- "3.1" , message = "Unrecognized location {!r}. Falling "
506- "back on 'upper right'; valid locations are\n \t {}\n '"
507- "This will raise an exception %(removal)s."
508- .format (loc , '\n \t ' .join (self .allcodes )))
509- loc = 1
510- else :
511- loc = self .allcodes [loc .lower ()]
512- if not self .isaxes and loc == 0 :
513- cbook .warn_deprecated (
514- "3.1" , message = "Automatic legend placement (loc='best') not "
515- "implemented for figure legend. Falling back on 'upper "
516- "right'. This will raise an exception %(removal)s." )
517- loc = 1
518-
519478 self ._mode = mode
520479 self .set_bbox_to_anchor (bbox_to_anchor , bbox_transform )
521480
@@ -561,6 +520,9 @@ def __init__(self, parent, handles, labels,
561520 # init with null renderer
562521 self ._init_legend_box (handles , labels , markerfirst )
563522
523+ # location must be set after _legend_box is created.
524+ self ._loc = loc
525+
564526 # If shadow is activated use framealpha if not
565527 # explicitly passed. See Issue 8943
566528 if framealpha is None :
@@ -571,7 +533,6 @@ def __init__(self, parent, handles, labels,
571533 else :
572534 self .get_frame ().set_alpha (framealpha )
573535
574- self ._loc = loc
575536 # figure out title fontsize:
576537 if title_fontsize is None :
577538 title_fontsize = rcParams ['legend.title_fontsize' ]
@@ -592,6 +553,16 @@ def _set_artist_props(self, a):
592553 a .set_transform (self .get_transform ())
593554
594555 def _set_loc (self , loc ):
556+ if loc is None :
557+ loc = rcParams ["legend.loc" ]
558+ if not self .isaxes and loc in [0 , 'best' ]:
559+ loc = 'NE'
560+ if self .isaxes :
561+ loc = _map_loc_to_compass (loc , allowtuple = True , allowbest = True ,
562+ fallback = "best" , warnonly = True )
563+ else :
564+ loc = _map_loc_to_compass (loc , allowtuple = True , allowbest = False ,
565+ fallback = "NE" , warnonly = True )
595566 # find_offset function will be provided to _legend_box and
596567 # _legend_box will draw itself at the location of the return
597568 # value of the find_offset.
@@ -604,12 +575,26 @@ def _get_loc(self):
604575
605576 _loc = property (_get_loc , _set_loc )
606577
578+ def set_loc (self , loc ):
579+ """
580+ Set the legend location. For possible values see the `~.Axes.legend`
581+ docstring.
582+ """
583+ self ._set_loc (loc )
584+
585+ def get_loc (self ):
586+ """
587+ Get the legend location. This will be one of 'best', 'NE', 'NW',
588+ 'SW', 'SE', 'E', 'W', 'E', 'S', 'N', 'C' or a tuple of floats.
589+ """
590+ return self ._get_loc ()
591+
607592 def _findoffset (self , width , height , xdescent , ydescent , renderer ):
608593 "Helper function to locate the legend."
609594
610- if self ._loc == 0 : # "best".
595+ if self ._loc == 'best' : # "best".
611596 x , y = self ._find_best_position (width , height , renderer )
612- elif self ._loc in Legend . codes . values ( ): # Fixed location.
597+ elif isinstance ( self ._loc , str ): # Fixed location.
613598 bbox = Bbox .from_bounds (0 , 0 , width , height )
614599 x , y = self ._get_anchored_bbox (self ._loc , bbox ,
615600 self .get_bbox_to_anchor (),
@@ -1079,26 +1064,12 @@ def _get_anchored_bbox(self, loc, bbox, parentbbox, renderer):
10791064 - parentbbox: a parent box which will contain the bbox. In
10801065 display coordinates.
10811066 """
1082- assert loc in range (1 , 11 ) # called only internally
1083-
1084- BEST , UR , UL , LL , LR , R , CL , CR , LC , UC , C = range (11 )
1085-
1086- anchor_coefs = {UR : "NE" ,
1087- UL : "NW" ,
1088- LL : "SW" ,
1089- LR : "SE" ,
1090- R : "E" ,
1091- CL : "W" ,
1092- CR : "E" ,
1093- LC : "S" ,
1094- UC : "N" ,
1095- C : "C" }
10961067
1097- c = anchor_coefs [ loc ]
1068+ assert loc in _valid_compass # as this is called only internally
10981069
10991070 fontsize = renderer .points_to_pixels (self ._fontsize )
11001071 container = parentbbox .padded (- (self .borderaxespad ) * fontsize )
1101- anchored_box = bbox .anchored (c , container = container )
1072+ anchored_box = bbox .anchored (loc , container = container )
11021073 return anchored_box .x0 , anchored_box .y0
11031074
11041075 def _find_best_position (self , width , height , renderer , consider = None ):
@@ -1115,10 +1086,10 @@ def _find_best_position(self, width, height, renderer, consider=None):
11151086
11161087 bbox = Bbox .from_bounds (0 , 0 , width , height )
11171088 if consider is None :
1118- consider = [self ._get_anchored_bbox (x , bbox ,
1089+ consider = [self ._get_anchored_bbox (loc , bbox ,
11191090 self .get_bbox_to_anchor (),
11201091 renderer )
1121- for x in range ( 1 , len ( self . codes )) ]
1092+ for loc in _valid_compass ]
11221093
11231094 candidates = []
11241095 for idx , (l , b ) in enumerate (consider ):
@@ -1199,7 +1170,7 @@ def draggable(self, state=None, use_blit=False, update="loc"):
11991170 is changed. If "bbox", the *bbox_to_anchor* parameter is changed.
12001171 """
12011172 warn_deprecated ("2.2" ,
1202- message = "Legend.draggable() is drepecated in "
1173+ message = "Legend.draggable() is deprecated in "
12031174 "favor of Legend.set_draggable(). "
12041175 "Legend.draggable may be reintroduced as a "
12051176 "property in future releases." )
0 commit comments