|
| 1 | +""" |
| 2 | +The Box class for specifying the box around GMT embellishments. |
| 3 | +""" |
| 4 | + |
| 5 | +import dataclasses |
| 6 | +from collections.abc import Sequence |
| 7 | + |
| 8 | +from pygmt.alias import Alias |
| 9 | +from pygmt.exceptions import GMTValueError |
| 10 | +from pygmt.params.base import BaseParam |
| 11 | + |
| 12 | + |
| 13 | +@dataclasses.dataclass(repr=False) |
| 14 | +class Box(BaseParam): |
| 15 | + """ |
| 16 | + Class for specifying the box around GMT embellishments. |
| 17 | +
|
| 18 | + Attributes |
| 19 | + ---------- |
| 20 | + clearance |
| 21 | + Set clearances between the embellishment and the box border. It can be either a |
| 22 | + scalar value or a sequence of two/four values. |
| 23 | +
|
| 24 | + - a scalar value means a uniform clearance in all four directions. |
| 25 | + - a sequence of two values means separate clearances in x- and y- directions. |
| 26 | + - a sequence of four values means separate clearances for left/right/bottom/top. |
| 27 | + fill |
| 28 | + Fill for the box. Default is no fill. |
| 29 | + pen |
| 30 | + Pen attributes for the box outline. |
| 31 | + radius |
| 32 | + Draw a rounded rectangular borders instead of sharp. Passing a value with unit |
| 33 | + to control the corner radius (default is ``"6p"``). |
| 34 | + inner_gap |
| 35 | + Gap between the outer and inner border. Default is ``"2p"``. |
| 36 | + inner_pen |
| 37 | + Pen attributes for the inner border. Default to :term:`MAP_DEFAULT_PEN`. |
| 38 | + shading_offset |
| 39 | + Place an offset background shaded region behind the box. A sequence of two |
| 40 | + values (dx, dy) indicates the shift relative to the foreground frame. Default is |
| 41 | + ``("4p", "-4p")``. |
| 42 | + shading_fill |
| 43 | + Fill for the shading region. Default is ``"gray50"``. |
| 44 | +
|
| 45 | + Examples |
| 46 | + -------- |
| 47 | + >>> from pygmt.params import Box |
| 48 | + >>> str(Box(fill="red@20")) |
| 49 | + '+gred@20' |
| 50 | + >>> str(Box(clearance=(0.2, 0.2), fill="red@20", pen="blue")) |
| 51 | + '+c0.2/0.2+gred@20+pblue' |
| 52 | + >>> str(Box(clearance=(0.2, 0.2), pen="blue", radius=True)) |
| 53 | + '+c0.2/0.2+pblue+r' |
| 54 | + >>> str(Box(clearance=(0.1, 0.2, 0.3, 0.4), pen="blue", radius="10p")) |
| 55 | + '+c0.1/0.2/0.3/0.4+pblue+r10p' |
| 56 | + >>> str( |
| 57 | + ... Box( |
| 58 | + ... clearance=0.2, |
| 59 | + ... pen="blue", |
| 60 | + ... radius="10p", |
| 61 | + ... shading_offset=("5p", "5p"), |
| 62 | + ... shading_fill="lightred", |
| 63 | + ... ) |
| 64 | + ... ) |
| 65 | + '+c0.2+pblue+r10p+s5p/5p/lightred' |
| 66 | + >>> str(Box(clearance=0.2, inner_gap="2p", inner_pen="1p,red", pen="blue")) |
| 67 | + '+c0.2+i2p/1p,red+pblue' |
| 68 | + >>> str(Box(clearance=0.2, shading_offset=("5p", "5p"), shading_fill="lightred")) |
| 69 | + '+c0.2+s5p/5p/lightred' |
| 70 | + """ |
| 71 | + |
| 72 | + """ |
| 73 | + [+i[[<gap>/]<pen>]] |
| 74 | + [+s[<dx>/<dy>/][<fill>]] |
| 75 | + """ |
| 76 | + # The GMT CLI syntax is: |
| 77 | + # |
| 78 | + # -F[+cclearances][+gfill][+i[[gap/]pen]][+p[pen]][+r[radius]][+s[[dx/dy/][shade]]] |
| 79 | + clearance: float | str | Sequence[float | str] | None = None |
| 80 | + fill: str | None = None |
| 81 | + inner_gap: float | str | None = None |
| 82 | + inner_pen: str | None = None |
| 83 | + pen: str | None = None |
| 84 | + radius: str | bool = False |
| 85 | + shading_offset: Sequence[float | str] | None = None |
| 86 | + shading_fill: str | None = None |
| 87 | + |
| 88 | + def _innerborder(self) -> list[str | float] | None: |
| 89 | + """ |
| 90 | + Inner border of the box, formatted as a list of 1-2 values, or None. |
| 91 | + """ |
| 92 | + return [v for v in (self.inner_gap, self.inner_pen) if v is not None] or None |
| 93 | + |
| 94 | + def _shading(self) -> list[str | float] | None: |
| 95 | + """ |
| 96 | + Shading for the box, formatted as a list of 1-3 values, or None. |
| 97 | + """ |
| 98 | + # Local variable to simplify the code. |
| 99 | + _shading_offset = [] if self.shading_offset is None else self.shading_offset |
| 100 | + |
| 101 | + # shading_offset must be a sequence of two values (dx, dy) or None. |
| 102 | + if len(_shading_offset) not in {0, 2}: |
| 103 | + raise GMTValueError( |
| 104 | + self.shading_offset, |
| 105 | + description="value for parameter 'shading_offset'", |
| 106 | + reason="Must be a sequence of two values (dx, dy) or None.", |
| 107 | + ) |
| 108 | + return [ |
| 109 | + v for v in (*_shading_offset, self.shading_fill) if v is not None |
| 110 | + ] or None |
| 111 | + |
| 112 | + @property |
| 113 | + def _aliases(self): |
| 114 | + """ |
| 115 | + Aliases for the parameter. |
| 116 | + """ |
| 117 | + return [ |
| 118 | + Alias(self.clearance, prefix="+c", separator="/", size=[1, 2, 4]), |
| 119 | + Alias(self.fill, prefix="+g"), |
| 120 | + Alias(self._innerborder(), prefix="+i", separator="/", size=[1, 2]), |
| 121 | + Alias(self.pen, prefix="+p"), |
| 122 | + Alias(self.radius, prefix="+r"), |
| 123 | + Alias(self._shading(), prefix="+s", separator="/", size=[1, 2, 3]), |
| 124 | + ] |
0 commit comments