Skip to content

Commit cda4b8c

Browse files
committed
Figure.shift_origin: Refactor to support shifting origins temporarily
1 parent 9dda628 commit cda4b8c

File tree

2 files changed

+114
-26
lines changed

2 files changed

+114
-26
lines changed

pygmt/src/shift_origin.py

Lines changed: 91 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,39 @@
11
"""
2-
shift_origin - Shift plot origin in x and/or y directions.
2+
shift_origin - Shift the plot origin in x and/or y directions.
33
"""
44

55
from pygmt.clib import Session
6+
from pygmt.helpers import build_arg_list
67

78

8-
def shift_origin(
9-
self, xshift: float | str | None = None, yshift: float | str | None = None
10-
):
9+
class shift_origin: # noqa: N801
1110
r"""
12-
Shift plot origin in x and/or y directions.
11+
Shift the plot origin in x and/or y directions.
1312
14-
This method shifts the plot origin relative to the current origin by *xshift* and
15-
*yshift* in x and y directions, respectively. Optionally, append the length unit
13+
The shifts can be temporary or permanent. If used as a context manager, the shifts
14+
are temporary and only apply to the block of code within the context manager. If
15+
used as a standalone method, the shifts are permanent and apply to all subsequent
16+
plots.
17+
18+
1. Use as a context manager to shift the plot origin temporarily:
19+
20+
.. code-block:: python
21+
22+
with fig.shift_origin(...):
23+
... # Other plot commands
24+
...
25+
26+
2. Use as a standalone method to shift the plot origin permanently:
27+
28+
.. code-block:: python
29+
30+
fig.shift_origin(xshift=12)
31+
... # Other plot commands
32+
33+
The shifts *xshift* and *yshift* in x and y directions are relative to the current
34+
plot origin. The default unit for shifts is centimeters (**c**) but can be changed
35+
to other units via :gmt-term:`PROJ_LENGTH_UNIT`. Optionally, append the length unit
1636
(**c** for centimeters, **i** for inches, or **p** for points) to the shifts.
17-
Default unit if not explicitly given is **c**, but can be changed to other units via
18-
:gmt-term:`PROJ_LENGTH_UNIT`.
1937
2038
For *xshift*, a special character **w** can also be used, which represents the
2139
bounding box **width** of the previous plot. The full syntax is
@@ -44,23 +62,71 @@ def shift_origin(
4462
4563
Examples
4664
--------
65+
4766
>>> import pygmt
67+
68+
Shifting the plot origin permanently:
69+
70+
>>> fig = pygmt.Figure()
71+
>>> fig.basemap(region=[0, 5, 0, 5], projection="X5c/5c", frame=True)
72+
>>> # Shift the plot origin in x direction by 6 cm
73+
>>> fig.shift_origin(xshift=6)
74+
<pygmt.src.shift_origin.shift_origin object at ...>
75+
>>> fig.basemap(region=[0, 7, 0, 5], projection="X7c/5c", frame=True)
76+
>>> # Shift the plot origin in x direction based on the previous plot width.
77+
>>> # Here, the width is 7 cm, and xshift is 8 cm.
78+
>>> fig.shift_origin(xshift="w+1c")
79+
<pygmt.src.shift_origin.shift_origin object at ...>
80+
>>> fig.basemap(region=[0, 10, 0, 5], projection="X10c/5c", frame=True)
81+
>>> fig.show()
82+
83+
Shifting the plot origin temporarily:
84+
4885
>>> fig = pygmt.Figure()
49-
>>> fig.basemap(region=[0, 10, 0, 10], projection="X10c/10c", frame=True)
50-
>>> # Shift the plot origin in x direction by 12 cm
51-
>>> fig.shift_origin(xshift=12)
52-
>>> fig.basemap(region=[0, 10, 0, 10], projection="X14c/10c", frame=True)
53-
>>> # Shift the plot origin in x direction based on the previous plot width
54-
>>> # Here, the width is 14 cm, and xshift is 16 cm
55-
>>> fig.shift_origin(xshift="w+2c")
86+
>>> fig.basemap(region=[0, 5, 0, 5], projection="X5c/5c", frame=True)
87+
>>> # Shift the plot origin in x direction by 6 cm temporarily. The plot origin will
88+
>>> # revert back to the original plot origin after the block of code is executed.
89+
>>> with fig.shift_origin(xshift=6):
90+
... fig.basemap(region=[0, 5, 0, 5], projection="X5c/5c", frame=True)
91+
>>> # Shift the plot origin in y direction by 6 cm temporarily.
92+
>>> with fig.shift_origin(yshift=6):
93+
... fig.basemap(region=[0, 5, 0, 5], projection="X5c/5c", frame=True)
94+
>>> # Shift the plot origin in x and y directions by 6 cm temporarily.
95+
>>> with fig.shift_origin(xshift=6, yshift=6):
96+
... fig.basemap(region=[0, 5, 0, 5], projection="X5c/5c", frame=True)
5697
>>> fig.show()
98+
5799
"""
58-
self._preprocess()
59-
args = ["-T"]
60-
if xshift:
61-
args.append(f"-X{xshift}")
62-
if yshift:
63-
args.append(f"-Y{yshift}")
64-
65-
with Session() as lib:
66-
lib.call_module(module="plot", args=args)
100+
101+
def __init__(
102+
self, xshift: float | str | None = None, yshift: float | str | None = None
103+
):
104+
"""
105+
Shift the plot origin in x/y directions and store the shift values.
106+
"""
107+
# self._preprocess() # pylint: disable=protected-access
108+
kwdict = {"T": True, "X": xshift, "Y": yshift}
109+
with Session() as lib:
110+
lib.call_module(module="plot", args=build_arg_list(kwdict))
111+
self._xshift = lib.get_common("X") # False or xshift in inches
112+
self._yshift = lib.get_common("Y") # False or yshift in inches
113+
114+
def __enter__(self):
115+
"""
116+
Do nothing but return self.
117+
"""
118+
return self
119+
120+
def __exit__(self, exc_type, exc_value, traceback):
121+
"""
122+
Revert to the original plot origin if the context manager is exited.
123+
"""
124+
# xshift and yshift are in inches, so we need to negate them to revert to the
125+
# original plot origin.
126+
kwdict = {
127+
"T": True,
128+
"X": f"{-1.0 * self._xshift}i" if self._xshift else None,
129+
"Y": f"{-1.0 * self._yshift}i" if self._yshift else None,
130+
}
131+
with Session() as lib:
132+
lib.call_module(module="plot", args=build_arg_list(kwdict))

