Skip to content

Commit 1911a34

Browse files
Merge branch 'main' into wrap/gmtselect
2 parents e204dda + d12470f commit 1911a34

File tree

3 files changed

+161
-29
lines changed

3 files changed

+161
-29
lines changed

pygmt/src/grdgradient.py

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@
2121
D="direction",
2222
E="radiance",
2323
G="outgrid",
24+
N="normalize",
25+
Q="tiles",
2426
R="region",
27+
S="slope_file",
2528
V="verbose",
29+
f="coltypes",
2630
n="interpolation",
2731
)
2832
@kwargs_to_strings(A="sequence", E="sequence", R="sequence")
@@ -49,35 +53,38 @@ def grdgradient(grid, **kwargs):
4953
Azimuthal direction for a directional derivative; *azim* is the
5054
angle in the x,y plane measured in degrees positive clockwise from
5155
north (the +y direction) toward east (the +x direction). The
52-
negative of the directional derivative, -[dz/dx\*sin(*azim*) +
53-
dz/dy\*cos(\ *azim*)], is found; negation yields positive values
54-
when the slope of z(x,y) is downhill in the *azim* direction, the
55-
correct sense for shading the illumination of an image by a light
56-
source above the x,y plane shining from the *azim* direction.
57-
Optionally, supply two azimuths, *azim*/*azim2*, in which case the
58-
gradients in each of these directions are calculated and the one
59-
larger in magnitude is retained; this is useful for illuminating data
60-
with two directions of lineated structures, e.g., *0*/*270*
56+
negative of the directional derivative,
57+
:math:`-(\frac{{dz}}{{dx}}\sin(\mbox{{azim}}) + \
58+
\frac{{dz}}{{dy}}\cos(\mbox{{azim}}))`, is found; negation yields
59+
positive values when the slope of :math:`z(x,y)` is downhill in the
60+
*azim* direction, the correct sense for shading the illumination of an
61+
image by a light source above the x,y plane shining from the *azim*
62+
direction. Optionally, supply two azimuths, *azim*/*azim2*, in which
63+
case the gradients in each of these directions are calculated and the
64+
one larger in magnitude is retained; this is useful for illuminating
65+
data with two directions of lineated structures, e.g., *0*/*270*
6166
illuminates from the north (top) and west (left). Finally, if *azim*
6267
is a file it must be a grid of the same domain, spacing and
6368
registration as *grid* that will update the azimuth at each output
6469
node when computing the directional derivatives.
6570
direction : str
6671
[**a**][**c**][**o**][**n**].
6772
Find the direction of the positive (up-slope) gradient of the data.
68-
To instead find the aspect (the down-slope direction), use **a**.
69-
By default, directions are measured clockwise from north, as *azim*
70-
in ``azimuth``. Append **c** to use conventional Cartesian angles
71-
measured counterclockwise from the positive x (east) direction.
72-
Append **o** to report orientations (0-180) rather than
73-
directions (0-360). Append **n** to add 90 degrees to all angles
74-
(e.g., to give local strikes of the surface).
73+
The following options are supported:
74+
75+
- **a** - Find the aspect (i.e., the down-slope direction)
76+
- **c** - Use the conventional Cartesian angles measured
77+
counterclockwise from the positive x (east) direction.
78+
- **o** - Report orientations (0-180) rather than directions (0-360).
79+
- **n** - Add 90 degrees to all angles (e.g., to give local strikes of
80+
the surface).
7581
radiance : str or list
7682
[**m**\|\ **s**\|\ **p**]\ *azim/elev*\ [**+a**\ *ambient*][**+d**\
7783
*diffuse*][**+p**\ *specular*][**+s**\ *shine*].
78-
Compute Lambertian radiance appropriate to use with ``grdimage``
79-
and ``grdview``. The Lambertian Reflection assumes an ideal surface
80-
that reflects all the light that strikes it and the surface appears
84+
Compute Lambertian radiance appropriate to use with
85+
:doc:`pygmt.Figure.grdimage` and :doc:`pygmt.Figure.grdview`. The
86+
Lambertian Reflection assumes an ideal surface that reflects all the
87+
light that strikes it and the surface appears
8188
equally bright from all viewing directions. Here, *azim* and *elev* are
8289
the azimuth and elevation of the light vector. Optionally, supply
8390
*ambient* [0.55], *diffuse* [0.6], *specular* [0.4], or *shine* [10],
@@ -86,11 +93,51 @@ def grdgradient(grid, **kwargs):
8693
simpler Lambertian algorithm. Note that with this form you only have
8794
to provide azimuth and elevation. Alternatively, use **p** for
8895
the Peucker piecewise linear approximation (simpler but faster
89-
algorithm; in this case the *azim* and *elev* are hardwired to 315
96+
algorithm; in this case *azim* and *elev* are hardwired to 315
9097
and 45 degrees. This means that even if you provide other values
91-
they will be ignored.)
98+
they will be ignored.).
99+
normalize : str or bool
100+
[**e**\|\ **t**][*amp*][**+a**\ *ambient*][**+s**\ *sigma*]\
101+
[**+o**\ *offset*].
102+
The actual gradients :math:`g` are offset and scaled to produce
103+
normalized gradients :math:`g_n` with a maximum output magnitude of
104+
*amp*. If *amp* is not given, default *amp* = 1. If *offset* is not
105+
given, it is set to the average of :math:`g`. The following forms are
106+
supported:
107+
108+
- **True** - Normalize using :math:`g_n = \mbox{{amp}}\
109+
(\frac{{g - \mbox{{offset}}}}{{max(|g - \mbox{{offset}}|)}})`
110+
- **e** - Normalize using a cumulative Laplace distribution yielding:
111+
:math:`g_n = \mbox{{amp}}(1 - \
112+
\exp{{(\sqrt{{2}}\frac{{g - \mbox{{offset}}}}{{\sigma}}))}}`, where
113+
:math:`\sigma` is estimated using the L1 norm of
114+
:math:`(g - \mbox{{offset}})` if it is not given.
115+
- **t** - Normalize using a cumulative Cauchy distribution yielding:
116+
:math:`g_n = \
117+
\frac{{2(\mbox{{amp}})}}{{\pi}}(\tan^{{-1}}(\frac{{g - \
118+
\mbox{{offset}}}}{{\sigma}}))` where :math:`\sigma` is estimated
119+
using the L2 norm of :math:`(g - \mbox{{offset}})` if it is not
120+
given.
121+
122+
As a final option, you may add **+a**\ *ambient* to add *ambient* to
123+
all nodes after gradient calculations are completed.
124+
tiles : str
125+
**c**\|\ **r**\|\ **R**.
126+
Controls how normalization via ``normalize`` is carried out. When
127+
multiple grids should be normalized the same way (i.e., with the same
128+
*offset* and/or *sigma*),
129+
we must pass these values via ``normalize``. However, this is
130+
inconvenient if we compute these values from a grid. Use **c** to
131+
save the results of *offset* and *sigma* to a statistics file; if
132+
grid output is not needed for this run then do not specify
133+
``outgrid``. For subsequent runs, just use **r** to read these
134+
values. Using **R** will read then delete the statistics file.
92135
{R}
136+
slope_file : str
137+
Name of output grid file with scalar magnitudes of gradient vectors.
138+
Requires ``direction`` but makes ``outgrid`` optional.
93139
{V}
140+
{f}
94141
{n}
95142
96143
Returns
@@ -103,6 +150,8 @@ def grdgradient(grid, **kwargs):
103150
``outgrid``)
104151
"""
105152
with GMTTempFile(suffix=".nc") as tmpfile:
153+
if "Q" in kwargs and "N" not in kwargs:
154+
raise GMTInvalidInput("""Must specify normalize if tiles is specified.""")
106155
if not args_in_kwargs(args=["A", "D", "E"], kwargs=kwargs):
107156
raise GMTInvalidInput(
108157
"""At least one of the following parameters must be specified:

