Skip to content

Commit 6a05085

Browse files
committed
Figure.subplot: Pythonic implemention for the subplot tagging
1 parent 3062417 commit 6a05085

2 files changed

Lines changed: 170 additions & 27 deletions

File tree

pygmt/src/subplot.py

Lines changed: 147 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,104 @@
66
from collections.abc import Sequence
77
from typing import Literal
88

9+
from pygmt._typing import AnchorCode
910
from pygmt.alias import Alias, AliasSystem
1011
from pygmt.clib import Session
1112
from pygmt.exceptions import GMTInvalidInput, GMTValueError
1213
from pygmt.helpers import build_arg_list, fmt_docstring, kwargs_to_strings, use_alias
14+
from pygmt.params import Box, Position
15+
from pygmt.src._common import _parse_position
16+
17+
18+
def _alias_option_A( # noqa: N802
19+
autotag: str | bool = False,
20+
tag_position: AnchorCode | Position | None = None,
21+
tag_box: Box | None = None,
22+
tag_number_style: Literal["arabic", "roman", "Roman"] | None = None,
23+
tag_orientation: Literal["horizontal", "vertical"] | None = None,
24+
autolabel: str | bool = False,
25+
):
26+
"""
27+
Helper function to create Alias for option A in subplot.
28+
29+
Examples
30+
--------
31+
>>> def parse(**kwargs):
32+
... return AliasSystem(A=_alias_option_A(**kwargs)).get("A")
33+
>>> parse(autotag=True)
34+
''
35+
>>> parse(autotag="a)")
36+
'a)'
37+
>>> parse(autotag="(a)", tag_position="TL")
38+
'(a)+jTL'
39+
>>> parse(autotag="i)", tag_number_style="roman")
40+
'i)+r'
41+
>>> parse(autotag="i)", tag_number_style="Roman")
42+
'i)+R'
43+
>>> parse(autotag="a)", tag_orientation="vertical")
44+
'a)+v'
45+
>>> parse(autotag="i)", tag_box=Box(pen="1p,red", clearance="2p"))
46+
'i)+c2p+p1p,red'
47+
>>> parse(
48+
... autotag="a)",
49+
... tag_position=Position("BL", cstype="outside", offset=("3p", "3p")),
50+
... )
51+
'a)+JBL+o3p/3p'
52+
"""
53+
# Check conflicts with deprecated 'autolabel' parameter.
54+
if autolabel:
55+
if any(
56+
v is not None and v is not False
57+
for v in [autotag, tag_position, tag_box, tag_number_style, tag_orientation]
58+
):
59+
msg = (
60+
"The 'autolabel' parameter is deprecated since v0.18.0. "
61+
"Please use the parameters 'autotag', 'tag_position', 'tag_box', "
62+
"'tag_number_style', 'tag_orientation', and 'tag_font' instead."
63+
)
64+
raise GMTInvalidInput(msg)
65+
return Alias(autolabel, name="autolabel")
66+
67+
return [
68+
Alias(autotag, name="autotag"),
69+
Alias(
70+
_parse_position(tag_position), name="tag_position", prefix="+"
71+
), # Prefix is "+".
72+
Alias(tag_box, name="tag_box"),
73+
Alias(
74+
tag_number_style,
75+
name="tag_number_style",
76+
mapping={"arabic": "", "roman": "+r", "Roman": "+R"},
77+
),
78+
Alias(
79+
tag_orientation,
80+
name="tag_orientation",
81+
mapping={"horizontal": "", "vertical": "+v"},
82+
),
83+
]
1384

1485

