This is built with spread2() and is still experimental. This one differs from other attempts in that it treats the advection and dispersal as mathematical vectors that are added together. They are "rounded" to pixel centres.

spread3(
  start,
  rasQuality,
  rasAbundance,
  advectionDir,
  advectionMag,
  meanDist,
  dispersalKernel = "exponential",
  sdDist = 1,
  plot.it = 2,
  minNumAgents = 50,
  verbose = getOption("LandR.verbose", 0),
  saveStack = NULL,
  skipChecks = FALSE
)

Arguments

start

Raster indices from which to initiate dispersal

rasQuality

A raster with habitat quality. Currently, must be scaled from 0 to 1, i.e., a probability of "settling"

rasAbundance

A raster where each pixel represents the number of "agents" or pseudo-agents contained. This number of agents, will be spread horizontally, and distributed from each pixel that contains a non-zero non NA value.

advectionDir

A single number or RasterLayer in degrees from North = 0 (though it will use radians if all values are abs(advectionDir) > 2 * pi). This indicates the direction of advective forcing (i.e., wind).

advectionMag

A single number or RasterLayer in distance units of the rasQuality, e.g., meters, indicating the relative forcing that will occur. It is imposed on the total event, i.e., if the meanDist is 10000, and advectionMag is 5000, then the expected distance (i.e., 63% of agents) will have settled by 15000 map units.

meanDist

A single number indicating the mean distance parameter in map units (not pixels), for a negative exponential distribution dispersal kernel (e.g., dexp). This will mean that 63% of agents will have settled at this meanDist (still experimental).

dispersalKernel

One of either "exponential" or "weibull".

sdDist

A single number indicating the sd parameter of a two-parameter dispersalKernel. Defaults to 1, which is the same as the exponential distribution.

plot.it

Numeric. With increasing numbers above 0, there will be plots produced during iterations. Currently, only 0, 1, or 2+ are distinct.

minNumAgents

Single numeric indicating the minimum number of agents to consider all dispersing finished. Default is 50.

verbose

Numeric. With increasing numbers above 0, there will be more messages produced. Currently, only 0, 1, or 2+ are distinct.

saveStack

If provided as a character string, it will save each iteration as part of a rasterStack to disk upon exit.

skipChecks

Logical. If TRUE, assertions will be skipped (faster, but could miss problems)

Value

A data.table with all information used during the spreading

Examples

## these tests are fairly heavy, so don't run during automated tests
#########################################################
# Simple case, no variation in rasQuality, numeric advectionDir and advectionMag
#########################################################
# \donttest{
  library(terra)

  origDTThreads <- data.table::setDTthreads(2L)
  origNcpus <- options(Ncpus = 2L)

  maxDim <- 10000
  ras <- terra::rast(terra::ext(c(0, maxDim, 0, maxDim)), res = 100, vals = 0)
  rasQuality <- terra::rast(ras)
  rasQuality[] <- 1
  rasAbundance <- terra::rast(rasQuality)
  rasAbundance[] <- 0
  # startPixel <- middlePixel(rasAbundance)
  startPixel <- sample(seq(terra::ncell(rasAbundance)), 30)
  rasAbundance[startPixel] <- 1000
  advectionDir <- 70
  advectionMag <- 4 * res(rasAbundance)[1]
  meanDist <- 2600

  # Test the dispersal kernel -- create a function
  plotDispersalKernel <- function(out, meanAdvectionMag) {
    out[, disGroup := round(distance / 100) * 100]
    freqs <- out[, .N, by = "disGroup"]
    freqs[, `:=`(cumSum = cumsum(N), N = N)]
    plot(freqs$disGroup, freqs$cumSum, # addTo = "CumulativeNumberSettled",
         main = "Cumulative Number Settled") # can plot the distance X number
    abline(v = meanAdvectionMag + meanDist)
    newTitle <- "Number Settled By Distance"
    plot(freqs$disGroup, freqs$N, # addTo = gsub(" ", "", newTitle),
         main = newTitle) # can plot the distance X number
    abline(v = meanAdvectionMag + meanDist)
    # should be 0.63:
    freqs[disGroup == meanAdvectionMag + meanDist, cumSum] / tail(freqs, 1)[, cumSum]
    mtext(side = 3, paste("Average habitat quality: ",
                          round(mean(rasQuality[], na.rm = TRUE), 2)),
          outer = TRUE, line = -2, cex = 2)
  }
  out <- spread3(rasAbundance = rasAbundance,
                 rasQuality = rasQuality,
                 advectionDir = advectionDir,
                 advectionMag = advectionMag,
                 meanDist = meanDist, verbose = 2,
                 plot.it = interactive())
