Skip to content

Commit 2200457

Browse files
seismanmichaelgrundyvonnefroehlichweiji14
authored
Add the Box class for specifying the box of GMT embellishments (#3995)
Co-authored-by: Michael Grund <[email protected]> Co-authored-by: Yvonne Fröhlich <[email protected]> Co-authored-by: Wei Ji <[email protected]>
1 parent bd1086e commit 2200457

File tree

15 files changed

+311
-18
lines changed

15 files changed

+311
-18
lines changed

doc/api/index.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,16 @@ Xarray Integration
203203
GMTBackendEntrypoint
204204
GMTDataArrayAccessor
205205

206+
Class-style Parameters
207+
----------------------
208+
209+
.. currentmodule:: pygmt.params
210+
211+
.. autosummary::
212+
:toctree: generated
213+
214+
Box
215+
206216
Enums
207217
-----
208218

examples/gallery/embellishments/inset.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
# %%
1212
import pygmt
13+
from pygmt.params import Box
1314

1415
fig = pygmt.Figure()
1516
# Create the primary figure, setting the region to Madagascar, the land color
@@ -19,7 +20,7 @@
1920
# Create an inset, placing it in the Top Left (TL) corner with a width of 3.5 cm and
2021
# x- and y-offsets of 0.2 cm. The margin is set to 0, and the border is "gold" with a
2122
# pen size of 1.5 points.
22-
with fig.inset(position="jTL+w3.5c+o0.2c", margin=0, box="+p1.5p,gold"):
23+
with fig.inset(position="jTL+w3.5c+o0.2c", margin=0, box=Box(pen="1.5p,gold")):
2324
# Create a figure in the inset using coast. This example uses the azimuthal
2425
# orthogonal projection centered at 47E, 20S. The land color is set to
2526
# "gray" and Madagascar is highlighted in "red3".

examples/gallery/embellishments/inset_rectangle_region.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
# %%
1212
import pygmt
13+
from pygmt.params import Box
1314

1415
# Set the region of the main figure
1516
region = [137.5, 141, 34, 37]
@@ -34,7 +35,7 @@
3435
# a pen of "1p".
3536
with fig.inset(
3637
position="jBR+o0.1c",
37-
box="+gwhite+p1p",
38+
box=Box(fill="white", pen="1p"),
3839
region=[129, 146, 30, 46],
3940
projection="U54S/3c",
4041
):

examples/gallery/embellishments/scalebar.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
# %%
4444
import pygmt
45+
from pygmt.params import Box
4546

4647
# Create a new Figure instance
4748
fig = pygmt.Figure()
@@ -103,7 +104,7 @@
103104
# Fill the box in white with a transparency of 30 percent, add a solid
104105
# outline in darkgray (gray30) with a thickness of 0.5 points, and use
105106
# rounded edges with a radius of 3 points
106-
box="+gwhite@30+p0.5p,gray30,solid+r3p",
107+
box=Box(fill="white@30", pen="0.5p,gray30,solid", radius="3p"),
107108
)
108109

109110
fig.show()

examples/gallery/images/cross_section.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
# %%
1616
import pygmt
17+
from pygmt.params import Box
1718

1819
# Define region of study area
1920
# lon_min, lon_max, lat_min, lat_max in degrees East and North
@@ -44,9 +45,9 @@
4445
# corner with an offset ("+o") of 0.7 centimeters and 0.3 centimeters in x- or y-
4546
# directions, respectively; move the x-label above the horizontal colorbar ("+ml")
4647
position="jBR+o0.7c/0.8c+h+w5c/0.3c+ml",
47-
# Add a box around the colobar with a fill ("+g") in "white" color and a
48-
# transparency ("@") of 30 % and with a 0.8-points thick, black, outline ("+p")
49-
box="+gwhite@30+p0.8p,black",
48+
# Add a box around the colobar, filled in white and a 30 % transparency, with a
49+
# 0.8-points thick, black, outline.
50+
box=Box(pen="0.8p,black", fill="white@30"),
5051
# Add x- and y-labels ("+l")
5152
frame=["x+lElevation", "y+lm"],
5253
)

examples/gallery/lines/hlines_vlines.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# In Cartesian coordinate systems lines are plotted as straight lines.
1313

1414
import pygmt
15+
from pygmt.params import Box
1516

1617
fig = pygmt.Figure()
1718

@@ -31,7 +32,7 @@
3132
fig.hlines(
3233
y=[2, 3], xmin=[0, 1], xmax=[7, 7.5], pen="1.5p,dodgerblue3", label="Lines 7 & 8"
3334
)
34-
fig.legend(position="JBR+jBR+o0.2c", box="+gwhite+p1p")
35+
fig.legend(position="JBR+jBR+o0.2c", box=Box(pen="1p", fill="white"))
3536

3637
fig.shift_origin(xshift="w+2c")
3738

examples/tutorials/advanced/insets.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
# %%
1212
import pygmt
13+
from pygmt.params import Box
1314

