Nothing
#' Summarise and display tree information by subplot
#'
#' @description
#' After applying the [divide_plot()] function, this function summarises with any defined function the desired tree metric (including AGB simulations calculated by the [AGBmonteCarlo()] function) by sub-plot and displays the plot representation.
#'
#' @param subplots output of the [divide_plot()] function
#' @param value a character indicating the column in subplots$tree_data to be summarised (or character vector to summarise several metrics at once)
#' @param AGB_simu a n x m matrix containing individual AGB where n is the number of tree and m is the number of monte carlo simulation. Typically, the output '$AGB_simu' of the AGBmonteCarlo() function.
#' @param draw_plot a logical indicating whether the plot design should be displayed
#' @param per_ha a logical indicating whether the metric summary should be per hectare (or, if summarising several metrics at once: a logical vector corresponding to each metric (see examples))
#' @param fun the function to be applied on tree metric of each subplot (or, if summarising several metrics at once: a list of functions named according to each metric (see examples))
#' @param ref_raster A SpatRaster object from terra package, typically a chm raster created from LiDAR data. Note that in the case of a multiple attributes raster, only the first variable "z" will be summarised.
#' @param raster_fun the function (or a list of functions) to be applied on raster values of each subplot.
#' @param ... optional arguments to fun
#'
#' @return a list containing the following elements:
#' - `tree_summary`: a summary of the metric(s) per subplot
#' - `polygon`: a simple feature collection of the summarised subplot's polygon
#' - `plot_design` : a ggplot object (or a list of ggplot objects) that can easily be modified
#'
#' If 'AGB_simu' is provided, the function also return `$long_AGB_simu`: a data.table containing the resulting AGBD, the extracted raster values (if ref_raster is provided) and the coordinates of the center per subplot and per simulation.
#'
#' @export
#'
#' @author Arthur Bailly
#'
#' @importFrom data.table data.table := set
#' @importFrom stats reshape setNames
#' @importFrom grDevices terrain.colors
#' @importFrom ggplot2 ggplot aes geom_sf theme_minimal scale_fill_gradientn theme ggtitle lims
#' @importFrom terra extract
#'
#' @examples
#' # One plot with repeated measurements of each corner
#' data("NouraguesPlot201")
#' data("NouraguesTrees")
#'
#' check_plot201 <- check_plot_coord(
#' corner_data = NouraguesPlot201,
#' proj_coord = c("Xutm","Yutm"), rel_coord = c("Xfield","Yfield"),
#' trust_GPS_corners = TRUE, draw_plot = FALSE)
#' subplots_201 <- suppressWarnings(
#' divide_plot(
#' corner_data = check_plot201$corner_coord,
#' rel_coord = c("x_rel","y_rel"), proj_coord = c("x_proj","y_proj"),
#' grid_size = 50,
#' tree_data = NouraguesTrees[NouraguesTrees$Plot == 201,],
#' tree_coords = c("Xfield","Yfield")))
#' # Sum summary (by default) of diameter
#' subplots_201_sum <- subplot_summary(subplots_201 , value = "D", draw_plot = FALSE)
#' subplots_201_sum$tree_summary
#' \donttest{
#' subplots_201_sum$plot_design
#' }
#' # 9th quantile summary (for example) of diameter
#' subplots_201_quant <- subplot_summary(subplots_201 , value = "D", draw_plot = FALSE,
#' fun = quantile, probs=0.9)
#'
#'
#' # Dealing with multiple plots and metrics
#' \dontrun{
#' data("NouraguesCoords")
#' nouragues_subplots <- suppressWarnings(
#' divide_plot(
#' corner_data = NouraguesCoords,
#' rel_coord = c("Xfield","Yfield"), proj_coord = c("Xutm","Yutm"),
#' corner_plot_ID = "Plot",
#' grid_size = 50,
#' tree_data = NouraguesTrees, tree_coords = c("Xfield","Yfield"),
#' tree_plot_ID = "Plot"))
#' nouragues_mult <- subplot_summary(nouragues_subplots ,
#' value = c("D","D","x_rel"),
#' fun = list(D=sum,D=mean,x_rel=mean),
#' per_ha = c(T,F,F),
#' draw_plot = FALSE)
#' nouragues_mult$tree_summary
#' nouragues_mult$plot_design$`201`[[1]]
#' nouragues_mult$plot_design$`201`[[2]]
#' nouragues_mult$plot_design$`201`[[3]]
#' }
#'
#' # Dealing with AGB simulations, coordinates uncertainties of corners and a CHM raster
#' \dontrun{
#' NouraguesTrees201 <- NouraguesTrees[NouraguesTrees$Plot == 201,]
#' nouragues_raster <- terra::rast(
#' system.file("extdata", "NouraguesRaster.tif",
#' package = "BIOMASS", mustWork = TRUE)
#' )
#'
#' # Modelling height-diameter relationship
#' HDmodel <- modelHD(D = NouraguesHD$D, H = NouraguesHD$H, method = "log2")
#' # Retrieving wood density values
#' Nouragues201WD <- getWoodDensity(
#' genus = NouraguesTrees201$Genus,
#' species = NouraguesTrees201$Species)
#' # MCMC AGB simulations
#' resultMC <- AGBmonteCarlo(
#' D = NouraguesTrees201$D, Dpropag = "chave2004",
#' WD = Nouragues201WD$meanWD, errWD = Nouragues201WD$sdWD,
#' HDmodel = HDmodel,
#' n = 200
#' )
#' # Dividing plot 201 with coordinates uncertainties
#' nouragues_subplots <- suppressWarnings(
#' divide_plot(
#' corner_data = check_plot201$corner_coord,
#' rel_coord = c("x_rel","y_rel"), proj_coord = c("x_proj","y_proj"),
#' grid_size = 50,
#' tree_data = NouraguesTrees201, tree_coords = c("Xfield","Yfield"),
#' sd_coord = check_plot201$sd_coord, n = 200
#' )
#' )
#' # Summary (may take few minutes to extract all raster metrics)
#' res_summary <- subplot_summary(
#' subplots = nouragues_subplots,
#' AGB_simu = resultMC$AGB_simu,
#' ref_raster = nouragues_raster, raster_fun = mean)
#'
#' res_summary$tree_summary
#' res_summary$plot_design[[1]]
#' head(res_summary$long_AGB_simu)
#' }
subplot_summary <- function(subplots, value = NULL, AGB_simu = NULL, draw_plot = TRUE, per_ha = TRUE, fun = sum, ref_raster = NULL, raster_fun = mean, ...) {
# Checking parameters --------------------------------------------------------
if( any(!c("x_proj","y_proj") %in% names(subplots$sub_corner_coord)) ) {
subplots$sub_corner_coord[,c("x_proj","y_proj")] <- subplots$sub_corner_coord[,c("x_rel","y_rel")]
message("Projected coordinates are not found in sub_corner_coord$subplots, metric(s) will be summarised in the relative coordinate system.")
}
if(!is.null(value) && !any(value %in% names(subplots$tree_data))) {
stop(paste(value,"is not a column name of subplots$tree_data"))
}
if (!is.null(AGB_simu) && !is.matrix(AGB_simu)) stop("'AGB_simu' must be a matrix containing individual AGB (one row per tree), typically, the output '$AGB_simu' of the AGBmonteCarlo() function.")
if (!is.null(AGB_simu) && nrow(AGB_simu) != nrow(subplots$tree_data) ) {
stop("The rows in 'subplots$tree_data' must match the rows in 'AGB_simu'")
}
if (!is.null(AGB_simu)) {
if(is.null(subplots$simu_coord)) {
message("AGB uncertainties will be propagated without propagation of corner GPS measurement uncertainties.")
} else if (ncol(AGB_simu) != length(subplots$simu_coord)) {
if( ncol(AGB_simu) < length(subplots$simu_coord) ) {
message(paste("As the 'AGB_simu' matrix only contains", ncol(AGB_simu),"simulations, the first", ncol(AGB_simu),"simulations contained in 'subplots$simu_coord' will be considered."))
subplots$simu_coord <- subplots$simu_coord[1:ncol(AGB_simu)]
} else {
message(paste("As 'AGB_simu' contains", ncol(AGB_simu),"simulations, and 'subplots$simu_coord' contains", length(subplots$simu_coord),"simulations,", ncol(AGB_simu)-length(subplots$simu_coord),"simulations will be resampled in 'subplots'."))
# keep in memory some parameters
N_simu_coord <- length(subplots$simu_coord)
N_row_coord <- nrow(subplots$simu_coord[[1]])
simu_resampled <- sample(x = 1:N_simu_coord, size = ncol(AGB_simu)-length(subplots$simu_coord), replace = TRUE)
# resample coordinate simulations to match the number of AGB simulations
subplots$simu_coord <- c(subplots$simu_coord, subplots$simu_coord[simu_resampled])
}
}
}
if(is.null(AGB_simu) & is.null(ref_raster) & !is.null(subplots$simu_coord)) {
warning("In BIOMASS, the propagation of uncertainties in the coordinates of the plot corners does not make sense if it is not accompanied by raster data or the propagation of biomass uncertainties. For this reason, the following results will not take into account the uncertainty of corner coordinates.")
}
### Getting function's name ----
# the issue was in BIOMASSapp when calling the function as follows:
# arg_fun <- available_functions[[input$sel_user_function]]
# subplot_summary(..., fun = arg_fun)
# it returned column names like "AGB_arg_fun_per_ha"
# code generated with Claude AI
# Helper function to find original function name ----
find_function_name <- function(func) {
search_packages <- c(".GlobalEnv", search())
for (env_name in search_packages) {
env <- tryCatch(as.environment(env_name), error = function(e) NULL)
if (is.null(env)) next
obj_names <- ls(envir = env, all.names = FALSE)
for (obj_name in obj_names) {
obj <- tryCatch(get(obj_name, envir = env, inherits = FALSE), error = function(e) NULL)
if (!is.null(obj) && is.function(obj) && identical(obj, func)) {
return(obj_name)
}
}
}
return(NULL)
}
## retrieve the name(s) of the function(s) as character(s) ----
fun_name_raw <- if (length(value) > 1) {
as.character(substitute(fun))[-1]
} else {
deparse(substitute(fun))
}
# Evaluate the function in the parent frame
fun_eval <- eval(substitute(fun), envir = parent.frame())
# Get the name(s) of the function(s)
if (is.list(fun_eval)) { # if multiple functions provided in a list, loop on fun_eval
fun_name <- sapply(seq_along(fun_eval), function(i) {
if (!is.function(fun_eval[[i]])) { # if not a correct function
stop(paste("the function", fun_name_raw[i], "provided in `fun =` is not a function"))
} else {
found_name <- find_function_name(fun_eval[[i]])
if(!is.null(found_name)) { # if the name has been found, return it
return(found_name)
} else { # else, return the fun_name_raw
return(fun_name_raw[i])
}
}
})
} else if (is.function(fun_eval)) { # if only one function
found_name <- find_function_name(fun_eval)
if(!is.null(found_name)) { # if the name has been found, return it
fun_name <- found_name
} else { # else, return the fun_name_raw
fun_name <- fun_name_raw
}
} else {
stop("the function provided using `fun =` is not a function")
}
### The same for raster_fun ----
# Check is ref_raster is a SpatRaster
if(!is.null(ref_raster) && !is(ref_raster, "SpatRaster") ) {
stop("ref_raster is not recognised as a SpatRaster of terra package")
}
# retrieve the name(s) of the function(s) as character(s)
raster_fun_name_raw <- if (length(raster_fun) > 1) {
as.character(substitute(raster_fun))[-1]
} else {
deparse(substitute(raster_fun))
}
# Evaluate the function in the parent frame
raster_fun_eval <- eval(substitute(raster_fun), envir = parent.frame())
# Get the name(s) of the function(s)
if (is.list(raster_fun_eval)) { # if multiple functions provided in a list, loop on fun_eval
raster_fun_name <- sapply(seq_along(raster_fun_eval), function(i) {
if (!is.function(raster_fun_eval[[i]])) { # if not a correct function
stop(paste("the function", raster_fun_name_raw[i], "provided in `raster_fun =` is not a function"))
} else {
found_name <- find_function_name(raster_fun_eval[[i]])
if(!is.null(found_name)) { # if the name has been found, return it
return(found_name)
} else { # else, return the fun_name_raw
return(raster_fun_name_raw[i])
}
}
})
} else if (is.function(raster_fun_eval)) { # if only one function
found_name <- find_function_name(raster_fun_eval)
if(!is.null(found_name)) { # if the name has been found, return it
raster_fun_name <- found_name
} else { # else, return the fun_name_raw
raster_fun_name <- fun_name_raw
}
} else {
stop("the function provided using `raster_fun =` is not a function")
}
# Check parameter length
if(!is.null(value)) {
# Replicate per_ha if needed
if( length(value) > 1 && length(per_ha) == 1 ) {
per_ha = rep(per_ha, length(value))
}
if( is.list(fun) && length(value) != length(fun) ) {
stop("the lengths of 'value' and 'fun' are not the same")
}
if( is.list(fun) && length(value) != length(per_ha) ) {
stop("the lengths of 'value' and 'per_ha' are not the same")
}
}
# Data processing ------------------------------------------------------------
if(!is.null(value)) {
value_fun_name <- paste(value, fun_name, sep = "_")
} else {
value_fun_name <- NULL
}
# Formatting intial corner data
corner_dat <- data.table(subplots$sub_corner_coord)
corner_dat[ , c("x_center","y_center") := list(mean(x_proj),mean(y_proj)) , by = subplot_ID]
# Formatting simulated corner data
if(!is.null(subplots$simu_coord)) {
simu_corner_dat <- do.call(rbind,lapply(1:length(subplots$simu_coord), function(n_simu) { # n_simu = 1
cor_dat <- data.table(subplots$simu_coord[[n_simu]])
# Adding simulation number and subplot centre coordinates
cor_dat[, N_simu := n_simu]
cor_dat[ , c("x_center","y_center") := list(mean(x_proj),mean(y_proj)) , by = subplot_ID]
}))
}
# Creating tree_summary that will be the main output
tree_summary <- data.table(subplots$tree_data)
# stop if there is any column named "Vn" where n is an integer
if( length(grep("^V[0-9]+",names(tree_summary))) != 0) stop("Sorry but you can't have any columns named 'Vn', where n is an integer, in subplots$tree_data")
# Calculating metric(s) summary ----
# and adding it(s) to tree_summary at subplot level
if(!is.null(value)) {
if(length(value) == 1) {
tree_summary <- tree_summary[!is.na(subplot_ID), fun(get(value), ...) , by=c("subplot_ID")]
setnames(tree_summary, "V1", value_fun_name)
} else { # Apply functions to values by subplot
tree_summary <- tree_summary[!is.na(subplot_ID), lapply( 1:length(value), function(i) fun[[i]](get(value[i]), ...) ) , by=c("subplot_ID")]
setnames(tree_summary, 2:(length(value)+1), value_fun_name)
}
# Check function(s) that would returned more than one value
if(any(duplicated(tree_summary$subplot_ID))) {
stop("the function provided using `fun` must return a single value")
}
# Check if there was a subplot without any tree
if(nrow(tree_summary) != nrow(corner_dat)) {
tree_summary <- tree_summary[data.table(subplot_ID = unique(corner_dat$subplot_ID)),
on = "subplot_ID"]
}
} else {
# if value is not provided, we still need to format tree_summary
tree_summary <- data.table(subplot_ID = unique(corner_dat$subplot_ID))
}
# Calculating summary of AGB simulated values at subplot level
if(!is.null(AGB_simu)) {
AGB_simu <- data.table(AGB_simu)
names(AGB_simu) <- paste("AGB",1:ncol(AGB_simu), sep="_")
AGB_simu[, plot_ID := subplots$tree_data$plot_ID]
AGB_simu[, subplot_ID := subplots$tree_data$subplot_ID]
AGB_simu_sum <- AGB_simu[!is.na(subplot_ID), lapply(.SD, sum, na.rm=TRUE), by = c("plot_ID","subplot_ID"), .SDcols = !c("plot_ID","subplot_ID")]
# Check if there was a subplot without any tree
if(nrow(AGB_simu_sum) != length(unique(corner_dat$subplot_ID))) {
AGB_simu_sum <- AGB_simu_sum[
data.table(plot_ID = rep(as.character(unique(corner_dat$plot_ID)) , e=4),
subplot_ID = as.character(unique(corner_dat$subplot_ID))),
on = c("plot_ID","subplot_ID")]
AGB_simu_sum[is.na(AGB_simu_sum)] <- 0
}
}
# Function to create sf_polygons and calculate the associated area ----
create_polygon_fct <- function(dat) { # dat = corner_dat[ 1:4 , ]
# Create the associated polygon
mat <- dat[, .(x_proj, y_proj)]
mat <- as.matrix(rbind(mat, mat[1, ]))
subplot_polygon <- sf::st_polygon(list(mat))
# Get 1 summary row instead of 4 (because there was 4 corners)
coord_col_names <- c("x_rel","y_rel","x_proj","y_proj","long","lat")
df_polygon <- unique(dat[,.SD,.SDcols = names(dat)[! names(dat) %in% coord_col_names] ])
df_polygon$area <- sf::st_area(subplot_polygon) / 10000
# Replace projected coordinates with geographic coordinates if exist
if( !is.null(subplots$UTM_code) ) {
mat <- dat[, .(long, lat)]
mat <- as.matrix(rbind(mat, mat[1, ]))
subplot_polygon <- sf::st_polygon(list(mat))
}
# Convert subplot_polygon from a POLYGON to a simple feature collection of 1 POLYGON
sf_polygon <- sf::st_sf(df_polygon,list(subplot_polygon))
# Setting CRS if geographic coordinates
if( !is.null(subplots$UTM_code) ) {
sf::st_crs(sf_polygon) <- subplots$UTM_code$UTM_code[subplots$UTM_code$plot_ID == unique(dat$plot_ID)]
}
sf::st_geometry(sf_polygon) <- "sf_subplot_polygon" #rename last column
return(sf_polygon)
}
# Creating sf_polygons ----
## for initial corners
# that will be the $polygon output of the function (a simple feature collection of the subplot's polygon)
sf_polygons <- corner_dat[ , create_polygon_fct(.SD) , by = "subplot_ID", .SDcols = colnames(corner_dat)][,-1]
# for simulated corners
# used to extract the raster metric : we take unique polygons to not calculate multiple times the same subplot in the case of repeated corner simulations
# print("Calculating area for simulated corners...")
if(!is.null(subplots$simu_coord)) {
if(exists("N_simu_coord")) {
inital_simu_coord <- copy(simu_corner_dat[1:(N_simu_coord*N_row_coord),])
} else {
inital_simu_coord <- copy(simu_corner_dat)
}
sf_simu_polygons <- inital_simu_coord[ , create_polygon_fct(.SD) , by = c("subplot_ID","N_simu"), .SDcols = colnames(inital_simu_coord)][,-c(1,2)]
}
# Divide tree metrics per number of hectare ----
if(!is.null(value)) {
for(i in 1:length(per_ha)) {
if (per_ha[i]) {
tree_summary[ , paste(value_fun_name[i], "per_ha", sep = "_") := get(value_fun_name[i])/ sf_polygons$area]
tree_summary[ , eval(value_fun_name[i]) := NULL]
}
}
}
# Extract raster values ----
# use of extract just once instead of multiple time if it was done during the sf_polygons creation
if( !is.null(ref_raster)) {
cat("Extracting raster metric...")
if(is.null(subplots$simu_coord)) {
extract_rast_val <- extract(x = ref_raster, y = vect(sf_polygons$sf_subplot_polygon), exact = TRUE)
} else {
extract_rast_val <- extract(x = ref_raster, y = vect(sf_simu_polygons$sf_subplot_polygon), exact = TRUE)
}
cat("Extracting raster metric done.\n")
# keeping raster values whose fraction are > 0.5
extract_rast_val = data.table(extract_rast_val)[ fraction >0.5,]
rast_val_name <- names(extract_rast_val)[2]
raster_value_fun_name <- paste(rast_val_name, raster_fun_name, sep = "_")
if(is.null(subplots$simu_coord)) {
# Adding raster metric(s) summary to tree_summary at subplot level
if(length(raster_fun) == 1) {
tree_summary[, eval(raster_value_fun_name) := extract_rast_val[, raster_fun(get(rast_val_name), ...) , by=c("ID")][,"V1"]]
} else {
for(i in 1:length(raster_fun)) {
tree_summary[, raster_value_fun_name[i] := extract_rast_val[, raster_fun[[i]](get(rast_val_name), ...) , by=c("ID")][,"V1"]]
}
}
# Check raster_fun(s) that would returned more than one value
if("list" %in% sapply(tree_summary,typeof)) {
stop("the function provided using `raster_fun` must return a single value")
}
} else {
# Adding raster metric(s) summary to sf_simu_polygons at subplot level
if(length(raster_fun) == 1) {
sf_simu_polygons[, eval(raster_value_fun_name) := extract_rast_val[, raster_fun(get(rast_val_name), ...) , by=c("ID")][,"V1"]]
} else {
for(i in 1:length(raster_fun)) {
sf_simu_polygons[, raster_value_fun_name[i] := extract_rast_val[, raster_fun[[i]](get(rast_val_name), ...) , by=c("ID")][,"V1"]]
}
}
# Check raster_fun(s) that would returned more than one value
if("list" %in% sapply(sf_simu_polygons[,.SD,.SDcols = !"sf_subplot_polygon"],typeof)) {
stop("the function provided using `raster_fun` must return a single value")
}
}
}
# Recreate the simulated coord data-table with all simulations if needed
if(exists("simu_resampled")) {
sf_simu_polygons <- rbind(
sf_simu_polygons,
do.call(rbind, lapply(simu_resampled, function(i) sf_simu_polygons[N_simu==i,]))
)
}
# AGBD calculation ----
# Calculate AGBD_sum_median, cred_2.5, cred_97.5
if(!is.null(AGB_simu)) { # Reshape AGB_simu_sum to a long format to include the simulation number in subplot_ID
# long_AGB_simu_sum will be the long format output of the function
long_AGB_simu_sum <- reshape(data = AGB_simu_sum[,.SD, .SDcols = colnames(AGB_simu_sum)],
varying = grep("AGB",colnames(AGB_simu_sum),value = TRUE) , direction = "long", v.names = "AGB_sum", ids = NULL)
setnames(long_AGB_simu_sum,"time","N_simu")
if(exists("sf_simu_polygons")) {
# Add the area and coord_center of simulated subplots
sf_simu_polygons[, N_simu := long_AGB_simu_sum$N_simu]
long_AGB_simu_sum <- long_AGB_simu_sum[sf_simu_polygons[,c("subplot_ID","N_simu","x_center","y_center","area")], on = c("subplot_ID","N_simu")]
} else {
# Add the area and coord_center of initial subplots
rep_sf_polygons <- do.call(rbind,lapply(1:max(long_AGB_simu_sum$N_simu), function(x) sf_polygons))
rep_sf_polygons[, N_simu := long_AGB_simu_sum$N_simu]
long_AGB_simu_sum <- long_AGB_simu_sum[rep_sf_polygons[,c("subplot_ID","N_simu","x_center","y_center","area")], on = c("subplot_ID","N_simu")]
}
long_AGB_simu_sum[, AGBD := AGB_sum/area]
res_AGBD_simu <- long_AGB_simu_sum[, .(AGBD_median = median(AGBD, na.rm=TRUE),
AGBD_cred_2.5 = quantile(AGB_sum / area, probs = 0.025, na.rm=TRUE),
AGBD_cred_97.5 = quantile(AGB_sum / area, probs = 0.975, na.rm=TRUE)),
by = subplot_ID]
long_AGB_simu_sum[, c("AGB_sum","area") := list(NULL, NULL)]
tree_summary <- tree_summary[res_AGBD_simu, on = "subplot_ID"]
}
# Raster metric(s) calculation ----
# Calculate raster_metric_median, cred_2.5, cred_97.5 if simulated corners
if(!is.null(ref_raster) && !is.null(subplots$simu_coord)) {
res_raster_sim <- sf_simu_polygons[,
setNames(
list(
median(get(raster_value_fun_name)),
quantile(get(raster_value_fun_name), probs = 0.025, na.rm=TRUE),
quantile(get(raster_value_fun_name), probs = 0.975, na.rm=TRUE)
),
paste0(raster_value_fun_name, c("_median", "_cred_2.5", "_cred_97.5"))
),
by = subplot_ID]
tree_summary <- tree_summary[res_raster_sim, on = "subplot_ID"]
if(exists("long_AGB_simu_sum")) {
long_AGB_simu_sum[, eval(raster_value_fun_name) := sf_simu_polygons[[raster_value_fun_name]] ]
}
}
# Add metrics to sf_polygons
sf_polygons <- sf_polygons[tree_summary, on = "subplot_ID"]
# Delete coord_center and area columns
sf_polygons[, c("x_center","y_center","area") := list(NULL, NULL, NULL)]
# Plot the plot(s) -----------------------------------------------------------
displayed_cols <- names(sf_polygons)[! names(sf_polygons) %in% c("plot_ID","subplot_ID","area","sf_subplot_polygon")]
displayed_cols <- displayed_cols[!grepl("_cred_",displayed_cols)]
plot_list <- lapply( split(sf_polygons , sf_polygons$plot_ID) , function(sf_pol) { # sf_pol = split(sf_polygons , sf_polygons$plot_ID)[[1]]
plot_design <- lapply(displayed_cols , function(metric_name) {
pol_bbox <- sapply(sf_pol$sf_subplot_polygon, sf::st_bbox)
ggplot(data = sf_pol) +
geom_sf(mapping = aes(fill=.data[[metric_name]], geometry = sf_subplot_polygon)) +
theme_minimal() +
scale_fill_gradientn(colours = rev(terrain.colors(20))) +
lims(x = c(min(pol_bbox["xmin",]), max(pol_bbox["xmax",])),
y = c(min(pol_bbox["ymin",]), max(pol_bbox["ymax",]))) +
theme(legend.position="bottom", #legend.title = element_blank(),
legend.key.size = unit(0.8,"cm"),
axis.title = element_blank(),
axis.text.x = element_text(angle = 45, hjust=1)
) +
ggtitle( paste( ifelse(unique(sf_pol$plot_ID) == "" , "" , paste(unique(sf_pol$plot_ID) , ":")),
gsub("_",replacement = " ", metric_name)) )
})
if(draw_plot) print(plot_design)
return(invisible(plot_design))
})
# Outputs --------------------------------------------------------------------
# Organize the ggplots outputs :
if(length(plot_list) == 1) { # if just one plot:
plot_list <- plot_list[[1]]
if(length(plot_list) == 1) { # if just one metric:
plot_list <- plot_list[[1]]
}
} else { # if several plots:
if(length(plot_list[[1]]) == 1) { # if just one metric:
plot_list <- lapply(plot_list, function(x) x[[1]])
}
}
output <- list(tree_summary = tree_summary, polygon = st_as_sf(sf_polygons), plot_design = plot_list)
if(! is.null(AGB_simu)) {
output$long_AGB_simu = long_AGB_simu_sum
}
return(output)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.