Skip to content

Commit c95883c

Browse files
committed
Figure.subplot: Pythonic implemention for the subplot tagging
1 parent 886355f commit c95883c

File tree

2 files changed

+171
-28
lines changed

2 files changed

+171
-28
lines changed

pygmt/src/subplot.py

Lines changed: 118 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,75 @@
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+
16+
17+
def _alias_option_A( # noqa: N802
18+
autotag: str | bool = False,
19+
tag_position: AnchorCode | Position | None = None,
20+
tag_box: Box | None = None,
21+
tag_number_style: Literal["arabic", "roman", "Roman"] | None = None,
22+
tag_orientation: Literal["horizontal", "vertical"] | None = None,
23+
autolabel: str | bool = False,
24+
):
25+
"""Helper function to create Alias for option A in subplot."""
26+
# Check conflicts with deprecated 'autolabel' parameter.
27+
if autolabel:
28+
if any(
29+
v is not None and v is not False
30+
for v in [autotag, tag_position, tag_box, tag_number_style, tag_orientation]
31+
):
32+
msg = (
33+
"The 'autolabel' parameter is deprecated since v0.18.0. "
34+
"Please use the parameters 'autotag', 'tag_position', 'tag_box', "
35+
"'tag_number_style', 'tag_orientation', and 'tag_font' instead."
36+
)
37+
raise GMTInvalidInput(msg)
38+
return Alias(autolabel, name="autolabel")
39+
40+
return [
41+
Alias(autotag, name="autotag"),
42+
Alias(tag_position, name="tag_position", prefix="+"), # Prefix is "+".
43+
Alias(tag_box, name="tag_box"),
44+
Alias(
45+
tag_number_style,
46+
name="tag_number_style",
47+
mapping={"arabic": "", "roman": "+r", "Roman": "+R"},
48+
),
49+
Alias(
50+
tag_orientation,
51+
name="tag_orientation",
52+
mapping={"horizontal": "", "vertical": "+v"},
53+
),
54+
]
1355

1456

