Search code examples
rfor-looprasterr-rasterterra

How to iterate crop raster in R using a moving window?


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)

Solution

  • 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 can probably use terra::makeTiles but if that does not apply 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

    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)
        })