From fad1d47c97e6d0baf5ce774f048bd9e472d28f75 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sat, 26 Jul 2025 15:27:00 +0800 Subject: [PATCH 1/3] Add the Axes, Axis, Frame classes for the frame parameter --- pygmt/params/frame.py | 104 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 pygmt/params/frame.py diff --git a/pygmt/params/frame.py b/pygmt/params/frame.py new file mode 100644 index 00000000000..286401d30bf --- /dev/null +++ b/pygmt/params/frame.py @@ -0,0 +1,104 @@ +""" +The frame parameter. +""" + +from dataclasses import dataclass +from typing import Any + +from pygmt.alias import Alias +from pygmt.params.base import BaseParam + + +@dataclass(repr=False) +class Axes(BaseParam): + """ + Class for setting up the axes, title, and fill of a plot. + + Examples + -------- + >>> from pygmt.params import Axes + >>> str(Axes("WSen", title="My Plot Title", fill="lightred")) + 'WSen+glightred+tMy Plot Title' + """ + + axes: Any = None + fill: Any = None + title: Any = None + + @property + def _aliases(self): + return [ + Alias(self.axes), + Alias(self.fill, prefix="+g"), + Alias(self.title, prefix="+t"), + ] + + +@dataclass(repr=False) +class Axis(BaseParam): + """ + Class for setting up one axis of a plot. + + Examples + -------- + >>> from pygmt.params import Axis + >>> str(Axis(10, angle=30, label="X axis", unit="km")) + '10+a30+lX axis+ukm' + """ + + interval: float | str + angle: float | str | None = None + label: str | None = None + unit: str | None = None + + @property + def _aliases(self): + return [ + Alias(self.interval), + Alias(self.angle, prefix="+a"), + Alias(self.label, prefix="+l"), + Alias(self.unit, prefix="+u"), + ] + + +@dataclass(repr=False) +class Frame(BaseParam): + """ + Class for setting up the frame of a plot. + + >>> from pygmt.alias import AliasSystem, Alias + >>> from pygmt.params import Frame, Axes, Axis + >>> frame = Frame( + ... axes=Axes("WSen", title="My Plot Title", fill="lightred"), + ... xaxis=Axis(10, angle=30, label="X axis", unit="km"), + ... ) + >>> def func(frame): + ... alias = AliasSystem(B=Alias(frame)) + ... return alias.kwdict + >>> dict(func(frame)) + {'B': ['WSen+glightred+tMy Plot Title', 'x10+a30+lX axis+ukm']} + """ + + 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) From c5d07cf9173b525738bbb5500af67c1cee24e9c2 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 15 Sep 2025 20:13:45 +0800 Subject: [PATCH 2/3] updates --- pygmt/params/frame.py | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/pygmt/params/frame.py b/pygmt/params/frame.py index 286401d30bf..137beb12168 100644 --- a/pygmt/params/frame.py +++ b/pygmt/params/frame.py @@ -1,18 +1,30 @@ """ -The frame parameter. +The Axes, Axis, and Frame classes for specifying the frame. """ -from dataclasses import dataclass -from typing import Any - +import dataclasses from pygmt.alias import Alias from pygmt.params.base import BaseParam +from typing import Any -@dataclass(repr=False) +@dataclasses.dataclass(repr=False) class Axes(BaseParam): """ - Class for setting up the axes, title, and fill of a plot. + Class for specifying the frame of a plot. + + Attributes + ---------- + axes + Specify which axes to draw and their attributes. + fill + Fill for the interior of the canvas [Default is no fill]. This also sets fill + for the two back-walls in 3-D plots. + title + The title string centered above the plot frame [Default is no title]. + subtitle + The subtitle string beneath the title [Default is no subtitle]. This requires + ``title`` to be set. Examples -------- @@ -21,20 +33,22 @@ class Axes(BaseParam): 'WSen+glightred+tMy Plot Title' """ - axes: Any = None - fill: Any = None - title: Any = None + axes: str | None = None + fill: str | None = None + title: str | None = None + subtitle: str | None = None @property def _aliases(self): return [ - Alias(self.axes), - Alias(self.fill, prefix="+g"), - Alias(self.title, prefix="+t"), + 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"), ] -@dataclass(repr=False) +@dataclasses.dataclass(repr=False) class Axis(BaseParam): """ Class for setting up one axis of a plot. @@ -61,7 +75,7 @@ def _aliases(self): ] -@dataclass(repr=False) +@dataclasses.dataclass(repr=False) class Frame(BaseParam): """ Class for setting up the frame of a plot. From e0af7dcb1737756af8bb4c59644cd644718fe3bf Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 17 Sep 2025 00:28:53 +0800 Subject: [PATCH 3/3] Improve frame --- pygmt/params/frame.py | 67 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/pygmt/params/frame.py b/pygmt/params/frame.py index 137beb12168..c9fa4224f0d 100644 --- a/pygmt/params/frame.py +++ b/pygmt/params/frame.py @@ -3,9 +3,10 @@ """ import dataclasses +from typing import Any, Literal + from pygmt.alias import Alias from pygmt.params.base import BaseParam -from typing import Any @dataclasses.dataclass(repr=False) @@ -53,6 +54,42 @@ class Axis(BaseParam): """ Class for setting up one axis of a plot. + Attributes + ---------- + interval + Intervals for annotations and major tick spacing, minor tick spacing, and/or + grid line spacing. + angle + 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`. + skip_edge + 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. + fancy + Give fancy annotations with W|E|S|N suffixes encoding the sign (for geographic + axes only). + label/hlabel + 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. + alt_label/alt_hlabel + 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]. + prefix + 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`. + unit + 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`. + Examples -------- >>> from pygmt.params import Axis @@ -60,18 +97,34 @@ class Axis(BaseParam): '10+a30+lX axis+ukm' """ - interval: float | str - angle: float | str | None = None + interval: float | str # How to make it more Pythonic? + angle: float | None = None + skip_edge: Literal["left", "right"] | bool = False + fancy: bool = False label: str | None = None + hlabel: str | None = None + alt_label: str | None = None + alt_hlabel: str | None = None + prefix: str | None = None unit: str | None = None @property def _aliases(self): return [ - Alias(self.interval), - Alias(self.angle, prefix="+a"), - Alias(self.label, prefix="+l"), - Alias(self.unit, prefix="+u"), + 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"), ]