1557
@fmt_docstring
1658
@contextlib.contextmanager
1759
@use_alias(
1860
Ff="figsize",
1961
Fs="subsize",
20-
A="autolabel",
2162
C="clearance",
2263
SC="sharex",
2364
SR="sharey",
2465
)
2566
@kwargs_to_strings(Ff="sequence", Fs="sequence")
26-
def subplot(
67+
def subplot( # noqa: PLR0913
2768
self,
2869
nrows: int = 1,
2970
ncols: int = 1,
71+
autotag: str | bool = False,
72+
tag_position: AnchorCode | Position | None = None,
73+
tag_box: Box | None = None,
74+
tag_orientation: Literal["horizontal", "vertical"] | None = None,
75+
tag_number_style: Literal["arabic", "roman", "Roman"] | None = None,
76+
tag_font: str | None = None,
77+
autolabel: str | bool = False,
3078
margins: float | str | Sequence[float | str] | None = None,
3179
title: str | None = None,
3280
projection: str | None = None,
@@ -67,31 +115,59 @@ def subplot(
67115
Specify the dimensions of each subplot directly as [*width*, *height*].
68116
Note that only one of ``figsize`` or ``subsize`` can be provided at
69117
once.
118+
autolabel
119+
Specify automatic tagging of each subplot.
120+
121+
.. deprecated:: v0.18.0
122+
123+
Use the parameters ``autotag``, ``tag_position``, ``tag_box``,
124+
``tag_number_style``, ``tag_orientation``, and ``tag_font`` instead.
125+
autotag
126+
Specify automatic tagging of each subplot. It can accept a number, or a letter.
127+
The number or letter can be surrounded by parentheses on any side if these
128+
should be typeset as part of the tag. This sets the tag of the first, top-left
129+
subplot and others follow sequentially. If set to ``True``, default to ``"a)"``.
130+
131+
Examples are:
70132
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].
133+
- ``autotag="a"``: tags are ``a``, ``b``, ``c``, ...
134+
- ``autotag="1"``: tags are ``1``, ``2``, ``3``, ...
135+
- ``autotag="a)"``: tags are ``a)``, ``b)``, ``c)``, ...
136+
- ``autotag="(c)"``: tags are ``(c)``, ``(d)``, ``(e)``, ...
137+
- ``autotag=True``: same as ``autotag="a)"``.
138+
tag_position
139+
Position of the subplot tag on the plot. It can be specified in two ways:
140+
141+
- A :doc:`2-character justification code </techref/justification_codes>` for a
142+
position inside the plot, e.g., ``"TL"`` for Top Left corner inside the plot.
143+
- A :class:`pygmt.params.Position` object to fully control the position and
144+
offset. **Note**: the ``refpoint`` propterty of the Position object must be
145+
an two-character justification code, and ``cstype`` must be set to either
146+
``"inside"`` or ``"outside"``,
147+
148+
If not specified, defaults to Top Left corner inside the plot with the offset
149+
default to ``("4p", "4p")``, i.e., 20% of the :gmt-term:`FONT_TAG` size.
150+
tag_box
151+
Draw a box around the subplot tag. See :class:`pygmt.params.Box` for details on
152+
how to specify the box.
153+
154+
**Notes on the use of the ``Box`` class:**
155+
156+
- The property ``clearance`` only accept one or two values.
157+
- The property ``inner_pen``/``inner_gap``/``radius`` is not supported.
158+
tag_number_style
159+
Style of the subplot tag numbers. It can be:
160+
161+
- ``"arabic"``: Arabic numerals: 1, 2, 3, ... [Default].
162+
- ``"roman"``: Lowercase Roman numerals: i, ii, iii, ...
163+
- ``"Roman"``: Uppercase Roman numerals: I, II, III, ...
164+
tag_orientation
165+
Orientation of the subplot tag. It can be:
166+
167+
- ``"horizontal"``: Increase tag numbers horizontally across rows [Default].
168+
- ``"vertical"``: Increase tag numbers vertically down columns.
169+
tag_font
170+
Font for the subplot tag [Default to ``"20p,Helvetica,black"``].
95171
clearance : str or list
96172
[*side*]\ *clearance*.
97173
Reserve a space of dimension *clearance* between the margin and the
@@ -170,6 +246,14 @@ def subplot(
170246
raise GMTInvalidInput(msg)
171247

172248
aliasdict = AliasSystem(
249+
A=_alias_option_A(
250+
autotag=autotag,
251+
tag_position=tag_position,
252+
tag_box=tag_box,
253+
tag_number_style=tag_number_style,
254+
tag_orientation=tag_orientation,
255+
autolabel=autolabel,
256+
),
173257
M=Alias(margins, name="margins", sep="/", size=(2, 4)),
174258
T=Alias(title, name="title"),
175259
).add_common(
@@ -180,6 +264,9 @@ def subplot(
180264
)
181265
aliasdict.merge(kwargs)
182266

267+
# Configure FONT_TAG if tag_font is set
268+
confdict = {"FONT_TAG": tag_font} if tag_font is not None else {}
269+
183270
# Need to use separate sessions for "subplot begin" and "subplot end".
184271
# Otherwise, "subplot end" will use the last session, which may cause
185272
# strange positioning issues for later plotting calls.
@@ -188,7 +275,11 @@ def subplot(
188275
with Session() as lib:
189276
lib.call_module(
190277
module="subplot",
191-
args=["begin", f"{nrows}x{ncols}", *build_arg_list(aliasdict)],
278+
args=[
279+
"begin",
280+
f"{nrows}x{ncols}",
281+
*build_arg_list(aliasdict, confdict=confdict),
282+
],
192283
)
193284
yield
194285
finally:

pygmt/tests/test_subplot.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
import pytest
66
from pygmt import Figure
7+
from pygmt.alias import AliasSystem
78
from pygmt.exceptions import GMTInvalidInput, GMTValueError
8-
from pygmt.params import Position
9+
from pygmt.params import Box, Position
10+
from pygmt.src.subplot import _alias_option_A
911

1012

1113
@pytest.mark.benchmark
@@ -125,3 +127,53 @@ def test_subplot_outside_plotting_positioning():
125127
frame=True,
126128
)
127129
return fig
130+
131+
132+
def test_alias_option_A(): # noqa: N802
133+
"""
134+
Test _alias_option_A with only autotag parameter.
135+
"""
136+
137+
def alias_wrapper(**kwargs):
138+
"""
139+
A wrapper function for testing the parameters of -A option.
140+
"""
141+
return AliasSystem(A=_alias_option_A(**kwargs)).get("A")
142+
143+
assert alias_wrapper(autotag=True) == ""
144+
assert alias_wrapper(autotag="a)") == "a)"
145+
assert alias_wrapper(autotag="(a)", tag_position="TL") == "(a)+jTL"
146+
assert alias_wrapper(autotag="i)", tag_number_style="roman") == "i)+r"
147+
assert alias_wrapper(autotag="i)", tag_number_style="Roman") == "i)+R"
148+
assert alias_wrapper(autotag="a)", tag_orientation="vertical") == "a)+v"
149+
150+
tag = alias_wrapper(autotag="i)", tag_box=Box(pen="1p,red", offset="2p"))
151+
assert tag == "i)+b1p,red+o2p"
152+
153+
tag = alias_wrapper(
154+
autotag="a)", tag_position=Position("BL", cstype="outside", offset=("3p", "3p"))
155+
)
156+
assert tag == "a)+JBL+o3p/3p"
157+
158+
159+
def test_deprecated_autolabel():
160+
"""
161+
Test that using the deprecated autolabel parameter raises a warning when conflicted
162+
with tag parameters.
163+
"""
164+
fig = Figure()
165+
with pytest.raises(GMTInvalidInput):
166+
with fig.subplot(nrows=1, ncols=1, autolabel=True, autotag="a)"):
167+
pass
168+
with pytest.raises(GMTInvalidInput):
169+
with fig.subplot(nrows=1, ncols=1, autolabel=True, tag_box=True):
170+
pass
171+
with pytest.raises(GMTInvalidInput):
172+
with fig.subplot(nrows=1, ncols=1, autolabel=True, tag_orientation="vertical"):
173+
pass
174+
with pytest.raises(GMTInvalidInput):
175+
with fig.subplot(nrows=1, ncols=1, autolabel=True, tag_number_style="roman"):
176+
pass
177+
with pytest.raises(GMTInvalidInput):
178+
with fig.subplot(nrows=1, ncols=1, autolabel=True, tag_position="TL"):
179+
pass

0 commit comments

Comments
 (0)