2

I want to crop a raster using a bbox or a known extent, i.e., 10 pixels in row and col.

Below you can see a reproducible example:

library(terra)
r <- terra::set.ext(rast(volcano), terra::ext(0, nrow(volcano), 0, ncol(volcano)))
plot(r)

xmin <- xmin(r)
ymax <- ymax(r)
rs <- res(r)

n <- 10 # i.e. of rows and cols size
xmax <- xmin + n * rs[1] 
ymin <- ymax - n * rs[2]

x <- crop(r, c(xmin, xmax, ymin, ymax))
plot(x)

plot x

The intended loop is to:

  • To go through all the raster (r) length cropping and save each raster piece temporarily into data.frame (or data.table, raster, spatraster, list)

2 Answers2

3

It would be useful to have more context about why you would do this. It is also not clear from your question if you want the cropped areas to overlap; I assume you do not want that.

You could so something like:

library(terra)
r <- rast(volcano)
n <- 10

Get the starting cells of interest

rows <- seq(1, nrow(r), by=n)
cols <- seq(1, ncol(r), by=n)    
cells <- cellFromRowColCombine(r, rows, cols)

Get the coordinates

# upper-left coordinates of the starting cells 
xy <- xyFromCell(r, cells)
rs <- res(r)
xy[,1] <- xy[,1] - rs[1]/2
xy[,2] <- xy[,2] + rs[2]/2

# add the lower-right coordinates of the end cell
xy <- cbind(xy[,1], xy[,1] + n*rs[1], xy[,2] - n*rs[2], xy[,2])

And loop

x <- lapply(1:nrow(xy), function(i) {
         crop(r, xy[i,])
       })
    

Verify

e <- lapply(x, \(i) ext(i) |> as.polygons()) |> vect()
plot(r)
lines(e, col="blue", lwd=2)

enter image description here

sapply(x, dim) |> t() |> head()
#     [,1] [,2] [,3]
#[1,]   10   10    1
#[2,]   10   10    1
#[3,]   10   10    1
#[4,]   10   10    1
#[5,]   10   10    1
#[6,]   10   10    1

Or use an alternative approach based on the start- and end-cell numbers (for this to work you need terra 1.5-25, currently the development version that you can install with install.packages('terra', repos='https://rspatial.r-universe.dev'))

srows <- seq(1, nrow(r), by=n)
scols <- seq(1, ncol(r), by=n)
erows <- pmin(nrow(r), srows+n-1)
ecols <- pmin(ncol(r), scols+n-1)
scell <- cellFromRowColCombine(r, srows, scols)
ecell <- cellFromRowColCombine(r, erows, ecols)
cells <- cbind(scell, ecell)

x <- lapply(1:nrow(cells), function(i) {
        e <- ext(r, cells[i,])
        crop(r, e)
    })
    
Robert Hijmans
  • 40,301
  • 4
  • 55
  • 63
  • Hey @Robert Hijmans. Thanks again for answering another post. Here, at the alternative answer above I'm receiving this errors below: `x <- lapply(1:nrow(cells), function(i) { e <- ext(r, cells[i,]) crop(r, e) })` **Error in .local(x, ...) : unused argument (c(1, 559))** – Hugo Machado Rodrigues Apr 03 '22 at 16:07
  • 1
    Ah, you need the dev version of terra for that, I have updated my post. – Robert Hijmans Apr 03 '22 at 16:31
2

Using terra data, by your approach, which I like:

library(terra)
r <- terra::set.ext(rast(volcano), terra::ext(0, nrow(volcano), 0, ncol(volcano)))

Then we need extents that have 4 values (xmin,xmax,ymin,ymax), [for indices {i,j,k,l}} we then want to traverse the raster from top left to top right, then again stepping down, and down till we get to bottom right [for indices {o,p}], then a further crop [for index {q}]. This is a pretty deep nesting for for loops, and in the interest of preserving mental...it might be better to make some helpers.

x_mtx <- matrix(c(seq(0,70,10), seq(10,80,10)), ncol = 2)
y_mtx <- matrix(c(seq(51, 1, -10), seq(61, 11, -10)), ncol = 2)

these eliminate indices i:l, then we make our extents:

crop_exts <- vector(mode = 'list', length= 48L)
for(p in 1:6){
  for(o in 1:8){
    crop_exts[[p]][[o]] <- ext(x_mtx[o, 1], x_mtx[o, 2], y_mtx[p, 1], y_mtx[p, 2])
  }
}
# check our work
crop_exts[[1]][[1]] |> as.vector()
#xmin xmax ymin ymax 
#0   10   51   61 
crop_exts[[6]][[8]] |> as.vector()
#xmin xmax ymin ymax 
#  70   80    1   11 

Looks like top left to bottom right. And noting the [[1]][[1]], [[6]][[8]], we have a second nested {p, o} loop as above in view of our indexing here to pull out the crops:

cropped_r <- vector(mode='list', length = 48L)# edit, establish outside `for` 
for(p in 1:6){
 for(o in 1:8){
   cropped_r[[p]][[o]] <- crop(r, crop_exts[[p]][[o]])
 }
}
plot(cropped_r[[1]][[1]])
plot(cropped_r[[6]][[8]])

Possibly easier, overall, is to generate the complete extent matrix, dim(48,4) in this case, but couldn't wrap my head around it, and keep in mind indices, and preserve my mental...

Chris
  • 1,647
  • 1
  • 18
  • 25
  • Hey @Chris, the loop is returning an error, see below: `> for(p in 1:6){ for(o in 1:8){ cropped_r[[p]][[o]] <- crop(r, crop_exts[[p]][[o]]) } }` **Error: object 'cropped_r' not found** – Hugo Machado Rodrigues Apr 03 '22 at 16:01
  • 1
    Yup, missed setting up a list to receive the `for` loop operation, probably in cut and paste to here. Also Note, my approach takes no notice of raster resolution, while @robert-hijmans presents where it is central, and shoul find it's way into your workflow. My contribution, such as it is, is to address doing this in a `for`, while, as seen above, all the tools are in `terra` to accomplish better. – Chris Apr 03 '22 at 16:50