|
| 1 | +#' Compute plot-level stand height metrics |
| 2 | +#' |
| 3 | +#' `compute_ht_metrics()` computes several stand height metrics for a given FIA |
| 4 | +#' plot, i.e., across the full four-subplot cluster (see Details). |
| 5 | +#' |
| 6 | +#' @details |
| 7 | +#' The following plot-level height metrics are returned in a named list with |
| 8 | +#' elements: |
| 9 | +#' * `meanTreeHt`: mean height of trees `>= 5.0` in. (`12.7` cm) diameter |
| 10 | +#' * `meanTreeHtBAW`: basal-area weighted mean height of trees `>= 5.0` in. |
| 11 | +#' (`12.7` cm) diameter |
| 12 | +#' * `meanTreeHtDom`: mean height of canopy dominant/codominant trees |
| 13 | +#' `>= 5.0` in. (`12.7` cm) diameter |
| 14 | +#' * `meanTreeHtDomBAW`: basal-area weighted mean height of canopy |
| 15 | +#' dominant/codominant trees `>= 5.0` in. (`12.7` cm) diameter |
| 16 | +#' * `maxTreeHt`: height of the tallest tree `>= 5.0` in. (`12.7` cm) diameter |
| 17 | +#' * `predomTreeHt`: predominant tree height, as the mean height of the tallest |
| 18 | +#' trees `>= 5.0` in. (`12.7` cm) diameter comprising `16` trees per acre |
| 19 | +#' (`39.5` trees per hectare) |
| 20 | +#' * `meanSapHt`: mean height of saplings (trees `>= 1.0` in. diameter but |
| 21 | +#' `< 5.0` in. diameter, i.e., `>= 2.54` cm but `< 12.7` cm) |
| 22 | +#' * `maxSapHt`: height of the tallest sapling |
| 23 | +#' |
| 24 | +#' For the purpose of height calculations, canopy dominant/codominant include |
| 25 | +#' "open grown" trees, i.e., include trees with FIA crown class codes (`CCLCD`) |
| 26 | +#' of `1` (open grown), `2` (dominant) and `3` (codominant), but exclude trees |
| 27 | +#' with `CCLCD` of `4` (intermediate) and `5` (overtopped). |
| 28 | +#' |
| 29 | +#' @param tree_list A data frame with tree records for one FIA plot. Must have |
| 30 | +#' columns `DIA` (tree diameter), `HT` (tree height), `ACTUALHT` (tree actual |
| 31 | +#' height, `ACTUALHT < HT` indicating a broken top), `CCLCD1` (FIA crown class |
| 32 | +#' code), `TPA_UNADJ` (trees per acre). |
| 33 | +#' @return |
| 34 | +#' A named list of height metrics computed for the input tree list, as described |
| 35 | +#' in Details. |
| 36 | +#' |
| 37 | +compute_ht_metrics <- function(tree_list) { |
| 38 | + |
| 39 | + if (missing(tree_list) || is.null(tree_list)) |
| 40 | + stop("'tree_list' is required", call. = FALSE) |
| 41 | + |
| 42 | + if (!is.data.frame(tree_list)) |
| 43 | + stop("'tree_list' must be a data frame", call. = FALSE) |
| 44 | + |
| 45 | + required_cols <- c("DIA", "HT", "ACTUALHT", "CCLCD", "TPA_UNADJ") |
| 46 | + |
| 47 | + if (!all(required_cols %in% colnames(tree_list))) |
| 48 | + stop("'tree_list' is missing required columns", call. = FALSE) |
| 49 | + |
| 50 | + # separate "trees" vs "saplings" here |
| 51 | + if ("STATUSCD" %in% colnames(tree_list)) { |
| 52 | + trees_in <- tree_list[tree_list$STATUSCD == 1 & tree_list$DIA >= 5, ] |
| 53 | + saplings_in <- tree_list[tree_list$STATUSCD == 1 & tree_list$DIA < 5, ] |
| 54 | + } else { |
| 55 | + trees_in <- tree_list[tree_list$DIA >= 5, ] |
| 56 | + saplings_in <- tree_list[tree_list$DIA < 5, ] |
| 57 | + } |
| 58 | + |
| 59 | + tree_ht <- pmin(trees_in$HT, trees_in$ACTUALHT, na.rm = TRUE) |
| 60 | + if (any(is.na(tree_ht))) |
| 61 | + warning("one or more tree heights are missing", call. = FALSE) |
| 62 | + |
| 63 | + sapling_ht <- pmin(saplings_in$HT, saplings_in$ACTUALHT, na.rm = TRUE) |
| 64 | + if (any(is.na(sapling_ht))) |
| 65 | + warning("one or more sapling heights are missing", call. = FALSE) |
| 66 | + |
| 67 | + ht_metrics <- vector(mode = "list", length = 8) |
| 68 | + names(ht_metrics) <- c("meanTreeHt", "meanTreeHtBAW", "meanTreeHtDom", |
| 69 | + "meanTreeHtDomBAW", "maxTreeHt", "predomTreeHt", |
| 70 | + "meanSapHt", "maxSapHt") |
| 71 | + |
| 72 | + basal_area <- pi * (trees_in$DIA / 2)^2 |
| 73 | + |
| 74 | + ht_metrics$meanTreeHt <- mean(tree_ht, na.rm = TRUE) |
| 75 | + ht_metrics$meanTreeHtBAW <- stats::weighted.mean(tree_ht, basal_area, |
| 76 | + na.rm = TRUE) |
| 77 | + |
| 78 | + tree_ht_doms <- tree_ht[trees_in$CCLCD %in% c(1, 2, 3)] |
| 79 | + ht_metrics$meanTreeHtDom <- mean(tree_ht_doms, na.rm = TRUE) |
| 80 | + |
| 81 | + basal_area_doms <- basal_area[trees_in$CCLCD %in% c(1, 2, 3)] |
| 82 | + ht_metrics$meanTreeHtDomBAW <- |
| 83 | + stats::weighted.mean(tree_ht_doms, basal_area_doms, na.rm = TRUE) |
| 84 | + |
| 85 | + ht_metrics$maxTreeHt <- max(tree_ht, na.rm = TRUE) |
| 86 | + |
| 87 | + tree_ht_tpa <- data.frame(tree_ht, trees_in$TPA_UNADJ, check.names = FALSE) |
| 88 | + tree_ht_tpa <- tree_ht_tpa[order(tree_ht, decreasing = TRUE), ] |
| 89 | + tpa <- 0 |
| 90 | + sum_ht <- 0 |
| 91 | + n <- 0 |
| 92 | + for (i in seq_len(nrow(tree_ht_tpa))) { |
| 93 | + sum_ht <- sum_ht + tree_ht_tpa[i, 1] |
| 94 | + tpa <- tpa + tree_ht_tpa[i, 2] |
| 95 | + n <- n + 1 |
| 96 | + if (tpa > 16) |
| 97 | + break |
| 98 | + } |
| 99 | + |
| 100 | + |
| 101 | + |
| 102 | + |
| 103 | + |
| 104 | + |
| 105 | + return(ht_metrics) |
| 106 | +} |
0 commit comments