Skip to content

Commit 1f2282a

Browse files
brendancolthuydotm
andauthored
Stubbed out function for Analytics module (#621)
* stubbed out summarize_terrain function in analytics module #620 * added docs * renamed parameter to * update tests * add example * create_test_raster(): added name param * test analytics: use random_data pytest fixture * flake8 * add a new line to avoid sphinx warnings Co-authored-by: thuydotm <[email protected]>
1 parent 179347b commit 1f2282a

File tree

3 files changed

+121
-2
lines changed

3 files changed

+121
-2
lines changed

xrspatial/analytics.py

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from xrspatial import slope
2+
from xrspatial import curvature
3+
from xrspatial import aspect
4+
5+
import xarray as xr
6+
7+
8+
def summarize_terrain(terrain: xr.DataArray):
9+
"""
10+
Calculates slope, aspect, and curvature of an elevation terrain and return a dataset
11+
of the computed data.
12+
13+
Parameters
14+
----------
15+
terrain: xarray.DataArray
16+
2D NumPy, CuPy, or Dask with NumPy-backed xarray DataArray of elevation values.
17+
18+
Returns
19+
-------
20+
summarized_terrain: xarray.Dataset
21+
Dataset with slope, aspect, curvature variables with a naming convention of
22+
`terrain.name-variable_name`
23+
24+
Examples
25+
--------
26+
.. sourcecode:: python
27+
28+
>>> import numpy as np
29+
>>> import xarray as xr
30+
>>> from xrspatial.analytics import summarize_terrain
31+
>>> data = np.array([
32+
[0, 0, 0, 0, 0, 0, 0, 0],
33+
[0, 0, 0, 0, 0, 0, 0, 0],
34+
[0, 0, 1, 0, 0, -1, 0, 0],
35+
[0, 0, 0, 0, 0, 0, 0, 0],
36+
[0, 0, 0, 0, 0, 0, 0, 0]], dtype=np.float64)
37+
>>> raster = xr.DataArray(data, name='myraster', attrs={'res': (1, 1)})
38+
>>> summarized_terrain = summarize_terrain(raster)
39+
>>> summarized_terrain
40+
<xarray.Dataset>
41+
Dimensions: (dim_0: 5, dim_1: 8)
42+
Dimensions without coordinates: dim_0, dim_1
43+
Data variables:
44+
myraster (dim_0, dim_1) float64 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0
45+
myraster-slope (dim_0, dim_1) float32 nan nan nan nan ... nan nan nan
46+
myraster-curvature (dim_0, dim_1) float64 nan nan nan nan ... nan nan nan
47+
myraster-aspect (dim_0, dim_1) float32 nan nan nan nan ... nan nan nan
48+
>>> summarized_terrain['myraster-slope']
49+
<xarray.DataArray 'myraster-slope' (dim_0: 5, dim_1: 8)>
50+
array([[ nan, nan, nan, nan, nan, nan, nan, nan],
51+
[ nan, 10.024988, 14.036243, 10.024988, 10.024988, 14.036243, 10.024988, nan],
52+
[ nan, 14.036243, 0. , 14.036243, 14.036243, 0. , 14.036243, nan],
53+
[ nan, 10.024988, 14.036243, 10.024988, 10.024988, 14.036243, 10.024988, nan],
54+
[ nan, nan, nan, nan, nan, nan, nan, nan]], dtype=float32) # noqa
55+
Dimensions without coordinates: dim_0, dim_1
56+
Attributes:
57+
res: (1, 1)
58+
59+
>>> summarized_terrain['myraster-curvature']
60+
<xarray.DataArray 'myraster-curvature' (dim_0: 5, dim_1: 8)>
61+
array([[ nan, nan, nan, nan, nan, nan, nan, nan],
62+
[ nan, -0., -100., -0., -0., 100., -0., nan],
63+
[ nan, -100., 400., -100., 100., -400., 100., nan],
64+
[ nan, -0., -100., -0., -0., 100., -0., nan],
65+
[ nan, nan, nan, nan, nan, nan, nan, nan]])
66+
Dimensions without coordinates: dim_0, dim_1
67+
Attributes:
68+
res: (1, 1)
69+
70+
>>> summarized_terrain['myraster-aspect']
71+
<xarray.DataArray 'myraster-aspect' (dim_0: 5, dim_1: 8)>
72+
array([[ nan, nan, nan, nan, nan, nan, nan, nan],
73+
[ nan, 315., 0., 45., 135., 180., 225., nan],
74+
[ nan, 270., -1., 90., 90., -1., 270., nan],
75+
[ nan, 225., 180., 135., 45., 0., 315., nan],
76+
[ nan, nan, nan, nan, nan, nan, nan, nan]], dtype=float32)
77+
Dimensions without coordinates: dim_0, dim_1
78+
Attributes:
79+
res: (1, 1)
80+
"""
81+
82+
if terrain.name is None:
83+
raise NameError('Requires xr.DataArray.name property to be set')
84+
85+
ds = terrain.to_dataset()
86+
ds[f'{terrain.name}-slope'] = slope(terrain)
87+
ds[f'{terrain.name}-curvature'] = curvature(terrain)
88+
ds[f'{terrain.name}-aspect'] = aspect(terrain)
89+
return ds

xrspatial/tests/general_checks.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
from xrspatial.utils import ArrayTypeFunctionMapping
77

88

9-
def create_test_raster(data, backend='numpy', dims=['y', 'x'], attrs=None, chunks=(3, 3)):
10-
raster = xr.DataArray(data, dims=dims, attrs=attrs)
9+
def create_test_raster(
10+
data, backend='numpy', name='myraster', dims=['y', 'x'], attrs=None, chunks=(3, 3)
11+
):
12+
raster = xr.DataArray(data, name=name, dims=dims, attrs=attrs)
1113
# set coords for test raster
1214
for i, dim in enumerate(dims):
1315
raster[dim] = np.linspace(0, data.shape[i] - 1, data.shape[i])

xrspatial/tests/test_analytics.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import pytest
2+
import numpy as np
3+
4+
from xrspatial import aspect
5+
from xrspatial import curvature
6+
from xrspatial import slope
7+
from xrspatial.analytics import summarize_terrain
8+
9+
from xrspatial.tests.general_checks import create_test_raster
10+
11+
12+
@pytest.mark.parametrize("size", [(2, 4), (100, 150)])
13+
@pytest.mark.parametrize(
14+
"dtype", [np.int32, np.int64, np.uint32, np.uint64, np.float32, np.float64]
15+
)
16+
def test_summarize_terrain(random_data):
17+
test_terrain = create_test_raster(random_data, name='myterrain')
18+
summarized_ds = summarize_terrain(test_terrain)
19+
variables = [v for v in summarized_ds]
20+
should_have = ['myterrain',
21+
'myterrain-slope',
22+
'myterrain-curvature',
23+
'myterrain-aspect']
24+
assert variables == should_have
25+
26+
np.testing.assert_allclose(summarized_ds['myterrain-slope'], slope(test_terrain))
27+
np.testing.assert_allclose(summarized_ds['myterrain-curvature'], curvature(test_terrain))
28+
np.testing.assert_allclose(summarized_ds['myterrain-aspect'], aspect(test_terrain))

0 commit comments

Comments
 (0)