Skip to content

Commit c72967f

Browse files
authored
params.Frame: Support for setting up secondary axis for both x- and y-axis (#4583)
1 parent b7775ef commit c72967f

2 files changed

Lines changed: 51 additions & 5 deletions

File tree

pygmt/params/frame.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,11 @@ class Frame(BaseParam):
189189
#:
190190
#: GMT uses the notion of primary (the default) and secondary axes, while secondary
191191
#: axes are optional and mostly used for time axes annotations. To specify the
192-
#: attributes for the secondary axes, use the ``xaxis2``, ``yaxis2``, and ``zaxis2``
193-
#: parameters.
192+
#: same attributes for both secondary x and y axes, use the ``axis2`` parameter.
193+
#: To specify the attributes for the secondary axes separately, use the ``xaxis2``,
194+
#: ``yaxis2``, and ``zaxis2`` parameters.
194195
axis: Axis | None = None
196+
axis2: Axis | None = None
195197
xaxis: Axis | None = None
196198
yaxis: Axis | None = None
197199
zaxis: Axis | None = None
@@ -210,17 +212,34 @@ def _validate(self):
210212
conflicts_with=("axis", ["xaxis", "yaxis", "xaxis2", "yaxis2"]),
211213
reason="Either 'axis' or the individual axis parameters can be set, but not both.",
212214
)
215+
if self.axis2 is not None and any(
216+
[self.xaxis, self.yaxis, self.xaxis2, self.yaxis2]
217+
):
218+
raise GMTParameterError(
219+
conflicts_with=("axis2", ["xaxis", "yaxis", "xaxis2", "yaxis2"]),
220+
reason="Either 'axis2' or the individual axis parameters can be set, but not both.",
221+
)
213222

214223
@property
215224
def _aliases(self):
216225
# _Axes() maps to an empty string, which becomes '-B' without arguments and is
217226
# invalid when combined with individual axis settings (e.g., '-B -Bxaf -Byaf').
218227
frame_settings = _Axes(axes=self.axes, title=self.title, fill=self.fill)
228+
has_secondary_xy_axis = any([self.axis2, self.xaxis2, self.yaxis2])
219229
return [
220230
Alias(frame_settings) if str(frame_settings) else Alias(None),
221-
Alias(self.axis, name="axis"),
222-
Alias(self.xaxis, name="xaxis", prefix="px" if self.xaxis2 else "x"),
223-
Alias(self.yaxis, name="yaxis", prefix="py" if self.yaxis2 else "y"),
231+
Alias(self.axis, name="axis", prefix="p" if has_secondary_xy_axis else ""),
232+
Alias(self.axis2, name="axis2", prefix="s"),
233+
Alias(
234+
self.xaxis,
235+
name="xaxis",
236+
prefix="px" if self.xaxis2 or self.axis2 else "x",
237+
),
238+
Alias(
239+
self.yaxis,
240+
name="yaxis",
241+
prefix="py" if self.yaxis2 or self.axis2 else "y",
242+
),
224243
Alias(self.zaxis, name="zaxis", prefix="pz" if self.zaxis2 else "z"),
225244
Alias(self.xaxis2, name="xaxis2", prefix="sx"),
226245
Alias(self.yaxis2, name="yaxis2", prefix="sy"),

pygmt/tests/test_params_frame.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Test the Frame and Axis classes.
33
"""
44

5+
import pytest
6+
from pygmt.exceptions import GMTParameterError
57
from pygmt.params import Axis, Frame
68

79

@@ -41,6 +43,14 @@ def test_params_frame_axis():
4143
)
4244
assert list(frame) == ["WSEN+tMy Title", "afg+lLABEL"]
4345

46+
frame = Frame(
47+
axes="WSEN",
48+
title="My Title",
49+
axis=Axis(annot=30, tick=15, grid=10),
50+
axis2=Axis(annot=60, tick=30, grid=20),
51+
)
52+
assert list(frame) == ["WSEN+tMy Title", "pa30f15g10", "sa60f30g20"]
53+
4454

4555
def test_params_frame_separate_axes():
4656
"""
@@ -96,3 +106,20 @@ def test_params_frame_separate_axis_secondary():
96106
yaxis=Axis(annot=True, tick=True, grid=True, label="Y-LABEL"),
97107
)
98108
assert list(frame) == ["WSEN+tMy Title", "xafg+lX-LABEL", "yafg+lY-LABEL"]
109+
110+
111+
def test_params_frame_invalid_axis_combinations():
112+
"""
113+
Test that invalid combinations of uniform and individual axis settings fail.
114+
"""
115+
with pytest.raises(GMTParameterError, match="Either 'axis' or"):
116+
Frame(axis=Axis(annot=1), xaxis=Axis(annot=2))
117+
118+
with pytest.raises(GMTParameterError, match="Either 'axis' or"):
119+
Frame(axis=Axis(annot=1), xaxis2=Axis(annot=2))
120+
121+
with pytest.raises(GMTParameterError, match="Either 'axis2' or"):
122+
Frame(axis2=Axis(annot=1), xaxis=Axis(annot=2))
123+
124+
with pytest.raises(GMTParameterError, match="Either 'axis2' or"):
125+
Frame(axis2=Axis(annot=1), yaxis2=Axis(annot=2))

0 commit comments

Comments
 (0)