-
Notifications
You must be signed in to change notification settings - Fork 230
Add the Box class for specifying the box of GMT embellishments #3995
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 20 commits
b73a6e2
9f08d1b
01b563c
d2ae10c
ec1af1d
48702b5
210292f
8e5321e
926a690
b1c6330
bc31c7c
de1545f
44b0be3
1d14eb5
6ddcf4e
84ca37e
0313330
40a47a8
457abed
16c3d6c
ab7ad67
3c1ee05
9d60637
c4a19a5
5ec1180
86d0a32
4e98acf
22fb1eb
a83f21d
dce0e47
078dffb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
""" | ||
Classes for common parameters in PyGMT. | ||
""" | ||
|
||
from pygmt.params.box import Box |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
""" | ||
Base class for common parameters shared in PyGMT. | ||
""" | ||
|
||
|
||
class BaseParam: | ||
""" | ||
Base class for parameters in PyGMT. | ||
|
||
To define a new parameter class, inherit from this class and define the attributes | ||
that correspond to the parameters you want to include. | ||
|
||
The class should also implement the ``_aliases`` property, which returns a list of | ||
``Alias`` objects. Each ``Alias`` object represents a parameter and its value, and | ||
the ``__str__`` method will concatenate these values into a single string that can | ||
be passed to GMT. | ||
|
||
Optionally, you can override the ``_validate`` method to perform any necessary | ||
validation on the parameters after initialization. | ||
|
||
Examples | ||
-------- | ||
>>> from typing import Any | ||
>>> import dataclasses | ||
>>> from pygmt.params.base import BaseParam | ||
>>> from pygmt.alias import Alias | ||
>>> | ||
>>> @dataclasses.dataclass(repr=False) | ||
... class Test(BaseParam): | ||
... par1: Any = None | ||
... par2: Any = None | ||
... par3: Any = None | ||
... | ||
... @property | ||
... def _aliases(self): | ||
... return [ | ||
... Alias(self.par1), | ||
... Alias(self.par2, prefix="+a"), | ||
... Alias(self.par3, prefix="+b", sep="/"), | ||
... ] | ||
|
||
>>> var = Test(par1="val1") | ||
>>> str(var) | ||
'val1' | ||
>>> repr(var) | ||
"Test(par1='val1')" | ||
|
||
>>> var = Test(par1="val1", par2="val2", par3=("val3a", "val3b")) | ||
>>> str(var) | ||
'val1+aval2+bval3a/val3b' | ||
>>> repr(var) | ||
"Test(par1='val1', par2='val2', par3=('val3a', 'val3b'))" | ||
""" | ||
|
||
def __post_init__(self): | ||
""" | ||
Post-initialization method to _validate the _aliases property. | ||
""" | ||
self._validate() | ||
|
||
def _validate(self): | ||
""" | ||
Validate the parameters of the object. | ||
|
||
This method should be overridden in subclasses to perform any necessary | ||
validation on the parameters. | ||
""" | ||
|
||
@property | ||
def _aliases(self): | ||
""" | ||
List of Alias objects representing the parameters of this class. | ||
|
||
This property must be implemented in subclasses to define the parameters | ||
and their aliases. | ||
""" | ||
msg = "The _aliases property must be implemented in subclasses." | ||
raise NotImplementedError(msg) | ||
|
||
def __str__(self): | ||
""" | ||
String representation of the object that can be passed to GMT directly. | ||
""" | ||
return "".join( | ||
[alias._value for alias in self._aliases if alias._value is not None] | ||
) | ||
|
||
def __repr__(self): | ||
""" | ||
String representation of the object. | ||
""" | ||
params = ", ".join( | ||
f"{k}={v!r}" | ||
for k, v in vars(self).items() | ||
if v is not None and v is not False | ||
) | ||
return f"{self.__class__.__name__}({params})" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
""" | ||
The Box class for specifying the box around GMT embellishments. | ||
""" | ||
|
||
import dataclasses | ||
from collections.abc import Sequence | ||
|
||
from pygmt.alias import Alias | ||
from pygmt.exceptions import GMTValueError | ||
from pygmt.params.base import BaseParam | ||
|
||
|
||
@dataclasses.dataclass(repr=False) | ||
class Box(BaseParam): | ||
""" | ||
Class for specifying the box around GMT embellishments. | ||
|
||
Attributes | ||
---------- | ||
clearance | ||
Set clearances between the embellishment and the box border. It can be either a | ||
scalar value or a sequence of two/four values. | ||
|
||
- a scalar value means a uniform clearance in all four directions. | ||
- a sequence of two values means separate clearances in x- and y-directions. | ||
- a sequence of four values means separate clearances for left/right/bottom/top. | ||
fill | ||
Fill for the box. Default is no fill. | ||
seisman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pen | ||
Pen attributes for the box outline. | ||
radius | ||
Draw a rounded rectangular border instead of sharp. Passing a value with unit | ||
to control the corner radius [Default is ``"6p"``]. | ||
inner_gap | ||
Gap between the outer and inner borders. Default is ``"2p"``. | ||
seisman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
inner_pen | ||
Pen attributes for the inner border. Default to :gmt-term:`MAP_DEFAULT_PEN`. | ||
shading_offset | ||
Place an offset background shaded region behind the box. A sequence of two | ||
values (dx, dy) indicates the shift relative to the foreground frame. Default is | ||
``("4p", "-4p")``. | ||
seisman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
shading_fill | ||
Fill for the shading region. Default is ``"gray50"``. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We still have the rule to use underscores in parameter names only for bridging vocals (https://www.pygmt.org/latest/contributing.html#code-style). How strict do we want to be with this for introducing aliases for the modifiers? I personally feel that underscores make the aliases in general more readable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If we follow the rule strictly, then the two parameter names would be
seisman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Examples | ||
-------- | ||
>>> from pygmt.params import Box | ||
>>> str(Box(fill="red@20")) | ||
'+gred@20' | ||
>>> str(Box(clearance=(0.2, 0.2), fill="red@20", pen="blue")) | ||
'+c0.2/0.2+gred@20+pblue' | ||
>>> str(Box(clearance=(0.2, 0.2), pen="blue", radius=True)) | ||
'+c0.2/0.2+pblue+r' | ||
>>> str(Box(clearance=(0.1, 0.2, 0.3, 0.4), pen="blue", radius="10p")) | ||
'+c0.1/0.2/0.3/0.4+pblue+r10p' | ||
>>> str( | ||
... Box( | ||
... clearance=0.2, | ||
... pen="blue", | ||
... radius="10p", | ||
... shading_offset=("5p", "5p"), | ||
... shading_fill="lightred", | ||
... ) | ||
... ) | ||
'+c0.2+pblue+r10p+s5p/5p/lightred' | ||
>>> str(Box(clearance=0.2, inner_gap="2p", inner_pen="1p,red", pen="blue")) | ||
'+c0.2+i2p/1p,red+pblue' | ||
>>> str(Box(clearance=0.2, shading_offset=("5p", "5p"), shading_fill="lightred")) | ||
'+c0.2+s5p/5p/lightred' | ||
""" | ||
|
||
clearance: float | str | Sequence[float | str] | None = None | ||
fill: str | None = None | ||
inner_gap: float | str | None = None | ||
inner_pen: str | None = None | ||
pen: str | None = None | ||
radius: str | bool = False | ||
shading_offset: Sequence[float | str] | None = None | ||
shading_fill: str | None = None | ||
|
||
def _validate(self): | ||
""" | ||
Validate the parameters. | ||
""" | ||
# shading_offset must be a sequence of two values or None. | ||
if self.shading_offset and ( | ||
not isinstance(self.shading_offset, Sequence) | ||
or len(self.shading_offset) != 2 | ||
): | ||
raise GMTValueError( | ||
self.shading_offset, | ||
description="value for parameter 'shading_offset'", | ||
reason="Must be a sequence of two values (dx, dy) or None.", | ||
) | ||
|
||
@property | ||
def _innerborder(self) -> list[str | float] | None: | ||
""" | ||
Inner border of the box, formatted as a list of 1-2 values, or None. | ||
""" | ||
return [v for v in (self.inner_gap, self.inner_pen) if v is not None] or None | ||
seisman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@property | ||
def _shading(self) -> list[str | float] | None: | ||
""" | ||
Shading for the box, formatted as a list of 1-3 values, or None. | ||
""" | ||
_shading_offset = self.shading_offset or [] | ||
return [ | ||
v for v in (*_shading_offset, self.shading_fill) if v is not None | ||
] or None | ||
|
||
@property | ||
def _aliases(self): | ||
""" | ||
Aliases for the parameter. | ||
""" | ||
return [ | ||
Alias(self.clearance, prefix="+c", sep="/", size=(2, 4)), | ||
Alias(self.fill, prefix="+g"), | ||
Alias(self._innerborder, prefix="+i", sep="/", size=(1, 2)), | ||
Alias(self.pen, prefix="+p"), | ||
Alias(self.radius, prefix="+r"), | ||
Alias(self._shading, prefix="+s", sep="/", size=(1, 2, 3)), | ||
] |
Uh oh!
There was an error while loading. Please reload this page.