1586
@fmt_docstring
1687
@contextlib.contextmanager
1788
@use_alias(
1889
Ff="figsize",
1990
Fs="subsize",
20-
A="autolabel",
2191
C="clearance",
2292
SC="sharex",
2393
SR="sharey",
2494
)
2595
@kwargs_to_strings(Ff="sequence", Fs="sequence")
26-
def subplot(
96+
def subplot( # noqa: PLR0913
2797
self,
2898
nrows: int = 1,
2999
ncols: int = 1,
100+
autotag: str | bool = False,
101+
tag_position: AnchorCode | Position | None = None,
102+
tag_box: Box | None = None,
103+
tag_orientation: Literal["horizontal", "vertical"] | None = None,
104+
tag_number_style: Literal["arabic", "roman", "Roman"] | None = None,
105+
tag_font: str | None = None,
106+
autolabel: str | bool = False,
30107
margins: float | str | Sequence[float | str] | None = None,
31108
title: str | None = None,
32109
projection: str | None = None,
@@ -67,31 +144,59 @@ def subplot(
67144
Specify the dimensions of each subplot directly as [*width*, *height*].
68145
Note that only one of ``figsize`` or ``subsize`` can be provided at
69146
once.
147+
autolabel
148+
Specify automatic tagging of each subplot.
149+
150+
.. deprecated:: v0.18.0
70151
71-
autolabel : bool or str
72-
[*autolabel*][**+c**\ *dx*\ [/*dy*]][**+g**\ *fill*][**+j**\|\ **J**\
73-
*refpoint*][**+o**\ *dx*\ [/*dy*]][**+p**\ *pen*][**+r**\|\ **R**]\ [**+v**].
74-
Specify automatic tagging of each subplot. Append either a number or letter
75-
[Default is ``"a"``]. This sets the tag of the first, top-left subplot and
76-
others follow sequentially. Surround the number or letter by parentheses on
77-
any side if these should be typeset as part of the tag [Default is ``")"``].
78-
Use **+j**\|\ **J** for setting *refpoint* via a
79-
:doc:`2-character justification code </techref/justification_codes>`
80-
to specify where the tag should be placed in the subplot [Default is ``"TL"``
81-
for the Top Left corner]. **Note**: **+j** sets the justification of the tag
82-
to *refpoint* (suitable for interior tags) while **+J** instead selects the
83-
mirror opposite (suitable for exterior tags). Append **+c**\ *dx*\[/*dy*] to
84-
set the clearance between the tag and a surrounding text box requested via
85-
**+g** or **+p** [Default is ``"3p/3p"``, i.e., 15 % of the
86-
:gmt-term:`FONT_TAG` size dimension]. Append **+g**\ *fill* to paint the tag's
87-
text box with *fill* [Default is no fill]. Append **+o**\ *dx*\ [/*dy*] to
88-
offset the tag's reference point in the direction implied by the justification
89-
[Default is ``"4p/4p"``, i.e., 20 % of the :gmt-term:`FONT_TAG` size]. Append
90-
**+p**\ *pen* to draw the outline of the tag's text box using the selected *pen*
91-
[Default is no outline]. Append **+r** to typeset your tag numbers using
92-
lowercase Roman numerals; use **+R** for uppercase Roman numerals [Default is
93-
Arabic numerals]. Append **+v** to increase tag numbers vertically down columns
94-
[Default is horizontally across rows].
152+
Use the parameters ``autotag``, ``tag_position``, ``tag_box``,
153+
``tag_number_style``, ``tag_orientation``, and ``tag_font`` instead.
154+
autotag
155+
Specify automatic tagging of each subplot. It can accept a number, or a letter.
156+
The number or letter can be surrounded by parentheses on any side if these
157+
should be typeset as part of the tag. This sets the tag of the first, top-left
158+
subplot and others follow sequentially. If set to ``True``, default to ``"a)"``.
159+
160+
Examples are:
161+
162+
- ``autotag="a"``: tags are ``a``, ``b``, ``c``, ...
163+
- ``autotag="1"``: tags are ``1``, ``2``, ``3``, ...
164+
- ``autotag="a)"``: tags are ``a)``, ``b)``, ``c)``, ...
165+
- ``autotag="(c)"``: tags are ``(c)``, ``(d)``, ``(e)``, ...
166+
- ``autotag=True``: same as ``autotag="a)"``.
167+
tag_position
168+
Position of the subplot tag on the plot. It can be specified in two ways:
169+
170+
- A :doc:`2-character justification code </techref/justification_codes>` for a
171+
position inside the plot, e.g., ``"TL"`` for Top Left corner inside the plot.
172+
- A :class:`pygmt.params.Position` object to fully control the position and
173+
offset. **Note**: the ``refpoint`` propterty of the Position object must be
174+
an two-character justification code, and ``cstype`` must be set to either
175+
``"inside"`` or ``"outside"``,
176+
177+
If not specified, defaults to Top Left corner inside the plot with the offset
178+
default to ``("4p", "4p")``, i.e., 20% of the :gmt-term:`FONT_TAG` size.
179+
tag_box
180+
Draw a box around the subplot tag. See :class:`pygmt.params.Box` for details on
181+
how to specify the box.
182+
183+
**Notes on the use of the ``Box`` class:**
184+
185+
- The property ``clearance`` only accept one or two values.
186+
- The property ``inner_pen``/``inner_gap``/``radius`` is not supported.
187+
tag_number_style
188+
Style of the subplot tag numbers. It can be:
189+
190+
- ``"arabic"``: Arabic numerals: 1, 2, 3, ... [Default].
191+
- ``"roman"``: Lowercase Roman numerals: i, ii, iii, ...
192+
- ``"Roman"``: Uppercase Roman numerals: I, II, III, ...
193+
tag_orientation
194+
Orientation of the subplot tag. It can be:
195+
196+
- ``"horizontal"``: Increase tag numbers horizontally across rows [Default].
197+
- ``"vertical"``: Increase tag numbers vertically down columns.
198+
tag_font
199+
Font for the subplot tag [Default to ``"20p,Helvetica,black"``].
95200
clearance : str or list
96201
[*side*]\ *clearance*.
97202
Reserve a space of dimension *clearance* between the margin and the
@@ -170,6 +275,14 @@ def subplot(
170275
raise GMTInvalidInput(msg)
171276

172277
aliasdict = AliasSystem(
278+
A=_alias_option_A(
279+
autotag=autotag,
280+
tag_position=tag_position,
281+
tag_box=tag_box,
282+
tag_number_style=tag_number_style,
283+
tag_orientation=tag_orientation,
284+
autolabel=autolabel,
285+
),
173286
M=Alias(margins, name="margins", sep="/", size=(2, 4)),
174287
T=Alias(title, name="title"),
175288
).add_common(
@@ -180,6 +293,9 @@ def subplot(
180293
)
181294
aliasdict.merge(kwargs)
182295

296+
# Configure FONT_TAG if tag_font is set
297+
confdict = {"FONT_TAG": tag_font} if tag_font is not None else {}
298+
183299
# Need to use separate sessions for "subplot begin" and "subplot end".
184300
# Otherwise, "subplot end" will use the last session, which may cause
185301
# strange positioning issues for later plotting calls.
@@ -188,7 +304,11 @@ def subplot(
188304
with Session() as lib:
189305
lib.call_module(
190306
module="subplot",
191-
args=["begin", f"{nrows}x{ncols}", *build_arg_list(aliasdict)],
307+
args=[
308+
"begin",
309+
f"{nrows}x{ncols}",
310+
*build_arg_list(aliasdict, confdict=confdict),
311+
],
192312
)
193313
yield
194314
finally:

pygmt/tests/test_subplot.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,26 @@ def test_subplot_outside_plotting_positioning():
125125
frame=True,
126126
)
127127
return fig
128+
129+
130+
def test_deprecated_autolabel():
131+
"""
132+
Test that using the deprecated autolabel parameter raises a warning when conflicted
133+
with tag parameters.
134+
"""
135+
fig = Figure()
136+
with pytest.raises(GMTInvalidInput):
137+
with fig.subplot(nrows=1, ncols=1, autolabel=True, autotag="a)"):
138+
pass
139+
with pytest.raises(GMTInvalidInput):
140+
with fig.subplot(nrows=1, ncols=1, autolabel=True, tag_box=True):
141+
pass
142+
with pytest.raises(GMTInvalidInput):
143+
with fig.subplot(nrows=1, ncols=1, autolabel=True, tag_orientation="vertical"):
144+
pass
145+
with pytest.raises(GMTInvalidInput):
146+
with fig.subplot(nrows=1, ncols=1, autolabel=True, tag_number_style="roman"):
147+
pass
148+
with pytest.raises(GMTInvalidInput):
149+
with fig.subplot(nrows=1, ncols=1, autolabel=True, tag_position="TL"):
150+
pass

0 commit comments

Comments
 (0)