R/stand.R

#' Initialize the model Stand
#'
#' @details
#' If soil is a matrix, the number of columns must be equal to npatch. In that way each patch can have its own soil depth.
#' The patches represented as hexagons can either be arranged in a square or in a line. The later one for example to represent a time series (succession).
#' @param npatch number of patches
#' @param year the initialization year
#' @param soil a vector or matrix of soil depths.
#' @param z the height of each patch.
#' @param layout patch layout ('square' or 'linear'), a two element vector with number of rows/colums. A matrix for layout (not yet ready).
#' @param composition 'spatial' or 'temporal'
#' @param dist the fractional distance between the hexagons
#' @return a \code{\link{Stand-class}}
#' @importFrom methods new
#' @importFrom grDevices colorRampPalette
#' @export
#' @include classes.R
#' @examples
#' \dontrun{
#' stand <- initStand(npatch=9, z=sort(rnorm(9, sd=2)))
#' stand3D(stand)
#'
#' stand <- initStand(npatch=9, z=sort(rnorm(9, sd=2)), layout='linear')
#' stand3D(stand)
#' }
initStand <- function(npatch=1, year=2000, soil=c(0, -0.5, -1.5), z=0, layout="square", composition="spatial", dist=0.05) {
  if (is.matrix(soil)) {
    if (ncol(soil) != npatch)
      stop("'npatch' and 'ncol(soil)' differ!")
  } else {
    if (!is.vector(soil))
      stop("'soil' must either be a matrix or vector")
  }
  if (length(z) == 1) {
    z = rep(z, npatch)
  } else if (length(z) != npatch) {
    stop("'z' must be either of length 'npatch' or 1!")
  }

  hexagon <- getHexagon(area=dgvm3d.options("patch.area"), z=c(0, -1))

  if (typeof(layout) == "character") {
    if (layout == "square") {
      nxy.max = ceiling(sqrt(npatch))
      nxy.min = floor(sqrt(npatch))
      ## with 3 patches the above got the values 2 and 1, so 3 was never reached in the loop below
      if (npatch == 3)
        nxy.min=2
      layout = c(nxy.min, nxy.max)
    } else {
      nxy.min = npatch
      nxy.max = 1
      layout = c(nxy.min, nxy.max)
    }
  } else if (typeof(layout) == "integer" && is.vector(layout)) {
    nxy.min = layout[2]
    nxy.max = layout[1]
  } else if (typeof(layout) == "integer" && is.matrix(layout)) {
    nxy.min = nrow(layout)
    nxy.max = ncol(layout)
    stop("Not yet ready!")
  }

  ## How I choose the colors:
  ## library(RColorBrewer)
  ## bp = brewer.pal(9, 'YlOrBr')
  ## plot(rep(1,9), cex=5, pch=16, col=bp)
  ## bp[c(4,8)] ##  => c("#FEC44F", "#993404")
  soil.colors = colorRampPalette(c("#993404", "#FEC44F"))

  patches=list()
  for (i in 1:nxy.max) {
    for (j in 1:nxy.min) {
      id <- (i - 1) * nxy.min + j
      if (id > npatch)
        break
      if (composition == "temporal") {
        x = (2 + dist) * (i - 1) * hexagon@supp[['outer.radius']]
        y = (2 + dist) * (j - 1) * hexagon@supp[['inner.radius']]
      } else {
        x = (1.5 + dist) * (i - 1) * hexagon@supp[['outer.radius']]
        y = (2.0 + dist) * (j - 1) * hexagon@supp[['inner.radius']] + (i %% 2) * (1 + dist / 1.95) * hexagon@supp[['inner.radius']]
      }
      if (id == 1) {
        patch.pos <- matrix(c(x, y, z[1]), 3, 1)
        row.names(patch.pos) <- c("x", "y", "z")
      } else {
        patch.pos = cbind(patch.pos, c(x, y, z[id]))
      }
      if (is.matrix(soil)) {
        coltab <- list()
        coltab[['soil']] = soil.colors(length(soil[, id])-1)
        patches[[id]] = new("Patch", id=id, soil=soil[, id], color.table=coltab)
      } else {
        coltab <- list()
        coltab[['soil']] = soil.colors(length(soil)-1)
        patches[[id]] = new("Patch", id=id, soil=soil, color.table=coltab)
      }
    }
  }

  return(new("Stand",
             area=dgvm3d.options("patch.area"),
             year=year,
             hexagon=hexagon,
             layout=layout,
             composition=composition,
             patch.pos=t(patch.pos),
             patches=patches))
}

