|
2 | 2 | #' |
3 | 3 | #' Plot the bucking outcome |
4 | 4 | #' |
5 | | -#' @param Bucking output structure of getBucking(), buckStem() or buckHpr() |
| 5 | +#' @param Res output structure for single stem of getBucking(), buckStem() or buckHpr() |
6 | 6 | #' @param StemProfile StemProfile (getStemprofile()) |
7 | | -#' @param Key StemKey of the stem to be plotted |
| 7 | +#' @param StemKey StemKey of the stem to be plotted |
| 8 | +#' @param ProductData Product definition table from getProductData(), |
| 9 | +#' used to map \code{ProductKey} to \code{ProductName}. |
8 | 10 | #' @return plot of bucking outcome |
9 | 11 | #' @author Lennart Noordermeer \email{lennart.noordermeer@nmbu.no} |
10 | 12 | #' @export |
11 | | -plotBucking=function(Res, StemProfile, Stem, ProductData){ |
12 | | - require(ggplot2);require(plyr);require(RColorBrewer) |
13 | | - tab=Res[Res$StemKey==Stem,] |
14 | | - tre=StemProfile[StemProfile$StemKey==paste(Stem),] |
15 | | - h = tre$diameterPosition |
16 | | - plotdf=c() |
17 | | - i=1 |
18 | | - for (i in 1:nrow(tab)){ |
19 | | - log = tre[which(tre$diameterPosition == round_any(tab$StartPos[i], 10)):which(tre$diameterPosition ==round_any(tab$StopPos[i], |
20 | | - 10)), ] |
21 | | - log = cbind(log, unique(ProductData$ProductName[which(ProductData$ProductKey==tab$ProductKey[i])]) ) %>% as.data.frame() |
22 | | - names(log)[ncol(log)]="ProductName" |
23 | | - D_Bob = max(log$DiameterValue)/2 |
24 | | - D_Mob = median(log$DiameterValue)/2 |
25 | | - D_Tob = min(log$DiameterValue)/2 |
26 | | - H_B = min(log$diameterPosition) |
27 | | - H_M = median(log$diameterPosition) |
28 | | - H_T = max(log$diameterPosition) |
29 | | - log=data.frame(log=i, |
30 | | - diam=c(D_Bob, D_Mob, D_Tob, -D_Tob, -D_Mob, -D_Bob, D_Bob), |
31 | | - diameterPosition=c(H_B, H_M, H_T, H_T, H_M, H_B, H_B), |
32 | | - ProductName=unique(log$ProductName)) |
33 | | - plotdf=rbind(plotdf,log) |
| 13 | +plotBucking <- function(Res, StemProfile, StemKey, ProductData) { |
| 14 | + stopifnot(requireNamespace("ggplot2", quietly = TRUE)) |
| 15 | + stopifnot(requireNamespace("data.table", quietly = TRUE)) |
| 16 | + stopifnot(requireNamespace("RColorBrewer", quietly = TRUE)) |
| 17 | + |
| 18 | + round_any <- function(x, accuracy, f = round) f(x / accuracy) * accuracy |
| 19 | + |
| 20 | + Res <- data.table::as.data.table(Res) |
| 21 | + StemProfile <- data.table::as.data.table(StemProfile) |
| 22 | + ProductData <- data.table::as.data.table(ProductData) |
| 23 | + |
| 24 | + sk <- as.integer(StemKey) |
| 25 | + |
| 26 | + tab <- Res[StemKey %in% sk] |
| 27 | + if (nrow(tab) == 0) stop("StemKey not found in Res.") |
| 28 | + |
| 29 | + tre <- StemProfile[StemKey %in% sk] |
| 30 | + if (nrow(tre) == 0) stop("StemKey not found in StemProfile.") |
| 31 | + |
| 32 | + prod_map <- unique(ProductData[, .( |
| 33 | + ProductKey = as.integer(ProductKey), |
| 34 | + ProductName = as.character(ProductName) |
| 35 | + )]) |
| 36 | + |
| 37 | + tab <- merge(tab, prod_map, by = "ProductKey", all.x = TRUE) |
| 38 | + tab[is.na(ProductName), ProductName := "Unknown"] |
| 39 | + |
| 40 | + plot_list <- vector("list", nrow(tab)) |
| 41 | + |
| 42 | + for (i in seq_len(nrow(tab))) { |
| 43 | + st <- round_any(as.numeric(tab$StartPos[i]), 10) |
| 44 | + en <- round_any(as.numeric(tab$StopPos[i]), 10) |
| 45 | + |
| 46 | + seg <- tre[diameterPosition >= st & diameterPosition <= en] |
| 47 | + if (nrow(seg) == 0) next |
| 48 | + |
| 49 | + D_Bob <- max(seg$DiameterValue) / 2 |
| 50 | + D_Mob <- stats::median(seg$DiameterValue) / 2 |
| 51 | + D_Tob <- min(seg$DiameterValue) / 2 |
| 52 | + |
| 53 | + H_B <- min(seg$diameterPosition) |
| 54 | + H_M <- stats::median(seg$diameterPosition) |
| 55 | + H_T <- max(seg$diameterPosition) |
| 56 | + |
| 57 | + plot_list[[i]] <- data.table::data.table( |
| 58 | + log = i, |
| 59 | + diam = c(D_Bob, D_Mob, D_Tob, -D_Tob, -D_Mob, -D_Bob, D_Bob), |
| 60 | + diameterPosition = c(H_B, H_M, H_T, H_T, H_M, H_B, H_B), |
| 61 | + ProductName = tab$ProductName[i] |
| 62 | + ) |
34 | 63 | } |
35 | | - ProductData$ProductName=factor( |
36 | | - ProductData$ProductName, levels=unique(ProductData$ProductName)) |
37 | | - plotdf$ProductName=factor(plotdf$ProductName, |
38 | | - levels=unique(ProductData$ProductName)) |
39 | | - colors=brewer.pal(length(unique(ProductData$ProductName)),"Spectral") |
40 | | - colors=colors[unique(ProductData$ProductName)%in%unique(plotdf$ProductName)] |
41 | | - colors = c("#3288BD", "#F46D43" ,"#E6F598") |
42 | | - ticks=seq(0,round_any(max(plotdf$diameterPosition),100),by=200) |
43 | | - lim=c(0,round_any(max(tre$diameterPosition),200,f = ceiling)) |
44 | | - plot=ggplot(plotdf, aes(x=diam,y=diameterPosition,group=log)) + |
45 | | - geom_polygon(aes(fill = ProductName),color="black")+ |
46 | | - theme(panel.grid.major = element_blank(), |
47 | | - panel.grid.minor = element_blank(), |
48 | | - panel.background = element_blank(), |
49 | | - legend.position="bottom", |
50 | | - axis.text.y=element_blank(), |
51 | | - axis.ticks.y = element_blank(), |
52 | | - legend.title = element_blank(), |
53 | | - aspect.ratio = .1)+ |
54 | | - scale_y_continuous(limits=lim,breaks = ticks)+ |
55 | | - xlab("")+ |
56 | | - ylab("Diameter position (cm)")+ |
57 | | - scale_fill_manual(values=colors)+ |
58 | | - coord_flip()+ |
59 | | - ggtitle(paste("Stem value:", round(max(tab$CumulativeValue)))) |
60 | | - plot |
61 | | - return(plot) |
| 64 | + |
| 65 | + plotdf <- data.table::rbindlist(plot_list, fill = TRUE) |
| 66 | + if (nrow(plotdf) == 0) stop("No segments could be plotted (check StartPos/StopPos vs StemProfile diameterPosition).") |
| 67 | + |
| 68 | + prod_levels <- unique(as.character(ProductData$ProductName)) |
| 69 | + plotdf[, ProductName := factor(ProductName, levels = prod_levels)] |
| 70 | + |
| 71 | + n_prod <- length(levels(plotdf$ProductName)) |
| 72 | + pal <- RColorBrewer::brewer.pal(min(max(n_prod, 3), 11), "Spectral") |
| 73 | + names(pal) <- levels(plotdf$ProductName) |
| 74 | + |
| 75 | + ticks <- seq(0, round_any(max(tre$diameterPosition), 100, ceiling), by = 200) |
| 76 | + lim <- c(0, round_any(max(tre$diameterPosition), 200, ceiling)) |
| 77 | + |
| 78 | + stem_value <- if ("CumulativeValue" %in% names(tab)) max(tab$CumulativeValue, na.rm = TRUE) else NA_real_ |
| 79 | + |
| 80 | + ggplot2::ggplot(plotdf, ggplot2::aes(x = diam, y = diameterPosition, group = log)) + |
| 81 | + ggplot2::geom_polygon(ggplot2::aes(fill = ProductName), colour = "black") + |
| 82 | + ggplot2::coord_flip() + |
| 83 | + ggplot2::scale_y_continuous(limits = lim, breaks = ticks) + |
| 84 | + ggplot2::scale_fill_manual(values = pal, drop = FALSE) + |
| 85 | + ggplot2::theme( |
| 86 | + panel.grid.major = ggplot2::element_blank(), |
| 87 | + panel.grid.minor = ggplot2::element_blank(), |
| 88 | + panel.background = ggplot2::element_blank(), |
| 89 | + legend.position = "bottom", |
| 90 | + legend.title = ggplot2::element_blank(), |
| 91 | + axis.text.y = ggplot2::element_blank(), |
| 92 | + axis.ticks.y = ggplot2::element_blank(), |
| 93 | + aspect.ratio = 0.1 |
| 94 | + ) + |
| 95 | + ggplot2::xlab("") + |
| 96 | + ggplot2::ylab("Diameter position (cm)") + |
| 97 | + ggplot2::ggtitle( |
| 98 | + if (is.finite(stem_value)) { |
| 99 | + paste0("StemKey: ", sk, " | Stem value: ", round(stem_value, 2)) |
| 100 | + } else { |
| 101 | + paste0("StemKey: ", sk) |
| 102 | + } |
| 103 | + ) |
62 | 104 | } |
0 commit comments