1415
# %%
1516
# Prior to creating an inset figure, a larger figure must first be plotted. In
@@ -48,7 +49,7 @@
4849
water="lightblue",
4950
frame="a",
5051
)
51-
with fig.inset(position="jBL+w3c", box="+pblack+glightred"):
52+
with fig.inset(position="jBL+w3c", box=Box(pen="black", fill="lightred")):
5253
# pass is used to exit the with statement as no plotting methods are
5354
# called
5455
pass
@@ -72,7 +73,7 @@
7273
water="lightblue",
7374
frame="a",
7475
)
75-
with fig.inset(position="jBL+w3c+o0.5c/0.2c", box="+pblack+glightred"):
76+
with fig.inset(position="jBL+w3c+o0.5c/0.2c", box=Box(pen="black", fill="lightred")):
7677
pass
7778
fig.show()
7879

@@ -97,7 +98,7 @@
9798
# parameters.
9899
with fig.inset(
99100
position="jBL+o0.5c/0.2c",
100-
box="+pblack",
101+
box=Box(pen="black"),
101102
region=[-80, -65, 35, 50],
102103
projection="M3c",
103104
):

examples/tutorials/advanced/legends.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io
1111

1212
import pygmt
13+
from pygmt.params import Box
1314

1415
# %%
1516
# Create an auto-legend
@@ -89,8 +90,8 @@
8990
fig.plot(x=[-3, 3], y=[-2, -2], pen="darkred", label="darkred line")
9091

9192
# Add a box with a 2-points thick blue, solid outline and a white fill with a
92-
# transparency of 70 percentage ("@30").
93-
fig.legend(position="jTL+o0.3c/0.2c", box="+p2p,blue+gwhite@30")
93+
# transparency of 30 percent ("@30").
94+
fig.legend(position="jTL+o0.3c/0.2c", box=Box(pen="2p,blue", fill="white@30"))
9495

9596
fig.show()
9697

@@ -152,7 +153,7 @@
152153
fig.basemap(region=[-5, 5, -5, 5], projection="M10c", frame=True)
153154

154155
# Pass the io.StringIO object to the "spec" parameter
155-
fig.legend(spec=spec_io, position="jMC+w9c", box="+p1p,gray50+ggray95")
156+
fig.legend(spec=spec_io, position="jMC+w9c", box=Box(pen="1p,gray50", fill="gray95"))
156157

157158
fig.show()
158159

pygmt/params/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""
2+
Classes for common parameters in PyGMT.
3+
"""
4+
5+
from pygmt.params.box import Box

pygmt/params/base.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
"""
2+
Base class for common parameters shared in PyGMT.
3+
"""
4+
5+
from abc import ABC, abstractmethod
6+
7+
8+
class BaseParam(ABC):
9+
"""
10+
Base class for parameters in PyGMT.
11+
12+
To define a new parameter class, inherit from this class and define the attributes
13+
that correspond to the parameters you want to include.
14+
15+
The class should also implement the ``_aliases`` property, which returns a list of
16+
``Alias`` objects. Each ``Alias`` object represents a parameter and its value, and
17+
the ``__str__`` method will concatenate these values into a single string that can
18+
be passed to GMT.
19+
20+
Optionally, you can override the ``_validate`` method to perform any necessary
21+
validation on the parameters after initialization.
22+
23+
Examples
24+
--------
25+
>>> from typing import Any
26+
>>> import dataclasses
27+
>>> from pygmt.params.base import BaseParam
28+
>>> from pygmt.alias import Alias
29+
>>>
30+
>>> @dataclasses.dataclass(repr=False)
31+
... class Test(BaseParam):
32+
... par1: Any = None
33+
... par2: Any = None
34+
... par3: Any = None
35+
...
36+
... @property
37+
... def _aliases(self):
38+
... return [
39+
... Alias(self.par1),
40+
... Alias(self.par2, prefix="+a"),
41+
... Alias(self.par3, prefix="+b", sep="/"),
42+
... ]
43+
44+
>>> var = Test(par1="val1")
45+
>>> str(var)
46+
'val1'
47+
>>> repr(var)
48+
"Test(par1='val1')"
49+
50+
>>> var = Test(par1="val1", par2="val2", par3=("val3a", "val3b"))
51+
>>> str(var)
52+
'val1+aval2+bval3a/val3b'
53+
>>> repr(var)
54+
"Test(par1='val1', par2='val2', par3=('val3a', 'val3b'))"
55+
"""
56+
57+
def __post_init__(self):
58+
"""
59+
Post-initialization method to _validate the _aliases property.
60+
"""
61+
self._validate()
62+
63+
def _validate(self): # noqa: B027
64+
"""
65+
Validate the parameters of the object.
66+
67+
Optional method but can be overridden in subclasses to perform any necessary
68+
validation on the parameters.
69+
"""
70+
71+
@property
72+
@abstractmethod
73+
def _aliases(self):
74+
"""
75+
List of Alias objects representing the parameters of this class.
76+
77+
Must be implemented in subclasses to define the parameters and their aliases.
78+
"""
79+
80+
def __str__(self):
81+
"""
82+
String representation of the object that can be passed to GMT directly.
83+
"""
84+
return "".join(
85+
[alias._value for alias in self._aliases if alias._value is not None]
86+
)
87+
88+
def __repr__(self):
89+
"""
90+
String representation of the object.
91+
"""
92+
params = ", ".join(
93+
f"{k}={v!r}"
94+
for k, v in vars(self).items()
95+
if v is not None and v is not False
96+
)
97+
return f"{self.__class__.__name__}({params})"

0 commit comments

Comments
 (0)