Skip to content

Commit 8ae6a5d

Browse files
committed
add process_tree_data()
1 parent 3abdfe2 commit 8ae6a5d

10 files changed

Lines changed: 282 additions & 56 deletions

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ export(create_fia_owin)
1111
export(create_fia_ppp)
1212
export(load_tree_data)
1313
export(plot_crowns)
14+
export(process_tree_data)

R/calc_crwidth.R

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919
#' equation presented by Gill et al. (2000) to estimate crown width for nine
2020
#' tree species when their diameter is greater than 50 in. (127 cm).
2121
#'
22-
#' @param tree_list A data frame containing tree records. Must have columns
22+
#' @param tree_table A data frame containing tree records. Must have columns
2323
#' `SPCD` (FIA integer species code), `STATUSCD` (FIA integer tree status code,
2424
#' 1 = live) and `DIA` (FIA tree diameter in inches).
2525
#' @param digits Optional integer indicating the number of digits to keep in the
2626
#' return values (defaults to `1`).
2727
#' @return
28-
#' A numeric vector of length `nrow(tree_list)` with predicted crown width in
28+
#' A numeric vector of length `nrow(tree_table)` with predicted crown width in
2929
#' feet for live trees. `NA` is returned for trees with `STATUSCD != 1`.
3030
#'
3131
#' @references
@@ -47,87 +47,87 @@
4747
#' @examples
4848
#' calc_crwidth(plantation)
4949
#' @export
50-
calc_crwidth <- function(tree_list, digits = 1) {
51-
if (missing(tree_list) || is.null(tree_list))
52-
stop("'tree_list' is required", call. = FALSE)
50+
calc_crwidth <- function(tree_table, digits = 1) {
51+
if (missing(tree_table) || is.null(tree_table))
52+
stop("'tree_table' is required", call. = FALSE)
5353

54-
if (!is.data.frame(tree_list))
55-
stop("'tree_list' must be a data frame", call. = FALSE)
54+
if (!is.data.frame(tree_table))
55+
stop("'tree_table' must be a data frame", call. = FALSE)
5656

5757
required_cols <- c("SPCD", "STATUSCD", "DIA")
58-
if (!all(required_cols %in% colnames(tree_list)))
59-
stop("'tree_list' is missing required columns", call. = FALSE)
58+
if (!all(required_cols %in% colnames(tree_table)))
59+
stop("'tree_table' is missing required columns", call. = FALSE)
6060

61-
if (!is.numeric(tree_list$SPCD))
62-
stop("'tree_list$SPCD' must be numeric or integer", call. = FALSE)
63-
if (any(is.na(tree_list$SPCD)))
64-
stop("'tree_list$SPCD' cannot have missing values", call. = FALSE)
61+
if (!is.numeric(tree_table$SPCD))
62+
stop("'SPCD' must be numeric or integer", call. = FALSE)
63+
if (any(is.na(tree_table$SPCD)))
64+
stop("'SPCD' cannot have missing values", call. = FALSE)
6565

66-
if (!is.numeric(tree_list$STATUSCD))
67-
stop("'tree_list$STATUSCD' must be numeric or integer", call. = FALSE)
68-
if (any(is.na(tree_list$STATUSCD)))
69-
stop("'tree_list$STATUSCD' cannot have missing values", call. = FALSE)
66+
if (!is.numeric(tree_table$STATUSCD))
67+
stop("'STATUSCD' must be numeric or integer", call. = FALSE)
68+
if (any(is.na(tree_table$STATUSCD)))
69+
stop("'STATUSCD' cannot have missing values", call. = FALSE)
7070

71-
if (!is.numeric(tree_list$DIA))
72-
stop("'tree_list$DIA' must be numeric", call. = FALSE)
73-
if (any(is.na(tree_list$DIA)))
74-
stop("'tree_list$DIA' cannot have missing values", call. = FALSE)
71+
if (!is.numeric(tree_table$DIA))
72+
stop("'DIA' must be numeric", call. = FALSE)
73+
if (any(is.na(tree_table$DIA[tree_table$STATUSCD == 1])))
74+
stop("'DIA' has missing values for live trees", call. = FALSE)
7575

7676
if (is.null(digits))
7777
digits <- 1
7878

79-
cw <- rep_len(NA_real_, nrow(tree_list))
79+
cw <- rep_len(NA_real_, nrow(tree_table))
8080

8181
# define a default equation to use in case a species-specific one is missing
8282
# SPCD == 807, blue oak
8383
b_default <- cw_coef[cw_coef$SPCD == 807, c("b0", "b1", "b2")]
8484

8585
# special case for large trees of certain species in the PNW region:
8686
# use the "old growth" equation from Gill et al. (2000)
87-
old_growth_trees <- tree_list$DIA > 50 & tree_list$STATUSCD == 1 &
88-
tree_list$SPCD %in% c(11, 98, 108, 119, 122, 202, 242, 263, 264)
87+
old_growth_trees <- tree_table$DIA > 50 & tree_table$STATUSCD == 1 &
88+
tree_table$SPCD %in% c(11, 98, 108, 119, 122, 202, 242, 263, 264)
8989

90-
cw[old_growth_trees] <- 16.449 + 0.4067 * tree_list$DIA[old_growth_trees]
90+
cw[old_growth_trees] <- 16.449 + 0.4067 * tree_table$DIA[old_growth_trees]
9191

9292
# apply species-specific equations
9393
# NB: crwidth of trees with DIA < 5 in. (i.e. "saplings") is predicted for
9494
# DIA = 5 and then sapling crwidth adjustment factors are applied afterward
95-
for (spcd in unique(tree_list$SPCD)) {
95+
for (spcd in unique(tree_table$SPCD)) {
9696
b <- cw_coef[cw_coef$SPCD == spcd, c("b0", "b1", "b2")]
9797
if (nrow(b) == 0)
9898
b <- b_default
9999

100100
this_subset <-
101-
tree_list$SPCD == spcd & tree_list$STATUSCD == 1 & is.na(cw)
101+
tree_table$SPCD == spcd & tree_table$STATUSCD == 1 & is.na(cw)
102102

103103
cw[this_subset] <-
104-
b$b0 + b$b1 * pmax(5, tree_list$DIA[this_subset]) +
105-
b$b2 * pmax(5, tree_list$DIA[this_subset])^2
104+
b$b0 + b$b1 * pmax(5, tree_table$DIA[this_subset]) +
105+
b$b2 * pmax(5, tree_table$DIA[this_subset])^2
106106
}
107107

108108
# apply sapling crown width adjustment factors
109-
saplings <- tree_list$DIA < 5 & tree_list$STATUSCD == 1
110-
sapling_spp <- unique(tree_list$SPCD[saplings])
109+
saplings <- tree_table$DIA < 5 & tree_table$STATUSCD == 1
110+
sapling_spp <- unique(tree_table$SPCD[saplings])
111111
# species-specific adjustment factors if any (based on Bragg 2001)
112112
spcd_adj <- intersect(sapling_spp, cw_sapling_adj$SPCD)
113113
for (spcd in spcd_adj) {
114114
rowid <- which(cw_sapling_adj$SPCD == spcd)
115115
# adjustment factors at 1, 2, 3, 4, 5 inches DIA:
116116
adj_factors <- c(as.numeric(cw_sapling_adj[rowid, 2:5]), 1)
117-
this_subset <- saplings & tree_list$SPCD == spcd
117+
this_subset <- saplings & tree_table$SPCD == spcd
118118
# interpolated adjustment factors for the actual sapling diameters:
119-
n <- trunc(tree_list$DIA[this_subset])
120-
cw_adj <- (tree_list$DIA[this_subset] - n) *
119+
n <- trunc(tree_table$DIA[this_subset])
120+
cw_adj <- (tree_table$DIA[this_subset] - n) *
121121
(adj_factors[n + 1] - adj_factors[n]) + adj_factors[n]
122122
cw[this_subset] <- cw[this_subset] * cw_adj
123123
}
124124
# otherwise use avarage adjustment factors based on Bragg (2001) data
125125
# average adjustment factors at 1, 2, 3, 4, 5 inches DIA:
126126
adj_factors <- c(0.509, 0.644, 0.767, 0.885, 1.0)
127-
this_subset <- saplings & !(tree_list$SPCD %in% cw_sapling_adj$SPCD)
127+
this_subset <- saplings & !(tree_table$SPCD %in% cw_sapling_adj$SPCD)
128128
# interpolated adjustment factors for the actual sapling diameters:
129-
n <- trunc(tree_list$DIA[this_subset])
130-
cw_adj <- (tree_list$DIA[this_subset] - n) *
129+
n <- trunc(tree_table$DIA[this_subset])
130+
cw_adj <- (tree_table$DIA[this_subset] - n) *
131131
(adj_factors[n + 1] - adj_factors[n]) + adj_factors[n]
132132
cw[this_subset] <- cw[this_subset] * cw_adj
133133

R/calc_tcc_metrics.R

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@
9797
#' @param digits Optional integer indicating the number of digits to keep in the
9898
#' return values (defaults to `1`). May be passed to `calc_crwidth()` and
9999
#' `calc_ht_metrics()`.
100+
#' @param ... Optional arguments passed to `create_fia_ppp()` if
101+
#' `stem_map = TRUE`.
100102
#' @return
101103
#' If `full_output = TRUE`, a named list with element `model_tcc` containing
102104
#' the plot-level predicted tree canopy cover as percent (`0:100`), and
@@ -142,7 +144,7 @@
142144
#' calc_tcc_metrics(plantation, stem_map = FALSE, full_output = FALSE)
143145
#' @export
144146
calc_tcc_metrics <- function(tree_list, stem_map = TRUE, full_output = TRUE,
145-
digits = 1) {
147+
digits = 1, ...) {
146148

147149
if (!(is.logical(stem_map) && length(stem_map) == 1))
148150
stop("'stem_map' must be a single logical value", call. = FALSE)
@@ -155,7 +157,7 @@ calc_tcc_metrics <- function(tree_list, stem_map = TRUE, full_output = TRUE,
155157
# validate the input tree list for stem-mapping and get an estimate
156158
# of the L-function (square root transform of Ripley's K)
157159
# r = 0:12 feet
158-
L <- create_fia_ppp(tree_list) |>
160+
L <- create_fia_ppp(tree_list, ...) |>
159161
spatstat.explore::Lest(r = 0:12, correction = "isotropic")
160162

161163
# mean of L at r = 6, 8, 10, 12 ft (Ripley's isotropic edge corrected)

R/process_tree_data.R

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#' Generate plot-level stand structure metrics for a tree dataset
2+
#'
3+
#' `process_tree_data()` takes a table of tree records for a set of forest
4+
#' inventory plots as input, and generates selected plot-level stand structure
5+
#' metrics.
6+
#'
7+
#' @param tree_table A data frame containing tree records for a set of forest
8+
#' inventory plots. Must have column `PLT_CN` containing the plot unique
9+
#' identifier for each tree. Other required columns are those of
10+
#' `calc_crwidth()` (if column `CRWIDTH` is not included), `calc_ht_metrics()`
11+
#' and `calc_tcc_metrics()`, depending on values given for `stem_map` and
12+
#' `full_output`.
13+
#' @param stem_map A logical value indicating whether to map individual tree
14+
#' stems explicitly, using coordinates specified in terms of distance and
15+
#' azimuth from subplot/microplot centers. The default is `TRUE`, in which case
16+
#' the input `tree_table` must contain columns `"DIST"` and `"AZIMUTH"`. This
17+
#' argument may be set to `FALSE` if individual tree locations are not
18+
#' available, in which case TCC will be predicted assuming a random arrangement
19+
#' of tree locations (see Details for [calc_tcc_metrics()]).
20+
#' @param full_output A logical value indicating whether to include the full set
21+
#' of components used to derive the plot-level TCC prediction. By default, the
22+
#' output data includes subplot-level TCC estimates, live tree and sapling
23+
#' counts, stand height metrics, and point pattern statistics, depending on the
24+
#' value given for `stem_map` (see Details for [calc_tcc_metrics()]).
25+
#' @param digits Optional integer indicating the number of digits to keep in the
26+
#' return values (defaults to `1`). May be passed to `calc_crwidth()` and
27+
#' `calc_ht_metrics()`.
28+
#' @return
29+
#' A data frame with one row for each unique `PLT_CN` in the input `tree_table`,
30+
#' and additional columns containing the output of `calc_tcc_metrics()`
31+
#' conditional on the values given for `stem_map` and `full_output`.
32+
#'
33+
#' @seealso
34+
#' [calc_ht_metrics()], [calc_tcc_metrics()], [load_tree_data()]
35+
#'
36+
#' @examples
37+
#' # Lolo NF, single-condition forest plots, INVYR 2022, from public FIADB
38+
#' f <- system.file("extdata/mt_lnf_2022_1cond_tree.csv", package="FIAstemmap")
39+
#' tree_table <- load_tree_data(f)
40+
#'
41+
#' process_tree_data(tree_table, stem_map = FALSE, full_output = TRUE)
42+
#' @export
43+
process_tree_data <- function(tree_table, stem_map = TRUE, full_output = TRUE,
44+
digits = 1) {
45+
46+
if (missing(tree_table) || is.null(tree_table))
47+
stop("'tree_table' is required", call. = FALSE)
48+
49+
if (!is.data.frame(tree_table))
50+
stop("'tree_table' must be a data frame", call. = FALSE)
51+
52+
if (!("PLT_CN" %in% colnames(tree_table))) {
53+
stop("'tree_table' must have column 'PLT_CN' with unique plot IDs",
54+
call. = FALSE)
55+
}
56+
57+
if (!(is.logical(stem_map) && length(stem_map) == 1))
58+
stop("'stem_map' must be a single logical value", call. = FALSE)
59+
60+
if (!(is.logical(full_output) && length(full_output) == 1))
61+
stop("'full_output' must be a single logical value", call. = FALSE)
62+
63+
if (is.null(digits))
64+
digits <- 1
65+
66+
plot_id_dt <- storage.mode(tree_table$PLT_CN)
67+
if (!(plot_id_dt %in% c("character", "numeric", "integer", "integer64"))) {
68+
stop("'PLT_CN' must be character, numeric, integer or integer64",
69+
call. = FALSE)
70+
}
71+
plot_ids <- unique(tree_table$PLT_CN)
72+
num_plots <- length(plot_ids)
73+
74+
# avoid creating a new owin on every call, pass through to create_fia_ppp()
75+
w <- create_fia_owin()
76+
77+
# get the output for one plot, this validates input columns and defines
78+
# the output data structure
79+
tree_list <- tree_table[tree_table$PLT_CN == plot_ids[1], ]
80+
x <- calc_tcc_metrics(tree_list, stem_map, full_output, digits, window = w)
81+
82+
out <- vector("list", 1 + length(x))
83+
names(out) <- c("PLT_CN", names(x))
84+
85+
if (plot_id_dt == "character")
86+
out$PLT_CN <- character(num_plots)
87+
else if (plot_id_dt == "numeric")
88+
out$PLT_CN <- rep(NA_real_, num_plots)
89+
else if (plot_id_dt == "integer")
90+
out$PLT_CN <- rep(NA_integer_, num_plots)
91+
else if (plot_id_dt == "integer64")
92+
out$PLT_CN <- rep(bit64::NA_integer64_, num_plots)
93+
94+
for (j in 2:length(out)) {
95+
if (storage.mode(x[[j - 1]]) == "integer")
96+
out[[j]] <- rep(NA_integer_, num_plots)
97+
else
98+
out[[j]] <- rep(NA_real_, num_plots)
99+
}
100+
101+
cli::cli_progress_bar("Processing tree data", total = num_plots)
102+
for (i in seq_along(plot_ids)) {
103+
tree_list <- tree_table[tree_table$PLT_CN == plot_ids[i], ]
104+
x <- calc_tcc_metrics(tree_list, stem_map, full_output, digits,
105+
window = w)
106+
out$PLT_CN[i] <- plot_ids[i]
107+
for (j in 2:length(out)) {
108+
out[[j]][i] <- x[[j - 1]]
109+
}
110+
cli::cli_progress_update()
111+
}
112+
cli::cli_progress_done()
113+
114+
as.data.frame(out)
115+
}

README.Rmd

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,14 @@ calc_tcc_metrics(plantation, stem_map = FALSE, full_output = FALSE)
129129
### Data processing
130130

131131
```{r data-proc}
132-
## load tree data from a file or database connection
132+
# load tree data from a file or database connection
133+
# Lolo NF, single-condition forest plots, INVYR 2022, from public FIADB
133134
f <- system.file("extdata/mt_lnf_2022_1cond_tree.csv", package="FIAstemmap")
134-
tree <- load_tree_data(f)
135+
tree_table <- load_tree_data(f)
135136
136-
head(tree)
137+
head(tree_table)
137138
138-
## process tree data
139-
140-
# TODO...
139+
process_tree_data(tree_table, stem_map = FALSE, full_output = TRUE)
141140
```
142141

143142
## References

README.md

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -294,16 +294,17 @@ calc_tcc_metrics(plantation, stem_map = FALSE, full_output = FALSE)
294294
### Data processing
295295

296296
``` r
297-
## load tree data from a file or database connection
297+
# load tree data from a file or database connection
298+
# Lolo NF, single-condition forest plots, INVYR 2022, from public FIADB
298299
f <- system.file("extdata/mt_lnf_2022_1cond_tree.csv", package="FIAstemmap")
299-
tree <- load_tree_data(f)
300+
tree_table <- load_tree_data(f)
300301
#> ! The data source does not have DIST and/or AZIMUTH
301302
#> ℹ Fetching tree data...
302-
#> ✔ Fetching tree data... [15ms]
303+
#> ✔ Fetching tree data... [14ms]
303304
#>
304305
#> ℹ 910 tree records returned
305306

306-
head(tree)
307+
head(tree_table)
307308
#> PLT_CN SUBP TREE STATUSCD SPCD DIA HT ACTUALHT CCLCD TPA_UNADJ
308309
#> 1 670951075126144 1 1 2 108 NA NA NA NA NA
309310
#> 2 670951075126144 1 2 1 108 1 9 9 3 74.96528
@@ -312,9 +313,53 @@ head(tree)
312313
#> 5 670951075126144 2 3 2 108 NA NA NA NA NA
313314
#> 6 670951075126144 2 4 2 108 NA NA NA NA NA
314315

315-
## process tree data
316-
317-
# TODO...
316+
process_tree_data(tree_table, stem_map = FALSE, full_output = TRUE)
317+
#> PLT_CN model_tcc numTrees meanTreeHt meanTreeHtBAW meanTreeHtDom
318+
#> 1 670951075126144 1.2 0 0.0 0.0 0.0
319+
#> 2 670950940126144 38.4 24 61.4 66.4 64.5
320+
#> 3 670950992126144 3.4 1 43.0 43.0 43.0
321+
#> 4 670950609126144 17.2 4 102.2 102.6 102.2
322+
#> 5 670950600126144 34.9 16 62.1 79.0 69.9
323+
#> 6 670951118126144 20.6 9 24.6 28.0 30.0
324+
#> 7 670950964126144 37.9 16 58.8 67.1 64.1
325+
#> 8 670951031126144 51.2 29 70.2 72.8 72.4
326+
#> 9 670950608126144 70.8 32 73.7 94.8 86.6
327+
#> 10 670950599126144 66.4 44 61.8 66.4 64.1
328+
#> 11 670950967126144 57.4 23 86.0 100.7 96.1
329+
#> 12 670950732126144 34.4 12 64.3 91.8 72.6
330+
#> 13 670950725126144 66.5 69 66.3 87.0 73.9
331+
#> 14 670950598126144 55.8 20 65.7 89.6 83.9
332+
#> 15 670950965126144 81.3 74 53.1 55.1 54.5
333+
#> 16 670951032126144 32.5 5 15.0 14.2 15.0
334+
#> 17 670951034126144 16.4 7 40.7 45.0 40.7
335+
#> 18 670950625126144 44.5 23 42.0 61.9 42.9
336+
#> 19 670951029126144 55.1 33 64.7 68.5 64.7
337+
#> 20 670951035126144 97.6 54 44.9 50.6 45.7
338+
#> 21 670951089126144 21.1 7 79.9 83.0 79.9
339+
#> 22 670951152126144 5.3 3 21.3 21.7 21.3
340+
#> meanTreeHtDomBAW maxTreeHt predomTreeHt numSaplings meanSapHt maxSapHt
341+
#> 1 0.0 0 0.0 1 9.0 9
342+
#> 2 67.9 85 81.7 1 16.0 16
343+
#> 3 43.0 43 43.0 0 0.0 0
344+
#> 4 102.6 114 106.3 0 0.0 0
345+
#> 5 83.4 104 99.3 0 0.0 0
346+
#> 6 34.8 47 39.7 0 0.0 0
347+
#> 7 69.0 80 78.0 0 0.0 0
348+
#> 8 73.6 85 83.0 0 0.0 0
349+
#> 9 98.3 120 112.7 19 15.8 38
350+
#> 10 67.2 84 81.7 1 14.0 14
351+
#> 11 103.7 123 117.0 2 13.0 16
352+
#> 12 94.2 109 93.7 0 0.0 0
353+
#> 13 89.6 118 116.3 2 12.5 16
354+
#> 14 97.3 128 112.0 5 11.4 18
355+
#> 15 56.1 72 67.0 3 40.3 45
356+
#> 16 14.2 22 18.3 15 12.3 20
357+
#> 17 45.0 53 48.3 0 0.0 0
358+
#> 18 63.0 104 70.0 2 20.5 25
359+
#> 19 68.5 87 83.3 3 19.0 22
360+
#> 20 51.2 74 66.0 27 23.5 39
361+
#> 21 83.0 92 85.3 0 0.0 0
362+
#> 22 21.7 24 21.3 1 14.0 14
318363
```
319364

320365
## References

_pkgdown.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ reference:
2121
- spatstat_helpers
2222
- load_tree_data
2323
- plot_crowns
24+
- process_tree_data
2425

2526
- title: Data
2627
- contents:

0 commit comments

Comments
 (0)