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 :)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With