#> assuming that advectionDir is in geographic degrees(i.e., North is 0)
#> Iteration 1
#> Number still dispersing 29942.6974241127
#> Iteration 2
#> Number still dispersing 29844.9762566839
#> Iteration 3
#> Number still dispersing 29685.3802485319
#> Iteration 4
#> Number still dispersing 29011.6874320189
#> Iteration 5
#> Number still dispersing 28738.7043861212
#> Iteration 6
#> Number still dispersing 28390.2095263323
#> Iteration 7
#> Number still dispersing 27499.1360749765
#> Iteration 8
#> Number still dispersing 26606.2258994556
#> Iteration 9
#> Number still dispersing 25877.0356570589
#> Iteration 10
#> Number still dispersing 25375.2295609656
#> Iteration 11
#> Number still dispersing 24770.6994268542
#> Iteration 12
#> Number still dispersing 24194.6347369196
#> Iteration 13
#> Number still dispersing 23494.7064422054
#> Iteration 14
#> Number still dispersing 22829.6665195045
#> Iteration 15
#> Number still dispersing 21861.0967716493
#> Iteration 16
#> Number still dispersing 21117.7376380452
#> Iteration 17
#> Number still dispersing 20070.0945202608
#> Iteration 18
#> Number still dispersing 18946.9787466031
#> Iteration 19
#> Number still dispersing 18077.9637351511
#> Iteration 20
#> Number still dispersing 16884.0179906487
#> Iteration 21
#> Number still dispersing 15804.2381815924
#> Iteration 22
#> Number still dispersing 14880.9809887794
#> Iteration 23
#> Number still dispersing 13799.901161894
#> Iteration 24
#> Number still dispersing 12956.7343588249
#> Iteration 25
#> Number still dispersing 12214.5852104103
#> Iteration 26
#> Number still dispersing 11329.8197657753
#> Iteration 27
#> Number still dispersing 10632.044157983
#> Iteration 28
#> Number still dispersing 9920.91528468153
#> Iteration 29
#> Number still dispersing 9258.39007676729
#> Iteration 30
#> Number still dispersing 8637.25826783464
#> Iteration 31
#> Number still dispersing 7975.86058020982
#> Iteration 32
#> Number still dispersing 7420.90295069017
#> Iteration 33
#> Number still dispersing 6881.67182789445
#> Iteration 34
#> Number still dispersing 6400.70194654077
#> Iteration 35
#> Number still dispersing 5845.83912622311
#> Iteration 36
#> Number still dispersing 5400.16540393561
#> Iteration 37
#> Number still dispersing 5015.24335692709
#> Iteration 38
#> Number still dispersing 4618.26575086753
#> Iteration 39
#> Number still dispersing 4237.60716804517
#> Iteration 40
#> Number still dispersing 3827.36918125638
#> Iteration 41
#> Number still dispersing 3476.61827631714
#> Iteration 42
#> Number still dispersing 3138.94216473754
#> Iteration 43
#> Number still dispersing 2862.38664117625
#> Iteration 44
#> Number still dispersing 2563.92759377828
#> Iteration 45
#> Number still dispersing 2294.12689211881
#> Iteration 46
#> Number still dispersing 2087.17205405477
#> Iteration 47
#> Number still dispersing 1834.3348834748
#> Iteration 48
#> Number still dispersing 1664.34159685505
#> Iteration 49
#> Number still dispersing 1439.17020145494
#> Iteration 50
#> Number still dispersing 1128.02925662406
#> Iteration 51
#> Number still dispersing 998.262361459414
#> Iteration 52
#> Number still dispersing 850.687521593722
#> Iteration 53
#> Number still dispersing 719.772092270625
#> Iteration 54
#> Number still dispersing 591.002012774563
#> Iteration 55
#> Number still dispersing 526.895299153967
#> Iteration 56
#> Number still dispersing 419.700192629597
#> Iteration 57
#> Number still dispersing 372.959977379308
#> Iteration 58
#> Number still dispersing 309.013970986235
#> Iteration 59
#> Number still dispersing 244.361662343633
#> Iteration 60
#> Number still dispersing 179.370246150411
#> Iteration 61
#> Number still dispersing 140.255807667946
#> Iteration 62
#> Number still dispersing 104.798536501852
#> Iteration 63
#> Number still dispersing 36.249374064126

  plotDispersalKernel(out, advectionMag)



  # The next examples are potentially time consuming; avoid on automated testing
  if (interactive()) {
    #########################################################
    ### The case of variable quality raster
    #########################################################
    rasQuality <- terra::rast(system.file("extdata", "rasQuality.tif",
                                          package = "SpaDES.tools"))
    terra::crs(rasQuality) <- system.file("extdata", "targetCRS.rds", package = "SpaDES.tools") |>
      readRDS() |>
      slot("projargs")
    mask <- rasQuality < 5
    rasQuality[mask[] %in% TRUE] <- 0
    # rescale so min is 0.75 and max is 1
    rasQuality[] <- rasQuality[] / (reproducible::maxFn(rasQuality) * 4) + 1 / 4
    rasAbundance <- terra::rast(rasQuality)
    rasAbundance[] <- 0
    startPixel <- sample(seq(ncell(rasAbundance)), 300)
    rasAbundance[startPixel] <- 1000
    advectionDir <- 75
    advectionMag <- 4 * res(rasAbundance)[1]
    meanDist <- 2600
    out <- spread3(rasAbundance = rasAbundance,
                   rasQuality = rasQuality,
                   advectionDir = advectionDir,
                   advectionMag = advectionMag,
                   meanDist = meanDist, verbose = 2,
                   plot.it = interactive())
    if (interactive()) {
      plotDispersalKernel(out, advectionMag)
    }

    ###############################################################################
    ### The case of variable quality raster, raster for advectionDir & advectionMag
    ###############################################################################
    maxDim <- 10000
    ras <- terra::rast(terra::ext(c(0, maxDim, 0, maxDim)), res = 100, vals = 0)
    rasQuality <- terra::rast(ras)
    rasQuality[] <- 1
    rasAbundance <- terra::rast(rasQuality)
    rasAbundance[] <- NA
    # startPixel <- middlePixel(rasAbundance)
    startPixel <- sample(seq(ncell(rasAbundance)), 25)
    rasAbundance[startPixel] <- 1000

    # raster for advectionDir
    advectionDir <- terra::rast(system.file("extdata", "advectionDir.tif",
                                            package = "SpaDES.tools"))
    crs(advectionDir) <- crs(rasQuality)
    # rescale so min is 0.75 and max is 1
    advectionDir[] <- advectionDir[] / (reproducible::maxFn(advectionDir)) * 180

    # raster for advectionMag
    advectionMag <- terra::rast(system.file("extdata", "advectionMag.tif",
                                            package = "SpaDES.tools"))
    crs(advectionMag) <- crs(rasQuality)
    # rescale so min is 0.75 and max is 1
    advectionMag[] <- advectionMag[] / (reproducible::maxFn(advectionMag)) * 600

    out <- spread3(rasAbundance = rasAbundance,
                   rasQuality = rasQuality,
                   advectionDir = advectionDir,
                   advectionMag = advectionMag,
                   meanDist = meanDist, verbose = 2,
                   plot.it = interactive())

    if (interactive()) {
      names(advectionDir) <- "Wind direction"
      names(advectionMag) <- "Wind speed"
      names(rasAbundance) <- "Initial abundances"
      terra::plot(c(advectionDir, advectionMag, rasAbundance))

      plotDispersalKernel(out, mean(advectionMag[]))
    }

    #########################################
    # save iterations to a stack to make animated GIF
    ########################################
    tmpStack <- tempfile(pattern = "stackToAnimate", fileext = ".tif")
    out <- spread3(rasAbundance = rasAbundance,
                   rasQuality = rasQuality,
                   advectionDir = advectionDir,
                   advectionMag = advectionMag,
                   meanDist = 2600, verbose = 2,
                   plot.it = interactive(), saveStack = tmpStack)

    ## This animates the series of images into an animated GIF
    if (require(animation, quietly = TRUE)) {
      out2 <- terra::rast(tmpStack)
      gifName <- file.path(tempdir(), "animation.gif")

      # Only works on some systems; may need to configure
      # Works on Windows without system adjustments
      if (identical(.Platform$OS.type, "windows"))
        saveGIF(interval = 0.1, movie.name = gifName, expr = {
          for (i in seq(length(names(out2)))) terra::plot(out2[[i]])
        })
    }
  }

  # clean up
  data.table::setDTthreads(origDTThreads)
  options(Ncpus = origNcpus)
# }