Skip to contents

compete_inv() computes one or several distance-height- or distance-dbh-dependent competition indices based on forest inventory data.

Usage

compete_inv(
  inv_source,
  target_source = "buff_edge",
  radius,
  method = "all_methods",
  x = NULL,
  y = NULL,
  dbh = NULL,
  height = NULL,
  size = NULL,
  id = NULL,
  dbh_unit = c("cm", "m", "mm"),
  height_unit = c("m", "cm", "mm"),
  keep_rest = FALSE,
  verbose = TRUE,
  parallelize = FALSE,
  cores = ifelse(!is.null(options("cores")[[1]]), options("cores")[[1]],
    parallel::detectCores()),
  tol = 1,
  kmax = 999,
  ...
)

Arguments

inv_source

either an object of class forest_inv or target_inv, or any object that can be imported by read_inv(). If the object is read in from a source path or an unformatted data.frame,x, y, id, dbh, height and/or size can be specified as in read_inv() (see the corresponding documentation for details). If the input is a forest_inv or target_inv object, the column definitions and further arguments passed to read_inv() will be ignored. For target_inv objects, the function also ignores target_source and overrides further arguments passed to define_target().

target_source

one of the following:

  1. a path to an any object that can be imported by read_inv() (in this case, column specifications have to be the same as in inv_source - if this is not possible, load outside of compete_inv()).

  2. a vector of class "character" containing the tree IDs identifying the target trees in the same format as in the id column of inv,

  3. a vector of class logical specifying for each row of inv whether the corresponding tree is a target tree,

  4. another object of class forest_inv containing the coordinates of the target trees. In this case, the coordinates are matched against the coordinates in inv and IDs may differ (useful e.g. when target trees are defined based on GPS coordinates and maWarumtched against an airborne laser scanning dataset).

  5. a character vector of length 1 defining the method by which the target trees should be determined. Allowed are "buff_edge" for excluding all trees that are at least one search radius from the forest edge, "exclude_edge" for only excluding edge trees or "all_trees" for including all trees of the dataset (which is hardly ever a good idea unless all trees in the entire forest are in the dataset). The standard is "buff_edge". See define_target() for details.

radius

numeric of length 1. Search radius (in m) around the target tree. All neighboring trees within this radius are classified as competitors.

method

character string assigning the competition index functions to calculate. Can either be a vector with one or several of "CI_Hegyi", "CI_RK1","CI_RK2", "CI_Braathe", "CI_RK3", "CI_RK4", "CI_size" and/or the names of user-specified functions, or "all_methods" (the default)."all_methods" computes all built-in indices that can be calculated with the available data.

x

character of length 1 or name of the variable in inv_source containing the x coordinates of the tree in m. If NULL (default), the function tries to identify the x coordinate from the data.

y

character of length 1 or name of the variable in inv_source containing the y coordinates of the tree in meters. If NULL (default), the function tries to identify the y coordinate from the data.

dbh

character of length 1 or name of the variable in inv_source containing the diameter at breast height of the tree (by default in cm, but can be defined via dbh_unit). If NULL (default), the function tries to identify the dbh from the data.

height

character of length 1 or name of the variable in inv_source containing the height of the tree (by default in m, but can be defined via heigh_unit). If NULL (default), the function tries to identify the height from the data.

size

character of length 1 or name of the variable in inv_source containing a generic size-related variable for CI_size(). If NULL (default), the variable is not assigned.

id

character of length 1 or name of the variable in inv_source containing a unique tree ID. If NULL (default), the function tries to identify the ID from the data. If this is not possible, the trees are assigned a unique number. All IDs are coerced to character.

dbh_unit

character of length 1. Unit for the diameter measurements (one of "cm", "m" or "mm". defaults to "cm").

height_unit

character of length 1. Unit for the diameter measurements (one of "m", "cm" or "mm". defaults to "m").

keep_rest

logical of length 1. Keep additional variables in the inventory table besides x, y, dbh, size and id for filtering or further computations? Defaults to FALSE.

verbose

logical of length 1. Should information about progress be printed? Defaults to TRUE.

parallelize

logical of length 1. Should the computation of the competition indices be split over several cores? Defaults to FALSE.

cores

numeric of length 1. If parallelize = TRUE, on how many cores should the computations be run on? Defaults to the value registered in options("cores")[[1]], or, if this is not available, to parallel::detectCores()).

tol

numeric of length 1. Tolerance for the match with the tree coordinates. If coordinates are measured in the field with GPS, but inv_source contains x and y coordinates from a larger number of trees obtained by segmentation, this is the tolerance for the matching the forest inventory against the target tree positions. If no forest tree is within the tolerance to a target tree, no competition indices will be calculated for this tree and the function will return a warning. tol defaults to 1 m, but should be chosen depending on the measurement accuracy of the GPS coordinates.

