Skip to content

Commit cb607ed

Browse files
authored
pygmt.grdfilter: Support Pythonic arguments for the distance parameter (#4405)
1 parent 903b8dc commit cb607ed

2 files changed

Lines changed: 91 additions & 28 deletions

File tree

pygmt/src/grdfilter.py

Lines changed: 73 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,27 @@
99
from pygmt._typing import PathLike
1010
from pygmt.alias import Alias, AliasSystem
1111
from pygmt.clib import Session
12+
from pygmt.exceptions import GMTParameterError
1213
from pygmt.helpers import build_arg_list, fmt_docstring, use_alias
1314

1415
__doctest_skip__ = ["grdfilter"]
1516

1617

1718
@fmt_docstring
18-
@use_alias(D="distance", F="filter", f="coltypes")
19+
@use_alias(F="filter", f="coltypes")
1920
def grdfilter(
2021
grid: PathLike | xr.DataArray,
2122
outgrid: PathLike | None = None,
23+
distance: Literal[
24+
"pixel",
25+
"cartesian",
26+
"geo_cartesian",
27+
"geo_flatearth1",
28+
"geo_flatearth2",
29+
"geo_spherical",
30+
"geo_mercator",
31+
]
32+
| None = None,
2233
spacing: Sequence[float | str] | None = None,
2334
nans: Literal["ignore", "replace", "preserve"] | None = None,
2435
toggle: bool = False,
@@ -45,6 +56,7 @@ def grdfilter(
4556
Full GMT docs at :gmt-docs:`grdfilter.html`.
4657
4758
$aliases
59+
- D = distance
4860
- G = outgrid
4961
- I = spacing
5062
- N = nans
@@ -71,27 +83,48 @@ def grdfilter(
7183
- **p**: Maximum Likelihood probability
7284
- **h**: Histogram
7385
74-
distance : str
75-
State how the grid (x,y) relates to the filter *width*:
76-
77-
- ``"p"``: grid (px,py) with *width* an odd number of pixels,
78-
Cartesian distances.
79-
- ``"0"``: grid (x,y) same units as *width*, Cartesian distances.
80-
- ``"1"``: grid (x,y) in degrees, *width* in kilometers, Cartesian
81-
distances.
82-
- ``"2"``: grid (x,y) in degrees, *width* in km, dx scaled by
83-
cos(middle y), Cartesian distances.
84-
85-
The above options are fastest because they allow weight matrix to be
86-
computed only once. The next three options are slower because they
87-
recompute weights for each latitude.
88-
89-
- ``"3"``: grid (x,y) in degrees, *width* in km, dx scaled by cos(y),
90-
Cartesian distance calculation.
91-
- ``"4"``: grid (x,y) in degrees, *width* in km, Spherical distance
92-
calculation.
93-
- ``"5"``: grid (x,y) in Mercator ``projection="m1"`` img units,
94-
*width* in km, Spherical distance calculation.
86+
distance
87+
Determine how grid (*x, y*) relates to filter *width* and how distances are
88+
calculated. Valid values are list below. The first four options are fastest
89+
because they allow weight matrix to be computed only once. The last three
90+
options are slower because they recompute weights for each latitude.
91+
92+
.. list-table::
93+
:header-rows: 1
94+
:widths: 16 32 20 32
95+
96+
* - Value
97+
- Grid (x,y)
98+
- Width
99+
- Distance Calculation
100+
* - ``"pixel"``
101+
- Pixels (px, py)
102+
- Odd number of pixels
103+
- Cartesian
104+
* - ``"cartesian"``
105+
- Same units as *width*
106+
- Any
107+
- Cartesian
108+
* - ``"geo_cartesian"``
109+
- Degrees
110+
- km
111+
- Cartesian
112+
* - ``"geo_flatearth1"``
113+
- Degrees
114+
- km
115+
- Cartesian, dx scaled by cos(middle y)
116+
* - ``"geo_flatearth2"``
117+
- Degrees
118+
- km
119+
- Cartesian, dx scaled by cos(y) per row
120+
* - ``"geo_spherical"``
121+
- Degrees
122+
- km
123+
- Spherical (great circle)
124+
* - ``"geo_mercator"``
125+
- Mercator **-Jm1** img units
126+
- km
127+
- Spherical
95128
$spacing
96129
nans
97130
Determine how NaN-values in the input grid affect the filtered output grid.
@@ -131,7 +164,7 @@ def grdfilter(
131164
>>> pygmt.grdfilter(
132165
... grid="@earth_relief_30m_g",
133166
... filter="m600",
134-
... distance="4",
167+
... distance="geo_spherical",
135168
... region=[150, 250, 10, 40],
136169
... spacing=0.5,
137170
... outgrid="filtered_pacific.nc",
@@ -140,9 +173,25 @@ def grdfilter(
140173
>>> # Apply a Gaussian smoothing filter of 600 km to the input DataArray and return
141174
>>> # a filtered DataArray with the smoothed grid.
142175
>>> grid = pygmt.datasets.load_earth_relief()
143-
>>> smooth_field = pygmt.grdfilter(grid=grid, filter="g600", distance="4")
176+
>>> smoothed = pygmt.grdfilter(grid=grid, filter="g600", distance="geo_spherical")
144177
"""
178+
if kwargs.get("D", distance) is None:
179+
raise GMTParameterError(required="distance")
180+
145181
aliasdict = AliasSystem(
182+
D=Alias(
183+
distance,
184+
name="distance",
185+
mapping={
186+
"pixel": "p",
187+
"cartesian": 0,
188+
"geo_cartesian": 1,
189+
"geo_flatearth1": 2,
190+
"geo_flatearth2": 3,
191+
"geo_spherical": 4,
192+
"geo_mercator": 5,
193+
},
194+
),
146195
I=Alias(spacing, name="spacing", sep="/", size=2),
147196
N=Alias(
148197
nans, name="nans", mapping={"ignore": "i", "replace": "r", "preserve": "p"}

pygmt/tests/test_grdfilter.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import xarray as xr
1010
from pygmt import grdfilter
1111
from pygmt.enums import GridRegistration, GridType
12-
from pygmt.exceptions import GMTTypeError
12+
from pygmt.exceptions import GMTParameterError, GMTTypeError
1313
from pygmt.helpers import GMTTempFile
1414
from pygmt.helpers.testing import load_static_earth_relief
1515

@@ -47,7 +47,11 @@ def test_grdfilter_dataarray_in_dataarray_out(grid, expected_grid):
4747
Test grdfilter with an input DataArray, and output as DataArray.
4848
"""
4949
result = grdfilter(
50-
grid=grid, filter="g600", distance="4", region=[-53, -49, -20, -17], cores=2
50+
grid=grid,
51+
filter="g600",
52+
distance="geo_spherical",
53+
region=[-53, -49, -20, -17],
54+
cores=2,
5155
)
5256
# check information of the output grid
5357
assert isinstance(result, xr.DataArray)
@@ -66,7 +70,7 @@ def test_grdfilter_dataarray_in_file_out(grid, expected_grid):
6670
grid,
6771
outgrid=tmpfile.name,
6872
filter="g600",
69-
distance="4",
73+
distance="geo_spherical",
7074
region=[-53, -49, -20, -17],
7175
)
7276
assert result is None # return value is None
@@ -80,4 +84,14 @@ def test_grdfilter_fails():
8084
Check that grdfilter fails correctly.
8185
"""
8286
with pytest.raises(GMTTypeError):
83-
grdfilter(np.arange(10).reshape((5, 2)))
87+
grdfilter(
88+
np.arange(10).reshape((5, 2)), filter="g600", distance="geo_spherical"
89+
)
90+
91+
92+
def test_grdfilter_required(grid):
93+
"""
94+
Test that grdfilter raises an exception when required parameters are missing.
95+
"""
96+
with pytest.raises(GMTParameterError, match="distance"):
97+
grdfilter(grid=grid, filter="g600")

0 commit comments

Comments
 (0)