Skip to content

Commit 6938abb

Browse files
committed
Figure.subplot: Pythonic implemention for the subplot tagging
1 parent c245644 commit 6938abb

File tree

2 files changed

+174
-28
lines changed

2 files changed

+174
-28
lines changed

pygmt/src/subplot.py

Lines changed: 121 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,78 @@
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+
"""Helper function to create Alias for option A in subplot."""
27+
# Check conflicts with deprecated 'autolabel' parameter.
28+
if autolabel:
29+
if any(
30+
v is not None and v is not False
31+
for v in [autotag, tag_position, tag_box, tag_number_style, tag_orientation]
32+
):
33+
msg = (
34+
"The 'autolabel' parameter is deprecated since v0.18.0. "
35+
"Please use the parameters 'autotag', 'tag_position', 'tag_box', "
36+
"'tag_number_style', 'tag_orientation', and 'tag_font' instead."
37+
)
38+
raise GMTInvalidInput(msg)
39+
return Alias(autolabel, name="autolabel")
40+
41+
return [
42+
Alias(autotag, name="autotag"),
43+
Alias(
44+
_parse_position(tag_position), name="tag_position", prefix="+"
45+
), # Prefix is "+".
46+
Alias(tag_box, name="tag_box"),
47+
Alias(
48+
tag_number_style,
49+
name="tag_number_style",
50+
mapping={"arabic": "", "roman": "+r", "Roman": "+R"},
51+
),
52+
Alias(
53+
tag_orientation,
54+
name="tag_orientation",
55+
mapping={"horizontal": "", "vertical": "+v"},
56+
),
57+
]
1358

1459

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

172251
aliasdict = AliasSystem(
252+
A=_alias_option_A(
253+
autotag=autotag,
254+
tag_position=tag_position,
255+
tag_box=tag_box,
256+
tag_number_style=tag_number_style,
257+
tag_orientation=tag_orientation,
258+
autolabel=autolabel,
259+
),
173260
M=Alias(margins, name="margins", sep="/", size=(2, 4)),
174261
T=Alias(title, name="title"),
175262
).add_common(
@@ -180,6 +267,9 @@ def subplot(
180267
)
181268
aliasdict.merge(kwargs)
182269

270+
# Configure FONT_TAG if tag_font is set
271+
confdict = {"FONT_TAG": tag_font} if tag_font is not None else {}
272+
183273
# Need to use separate sessions for "subplot begin" and "subplot end".
184274
# Otherwise, "subplot end" will use the last session, which may cause
185275
# strange positioning issues for later plotting calls.
@@ -188,7 +278,11 @@ def subplot(
188278
with Session() as lib:
189279
lib.call_module(
190280
module="subplot",
191-
args=["begin", f"{nrows}x{ncols}", *build_arg_list(aliasdict)],
281+
args=[
282+
"begin",
283+
f"{nrows}x{ncols}",
284+
*build_arg_list(aliasdict, confdict=confdict),
285+
],
192286
)
193287
yield
194288
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", clearance="2p"))
151+
assert tag == "i)+c2p+p1p,red"
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)