kmax

integer of length 1. maximum number of nearest neighbors to consider for determining the neighbor trees of each tree. Lower values speed up computation, which is likely not an issue unlike your inventory contains tens of thousands of trees, but is very relevant for big datasets. Defaults to 999, which should be reasonable in almost all forest settings, but has to be adjusted when there are warnings (especially for large search radii and/or stands with small trees). If kmax is larger than the number of trees in the plot, the latter is passed on as k to nabor::knn().

...

additional arguments passed on to data.table::fread().

Value

object of classcompete_inv: a modified data.table with the position and size of the designated target tree(s) and one or more competition indices depending on chosen method(s).

Details

compete_inv() calculates one or several distance-dependent tree competition indices based on forest inventory data. These can be obtained either with classical forest inventory methods, or be derived from LiDAR point clouds (see below).

Inventory data can be either loaded from source, imported from an object inheriting from class data.frame (i.e., data.frames, tibbles, data.table objects etc.) or a forest_inv type object created with read_inv(). compete_inv() takes the same arguments for reading inventory data and has the same flexibility as read_inv().

To compute competition indices for trees from an inventory dataset, it is necessary to decide on the target trees for the analysis. While it is also possible to calculate competition indices for all trees in the inventory, this is almost never a good idea because unless the dataset covers all trees in the entire forest, there will be intense edge effects for the trees at the edge of the spatial extent of the dataset. compete_inv() allows to define target trees in a number of different ways based on the function define_target() that is called internally.

Available competition indices

All competition indices are calculated based on the subset of trees that are within the search radius of the target tree. The competition indices are computed according to the following equations, where \(d_i\) and \(h_i\) are the dbh and height of neighbor tree \(i\), \(d\) and \(h\) are dbh and height of the target tree, and \(dist_i\) is the distance from neighbor tree \(i\) to the target tree.

Diameter-based competition indices

  • CI_Hegyi introduced by Hegyi (1974):
    \(CI_{Hegyi} = \sum_{i=1}^{n} d_{i} / (d \cdot dist_{i})\)

  • CI_RK1 according to CI1 Rouvinen & Kuuluvainen (1997):
    \(CI_{RK1} = \sum_{i=1}^{n} \mathrm{arctan}(d_{i} / dist_{i})\)

  • CI_RK2 according to CI3 in Rouvinen & Kuuluvainen (1997):
    \(CI_{RK2} =\sum_{i=1}^{n} (d_{i} / d) \cdot \mathrm{arctan}(d_{i} / dist_{i})\)

Height-based competition indices

  • CI_Braathe according to Braathe (1980):
    \(CI_{Braathe} = \sum_{i=1}^{n} h_{i} / (h \cdot dist_{i})\)

  • CI_RK3 according to CI5 in Rouvinen & Kuuluvainen (1997):
    \(CI_{RK3} = \sum_{i=1}^{n} \mathrm{arctan}(h_{i} / dist_{i})\) for all trees with \(h_{i} > h\)

  • CI_RK4 based on CI3 in Rouvinen & Kuuluvainen (1997) and Contreras et al. (2011):
    \(CI_{RK4} = \sum_{i=1}^{n} (h_{i} / h) \cdot \mathrm{arctan}(h_{i} / dist_{i})\)

Generic size-based Hegyi-type competition index

  • CI_size based on Hegyi (1974), but with a user-specified size-related variable (\(s_i\): size for neighbor tree \(i\), \(s\): size of the target tree): \(CI_{size} = \sum_{i=1}^{n} s_{i} / (s \cdot dist_{i})\)

Further indices can be user-specified by developing a corresponding function (see competition_indices for details).

To efficiently deal with large inventory datasets (as can be expected from ALS sources), the neighbors within the search radius are computed with nabor::knn(), which is based on an efficient C++ implementation of the k-nearest neighbor algorithm from libnabo. For this, it is required to specify a maximum number of neighbors to take into account (kmax, which is passed to nabor::knn() as k) that should be chosen as low as possible, but high enough to ensure that it is sufficiently larger than the maximum number of trees expected in the search radius. When working with small datasets (from classical forest inventories or MLS/TLS sources), this will likely not matter, but when working with large ALS datasets this implementation can speed up calculation by several orders of magnitude. For even larger datasets, it is possible to compute the indices in parallel using foreach::foreach() via the doParallel backend. If parallelize = TRUE, the computation of the competition indices will be distributed onto cores parallel cores.

