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
12- from pygmt .exceptions import GMTInvalidInput , GMTTypeError
12+ from pygmt .exceptions import GMTTypeError
1313from pygmt .helpers import build_arg_list , data_kind , fmt_docstring , is_nonstr_iter
1414from pygmt .params import Box , Position
15+ from pygmt .src ._common import _parse_position
1516
1617
1718@fmt_docstring
1819def legend ( # noqa: PLR0913
1920 self ,
2021 spec : PathLike | io .StringIO | None = None ,
21- position : Position | None = None ,
22+ position : Position | Sequence [ float | str ] | AnchorCode | None = None ,
2223 width : float | str | None = None ,
2324 height : float | str | None = None ,
2425 line_spacing : float | None = None ,
@@ -30,8 +31,8 @@ def legend( # noqa: PLR0913
3031 verbose : Literal ["quiet" , "error" , "warning" , "timing" , "info" , "compat" , "debug" ]
3132 | bool = False ,
3233 panel : int | Sequence [int ] | bool = False ,
33- transparency : float | None = None ,
3434 perspective : float | Sequence [float ] | str | bool = False ,
35+ transparency : float | None = None ,
3536 ** kwargs ,
3637):
3738 """
@@ -73,25 +74,31 @@ def legend( # noqa: PLR0913
7374
7475 See :gmt-docs:`legend.html` for the definition of the legend specification.
7576 position
76- Specify the position of the legend on the plot. If not specified, defaults to
77- the top right corner inside the plot with a 0.2-cm offset. See
78- :class:`pygmt.params.Position` for details.
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.
7988 width
8089 height
81- Specify the width and height of the legend box in plot coordinates (inches, cm,
82- etc.). If not given, the width and height are computed automatically based on
83- the contents of the legend specification.
84-
85- If unit is ``%`` (percentage) then width is computed as that fraction of the
86- plot width. If height is given as percentage then height is recomputed as that
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
8794 fraction of the legend width (not plot height).
8895
8996 **Note:** Currently, the automatic height calculation only works when legend
9097 codes **D**, **H**, **L**, **S**, or **V** are used and that the number of
9198 symbol columns (**N**) is 1.
9299 line_spacing
93- Specify the line-spacing factor between legend entries in units of the current
94- font size [Default is 1.1].
100+ The line-spacing factor between legend entries in units of the current font size
101+ [Default is 1.1].
95102 box
96103 Draw a background box behind the legend. If set to ``True``, a simple
97104 rectangular box is drawn using :gmt-term:`MAP_FRAME_PEN`. To customize the box
@@ -109,22 +116,16 @@ def legend( # noqa: PLR0913
109116 """
110117 self ._activate_figure ()
111118
112- # Prior PyGMT v0.17.0, 'position' can accept a raw GMT CLI string. Check for
113- # conflicts with other parameters.
114- if isinstance (position , str ) and any (
115- v is not None for v in (width , height , line_spacing )
116- ):
117- msg = (
118- "Parameter 'position' is given with a raw GMT command string, and conflicts "
119- "with parameters 'width', 'height', and 'line_spacing'."
120- )
121- raise GMTInvalidInput (msg )
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" )
122123
123- # Set default position if not specified.
124- if kwargs . get ( "D" , position ) is None :
125- position = Position ( "TR" , anchor = "TR" , offset = 0.2 )
126- if kwargs . get ( "F " , box ) is False :
127- box = Box ( pen = "1p" , fill = "white" ) # Default box
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.
128+ )
128129
129130 # Set default width to 0 if height is given but width is not.
130131 if height is not None and width is None :
0 commit comments