Skip to content

Commit accd246

Browse files
committed
plot_crowns() [WIP]
1 parent 8f177d3 commit accd246

2 files changed

Lines changed: 139 additions & 12 deletions

File tree

R/plot_crowns.R

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,72 @@
1+
#' Display modeled tree crowns vertically projected on subplot boundaries
2+
#'
3+
#' `plot_crowns()` draws vertically projected tree crowns as discs overlaid on
4+
#' subplot or microplot boundaries. The full four-subplot cluster, or
5+
#' individual subplots, can be displayed with trees `>= 5.0` in. (`12.7` cm)
6+
#' diameter, or individual microplots can be display with saplings (i.e., trees
7+
#' `< 5` in. diameter).
8+
#'
9+
#' @param tree_list A data frame with tree records for one FIA plot. Must have
10+
#' columns `SUBP` (FIA subplot number), `STATUSCD` (FIA integer tree status,
11+
#' `1` = live), `DIA` (tree diameter), `HT` (tree height), `ACTUALHT` (tree
12+
#' actual height, `ACTUALHT < HT` indicating a broken top), `DIST` (stem
13+
#' distance from subplot/microplot center), `AZIMUTH` (horizontal angle from
14+
#' subplot/microplot center to the stem location, in the range `0:359`), and
15+
#' `CRWIDTH` (tree crown width).
16+
#' @param subplot Optional integer subplot number in the range `1:4` indicating
17+
#' a specific subplot for display. May be `NULL` or `NA` to display the full
18+
#' four-point cluster.
19+
#' @param microplot A logical value, `TRUE` to display the modeled crowns of
20+
#' saplings overlaid of the microplot boundary of `subplot = n`. The default is
21+
#' `FALSE`. Ignored if `subplot` is not specified.
22+
#' @param linear_unit An optional character string specifying the linear
23+
#' distance unit. Defaults to the native FIA unit of `"ft"`, but may be set to
24+
#' `"m"` instead (or `"meter"` / `"metre"`), in which case subplot boundaries
25+
#' will be display in meters, tree heights and crown widths are assumed to be
26+
#' given in meters, and tree diameters are assumed to be given in centimeters.
27+
#' **TODO: not currently implemented**
28+
#' @param main Character string giving the main plot title (on top).
29+
#' @return
30+
#' The input, invisibly.
31+
#'
32+
#' @examples
33+
#' trees <- within(plantation, CRWIDTH <- predict_crwidth(plantation))
34+
#'
35+
#' plot_crowns(trees, main = "plantation plot")
36+
#'
37+
#' plot_crowns(trees, subplot = 4, main = "plantation subplot 4")
38+
#'
39+
#' plot_crowns(trees, subplot = 4, microplot = TRUE,
40+
#' main = "plantation microplot 4")
141
#' @export
242
plot_crowns <- function(tree_list, subplot = NULL, microplot = FALSE,
343
linear_unit = "ft", main = "") {
444

45+
if (is.null(linear_unit))
46+
linear_unit <- "ft"
47+
else if (!(is.character(linear_unit) && length(linear_unit) == 1))
48+
stop("'linear_unit' must be a single character string", call. = FALSE)
49+
else
50+
linear_unit <- tolower(linear_unit)
51+
52+
if (!(linear_unit %in% c("ft", "foot", "m", "meter", "metre")))
53+
stop("'linear_unit' is invalid", call. = FALSE)
54+
555
if (is.null(subplot) || is.na(subplot))
656
subplot <- FALSE
757

58+
if (subplot) {
59+
if (!is.numeric(subplot) && subplot %in% 1:4)
60+
stop("'subplot' must be a numeric value in the range 1:4",
61+
call. = FALSE)
62+
}
63+
64+
if (is.null(microplot) || is.na(microplot))
65+
microplot <- FALSE
66+
67+
if (!(is.logical(microplot) && length(microplot) == 1))
68+
stop("'microplot' must be a single logical value", call. = FALSE)
69+
870
if (subplot && microplot) {
971
trees_in <- tree_list[tree_list$STATUSCD == 1 &
1072
tree_list$SUBP == subplot &
@@ -24,31 +86,33 @@ plot_crowns <- function(tree_list, subplot = NULL, microplot = FALSE,
2486
if (subplot) {
2587
pts <- vector(mode = "list", length = 2)
2688
names(pts) <- c("x", "y")
27-
pts$x <- tree_list$DIST * sin(tree_list$AZIMUTH * (pi / 180))
28-
pts$y <- tree_list$DIST * cos(tree_list$AZIMUTH * (pi / 180))
89+
pts$x <- trees_in$DIST * sin(trees_in$AZIMUTH * (pi / 180))
90+
pts$y <- trees_in$DIST * cos(trees_in$AZIMUTH * (pi / 180))
2991
} else {
3092
pts <- .get_tree_list_xy(trees_in)
3193
}
3294

3395
crowns <- lapply(seq_len(nrow(trees_in)), \(i) {
3496
gdalraster::g_create("POINT", c(pts$x[i], pts$y[i])) |>
35-
gdalraster::g_buffer(trees_in$CRWIDTH[i] / 2)
36-
})
37-
38-
rct <- as.list(gdalraster::g_build_collection(crowns) |>
39-
gdalraster::g_envelope())
40-
names(rct) <- c("xmin", "xmax", "ymin", "ymax")
97+
gdalraster::g_buffer(trees_in$CRWIDTH[i] / 2)})
4198

4299
if (subplot) {
43-
fia_poly <- gdalraster::g_buffer("POINT (0 0)", 24)
100+
if (microplot) {
101+
fia_poly <- gdalraster::g_buffer("POINT (0 0)", 6.8)
102+
} else {
103+
fia_poly <- gdalraster::g_buffer("POINT (0 0)", 24)
104+
}
44105
stems <- lapply(seq_len(nrow(trees_in)), \(i) {
45106
gdalraster::g_create("POINT", c(pts$x[i], pts$y[i])) |>
46-
gdalraster::g_buffer(trees_in$dia_ft[i] / 2)
47-
})
107+
gdalraster::g_buffer(trees_in$dia_ft[i] / 2)})
48108
} else {
49109
fia_poly <- .get_fia_plot_geom(linear_unit = linear_unit)
50110
}
51111

112+
rct <- as.list(gdalraster::g_build_collection(c(crowns, list(fia_poly))) |>
113+
gdalraster::g_envelope())
114+
names(rct) <- c("xmin", "xmax", "ymin", "ymax")
115+
52116
xlab <- paste0("x (", linear_unit, ")")
53117
ylab <- paste0("y (", linear_unit, ")")
54118
gdalraster::plot_geom(fia_poly, xlab, ylab, main, border = "gray62",
@@ -64,5 +128,8 @@ plot_crowns <- function(tree_list, subplot = NULL, microplot = FALSE,
64128
}
65129
}
66130

67-
invisible(NULL)
131+
border_col <- grDevices::adjustcolor("gray62", alpha.f = 0.2)
132+
gdalraster::plot_geom(fia_poly, border = border_col, lwd = 3, add = TRUE)
133+
134+
invisible(tree_list)
68135
}

man/plot_crowns.Rd

Lines changed: 60 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)