Simulate seed dispersal using user defined function. This is a "receiving pixel" focused dispersal approach. It is the "potentially receiving" cell that looks around itself for potential seed sources. If it finds a single seed source, that passes the probability function described by the dispersalFn. If this passes a comparison to a uniform random draw, then the receiving cell is deemed to have a "successful" dispersal for that species. This function can therefore only be used for a relatively specific situation where there is a yes/no returned for each potential receiving cell, i.e., not abundance. This function is also not cumulative, i.e,. there is no higher abundance of seeds received if a receiving cell has lots of seed sources around it vs. a single seed source. The difference will come with a higher probability of successfully receiving a "seed".

  dispersalFn = Ward,
  b = 0.01,
  k = 0.95, = FALSE,
  verbose = getOption("LandR.verbose", TRUE),







A RasterLayer with pixel values equal to a pixel group number that corresponds exactly to pixelGroup column in cohortData.


A species traits table, with at least the following columns:

  • speciesCode an character representation of species;

  • Area a character of geographical area within a species range for which trait values are relevant;

  • longevity species longevity in years, sexualmature age in years at sexual maturity;

  • shadetolerance numeric value between 1-5 of relative shade tolerance (with respect to other species);

  • seeddistance_eff the "effective" seed dispersal distance;

  • seeddistance_max a numeric with the maximum seed dispersal distance;

  • mortalityshape and growthcurve: growth curve shape parameters. Other columns (e.g. fire-related traits) can also be included depending on LandR modules in use. Please see the LANDIS-II Biomass Succession Extension v3.2.1 manual (Scheller and Miranda 2015) for further detail.


An expression that can take a "dis" argument. See details. Default is "Ward" (temporarily unused, as it is hard coded inside Rcpp function)


LANDIS Ward seed dispersal calibration coefficient (set to 0.01 in LANDIS)


LANDIS Ward seed dispersal the probability that seed will disperse within the effective distance (e.g., 0.95)

Deprecated. If TRUE, then plot the raster at every interaction, so one can watch the LANDISDisp event grow.


The time between successive seed dispersal events. In LANDIS-II, this is called "Succession Timestep".


Controls message output. Defaults to getOption("LandR.verbose")


Additional parameters. Currently none.


A numeric vector of raster pixel indices, in the same resolution and extent as seedSrc raster.


dispersalFn (temporarily unused as code is converted to Rcpp -- the default dispersalFn is hard coded within the spiralSeedDispersal function that uses C++) must be an expression that returns a probability distribution. Because it is a dispersal kernel, it must be a probability distribution. The expression that can take an argument named "dis" (without quotes) as this will be calculated internally and represents the distance from the initial (receiving) pixel and all active pixels within that cluster of active pixels. SpaDES includes the Ward() kernel as defined in the LANDIS-II documentation.


Eliot McIntire


