From da67d57504eb9bf037ef869c0fdc117ff8668bf8 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 13 Apr 2026 17:27:40 +0800 Subject: [PATCH 1/3] params.Frame: Support for setting up secondary axis for both x- and y-axis --- pygmt/params/frame.py | 21 ++++++++++++++++----- pygmt/tests/test_params_frame.py | 8 ++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/pygmt/params/frame.py b/pygmt/params/frame.py index 65d8b6213ad..05cd7d53867 100644 --- a/pygmt/params/frame.py +++ b/pygmt/params/frame.py @@ -189,9 +189,11 @@ class Frame(BaseParam): #: #: GMT uses the notion of primary (the default) and secondary axes, while secondary #: axes are optional and mostly used for time axes annotations. To specify the - #: attributes for the secondary axes, use the ``xaxis2``, ``yaxis2``, and ``zaxis2`` - #: parameters. + #: same attributes for both secondary x and y axes, use the ``axis2`` parameter. + #: To specify the attributes for the secondary axes separately, use the ``xaxis2``, + #: ``yaxis2``, and ``zaxis2`` parameters. axis: Axis | None = None + axis2: Axis | None = None xaxis: Axis | None = None yaxis: Axis | None = None zaxis: Axis | None = None @@ -210,17 +212,26 @@ def _validate(self): conflicts_with=("axis", ["xaxis", "yaxis", "xaxis2", "yaxis2"]), reason="Either 'axis' or the individual axis parameters can be set, but not both.", ) + if self.axis2 is not None and any( + [self.xaxis, self.yaxis, self.xaxis2, self.yaxis2] + ): + raise GMTParameterError( + conflicts_with=("axis2", ["xaxis", "yaxis", "xaxis2", "yaxis2"]), + reason="Either 'axis2' or the individual axis parameters can be set, but not both.", + ) @property def _aliases(self): # _Axes() maps to an empty string, which becomes '-B' without arguments and is # invalid when combined with individual axis settings (e.g., '-B -Bxaf -Byaf'). frame_settings = _Axes(axes=self.axes, title=self.title, fill=self.fill) + has_secondary_xy_axis = any([self.axis2, self.xaxis2, self.yaxis2]) return [ Alias(frame_settings) if str(frame_settings) else Alias(None), - Alias(self.axis, name="axis"), - Alias(self.xaxis, name="xaxis", prefix="px" if self.xaxis2 else "x"), - Alias(self.yaxis, name="yaxis", prefix="py" if self.yaxis2 else "y"), + Alias(self.axis, name="axis", prefix="p" if has_secondary_xy_axis else ""), + Alias(self.axis2, name="axis2", prefix="s"), + Alias(self.xaxis, name="xaxis", prefix="px" if self.xaxis2 or self.axis2 else "x"), + Alias(self.yaxis, name="yaxis", prefix="py" if self.yaxis2 or self.axis2 else "y"), Alias(self.zaxis, name="zaxis", prefix="pz" if self.zaxis2 else "z"), Alias(self.xaxis2, name="xaxis2", prefix="sx"), Alias(self.yaxis2, name="yaxis2", prefix="sy"), diff --git a/pygmt/tests/test_params_frame.py b/pygmt/tests/test_params_frame.py index bf4c6378cc0..b540498651d 100644 --- a/pygmt/tests/test_params_frame.py +++ b/pygmt/tests/test_params_frame.py @@ -41,6 +41,14 @@ def test_params_frame_axis(): ) assert list(frame) == ["WSEN+tMy Title", "afg+lLABEL"] + frame = Frame( + axes="WSEN", + title="My Title", + axis=Axis(annot=30, tick=15, grid=10), + axis2=Axis(annot=60, tick=30, grid=20), + ) + assert list(frame) == ["WSEN+tMy Title", "pa30f15g10", "sa60f30g20"] + def test_params_frame_separate_axes(): """ From 524d429aa9c48bfdf0ee60650f88560d317740b3 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 13 Apr 2026 17:40:06 +0800 Subject: [PATCH 2/3] Add validation tests for Frame axis conflicts --- pygmt/tests/test_params_frame.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pygmt/tests/test_params_frame.py b/pygmt/tests/test_params_frame.py index b540498651d..e7b743788b8 100644 --- a/pygmt/tests/test_params_frame.py +++ b/pygmt/tests/test_params_frame.py @@ -2,6 +2,9 @@ Test the Frame and Axis classes. """ +import pytest + +from pygmt.exceptions import GMTParameterError from pygmt.params import Axis, Frame @@ -104,3 +107,20 @@ def test_params_frame_separate_axis_secondary(): yaxis=Axis(annot=True, tick=True, grid=True, label="Y-LABEL"), ) assert list(frame) == ["WSEN+tMy Title", "xafg+lX-LABEL", "yafg+lY-LABEL"] + + +def test_params_frame_invalid_axis_combinations(): + """ + Test that invalid combinations of uniform and individual axis settings fail. + """ + with pytest.raises(GMTParameterError, match="Either 'axis' or"): + Frame(axis=Axis(annot=1), xaxis=Axis(annot=2)) + + with pytest.raises(GMTParameterError, match="Either 'axis' or"): + Frame(axis=Axis(annot=1), xaxis2=Axis(annot=2)) + + with pytest.raises(GMTParameterError, match="Either 'axis2' or"): + Frame(axis2=Axis(annot=1), xaxis=Axis(annot=2)) + + with pytest.raises(GMTParameterError, match="Either 'axis2' or"): + Frame(axis2=Axis(annot=1), yaxis2=Axis(annot=2)) From 3e4fe8be994e9b554cd34ae095996b56c8a4263c Mon Sep 17 00:00:00 2001 From: actions-bot <58130806+actions-bot@users.noreply.github.com> Date: Mon, 13 Apr 2026 12:28:09 +0000 Subject: [PATCH 3/3] [format-command] fixes --- pygmt/params/frame.py | 12 ++++++++++-- pygmt/tests/test_params_frame.py | 1 - 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/pygmt/params/frame.py b/pygmt/params/frame.py index 05cd7d53867..53a7ad0f3cb 100644 --- a/pygmt/params/frame.py +++ b/pygmt/params/frame.py @@ -230,8 +230,16 @@ def _aliases(self): Alias(frame_settings) if str(frame_settings) else Alias(None), Alias(self.axis, name="axis", prefix="p" if has_secondary_xy_axis else ""), Alias(self.axis2, name="axis2", prefix="s"), - Alias(self.xaxis, name="xaxis", prefix="px" if self.xaxis2 or self.axis2 else "x"), - Alias(self.yaxis, name="yaxis", prefix="py" if self.yaxis2 or self.axis2 else "y"), + Alias( + self.xaxis, + name="xaxis", + prefix="px" if self.xaxis2 or self.axis2 else "x", + ), + Alias( + self.yaxis, + name="yaxis", + prefix="py" if self.yaxis2 or self.axis2 else "y", + ), Alias(self.zaxis, name="zaxis", prefix="pz" if self.zaxis2 else "z"), Alias(self.xaxis2, name="xaxis2", prefix="sx"), Alias(self.yaxis2, name="yaxis2", prefix="sy"), diff --git a/pygmt/tests/test_params_frame.py b/pygmt/tests/test_params_frame.py index e7b743788b8..d50bc0988e5 100644 --- a/pygmt/tests/test_params_frame.py +++ b/pygmt/tests/test_params_frame.py @@ -3,7 +3,6 @@ """ import pytest - from pygmt.exceptions import GMTParameterError from pygmt.params import Axis, Frame