Skip to content

Commit b19c413

Browse files
committed
Add terrain metrics module: TRI, TPI, and Roughness (#919)
Implement three widely-used terrain morphometry metrics as 3×3 focal window operations on elevation values (no cellsize needed): - TRI (Terrain Ruggedness Index) — Riley et al. 1999 - TPI (Topographic Position Index) — Weiss 2001 - Roughness — GDAL definition (max - min in 3×3 window) All three support four backends (numpy, cupy, dask+numpy, dask+cupy), boundary modes (nan, nearest, reflect, wrap), and Dataset input via @supports_dataset. NaN propagates through all kernels for consistency between numpy and dask paths (standard GDAL behavior).
1 parent a676430 commit b19c413

File tree

6 files changed

+809
-0
lines changed

6 files changed

+809
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,11 @@ In the GIS world, rasters are used for representing continuous phenomena (e.g. e
227227
| [Aspect](xrspatial/aspect.py) | Computes downslope direction of each cell in degrees | ✅️ | ✅️ | ✅️ | ✅️ |
228228
| [Curvature](xrspatial/curvature.py) | Measures rate of slope change (concavity/convexity) at each cell | ✅️ |✅️ |✅️ | ✅️ |
229229
| [Hillshade](xrspatial/hillshade.py) | Simulates terrain illumination from a given sun angle and azimuth | ✅️ | ✅️ | ✅️ | ✅️ |
230+
| [Roughness](xrspatial/terrain_metrics.py) | Computes local relief as max minus min elevation in a 3×3 window | ✅️ | ✅️ | ✅️ | ✅️ |
230231
| [Slope](xrspatial/slope.py) | Computes terrain gradient steepness at each cell in degrees | ✅️ | ✅️ | ✅️ | ✅️ |
231232
| [Terrain Generation](xrspatial/terrain.py) | Generates synthetic terrain elevation using fractal noise | ✅️ | ✅️ | ✅️ | ✅️ |
233+
| [TPI](xrspatial/terrain_metrics.py) | Computes Topographic Position Index (center minus mean of neighbors) | ✅️ | ✅️ | ✅️ | ✅️ |
234+
| [TRI](xrspatial/terrain_metrics.py) | Computes Terrain Ruggedness Index (local elevation variation) | ✅️ | ✅️ | ✅️ | ✅️ |
232235
| [Viewshed](xrspatial/viewshed.py) | Determines visible cells from a given observer point on terrain | ✅️ | ✅️ | ✅️ | ✅️ |
233236
| [Min Observable Height](xrspatial/experimental/min_observable_height.py) | Finds the minimum observer height needed to see each cell *(experimental)* | ✅️ | | | |
234237
| [Perlin Noise](xrspatial/perlin.py) | Generates smooth continuous random noise for procedural textures | ✅️ | ✅️ | ✅️ | ✅️ |
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from xrspatial import tri, tpi, roughness
2+
3+
from .common import Benchmarking
4+
5+
6+
class TRI(Benchmarking):
7+
def __init__(self):
8+
super().__init__(func=tri)
9+
10+
def time_tri(self, nx, type):
11+
return self.time(nx, type)
12+
13+
14+
class TPI(Benchmarking):
15+
def __init__(self):
16+
super().__init__(func=tpi)
17+
18+
def time_tpi(self, nx, type):
19+
return self.time(nx, type)
20+
21+
22+
class Roughness(Benchmarking):
23+
def __init__(self):
24+
super().__init__(func=roughness)
25+
26+
def time_roughness(self, nx, type):
27+
return self.time(nx, type)

xrspatial/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
from xrspatial.surface_distance import surface_direction # noqa
3737
from xrspatial.surface_distance import surface_distance # noqa
3838
from xrspatial.terrain import generate_terrain # noqa
39+
from xrspatial.terrain_metrics import roughness # noqa
40+
from xrspatial.terrain_metrics import tpi # noqa
41+
from xrspatial.terrain_metrics import tri # noqa
3942
from xrspatial.polygonize import polygonize # noqa
4043
from xrspatial.viewshed import viewshed # noqa
4144
from xrspatial.zonal import apply as zonal_apply # noqa

xrspatial/accessor.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,20 @@ def curvature(self, **kwargs):
3939
from .curvature import curvature
4040
return curvature(self._obj, **kwargs)
4141

42+
# ---- Terrain Metrics ----
43+
44+
def tri(self, **kwargs):
45+
from .terrain_metrics import tri
46+
return tri(self._obj, **kwargs)
47+
48+
def tpi(self, **kwargs):
49+
from .terrain_metrics import tpi
50+
return tpi(self._obj, **kwargs)
51+
52+
def roughness(self, **kwargs):
53+
from .terrain_metrics import roughness
54+
return roughness(self._obj, **kwargs)
55+
4256
def viewshed(self, x, y, **kwargs):
4357
from .viewshed import viewshed
4458
return viewshed(self._obj, x, y, **kwargs)
@@ -221,6 +235,20 @@ def curvature(self, **kwargs):
221235
from .curvature import curvature
222236
return curvature(self._obj, **kwargs)
223237

238+
# ---- Terrain Metrics ----
239+
240+
def tri(self, **kwargs):
241+
from .terrain_metrics import tri
242+
return tri(self._obj, **kwargs)
243+
244+
def tpi(self, **kwargs):
245+
from .terrain_metrics import tpi
246+
return tpi(self._obj, **kwargs)
247+
248+
def roughness(self, **kwargs):
249+
from .terrain_metrics import roughness
250+
return roughness(self._obj, **kwargs)
251+
224252
# ---- Classification ----
225253

226254
def natural_breaks(self, **kwargs):

0 commit comments

Comments
 (0)