#' Find which cells of a GRaster match certain values
#'
#' @description The `match()` function takes a `GRaster` and a numeric, integer or character vector as inputs and returns a `GRaster` with cell values that correspond to the index of each element in the vector that matched the original cell value. For example, if a 4-cell raster had values 3, `NA`, 5, 4, and the vector was `c(3, 4)`, then the output would be a 4-cell raster with values 1, `NA`, `NA`, 2 because the first value in the vector was 3 (so the cell with 3 is assigned 1), and because the second value in the vector was 4 (so the cell with 4 was assigned 2). The other two values had no matches.
#'
#' If the `GRaster` is categorical, then the vector can be category labels instead of numeric values.
#'
#' The `%in%` operator returns a `GRaster` with cell values that are 1 if their original values appeared in the vector, and 0 if not (or `NA` if the original value was `NA`). If the `GRaster` is categorical, then the vector can be category labels instead of numeric values.
#'
#' The `%notin%` operator returns 1 for cells with values that are *not* found in the vector, and 0 otherwise. If the `GRaster` is categorical, then the vector can be category labels instead of numeric values.
#'
#' @param x A `GRaster`: Note that any kind of `GRaster` is acceptable (integer, float, double, or categorical), but matching may not work as intended for float and double rasters because of problems with comparing floating-point values.
#'
#' @param table A numeric, integer, or character vector.
#'
#' @param nomatch Numeric or integer: Value to return when no match is found.
#'
#' @returns A `GRaster`.
#'
#' @seealso [terra::match()], [match()], [omnibus::notIn()]
#'
#' @example man/examples/ex_match.r
#'
#' @aliases match
#' @rdname match
#' @exportMethod match
methods::setMethod(
f = "match",
signature = c(x = "GRaster"),
function(x, table, nomatch = NA) {
if (is.character(table) & !all(is.factor(x))) {
stop("Can only match category labels to categorical rasters.")
}
.locationRestore(x)
.region(x)
nLayers <- nlyr(x)
srcs <- .makeSourceName("r_mapcalc", "raster", nLayers)
for (i in seq_len(nLayers)) {
if (is.character(table)) {
levs <- levels(x)[[i]]
ac <- activeCat(x)[i]
labels <- levs[[ac + 1L]]
matches <- match(table, labels)
vals <- levs[[1L]][matches]
} else {
vals <- table
}
ex <- paste0(srcs[i], " = if(isnull(", sources(x)[i],"), null(), if(", sources(x)[i], "==", vals[1L], ",1,")
if (length(vals) > 1L) {
for (count in 2L:length(vals)) {
ex <- paste0(ex, "if(", sources(x)[i], "==", vals[count], ",", count, ",")
}
ex <- if (is.na(nomatch)) {
paste0(ex, "null())")
} else {
paste0(ex, nomatch, ")")
}
for (count in 2L:length(vals)) {
ex <- paste0(ex, ")")
}
}
ex <- paste0(ex, ")")
rgrass::execGRASS(
cmd = "r.mapcalc",
expression = ex,
flags = c(.quiet(), "overwrite")
)
} # next raster
# if (!is.na(nomatch)) {
# srcsIn <- srcs
# srcs <- .makeSourceName("r_mapcalc", "raster", nLayers)
# for (i in seq_len(nLayers)) {
# ex <- paste0(srcs[i], " = if(isnull(", sources(x)[i], "), null(), ", srcsIn[i], ")")
# rgrass::execGRASS(
# cmd = "r.mapcalc",
# expression = ex,
# flags = c(.quiet(), "overwrite")
# )
# }
# }
# add category levels
if (is.character(table)) {
levs <- .freq(srcs, dtype = rep("CELL", length(srcs)))
if (!inherits(levs, "list")) levs <- list(levs)
acs <- activeCat(x, names = TRUE)
for (i in seq_along(acs)) {
if (nrow(levs[[i]]) == 0L) {
levs[[i]] <- ""
} else {
levs[[i]] <- levs[[i]][ , "count" := NULL]
values <- levs[[i]][["value"]]
labels <- table[match(values, seq_along(table))]
labels[is.na(labels)] <- "nomatch"
levs[[i]][ , acs[i] := labels]
levs[[i]][ , "value" := lapply(.SD, as.integer), .SDcols = "value"]
data.table::setorderv(levs[[i]], col = "value")
}
}
} else {
levs <- NULL
}
.makeGRaster(srcs, names(x), levels = levs)
} # EOF
)
#' @aliases %in%
#' @rdname match
#' @exportMethod %in%
methods::setMethod(
f = "%in%",
signature = c(x = "GRaster"),
function(x, table) {
.locationRestore(x)
.region(x)
if (is.character(table) & !all(is.factor(x))) {
stop("Can only match category labels to categorical rasters.")
}
nLayers <- nlyr(x)
srcs <- .makeSourceName("r_mapcalc", "raster", nLayers)
for (i in seq_len(nLayers)) {
if (is.character(table)) {
levs <- levels(x)[[i]]
ac <- activeCat(x)[i]
labels <- levs[[ac + 1L]]
matches <- match(table, labels)
vals <- levs[[1L]][matches]
} else {
vals <- table
}
if (is.na(vals[1L])) {
ex <- paste0(srcs[i], " = 0 * ", sources(x))
} else {
ex <- paste0(srcs[i], " = if(isnull(", sources(x)[i], "), null(), if(", sources(x)[i], "==", vals[1L])
if (length(vals) > 1L) {
for (count in 2L:length(vals)) {
ex <- paste0(ex, "|", sources(x)[i], "==", vals[count])
}
}
ex <- paste0(ex, ",1,0))")
}
rgrass::execGRASS(
cmd = "r.mapcalc",
expression = ex,
flags = c(.quiet(), "overwrite")
)
} # next raster
.makeGRaster(srcs, names(x))
} # EOF
)
#' @aliases %notin%
#' @rdname match
#' @exportMethod %notin%
methods::setMethod(
f = "%notin%",
signature = c(x = "GRaster"),
function(x, table) {
.locationRestore(x)
.region(x)
if (is.character(table) & !all(is.factor(x))) {
stop("Can only match category labels to categorical rasters.")
}
nLayers <- nlyr(x)
srcs <- .makeSourceName("r_mapcalc", "raster", nLayers)
for (i in seq_len(nLayers)) {
if (is.character(table)) {
levs <- levels(x)[[i]]
ac <- activeCat(x)[i]
labels <- levs[[ac + 1L]]
matches <- match(table, labels)
vals <- levs[[1L]][matches]
} else {
vals <- table
}
ex <- paste0(srcs[i], " = if(isnull(", sources(x)[i], "), null(), if(", sources(x)[i], "!=", vals[1L])
if (length(vals) > 1L) {
for (count in 2L:length(vals)) {
ex <- paste0(ex, "&", sources(x)[i], "!=", vals[count])
}
}
ex <- paste0(ex, ",1,0))")
rgrass::execGRASS(
cmd = "r.mapcalc",
expression = ex,
flags = c(.quiet(), "overwrite")
)
} # next raster
.makeGRaster(srcs, names(x))
} # EOF
)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.