Skip to content

Commit

Permalink
Add Evansslope to GridObject (TopoToolbox#119)
Browse files Browse the repository at this point in the history
This pull request adds the evansslope function to the GridObject. There are a few differences to the Matlab implementation:

- removed removenans argument, since scipy.ndimage.convolve needs NaNs to be removed (so it's always replacing NaNs with nearest neighbor)
- padval is renamed mode, to better reflect the underlying scipy function.
- Added partial_derivitives argument to provide the option to return fx and fy like in Matlab
- removed a bunch of code used for padding the DEM, since the padding is handled in the convolution function
  • Loading branch information
Teschl authored Dec 30, 2024
1 parent 7a94f26 commit 90d97f4
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 1 deletion.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ disable = [
"duplicate-code",
"cyclic-import",
"too-many-positional-arguments",
"too-many-lines",
]
65 changes: 64 additions & 1 deletion src/topotoolbox/grid_object.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""This module contains the GridObject class.
"""
import copy
from typing import Tuple

import numpy as np
import matplotlib.pyplot as plt
Expand All @@ -10,7 +11,8 @@
median_filter,
generic_filter,
grey_erosion,
grey_dilation)
grey_dilation,
distance_transform_edt)
from scipy.signal import wiener
from rasterio import CRS
from rasterio.warp import reproject
Expand Down Expand Up @@ -628,6 +630,67 @@ def erode(
result.z = eroded
return result

def evansslope(
self, partial_derivitives: bool = False, mode: str = 'nearest',
modified: bool = False) -> 'GridObject' | Tuple['GridObject', 'GridObject']:
"""Evans method fits a second-order polynomial to 3x3 subgrids. The
parameters of the polynomial are the partial derivatives which are
used to calculate the surface slope = sqrt(Gx**2 + Gy**2).
Evans method approximates the surface by regression surfaces.
Gradients are thus less susceptible to noise in the DEM.
Parameters
----------
mode : str, optional
The mode parameter determines how the input DEM is extended
beyond its boundaries: ['reflect', 'constant', 'nearest', 'mirror',
'wrap', 'grid-mirror', 'grid-constant', 'grid-wrap']. See
scipy.ndimage.convolve for more information, by default 'nearest'
modified : bool, optional
If True, the surface is weakly smoothed before gradients are
calculated (see Shary et al., 2002), by default False
partial_derivitives : bool, optional
If True, both partial derivatives [fx, fy] will be returned as
GridObjects instead of just the evansslope, by default False
Returns
-------
GridObject
A GridObject containing the computed evansslope data.
"""
dem = self.z.copy()
# NaN replacement not optional since convolve can't handle NaNs
indices = distance_transform_edt(
np.isnan(dem), return_distances=False, return_indices=True)
dem = dem[tuple(indices)]

if modified:
kernel = np.array([[0, 1, 0], [1, 41, 1], [0, 1, 0]])/45
dem = convolve(dem, kernel, mode=mode)

kx = np.array(
[[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]])/(6*self.cellsize)
fx = convolve(dem, kx, mode=mode)
# kernel for dz/dy
ky = np.array(
[[1, 1, 1], [0, 0, 0], [-1, -1, -1]])/(6*self.cellsize)
fy = convolve(dem, ky, mode=mode)

if partial_derivitives:
result_kx = copy.copy(self)
result_ky = copy.copy(self)
result_kx.z = kx
result_ky.z = ky
return result_kx, result_ky

slope = np.sqrt(fx**2 + fy**2)
slope[np.isnan(self.z)] = np.nan

result = copy.copy(self)
result.z = slope
return result

def _gwdt_computecosts(self) -> np.ndarray:
"""
Compute the cost array used in the gradient-weighted distance
Expand Down

0 comments on commit 90d97f4

Please sign in to comment.