pygmt/src/xyz2grd.py

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,37 @@
1414

1515
@fmt_docstring
1616
@use_alias(
17+
A="duplicate",
1718
G="outgrid",
1819
I="spacing",
20+
J="projection",
1921
R="region",
2022
V="verbose",
23+
Z="convention",
24+
b="binary",
25+
d="nodata",
26+
e="find",
27+
f="coltypes",
28+
h="header",
29+
i="incols",
30+
r="registration",
31+
w="wrap",
2132
)
2233
@kwargs_to_strings(R="sequence")
2334
def xyz2grd(data=None, x=None, y=None, z=None, **kwargs):
24-
"""
35+
r"""
2536
Create a grid file from table data.
2637
27-
xyz2grd reads one or more z or xyz tables and creates a binary grid file.
28-
xyz2grd will report if some of the nodes are not filled in with data. Such
29-
unconstrained nodes are set to a value specified by the user [Default is
30-
NaN]. Nodes with more than one value will be set to the mean value.
38+
Reads one or more tables with *x, y, z* columns and creates a binary grid
39+
file. xyz2grd will report if some of the nodes are not filled in with
40+
data. Such unconstrained nodes are set to a value specified by the user
41+
[Default is NaN]. Nodes with more than one value will be set to the mean
42+
value.
3143
3244
Full option list at :gmt-docs:`xyz2grd.html`
3345
46+
{aliases}
47+
3448
Parameters
3549
----------
3650
data : str or {table-like}
@@ -41,9 +55,73 @@ def xyz2grd(data=None, x=None, y=None, z=None, **kwargs):
4155
outgrid : str or None
4256
Optional. The name of the output netCDF file with extension .nc to
4357
store the grid in.
58+
duplicate : str
59+
[**d**\|\ **f**\|\ **l**\|\ **m**\|\ **n**\|\
60+
**r**\|\ **S**\|\ **s**\|\ **u**\|\ **z**].
61+
By default we will calculate mean values if multiple entries fall on
62+
the same node. Use **-A** to change this behavior, except it is
63+
ignored if **-Z** is given. Append **f** or **s** to simply keep the
64+
first or last data point that was assigned to each node. Append
65+
**l** or **u** or **d** to find the lowest (minimum) or upper (maximum)
66+
value or the difference between the maximum and miminum value
67+
at each node, respectively. Append **m** or **r** or **S** to compute
68+
mean or RMS value or standard deviation at each node, respectively.
69+
Append **n** to simply count the number of data points that were
70+
assigned to each node (this only requires two input columns *x* and
71+
*y* as *z* is not consulted). Append **z** to sum multiple values that
72+
belong to the same node.
4473
{I}
74+
{J}
4575
{R}
4676
{V}
77+
convention : str
78+
[*flags*].
79+
Read a 1-column ASCII [or binary] table. This assumes that all the
80+
nodes are present and sorted according to specified ordering
81+
convention contained in *flags*. If incoming data represents rows,
82+
make *flags* start with **T**\ (op) if first row is y
83+
= ymax or **B**\ (ottom) if first row is y = ymin.
84+
Then, append **L** or **R** to indicate that first element is at
85+
left or right end of row. Likewise for column formats: start with
86+
**L** or **R** to position first column, and then append **T** or
87+
**B** to position first element in a row. **Note**: These two
88+
row/column indicators are only required for grids; for other tables
89+
they do not apply. For gridline registered grids: If data are periodic
90+
in x but the incoming data do not contain the (redundant) column at
91+
x = xmax, append **x**. For data periodic in y without redundant row at
92+
y = ymax, append **y**. Append **s**\ *n* to skip the first *n* number
93+
of bytes (probably a header). If the byte-order or the words needs
94+
to be swapped, append **w**. Select one of several data types (all
95+
binary except **a**):
96+
97+
- **A** ASCII representation of one or more floating point values per
98+
record
99+
- **a** ASCII representation of a single item per record
100+
- **c** int8_t, signed 1-byte character
101+
- **u** uint8_t, unsigned 1-byte character
102+
- **h** int16_t, signed 2-byte integer
103+
- **H** uint16_t, unsigned 2-byte integer
104+
- **i** int32_t, signed 4-byte integer
105+
- **I** uint32_t, unsigned 4-byte integer
106+
- **l** int64_t, long (8-byte) integer
107+
- **L** uint64_t, unsigned long (8-byte) integer
108+
- **f** 4-byte floating point single precision
109+
- **d** 8-byte floating point double precision
110+
111+
Default format is scanline orientation of ASCII numbers: **-ZTLa**.
112+
The difference between **A** and **a** is that the latter can decode
113+
both *date*\ **T**\ *clock* and *ddd:mm:ss[.xx]* formats but expects
114+
each input record to have a single value, while the former can handle
115+
multiple values per record but can only parse regular floating point
116+
values. Translate incoming *z*-values via the ``incols`` parameter.
117+
{b}
118+
{d}
119+
{e}
120+
{f}
121+
{h}
122+
{i}
123+
{r}
124+
{w}
47125
48126
Returns
49127
-------

pygmt/tests/test_grdgradient.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,13 @@ def test_grdgradient_no_outgrid(grid):
5555

5656
def test_grdgradient_fails(grid):
5757
"""
58-
Check that grdgradient fails correctly when neither of azimuth, direction
59-
or radiance is given.
58+
Check that grdgradient fails correctly.
59+
60+
Check that grdgradient fails correctly when `tiles` is specified but
61+
normalize is not.
6062
"""
6163
with pytest.raises(GMTInvalidInput):
62-
grdgradient(grid=grid)
64+
grdgradient(grid=grid) # fails without required arguments
65+
with pytest.raises(GMTInvalidInput):
66+
# failes when tiles is specified but not normalize
67+
grdgradient(grid=grid, azimuth=10, direction="c", tiles="c")

0 commit comments

Comments
 (0)