As all these indices are distance-weighted sums of the relative size of all competitor trees within the search radius compared to the target tree (or a sum of transformations thereof), they are very sensitive to the choice of the search radius. It is not generally possible to meaningfully compare competition indices computed with different search radii. Over which distance competitors affect the growth of the central tree is certainly specific and likely also depends on the average size of trees of the same species as the target tree and how far its roots spread under the local conditions. Lorimer (1983) recommends to use 3.5 times the mean crown radius of the target trees, but it is likely that there is no single value that works well under all conditions, and possible that the same values of competition indices calculated with the same radius have different meanings for different species.

Tree Segmentation

Various approaches can be used to segment (airborne) laser scanning point clouds into single trees and to obtain inventory data based it. Existing R packages for this are for example:

  • TreeLS package for automated segmentation of terrestrial/mobile laser scans

  • lidR package with different options to segment the point cloud or a Canopy Height Model (CHM)

  • itcLiDARallo() from the package itcSegment

Be careful with low resolution/low density point clouds, as oversegmentation of trees is usually an issue!

For examples of workflows to obtain inventory dator from airborne laser scanning data or terrestrial/mobile laser scanning data, see ALS workflow and TLS workflow, respectively.

Literature

  • Hegyi, F., 1974. A simulation model for managing jackpine stands. In: Fries, J. (Ed.), Proceedings of IUFRO meeting S4.01.04 on Growth models for tree and stand simulation, Royal College of Forestry, Stockholm.

  • Braathe, P., 1980. Height increment of young single trees in relation to height and distance of neighboring trees. Mitt. Forstl. VersAnst. 130, 43–48.

  • Rouvinen, S., Kuuluvainen, T., 1997. Structure and asymmetry of tree crowns in relation to local competition in a natural mature Scot pine forest. Can. J. For. Res. 27, 890–902.

  • Contreras, M.A., Affleck, D. & Chung, W., 2011. Evaluating tree competition indices as predictors of basal area increment in western Montana forests. Forest Ecology and Management, 262(11): 1939-1949.

  • Lorimer, C.G., 1983. Tests of age-independent competition indices for individual trees in natural hardwood stands. For. Ecol. Manage. 6, 343–360.

See also

read_inv() to read forest inventory data, define_target() for designating target trees, competition_indices for a list of available indices, plot_target() to plot target tree positions in target_inv and compete_inv objects.

Examples

if (FALSE) { # \dontrun{
# get target trees
targets4 <- readr::read_csv("data/inventory4.csv") %>%
  dplyr::filter(plot_id == "Plot 1") %>%
  read_inv(dbh = diam, verbose = FALSE) %>%
  define_target(target_source = "buff_edge", radius = 8)

# compute Hegyi index based on existing target_inv object
CI1 <- compete_inv(inv_source = targets4, radius = 8,
                   method = "CI_Hegyi")

# compute Hegyi based on a file source
CI2 <- compete_inv(
  inv_source = "data/inventory5.csv",
  target_source = "buff_edge",
  radius = 12,
  method = "CI_Hegyi",
  x = Koord_x,
  y = Koord_y,
  id = Baumname,
  dbh = Durchmesser,
  sep = ";",
  dec = ","
)

# compute all built-in indices that are possible with a dataset
(CI3 <- compete_inv(inv_source = targets4,
                    radius = 8, method = "all_methods"))

# new implementation of Hegyi competition index
CI_Hegyi_new <- function(target, neigh)
  sum(neigh$dbh / (target$dbh * neigh$dist))

# R1 index only for trees taller than the target tree
CI_RK1_tall <- function(target, neigh)
  CI_RK1(target, neigh[neigh$height > target$height, ])

# get output for new indices
compete_inv(inv_source = targets4, radius = 8,
            method = c("CI_Hegyi", "CI_Hegyi_new", "CI_RK1_tall"))

# partial Hegyi index for oak
CI_Hegyi_QURO <- function(target, neigh)
  CI_Hegyi(target, neigh[neigh$species == "Quercus robur", ])
# partial Hegyi index for hornbeam
CI_Hegyi_CABE <- function(target, neigh)
  CI_Hegyi(target, neigh[neigh$species == "Carpinus betulus", ])
# partial Hegyi index for ash
CI_Hegyi_FREC <- function(target, neigh)
  CI_Hegyi(target, neigh[neigh$species == "Fraxinus excelsior", ])

# load dataset with species
inv_species <- read_inv("data/inventory6.csv", keep_rest = TRUE)
inv_species

# compute species-decomposed Hegyi indices
comp_species <- compete_inv(
  inv_source = inv_species, target_source = "buff_edge", radius = 12,
  method = c("CI_Hegyi_QURO", "CI_Hegyi_CABE", "CI_Hegyi_FREC", "CI_Hegyi")
  )

} # }