1

I am trying to shortern my code by using functions and for loops.

For the example below, I would like to create 5 different objects and then apply raster to each object that is created. How can I do this with loops and functions?

#Defining variables of interest
url = "https://files.isric.org/soilgrids/latest/data/" # Path to the webDAV data.
voi1 = "sand"
voi2 = "clay"
voi3 = "silt"
voi4 = "phh2o"
voi5 = "soc"
depth = "5-15cm"

quantile = "mean" # prediction uncertainty quantified by probability distribution. Using mean of distribution
voi_layer1 = paste(paste(paste(url, voi1, "/", voi1, sep=""), depth, quantile, sep="_"), '.vrt', sep="")
voi_layer2 = paste(paste(paste(url, voi2, "/", voi2, sep=""), depth, quantile, sep="_"), '.vrt', sep="")
voi_layer3 = paste(paste(paste(url, voi3, "/", voi3, sep=""), depth, quantile, sep="_"), '.vrt', sep="")
voi_layer4 = paste(paste(paste(url, voi4, "/", voi4, sep=""), depth, quantile, sep="_"), '.vrt', sep="")
voi_layer5 = paste(paste(paste(url, voi5, "/", voi5, sep=""), depth, quantile, sep="_"), '.vrt', sep="")

#Apply 'raster' so can derive descriptions of each layer
sand = raster(voi_layer1)
clay = raster(voi_layer2)
silt = raster(voi_layer3)
ph = raster(voi_layer4)
org_carb = raster(voi_layer5)
zephryl
  • 14,633
  • 3
  • 11
  • 30
Cameron So
  • 139
  • 11

3 Answers3

4

There are a few ways your code could be streamlined. Since you're performing the same operation for each VOI, you can store them in a vector. Note that voi here contains all 5 VOI:

base_url <- "https://files.isric.org/soilgrids/latest/data/" # Path to the webDAV data.
voi <- c('sand', 'clay', 'silt', 'phh2o', 'soc')
depth <- "5-15cm"
quantile <- "mean" 

We can use sprintf to more succinctly create the fetch URLs:

urls <- sprintf('%s%s/%s_%s_%s.vrt', base_url, voi, voi, depth, quantile)
names(urls) <- voi
                                                                       sand 
  "https://files.isric.org/soilgrids/latest/data/sand/sand_5-15cm_mean.vrt" 
                                                                       clay 
  "https://files.isric.org/soilgrids/latest/data/clay/clay_5-15cm_mean.vrt" 
                                                                       silt 
  "https://files.isric.org/soilgrids/latest/data/silt/silt_5-15cm_mean.vrt" 
                                                                      phh2o 
"https://files.isric.org/soilgrids/latest/data/phh2o/phh2o_5-15cm_mean.vrt" 
                                                                        soc 
    "https://files.isric.org/soilgrids/latest/data/soc/soc_5-15cm_mean.vrt" 

And finally, we can use lapply to perform the raster() function on each constructed URL (lapply is a looping function).

rasters <- lapply(urls, raster)

Note that because we've provided names for the elements of urls above, these are retained in the rasters result, so you can retrieve individual rasters either with numerical indexing (rasters[1]), or named indexing (rasters['sand']).

jdobres
  • 11,339
  • 1
  • 17
  • 37
  • thank you! this was helpful, although I didn't up using this variation because I've noticed that `raster` will be out-dated and moved onto another package. – Cameron So Nov 28 '22 at 08:16
1

In general, you can make a function that creates an object, call it in a for-loop and use assign to assign them variable names. I recommend using glue function from the "glue" package as it is more intuitive than paste. I was not able to get the raster function to work on my computer as I'm getting this error. However, that's the idea.

library(glue)

raster_object <- function(url, voi, depth, quantile){
  return(raster(glue("{url}/{voi}/{voi}_{depth}_{quantile}.vrt")))
}

for (voi in c("sand", "clay")){
  assign(glue("raster_{voi}"),  raster_object(url = "https://files.isric.org/soilgrids/latest/data", voi, depth = "5-15cm", quantile = "mean"))
}

Bei
  • 122
  • 6
  • Generally speaking, one should not use `assign` to generate a large number of independent objects. It's usually better to take advantage of R data structures like vectors, lists, or data frames. See here: https://stackoverflow.com/questions/17559390/why-is-using-assign-bad Also, `glue` is part of the tidyverse, which OP does not seem to be using. – jdobres Nov 27 '22 at 04:15
  • That's a good point. The ```assign``` function will add the created objects to your environment. So be mindful when using it. – Bei Nov 27 '22 at 04:28
  • 1
    In this case, the mindful thing is to not use assign. Use a list instead. – Robert Hijmans Nov 28 '22 at 02:39
1

Another variation

url = "https://files.isric.org/soilgrids/latest/data/"
voi = c("sand", "clay", "silt", "phh2o", "soc")
depth = "5-15cm"
quantile = "mean"

f <- paste0(url, voi, "/", paste(voi, depth, quantile, sep="_"), '.vrt')

"raster" is outdated, let's use "terra" instead and make a SpatRasterDataset

library(terra)
x <- sds(f)

Or a list of SpatRasters

y <- lapply(f, rast)

Also see geodata::soil_world and geodata::soil_world_vsi for access to these data.

Robert Hijmans
  • 40,301
  • 4
  • 55
  • 63
  • Thank you Robert. Will `geodata` provide functiosn for download SoilGrid data for a particular bounding box, or coordinate? The current tutorials provided on SoilGrid's website are outdated. – Cameron So Nov 28 '22 at 04:00