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