From 862dbb61714e21c1ce84e3ff0215ef6875c01932 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sat, 26 Jul 2025 15:27:00 +0800 Subject: [PATCH 1/7] Add the Axes, Axis, Frame classes for the frame parameter --- pygmt/params/__init__.py | 1 + pygmt/params/frame.py | 139 +++++++++++++++++++++++++++++++ pygmt/tests/test_params_frame.py | 30 +++++++ 3 files changed, 170 insertions(+) create mode 100644 pygmt/params/frame.py create mode 100644 pygmt/tests/test_params_frame.py diff --git a/pygmt/params/__init__.py b/pygmt/params/__init__.py index b80b921407a..12a788e3352 100644 --- a/pygmt/params/__init__.py +++ b/pygmt/params/__init__.py @@ -3,4 +3,5 @@ """ from pygmt.params.box import Box +from pygmt.params.frame import Axes, Axis, Frame from pygmt.params.pattern import Pattern diff --git a/pygmt/params/frame.py b/pygmt/params/frame.py new file mode 100644 index 00000000000..6c4c2969cfa --- /dev/null +++ b/pygmt/params/frame.py @@ -0,0 +1,139 @@ +""" +The Axes, Axis, and Frame classes for specifying the frame. +""" + +import dataclasses +from typing import Any, Literal + +from pygmt.alias import Alias +from pygmt.params.base import BaseParam + + +@dataclasses.dataclass(repr=False) +class Axis(BaseParam): + """ + Class for setting up one axis of a plot. + """ + + #: Intervals for annotations and major tick spacing, minor tick spacing, and/or + #: grid line spacing. + interval: float | str + + #: Plot slanted annotations (for Cartesian plots only), where *angle* is measured + #: with respect to the horizontal and must be in the -90 <= *angle* <= 90 range. + #: Default is normal (i.e., ``angle=90``) for y-axis and parallel (i.e., + #: ``angle=0``) for x-axis annotations. These defaults can be changed via + #: :gmt-term:`MAP_ANNOT_ORTHO`. + angle: float | None = None + + #: Skip annotations that fall exactly at the ends of the axis. Choose from ``left`` + #: or ``right`` to skip only the lower or upper annotation, respectively, or + #: ``True`` to skip both. + skip_edge: Literal["left", "right"] | bool = False + + #: Give fancy annotations with W|E|S|N suffixes encoding the sign (for geographic + #: axes only). + fancy: bool = False + + #: Add a label to the axis (for Cartesian plots only). The label is placed parallel + #: to the axis by default; use **hlabel** to force a horizontal label for y-axis, + #: which is useful for very short labels. + label: str | None = None + hlabel: str | None = None + + #: Add an alternate label for the right or upper axes. The label is placed parallel + #: to the axis by default; use **alt_hlabel** to force a horizontal label for + #: y-axis, which is useful for very short labels. [For Cartesian plots only]. + alt_label: str | None = None + alt_hlabel: str | None = None + + #: Add a leading text prefix for axis annotation (e.g., dollar sign for plots + #: related to money) (for Cartesian plots only). For geographic maps the addition + #: of degree symbols, etc. is automatic and controlled by + #: :gmt-term:`FORMAT_GEO_MAP`. + prefix: str | None = None + + #: Append a unit to the annotations (for Cartesian plots only). For geographic maps + #: the addition of degree symbols, etc. is automatic and controlled by + #: :gmt-term:`FORMAT_GEO_MAP`. + unit: str | None = None + + @property + def _aliases(self): + return [ + Alias(self.interval, name="interval"), + Alias(self.angle, name="angle", prefix="+a"), + Alias( + self.skip_edge, + name="skip_edge", + prefix="+e", + mapping={True: True, "left": "l", "right": "r"}, + ), + Alias(self.fancy, name="fancy", prefix="+f"), + Alias(self.label, name="label", prefix="+l"), + Alias(self.hlabel, name="hlabel", prefix="+L"), + Alias(self.alt_label, name="alt_label", prefix="+s"), + Alias(self.alt_hlabel, name="alt_hlabel", prefix="+S"), + Alias(self.unit, name="unit", prefix="+u"), + ] + + +@dataclasses.dataclass(repr=False) +class Axes(BaseParam): + """ + Class for specifying the frame of a plot. + """ + + #: Specify which axes to draw and their attributes. + axes: str | None = None + + #: Fill for the interior of the canvas [Default is no fill]. This also sets fill + #: for the two back-walls in 3-D plots. + fill: str | None = None + + #: The title string centered above the plot frame [Default is no title]. + title: str | None = None + + #: The subtitle string beneath the title [Default is no subtitle]. This requires + #: ``title`` to be set. + subtitle: str | None = None + + @property + def _aliases(self): + return [ + Alias(self.axes, name="axes"), + Alias(self.fill, name="fill", prefix="+g"), + Alias(self.title, name="title", prefix="+t"), + Alias(self.subtitle, name="subtitle", prefix="+s"), + ] + + +@dataclasses.dataclass(repr=False) +class Frame(BaseParam): + """ + Class for setting up the frame of a plot. + """ + + axes: Any = None + xaxis: Any = None + yaxis: Any = None + zaxis: Any = None + + @property + def _aliases(self): + return [ + Alias(self.axes), + Alias(self.xaxis, prefix="x"), + Alias(self.yaxis, prefix="y"), + Alias(self.zaxis, prefix="z"), + ] + + def __iter__(self): + """ + Iterate over the aliases of the class. + + Yields + ------ + The value of each alias in the class. None are excluded. + """ + yield from (alias._value for alias in self._aliases if alias._value is not None) diff --git a/pygmt/tests/test_params_frame.py b/pygmt/tests/test_params_frame.py new file mode 100644 index 00000000000..85de1bb406b --- /dev/null +++ b/pygmt/tests/test_params_frame.py @@ -0,0 +1,30 @@ +""" +Test the Frame/Axes/Axis classes. +""" + +from pygmt.params import Axes, Axis + + +def test_params_axis(): + """ + Test the Axis class. + """ + assert str(Axis(interval="a1f0.5")) == "a1f0.5" + assert str(Axis(interval="a1f0.5", angle=30)) == "a1f0.5+a30" + assert str(Axis(interval="a1f0.5", angle=30, skip_edge="left")) == "a1f0.5+a30+el" + assert str(Axis(interval="a1f0.5", fancy=True)) == "a1f0.5+f" + assert str(Axis(interval="a1f0.5", label="My Label")) == "a1f0.5+lMy Label" + assert str(Axis(interval="a1f0.5", hlabel="My HLabel")) == "a1f0.5+LMy HLabel" + assert str(Axis(interval="a1f0.5", alt_label="Alt Label")) == "a1f0.5+sAlt Label" + assert str(Axis(interval="a1f0.5", alt_hlabel="Alt HLabel")) == "a1f0.5+SAlt HLabel" + assert str(Axis(interval="a1f0.5", unit="km")) == "a1f0.5+ukm" + + +def test_params_axes(): + """ + Test the Axes class. + """ + assert ( + str(Axes("WSen", title="My Plot Title", fill="lightred")) + == "WSen+glightred+tMy Plot Title" + ) From 2714319d2b727ebb6ebe0495b528ba0f738610d3 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sat, 15 Nov 2025 13:50:20 +0800 Subject: [PATCH 2/7] Add to API documentation --- doc/api/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/api/index.rst b/doc/api/index.rst index 3656bba286e..5c4c4cc0ed6 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -213,6 +213,7 @@ Class-style Parameters :template: autosummary/params.rst Box + Frame Pattern Enums From bf8e8109b760fbd8b48d2bf5a25a1199b5224b99 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 9 Jan 2026 17:55:06 +0800 Subject: [PATCH 3/7] Make the Axes class private --- pygmt/params/__init__.py | 2 +- pygmt/params/frame.py | 118 ++++++++++++++++++++++++------- pygmt/tests/test_params_frame.py | 66 ++++++++++++----- 3 files changed, 144 insertions(+), 42 deletions(-) diff --git a/pygmt/params/__init__.py b/pygmt/params/__init__.py index 46e61fe964f..43b83ab0131 100644 --- a/pygmt/params/__init__.py +++ b/pygmt/params/__init__.py @@ -3,6 +3,6 @@ """ from pygmt.params.box import Box -from pygmt.params.frame import Axes, Axis, Frame +from pygmt.params.frame import Axis, Frame from pygmt.params.pattern import Pattern from pygmt.params.position import Position diff --git a/pygmt/params/frame.py b/pygmt/params/frame.py index 6c4c2969cfa..031f42e4961 100644 --- a/pygmt/params/frame.py +++ b/pygmt/params/frame.py @@ -3,9 +3,10 @@ """ import dataclasses -from typing import Any, Literal +from typing import Literal from pygmt.alias import Alias +from pygmt.exceptions import GMTInvalidInput from pygmt.params.base import BaseParam @@ -15,9 +16,10 @@ class Axis(BaseParam): Class for setting up one axis of a plot. """ - #: Intervals for annotations and major tick spacing, minor tick spacing, and/or - #: grid line spacing. - interval: float | str + #: Intervals for annotations, ticks and grid lines. + annotation: float | None = None + tick: float | None = None + grid: float | None = None #: Plot slanted annotations (for Cartesian plots only), where *angle* is measured #: with respect to the horizontal and must be in the -90 <= *angle* <= 90 range. @@ -61,7 +63,9 @@ class Axis(BaseParam): @property def _aliases(self): return [ - Alias(self.interval, name="interval"), + Alias(self.annotation, name="annotation", prefix="a"), + Alias(self.tick, name="tick", prefix="f"), + Alias(self.grid, name="grid", prefix="g"), Alias(self.angle, name="angle", prefix="+a"), Alias( self.skip_edge, @@ -79,23 +83,15 @@ def _aliases(self): @dataclasses.dataclass(repr=False) -class Axes(BaseParam): +class _Axes(BaseParam): """ - Class for specifying the frame of a plot. + A private class to build the Axes part of the Frame class. """ - #: Specify which axes to draw and their attributes. + # Refer to the Frame class for full documentation. axes: str | None = None - - #: Fill for the interior of the canvas [Default is no fill]. This also sets fill - #: for the two back-walls in 3-D plots. fill: str | None = None - - #: The title string centered above the plot frame [Default is no title]. title: str | None = None - - #: The subtitle string beneath the title [Default is no subtitle]. This requires - #: ``title`` to be set. subtitle: str | None = None @property @@ -114,18 +110,92 @@ class Frame(BaseParam): Class for setting up the frame of a plot. """ - axes: Any = None - xaxis: Any = None - yaxis: Any = None - zaxis: Any = None + #: Specify the attributes for each axis. + #: + #: ``axis`` means specifying the same attributes for all axes. + #: ``x``, ``y``, ``z`` mean specifying attributes for the x-, y-, and z-axes, + #: respectively, while ``xp``, ``yp``, ``zp`` mean specifying attributes for the + #: x-, y-, and z-axes' primary axes, and ``xs``, ``ys``, ``zs`` mean specifying + #: attributes for the x-, y-, and z-axes' secondary axes. + axis: Axis | None = None + x: Axis | None = None + y: Axis | None = None + z: Axis | None = None + xp: Axis | None = None + yp: Axis | None = None + zp: Axis | None = None + xs: Axis | None = None + ys: Axis | None = None + zs: Axis | None = None + + #: Specify which axes to draw and their attributes. + axes: str | None = None + + #: Fill for the interior of the canvas [Default is no fill]. This also sets fill + #: for the two back-walls in 3-D plots. + fill: str | None = None + + #: The title string centered above the plot frame [Default is no title]. + title: str | None = None + + #: The subtitle string beneath the title [Default is no subtitle]. This requires + #: ``title`` to be set. + subtitle: str | None = None + + def _validate(self): + """ + Validate the parameters. + """ + # Can't specify both axis and individual axes. + if self.axis is not None and any( + getattr(self, k) is not None + for k in [ + "x", + "y", + "z", + "xp", + "yp", + "zp", + "xs", + "ys", + "zs", + ] + ): + msg = ( + "Cannot specify both 'axis' and individual axes ('x', 'y', 'z', etc.)." + ) + raise GMTInvalidInput(msg) + if self.x is not None and (self.xp is not None or self.xs is not None): + msg = "Cannot specify both 'x' and 'xp'/'xs'." + raise GMTInvalidInput(msg) + if self.y is not None and (self.yp is not None or self.ys is not None): + msg = "Cannot specify both 'y' and 'yp'/'ys'." + raise GMTInvalidInput(msg) + if self.z is not None and (self.zp is not None or self.zs is not None): + msg = "Cannot specify both 'z' and 'zp'/'zs'." + raise GMTInvalidInput(msg) @property def _aliases(self): return [ - Alias(self.axes), - Alias(self.xaxis, prefix="x"), - Alias(self.yaxis, prefix="y"), - Alias(self.zaxis, prefix="z"), + Alias(self.axis, name="axis"), + Alias(self.x, prefix="x"), + Alias(self.y, prefix="y"), + Alias(self.z, prefix="z"), + Alias(self.xp, prefix="px"), + Alias(self.yp, prefix="py"), + Alias(self.zp, prefix="pz"), + Alias(self.xs, prefix="sx"), + Alias(self.ys, prefix="sy"), + Alias(self.zs, prefix="sz"), + Alias( + _Axes( + axes=self.axes, + fill=self.fill, + title=self.title, + subtitle=self.subtitle, + ) + ), ] def __iter__(self): diff --git a/pygmt/tests/test_params_frame.py b/pygmt/tests/test_params_frame.py index 85de1bb406b..779db180918 100644 --- a/pygmt/tests/test_params_frame.py +++ b/pygmt/tests/test_params_frame.py @@ -2,29 +2,61 @@ Test the Frame/Axes/Axis classes. """ -from pygmt.params import Axes, Axis +from pygmt.params import Axis, Frame def test_params_axis(): """ Test the Axis class. """ - assert str(Axis(interval="a1f0.5")) == "a1f0.5" - assert str(Axis(interval="a1f0.5", angle=30)) == "a1f0.5+a30" - assert str(Axis(interval="a1f0.5", angle=30, skip_edge="left")) == "a1f0.5+a30+el" - assert str(Axis(interval="a1f0.5", fancy=True)) == "a1f0.5+f" - assert str(Axis(interval="a1f0.5", label="My Label")) == "a1f0.5+lMy Label" - assert str(Axis(interval="a1f0.5", hlabel="My HLabel")) == "a1f0.5+LMy HLabel" - assert str(Axis(interval="a1f0.5", alt_label="Alt Label")) == "a1f0.5+sAlt Label" - assert str(Axis(interval="a1f0.5", alt_hlabel="Alt HLabel")) == "a1f0.5+SAlt HLabel" - assert str(Axis(interval="a1f0.5", unit="km")) == "a1f0.5+ukm" - - -def test_params_axes(): + assert str(Axis(annotation=1, tick=0.5)) == "a1f0.5" + assert str(Axis(annotation=1, tick=0.5, angle=30)) == "a1f0.5+a30" + assert ( + str(Axis(annotation=1, tick=0.5, angle=30, skip_edge="left")) == "a1f0.5+a30+el" + ) + assert str(Axis(annotation=1, tick=0.5, fancy=True)) == "a1f0.5+f" + assert str(Axis(annotation=1, tick=0.5, label="My Label")) == "a1f0.5+lMy Label" + assert str(Axis(annotation=1, tick=0.5, hlabel="My HLabel")) == "a1f0.5+LMy HLabel" + assert ( + str(Axis(annotation=1, tick=0.5, alt_label="Alt Label")) == "a1f0.5+sAlt Label" + ) + assert ( + str(Axis(annotation=1, tick=0.5, alt_hlabel="Alt HLabel")) + == "a1f0.5+SAlt HLabel" + ) + assert str(Axis(annotation=1, tick=0.5, unit="km")) == "a1f0.5+ukm" + + +def test_params_frame(): """ - Test the Axes class. + Test the Frame class. """ - assert ( - str(Axes("WSen", title="My Plot Title", fill="lightred")) - == "WSen+glightred+tMy Plot Title" + frame = Frame(axes="WSen") + assert str(frame) == "WSen" + + frame = Frame(title="My Plot Title") + assert str(frame) == "+tMy Plot Title" + + frame = Frame(subtitle="My Subtitle") + assert str(frame) == "+sMy Subtitle" + + frame = Frame(fill="red") + assert str(frame) == "+gred" + + frame = Frame(fill="lightblue", title="Plot Title", subtitle="My Subtitle") + assert str(frame) == "+glightblue+tPlot Title+sMy Subtitle" + + frame = Frame( + axes="WSen", fill="lightblue", title="Plot Title", subtitle="My Subtitle" + ) + assert str(frame) == "WSen+glightblue+tPlot Title+sMy Subtitle" + + frame = Frame( + x=Axis(annotation=1, tick=0.5), y=Axis(label="Y Axis"), title="Plot Title" + ) + assert list(frame) == ["xa1f0.5", "y+lY Axis", "+tPlot Title"] + + frame = Frame( + axis=Axis(annotation=1, tick=0.5, label="Label", angle=30), title="Plot Title" ) + assert list(frame) == ["a1f0.5+a30+lLabel", "+tPlot Title"] From 600e32d9006229c4e75b64da94f7240c6f5364bc Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 9 Jan 2026 17:59:37 +0800 Subject: [PATCH 4/7] Add Axis to doc --- doc/api/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/api/index.rst b/doc/api/index.rst index 0376ef29b5f..13dd61f71fd 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -212,6 +212,7 @@ Class-style Parameters :toctree: generated :template: autosummary/params.rst + Axis Box Frame Pattern From f4e04041a90ca6ec23b52daba4447e9c52f015c4 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 12 Jan 2026 00:10:09 +0800 Subject: [PATCH 5/7] Add more parameters --- pygmt/params/frame.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/pygmt/params/frame.py b/pygmt/params/frame.py index 031f42e4961..c74b9c8dd49 100644 --- a/pygmt/params/frame.py +++ b/pygmt/params/frame.py @@ -3,6 +3,7 @@ """ import dataclasses +from collections.abc import Sequence from typing import Literal from pygmt.alias import Alias @@ -93,6 +94,12 @@ class _Axes(BaseParam): fill: str | None = None title: str | None = None subtitle: str | None = None + box: bool = False + pen: str | bool = False + yzfill: str | None = None + xzfill: str | None = None + xyfill: str | None = None + pole: Sequence[float | str] | None = None @property def _aliases(self): @@ -101,6 +108,12 @@ def _aliases(self): Alias(self.fill, name="fill", prefix="+g"), Alias(self.title, name="title", prefix="+t"), Alias(self.subtitle, name="subtitle", prefix="+s"), + Alias(self.box, name="box", prefix="+b"), + Alias(self.pen, name="pen", prefix="+w"), + Alias(self.yzfill, name="yzfill", prefix="+y"), + Alias(self.xzfill, name="xzfill", prefix="+x"), + Alias(self.xyfill, name="xyfill", prefix="+z"), + Alias(self.pole, name="pole", prefix="+o"), ] @@ -142,6 +155,25 @@ class Frame(BaseParam): #: ``title`` to be set. subtitle: str | None = None + #: [For 3-D plots only] Draw the foreground lines of the 3-D cube . + box: bool = False + + #: [For 3-D plots only] If ``True``, draw the outlines of the x-z and y-z planes. + #: Set pen to specify different pen attributes [default is + #: :gmt-term:`MAP_GRID_PEN_PRIMARY`]. + pen: str | bool = False + + #: Fill the y-z, x-z, or x-y planes with specified color/pattern. + yzfill: str | None = None + xzfill: str | None = None + xyfill: str | None = None + + #: Specify another pole (*lon*, *lat) to produce oblique gridlines about the + #: specified pole rather than the default [default references to the North pole]. + #: Ignored if no gridlines are requested. Note: One cannot specify oblique gridlines + #: for non-geographic projections as well as the oblique Mercator projection. + pole: Sequence[float | str] | None = None + def _validate(self): """ Validate the parameters. @@ -194,6 +226,12 @@ def _aliases(self): fill=self.fill, title=self.title, subtitle=self.subtitle, + box=self.box, + pen=self.pen, + yzfill=self.yzfill, + xzfill=self.xzfill, + xyfill=self.xyfill, + pole=self.pole, ) ), ] From bc1eb653b7ade48c9c58577a355fc87b9eca2e16 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 12 Jan 2026 00:29:53 +0800 Subject: [PATCH 6/7] Add more tests --- pygmt/params/frame.py | 6 +- pygmt/tests/test_params_frame.py | 101 ++++++++++++++++++++----------- 2 files changed, 67 insertions(+), 40 deletions(-) diff --git a/pygmt/params/frame.py b/pygmt/params/frame.py index c74b9c8dd49..c28a3f1c867 100644 --- a/pygmt/params/frame.py +++ b/pygmt/params/frame.py @@ -52,8 +52,7 @@ class Axis(BaseParam): #: Add a leading text prefix for axis annotation (e.g., dollar sign for plots #: related to money) (for Cartesian plots only). For geographic maps the addition - #: of degree symbols, etc. is automatic and controlled by - #: :gmt-term:`FORMAT_GEO_MAP`. + #: of degree symbols is automatic and controlled by :gmt-term:`FORMAT_GEO_MAP`. prefix: str | None = None #: Append a unit to the annotations (for Cartesian plots only). For geographic maps @@ -79,6 +78,7 @@ def _aliases(self): Alias(self.hlabel, name="hlabel", prefix="+L"), Alias(self.alt_label, name="alt_label", prefix="+s"), Alias(self.alt_hlabel, name="alt_hlabel", prefix="+S"), + Alias(self.prefix, name="prefix", prefix="+p"), Alias(self.unit, name="unit", prefix="+u"), ] @@ -113,7 +113,7 @@ def _aliases(self): Alias(self.yzfill, name="yzfill", prefix="+y"), Alias(self.xzfill, name="xzfill", prefix="+x"), Alias(self.xyfill, name="xyfill", prefix="+z"), - Alias(self.pole, name="pole", prefix="+o"), + Alias(self.pole, name="pole", prefix="+o", sep="/", size=2), ] diff --git a/pygmt/tests/test_params_frame.py b/pygmt/tests/test_params_frame.py index 779db180918..048992b3776 100644 --- a/pygmt/tests/test_params_frame.py +++ b/pygmt/tests/test_params_frame.py @@ -9,54 +9,81 @@ def test_params_axis(): """ Test the Axis class. """ - assert str(Axis(annotation=1, tick=0.5)) == "a1f0.5" - assert str(Axis(annotation=1, tick=0.5, angle=30)) == "a1f0.5+a30" - assert ( - str(Axis(annotation=1, tick=0.5, angle=30, skip_edge="left")) == "a1f0.5+a30+el" - ) - assert str(Axis(annotation=1, tick=0.5, fancy=True)) == "a1f0.5+f" - assert str(Axis(annotation=1, tick=0.5, label="My Label")) == "a1f0.5+lMy Label" - assert str(Axis(annotation=1, tick=0.5, hlabel="My HLabel")) == "a1f0.5+LMy HLabel" - assert ( - str(Axis(annotation=1, tick=0.5, alt_label="Alt Label")) == "a1f0.5+sAlt Label" - ) - assert ( - str(Axis(annotation=1, tick=0.5, alt_hlabel="Alt HLabel")) - == "a1f0.5+SAlt HLabel" + assert str(Axis(annotation=True)) == "a" + assert str(Axis(annotation=True, tick=True, grid=True)) == "afg" + assert str(Axis(annotation=1, tick=0.5, grid=0.1)) == "a1f0.5g0.1" + + assert str(Axis(annotation=1, angle=30)) == "a1+a30" + assert str(Axis(annotation=1, angle=30, skip_edge="left")) == "a1+a30+el" + assert str(Axis(annotation=1, fancy=True)) == "a1+f" + assert str(Axis(annotation=1, label="My Label")) == "a1+lMy Label" + assert str(Axis(annotation=1, hlabel="My HLabel")) == "a1+LMy HLabel" + assert str(Axis(annotation=1, alt_label="Alt Label")) == "a1+sAlt Label" + assert str(Axis(annotation=1, alt_hlabel="Alt HLabel")) == "a1+SAlt HLabel" + assert str(Axis(annotation=1, prefix="$")) == "a1+p$" + assert str(Axis(annotation=1, unit="km")) == "a1+ukm" + + axis = Axis( + annotation=1, + tick=0.5, + grid=0.1, + angle=45, + skip_edge="right", + fancy=True, + label="Label", + hlabel="HLabel", + alt_label="AltLabel", + alt_hlabel="AltHLabel", + prefix="$", + unit="m", ) - assert str(Axis(annotation=1, tick=0.5, unit="km")) == "a1f0.5+ukm" + assert str(axis) == "a1f0.5g0.1+a45+er+f+lLabel+LHLabel+sAltLabel+SAltHLabel+p$+um" def test_params_frame(): """ Test the Frame class. """ - frame = Frame(axes="WSen") - assert str(frame) == "WSen" - - frame = Frame(title="My Plot Title") - assert str(frame) == "+tMy Plot Title" - - frame = Frame(subtitle="My Subtitle") - assert str(frame) == "+sMy Subtitle" - - frame = Frame(fill="red") - assert str(frame) == "+gred" - - frame = Frame(fill="lightblue", title="Plot Title", subtitle="My Subtitle") - assert str(frame) == "+glightblue+tPlot Title+sMy Subtitle" + # Test individual parameters of the Axes part. + assert str(Frame(axes="WSen")) == "WSen" + assert str(Frame(fill="red")) == "+gred" + assert str(Frame(title="My Plot Title")) == "+tMy Plot Title" + assert str(Frame(subtitle="My Subtitle")) == "+sMy Subtitle" + assert str(Frame(box=True)) == "+b" + assert str(Frame(pen="thick")) == "+wthick" + assert str(Frame(yzfill="blue")) == "+yblue" + assert str(Frame(xzfill="green")) == "+xgreen" + assert str(Frame(xyfill="yellow")) == "+zyellow" + assert str(Frame(pole=[30, -90])) == "+o30/-90" + # Test all parameters of the Axes part. frame = Frame( - axes="WSen", fill="lightblue", title="Plot Title", subtitle="My Subtitle" + axes="lrtb", + fill="lightblue", + title="Plot Title", + subtitle="My Subtitle", + box=True, + pen="1p,blue", + yzfill="pink", + xzfill="orange", + xyfill="purple", + pole=["45N", "100W"], ) - assert str(frame) == "WSen+glightblue+tPlot Title+sMy Subtitle" - - frame = Frame( - x=Axis(annotation=1, tick=0.5), y=Axis(label="Y Axis"), title="Plot Title" + assert str(frame) == ( + "lrtb+glightblue+tPlot Title+sMy Subtitle+b+w1p,blue+ypink+xorange+zpurple+o45N/100W" ) - assert list(frame) == ["xa1f0.5", "y+lY Axis", "+tPlot Title"] + # Test Frame with Axis parameters. + frame = Frame(axis=Axis(annotation=True, tick=True)) + assert str(frame) == "af" + frame = Frame(axis=Axis(annotation=1, tick=0.5, label="Y Axis"), title="Plot Title") + assert str(frame) == "a1f0.5+lY Axis+tPlot Title" + + # Test Frame with separate Axis for x and y axes. frame = Frame( - axis=Axis(annotation=1, tick=0.5, label="Label", angle=30), title="Plot Title" + x=Axis(annotation=1, tick=0.5, label="X Axis"), + y=Axis(annotation=True, tick=True, label="Y Axis"), + axes="WSen", + title="Plot Title", ) - assert list(frame) == ["a1f0.5+a30+lLabel", "+tPlot Title"] + assert list(frame) == ["xa1f0.5+lX Axis", "yaf+lY Axis", "WSen+tPlot Title"] From 152b4c63122c7c994136df32cc643e10af9d0d4e Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 12 Jan 2026 00:32:03 +0800 Subject: [PATCH 7/7] Improve docstrings --- pygmt/params/frame.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pygmt/params/frame.py b/pygmt/params/frame.py index c28a3f1c867..0a949cefcc0 100644 --- a/pygmt/params/frame.py +++ b/pygmt/params/frame.py @@ -163,12 +163,16 @@ class Frame(BaseParam): #: :gmt-term:`MAP_GRID_PEN_PRIMARY`]. pen: str | bool = False - #: Fill the y-z, x-z, or x-y planes with specified color/pattern. + #: Fill the y-z plane with specified color/pattern. yzfill: str | None = None + + #: Fill the x-z plane with specified color/pattern. xzfill: str | None = None + + #: Fill the x-y plane with specified color/pattern. xyfill: str | None = None - #: Specify another pole (*lon*, *lat) to produce oblique gridlines about the + #: Specify another pole (*lon*, *lat*) to produce oblique gridlines about the #: specified pole rather than the default [default references to the North pole]. #: Ignored if no gridlines are requested. Note: One cannot specify oblique gridlines #: for non-geographic projections as well as the oblique Mercator projection.