Skip to content

Commit 931bfc1

Browse files
committed
Figure.clip: Initial implementation
1 parent 2992d22 commit 931bfc1

File tree

5 files changed

+222
-2
lines changed

5 files changed

+222
-2
lines changed

doc/_templates/autosummary/class.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
.. rubric:: Attributes
99

1010
{% for item in attributes %}
11-
.. autoproperty::
12-
{{ objname }}.{{ item }}
11+
.. autoproperty:: {{ objname }}.{{ item }}
12+
:no-index:
1313
{% endfor %}
1414
{% endif %}
1515

doc/api/index.rst

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Plotting map elements
2525
:toctree: generated
2626

2727
Figure.basemap
28+
Figure.clip
2829
Figure.coast
2930
Figure.colorbar
3031
Figure.hlines
@@ -218,6 +219,7 @@ Miscellaneous
218219

219220
which
220221
show_versions
222+
src.ClipAccessor
221223

222224
Datasets
223225
--------

pygmt/figure.py

+10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import Literal, overload
1010

1111
from pygmt._typing import PathLike
12+
from pygmt.src import ClipAccessor
1213

1314
try:
1415
import IPython
@@ -137,6 +138,15 @@ def region(self) -> np.ndarray:
137138
wesn = lib.extract_region()
138139
return wesn
139140

