diff --git a/BNRTools.Rproj b/BNRTools.Rproj index 69fafd4..0b025a9 100644 --- a/BNRTools.Rproj +++ b/BNRTools.Rproj @@ -1,4 +1,5 @@ Version: 1.0 +ProjectId: 3ed6f0ab-539e-414c-ac67-f900618cf932 RestoreWorkspace: No SaveWorkspace: No diff --git a/NAMESPACE b/NAMESPACE index 4254263..1d53a6a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -5,6 +5,7 @@ export(conv_downscalr2ibis) export(misc_objectSize) export(misc_sanitizeNames) export(spl_exportNetCDF) +export(spl_growGrid) export(spl_replaceGriddedNA) export(spl_resampleRas) importFrom(utils,object.size) diff --git a/R/spl_growGrid.R b/R/spl_growGrid.R new file mode 100644 index 0000000..9e8a486 --- /dev/null +++ b/R/spl_growGrid.R @@ -0,0 +1,78 @@ +#' Grow a categorical SpatRaster by certain amount of pixels or distance. +#' +#' @description +#' A common issue in aggregating categorical rasters is that coastal and boundary +#' gridcells tend to decrease owing to aggregation effects. This can cause issues +#' of non-matching grid cells later on. +#' This simple function takes a categorical [`SpatRaster`] object and grows it +#' into no-data areas (those with \code{NA} values) within an optionally provided +#' distance. +#' +#' @note +#' Only \code{NA} grid cells will be filled! +#' +#' @param x A categorical [`SpatRaster`] to be grown. +#' @param iter A [`numeric`] values of the maximum number of iterations from the grid +#' cell border of which \code{x} should be grown (Default: \code{2}). +#' +#' @returns [`SpatRaster`] +#' +#' @keywords spatial +#' +#' @seealso +#' \code{\link[terra]{buffer}}, +#' \code{\link[terra]{focal}} +#' +#' @examples +#' set.seed(42) +#' ras <- terra::rast(ncol = 100, nrow = 100, xmin = 0, xmax = 100, +#' ymin = 0, ymax = 100, resolution = 10, crs = NA) +#' +#' # Fill with dummy values but keep half as NA +#' terra::values(ras) <- c(rep(NA,50),rbinom(terra::ncell(ras)/2, 10, 0.5)) +#' +#' # Convert to factor +#' ras <- terra::as.factor(ras) +#' assertthat::assert_that(terra::is.factor(ras)) +#' +#' ras_nona <- spl_growGrid(x = ras, iter = 10) +#' terra::plot(ras_nona) +#' +#' @export +spl_growGrid <- function(x, iter = 2) { + + assertthat::assert_that( + inherits(x, "SpatRaster"), + is.numeric(iter) && iter > 0 + ) + + # Check if there is any NA, otherwise return + if(!terra::global(x, "anyNA")[,1]) return(x) + + # Save categories + if(terra::is.factor(x)) cats <- terra::cats(x) else cats <- NULL + + # Create dummy and progress per iteration + new <- x + pb <- progress::progress_bar$new(total = iter) + + for(i in 1:iter){ + pb$tick() + + # Check if no-data is still present. If no, skip + if(!terra::global(new, "anyNA")[,1]) next() + + # Buffer + new <- terra::focal(x = new, + w = 3, + fun = "modal", + na.policy = "only" + ) + } + + # Convert to factor again + if(terra::is.factor(x)) new <- terra::as.factor(new) + + # Return output + return(new) +} diff --git a/_pkgdown.yml b/_pkgdown.yml index 31d649f..7a878c6 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -43,6 +43,7 @@ reference: - spl_resampleRas - spl_exportNetCDF - spl_replaceGriddedNA + - spl_growGrid - has_keyword("spatial") - title: Miscellaneous functions diff --git a/man/spl_growGrid.Rd b/man/spl_growGrid.Rd new file mode 100644 index 0000000..eb7e99e --- /dev/null +++ b/man/spl_growGrid.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/spl_growGrid.R +\name{spl_growGrid} +\alias{spl_growGrid} +\title{Grow a categorical SpatRaster by certain amount of pixels or distance.} +\usage{ +spl_growGrid(x, iter = 2) +} +\arguments{ +\item{x}{A categorical \code{\link{SpatRaster}} to be grown.} + +\item{iter}{A \code{\link{numeric}} values of the maximum number of iterations from the grid +cell border of which \code{x} should be grown (Default: \code{2}).} +} +\value{ +\code{\link{SpatRaster}} +} +\description{ +A common issue in aggregating categorical rasters is that coastal and boundary +gridcells tend to decrease owing to aggregation effects. This can cause issues +of non-matching grid cells later on. +This simple function takes a categorical \code{\link{SpatRaster}} object and grows it +into no-data areas (those with \code{NA} values) within an optionally provided +distance. +} +\note{ +Only \code{NA} grid cells will be filled! +} +\examples{ +set.seed(42) +ras <- terra::rast(ncol = 100, nrow = 100, xmin = 0, xmax = 100, +ymin = 0, ymax = 100, resolution = 10, crs = NA) + +# Fill with dummy values but keep half as NA +terra::values(ras) <- c(rep(NA,50),rbinom(terra::ncell(ras)/2, 10, 0.5)) + +# Convert to factor +ras <- terra::as.factor(ras) +assertthat::assert_that(terra::is.factor(ras)) + +ras_nona <- spl_growGrid(x = ras, iter = 10) +terra::plot(ras_nona) + +} +\seealso{ +\code{\link[terra]{buffer}}, +\code{\link[terra]{focal}} +} +\keyword{spatial} diff --git a/tests/testthat/test-spl_tests.R b/tests/testthat/test-spl_tests.R index 7e978b1..91fba7c 100644 --- a/tests/testthat/test-spl_tests.R +++ b/tests/testthat/test-spl_tests.R @@ -22,6 +22,4 @@ test_that("Spatial object modifications", { ) expect_s4_class(r1_filled2, "SpatRaster") # --- # - - }) diff --git a/tests/testthat/test_spl_growGrid.R b/tests/testthat/test_spl_growGrid.R new file mode 100644 index 0000000..fc8dfda --- /dev/null +++ b/tests/testthat/test_spl_growGrid.R @@ -0,0 +1,25 @@ +test_that("Testing to grow a grid", { + + suppressWarnings(requireNamespace("terra", quietly = TRUE)) + + set.seed(42) + ras <- terra::rast(ncol = 100, nrow = 100, xmin = 0, xmax = 100, + ymin = 0, ymax = 100, resolution = 10, crs = NA) + + # Fill with dummy values but keep half as NA + terra::values(ras) <- c(rep(NA,50),rbinom(terra::ncell(ras)/2, 10, 0.5)) + + # Convert to factor + ras <- terra::as.factor(ras) + # Check + testthat::expect_s4_class(ras, "SpatRaster") + testthat::expect_true(terra::is.factor(ras)) + + # Grow grid + testthat::expect_no_error( + ras_nona <- spl_growGrid(x = ras, iter = 10) + ) + testthat::expect_s4_class(ras_nona, "SpatRaster") + # Expect no NA to remain + testthat::expect_false( terra::global(ras_nona, "anyNA")[,1] ) +})