Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion xrspatial/slope.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# std lib
from functools import partial
from math import atan
from math import atan, isnan, nan
from typing import Optional, Union

# 3rd-party
Expand Down Expand Up @@ -52,6 +52,8 @@ def _cpu(data, cellsize_x, cellsize_y):
rows, cols = data.shape
for y in range(1, rows - 1):
for x in range(1, cols - 1):
if np.isnan(data[y, x]):
continue
a = data[y + 1, x - 1]
b = data[y + 1, x]
c = data[y + 1, x + 1]
Expand Down Expand Up @@ -112,6 +114,8 @@ def _run_dask_cupy(data: da.Array,

@cuda.jit(device=True)
def _gpu(arr, cellsize_x, cellsize_y):
if isnan(arr[1, 1]):
return nan
a = arr[2, 0]
b = arr[2, 1]
c = arr[2, 2]
Expand Down
45 changes: 45 additions & 0 deletions xrspatial/tests/test_slope.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,48 @@ def test_degenerate_shape_geodesic(shape):
general_output_checks(raster, result)
assert result.shape == shape
assert np.all(np.isnan(result.data))


# A NoData (NaN) center cell must stay NaN in the slope output, even when all 8
# of its neighbours are valid. The planar kernels read the center cell, so a
# hole in the DEM can't masquerade as valid flat terrain. Regression for #2761.
def _center_nan_data():
data = np.zeros((5, 5), dtype=np.float64)
data[2, 2] = np.nan
return data


def test_center_nan_propagates_numpy():
agg = create_test_raster(_center_nan_data(), backend='numpy', attrs={'res': (1, 1)})
result = slope(agg)
general_output_checks(agg, result)
assert np.isnan(result.data[2, 2])


@dask_array_available
def test_center_nan_propagates_dask_numpy():
data = _center_nan_data()
numpy_agg = create_test_raster(data, backend='numpy', attrs={'res': (1, 1)})
dask_agg = create_test_raster(data, backend='dask+numpy',
attrs={'res': (1, 1)}, chunks=(3, 3))
assert_numpy_equals_dask_numpy(numpy_agg, dask_agg, slope, nan_edges=False)
assert np.isnan(slope(dask_agg).data.compute()[2, 2])


@cuda_and_cupy_available
def test_center_nan_propagates_cupy():
data = _center_nan_data()
numpy_agg = create_test_raster(data, backend='numpy', attrs={'res': (1, 1)})
cupy_agg = create_test_raster(data, backend='cupy', attrs={'res': (1, 1)})
assert_numpy_equals_cupy(numpy_agg, cupy_agg, slope, nan_edges=False)
assert np.isnan(slope(cupy_agg).data.get()[2, 2])


@dask_array_available
@cuda_and_cupy_available
def test_center_nan_propagates_dask_cupy():
data = _center_nan_data()
numpy_agg = create_test_raster(data, backend='numpy', attrs={'res': (1, 1)})
dask_cupy_agg = create_test_raster(data, backend='dask+cupy',
attrs={'res': (1, 1)}, chunks=(3, 3))
assert_numpy_equals_dask_cupy(numpy_agg, dask_cupy_agg, slope, nan_edges=False)
Loading