if (require("googledrive")) {
  seed <- sample(1e6, 1)

  # keep this here for interactive testing with a larger raster
  rasterTemplate <- LandR:::rasterRead(terra::ext(0, 2500, 0, 2500), res = 100)

  # make a pixelGroupMap
  pgs <- 4 # make even just because of approach below requires even
  pixelGroupMap <-, numTypes = pgs)
  pixelGroupMap[1:100] <- NA # emulate a mask at the start

  # Make a receive pixels table -- need pixelGroup and species
  nSpecies <- 3
  maxNSpeciesPerPixel <- min(5, nSpecies)
  rcvSpByPG <- lapply(seq_len(pgs / 2), function(pg) {
    data.table(speciesCode = sample(nSpecies, size = sample(maxNSpeciesPerPixel, 1)))
  seedReceive <- rbindlist(rcvSpByPG, idcol = "pixelGroup")

  # Make a source pixels table -- need pixelGroup and species
  srcSpByPG <- lapply(seq_len(pgs / 2), function(pg) {
    data.table(speciesCode = sample(nSpecies, size = sample(maxNSpeciesPerPixel, 1)))
  seedSource <- rbindlist(srcSpByPG, idcol = "pixelGroup")
  # make source pixels not same pixelGroups as receive
  seedSource[, pixelGroup := pixelGroup + pgs / 2]

  # Get a species table -- if using in Canada, can use this
  speciesTable <- getSpeciesTable(dPath = tempdir())
  speciesTable <- speciesTable[Area == "BSW"]
  speciesTable[, speciesCode := as.factor(LandisCode)]
  speciesTable[, seeddistance_eff := SeedEffDist]
  speciesTable[, seeddistance_max := SeedMaxDist]

  speciesTable <- speciesTable
  speciesTable <- data.table(speciesTable)[, speciesCode := seq_along(LandisCode)]
  seedReceiveFull <- speciesTable[seedReceive, on = "speciesCode"]
  output <- LANDISDisp(
    dtRcv = seedReceiveFull, = interactive(),
    dtSrc = seedSource,
    speciesTable = speciesTable,
    verbose = TRUE,
    successionTimestep = 10
  # Summarize
  output[, .N, by = speciesCode]

  ## Plot the maps
  if (interactive()) {
    spMap <- list()
    spMap$pixelGroupMap <- pixelGroupMap
    for (sppp in unique(output$speciesCode)) {
      spppChar <- paste0("Sp_", sppp)
      spMap[[spppChar]] <- LandR:::rasterRead(pixelGroupMap)
      ss <- unique(seedSource[speciesCode == sppp], on = c("pixelGroup", "speciesCode"))
      spMap[[spppChar]][pixelGroupMap[] %in% ss$pixelGroup] <- 1

      receivable <- LandR:::rasterRead(pixelGroupMap)
      srf <- unique(seedReceiveFull[speciesCode == sppp], on = c("pixelGroup", "speciesCode"))
      receivable[pixelGroupMap[] %in% srf$pixelGroup] <- 1

      forest <- which(![]))
      src <- which(![[spppChar]][]))
      recvable <- which(![]))
      rcvd <- output[speciesCode == sppp]$pixelIndex

      spMap[[spppChar]][forest] <- 0
      spMap[[spppChar]][recvable] <- 2
      spMap[[spppChar]][src] <- 1
      spMap[[spppChar]][rcvd] <- 3
      spMap[[spppChar]][intersect(src, rcvd)] <- 4

      levels(spMap[[spppChar]]) <- data.frame(ID = 0:4,
                                              type = c("OtherForest", "Source", "Didn't receive",
                                                       "Received", "Src&Rcvd"))
    Plot(spMap, cols = "Set2")

    # A summary
    rr <- apply(rast(spMap)[[-1]][] + 1, 2, tabulate)
    rownames(rr) <- raster::levels(spMap[[2]])[[1]][,"type"][1:NROW(rr)]
    # next line only works if there are some places that are both source and potential to receive
    # rr <- rbind(rr, propSrcRcved = round(rr[5,]/ (rr[5,]+rr[2,]), 2))
#> Loading required package: googledrive
#> No cachePath supplied and getOption('reproducible.cachePath') is
#>   inside a temporary directory;
#>   this will not persist across R
#>   sessions.
#>  Running `prepInputs`
#>   Running `preProcess`
#>    Preparing: speciesTraits.csv
#>    ...downloading...
#>    Downloading
#>      ...
  |                                                                      |   0%
  |======================================================================| 100%
#>    Hardlinked version of file(s) created:
#>    ... no copy/copies made.
#>    alsoExtract is unspecified; assuming that all files must be
#>      extracted
#>    Appending checksums to CHECKSUMS.txt. If you see this message
#>      repeatedly, you can specify targetFile (and optionally alsoExtract)
#>      so it knows what to look for.
#>   Running `process` (i.e., loading file into R)
#>    targetFile located at /tmp/RtmpU0Xt8U/speciesTraits.csv
#>    Loading object into R
#>    Saved! Cache file: 9325be38bac7e8b3.rds; fn:
#>      data.table::fread
#> Saved! Cache file: ff83cfcdb8675c83.rds; fn: prepInputs