#' Combine values/categories of multiple GRasters into a single GRaster
#'
#' @description This function takes from 2 to 10 integer or categorical (factor) `GRaster`s and creates a single `GRaster` that has one value per combination of values in the inputs. For example, say that there were two input rasters, with values 1 and 2 in the one raster, and 3 and 4 in the other. If the following combinations of values occurred between the two rasters, then the output raster would be re-coded with the new values:
#'
#' | `input_raster1` | `input_raster2` | `output_raster` |
#' | --------------- | --------------- | --------------- |
#' | 1 | 3 | 0 |
#' | 1 | 4 | 1 |
#' | 2 | 3 | 2 |
#' | 2 | 4 | 3 |
#'
#' If the argument `na.rm` is set to `TRUE` (which it is, by default), then whenever at least one cell has an `NA` value, then the output will also have an `NA` (i.e., a new category number is not created). However, if `na.rm` is `FALSE`, then combinations that include an `NA` are assigned a new category number, unless all values are `NA` (in which case the output will be `NA`).
#'
#' The difference between this function and [combineLevels()] is that this one creates a "combined" `GRaster` with a combined levels table, whereas `combineLevels()` just merges the levels tables.
#'
#' If the inputs are all categorical rasters, then a [levels()] table will also be returned with the new levels.
#'
#' @param x A `GRaster` with one or more layers, each of which must be have cells that represent integers or categories (factors).
#'
#' @param ... Either missing or integer/categorical (factor) `GRaster`s.
#'
#' @param na.rm Logical: If `TRUE` (default), then any combinations that include an `NA` cell will result in an `NA` cell in the output.
#'
#' @returns A `GRaster`. If the inputs are all categorical (factor) rasters, then a levels table will also be returned with the new combined levels.
#'
#' @example man/examples/ex_GRaster_categorical.r
#'
#' @seealso [combineLevels()], [terra::concats()], `vignette("GRasters", package = "fasterRaster")`, **GRASS** manual page for module `r.cross` (see `grassHelp("r.cross")`)
#'
#' @aliases concats
#' @rdname concats
#' @exportMethod concats
methods::setMethod(
f = "concats",
signature = c(x = "GRaster"),
function(x, ..., na.rm = TRUE) {
.locationRestore(x)
.region(x)
dt <- datatype(x)
if (any(!(dt %in% c("integer", "factor")))) stop("All rasters must be of type integer or factor.")
allFactors <- all(is.factor(x))
inSrcs <- sources(x)
dots <- list(...)
if (length(dots) > 0L) {
dots <- omnibus::unlistRecursive(dots)
xFirst <- x[[1L]]
for (i in seq_along(dots)) {
compareGeom(xFirst, dots[[i]])
dt <- datatype(dots[[i]])
if (any(!(dt %in% c("integer", "factor")))) stop("All rasters must be of type integer or factor.")
allFactors <- allFactors & all(is.factor(dots[[i]]))
inSrcs <- c(inSrcs, sources(dots[[i]]))
}
nLayersDots <- sapply(dots, nlyr)
} else {
nLayersDots <- 0L
}
nl <- nlyr(x) + nLayersDots
if (nl < 2L | nl > 10L) stop("From 2 to 10 raster layers can be combined. Too few/many layers.")
# combine
src <- .makeSourceName("r_cross", "raster")
args <- list(
cmd = "r.cross",
input = inSrcs,
output = src,
flags = c(.quiet(), "overwrite")
)
if (na.rm) args$flags <- c(args$flags, "z")
do.call(rgrass::execGRASS, args = args)
# collate levels tables
if (allFactors) {
# get levels tables from rasters
levs <- levels(x)
if (length(dots) > 0L) {
for (i in seq_along(dots)) levs <- c(levs, levels(dots[[i]]))
}
# get levels table from output
glevs <- rgrass::execGRASS(
cmd = "r.category",
map = src,
separator = "pipe",
intern = TRUE
)
glevs <- sapply(glevs, strsplit, split = "\\|")
n <- length(glevs)
# populate new levels table
table <- data.table::data.table(value = rep(NA_integer_, n), category = rep(NA_character_, n))
for (i in seq_along(glevs)) {
val <- as.integer(glevs[[i]][1L])
table$value[i] <- val
gcats <- strsplit(glevs[[i]][2L], split = "; ")[[1L]]
gcats <- gsub(gcats, pattern = "category ", replacement = "")
gcats[gcats == "NULL"] <- NA_integer_
gcats <- as.integer(gcats)
comboCat <- rep(NA_character_, nl)
for (j in seq_along(levs)) {
if (is.na(gcats[j])) {
comboCat[j] <- "NA"
} else {
comboCat[j] <- unlist(levs[[j]][levs[[j]][[1L]] == gcats[j], 2L])
}
}
comboCat <- paste(comboCat, collapse = " | ")
table$category[i] <- comboCat
}
} else {
table <- NULL
}
.makeGRaster(src, names = "combineCats", levels = table)
} # EOF
)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.