141+
@property
142+
def clip(self) -> ClipAccessor:
143+
"""
144+
Set up a clipping path and only plot data inside/outside the clipped path.
145+
146+
See :class:`pygmt.src.clip.ClipAccessor <ClipAccessor>` for the usage.
147+
"""
148+
return ClipAccessor(self)
149+
140150
def savefig(
141151
self,
142152
fname: PathLike,

pygmt/src/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pygmt.src.basemap import basemap
66
from pygmt.src.binstats import binstats
77
from pygmt.src.blockm import blockmean, blockmedian, blockmode
8+
from pygmt.src.clip import ClipAccessor
89
from pygmt.src.coast import coast
910
from pygmt.src.colorbar import colorbar
1011
from pygmt.src.config import config

pygmt/src/clip.py

+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
"""
2+
Clip.
3+
"""
4+
5+
from collections.abc import Sequence
6+
7+
from pygmt.clib import Session
8+
from pygmt.helpers import build_arg_list, is_nonstr_iter
9+
10+
11+
class ClipAccessor:
12+
"""
13+
Accessor for the clip methods.
14+
"""
15+
16+
def __init__(self, fig):
17+
self._fig = fig # The parent Figure object.
18+
19+
def land(self, **kwargs):
20+
"""
21+
Clip the land area (i.e., "dry" areas).
22+
23+
Must be used as a context manager. Any plotting operations within the context
24+
manager will be clipped to the land areas.
25+
26+
Parameters
27+
----------
28+
kwargs
29+
Additional arguments passed to :meth:`pygmt.Figure.coast`.
30+
31+
Examples
32+
--------
33+
>>> from pygmt import Figure
34+
>>> from pygmt.datasets import load_earth_relief
35+
>>>
36+
>>> grid = load_earth_relief()
37+
>>> fig = Figure()
38+
>>> fig.basemap(region="g", projection="W15c", frame=True)
39+
>>> with fig.clip.land():
40+
... fig.grdimage(grid, cmap="geo")
41+
>>> fig.show()
42+
"""
43+
self.data = None
44+
self.module_enter = self.module_exit = "coast"
45+
self.kwargs_enter = {"G": True} | kwargs
46+
self.kwargs_exit = {"Q": True}
47+
return self
48+
49+
def water(self, **kwargs):
50+
"""
51+
Clip the water areas (i.e., "wet" areas such as oceans and lakes).
52+
53+
Must be used as a context manager. Any plotting operations within the context
54+
manager will be clipped to the water areas.
55+
56+
Parameters
57+
----------
58+
kwargs
59+
Additional arguments passed to :meth:`pygmt.Figure.coast`.
60+
61+
Examples
62+
--------
63+
>>> from pygmt import Figure
64+
>>> from pygmt.datasets import load_earth_relief
65+
>>>
66+
>>> grid = load_earth_relief()
67+
>>> fig = Figure()
68+
>>> fig.basemap(region="g", projection="W15c", frame=True)
69+
>>> with fig.clip.water():
70+
... fig.grdimage(grid, cmap="geo")
71+
>>> fig.show()
72+
"""
73+
self.data = None
74+
self.module_enter = self.module_exit = "coast"
75+
self.kwargs_enter = {"S": True} | kwargs
76+
self.kwargs_exit = {"Q": True}
77+
return self
78+
79+
def polygon(self, x, y, **kwargs):
80+
"""
81+
Clip polygonal paths.
82+
83+
Parameters
84+
----------
85+
x/y
86+
Coordinates of polygon.
87+
kwargs
88+
Additional arguments passed to GMT's ``clip`` module.
89+
90+
Examples
91+
--------
92+
>>> from pygmt import Figure
93+
>>> from pygmt.datasets import load_earth_relief
94+
>>>
95+
>>> grid = load_earth_relief()
96+
>>> fig = Figure()
97+
>>> fig.basemap(region="g", projection="W15c", frame=True)
98+
>>> with fig.clip.polygon(x=[-10, 10, 10, -10], y=[-10, -10, 10, 10]):
99+
... fig.grdimage(grid, cmap="geo")
100+
>>> fig.show()
101+
"""
102+
self.data = (x, y)
103+
self.module_enter = self.module_exit = "clip"
104+
self.kwargs_enter = kwargs
105+
self.kwargs_exit = {"C": True}
106+
107+
return self
108+
109+
def dcw(self, code: str | Sequence[str]):
110+
"""
111+
Clip based on the Digital Chart of the World.
112+
113+
Examples
114+
--------
115+
>>> from pygmt import Figure
116+
>>> from pygmt.datasets import load_earth_relief
117+
>>>
118+
>>> grid = load_earth_relief()
119+
>>> fig = Figure()
120+
>>> fig.basemap(region="g", projection="W15c", frame=True)
121+
>>> with fig.clip.dcw(code="JP"):
122+
... fig.grdimage(grid, cmap="geo")
123+
>>> fig.show()
124+
"""
125+
_code = ",".join(code) if is_nonstr_iter(code) else code
126+
self.data = None
127+
self.module_enter = "coast"
128+
self.kwargs_enter = {"E": _code + "+c"}
129+
self.module_exit = "coast"
130+
self.kwargs_exit = {"Q": True}
131+
return self
132+
133+
def solar(self, **kwargs):
134+
"""
135+
Clip the data to the solar terminator.
136+
137+
Examples
138+
--------
139+
>>> from pygmt import Figure
140+
>>> from pygmt.datasets import load_earth_relief
141+
>>>
142+
>>> grid = load_earth_relief()
143+
>>> fig = Figure()
144+
>>> fig.basemap(region="g", projection="W15c", frame=True)
145+
>>> with fig.clip.solar(T="c"):
146+
... fig.grdimage(grid, cmap="geo")
147+
>>> fig.show()
148+
"""
149+
self.data = None
150+
self.module_enter = "solar"
151+
self.kwargs_enter = {"G": True} | kwargs
152+
self.module_exit = "clip"
153+
self.kwargs_exit = {"C": True}
154+
155+
return self
156+
157+
def mask(self, x, y, spacing, radius=None):
158+
"""
159+
Clip the data to a mask.
160+
161+
Examples
162+
--------
163+
>>> from pygmt import Figure
164+
>>> from pygmt.datasets import load_earth_relief
165+
>>>
166+
>>> grid = load_earth_relief()
167+
>>> fig = Figure()
168+
>>> fig.basemap(region="g", projection="Q15c", frame=True)
169+
>>> with fig.clip.mask(
170+
... x=[180] * 16, y=np.arange(-80, 80, 10), spacing="30m", radius="5d"
171+
... ):
172+
... fig.grdimage(grid, cmap="geo")
173+
>>> fig.show()
174+
"""
175+
self.data = (x, y)
176+
self.module_enter = self.module_exit = "mask"
177+
self.kwargs_enter = {"I": spacing, "S": radius}
178+
self.kwargs_exit = {"C": True}
179+
return self
180+
181+
def __enter__(self):
182+
"""
183+
Enter the context manager.
184+
"""
185+
self._fig._preprocess() # Activate the current figure.
186+
with Session() as lib:
187+
if self.data:
188+
with lib.virtualfile_in(x=self.data[0], y=self.data[1]) as vintbl:
189+
lib.call_module(
190+
module=self.module_enter,
191+
args=build_arg_list(self.kwargs_enter, infile=vintbl),
192+
)
193+
else:
194+
lib.call_module(
195+
module=self.module_enter, args=build_arg_list(self.kwargs_enter)
196+
)
197+
return self
198+
199+
def __exit__(self, exc_type, exc_value, traceback):
200+
"""
201+
Exit the context manager.
202+
"""
203+
self._fig._preprocess() # Activate the current figure.
204+
with Session() as lib:
205+
lib.call_module(
206+
module=self.module_exit, args=build_arg_list(self.kwargs_exit)
207+
)

0 commit comments

Comments
 (0)