Skip to content

Commit 02a1daf

Browse files
seismanmichaelgrundyvonnefroehlich
authored
pygmt.grdfill: Deprecate parameter 'mode' and add parameters 'constantfill/gridfill/neighborfill/splinefill' (deprecated since v0.15.0 and will be removed in v0.19.0) (#3855)
Co-authored-by: Michael Grund <[email protected]> Co-authored-by: Yvonne Fröhlich <[email protected]>
1 parent bcc00e6 commit 02a1daf

File tree

2 files changed

+132
-33
lines changed

2 files changed

+132
-33
lines changed

pygmt/src/grdfill.py

+102-13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
grdfill - Interpolate across holes in a grid.
33
"""
44

5+
import warnings
6+
57
import xarray as xr
68
from pygmt.clib import Session
79
from pygmt.exceptions import GMTInvalidInput
@@ -16,12 +18,66 @@
1618
__doctest_skip__ = ["grdfill"]
1719

1820

21+
def _parse_fill_mode(
22+
constantfill=None, gridfill=None, neighborfill=None, splinefill=None
23+
) -> str | None:
24+
"""
25+
Parse the fill parameters and return the appropriate string for the -A option.
26+
27+
>>> import numpy as np
28+
>>> import xarray as xr
29+
>>> _parse_fill_mode(constantfill=20.0)
30+
'c20.0'
31+
>>> _parse_fill_mode(gridfill="bggrid.nc")
32+
'g'
33+
>>> _parse_fill_mode(gridfill=xr.DataArray(np.zeros((10, 10))))
34+
'g'
35+
>>> _parse_fill_mode(neighborfill=20)
36+
'n20'
37+
>>> _parse_fill_mode(neighborfill=True)
38+
'n'
39+
>>> _parse_fill_mode(splinefill=0.5)
40+
's0.5'
41+
>>> _parse_fill_mode(splinefill=True)
42+
's'
43+
>>> _parse_fill_mode(constantfill=20, gridfill="bggrid.nc")
44+
Traceback (most recent call last):
45+
...
46+
pygmt.exceptions.GMTInvalidInput: The ... parameters are mutually exclusive.
47+
"""
48+
fill_params = [constantfill, gridfill, neighborfill, splinefill]
49+
if sum(param is not None for param in fill_params) > 1:
50+
msg = (
51+
"The 'constantfill', 'gridfill', 'neighborfill', and 'splinefill' "
52+
"parameters are mutually exclusive."
53+
)
54+
raise GMTInvalidInput(msg)
55+
56+
if constantfill is not None:
57+
return f"c{constantfill}"
58+
if gridfill is not None:
59+
return "g" # Append grid file name later to support xarray.DataArray.
60+
if neighborfill is not None and neighborfill is not False:
61+
return "n" if neighborfill is True else f"n{neighborfill}"
62+
if splinefill is not None and splinefill is not False:
63+
return "s" if splinefill is True else f"s{splinefill}"
64+
return None
65+
66+
1967
@fmt_docstring
2068
# TODO(PyGMT>=0.19.0): Remove the deprecated 'no_data' parameter.
2169
@deprecate_parameter("no_data", "hole", "v0.15.0", remove_version="v0.19.0")
2270
@use_alias(A="mode", N="hole", R="region", V="verbose")
2371
@kwargs_to_strings(R="sequence")
24-
def grdfill(grid, outgrid: str | None = None, **kwargs) -> xr.DataArray | None:
72+
def grdfill(
73+
grid: str | xr.DataArray,
74+
outgrid: str | None = None,
75+
constantfill: float | None = None,
76+
gridfill: str | xr.DataArray | None = None,
77+
neighborfill: float | bool | None = None,
78+
splinefill: float | bool | None = None,
79+
**kwargs,
80+
) -> xr.DataArray | None:
2581
r"""
2682
Interpolate across holes in a grid.
2783
@@ -31,25 +87,40 @@ def grdfill(grid, outgrid: str | None = None, **kwargs) -> xr.DataArray | None:
3187
replace the hole values. If no holes are found the original unchanged grid is
3288
returned.
3389
34-
Full option list at :gmt-docs:`grdfill.html`
90+
Full option list at :gmt-docs:`grdfill.html`.
3591
3692
{aliases}
3793
3894
Parameters
3995
----------
4096
{grid}
4197
{outgrid}
42-
mode : str
43-
Specify the hole-filling algorithm to use. Choose from **c** for
44-
constant fill and append the constant value, **n** for nearest
45-
neighbor (and optionally append a search radius in
46-
pixels [default radius is :math:`r^2 = \sqrt{{ X^2 + Y^2 }}`,
47-
where (*X,Y*) are the node dimensions of the grid]), or
48-
**s** for bicubic spline (optionally append a *tension*
49-
parameter [Default is no tension]).
98+
constantfill
99+
Fill the holes with a constant value. Specify the constant value to use.
100+
gridfill
101+
Fill the holes with values sampled from another (possibly coarser) grid. Specify
102+
the grid (a file name or an :class:`xarray.DataArray`) to use for the fill.
103+
neighborfill
104+
Fill the holes with the nearest neighbor. Specify the search radius in pixels.
105+
If set to ``True``, the default search radius will be used
106+
(:math:`r^2 = \sqrt{{n^2 + m^2}}`, where (*n,m*) are the node dimensions of the
107+
grid).
108+
splinefill
109+
Fill the holes with a bicubic spline. Specify the tension value to use. If set
110+
to ``True``, no tension will be used.
50111
hole : float
51112
Set the node value used to identify a point as a member of a hole [Default is
52113
NaN].
114+
mode : str
115+
Specify the hole-filling algorithm to use. Choose from **c** for constant fill
116+
and append the constant value, **n** for nearest neighbor (and optionally append
117+
a search radius in pixels [default radius is :math:`r^2 = \sqrt{{ X^2 + Y^2 }}`,
118+
where (*X,Y*) are the node dimensions of the grid]), or **s** for bicubic spline
119+
(optionally append a *tension* parameter [Default is no tension]).
120+
121+
.. deprecated:: 0.15.0
122+
Use ``constantfill``, ``gridfill``, ``neighborfill``, or ``splinefill``
123+
instead. The parameter will be removed in v0.19.0.
53124
{region}
54125
{verbose}
55126
@@ -67,19 +138,37 @@ def grdfill(grid, outgrid: str | None = None, **kwargs) -> xr.DataArray | None:
67138
>>> import pygmt
68139
>>> # Load a bathymetric grid with missing data
69140
>>> earth_relief_holes = pygmt.datasets.load_sample_data(name="earth_relief_holes")
70-
>>> # Perform grid filling operations on the sample grid
71-
>>> # Set all empty values to "20"
72-
>>> filled_grid = pygmt.grdfill(grid=earth_relief_holes, mode="c20")
141+
>>> # Fill the holes with a constant value of 20
142+
>>> filled_grid = pygmt.grdfill(grid=earth_relief_holes, constantfill=20)
73143
"""
144+
# TODO(PyGMT>=0.19.0): Remove the deprecated 'mode' parameter.
145+
if kwargs.get("A") is not None: # The deprecated 'mode' parameter is given.
146+
warnings.warn(
147+
"The 'mode' parameter is deprecated since v0.15.0 and will be removed in "
148+
"v0.19.0. Use 'constantfill'/'gridfill'/'neighborfill'/'splinefill' "
149+
"instead.",
150+
FutureWarning,
151+
stacklevel=1,
152+
)
153+
else:
154+
# Determine the -A option from the fill parameters.
155+
kwargs["A"] = _parse_fill_mode(constantfill, gridfill, neighborfill, splinefill)
156+
74157
if kwargs.get("A") is None and kwargs.get("L") is None:
75158
msg = "At least parameter 'mode' or 'L' must be specified."
76159
raise GMTInvalidInput(msg)
77160

78161
with Session() as lib:
79162
with (
80163
lib.virtualfile_in(check_kind="raster", data=grid) as vingrd,
164+
lib.virtualfile_in(
165+
check_kind="raster", data=gridfill, required_data=False
166+
) as vbggrd,
81167
lib.virtualfile_out(kind="grid", fname=outgrid) as voutgrd,
82168
):
169+
if gridfill is not None:
170+
# Fill by a grid. Append the actual or virtual grid file name.
171+
kwargs["A"] = f"g{vbggrd}"
83172
kwargs["G"] = voutgrd
84173
lib.call_module(
85174
module="grdfill", args=build_arg_list(kwargs, infile=vingrd)

pygmt/tests/test_grdfill.py

+30-20
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pathlib import Path
66

77
import numpy as np
8+
import numpy.testing as npt
89
import pytest
910
import xarray as xr
1011
from pygmt import grdfill, load_dataarray
@@ -49,23 +50,8 @@ def fixture_expected_grid():
4950
[347.5, 331.5, 309.0, 282.0, 190.0, 208.0, 299.5, 348.0],
5051
],
5152
coords={
52-
"lon": [-54.5, -53.5, -52.5, -51.5, -50.5, -49.5, -48.5, -47.5],
53-
"lat": [
54-
-23.5,
55-
-22.5,
56-
-21.5,
57-
-20.5,
58-
-19.5,
59-
-18.5,
60-
-17.5,
61-
-16.5,
62-
-15.5,
63-
-14.5,
64-
-13.5,
65-
-12.5,
66-
-11.5,
67-
-10.5,
68-
],
53+
"lon": np.arange(-54.5, -46.5, 1),
54+
"lat": np.arange(-23.5, -9.5, 1),
6955
},
7056
dims=["lat", "lon"],
7157
)
@@ -76,7 +62,7 @@ def test_grdfill_dataarray_out(grid, expected_grid):
7662
"""
7763
Test grdfill with a DataArray output.
7864
"""
79-
result = grdfill(grid=grid, mode="c20")
65+
result = grdfill(grid=grid, constantfill=20)
8066
# check information of the output grid
8167
assert isinstance(result, xr.DataArray)
8268
assert result.gmt.gtype == GridType.GEOGRAPHIC
@@ -91,7 +77,7 @@ def test_grdfill_asymmetric_pad(grid, expected_grid):
9177
9278
Regression test for https://github.com/GenericMappingTools/pygmt/issues/1745.
9379
"""
94-
result = grdfill(grid=grid, mode="c20", region=[-55, -50, -24, -16])
80+
result = grdfill(grid=grid, constantfill=20, region=[-55, -50, -24, -16])
9581
# check information of the output grid
9682
assert isinstance(result, xr.DataArray)
9783
assert result.gmt.gtype == GridType.GEOGRAPHIC
@@ -107,16 +93,40 @@ def test_grdfill_file_out(grid, expected_grid):
10793
Test grdfill with an outgrid set.
10894
"""
10995
with GMTTempFile(suffix=".nc") as tmpfile:
110-
result = grdfill(grid=grid, mode="c20", outgrid=tmpfile.name)
96+
result = grdfill(grid=grid, constantfill=20, outgrid=tmpfile.name)
11197
assert result is None # return value is None
11298
assert Path(tmpfile.name).stat().st_size > 0 # check that outfile exists
11399
temp_grid = load_dataarray(tmpfile.name)
114100
xr.testing.assert_allclose(a=temp_grid, b=expected_grid)
115101

116102

103+
def test_grdfill_gridfill_dataarray(grid):
104+
"""
105+
Test grdfill with a DataArray input.
106+
"""
107+
bggrid = xr.DataArray(
108+
np.arange(grid.size).reshape(grid.shape),
109+
dims=grid.dims,
110+
coords={"lon": grid.lon, "lat": grid.lat},
111+
)
112+
result = grdfill(grid=grid, gridfill=bggrid)
113+
assert not result.isnull().any()
114+
npt.assert_array_equal(result[3:6, 3:5], bggrid[3:6, 3:5])
115+
116+
117117
def test_grdfill_required_args(grid):
118118
"""
119119
Test that grdfill fails without arguments for `mode` and `L`.
120120
"""
121121
with pytest.raises(GMTInvalidInput):
122122
grdfill(grid=grid)
123+
124+
125+
# TODO(PyGMT>=0.19.0): Remove this test.
126+
def test_grdfill_deprecated_mode(grid, expected_grid):
127+
"""
128+
Test that grdfill fails with deprecated `mode` argument.
129+
"""
130+
with pytest.warns(FutureWarning):
131+
result = grdfill(grid=grid, mode="c20")
132+
xr.testing.assert_allclose(a=result, b=expected_grid)

0 commit comments

Comments
 (0)