Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using sapp() to apply terra::focal to each layer of a SpatRaster

I have recently started using terra and must commend the devs, it has made my life so much easier whilst working with many large rasters in R. However, I've stumbled into a minor issue, I am attempting use sapp to apply a focal function to each layer in a SpatRater as focal can only be applied to one layer at a time.

With a small reproducible RasterSpat I can run the following as an example of the desired output:

library(terra)
packageVersion("terra")
>[1] ‘1.2.10’
    
s <- rast(system.file("ex/logo.tif", package="terra"))
s <- ifel(s == 255, 1, NA)

r1 <- terra::focal(s[[1]], w=3, fun = "any", na.only=TRUE)
r2 <- terra::focal(s[[2]], w=3, fun = "any", na.only=TRUE)
r3 <- terra::focal(s[[3]], w=3, fun = "any", na.only=TRUE)
r <- c(r1,r2,r3)
r

#class       : SpatRaster 
#dimensions  : 77, 101, 3  (nrow, ncol, nlyr)
#resolution  : 1, 1  (x, y)
#extent      : 0, 101, 0, 77  (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs 
#sources     : memory  
#              memory  
#              memory  
#names       : red, green, blue 
#min values  :   0,     0,    0 
#max values  :   1,     1,    1 

When I run sapp for the above reproducible data using the same grammar as sapply:

f1 <- sapp(s, fun = function(x){terra::focal(x = x, w = 3, fun = "any", na.only = TRUE)})

I get:

Error in h(simpleError(msg, call)) : error in evaluating the argument 'x' in selecting a method for function 'rast': unused argument (wopt = wopt)

If I try:

f <- sapp(s,  terra::focal, c(w= 3, fun = "any", na.only = TRUE))
f

I get the following:

#class       : SpatRaster 
#dimensions  : 77, 101, 3  (nrow, ncol, nlyr)
#resolution  : 1, 1  (x, y)
#extent      : 0, 101, 0, 77  (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs 
#source      : memory 
#names       : red, green, blue 
#min values  :   0,     0,    0 
#max values  :   9,     9,    9 

Notice the max values of the layers. How can I adjust my code to get sapp to work as desired?

I can get the desired result with sapply, but I assume sapp would be far more efficient for larger data if I can get it to work.

f2 <- sapply(as.list(s), function(x){terra::focal(x = x, w= 3, fun = "any", na.only = TRUE)})
f2 <- rast(f2)
f2
#class       : SpatRaster 
#dimensions  : 77, 101, 3  (nrow, ncol, nlyr)
#resolution  : 1, 1  (x, y)
#extent      : 0, 101, 0, 77  (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs 
#sources     : memory  
               memory  
               memory  
#names       : red, green, blue 
#min values  :   0,     0,    0 
#max values  :   1,     1,    1 

If anybody can provide some assistance, it would be greatly appreciated :)

like image 293
LiamS Avatar asked Sep 03 '25 16:09

LiamS


1 Answers

I have solved my issue.

Turns out you need to include the ... argument to the function sent to fun like so:

s <- sapp(s, fun = function(x, ...) {focal(x, fun = "any", w = 3)})

Then it works

As it turns out sapp calls sapply internaly and hasn't implemented parallelization yet and multiple core can't be allocated, so there is not much of a speed improvement as compared to app which implements parallelization vs apply.

Here are some benchmarks if any body is interested...

terra::terraOptions(todisk = TRUE)
s <- rast(system.file("ex/logo.tif", package="terra"))
s <- terra::extend(s, c(500, 500))
s <- disaggregate(s, 10)
s <- terra::ifel(s == 255, 1, 0)

a <- function() {
    s <- sapply(as.list(s), function(x){terra::focal(x = x, w= 3, fun = "any")})
    s <- rast(s)}

b <- function(){
    s <- sapp(s, fun = function(x,...) {focal(x, fun = "any", w = 3)})}


race <- microbenchmark::microbenchmark(
    a(),
    b(),
    times = 5)

Results:

race
Unit: seconds
 expr      min       lq     mean   median       uq      max neval cld
  a() 63.32553 63.93399 65.03783 65.61699 65.73256 66.58011     5   a
  b() 62.88114 63.85961 64.34571 64.16861 65.22703 65.59215     5   a
like image 63
LiamS Avatar answered Sep 05 '25 09:09

LiamS



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!