Skip to content

Commit 18199d8

Browse files
committed
WIP: add compute_ht_metrics()
1 parent d4ff30d commit 18199d8

2 files changed

Lines changed: 108 additions & 1 deletion

File tree

DESCRIPTION

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ Imports:
2323
gdalraster (>= 2.4.0.9030),
2424
grDevices,
2525
methods,
26-
spatstat.geom
26+
spatstat.geom,
27+
stats
2728
Suggests:
2829
glue (>= 1.6.0),
2930
spatstat.explore,

R/compute_ht_metrics.R

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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

Comments
 (0)