Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions pygmt/params/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -210,17 +212,34 @@ 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"),
Expand Down
27 changes: 27 additions & 0 deletions pygmt/tests/test_params_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Test the Frame and Axis classes.
"""

import pytest
from pygmt.exceptions import GMTParameterError
from pygmt.params import Axis, Frame


Expand Down Expand Up @@ -41,6 +43,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():
"""
Expand Down Expand Up @@ -96,3 +106,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))
Loading