Skip to content

Commit 07c1b26

Browse files
committed
Merge branch 'main' into changelog/v0.15.0
2 parents 54ce533 + 2f80408 commit 07c1b26

File tree

6 files changed

+148
-45
lines changed

6 files changed

+148
-45
lines changed

.github/workflows/check-links.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ on:
1515
schedule:
1616
- cron: '0 12 * * 0'
1717

18-
permissions: {}
18+
permissions:
19+
issues: write
1920

2021
jobs:
2122
check_links:
@@ -73,7 +74,7 @@ jobs:
7374
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
7475

7576
- name: Create Issue From File
76-
if: env.lychee_exit_code != 0
77+
if: ${{ steps.lychee.outputs.exit_code }} != 0
7778
run: |
7879
cd repository/
7980
title="Link Checker Report on ${CURRENT_DATE}"

.github/workflows/ci_docs.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
#
33
# This workflow builds the documentation on Linux/macOS/Windows.
44
#
5-
# It is run on every commit to the main and pull request branches, and also
6-
# when a new release is published.
7-
# In draft pull requests, only the job on Linux is triggered to save on
8-
# Continuous Integration resources.
5+
# It is run on every commit to the main and pull request branches, and also when a new
6+
# release is published. In draft pull requests, only the job on Linux is triggered to
7+
# save on Continuous Integration resources.
98
#
109
# On the main branch, the workflow also handles the documentation deployment:
1110
#
12-
# * Updating the development documentation by pushing the built HTML pages
13-
# from the main branch onto the dev folder of the gh-pages branch.
11+
# * Updating the development documentation by pushing the built HTML pages from the main
12+
# branch onto the dev folder of the gh-pages branch.
1413
# * Updating the latest documentation link to the new release.
1514
#
1615
name: Docs
@@ -43,7 +42,8 @@ concurrency:
4342
group: ${{ github.workflow }}-${{ github.ref }}
4443
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
4544

46-
permissions: {}
45+
permissions:
46+
contents: write
4747

4848
jobs:
4949
docs:

.github/workflows/format-command.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ on:
88
repository_dispatch:
99
types: [format-command]
1010

11-
permissions: {}
11+
permissions:
12+
contents: write
1213

1314
jobs:
1415
format:

.github/workflows/slash-command-dispatch.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ on:
1010
# Add "edited" type for test purposes. Where possible, avoid using to prevent processing unnecessary events.
1111
# types: [created, edited]
1212

13-
permissions: {}
13+
permissions:
14+
issues: write
1415

1516
jobs:
1617
slashCommandDispatch:

pygmt/src/grdfill.py

Lines changed: 104 additions & 14 deletions
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")
22-
@use_alias(A="mode", N="hole", R="region", V="verbose")
70+
@use_alias(A="mode", N="hole", R="region", V="verbose", f="coltypes")
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,26 +87,42 @@ 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}
125+
{coltypes}
54126
{verbose}
55127
56128
Returns
@@ -67,19 +139,37 @@ def grdfill(grid, outgrid: str | None = None, **kwargs) -> xr.DataArray | None:
67139
>>> import pygmt
68140
>>> # Load a bathymetric grid with missing data
69141
>>> 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")
142+
>>> # Fill the holes with a constant value of 20
143+
>>> filled_grid = pygmt.grdfill(grid=earth_relief_holes, constantfill=20)
73144
"""
145+
# TODO(PyGMT>=0.19.0): Remove the deprecated 'mode' parameter.
146+
if kwargs.get("A") is not None: # The deprecated 'mode' parameter is given.
147+
warnings.warn(
148+
"The 'mode' parameter is deprecated since v0.15.0 and will be removed in "
149+
"v0.19.0. Use 'constantfill'/'gridfill'/'neighborfill'/'splinefill' "
150+
"instead.",
151+
FutureWarning,
152+
stacklevel=1,
153+
)
154+
else:
155+
# Determine the -A option from the fill parameters.
156+
kwargs["A"] = _parse_fill_mode(constantfill, gridfill, neighborfill, splinefill)
157+
74158
if kwargs.get("A") is None and kwargs.get("L") is None:
75159
msg = "At least parameter 'mode' or 'L' must be specified."
76160
raise GMTInvalidInput(msg)
77161

78162
with Session() as lib:
79163
with (
80164
lib.virtualfile_in(check_kind="raster", data=grid) as vingrd,
165+
lib.virtualfile_in(
166+
check_kind="raster", data=gridfill, required_data=False
167+
) as vbggrd,
81168
lib.virtualfile_out(kind="grid", fname=outgrid) as voutgrd,
82169
):
170+
if gridfill is not None:
171+
# Fill by a grid. Append the actual or virtual grid file name.
172+
kwargs["A"] = f"g{vbggrd}"
83173
kwargs["G"] = voutgrd
84174
lib.call_module(
85175
module="grdfill", args=build_arg_list(kwargs, infile=vingrd)

pygmt/tests/test_grdfill.py

Lines changed: 30 additions & 20 deletions
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)