2
2
grdclip - Clip the range of grid values.
3
3
"""
4
4
5
+ from collections .abc import Sequence
6
+
5
7
import xarray as xr
6
8
from pygmt ._typing import PathLike
7
9
from pygmt .clib import Session
10
+ from pygmt .exceptions import GMTInvalidInput
8
11
from pygmt .helpers import (
9
12
build_arg_list ,
10
13
deprecate_parameter ,
11
14
fmt_docstring ,
15
+ is_nonstr_iter ,
12
16
kwargs_to_strings ,
13
17
use_alias ,
14
18
)
15
19
16
20
__doctest_skip__ = ["grdclip" ]
17
21
18
22
23
+ def _parse_sequence (name , value , separator = "/" , size = 2 , ndim = 1 ):
24
+ """
25
+ Parse a 1-D or 2-D sequence of values and join them by a separator.
26
+
27
+ Parameters
28
+ ----------
29
+ name
30
+ The parameter name.
31
+ value
32
+ The 1-D or 2-D sequence of values to parse.
33
+ separator
34
+ The separator to join the values.
35
+ size
36
+ The number of values in the sequence.
37
+ ndim
38
+ The expected maximum number of dimensions of the sequence.
39
+
40
+ Returns
41
+ -------
42
+ str
43
+ The parsed sequence.
44
+
45
+ Examples
46
+ --------
47
+ >>> _parse_sequence("above_or_below", [1000, 0], size=2, ndim=1)
48
+ '1000/0'
49
+ >>> _parse_sequence("between", [1000, 1500, 10000], size=3, ndim=2)
50
+ '1000/1500/10000'
51
+ >>> _parse_sequence("between", [[1000, 1500, 10000]], size=3, ndim=2)
52
+ ['1000/1500/10000']
53
+ >>> _parse_sequence(
54
+ ... "between", [[1000, 1500, 10000], [1500, 2000, 20000]], size=3, ndim=2
55
+ ... )
56
+ ['1000/1500/10000', '1500/2000/20000']
57
+ >>> _parse_sequence("replace", [1000, 0], size=2, ndim=2)
58
+ '1000/0'
59
+ >>> _parse_sequence("replace", [[1000, 0]], size=2, ndim=2)
60
+ ['1000/0']
61
+ >>> _parse_sequence("replace", [[1000, 0], [1500, 10000]], size=2, ndim=2)
62
+ ['1000/0', '1500/10000']
63
+ >>> _parse_sequence("any", "1000/100")
64
+ '1000/100'
65
+ >>> _parse_sequence("any", None)
66
+ >>> _parse_sequence("any", [])
67
+ []
68
+ >>> _parse_sequence("above_or_below", [[100, 1000], [1500, 2000]], size=2, ndim=1)
69
+ Traceback (most recent call last):
70
+ ...
71
+ pygmt.exceptions.GMTInvalidInput: Parameter ... must be a 1-D sequence...
72
+ >>> _parse_sequence("above_or_below", [100, 200, 300], size=2, ndim=1)
73
+ Traceback (most recent call last):
74
+ ...
75
+ pygmt.exceptions.GMTInvalidInput: Parameter ... must be a 1-D sequence ...
76
+ >>> _parse_sequence("between", [[100, 200, 300], [500, 600]], size=3, ndim=2)
77
+ Traceback (most recent call last):
78
+ ...
79
+ pygmt.exceptions.GMTInvalidInput: Parameter ... must be a 2-D sequence with ...
80
+ """
81
+ # Return the value as is if not a sequence (e.g., str or None) or empty.
82
+ if not is_nonstr_iter (value ) or len (value ) == 0 :
83
+ return value
84
+
85
+ # 1-D sequence
86
+ if not is_nonstr_iter (value [0 ]):
87
+ if len (value ) != size :
88
+ msg = (
89
+ f"Parameter '{ name } ' must be a 1-D sequence of { size } values, "
90
+ f"but got { len (value )} values."
91
+ )
92
+ raise GMTInvalidInput (msg )
93
+ return separator .join (str (i ) for i in value )
94
+
95
+ # 2-D sequence
96
+ if ndim == 1 :
97
+ msg = f"Parameter '{ name } ' must be a 1-D sequence, not a 2-D sequence."
98
+ raise GMTInvalidInput (msg )
99
+
100
+ if any (len (i ) != size for i in value ):
101
+ msg = (
102
+ f"Parameter '{ name } ' must be a 2-D sequence with each sub-sequence "
103
+ f"having { size } values."
104
+ )
105
+ raise GMTInvalidInput (msg )
106
+ return [separator .join (str (j ) for j in value [i ]) for i in range (len (value ))]
107
+
108
+
19
109
# TODO(PyGMT>=0.19.0): Remove the deprecated "new" parameter.
20
110
@fmt_docstring
21
111
@deprecate_parameter ("new" , "replace" , "v0.15.0" , remove_version = "v0.19.0" )
22
- @use_alias (
23
- R = "region" ,
24
- Sa = "above" ,
25
- Sb = "below" ,
26
- Si = "between" ,
27
- Sr = "replace" ,
28
- V = "verbose" ,
29
- )
30
- @kwargs_to_strings (
31
- R = "sequence" ,
32
- Sa = "sequence" ,
33
- Sb = "sequence" ,
34
- Si = "sequence" ,
35
- Sr = "sequence" ,
36
- )
112
+ @use_alias (R = "region" , V = "verbose" )
113
+ @kwargs_to_strings (R = "sequence" )
37
114
def grdclip (
38
115
grid : PathLike | xr .DataArray ,
39
116
outgrid : PathLike | None = None ,
117
+ above : Sequence [float ] | None = None ,
118
+ below : Sequence [float ] | None = None ,
119
+ between : Sequence [float ] | Sequence [Sequence [float ]] | None = None ,
120
+ replace : Sequence [float ] | Sequence [Sequence [float ]] | None = None ,
40
121
** kwargs ,
41
122
) -> xr .DataArray | None :
42
- r """
123
+ """
43
124
Clip the range of grid values.
44
125
45
- Produce a clipped ``outgrid`` or :class:`xarray.DataArray` version of the
46
- input ``grid`` file.
126
+ This function operates on the values of a grid. It can:
127
+
128
+ - Set values smaller than a threshold to a new value
129
+ - Set values larger than a threshold to a new value
130
+ - Set values within a range to a new value
131
+ - Replace individual values with a new value
47
132
48
- The parameters ``above`` and ``below`` allow for a given value to be set
49
- for values above or below a set amount, respectively. This allows for
50
- extreme values in a grid, such as points below a certain depth when
51
- plotting Earth relief, to all be set to the same value .
133
+ Such operations are useful when you want all of a continent or an ocean to fall into
134
+ one color or gray shade in image processing, when clipping the range of data
135
+ values is required, or for reclassification of data values. The values can be any
136
+ number or NaN (Not a Number) .
52
137
53
138
Full option list at :gmt-docs:`grdclip.html`
54
139
@@ -59,19 +144,23 @@ def grdclip(
59
144
{grid}
60
145
{outgrid}
61
146
{region}
62
- above : str or list
63
- [*high*, *above*].
64
- Set all data[i] > *high* to *above*.
65
- below : str or list
66
- [*low*, *below*].
67
- Set all data[i] < *low* to *below*.
68
- between : str or list
69
- [*low*, *high*, *between*].
70
- Set all data[i] >= *low* and <= *high* to *between*.
71
- replace : str or list
72
- [*old*, *new*].
73
- Set all data[i] == *old* to *new*. This is mostly useful when
74
- your data are known to be integer values.
147
+ above
148
+ Pass a sequence of two values in the form of (*high*, *above*), to set all node
149
+ values greater than *high* to *above*.
150
+ below
151
+ Pass a sequence of two values in the form of (*low*, *below*) to set all node
152
+ values less than *low* to *below*.
153
+ between
154
+ Pass a sequence of three values in the form of (*low*, *high*, *between*) to set
155
+ all node values between *low* and *high* to *between*. It can also accept a
156
+ sequence of sequences (e.g., list of lists or 2-D numpy array) to set different
157
+ values for different ranges.
158
+ replace
159
+ Pass a sequence of two values in the form of (*old*, *new*) to replace all node
160
+ values equal to *old* with *new*. It can also accept a sequence of sequences
161
+ (e.g., list of lists or 2-D numpy array) to replace different old values with
162
+ different new values. This is mostly useful when your data are known to be
163
+ integer values.
75
164
{verbose}
76
165
77
166
Returns
@@ -101,6 +190,19 @@ def grdclip(
101
190
>>> [new_grid.data.min(), new_grid.data.max()]
102
191
[0.0, 10000.0]
103
192
"""
193
+ if all (v is None for v in (above , below , between , replace )):
194
+ msg = (
195
+ "Must specify at least one of the following parameters: " ,
196
+ "'above', 'below', 'between', or 'replace'." ,
197
+ )
198
+ raise GMTInvalidInput (msg )
199
+
200
+ # Parse the -S option.
201
+ kwargs ["Sa" ] = _parse_sequence ("above" , above , size = 2 , ndim = 1 )
202
+ kwargs ["Sb" ] = _parse_sequence ("below" , below , size = 2 , ndim = 1 )
203
+ kwargs ["Si" ] = _parse_sequence ("between" , between , size = 3 , ndim = 2 )
204
+ kwargs ["Sr" ] = _parse_sequence ("replace" , replace , size = 2 , ndim = 2 )
205
+
104
206
with Session () as lib :
105
207
with (
106
208
lib .virtualfile_in (check_kind = "raster" , data = grid ) as vingrd ,
0 commit comments