#' Remove/add trees with a new vegetation data.frame
#'
#' Removes those individuums with the shortest distance to any neighbour and adds new individuums randomly.
#'
#' @param stand stand to update
#' @param vegetation new vegetation data.frame
#' @param year the next year
#' @return stand
#' @export
updateStand <- function(stand, vegetation, year=NULL) {
  Year=PID=Crownarea=NULL
  if (is.null(year)) {
    old.year = stand@year
    new.years = sort(unique(vegetation$Year))
    if (all(new.years <= old.year))
      stop("No larger year in vegetation data.frame!")
    if (!is.null(year)) {
      if (all(new.years != year))
        stop(paste0("Given 'year' (", year, ") not in 'vegetation data.frame!"))
    } else {
      year = new.years[which(new.years > old.year)[1]]
    }
  }
  vegetation = subset(vegetation, Year == year)
  stand@year = year

  for ( i in 1:length(stand@patches)) {
    new.patch.veg = subset(vegetation, PID == stand@patches[[i]]@pid)
    if (nrow(new.patch.veg) > 0) {
      new.patch.veg$x = NA
      new.patch.veg$y = NA
      new.patch.veg$dnn = NA
      old.vid = unique(stand@patches[[i]]@vegetation$VID)
      if (length(old.vid) > 0) {
        ## removal of killed individuals
        for (j in 1:length(old.vid)) {
          remain = sum(new.patch.veg$VID == old.vid[j])
          old.trees = stand@patches[[i]]@vegetation[stand@patches[[i]]@vegetation$VID == old.vid[j], ]
          old.trees = old.trees[with(old.trees, order(-dnn)), ]
          new.patch.veg[new.patch.veg$VID == old.vid[j], "x"] = old.trees$x[1:remain]
          new.patch.veg[new.patch.veg$VID == old.vid[j], "y"] = old.trees$y[1:remain]
        }
      }
      ## f_js_20180224 DEBUG one grass has coord values
      stand@patches[[i]]@vegetation = subset(new.patch.veg, Crownarea < 0 | !is.finite(Crownarea))
      stand@patches[[i]]@vegetation = rbind(stand@patches[[i]]@vegetation,
                                            establishTrees(subset(new.patch.veg, Crownarea > 0 & is.finite(Crownarea)), stand@hexagon@supp$inner.radius))
    } else {
      stand@patches[[i]]@vegetation = data.frame()
    }
  }
  return(stand)
}

#' 3D view of the stands
#'
#' Uses \code{\link{rgl}} to visualize a single, if patch.id is given, or all patch soil hexagons
#'
#' @param stand the \code{\link{Stand-class}} to visualize
#' @param patch.id the patch IDs to create. Default: all.
#' @return None
#' @export
#' @import rgl
#' @include classes.R
#' @seealso \code{\link{initStand}} for examples
stand3D <- function(stand, patch.id=NULL) {
  if (is.null(patch.id))
    patch.id <- 1:length(stand@patches)

  for (i in patch.id) {
    patch.hex = stand@hexagon@vertices
    offset = matrix(stand@patch.pos[i, ], nrow(patch.hex), 3, byrow=TRUE)
    patch.hex[,1:2] = patch.hex[,1:2] + offset[,1:2]
    for (j in 1:(length(stand@patches[[i]]@soil) - 1)) {
      patch.hex[1:6,  3] = stand@patches[[i]]@soil[j]
      patch.hex[7:12, 3] = stand@patches[[i]]@soil[j + 1]
      patch.hex[,3] = patch.hex[,3] + offset[,3]
      triangles3d(patch.hex[stand@hexagon@id, ], col=stand@patches[[i]]@color.table[['soil']][j])
    }
  }
}

Try the DGVM3D package in your browser

Any scripts or data that you put into this service are public.

DGVM3D documentation built on May 2, 2019, 3:47 p.m.