From 931bfc1bf1bfe5bc33245b41cace200c9c7e4cc9 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 26 Mar 2025 19:38:27 +0800 Subject: [PATCH 01/13] Figure.clip: Initial implementation --- doc/_templates/autosummary/class.rst | 4 +- doc/api/index.rst | 2 + pygmt/figure.py | 10 ++ pygmt/src/__init__.py | 1 + pygmt/src/clip.py | 207 +++++++++++++++++++++++++++ 5 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 pygmt/src/clip.py diff --git a/doc/_templates/autosummary/class.rst b/doc/_templates/autosummary/class.rst index 57a35f189a2..c3d36b90a43 100644 --- a/doc/_templates/autosummary/class.rst +++ b/doc/_templates/autosummary/class.rst @@ -8,8 +8,8 @@ .. rubric:: Attributes {% for item in attributes %} -.. autoproperty:: - {{ objname }}.{{ item }} +.. autoproperty:: {{ objname }}.{{ item }} + :no-index: {% endfor %} {% endif %} diff --git a/doc/api/index.rst b/doc/api/index.rst index 3d299cce71f..9916a9c9c80 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -25,6 +25,7 @@ Plotting map elements :toctree: generated Figure.basemap + Figure.clip Figure.coast Figure.colorbar Figure.hlines @@ -218,6 +219,7 @@ Miscellaneous which show_versions + src.ClipAccessor Datasets -------- diff --git a/pygmt/figure.py b/pygmt/figure.py index f9c8478747d..31eb6f1206c 100644 --- a/pygmt/figure.py +++ b/pygmt/figure.py @@ -9,6 +9,7 @@ from typing import Literal, overload from pygmt._typing import PathLike +from pygmt.src import ClipAccessor try: import IPython @@ -137,6 +138,15 @@ def region(self) -> np.ndarray: wesn = lib.extract_region() return wesn + @property + def clip(self) -> ClipAccessor: + """ + Set up a clipping path and only plot data inside/outside the clipped path. + + See :class:`pygmt.src.clip.ClipAccessor ` for the usage. + """ + return ClipAccessor(self) + def savefig( self, fname: PathLike, diff --git a/pygmt/src/__init__.py b/pygmt/src/__init__.py index 8905124f917..49a569a94ac 100644 --- a/pygmt/src/__init__.py +++ b/pygmt/src/__init__.py @@ -5,6 +5,7 @@ from pygmt.src.basemap import basemap from pygmt.src.binstats import binstats from pygmt.src.blockm import blockmean, blockmedian, blockmode +from pygmt.src.clip import ClipAccessor from pygmt.src.coast import coast from pygmt.src.colorbar import colorbar from pygmt.src.config import config diff --git a/pygmt/src/clip.py b/pygmt/src/clip.py new file mode 100644 index 00000000000..08fb0d0d8e2 --- /dev/null +++ b/pygmt/src/clip.py @@ -0,0 +1,207 @@ +""" +Clip. +""" + +from collections.abc import Sequence + +from pygmt.clib import Session +from pygmt.helpers import build_arg_list, is_nonstr_iter + + +class ClipAccessor: + """ + Accessor for the clip methods. + """ + + def __init__(self, fig): + self._fig = fig # The parent Figure object. + + def land(self, **kwargs): + """ + Clip the land area (i.e., "dry" areas). + + Must be used as a context manager. Any plotting operations within the context + manager will be clipped to the land areas. + + Parameters + ---------- + kwargs + Additional arguments passed to :meth:`pygmt.Figure.coast`. + + Examples + -------- + >>> from pygmt import Figure + >>> from pygmt.datasets import load_earth_relief + >>> + >>> grid = load_earth_relief() + >>> fig = Figure() + >>> fig.basemap(region="g", projection="W15c", frame=True) + >>> with fig.clip.land(): + ... fig.grdimage(grid, cmap="geo") + >>> fig.show() + """ + self.data = None + self.module_enter = self.module_exit = "coast" + self.kwargs_enter = {"G": True} | kwargs + self.kwargs_exit = {"Q": True} + return self + + def water(self, **kwargs): + """ + Clip the water areas (i.e., "wet" areas such as oceans and lakes). + + Must be used as a context manager. Any plotting operations within the context + manager will be clipped to the water areas. + + Parameters + ---------- + kwargs + Additional arguments passed to :meth:`pygmt.Figure.coast`. + + Examples + -------- + >>> from pygmt import Figure + >>> from pygmt.datasets import load_earth_relief + >>> + >>> grid = load_earth_relief() + >>> fig = Figure() + >>> fig.basemap(region="g", projection="W15c", frame=True) + >>> with fig.clip.water(): + ... fig.grdimage(grid, cmap="geo") + >>> fig.show() + """ + self.data = None + self.module_enter = self.module_exit = "coast" + self.kwargs_enter = {"S": True} | kwargs + self.kwargs_exit = {"Q": True} + return self + + def polygon(self, x, y, **kwargs): + """ + Clip polygonal paths. + + Parameters + ---------- + x/y + Coordinates of polygon. + kwargs + Additional arguments passed to GMT's ``clip`` module. + + Examples + -------- + >>> from pygmt import Figure + >>> from pygmt.datasets import load_earth_relief + >>> + >>> grid = load_earth_relief() + >>> fig = Figure() + >>> fig.basemap(region="g", projection="W15c", frame=True) + >>> with fig.clip.polygon(x=[-10, 10, 10, -10], y=[-10, -10, 10, 10]): + ... fig.grdimage(grid, cmap="geo") + >>> fig.show() + """ + self.data = (x, y) + self.module_enter = self.module_exit = "clip" + self.kwargs_enter = kwargs + self.kwargs_exit = {"C": True} + + return self + + def dcw(self, code: str | Sequence[str]): + """ + Clip based on the Digital Chart of the World. + + Examples + -------- + >>> from pygmt import Figure + >>> from pygmt.datasets import load_earth_relief + >>> + >>> grid = load_earth_relief() + >>> fig = Figure() + >>> fig.basemap(region="g", projection="W15c", frame=True) + >>> with fig.clip.dcw(code="JP"): + ... fig.grdimage(grid, cmap="geo") + >>> fig.show() + """ + _code = ",".join(code) if is_nonstr_iter(code) else code + self.data = None + self.module_enter = "coast" + self.kwargs_enter = {"E": _code + "+c"} + self.module_exit = "coast" + self.kwargs_exit = {"Q": True} + return self + + def solar(self, **kwargs): + """ + Clip the data to the solar terminator. + + Examples + -------- + >>> from pygmt import Figure + >>> from pygmt.datasets import load_earth_relief + >>> + >>> grid = load_earth_relief() + >>> fig = Figure() + >>> fig.basemap(region="g", projection="W15c", frame=True) + >>> with fig.clip.solar(T="c"): + ... fig.grdimage(grid, cmap="geo") + >>> fig.show() + """ + self.data = None + self.module_enter = "solar" + self.kwargs_enter = {"G": True} | kwargs + self.module_exit = "clip" + self.kwargs_exit = {"C": True} + + return self + + def mask(self, x, y, spacing, radius=None): + """ + Clip the data to a mask. + + Examples + -------- + >>> from pygmt import Figure + >>> from pygmt.datasets import load_earth_relief + >>> + >>> grid = load_earth_relief() + >>> fig = Figure() + >>> fig.basemap(region="g", projection="Q15c", frame=True) + >>> with fig.clip.mask( + ... x=[180] * 16, y=np.arange(-80, 80, 10), spacing="30m", radius="5d" + ... ): + ... fig.grdimage(grid, cmap="geo") + >>> fig.show() + """ + self.data = (x, y) + self.module_enter = self.module_exit = "mask" + self.kwargs_enter = {"I": spacing, "S": radius} + self.kwargs_exit = {"C": True} + return self + + def __enter__(self): + """ + Enter the context manager. + """ + self._fig._preprocess() # Activate the current figure. + with Session() as lib: + if self.data: + with lib.virtualfile_in(x=self.data[0], y=self.data[1]) as vintbl: + lib.call_module( + module=self.module_enter, + args=build_arg_list(self.kwargs_enter, infile=vintbl), + ) + else: + lib.call_module( + module=self.module_enter, args=build_arg_list(self.kwargs_enter) + ) + return self + + def __exit__(self, exc_type, exc_value, traceback): + """ + Exit the context manager. + """ + self._fig._preprocess() # Activate the current figure. + with Session() as lib: + lib.call_module( + module=self.module_exit, args=build_arg_list(self.kwargs_exit) + ) From bb32777c73a0c4f49adf1727fa150fc367a3deca Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sat, 26 Apr 2025 14:44:40 +0800 Subject: [PATCH 02/13] Define more classes --- pygmt/src/clip.py | 188 ++++++++++++++++++++++++++++++---------------- 1 file changed, 124 insertions(+), 64 deletions(-) diff --git a/pygmt/src/clip.py b/pygmt/src/clip.py index 08fb0d0d8e2..3a7fb900633 100644 --- a/pygmt/src/clip.py +++ b/pygmt/src/clip.py @@ -8,6 +8,122 @@ from pygmt.helpers import build_arg_list, is_nonstr_iter +class _ClipContext: + """ + Base class for the clip context manager. + """ + + def __init__(self, figure, data=None, **kwargs): + self._figure = figure # The parent Figure object. + self._data = data + self._kwargs = kwargs + + def __enter__(self): + self._figure._preprocess() # Activate the current figure. + self._activate() + + def __exit__(self, exc_type, exc_val, exc_tb): + self._figure._preprocess() # Activate the current figure. + self._deactivate() + + def _activate(self): + """ + Activate clipping. + """ + raise NotImplementedError + + def _deactivate(self): + """ + Deactivate clipping. + """ + raise NotImplementedError + + +class _ClipLand(_ClipContext): + """ + Clip the land area (i.e., "dry" areas). + """ + + def _activate(self): + self._figure.coast(land=True, **self._kwargs) + + def _deactivate(self): + self._figure.coast(Q=True) + + +class _ClipWater(_ClipContext): + """ + Clip the water areas (i.e., "wet" areas such as oceans and lakes). + """ + + def _activate(self): + self._figure.coast(water=True, **self._kwargs) + + def _deactivate(self): + self._figure.coast(Q=True) + + +class _ClipDcw(_ClipContext): + """ + Clip based on the Digital Chart of the World. + """ + + def _activate(self): + self._figure.coast(**self._kwargs) + + def _deactivate(self): + self._figure.coast(Q=True) + + +class _ClipSolar(_ClipContext): + """ + Clip the data to the solar terminator. + """ + + def _activate(self): + self._figure.solar(fill=True, **self._kwargs) + + def _deactivate(self): + with Session() as lib: + lib.call_module(module="clip", args=build_arg_list({"C": True})) + + +class _ClipPolygon(_ClipContext): + """ + Clip polygonal paths. + """ + + def _activate(self): + with Session() as lib: + with lib.virtualfile_in(data=self._data) as vintbl: + lib.call_module( + module="clip", + args=build_arg_list(self._kwargs, infile=vintbl), + ) + + def _deactivate(self): + with Session() as lib: + lib.call_module(module="clip", args=build_arg_list({"C": True})) + + +class _ClipMask(_ClipContext): + """ + Clip the data to a mask. + """ + + def _activate(self): + with Session() as lib: + with lib.virtualfile_in(data=self._data) as vintbl: + lib.call_module( + module="mask", + args=build_arg_list(self._kwargs, infile=vintbl), + ) + + def _deactivate(self): + with Session() as lib: + lib.call_module(module="mask", args=build_arg_list({"C": True})) + + class ClipAccessor: """ Accessor for the clip methods. @@ -40,11 +156,7 @@ def land(self, **kwargs): ... fig.grdimage(grid, cmap="geo") >>> fig.show() """ - self.data = None - self.module_enter = self.module_exit = "coast" - self.kwargs_enter = {"G": True} | kwargs - self.kwargs_exit = {"Q": True} - return self + return _ClipLand(self._fig, **kwargs) def water(self, **kwargs): """ @@ -70,11 +182,7 @@ def water(self, **kwargs): ... fig.grdimage(grid, cmap="geo") >>> fig.show() """ - self.data = None - self.module_enter = self.module_exit = "coast" - self.kwargs_enter = {"S": True} | kwargs - self.kwargs_exit = {"Q": True} - return self + return _ClipWater(self._fig, **kwargs) def polygon(self, x, y, **kwargs): """ @@ -99,14 +207,9 @@ def polygon(self, x, y, **kwargs): ... fig.grdimage(grid, cmap="geo") >>> fig.show() """ - self.data = (x, y) - self.module_enter = self.module_exit = "clip" - self.kwargs_enter = kwargs - self.kwargs_exit = {"C": True} + return _ClipPolygon(self._fig, data={"x": x, "y": y}, **kwargs) - return self - - def dcw(self, code: str | Sequence[str]): + def dcw(self, code: str | Sequence[str], **kwargs): """ Clip based on the Digital Chart of the World. @@ -123,12 +226,7 @@ def dcw(self, code: str | Sequence[str]): >>> fig.show() """ _code = ",".join(code) if is_nonstr_iter(code) else code - self.data = None - self.module_enter = "coast" - self.kwargs_enter = {"E": _code + "+c"} - self.module_exit = "coast" - self.kwargs_exit = {"Q": True} - return self + return _ClipDcw(self._fig, dcw=f"{_code}+c", **kwargs) def solar(self, **kwargs): """ @@ -142,17 +240,11 @@ def solar(self, **kwargs): >>> grid = load_earth_relief() >>> fig = Figure() >>> fig.basemap(region="g", projection="W15c", frame=True) - >>> with fig.clip.solar(T="c"): + >>> with fig.clip.solar(terminator="civil"): ... fig.grdimage(grid, cmap="geo") >>> fig.show() """ - self.data = None - self.module_enter = "solar" - self.kwargs_enter = {"G": True} | kwargs - self.module_exit = "clip" - self.kwargs_exit = {"C": True} - - return self + return _ClipSolar(self._fig, **kwargs) def mask(self, x, y, spacing, radius=None): """ @@ -172,36 +264,4 @@ def mask(self, x, y, spacing, radius=None): ... fig.grdimage(grid, cmap="geo") >>> fig.show() """ - self.data = (x, y) - self.module_enter = self.module_exit = "mask" - self.kwargs_enter = {"I": spacing, "S": radius} - self.kwargs_exit = {"C": True} - return self - - def __enter__(self): - """ - Enter the context manager. - """ - self._fig._preprocess() # Activate the current figure. - with Session() as lib: - if self.data: - with lib.virtualfile_in(x=self.data[0], y=self.data[1]) as vintbl: - lib.call_module( - module=self.module_enter, - args=build_arg_list(self.kwargs_enter, infile=vintbl), - ) - else: - lib.call_module( - module=self.module_enter, args=build_arg_list(self.kwargs_enter) - ) - return self - - def __exit__(self, exc_type, exc_value, traceback): - """ - Exit the context manager. - """ - self._fig._preprocess() # Activate the current figure. - with Session() as lib: - lib.call_module( - module=self.module_exit, args=build_arg_list(self.kwargs_exit) - ) + return _ClipMask(self._fig, data={"x": x, "y": y}, I=spacing, S=radius) From 83f04fa501ede9a8ad0429cce18db11d86229e6c Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 12 May 2025 08:22:17 +0800 Subject: [PATCH 03/13] Improve docstrings --- pygmt/src/clip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygmt/src/clip.py b/pygmt/src/clip.py index 3a7fb900633..340a285b08a 100644 --- a/pygmt/src/clip.py +++ b/pygmt/src/clip.py @@ -1,5 +1,5 @@ """ -Clip. +clip - Clip a path and only plot data inside or outside. """ from collections.abc import Sequence @@ -134,7 +134,7 @@ def __init__(self, fig): def land(self, **kwargs): """ - Clip the land area (i.e., "dry" areas). + Clip the land area (i.e., "dry" areas) and only plot data inside. Must be used as a context manager. Any plotting operations within the context manager will be clipped to the land areas. From ccf4d92741dbfa32b67cac329e20b130b349e575 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 12 May 2025 17:37:43 +0800 Subject: [PATCH 04/13] Update clip.land and clip.water --- pygmt/figure.py | 2 +- pygmt/src/clip.py | 30 ++++++++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/pygmt/figure.py b/pygmt/figure.py index 31eb6f1206c..adcb9bf1301 100644 --- a/pygmt/figure.py +++ b/pygmt/figure.py @@ -141,7 +141,7 @@ def region(self) -> np.ndarray: @property def clip(self) -> ClipAccessor: """ - Set up a clipping path and only plot data inside/outside the clipped path. + Set up a clipping path and only plot data inside/outside it. See :class:`pygmt.src.clip.ClipAccessor ` for the usage. """ diff --git a/pygmt/src/clip.py b/pygmt/src/clip.py index 340a285b08a..26b3c5023b4 100644 --- a/pygmt/src/clip.py +++ b/pygmt/src/clip.py @@ -126,11 +126,14 @@ def _deactivate(self): class ClipAccessor: """ - Accessor for the clip methods. + Accessor for different clip methods. """ - def __init__(self, fig): - self._fig = fig # The parent Figure object. + def __init__(self, figure): + """ + Initialize the ClipAccessor. + """ + self._figure = figure # The parent Figure object. def land(self, **kwargs): """ @@ -142,7 +145,8 @@ def land(self, **kwargs): Parameters ---------- kwargs - Additional arguments passed to :meth:`pygmt.Figure.coast`. + Additional keyword arguments passed to :meth:`pygmt.Figure.coast`. Not all + parameters make sense in this context. Examples -------- @@ -156,11 +160,12 @@ def land(self, **kwargs): ... fig.grdimage(grid, cmap="geo") >>> fig.show() """ - return _ClipLand(self._fig, **kwargs) + return _ClipLand(self._figure, **kwargs) def water(self, **kwargs): """ - Clip the water areas (i.e., "wet" areas such as oceans and lakes). + Clip the water areas (i.e., "wet" areas such as oceans and lakes) and only plot + data inside. Must be used as a context manager. Any plotting operations within the context manager will be clipped to the water areas. @@ -168,7 +173,8 @@ def water(self, **kwargs): Parameters ---------- kwargs - Additional arguments passed to :meth:`pygmt.Figure.coast`. + Additional keyword arguments passed to :meth:`pygmt.Figure.coast`. Not all + parameters make sense in this context. Examples -------- @@ -182,7 +188,7 @@ def water(self, **kwargs): ... fig.grdimage(grid, cmap="geo") >>> fig.show() """ - return _ClipWater(self._fig, **kwargs) + return _ClipWater(self._figure, **kwargs) def polygon(self, x, y, **kwargs): """ @@ -207,7 +213,7 @@ def polygon(self, x, y, **kwargs): ... fig.grdimage(grid, cmap="geo") >>> fig.show() """ - return _ClipPolygon(self._fig, data={"x": x, "y": y}, **kwargs) + return _ClipPolygon(self._figure, data={"x": x, "y": y}, **kwargs) def dcw(self, code: str | Sequence[str], **kwargs): """ @@ -226,7 +232,7 @@ def dcw(self, code: str | Sequence[str], **kwargs): >>> fig.show() """ _code = ",".join(code) if is_nonstr_iter(code) else code - return _ClipDcw(self._fig, dcw=f"{_code}+c", **kwargs) + return _ClipDcw(self._figure, dcw=f"{_code}+c", **kwargs) def solar(self, **kwargs): """ @@ -244,7 +250,7 @@ def solar(self, **kwargs): ... fig.grdimage(grid, cmap="geo") >>> fig.show() """ - return _ClipSolar(self._fig, **kwargs) + return _ClipSolar(self._figure, **kwargs) def mask(self, x, y, spacing, radius=None): """ @@ -264,4 +270,4 @@ def mask(self, x, y, spacing, radius=None): ... fig.grdimage(grid, cmap="geo") >>> fig.show() """ - return _ClipMask(self._fig, data={"x": x, "y": y}, I=spacing, S=radius) + return _ClipMask(self._figure, data={"x": x, "y": y}, I=spacing, S=radius) From 1784795456e4fb931838ab0bb77d0a92933eda12 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 13 May 2025 08:41:52 +0800 Subject: [PATCH 05/13] Update clip.solar --- pygmt/src/clip.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pygmt/src/clip.py b/pygmt/src/clip.py index 26b3c5023b4..50fbb75802a 100644 --- a/pygmt/src/clip.py +++ b/pygmt/src/clip.py @@ -238,6 +238,12 @@ def solar(self, **kwargs): """ Clip the data to the solar terminator. + Parameters + ---------- + kwargs + Additional keyword arguments passed to :meth:`pygmt.Figure.solar`. Not all + parameters make sense in this context. + Examples -------- >>> from pygmt import Figure From cc46add9eaef0b17ecc2d94063d89838ba2020ec Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sat, 17 May 2025 16:52:57 +0800 Subject: [PATCH 06/13] Replace self._preprocess with self._activate_figure --- pygmt/src/clip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygmt/src/clip.py b/pygmt/src/clip.py index 50fbb75802a..abc829d6e6a 100644 --- a/pygmt/src/clip.py +++ b/pygmt/src/clip.py @@ -19,11 +19,11 @@ def __init__(self, figure, data=None, **kwargs): self._kwargs = kwargs def __enter__(self): - self._figure._preprocess() # Activate the current figure. + self._figure._activate_figure() self._activate() def __exit__(self, exc_type, exc_val, exc_tb): - self._figure._preprocess() # Activate the current figure. + self._figure._activate_figure() self._deactivate() def _activate(self): From 105c317f313c527771253e9d34f4fe5cf4c71c74 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 19 May 2025 19:42:42 +0800 Subject: [PATCH 07/13] Add more aliases to clip --- pygmt/src/clip.py | 51 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/pygmt/src/clip.py b/pygmt/src/clip.py index abc829d6e6a..d0ba131df9f 100644 --- a/pygmt/src/clip.py +++ b/pygmt/src/clip.py @@ -5,7 +5,7 @@ from collections.abc import Sequence from pygmt.clib import Session -from pygmt.helpers import build_arg_list, is_nonstr_iter +from pygmt.helpers import build_arg_list, is_nonstr_iter, kwargs_to_strings, use_alias class _ClipContext: @@ -190,16 +190,61 @@ def water(self, **kwargs): """ return _ClipWater(self._figure, **kwargs) + @use_alias( + A="straight_line", + B="frame", + R="region", + J="projection", + V="verbose", + N="invert", + W="pen", + ) + @kwargs_to_strings(R="sequence") def polygon(self, x, y, **kwargs): """ Clip polygonal paths. + {aliases} + Parameters ---------- x/y Coordinates of polygon. - kwargs - Additional arguments passed to GMT's ``clip`` module. + {B} + {R} + {J} + {V} + straight_line + By default, line segments are connected as straight lines in the Cartesian + and polar coordinate systems, and as great circle arcs (by resampling coarse + input data along such arcs) in the geographic coordinate system. The + ``straight_line`` parameter can control the connection of line segments. + Valid values are: + + - ``True``: Draw line segments as straight lines in geographic coordinate + systems. + - ``"x"``: Draw line segments by first along *x*, then along *y*. + - ``"y"``: Draw line segments by first along *y*, then along *x*. + + Here, *x* and *y* have different meanings depending on the coordinate system + + - **Cartesian** coordinate system: *x* and *y* are the X- and Y-axes. + - **Polar** coordinate system: *x* and *y* are theta and radius. + - **Geographic** coordinate system: *x* and *y* are parallels and meridians. + + .. attention:: + + There exits a bug in GMT<=6.5.0 that, in geographic coordinate systems, + the meaning of *x* and *y* is reversed, i.e., *x* means meridians and + *y* means parallels. The bug is fixed by upstream + `PR #8648 `__. + invert + Invert the sense of what is inside and outside. For example, when using a + single path, this means that only points outside that path will be shown. + Cannot be used together with ``frame``. + pen + Draw outline of clip path using given pen attributes before clipping is + initiated [Default is no outline]. Examples -------- From a95d351b15a2b277cb85c54aa61205b9139b8a2e Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 19 May 2025 19:52:31 +0800 Subject: [PATCH 08/13] Add fmt_docstring --- pygmt/src/clip.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pygmt/src/clip.py b/pygmt/src/clip.py index d0ba131df9f..b1599c09496 100644 --- a/pygmt/src/clip.py +++ b/pygmt/src/clip.py @@ -5,7 +5,13 @@ from collections.abc import Sequence from pygmt.clib import Session -from pygmt.helpers import build_arg_list, is_nonstr_iter, kwargs_to_strings, use_alias +from pygmt.helpers import ( + build_arg_list, + fmt_docstring, + is_nonstr_iter, + kwargs_to_strings, + use_alias, +) class _ClipContext: @@ -190,6 +196,7 @@ def water(self, **kwargs): """ return _ClipWater(self._figure, **kwargs) + @fmt_docstring @use_alias( A="straight_line", B="frame", From 3573bd77a1e691f8b62ea40cc76e07f676129d6b Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 19 May 2025 20:06:35 +0800 Subject: [PATCH 09/13] Fix a doctest --- pygmt/src/clip.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pygmt/src/clip.py b/pygmt/src/clip.py index b1599c09496..d393382b4dc 100644 --- a/pygmt/src/clip.py +++ b/pygmt/src/clip.py @@ -316,6 +316,7 @@ def mask(self, x, y, spacing, radius=None): Examples -------- + >>> import numpy as np >>> from pygmt import Figure >>> from pygmt.datasets import load_earth_relief >>> From a9527aeb2a464fed0d54058894c6de7e07f0bdc5 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 19 May 2025 20:48:14 +0800 Subject: [PATCH 10/13] Fix docstring --- pygmt/src/clip.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pygmt/src/clip.py b/pygmt/src/clip.py index d393382b4dc..9691f2d26b5 100644 --- a/pygmt/src/clip.py +++ b/pygmt/src/clip.py @@ -217,10 +217,10 @@ def polygon(self, x, y, **kwargs): ---------- x/y Coordinates of polygon. - {B} - {R} - {J} - {V} + {frame} + {region} + {projection} + {verbose} straight_line By default, line segments are connected as straight lines in the Cartesian and polar coordinate systems, and as great circle arcs (by resampling coarse From faaabb7f1423b6fd9c82aad16892f9bc1ea8394a Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 21 May 2025 18:49:18 +0800 Subject: [PATCH 11/13] clip.polygon should accept a path or pandas object --- pygmt/src/clip.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pygmt/src/clip.py b/pygmt/src/clip.py index 9691f2d26b5..d352c8cefc3 100644 --- a/pygmt/src/clip.py +++ b/pygmt/src/clip.py @@ -19,9 +19,11 @@ class _ClipContext: Base class for the clip context manager. """ - def __init__(self, figure, data=None, **kwargs): + def __init__(self, figure, data=None, x=None, y=None, **kwargs): self._figure = figure # The parent Figure object. self._data = data + self._x = x + self._y = y self._kwargs = kwargs def __enter__(self): @@ -101,7 +103,7 @@ class _ClipPolygon(_ClipContext): def _activate(self): with Session() as lib: - with lib.virtualfile_in(data=self._data) as vintbl: + with lib.virtualfile_in(data=self._data, x=self._x, y=self._y) as vintbl: lib.call_module( module="clip", args=build_arg_list(self._kwargs, infile=vintbl), @@ -207,7 +209,7 @@ def water(self, **kwargs): W="pen", ) @kwargs_to_strings(R="sequence") - def polygon(self, x, y, **kwargs): + def polygon(self, data=None, x=None, y=None, **kwargs): """ Clip polygonal paths. @@ -215,6 +217,8 @@ def polygon(self, x, y, **kwargs): Parameters ---------- + data + Coordinates of polygon. x/y Coordinates of polygon. {frame} @@ -265,7 +269,7 @@ def polygon(self, x, y, **kwargs): ... fig.grdimage(grid, cmap="geo") >>> fig.show() """ - return _ClipPolygon(self._figure, data={"x": x, "y": y}, **kwargs) + return _ClipPolygon(self._figure, data=data, x=x, y=y, **kwargs) def dcw(self, code: str | Sequence[str], **kwargs): """ From 6cf4956ef2edda72ef89a4a34a2f13f81ea9339c Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 22 May 2025 15:40:35 +0800 Subject: [PATCH 12/13] Move clip.dcw before clip.polygon --- pygmt/src/clip.py | 58 +++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/pygmt/src/clip.py b/pygmt/src/clip.py index d352c8cefc3..9c8250d9f23 100644 --- a/pygmt/src/clip.py +++ b/pygmt/src/clip.py @@ -198,6 +198,36 @@ def water(self, **kwargs): """ return _ClipWater(self._figure, **kwargs) + def dcw(self, code: str | Sequence[str], **kwargs): + """ + Clip based on the Digital Chart of the World. + + Must be used as a context manager. Any plotting operations within the context + manager will be clipped to the region defined by the codes. + + Parameters + ---------- + code + The codes of the region to clip to. + kwargs + Additional keyword arguments passed to :meth:`pygmt.Figure.coast`. Not all + parameters make sense in this context. + + Examples + -------- + >>> from pygmt import Figure + >>> from pygmt.datasets import load_earth_relief + >>> + >>> grid = load_earth_relief() + >>> fig = Figure() + >>> fig.basemap(region="g", projection="W15c", frame=True) + >>> with fig.clip.dcw(code="JP"): + ... fig.grdimage(grid, cmap="geo") + >>> fig.show() + """ + _code = ",".join(code) if is_nonstr_iter(code) else code + return _ClipDcw(self._figure, dcw=f"{_code}+c", **kwargs) + @fmt_docstring @use_alias( A="straight_line", @@ -213,6 +243,9 @@ def polygon(self, data=None, x=None, y=None, **kwargs): """ Clip polygonal paths. + Must be used as a context manager. Any plotting operations within the context + manager will be clipped to the polygons. + {aliases} Parameters @@ -271,29 +304,13 @@ def polygon(self, data=None, x=None, y=None, **kwargs): """ return _ClipPolygon(self._figure, data=data, x=x, y=y, **kwargs) - def dcw(self, code: str | Sequence[str], **kwargs): - """ - Clip based on the Digital Chart of the World. - - Examples - -------- - >>> from pygmt import Figure - >>> from pygmt.datasets import load_earth_relief - >>> - >>> grid = load_earth_relief() - >>> fig = Figure() - >>> fig.basemap(region="g", projection="W15c", frame=True) - >>> with fig.clip.dcw(code="JP"): - ... fig.grdimage(grid, cmap="geo") - >>> fig.show() - """ - _code = ",".join(code) if is_nonstr_iter(code) else code - return _ClipDcw(self._figure, dcw=f"{_code}+c", **kwargs) - def solar(self, **kwargs): """ Clip the data to the solar terminator. + Must be used as a context manager. Any plotting operations within the context + manager will be clipped to the solar terminator. + Parameters ---------- kwargs @@ -318,6 +335,9 @@ def mask(self, x, y, spacing, radius=None): """ Clip the data to a mask. + Must be used as a context manager. Any plotting operations within the context + manager will be clipped to the mask. + Examples -------- >>> import numpy as np From a4955b988212252cf07e17d9255b2ae2ed01c96a Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sun, 25 May 2025 19:24:27 +0800 Subject: [PATCH 13/13] Updates --- pygmt/src/clip.py | 81 ++++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/pygmt/src/clip.py b/pygmt/src/clip.py index 9c8250d9f23..8f42373a1b8 100644 --- a/pygmt/src/clip.py +++ b/pygmt/src/clip.py @@ -228,14 +228,41 @@ def dcw(self, code: str | Sequence[str], **kwargs): _code = ",".join(code) if is_nonstr_iter(code) else code return _ClipDcw(self._figure, dcw=f"{_code}+c", **kwargs) + def solar(self, **kwargs): + """ + Clip the data to the solar terminator. + + Must be used as a context manager. Any plotting operations within the context + manager will be clipped to the solar terminator. + + Parameters + ---------- + kwargs + Additional keyword arguments passed to :meth:`pygmt.Figure.solar`. Not all + parameters make sense in this context. + + Examples + -------- + >>> from pygmt import Figure + >>> from pygmt.datasets import load_earth_relief + >>> + >>> grid = load_earth_relief() + >>> fig = Figure() + >>> fig.basemap(region="g", projection="W15c", frame=True) + >>> with fig.clip.solar(terminator="civil"): + ... fig.grdimage(grid, cmap="geo") + >>> fig.show() + """ + return _ClipSolar(self._figure, **kwargs) + @fmt_docstring @use_alias( A="straight_line", B="frame", - R="region", J="projection", - V="verbose", N="invert", + R="region", + V="verbose", W="pen", ) @kwargs_to_strings(R="sequence") @@ -251,9 +278,9 @@ def polygon(self, data=None, x=None, y=None, **kwargs): Parameters ---------- data - Coordinates of polygon. + Either a file name to an ASCII data table, a 2-D {table-classes}. x/y - Coordinates of polygon. + X and Y coordinates of the polygon. {frame} {region} {projection} @@ -304,39 +331,29 @@ def polygon(self, data=None, x=None, y=None, **kwargs): """ return _ClipPolygon(self._figure, data=data, x=x, y=y, **kwargs) - def solar(self, **kwargs): + @fmt_docstring + @use_alias( + I="spacing", + N="invert", + S="radius", + ) + @kwargs_to_strings(R="sequence") + def mask(self, data=None, x=None, y=None, **kwargs): """ - Clip the data to the solar terminator. + Clip the data to a mask. Must be used as a context manager. Any plotting operations within the context - manager will be clipped to the solar terminator. + manager will be clipped to the mask. Parameters ---------- - kwargs - Additional keyword arguments passed to :meth:`pygmt.Figure.solar`. Not all - parameters make sense in this context. - - Examples - -------- - >>> from pygmt import Figure - >>> from pygmt.datasets import load_earth_relief - >>> - >>> grid = load_earth_relief() - >>> fig = Figure() - >>> fig.basemap(region="g", projection="W15c", frame=True) - >>> with fig.clip.solar(terminator="civil"): - ... fig.grdimage(grid, cmap="geo") - >>> fig.show() - """ - return _ClipSolar(self._figure, **kwargs) - - def mask(self, x, y, spacing, radius=None): - """ - Clip the data to a mask. - - Must be used as a context manager. Any plotting operations within the context - manager will be clipped to the mask. + data + Either a file name to an ASCII data table, a 2-D {table-classes}. + x/y + X and Y coordinates of the mask. + {spacing} + invert + Invert the sense of what is inside and outside. Examples -------- @@ -353,4 +370,4 @@ def mask(self, x, y, spacing, radius=None): ... fig.grdimage(grid, cmap="geo") >>> fig.show() """ - return _ClipMask(self._figure, data={"x": x, "y": y}, I=spacing, S=radius) + return _ClipMask(self._figure, data=data, x=x, y=y, **kwargs)