pygmt/tests/test_shift_origin.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"""
44

55
import pytest
6+
from pygmt import Figure
67
from pygmt.exceptions import GMTInvalidInput
7-
from pygmt.figure import Figure
88

99

1010
@pytest.mark.mpl_image_compare
@@ -27,6 +27,28 @@ def test_shift_origin():
2727
return fig
2828

2929

30+
def test_shift_origin_context_manager():
31+
"""
32+
Test Figure.shift_origin.
33+
"""
34+
fig = Figure()
35+
fig.basemap(region=[0, 1, 0, 1], projection="X3c", frame=["WSen+t1"])
36+
fig.shift_origin(xshift=3, yshift=3)
37+
fig.basemap(region=[0, 1, 0, 1], projection="X3c", frame=["WSen+t2"])
38+
39+
with fig.shift_origin(xshift=3):
40+
fig.basemap(region=[0, 1, 0, 1], projection="X3c", frame=["WSen+t3"])
41+
fig.basemap(region=[0, 1, 0, 1], projection="X3c", frame=["WSen+t4"])
42+
with fig.shift_origin(xshift=3):
43+
fig.basemap(region=[0, 1, 0, 1], projection="X3c", frame=["WSen+t5"])
44+
with fig.shift_origin(yshift=3):
45+
fig.basemap(region=[0, 1, 0, 1], projection="X3c", frame=["WSen+t6"])
46+
with fig.shift_origin(xshift=3, yshift=3):
47+
fig.basemap(region=[0, 1, 0, 1], projection="X3c", frame=["WSen+t7"])
48+
fig.basemap(region=[0, 1, 0, 1], projection="X3c", frame=["WSen+t8"])
49+
return fig
50+
51+
3052
def test_shift_origin_unsupported_xshift_yshift():
3153
"""
3254
Raise an exception if X/Y/xshift/yshift is used.

0 commit comments

Comments
 (0)