Skip to content

Potential create_mosaic_raster for spatialplanr #3

@ric325

Description

@ric325

#' Creates a single terra raster layer with a mosaic of patches with the specified number of categories and approximate percentage area of each based on relative cover values provided
#'
#' The area of each category in the mosaic will sum approximately to its specified relative cover provided
#' The patch generation process works by using a random initial patch centre
#' Thus, higher cover numbers lead to more initial patch centres and
#' The number of patch centres for each category is specified by the relative cover of each category
#' Patches then grow by assigning each pixel to the nearest random patch centre, resulting in patches for each category
#' Note that the number of patches depends on how dispersed the initial random centres are and this is more likely with higher covers
#'
#' @param nrows Integer specifying the number of columns in the raster
#' @param ncols Integer specifying the number of columns in the raster
#' @param setseed Boolean specifying whether the mosaic raster should be reproducible (1) or random (0)
#' @param cover A named vector of category cover (does not need to sum to 100)
#'
#' @return A terra raster stack of a single terra raster layer with a mosaic of patches with the specified number of categories and approximate % area of each
#'
#' @examples
#' \dontrun{
#' mosaic_cover <- create_mosaic_raster(
#' nrows = 100, ncols = 100, setseed = 1,
#' cover = c("cocoa plantation" = 40,
#' "disturbed forest" = 20,
#' "undisturbed forest" = 10,
#' "other areas" = 20,
#' "protected areas" = 10)
#' )
#' plot(mosaic_cover)
#'
#'#' \dontrun{
#' mosaic_cover <- create_mosaic_raster(
#' nrows = 10, ncols = 10, setseed = 0,
#' cover = c("Zone1" = 4,
#' "Zone2" = 2)
#' )
#' plot(mosaic_cover)

#'#' \dontrun{
#' mosaic_cover <- create_mosaic_raster(
#' nrows = 10, ncols = 10, setseed = 0,
#' cover = c("Zone1" = 40,
#' "Zone2" = 20)
#' )
#' plot(mosaic_cover)

create_mosaic_raster <- function(nrows = 100,
ncols = 100,
setseed = 1,
cover = c("Category1" = 50,
"Category2" = 30,
"Category3" = 20,
)){

Assign integer codes to each category and store their names

categories <- seq_along(cover)
names_vec <- names(cover)
total_cells <- nrows * ncols

Generate random patch centres for each category

The number of patch centres for each category is set by its cover value

if(setseed == 1) set.seed(123) # for reproducibility

patch_centres <- tibble::tibble(
x = sample(1:ncols, sum(cover), replace = TRUE), # x-coordinates of patch centers
y = sample(1:nrows, sum(cover), replace = TRUE), # y-coordinates of patch centers
cat = rep(categories, cover) # category code for each patch center
)

Create a grid of coordinates for all pixels in the raster

pixel_coords <- expand.grid(x = 1:ncols, y = 1:nrows)

For each pixel, find the nearest patch center and assign its category

This ensures pixels close to a patch center are grouped together (clumped)

assign_category <- function(px, py) {
dists <- sqrt((patch_centres$x - px)^2 + (patch_centres$y - py)^2) # Euclidean distance to all patch centers
nearest <- which.min(dists) # Find the nearest patch center
patch_centres$cat[nearest] # Assign the category of that patch center
}

Use purrr::map2_int to efficiently assign categories to all pixels

assigned <- purrr::map2_int(pixel_coords$x, pixel_coords$y, assign_category)

Convert the assigned categories into a matrix and then to a raster

mat <- matrix(assigned, nrow = nrows, ncol = ncols, byrow = FALSE)
r <- terra::rast(mat)

Add category names as levels for easier plotting and interpretation

levels(r) <- data.frame(id = categories, cover = names_vec)

Return the patchy raster

return(r)
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions