I do not know much about GRASS, but given there are variety of tools such as r.neighbors (focal statistics), I'm sure you already have found the solution with GRASS.
BTW, this is just to follow up on the other lead - comments by Jeffrey and whuber, both recommended raster algebra.
I tried to put it in R script, which can be run by copy/paste with Script Editor
- via Processing Toolbox | R scripts | Tools | Create new R script
.
TPI.rsx -- please copy this to Script Editor
##Raster_Tools= group
##Input_Raster_Layer= raster
##Band_number= number 1
##Outer_Window_Size= number 3
##Inner_Window_Size= optional number 1
##Compute_edges= boolean TRUE
##Topographic_Position_Index= output raster
library(raster)
ow <- Outer_Window_Size
iw <- Inner_Window_Size
edge <- Compute_edges
Layer <- Input_Raster_Layer[[Band_number]]
Outer_window <- focal(Layer, w=matrix(1, nrow=ow, ncol=ow),
fun=mean, na.rm=FALSE, NAonly=TRUE, pad=edge)
if(iw>=3){Inner_window = focal(Layer, w=matrix(1, nrow=iw, ncol=iw),
fun=mean, na.rm=FALSE, NAonly=TRUE, pad=edge)}
if(iw==1){Inner_window = Layer}
Topographic_Position_Index <- Inner_window - Outer_window
If you save this file into ~/.qgis2/processing/rscripts/
as TPI.rsx
there would appear a new geoalgorithm TPI under Raster Tools group.
Note
- Outer window size: default= 3 (cells)...odd number 3, 5, 7, ...
- Inner window size: default= 1 (cell)...odd number 1, 3, 5, ...
If we run it with Outer=3 and Inner= 1, it produces a raster layer which is equivalent to the gdaldem tpi. Please modify Outer window size as necessary.
Usually Inner window size would always be 1, unless you need to work with very fine grid (e.g. LiDAR) which can make your TPI analysis difficult.