66from collections .abc import Sequence
77from typing import Literal
88
9- from pygmt ._typing import PathLike
9+ from pygmt ._typing import AnchorCode , PathLike
1010from pygmt .alias import Alias , AliasSystem
1111from pygmt .clib import Session
1212from pygmt .exceptions import GMTTypeError
13- from pygmt .helpers import (
14- build_arg_list ,
15- data_kind ,
16- fmt_docstring ,
17- is_nonstr_iter ,
18- use_alias ,
19- )
20- from pygmt .params import Box
13+ from pygmt .helpers import build_arg_list , data_kind , fmt_docstring , is_nonstr_iter
14+ from pygmt .params import Box , Position
15+ from pygmt .src ._common import _parse_position
2116
2217
2318@fmt_docstring
24- @use_alias (D = "position" )
2519def legend ( # noqa: PLR0913
2620 self ,
2721 spec : PathLike | io .StringIO | None = None ,
28- scale : float | None = None ,
29- position = "JTR+jTR+o0.2c" ,
22+ position : Position | Sequence [float | str ] | AnchorCode | None = None ,
23+ width : float | str | None = None ,
24+ height : float | str | None = None ,
25+ line_spacing : float | None = None ,
3026 box : Box | bool = False ,
27+ scale : float | None = None ,
3128 projection : str | None = None ,
3229 region : Sequence [float | str ] | str | None = None ,
3330 frame : str | Sequence [str ] | bool = False ,
3431 verbose : Literal ["quiet" , "error" , "warning" , "timing" , "info" , "compat" , "debug" ]
3532 | bool = False ,
3633 panel : int | Sequence [int ] | bool = False ,
37- transparency : float | None = None ,
3834 perspective : float | Sequence [float ] | str | bool = False ,
35+ transparency : float | None = None ,
3936 ** kwargs ,
4037):
41- r """
38+ """
4239 Plot a legend.
4340
44- Makes legends that can be overlaid on maps. Reads specific
45- legend-related information from an input file, or automatically creates
46- legend entries from plotted symbols that have labels. Unless otherwise
47- noted, annotations will be made using the primary annotation font and
48- size in effect (i.e., :gmt-term:`FONT_ANNOT_PRIMARY`).
41+ Makes legends that can be overlaid on plots. It reads specific legend-related
42+ information from an input file, a :class:`io.StringIO` object, or automatically
43+ creates legend entries from plotted symbols that have labels. Unless otherwise
44+ noted, annotations will be made using the primary annotation font and size in effect
45+ (i.e., :gmt-term:`FONT_ANNOT_PRIMARY`).
4946
5047 Full GMT docs at :gmt-docs:`legend.html`.
5148
52- $aliases
49+ **Aliases:**
50+
51+ .. hlist::
52+ :columns: 3
53+
54+ - D = position, **+w**: width/height, **+l**: line_spacing
5355 - B = frame
5456 - F = box
5557 - J = projection
@@ -71,13 +73,32 @@ def legend( # noqa: PLR0913
7173 - A :class:`io.StringIO` object containing the legend specification
7274
7375 See :gmt-docs:`legend.html` for the definition of the legend specification.
74- position : str
75- [**g**\|\ **j**\|\ **J**\|\ **n**\|\ **x**]\ *refpoint*\
76- **+w**\ *width*\ [/*height*]\ [**+j**\ *justify*]\ [**+l**\ *spacing*]\
77- [**+o**\ *dx*\ [/*dy*]].
78- Define the reference point on the map for the legend. By default, uses
79- **JTR**\ **+jTR**\ **+o**\ 0.2c which places the legend at the Top Right corner
80- inside the map frame, with a 0.2 cm offset.
76+ position
77+ Position of the legend on the plot. It can be specified in multiple ways:
78+
79+ - A :class:`pygmt.params.Position` object to fully control the reference point,
80+ anchor point, and offset.
81+ - A sequence of two values representing the x- and y-coordinates in plot
82+ coordinates, e.g., ``(1, 2)`` or ``("1c", "2c")``.
83+ - A :doc:`2-character justification code </techref/justification_codes>` for a
84+ position inside the plot, e.g., ``"TL"`` for Top Left corner inside the plot.
85+
86+ If not specified, defaults to the Top Right corner inside the plot with a 0.2-cm
87+ offset.
88+ width
89+ height
90+ Width and height of the legend box. If not given, the width and height are
91+ computed automatically based on the contents of the legend specification. If
92+ unit is ``%`` (percentage) then width is computed as that fraction of the plot
93+ width. If height is given as percentage then height is recomputed as that
94+ fraction of the legend width (not plot height).
95+
96+ **Note:** Currently, the automatic height calculation only works when legend
97+ codes **D**, **H**, **L**, **S**, or **V** are used and that the number of
98+ symbol columns (**N**) is 1.
99+ line_spacing
100+ The line-spacing factor between legend entries in units of the current font size
101+ [Default is 1.1].
81102 box
82103 Draw a background box behind the legend. If set to ``True``, a simple
83104 rectangular box is drawn using :gmt-term:`MAP_FRAME_PEN`. To customize the box
@@ -95,11 +116,20 @@ def legend( # noqa: PLR0913
95116 """
96117 self ._activate_figure ()
97118
98- # Default position and box when not specified.
99- if kwargs .get ("D" ) is None :
100- kwargs ["D" ] = position
101- if box is False and kwargs .get ("F" ) is None :
102- box = Box (pen = "1p" , fill = "white" ) # Default box
119+ # Set default box if both position and box are not given.
120+ # The default position will be set later in _parse_position().
121+ if kwargs .get ("D" , position ) is None and kwargs .get ("F" , box ) is False :
122+ box = Box (pen = "1p" , fill = "white" )
123+
124+ position = _parse_position (
125+ position ,
126+ kwdict = {"width" : width , "height" : height , "line_spacing" : line_spacing },
127+ default = Position ("TR" , offset = 0.2 ), # Default to TR with 0.2-cm offset.
128+ )
129+
130+ # Set width to 0 (auto calculated) if height is given but width is not.
131+ if height is not None and width is None :
132+ width = 0
103133
104134 kind = data_kind (spec )
105135 if kind not in {"empty" , "file" , "stringio" }:
@@ -110,6 +140,12 @@ def legend( # noqa: PLR0913
110140 )
111141
112142 aliasdict = AliasSystem (
143+ D = [
144+ Alias (position , name = "position" ),
145+ Alias (width , name = "width" , prefix = "+w" ), # +wwidth/height
146+ Alias (height , name = "height" , prefix = "/" ),
147+ Alias (line_spacing , name = "line_spacing" , prefix = "+l" ),
148+ ],
113149 F = Alias (box , name = "box" ),
114150 S = Alias (scale , name = "scale" ),
115151 ).add_common (
0 commit comments