#' Determine if GRasters and/or GVectors are geographically comparable
#'
#' `compareGeom()` compares geographic metadata between two or more `GRaster`s and/or `GVector`s. In many cases, spatial objects must be comparable for them to "interact" (e.g., conducting arithmetic operations, masking, etc.).
#'
#' @param x,y,... `GRaster`s or `GVector`s. If `y` is `GRaster`, then the `...` must also be `GRaster`s (or missing). If `y` is `GVector`, then the `...` must also be `GVector`s (or missing).
#'
#' @param location,mapset Logical: Compare **GRASS** "project/location" and "mapsets" (see `vignette("projects_mapsets", package = "fasterRaster")`). Default is `TRUE`.
#'
#' @param crs Logical: Compare coordinate reference systems. Default is `TRUE`.
#'
#' @param lyrs Logical (rasters only): Compare number of layers of "stacked" rasters. Note this is different from number of vertical "depths" of a raster. Default is `FALSE`.
#'
#' @param topo Logical: Test for same topology (2D or 3D). By default, this is `TRUE` for raster-raster comparisons, and `FALSE` for all others.
#'
#' @param ext Logical: If `TRUE`, test for same extent. By default, is `TRUE` for raster-raster comparison and `FALSE` for all others.
#'
#' @param zext Logical: Test for same vertical extents (3D only). By default, this is `TRUE` for raster-raster comparisons, and `FALSE` for all others.
#'
#' @param rowcol Logical (rasters only): Test for same number of rows and columns. Default is `TRUE`.
#'
#' @param depths Logical (rasters only): Test for same number of depths. Default is `TRUE`.
#'
#' @param res Logical (rasters only): Test for same resolution in x- and y-dimensions. Default is `TRUE`.
#'
#' @param zres Logical (rasters only): Test for same resolution in z dimension. Default is `TRUE`.
#'
#' @param geometry Logical (vector-vector comparison only): Compare geometry. Default is `FALSE`.
#'
#' @param stopOnError Logical: If `TRUE` (default), throw an error with an explanation if the objects are not comparable. If `FALSE` (default), return `TRUE` or `FALSE`.
#'
#' @param messages Logical: If `TRUE (default), display a warning if a condition is not met. This only comes into effect if `stopOnError` is `FALSE`.
#'
#' @returns Logical (invisibly): `TRUE` for no mismatches detected, `FALSE` for incompatibility), or side effect of throwing an error.
#'
#' @aliases compareGeom
#' @rdname compareGeom
#' @exportMethod compareGeom
methods::setMethod(
"compareGeom",
signature = c(x = "GRaster", y = "GRaster"),
definition = function(
x,
y,
...,
location = TRUE,
mapset = TRUE,
topo = TRUE,
lyrs = FALSE,
crs = TRUE,
ext = TRUE,
zext = TRUE,
rowcol = TRUE,
depths = TRUE,
res = TRUE,
zres = TRUE,
stopOnError = TRUE,
messages = TRUE
) {
out <- TRUE
y <- list(y, ...)
for (i in seq_along(y)) {
if (location) out <- .locationCompare(out=out, x=x, y=y[[i]], stopOnError = stopOnError, messages = messages)
if (mapset) out <- .mapsetCompare(out = out, x=x, y=y[[i]], stopOnError = stopOnError, messages = messages)
if (crs) out <- .crsCompare(out = out, x = x, y = y[[i]], stopOnError = stopOnError, messages = messages)
if (lyrs) {
if (nlyr(x) != nlyr(y[[i]])) {
msg <- "The rasters have a different number of layers."
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
}
if (topo) out <- .topoCompare(out=out, x=x, y=y[[i]], stopOnError=stopOnError, messages=messages)
if (ext) out <- .extentCompare(out=out, x=x, y=y[[i]], stopOnError=stopOnError, messages=messages)
if (zext) out <- .zextentCompare(out=out, x=x, y=y[[i]], stopOnError=stopOnError, messages=messages)
if (rowcol) {
xdim <- dim(x)[1L:2L]
ydim <- dim(y[[i]])[1L:2L]
if (!all(xdim == ydim)) {
msg <- "The rasters have different numbers of rows and/or columns."
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
}
if (depths) {
msg <- "The rasters have a different numbers of depths."
xd <- ndepth(x)
yd <- ndepth(y[[i]])
xna <- is.na(xd)
yna <- is.na(yd)
if (any(xna) | any(yna)) {
if (!all(xna == yna)) {
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
}
if (any(!xna) | any(!yna)) {
if (!all(xd[!xna] == yd[!yna])) {
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
}
}
if (res) {
xres <- res(x)[1L:2L]
yres <- res(y[[i]])[1L:2L]
if (!all(omnibus::compareFloat(xres, yres, "=="))) {
msg <- "The rasters have a different horizontal resolutions."
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
}
if (zres) {
xzres <- zres(x)
yzres <- zres(y[[i]])
if (!is.na(xzres) & !is.na(yzres)) {
msg <- "The rasters have a different vertical resolutions."
if (
(is.na(xzres) & !is.na(yzres)) |
(!is.na(xzres) & is.na(yzres))
) {
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
} else if (omnibus::compareFloat(xzres, yzres, "!=")) {
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
}
}
} # next object to compare to
invisible(out)
} # EOF
)
#' @aliases compareGeom
#' @rdname compareGeom
#' @exportMethod compareGeom
methods::setMethod(f = "compareGeom",
signature = c(x = "GVector", y = "GVector"),
definition = function(
x,
y,
...,
location = TRUE,
mapset = TRUE,
topo = FALSE,
crs = TRUE,
ext = FALSE,
zext = FALSE,
geometry = FALSE,
stopOnError = TRUE,
messages = TRUE
) {
.compareGeneric(
x = x,
y = y,
...,
location = location,
mapset = mapset,
crs = crs,
topo = topo,
ext = ext,
zext = zext,
geometry = geometry,
stopOnError = stopOnError,
messages = messages
)
} # EOF
)
#' @aliases compareGeom
#' @rdname compareGeom
#' @exportMethod compareGeom
methods::setMethod(f = "compareGeom",
signature = c(x = "GRaster", y = "GVector"),
definition = function(
x,
y,
...,
location = TRUE,
mapset = TRUE,
topo = FALSE,
crs = TRUE,
ext = FALSE,
zext = FALSE,
stopOnError = TRUE,
messages = TRUE
) {
.compareGeneric(
x = x,
y = y,
...,
location = location,
mapset = mapset,
crs = crs,
topo = topo,
ext = ext,
zext = zext,
geometry = FALSE,
stopOnError = stopOnError,
messages = messages
)
} # EOF
)
#' @aliases compareGeom
#' @rdname compareGeom
#' @exportMethod compareGeom
methods::setMethod(f = "compareGeom",
signature = c(x = "GVector", y = "GRaster"),
definition = function(
x,
y,
...,
location = TRUE,
mapset = TRUE,
topo = FALSE,
crs = TRUE,
ext = FALSE,
zext = FALSE,
stopOnError = TRUE,
messages = TRUE
) {
.compareGeneric(
x = x,
y = y,
...,
location = location,
mapset = mapset,
crs = crs,
topo = topo,
ext = ext,
zext = zext,
geometry = FALSE,
stopOnError = stopOnError,
messages = messages
)
} # EOF
)
# x is a GRaster and y and ... GVectors OR
# x is a GVector and y and ... GRasters
.compareGeneric <- function(
x,
y,
...,
location,
mapset,
crs,
topo,
ext,
zext,
geometry,
stopOnError,
messages
) {
out <- TRUE
y <- list(y, ...)
allGVector <- sapply(y, inherits, "GVector")
allGRaster <- sapply(y, inherits, "GRaster")
allGVector <- all(allGVector)
allGRaster <- all(allGRaster)
allSameClass <- allGRaster | allGVector
if (!allSameClass) stop("The objects supplied in ... must be of the same class as the y object.")
for (i in seq_along(y)) {
if (location) out <- .locationCompare(out=out, x=x, y=y[[i]], stopOnError=stopOnError, messages=messages)
if (mapset) out <- .mapsetCompare(out=out, x=x, y=y[[i]], stopOnError=stopOnError, messages=messages)
if (crs) out <- .crsCompare(out=out, x=x, y=y[[i]], stopOnError=stopOnError, messages=messages)
if (topo) out <- .topoCompare(out=out, x=x, y=y[[i]], stopOnError=stopOnError, messages=messages)
if (ext) out <- .extentCompare(out=out, x=x, y=y[[i]], stopOnError=stopOnError, messages=messages)
if (zext) out <- .zextentCompare(out=out, x=x, y=y[[i]], stopOnError=stopOnError, messages=messages)
if (geometry) {
if (geomtype(x) != geomtype(y[[i]])) {
msg <- "The vectors have a different geometries."
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
}
} # next object
out
}
### compare locations
.locationCompare <- function(out, x, y, stopOnError, messages) {
if (.location(x) != .location(y)) {
msg <- paste0("The objects have different coordinate reference systems. You can use\n project() to warp one of them to the reference system of the other.")
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
out
}
### compare mapsets
.mapsetCompare <- function(out, x, y, stopOnError, messages) {
if (.mapset(x) != .mapset(y)) {
msg <- "The objects are in different GRASS mapsets."
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
out
}
### compare CRS
.crsCompare <- function(out, x, y, stopOnError, messages) {
if (st_crs(x) != st_crs(y)) {
msg <- "The objects have different coordinate reference systems."
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
out
}
### compare extents
.extentCompare <- function(out, x, y, stopOnError, messages) {
xx <- ext(x, vector=TRUE)
yy <- ext(y, vector=TRUE)
if (any(omnibus::compareFloat(xx, yy, "!="))) {
msg <- "The objects have different extents."
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
out
}
### compare z-extents
.zextentCompare <- function(out, x, y, stopOnError, messages) {
if (topology(x) == "3D" & topology(y) == "3D") {
xzext <- zext(x)
xbottom <- xzext[1L]
xtop <- xzext[2L]
yzext <- zext(y)
ybottom <- yzext[1L]
ytop <- yzext[2L]
if (anyNA(c(xzext, yzext))) {
msg <- "At least one raster has a missing z-ext."
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
if (omnibus::compareFloat(xbottom, ybottom, "!=")) {
msg <- "Objects have different vertical extents."
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
if (omnibus::compareFloat(xtop, ytop, "!=")) {
stop("Objects have different vertical extents.")
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
}
out
} # EOF
### compare topology
.topoCompare <- function(out, x, y, stopOnError, messages) {
if (topology(x) != topology(y)) {
msg <- "The rasters have different topologies."
if (stopOnError) stop(msg)
if (messages & !stopOnError) warning(msg)
out <- FALSE
}
out
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.