#' Add rows and columns around a writeRaster
#'
#' @description `extend()` adds cells around a raster, making it larger.
#'
#' @param x A `GRaster`.
#'
#' @param y Any of:
#' * An object from which an extent can be obtained; i.e., a `SpatRaster`, `SpatVector`, `SpatExtent`, `sf` vector, or a `GSpatial` object (any of `GRaster`, `GVector`, or `GRegion`). If the extent of `x` is "outside" the extent of `y` on any side, the side(s) of `x` that are outside will be kept as-is (i.e., the extent of `x` will never be shrunk).
#' * A single positive integer: Number of rows and columns to add to the top, bottom, and sides of the raster.
#' * Two integers >= 0: Number of columns (1st value) to add to the sides, and number of rows (2nd value) to add to the top and bottom of the raster.
#' * Four integers >= 0: Number of rows and columns to add (left column, right column, bottom row, top row).
#'
#' @param snap Character: Method used to align `y` to `x`. Partial matching is used. This is only used if `y` is not a set of numbers.
#' * `"near"` (default): Round to nearest row/column
#' * `"in"`: Round "inward" toward the extent of `x` to nearest row/column
#' * `"out"`: Round "outward" away from the extent of `x` to the nearest row/column.
#'
#' @param fill Numeric: Value to place in the new cells. The default is `NA`.
#'
#' @returns A `GRaster`.
#'
#' @details Known issues: When `GRaster`s are saved to disk explicitly using [writeRaster()], or implicitly using [rast()] or [plot()], rows and columns that are entirely `NA` are dropped.
#'
#' @seealso [terra::extend()]
#'
#' @example man/examples/ex_extend.r
#'
#' @aliases extend
#' @rdname extend
#' @exportMethod extend
methods::setMethod(
f = "extend",
signature = c(x = "GRaster", y = "numeric"),
function(x, y, fill = NA) .extend(x = x, y = y, snap = NA, fill = fill)
)
#' @aliases extend
#' @rdname extend
#' @exportMethod extend
methods::setMethod(
f = "extend",
signature = c(x = "GRaster", y = "SpatRaster"),
function(x, y, snap = "near", fill = NA) .extend(x = x, y = y, snap = snap, fill = fill)
)
#' @aliases extend
#' @rdname extend
#' @exportMethod extend
methods::setMethod(
f = "extend",
signature = c(x = "GRaster", y = "SpatVector"),
function(x, y, snap = "near", fill = NA) .extend(x = x, y = y, snap = snap, fill = fill)
)
#' @aliases extend
#' @rdname extend
#' @exportMethod extend
methods::setMethod(
f = "extend",
signature = c(x = "GRaster", y = "SpatExtent"),
function(x, y, snap = "near", fill = NA) .extend(x = x, y = y, snap = snap, fill = fill)
)
#' @aliases extend
#' @rdname extend
#' @exportMethod extend
methods::setMethod(
f = "extend",
signature = c(x = "GRaster", y = "sf"),
function(x, y, snap = "near", fill = NA) .extend(x = x, y = y, snap = snap, fill = fill)
)
#' @aliases extend
#' @rdname extend
#' @exportMethod extend
methods::setMethod(
f = "extend",
signature = c(x = "GRaster", y = "GSpatial"),
function(x, y, snap = "near", fill = NA) .extend(x = x, y = y, snap = snap, fill = fill)
)
#' @noRd
.extend <- function(x, y, snap, fill) {
# resolution of x
ewres <- xres(x)
nsres <- yres(x)
### find coordinates of new extent
if (inherits(y, "numeric")) {
if (!(length(y) %in% c(1L, 2L, 4L))) {
stop("Argument `y` is invalid.")
} else if (length(y) == 1L) {
y <- rep(y, 4L)
} else if (length(y) == 2L) {
y <- rep(y, each = 2L)
}
if (any(!omnibus::is.wholeNumber(y))) stop("Values of `y` must be numeric integers.")
if (any(y < 0L)) y[y < 0L] <- 0L
} else {
if (inherits(y, c("SpatRaster", "SpatVector", "SpatExtent"))) {
extent <- terra::ext(y)
extent <- as.vector(extent)
} else if (inherits(y, "GSpatial")) {
extent <- ext(y, vector = TRUE)
} else {
stop("Argument `y` is invalid.")
}
# by how many rows and columns do we grow?
wgrow <- max(0, W(x) - extent[1L]) / ewres
egrow <- max(0, extent[2L] - E(x)) / ewres
sgrow <- max(0, S(x) - extent[3L]) / nsres
ngrow <- max(0, extent[4L] - N(x)) / nsres
snap <- omnibus::pmatchSafe(snap, c("near", "in", "out"))
fx <- if (snap == "near") {
round
} else if (snap == "in") {
floor
} else if (snap == "out") {
ceiling
}
grows <- c(wgrow, egrow, sgrow, ngrow)
y <- do.call(fx, list(grows))
} # if y is an object with an extent
# new extent coordinates
w <- W(x) - y[1L] * ewres
e <- E(x) + y[2L] * ewres
s <- S(x) - y[3L] * nsres
n <- N(x) + y[4L] * nsres
w <- as.character(w)
e <- as.character(e)
s <- as.character(s)
n <- as.character(n)
ewres <- as.character(ewres)
nsres <- as.character(nsres)
# reset region
rgrass::execGRASS(
cmd = "g.region",
w = w, e = e, s = s, n = n,
ewres = ewres, nsres = nsres,
flags = c(.quiet(), "overwrite")
)
reg <- .region()
nc <- ncol(reg)
nr <- nrow(reg)
### extent
nLayers <- nlyr(x)
srcs <- rep(NA_character_, nLayers)
for (i in seq_len(nLayers)) {
if (is.na(fill)) {
src <- .copyGSpatial(x = x[[i]], reshapeRegion = FALSE)
} else {
src <- .makeSourceName("extend", "raster", nLayers)
ex <- paste0(src, " = if(col() <= ", y[1L], ", ", fill, ", if(col() > ncols() - ", y[2L], ", ", fill, ", if(row() <= ", y[4L], ", ", fill, ", if(row() > nrows() - ", y[3], ", ", fill, ", ", sources(x)[i], "))))")
args <- list(
cmd = "r.mapcalc",
expression = ex,
region = "current",
flags = c(.quiet(), "overwrite")
)
do.call(rgrass::execGRASS, args = args)
}
srcs[i] <- src
} # next raster
.makeGRaster(srcs, names = names(x), levels = levels(x), ac = activeCats(x))
} # EOF
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.