#'Tree felling
#'
#'@description Simulates the tree felling, with the success or failure of the
#' direction of the tree fall, foot to the trail, with an angle to the trail
#' and avoiding the trees to protect, as desired. If FWE (Fuel Wood
#' Exploitation), the tree will be directed with its crown towards the trail
#' (if the orientation is successful) if it can be retrieved with a grapple.
#'
#'@param inventory Input inventory (see the inputs formats and metadata in the
#' vignette) (data.frame)
#'
#'@param scenario Logging scenario among: "RIL1", "RIL2broken", "RIL2", "RIL3",
#' "RIL3fuel", "RIL3fuelhollow" or "manual"(character) (see the
#' vignette)
#'
#'@param fuel Fuel wood exploitation: no exploitation = "0", exploitation of
#' damage and unused part of logged trees for fuel = "1", exploitation of
#' hollow trees, damage and and unused part of the log for fuel = "2"
#' If fuel wood exploitation (fuel = "1" or "2") the tree will be recovered
#' from the crown with a grapple if possible (respected grapple conditions).
#' If not, recovery at the foot with a cable at an angle to the trail.
#' Avoid future/reserve trees if chosen.
#'
#'@param winching
#' "0": no cable or grapple (trail to tree foot)
#' "1": only cable (default = 40 m)
#' "2": grapple (default = 6 m) + cable (grapple priority)
#' If grapple + cable (winching = "2") without fuel wood (fuel = "0")
#' recovery of the tree foot with grapple if possible (respected grapple
#' conditions) otherwise with cable with angle to the trail.
#' Avoidance of future/reserves if chosen.
#'
#'@param directionalfelling Directional felling =
#' "0": only to direct the foot of the tree towards the trail
#' "1": to direct the foot of the tree towards the trail + to avoid damage to
#' future and reserve trees if possible
#' "2": to avoid damage to future and reserve trees if possible
#' + orientation angle to the trail. Among the 2 possible angle positions,
#' the position that favours the return to the main trail should be chosen.
#' The angle to the trail is favoured to avoid future/reserve trees.
#' If the avoidance of future/reserve trees could not be performed,
#' a message is returned.
#'
#'@param maintrailsaccess Access point of main trail for each harvestable zone (sf or sfc)
#'@param scndtrail Secondary trails (sf)
#'
#'@param advancedloggingparameters Other parameters of the logging simulator
#' \code{\link{loggingparameters}} (list)
#'
#'
#'@return Input inventory with new columns:
#'- The tree felling success or fail("*TreeFellingOrientationSuccess*")
#'- The fallen trees ("*TreePolygon*"): a MULTIPOLYGON of the tree oriented
#' according to the chosen scenario
#'- The dead trees under felled trees (*DeathCause* = "*treefall2nd*")
#'
#'@details The felling of the tree creates a tree (including crown) on the
#' ground, with dimensions calculated with specific allometries
#' ('advancedloggingparameters').
#'
#'The crowns (fuel wood exploitation case) can only be retrieved with a grapple.
#'
#'RIL1/RIL2broken/RIL2:
#' - at 40%: random fall
#' - at 60% ('TreefallSuccessProportion'):
#' *base of the tree towards the nearest trail* (main or secondary)
#'
#'RIL3/RIL3 timber + fuel wood:
#' - at 40%: random fall
#' - at 60% ('TreefallSuccessProportion'):
#' * if trees < 6 m from the trail and slope <20% (grapple use):
#' - no particular angle to orientate to the trail,
#' only to orient the tree *base*(*crown* if fuel wood exploitation)
#' as close as possible to the trail
#' - priority 1: avoid futures and reserves,
#' - priority 2: conformation allowing skidding back to the main trail
#'
#' * otherwise (trees > 6 m from the trail and/or slope >20%)(cable use):
#' - 30-45◦ (default) orientation
#' ('MinTreefallOrientation'; 'MaxTreefallOrientation')
#' - *base* to nearest trail
#' - conformation allowing skidding back to the main trail
#' - avoid futures and reserves if possible
#'
#'Damage:
#'Secondary windfall: Not all trees under the felled tree (timber or energy)
#'will be considered dead. The probability of a tree dying under a felled tree
#'is estimated by the model 'Treefall2ndDeathModel', according to the DBH of the
#'tree whose probability of dying is estimated.
#'
#'@export
#'
#'@importFrom sf st_as_sf st_as_text st_geometry st_intersection st_make_valid
#' st_buffer st_union
#'@importFrom dplyr filter group_by do left_join mutate select
#'@importFrom tibble add_column
#'@importFrom tidyr unnest
#'@importFrom stats runif
#'
#' @examples
#' \dontrun{
#' set.seed(1)
#' data(SecondaryTrails)
#'
#'
#' scenario <- "manual"
#' winching <- "2"
#' fuel <- "2"
#' directionalfelling <- "2"
#'
#'
#' NewInventory <- treefelling(SecondaryTrails$inventory,
#' scenario = scenario, fuel = fuel,
#' winching = winching,
#' directionalfelling = directionalfelling,
#' maintrailsaccess = SecondaryTrails$MainTrailsAccess,
#' scndtrail = SecondaryTrails$SmoothedTrails,
#' advancedloggingparameters = loggingparameters())
#'
#' TreePolygon <- NewInventory %>%
#' getgeometry(TreePolygon) %>%
#' sf::st_set_crs(sf::st_crs(MainTrails)) # set a crs
#'
#' Inventory_crs <- sf::st_as_sf(NewInventory, coords = c("Xutm", "Yutm")) # as sf
#' Inventory_crs <- sf::st_set_crs(Inventory_crs, sf::st_crs(MainTrails)) # set a crs
#'
#' Treefall <- sf::st_as_sf(
#' dplyr::filter(NewInventory, DeathCause == "treefall2nd"),
#' coords = c("Xutm", "Yutm")) %>%
#' sf::st_set_crs(sf::st_crs(MainTrails)) # set a crs
#'
#' NonHarvestable <- sf::st_as_sf(
#' dplyr::filter(Inventory_crs, LoggingStatus == "non-harvestable"),
#' coords = c("Xutm", "Yutm"))
#'
#' Harvestable <- sf::st_as_sf(
#' dplyr::filter(Inventory_crs, LoggingStatus == "harvestable"),
#' coords = c("Xutm", "Yutm"))
#'
#' HarvestableUp <- sf::st_as_sf(
#' dplyr::filter(Inventory_crs, LoggingStatus == "harvestableUp"),
#' coords = c("Xutm", "Yutm"))
#'
#' Selected <- sf::st_as_sf(
#' dplyr::filter(Inventory_crs, Selected == "1"), coords = c("Xutm", "Yutm"))
#'
#' Reserve <- sf::st_as_sf(
#' dplyr::filter(Inventory_crs, LoggingStatus == "reserve"),
#' coords = c("Xutm", "Yutm"))
#'
#' Future <- sf::st_as_sf(
#' dplyr::filter(Inventory_crs, LoggingStatus == "future"),
#' coords = c("Xutm", "Yutm"))
#'
#' ProbedHollow <- sf::st_as_sf(
#' dplyr::filter(Inventory_crs, ProbedHollow == "1"), coords = c("Xutm", "Yutm"))
#'
#' VisibleDefect <- sf::st_as_sf(
#' dplyr::filter(Inventory_crs, VisibleDefect == "1"), coords = c("Xutm", "Yutm"))
#'
#' Treefall2nd <- sf::st_as_sf(
#' dplyr::filter(Inventory_crs, DeathCause == "treefall2nd"), coords = c("Xutm", "Yutm"))
#'
#' library(ggplot2)
#' ggplot() +
#' geom_sf(data = Inventory_crs) +
#' geom_sf(data = NonHarvestable,
#' aes(colour = "Non-harvestable"), show.legend = "point") +
#' geom_sf(data = VisibleDefect,
#' aes(colour = "Visible defect"), show.legend = "point") +
#' geom_sf(data = Future,
#' aes(colour = "Future"), show.legend = "point", size = 4) +
#' geom_sf(data = Reserve,
#' aes(colour = "Reserve"), show.legend = "point", size = 4) +
#' geom_sf(data = Harvestable,
#' aes(colour = "Harvestable"), show.legend = "point", size = 4) +
#' geom_sf(data = HarvestableUp,
#' aes(colour = "HarvestableUp"), show.legend = "point", size = 4) +
#' geom_sf(data = TreePolygon, # cuted trees
#' alpha = 0.5, fill = "red") +
#' geom_sf(data = Selected, aes(colour = "Selected"), show.legend = "point") +
#' geom_sf(data = ProbedHollow,
#' aes(colour = "Probed hollow"), show.legend = "point") +
#' geom_sf(data = Treefall2nd,
#' aes(colour = "Treefall2nd"), show.legend = "point") +
#' geom_sf(data = SecondaryTrails$maintrailsaccess,
#' alpha = 0.5, fill = "black") +
#' geom_sf(data = SecondaryTrails$SmoothedTrails,
#' alpha = 0.5, fill = "black") +
#'
#' scale_colour_manual(values = c("Non-harvestable" = "grey",
#' "Visible defect" = "pink", "Harvestable" = "skyblue",
#' "HarvestableUp" = "blue", "Selected" = "red", "Future" = "orange",
#' "Reserve" = "purple", "Probed hollow" = "forestgreen",
#' "Treefall2nd" = "chocolate4")) +
#' labs(color = "Logging status")
#' }
#'
#'\dontrun{
#' # The trees under the fallen trees
#' suppressWarnings(sf::st_intersection( # trees under the fallen trees
#' getgeometry (NewInventory, TreePolygon),
#' sf::st_as_sf(NewInventory, coords = c("Xutm", "Yutm"))
#' )) %>%
#' ggplot() +
#' geom_sf()
#'}
#'
treefelling <- function(
inventory,
scenario,
fuel = NULL,
winching = NULL,
directionalfelling = NULL,
maintrailsaccess,
scndtrail,
advancedloggingparameters = loggingparameters()
){
# Arguments check
if(!inherits(inventory, "data.frame"))
stop("The 'inventory'argument of the 'treefelling' function must be data.frame")
if (!any(scenario == "RIL1" || scenario == "RIL2broken" || scenario == "RIL2"
|| scenario == "RIL3" || scenario == "RIL3fuel"||
scenario == "RIL3fuelhollow" || scenario =="manual"))
stop("The 'scenario' argument of the 'treefelling' function must be
'RIL1', 'RIL2broken', 'RIL2', 'RIL3', 'RIL3fuel', 'RIL3fuelhollow' or 'manual'")
if (!any(fuel == "0" || fuel == "1"|| fuel == "2"|| is.null(fuel)))
stop("The 'fuel' argument of the 'treefelling' function must be '0', '1', '2' or NULL")
if (!any(directionalfelling == "0" || directionalfelling == "1" || directionalfelling == "2" || is.null(directionalfelling)))
stop("The 'directionalfelling' argument of the 'treefelling' function must be '0', '1', '2' or NULL")
if(!all(unlist(lapply(list(maintrailsaccess, scndtrail), inherits, c("sf", "sfc")))))
stop("The 'maintrailsaccess' and 'scndtrail'arguments of the 'treefelling' function must be sf or sfc")
if(!inherits(advancedloggingparameters, "list"))
stop("The 'advancedloggingparameters' argument of the 'treefelling' function must be a list")
if(scenario == "manual" &&
(is.null(fuel) || is.null(directionalfelling)))
stop("If you choose the 'manual' mode,
you must fill in the arguments 'fuel' and 'directionalfelling'")
# Global variables
Accessible <- CircCorr <- CodeAlive <-NULL
Condition <- DBH <- MinFD <- Taxo <- alpha <- bCoef <- NULL
DeathCause <- DistCriteria <- Family <- CrownHeight <- CrownDiameter <- NULL
Genus <- Logged <- TreePolygon <- NULL
LoggingStatus <- MaxFD <- Crowns <- NULL
ProbedHollow <- ProbedHollowProba <- ScientificName <- NULL
Selected <- SlopeCriteria <- Species <- NULL
TreeFellingOrientationSuccess <- TreeHarvestableVolume <- NULL
TreeHeight <- TrunkHeight <- Up <- UpMinFD <- NULL
VolumeCumSum <- Xutm <- Yutm <- aCoef <- NULL
geometry <- idTree <- . <- Treefall2ndDeath <- Treefall2ndDeathProba <- NULL
# initial inventory
inventory0 <- inventory
# Redefinition of the parameters according to the chosen scenario
scenariosparameters <- scenariosparameters(scenario = scenario, fuel = fuel, winching = winching,
directionalfelling = directionalfelling)
directionalfelling <- scenariosparameters$directionalfelling
fuel <- scenariosparameters$fuel
winching <- scenariosparameters$winching
# Compute treefelling success and fails
inventory <- directionalfellingsuccessdef(
inventory,
fuel = fuel,
directionalfelling = directionalfelling,
advancedloggingparameters = advancedloggingparameters)
# Future/reserve crowns
FutureReserveCrowns <- inventory %>% # create an object with future/reserve crowns only
dplyr::filter(LoggingStatus == "future" | LoggingStatus == "reserve") %>%
createcanopy() %>% # create all inventory crowns in the 'Crowns' column
getgeometry(Crowns)
# Main trails accesses with a width
MainTrailsAccessBuff <- maintrailsaccess %>%
sf::st_buffer(dist = runif(1,
advancedloggingparameters$MinMainTrailWidth,
advancedloggingparameters$MaxMainTrailWidth)) %>%
sf::st_union() %>% sf::st_as_sf()
# Treefelling
felttrees <- inventory %>%
filter(!is.na(TreeFellingOrientationSuccess)) %>%
dplyr::group_by(idTree) %>% # for each tree
dplyr::do(TreePolygon = # inform geometry. # Filling a column from a function whose input is a table
felling1tree(.,
fuel = fuel, winching = winching, directionalfelling = directionalfelling,
maintrailsaccess = MainTrailsAccessBuff, scndtrail = scndtrail,
FutureReserveCrowns = FutureReserveCrowns,
advancedloggingparameters = advancedloggingparameters)$FallenTree %>%
sf::st_as_text()) %>% # as text to easy join with a non spacial table
tidyr::unnest(TreePolygon) # here to pass from list to character
inventory <- left_join(inventory, felttrees, by = "idTree") # join spatial filtered inventory and non spatial complete inventory
# Mortality
# Records the felled trees
if (!("DeathCause" %in% names(inventory))){
inventory <- inventory %>%
add_column(DeathCause = NA) # if "DeathCause" column doesnt exist create it
}
inventory <- inventory %>%
mutate(DeathCause = ifelse(is.na(DeathCause) & !is.na(TreePolygon) & ProbedHollow == "0",
"cut", DeathCause)) %>% # timber exploitation
mutate(DeathCause = ifelse(is.na(DeathCause) & !is.na(TreePolygon) & ProbedHollow == "1",
"hollowfuel", DeathCause)) # fuel wood exploitation
# Trees under the fallen trees
felttrees <- felttrees %>% dplyr::select(-idTree) # remove pol infos to keep the information of the points
DeadTrees <- suppressWarnings(sf::st_intersection(
sf::st_make_valid(st_as_sf(inventory, coords = c("Xutm", "Yutm"))),
sf::st_make_valid(getgeometry(felttrees, TreePolygon)) # "make valid" to avoid self-intersection
)) %>%
add_column(DeadTrees = "1") %>%
dplyr::select(idTree, DeadTrees)
sf::st_geometry(DeadTrees) <- NULL # remove TreePolygon column (sf to data.frame)
DeadTrees <- unique(DeadTrees)
inventory <- inventory %>%
left_join(DeadTrees, by = "idTree") %>%
mutate(DeathCause = ifelse(is.na(DeathCause) & is.na(TreePolygon) & DeadTrees == "1",
"treefall2nd", DeathCause)) %>% # Damage trees
dplyr::select(-DeadTrees)
# length(which(inventory$DeathCause == "treefall2nd"))
# Not all trees die under the felled tree:
inventory <- inventory %>%
mutate(Treefall2ndDeathProba = ifelse(DeathCause == "treefall2nd",
advancedloggingparameters$Treefall2ndDeathModel(DBH), NA)) %>% # Proba to be dead under a felled tree
# generate either "1" or "0" randomly for each line, depending on the proba associated with the line:
rowwise() %>%
mutate(Treefall2ndDeath = ifelse(!is.na(Treefall2ndDeathProba),
sample(c(1,0), size = 1, replace = F,
prob = c(Treefall2ndDeathProba, 1-Treefall2ndDeathProba)), NA)) %>% # 1 = dead tree, 0 = alive tree
ungroup() %>%
mutate(Treefall2ndDeath = as.factor(Treefall2ndDeath)) %>%
mutate(DeathCause = ifelse(Treefall2ndDeath %in% "0", NA, # alive trees ("0") -> DeathCause = NA
DeathCause))
# length(which(inventory$DeathCause == "treefall2nd"))
if(nrow(inventory) != nrow(inventory0))
stop("The number of rows between the input inventory and the output inventory
of the function treefelling() is not the same.The function must be corrected.")
return(inventory)
}
#'Directional felling success definition
#'
#'@description Defines whether the directed fall of the tree is successful or
#' not by drawing in a Bernoulli distribution where the probability of success
#' is by default 60%, and can be changed with the
#' \code{advancedloggingparameters} argument.
#'
#'@param inventory input inventory (see the inputs formats and metadata in the
#' vignette) (data.frame)
#'
#'@param fuel Fuel wood exploitation: no exploitation = "0", exploitation of
#' damage and unused part of logged trees for fuel = "1", exploitation of
#' hollow trees, damage and and unused part of the log for fuel = "2"
#'
#'@param directionalfelling Directional felling =
#' "0": only to direct the foot of the tree towards the trail
#' "1": to direct the foot of the tree towards the trail + to avoid damage to
#' future and reserve trees
#' "2": to avoid damage to future and reserve trees + orientation angle
#' to the trail
#'
#'@param advancedloggingparameters Other parameters of the logging simulator
#' \code{\link{loggingparameters}} (list)
#'
#'@return Input inventory with the "TreeFellingOrientationSuccess" new column (see
#' the outputs metadata in the vignette).
#'@export
#'
#'@importFrom dplyr mutate rowwise
#'
#' @examples
#'
#' data(Paracou6_2016)
#' data(DTMParacou)
#' data(MainTrails)
#' data(HarvestableAreaOutputsCable)
#'
#' inventory <- addtreedim(cleaninventory(Paracou6_2016, PlotMask),
#' volumeparameters = ForestZoneVolumeParametersTable)
#'
#' inventory <- treeselection(inventory,
#' topography = DTMParacou,
#' speciescriteria = SpeciesCriteria,
#' scenario = "manual", objective = 10, fuel = "2", diversification = TRUE,
#' winching = "2", specieslax = FALSE, objectivelax = TRUE,
#' harvestablearea = HarvestableAreaOutputsCable$HarvestableArea,
#' plotslope = HarvestableAreaOutputsCable$PlotSlope,
#' maintrails = MainTrails,
#' harvestablepolygons = HarvestableAreaOutputsCable$HarvestablePolygons,
#' advancedloggingparameters = loggingparameters())$inventory
#'
#' new <- directionalfellingsuccessdef(inventory, fuel = "2",
#' directionalfelling = "2",
#' advancedloggingparameters = loggingparameters())
#'
directionalfellingsuccessdef <- function(
inventory,
fuel,
directionalfelling,
advancedloggingparameters = loggingparameters()) {
# Arguments check
if(!inherits(inventory, "data.frame"))
stop("The 'inventory'argument of the 'directionalfellingsuccessdef' function must be data.frame")
if (!any(fuel == "0" || fuel == "1"|| fuel == "2"|| is.null(fuel)))
stop("The 'fuel' argument of the 'directionalfellingsuccessdef' function must be '0', '1', '2' or NULL")
if (!any(directionalfelling == "0" || directionalfelling == "1"||
directionalfelling == "2"|| is.null(directionalfelling)))
stop("The 'directionalfelling' argument of the 'directionalfellingsuccessdef' function must be '0', '1', '2' or NULL")
if(!inherits(advancedloggingparameters, "list"))
stop("The 'advancedloggingparameters' argument of the 'directionalfellingsuccessdef' function must be a list")
# Global variables
Accessible <- CircCorr <- CodeAlive <- NULL
Condition <- DBH <- NULL
DeathCause <- DistCriteria <- Family <- CrownHeight <- CrownDiameter <- NULL
ForestZoneVolumeParametersTable <- Genus <- Logged <- TreePolygon <- NULL
LoggingStatus <- MaxFD <- MaxFD.genus <- NULL
MaxFD.species <- MinFD <- MinFD.genus <- MinFD.species <- NULL
ParamCrownDiameterAllometry <- NULL
ProbedHollow <- ProbedHollowProba <- ScientificName <- NULL
Selected <- SlopeCriteria <- Species <- Species.genus <- NULL
Taxo <- Taxo.family <- Taxo.genus <- Taxo.species <- NULL
TreeFellingOrientationSuccess <- TreeHarvestableVolume <- NULL
TreeHeight <- TrunkHeight <- Up <- UpMinFD <- UpMinFD.genus <- NULL
UpMinFD.species <- NULL
VolumeCumSum <- Xutm <- Yutm <- aCoef <- NULL
alpha <- alpha.family <- alpha.genus <- alpha.species <- bCoef <- NULL
beta.family <- beta.genus <- beta.species <- geometry <- idTree <- NULL
# initial inventory
inventory0 <- inventory
# Totally random
# if (totally random case){ # No directional felling
# inventory <- inventory %>%
# mutate(TreeFellingOrientationSuccess = ifelse(Selected == "1", 0, NA)) # always fail -> totally random
# }
# No hollow trees exploitation
if (fuel !="2"){
inventory <- inventory %>%
rowwise() %>%
mutate(TreeFellingOrientationSuccess =
ifelse(Selected == "1",
sample(c(1,0), size = 1, replace = F,
prob = c(advancedloggingparameters$TreefallSuccessProportion,
1-advancedloggingparameters$TreefallSuccessProportion)), NA))
# Accessible <- Selected !!!!!!!!!!!! pas oublier pour if (fuel == "0")
}
# Hollow trees exploitation too
if (fuel =="2") {
inventory <- inventory %>%
rowwise() %>%
mutate(TreeFellingOrientationSuccess =
ifelse(Selected == "1"| (Selected == "1" & ProbedHollow == "1"), # Selected = not yet linked by 2ndtrails, because 2ndtrails came after
sample(c(1,0), size = 1, replace = F,
prob = c(advancedloggingparameters$TreefallSuccessProportion,
1-advancedloggingparameters$TreefallSuccessProportion)), NA))
}
inventory$TreeFellingOrientationSuccess <- as.factor(inventory$TreeFellingOrientationSuccess)
if(nrow(inventory) != nrow(inventory0))
stop("The number of rows between the input inventory and the output inventory
of the function directionalfellingsuccessdef() is not the same.The function must be corrected.")
return(inventory)
}
#' rotatepolygon
#'
#' @description Rotate the input polygon with a given angle and around a fix point.
#' Function adapted from Jeffrey Evans' 'rotate.polygon' function:
#' https://github.com/jeffreyevans/spatialEco/blob/master/R/rotate.polygon.R
#'
#' @param p Polygon (POLYGON or sfc_POLYGON)
#' @param angle Angle in degrees in the clockwise direction (numeric)
#' @param fixed Fix point around which the polygon will be rotated (POINT)
#'
#' @return The polygon (sfc_POLYGON) rotated.
#' @export
#' @importFrom nngeo st_ellipse
#' @importFrom sf st_polygon st_coordinates st_sfc st_difference st_union
#'
#' @examples
#' data(Paracou6_2016)
#' data(DTMParacou)
#' data(SpeciesCriteria)
#' data(MainTrails)
#'
#' inventory <- addtreedim(cleaninventory(Paracou6_2016, PlotMask),
#' volumeparameters = ForestZoneVolumeParametersTable)
#'
#' inventory <- suppressMessages(treeselection(inventory,
#' topography = DTMParacou,
#' speciescriteria = SpeciesCriteria,
#' scenario = "manual", objective = 10, fuel = "2", diversification = TRUE,
#' winching = "0", specieslax = FALSE, objectivelax = TRUE,
#' plotslope = HarvestableAreaOutputsCable$PlotSlope,
#' maintrails = MainTrails,
#' harvestablearea = HarvestableAreaOutputsCable$HarvestableArea,
#' harvestablepolygons = HarvestableAreaOutputsCable$HarvestablePolygons,
#' advancedloggingparameters = loggingparameters())$inventory)
#'
#' inventory <- inventory %>%
#' dplyr::filter(Selected == "1") %>%
#' dplyr::select(idTree,DBH,TrunkHeight,TreeHeight,CrownHeight,
#' CrownDiameter,Selected, Xutm, Yutm)
#' dat <- inventory[1,]
#'
#' library(sf)
#' library(nngeo)
#' library(dplyr)
#' Foot <- st_point(c(dat$Xutm,dat$Yutm)) # tree foot point
#' Crown <- dat %>%
#' dplyr::mutate(xCrown = Xutm,
#' yCrown = Yutm + TrunkHeight + CrownHeight/2,
#' exCrown = CrownDiameter/2,
#' eyCrown = CrownHeight/2) %>%
#' sf::st_as_sf(coords = c("xCrown", "yCrown")) # ellipse centroid coordinates
#' Crown <- st_ellipse(Crown, Crown$exCrown, Crown$eyCrown) # create the ellipse
#' Trunk <- with(dat, # and the trunk
#' st_polygon(list(matrix(c(Xutm-(DBH/100)/2, Yutm,
#' Xutm-(DBH/100)/2, Yutm + TrunkHeight,
#' Xutm+(DBH/100)/2, Yutm + TrunkHeight,
#' Xutm+(DBH/100)/2, Yutm,
#' Xutm-(DBH/100)/2, Yutm) # the return
#' ,ncol=2, byrow=TRUE))))
#' RandomAngle <- as.numeric(sample(c(0:359), size = 1))
#'
#' TreeP <- st_difference(st_union(
#' rotatepolygon(Trunk, angle = RandomAngle, fixed = Foot), # turned trunk
#' rotatepolygon(Crown, angle = RandomAngle, fixed = Foot) # turned crown
#' ))
#'
#' library(ggplot2)
#' ggplot() +
#' geom_sf(data = st_union(Trunk, Crown), colour = "red") +
#' geom_sf(data = TreeP, colour = "green")
#'
rotatepolygon <- function(
p,
angle,
fixed
){
# Arguments check
if(!inherits(p, c("sf", "sfc_POLYGON", "POLYGON")))
stop("The 'p' argument of the 'rotatepolygon' function must be a sf, a sfc_POLYGON or a POLYGON")
if(!inherits(angle, "numeric"))
stop("The 'angle' argument of the 'rotatepolygon' function must be numeric")
if(!inherits(fixed, "POINT"))
stop("The 'fixed' argument of the 'rotatepolygon' function must be a POINT")
p.coords <- st_coordinates(p)[,1:2] # Polygon coordinates extraction
p.center <- st_coordinates(fixed)
center = c(p.center[1],
p.center[2])
co <- cos(-angle * pi / 180)
si <- sin(-angle * pi / 180)
adj <- matrix(rep(center, nrow(p.coords)), ncol = 2, byrow = TRUE) # matrix with fixed point coordinates
p.coords <- p.coords-adj
p.rotate <- cbind(co * p.coords[,1] - si * p.coords[,2],si * p.coords[,1] + co * p.coords[,2]) + adj
Turned <- st_sfc(st_polygon(list(p.rotate))) # create the turned polygon
return(Turned)
}
#'felling1tree
#'
#'@description Simulates the tree (multipolygon) falling towards the trail or
#' not, at a given angle. If it has been decided to exploit fuel wood, the tree
#' crowns will be directed towards the trail if they can be accessed with a
#' grapple (see the \code{GrappleLength} argument of the
#' \code{\link{loggingparameters}} function). In other cases, the fall will be
#' made from the base of the tree towards the trail. The orientation of the
#' fall succeeds or fails according to a Bernoulli law where the probability of
#' success is by default 60%, and can be changed with the
#' \code{advancedloggingparameters} argument.
#'
#'@param dat 1 row data.frame with columns: Xutm, Yutm, CrownDiameter,
#' CrownHeight, DBH, TrunkHeight, TreeHeight, TreeFellingOrientationSuccess
#'
#'@param fuel Fuel wood exploitation: no exploitation = "0", exploitation of
#' damage and unused part of logged trees for fuel = "1", exploitation of
#' hollow trees, damage and and unused part of the log for fuel = "2"
#' If fuel wood exploitation (fuel = "1" or "2") the tree will be recovered
#' from the crown with a grapple if possible (respected grapple conditions).
#' If not, recovery at the foot with a cable at an angle to the trail.
#' Avoid future/reserve trees if chosen.
#'
#'@param winching
#' "0": no cable or grapple (trail to tree foot)
#' "1": only cable (default = 40m)
#' "2": grapple (default = 6m) + cable (grapple priority)
#' If grapple + cable (winching = "2") without fuel wood (fuel = "0")
#' recovery of the tree foot with grapple if possible (respected grapple
#' conditions) otherwise with cable with angle to the trail.
#' Avoidance of future/reserves if chosen.
#'
#'@param directionalfelling Directional felling =
#' "0": only to direct the foot of the tree towards the trail
#' "1": to direct the foot of the tree towards the trail + to avoid damage to
#' future and reserve trees if possible
#' "2": to avoid damage to future and reserve trees if possible
#' + orientation angle to the trail. Among the 2 possible angle positions,
#' the position that favours the return to the main trail should be chosen.
#' The angle to the trail is favoured to avoid future/reserve trees.
#'
#'@param maintrailsaccess Main trails access points (sf)
#'@param scndtrail Secondary trails (sf)
#'
#'@param FutureReserveCrowns Future/reserve trees crown (sf)
#'
#'@param advancedloggingparameters Other parameters of the logging simulator
#' \code{\link{loggingparameters}} (list)
#'
#'@return A list with: FallenTree: a sfc_MULTIPOLYGON of the tree oriented
#' according to the chosen scenario. Foot: a POINT for the base of the tree
#' (the rotation fixed point). NearestPoints: a sfc_LINESTRING for the shortest
#' path from the base of the tree to the nearest trail, Trail: the union of the
#' main and the secondary trails. TrailPt: the POINT on the Trail closest
#' to the location of the tree.
#'
#'@seealso \code{\link{loggingparameters}}
#'
#'@export
#'
#'@importFrom dplyr mutate filter rowwise ungroup bind_rows summarise sample_n
#'@importFrom sf st_point st_multipolygon st_as_sf st_nearest_points st_distance
#' st_cast st_intersects st_buffer st_sfc st_combine st_set_crs st_crs
#'@importFrom nngeo st_ellipse
#'@importFrom matlib angle
#'
#' @examples
#' data(SecondaryTrails)
#'
#' # create an object with future/reserve crowns only
#' FutureReserveCrowns <- SecondaryTrails$inventory %>%
#' dplyr::filter(LoggingStatus == "future" | LoggingStatus == "reserve") %>%
#' createcanopy() %>% # create all inventory crowns in the 'Crowns' column
#' getgeometry(Crowns)
#'
#' dat <- SecondaryTrails$inventory %>%
#' dplyr::filter(Selected == "1") %>%
#' dplyr::select(idTree,DBH,TrunkHeight,TreeHeight,CrownHeight,
#' CrownDiameter,Selected, Xutm, Yutm) %>%
#' dplyr::sample_n(1) %>% # just 1 row (1 tree)
#' # force the orientation success for the exemple
#' tibble::add_column(TreeFellingOrientationSuccess = "1")
#'
#' rslt <- felling1tree(dat,
#' fuel = "0", winching = "0", directionalfelling = "0",
#' maintrailsaccess = SecondaryTrails$MainTrailsAccess,
#' scndtrail = SecondaryTrails$SmoothedTrails,
#' FutureReserveCrowns = FutureReserveCrowns,
#' advancedloggingparameters = loggingparameters())
#'
#' library(ggplot2)
#' ggplot() +
#' geom_sf(data = sf::st_set_crs(sf::st_sfc(rslt$Foot),
#' sf::st_crs(SecondaryTrails$MainTrailsAccess))) +
#' geom_sf(data = sf::st_set_crs(sf::st_as_sf(rslt$Trail),
#' sf::st_crs(SecondaryTrails$MainTrailsAccess))) +
#' geom_sf(data = rslt$NearestPoints) +
#' geom_sf(data = sf::st_set_crs(sf::st_sfc(rslt$TrailPt),
#' sf::st_crs(SecondaryTrails$MainTrailsAccess))) +
#' geom_sf(data = sf::st_set_crs(rslt$FallenTree,
#' sf::st_crs(SecondaryTrails$MainTrailsAccess))) +
#' geom_sf(data = sf::st_set_crs(FutureReserveCrowns,
#' sf::st_crs(SecondaryTrails$MainTrailsAccess))) # set a crs
#'
felling1tree <- function(
dat,
fuel,
winching,
directionalfelling,
maintrailsaccess,
scndtrail,
FutureReserveCrowns,
advancedloggingparameters = loggingparameters()
){
# Arguments check
if(!inherits(dat, "data.frame"))
stop("The 'dat' argument of the 'felling1tree' function must be data.frame")
if(nrow(dat)!=1)
stop("the data.frame given in the 'dat' argument
of the 'felling1tree' function must contain only 1 row")
if (!any(fuel == "0" || fuel == "1"|| fuel == "2"|| is.null(fuel)))
stop("The 'fuel' argument of the 'felling1tree' function must be '0', '1', '2' or NULL")
if (!any(directionalfelling == "0" || directionalfelling == "1"
|| directionalfelling == "2" || is.null(directionalfelling)))
stop("The 'directionalfelling' argument of the 'felling1tree' function must be '0', '1', '2' or NULL")
if(!all(unlist(lapply(list(maintrailsaccess, scndtrail), inherits, c("sf", "sfc")))))
stop("The 'maintrailsaccess' and 'scndtrail'arguments of the 'felling1tree' function must be sf or sfc")
if(!inherits(advancedloggingparameters, "list"))
stop("The 'advancedloggingparameters' argument of the 'felling1tree' function must be a list")
# Global variables
Accessible <- CircCorr <- CodeAlive <- NULL
Condition <- DBH <- NULL
DeathCause <- DistCriteria <- Family <- CrownHeight <- CrownDiameter <- NULL
Genus <- Logged <- TreePolygon <- Crowns <- NULL
LoggingStatus <- MaxFD <- MaxFD.genus <- NULL
MaxFD.species <- MinFD <- MinFD.genus <- MinFD.species <- NULL
ProbedHollow <- ProbedHollowProba <- ScientificName <- NULL
Selected <- SlopeCriteria <- Species <- Species.genus <- NULL
Taxo <- Taxo.family <- Taxo.genus <- Taxo.species <- NULL
TreeFellingOrientationSuccess <- TreeHarvestableVolume <- NULL
TreeHeight <- TrunkHeight <- Up <- UpMinFD <- UpMinFD.genus <- NULL
UpMinFD.species <- NULL
VolumeCumSum <- Xutm <- Yutm <- aCoef <- NULL
alpha <- alpha.family <- alpha.genus <- alpha.species <- bCoef <- NULL
beta.family <- beta.genus <- beta.species <- geometry <- idTree <- NULL
#### Individual geometry ####
# The foot
Foot <- st_point(c(dat$Xutm,dat$Yutm))
Foot_crs <- st_sfc(Foot) # as sfc
Foot_crs <- st_set_crs(Foot_crs, st_crs(maintrailsaccess)) # set a crs
# The crown
Crown <- dat %>%
mutate(xCrown = Xutm,
yCrown = Yutm + TrunkHeight + CrownHeight/2,
exCrown = CrownDiameter/2,
eyCrown = CrownHeight/2) %>%
st_as_sf(coords = c("xCrown", "yCrown")) # ellipse centroid coordinates
Crown <- st_ellipse(Crown, Crown$exCrown, Crown$eyCrown) # create the ellipse
# The trunk
Trunk <- with(dat,
st_polygon(list(matrix(c(Xutm-(DBH/100)/2, Yutm,
Xutm-(DBH/100)/2, Yutm + TrunkHeight,
Xutm+(DBH/100)/2, Yutm + TrunkHeight,
Xutm+(DBH/100)/2, Yutm,
Xutm-(DBH/100)/2, Yutm) # the return
,ncol=2, byrow=TRUE))))
#### Tree-Trail link ####
# Find the point (TrailPt) on the Trail closest to the location of the tree (Foot)
Trail <- st_union(maintrailsaccess, scndtrail) # Our trail will be maintrailsaccess or scndtrail
#Trail <- scndtrail # only scndtrail
NearestPoints <- st_nearest_points(Foot_crs, Trail) # from the Foot of the tree to the Trail (linestring)
NearestPoint <- st_cast(NearestPoints, "POINT") # to have start (Foot) and end (TrailPt) points
TrailPt <- NearestPoint[[2]] # the point (TrailPt) on the Trail closest to the location of the tree (Foot)
#### Angles ####
# Compute the angle between the tree default position and the shortest way from the foot to the trail
## Right-hand trail
if(TrailPt[1] >= Foot[1]){ # x trail > x foot
theta <- as.numeric(matlib::angle(c(Foot[1] - Foot[1], dat$TreeHeight),
c(TrailPt[1] - Foot[1], TrailPt[2] - Foot[2]), degree = TRUE))
## Left-hand trail
}else if(TrailPt[1] < Foot[1]){ # x trail < x foot
theta <- 180 - as.numeric(matlib::angle(c(Foot[1] - Foot[1], dat$TreeHeight),
c(TrailPt[1] - Foot[1], TrailPt[2] - Foot[2]), degree = TRUE))
}
## when the tree is on the trail (no angle)
if(is.na(theta)) theta <- 0L
# Felling angle
TreefallOrientation <- as.numeric(c(1:179)) # angle limits
OppAng <- 180-(90 + TreefallOrientation) # Compute the third angle of the right-angled triangle (see vignette figure)
# For cable (default = [30;45°]) if directionalfelling == "2" or fuel != "0"
if(directionalfelling == "2" || winching == "2" || fuel != "0"){
CableTreefallOrientation <- as.numeric(c(advancedloggingparameters$MinTreefallOrientation:
advancedloggingparameters$MaxTreefallOrientation))
CableOppAng <- 180-(90 + CableTreefallOrientation) # the angle between the closest position to the trail (90°) and the desired position (desired angle to the trail)
}
## Angle to the trail
# if no angle to the trail or fuel wood exploitation
if (directionalfelling != "2" && fuel == "0") {
### Right-hand trail
if(TrailPt[1] >= Foot[1]){ # x trail > x foot
Aangle <- round(as.numeric(180 + OppAng + theta), digits = 0) # Foot oriented
### Left-hand trail
}else if(TrailPt[1] < Foot[1]){ # x trail < x foot
Aangle <- round(as.numeric(360 - OppAng + theta), digits = 0) # Foot oriented
}
}
# if winching by grapple and cable without fuel wood exploitation
if(winching == "2" && fuel == "0"){
# Angle depending on whether the trail is to the right/left of the default position of the tree
## Right-hand trail
if(TrailPt[1] >= Foot[1]){ # x trail > x foot
# CABLE
CableAangle <- round(as.numeric(180 + CableOppAng + theta), digits = 0)
CableBangle <- round(as.numeric(180 - CableOppAng + theta), digits = 0)
# GRAPPLE
Aangle <- round(as.numeric(180 + OppAng + theta), digits = 0) # Foot oriented
## Left-hand trail
}else if(TrailPt[1] < Foot[1]){ # x trail < x foot
# CABLE
CableAangle <- round(as.numeric(360 - CableOppAng + theta), digits = 0)
CableBangle <- round(as.numeric(theta + CableOppAng), digits = 0)
# GRAPPLE
Aangle <- round(as.numeric(360 - OppAng + theta), digits = 0) # Foot oriented
}
}
# if angle to the trail or fuel wood exploitation
if ((directionalfelling == "2" && winching != "2") || (fuel =="1" || fuel =="2")) {
# Angle depending on whether the trail is to the right/left of the default position of the tree
## Right-hand trail
if(TrailPt[1] >= Foot[1]){ # x trail > x foot
# Foot oriented
Aangle <- round(as.numeric(180 + CableOppAng + theta), digits = 0) # CABLE
Bangle <- round(as.numeric(180 - CableOppAng + theta), digits = 0) # CABLE
# Crown oriented
CrownAangle <- as.numeric(theta + OppAng) # GRAPPLE
## Left-hand trail
}else if(TrailPt[1] < Foot[1]){ # x trail < x foot
# Foot oriented
Aangle <- round(as.numeric(360 - CableOppAng + theta), digits = 0) # CABLE
Bangle <- round(as.numeric(theta + CableOppAng), digits = 0) # CABLE
# Crown oriented
CrownAangle <- as.numeric(180 + OppAng + theta) # GRAPPLE
}
}
#### Felling function ####
felling0angle <- function(Angl){
Trunksf <- st_as_sf(rotatepolygon(Trunk, angle = Angl, fixed = Foot))
Crownsf <- st_as_sf(rotatepolygon(Crown, angle = Angl, fixed = Foot))
Trunk_Crowns <- Trunksf %>% rbind(Crownsf)
FallenTree <- st_sfc(do.call(c, st_geometry(Trunk_Crowns)))
# Previous version
# FallenTree <- st_difference(st_union(
# rotatepolygon(Trunk, angle = Angl, fixed = Foot), # turned trunk
# rotatepolygon(Crown, angle = Angl, fixed = Foot) # turned crown
# ))
return(FallenTree)
}
#### Scenarios ####
#### For a totally random direction felling ####
# if(totally random)){
# RandomAngle <- as.numeric(sample(c(0:359), size = 1))
# FallenTree <- felling0angle(RandomAngle)
# }
#### No other orientation than to direct the foot of the tree towards the trail (0-180°) ####
if (directionalfelling == "0" && fuel == "0" && winching != "2") {
if(dat$TreeFellingOrientationSuccess == "1"){
Aangle <- as.numeric(sample(Aangle, size = 1))
FallenTree <- felling0angle(Aangle)
}else if(dat$TreeFellingOrientationSuccess == "0"){ # else random felling
RandomAngle <- as.numeric(sample(c(0:359), size = 1))
FallenTree <- felling0angle(RandomAngle)
}
}
#### To direct to !avoid damage to future and reserve trees!, and foot directed to the trail
if (directionalfelling == "1" && fuel == "0" && winching != "2") {
if(dat$TreeFellingOrientationSuccess == "1"){
# Available felling positions
AvailableFellingPositions <- lapply(as.list(Aangle), felling0angle) %>%
lapply(function(x) data.frame(Tree = x)) %>%
bind_rows()
# Overlaps with future/reserve trees
overlaps <- st_intersects(AvailableFellingPositions$geometry,
summarise(FutureReserveCrowns, Crowns = st_combine(Crowns))$Crowns) %>%
lengths()
## if there are any position without intersection
if(any(overlaps == 0)){
EmptyFellingPositions <- AvailableFellingPositions %>%
mutate(overlaps = overlaps) %>%
filter(overlaps == 0) # only positions without intersection
## if there are no position without intersection
}else{
EmptyFellingPositions <- AvailableFellingPositions # take all the positions
message("It was not possible to avoid future/reserve trees in the felling of a tree")
}
# Sample only one position
FallenTree <- sample_n(EmptyFellingPositions, 1)$geometry
}else if(dat$TreeFellingOrientationSuccess == "0"){ # else random felling
RandomAngle <- as.numeric(sample(c(0:359), size = 1))
FallenTree <- felling0angle(RandomAngle)
}
}
#### Scenarios with orientation angle to the trail or fuel wood exploitation ####
#### To direct to avoid damage to future and reserve trees + orientation angle to the trail (no fuel wood exploitation) ####
if(winching == "1" && directionalfelling == "2" && fuel == "0"){ # = CABLE only
if(dat$TreeFellingOrientationSuccess == "1"){
# Calculate the two possible crown configurations
ACrown <- lapply(as.list(Aangle), function(element) rotatepolygon(Crown, angle = element, fixed = Foot)) # turned crowns
BCrown <- lapply(as.list(Bangle), function(element) rotatepolygon(Crown, angle = element, fixed = Foot)) # turned crowns
ACrown <- lapply(ACrown, function(element) st_set_crs(element, st_crs(maintrailsaccess))) # set a crs
BCrown <- lapply(BCrown, function(element) st_set_crs(element, st_crs(maintrailsaccess))) # set a crs
# Test the best to pull the tree back to the main trail (farthest crown from the main trail)
ADist <- unlist(lapply(as.list(ACrown), function(element) st_distance(element, maintrailsaccess)[1,1])) #matrix to value
BDist <- unlist(lapply(as.list(BCrown), function(element) st_distance(element, maintrailsaccess)[1,1])) #matrix to value
if(max(ADist, BDist) == max(BDist)){
Aangle <- Bangle # B configuration
}
# Available felling positions
AvailableFellingPositions <- lapply(as.list(Aangle), felling0angle) %>%
lapply(function(x) data.frame(Tree = x)) %>%
bind_rows()
# Overlaps with future/reserve trees
overlaps <- st_intersects(AvailableFellingPositions$geometry,
summarise(FutureReserveCrowns, Crowns = st_combine(Crowns))$Crowns) %>%
lengths()
## if there are any position without intersection
if(any(overlaps == 0)){
EmptyFellingPositions <- AvailableFellingPositions %>%
mutate(overlaps = overlaps) %>%
filter(overlaps == 0) # only positions without intesection
## if there are no position without intersection
}else{
EmptyFellingPositions <- AvailableFellingPositions # take all the positions
message("It was not possible to avoid future/reserve trees in the felling of a tree")
}
# Sample only one position
FallenTree <- sample_n(EmptyFellingPositions, 1)$geometry
}else if(dat$TreeFellingOrientationSuccess == "0"){ # else random felling
RandomAngle <- as.numeric(sample(c(0:359), size = 1))
FallenTree <- felling0angle(RandomAngle)
}
}
#### Grapple + cable without fuel wood exploitation (always foot towards the trail) ####
if(winching == "2" & fuel == "0"){
# TrailDist <- st_distance(Foot, TrailPt) # distance between the tree foot and the Trail closest point
if(dat$TreeFellingOrientationSuccess == "1"){
if(dat$WinchingMachine %in% "Grpl"){ # accessible by grapple (slope and length) -> winching by grapple
if(directionalfelling == "0"){
Aangle <- as.numeric(sample(Aangle, size = 1))
FallenTree <- felling0angle(Aangle)
# Fut/res tree avoidance
}else{
# Available felling positions
AvailableFellingPositions <- lapply(as.list(Aangle), felling0angle) %>%
lapply(function(x) data.frame(Tree = x)) %>%
bind_rows()
# Overlaps with future/reserve trees
overlaps <- st_intersects(AvailableFellingPositions$geometry,
summarise(FutureReserveCrowns, Crowns = st_combine(Crowns))$Crowns) %>%
lengths()
## if there are any position without intersection
if(any(overlaps == 0)){
EmptyFellingPositions <- AvailableFellingPositions %>%
mutate(overlaps = overlaps) %>%
filter(overlaps == 0) # only positions without intesection
## if there are no position without intersection
}else{
EmptyFellingPositions <- AvailableFellingPositions # take all the positions
message("It was not possible to avoid future/reserve trees in the felling of a tree")
}
# Sample only one position
FallenTree <- sample_n(EmptyFellingPositions, 1)$geometry
} # end Fut/res tree avoidance
}else{ # > 6m -> winching by CABLE
# Calculate the two possible crown configurations
ACrown <- lapply(as.list(CableAangle), function(element) rotatepolygon(Crown, angle = element, fixed = Foot)) # turned crowns
BCrown <- lapply(as.list(CableBangle), function(element) rotatepolygon(Crown, angle = element, fixed = Foot)) # turned crowns
ACrown <- lapply(ACrown, function(element) st_set_crs(element, st_crs(maintrailsaccess))) # set a crs
BCrown <- lapply(BCrown, function(element) st_set_crs(element, st_crs(maintrailsaccess))) # set a crs
# Test the best to pull the tree back to the main trail (farthest crown from the main trail)
ADist <- unlist(lapply(as.list(ACrown), function(element) st_distance(element, maintrailsaccess)[1,1])) #matrix to value
BDist <- unlist(lapply(as.list(BCrown), function(element) st_distance(element, maintrailsaccess)[1,1])) #matrix to value
if(max(ADist, BDist) == max(BDist)){
CableAangle <- CableBangle # B configuration
}
# No fut/res tree avoidance
if(directionalfelling == "0"){
CableAangle <- as.numeric(sample(CableAangle, size = 1))
FallenTree <- felling0angle(CableAangle)
# Fut/res tree avoidance
}else{
# Available felling positions
AvailableFellingPositions <- lapply(as.list(CableAangle), felling0angle) %>%
lapply(function(x) data.frame(Tree = x)) %>%
bind_rows()
# Overlaps with future/reserve trees
overlaps <- st_intersects(AvailableFellingPositions$geometry,
summarise(FutureReserveCrowns, Crowns = st_combine(Crowns))$Crowns) %>%
lengths()
## if there are any position without intersection
if(any(overlaps == 0)){
EmptyFellingPositions <- AvailableFellingPositions %>%
mutate(overlaps = overlaps) %>%
filter(overlaps == 0) # only positions without intersection
## if there are no position without intersection
}else{
EmptyFellingPositions <- AvailableFellingPositions # take all the positions
message("It was not possible to avoid future/reserve trees in the felling of a tree")
}
# Sample only one position
FallenTree <- sample_n(EmptyFellingPositions, 1)$geometry
}
}
}else if(dat$TreeFellingOrientationSuccess == "0"){ # else random felling
RandomAngle <- as.numeric(sample(c(0:359), size = 1))
FallenTree <- felling0angle(RandomAngle)
}
}
#### Fuel wood exploitation (including to avoid damage to future and reserve trees + trail orientation (if cable)) ####
if(fuel =="1" || fuel =="2"){
# TrailDist <- st_distance(Foot, TrailPt) # distance between the tree foot and the Trail closest point
if(dat$TreeFellingOrientationSuccess == "1"){
if(dat$WinchingMachine %in% "Grpl"){ # accessible by grapple (slope and length) -> winching by grapple
# No fut/res tree avoidance
if(directionalfelling == "0"){
CrownAangle <- as.numeric(sample(CrownAangle, size = 1))
FallenTree <- felling0angle(CrownAangle)
# Fut/res tree avoidance
}else{
# Available felling positions
AvailableFellingPositions <- lapply(as.list(CrownAangle), felling0angle) %>%
lapply(function(x) data.frame(Tree = x)) %>%
bind_rows()
# Overlaps with future/reserve trees
overlaps <- st_intersects(AvailableFellingPositions$geometry,
summarise(FutureReserveCrowns, Crowns = st_combine(Crowns))$Crowns) %>%
lengths()
## if there are any position without intersection
if(any(overlaps == 0)){
EmptyFellingPositions <- AvailableFellingPositions %>%
mutate(overlaps = overlaps) %>%
filter(overlaps == 0) # only positions without intesection
## if there are no position without intersection
}else{
EmptyFellingPositions <- AvailableFellingPositions # take all the positions
message("It was not possible to avoid future/reserve trees in the felling of a tree")
}
# Sample only one position
FallenTree <- sample_n(EmptyFellingPositions, 1)$geometry
} # end Fut/res tree avoidance
}else{ # > 6m -> winching by CABLE -> foot to trail
# Calculate the two possible crown configurations
ACrown <- lapply(as.list(Aangle), function(element) rotatepolygon(Crown, angle = element, fixed = Foot)) # turned crowns
BCrown <- lapply(as.list(Bangle), function(element) rotatepolygon(Crown, angle = element, fixed = Foot)) # turned crowns
ACrown <- lapply(ACrown, function(element) st_set_crs(element, st_crs(maintrailsaccess))) # set a crs
BCrown <- lapply(BCrown, function(element) st_set_crs(element, st_crs(maintrailsaccess))) # set a crs
# Test the best to pull the tree back to the main trail (farthest crown from the main trail)
ADist <- unlist(lapply(as.list(ACrown), function(element) st_distance(element, maintrailsaccess)[1,1])) #matrix to value
BDist <- unlist(lapply(as.list(BCrown), function(element) st_distance(element, maintrailsaccess)[1,1])) #matrix to value
if(max(ADist, BDist) == max(BDist)){
Aangle <- Bangle # B configuration
}
# No fut/res tree avoidance
if(directionalfelling == "0"){
Aangle <- as.numeric(sample(Aangle, size = 1))
FallenTree <- felling0angle(Aangle)
# Fut/res tree avoidance
}else{
# Available felling positions
AvailableFellingPositions <- lapply(as.list(Aangle), felling0angle) %>%
lapply(function(x) data.frame(Tree = x)) %>%
bind_rows()
# Overlaps with future/reserve trees
overlaps <- st_intersects(AvailableFellingPositions$geometry,
summarise(FutureReserveCrowns, Crowns = st_combine(Crowns))$Crowns) %>%
lengths()
## if there are any position without intersection
if(any(overlaps == 0)){
EmptyFellingPositions <- AvailableFellingPositions %>%
mutate(overlaps = overlaps) %>%
filter(overlaps == 0) # only positions without intesection
## if there are no position without intersection
}else{
EmptyFellingPositions <- AvailableFellingPositions # take all the positions
message("It was not possible to avoid future/reserve trees in the felling of a tree")
}
# Sample only one position
FallenTree <- sample_n(EmptyFellingPositions, 1)$geometry
}
}
}else if(dat$TreeFellingOrientationSuccess == "0"){ # else random felling
RandomAngle <- as.numeric(sample(c(0:359), size = 1))
FallenTree <- felling0angle(RandomAngle)
}
}
FellingOuputs <- list(Foot = Foot, # Foot_crs
NearestPoints = NearestPoints,
TrailPt = TrailPt, # set a crs : st_set_crs(st_sfc(TrailPt), st_crs(maintrailsaccess))
Trail = Trail, # set a crs : st_set_crs(st_as_sf(Trail), st_crs(maintrailsaccess))
FallenTree = FallenTree # set a crs : st_set_crs(st_sfc(FallenTree), st_crs(maintrailsaccess))
)
return(FellingOuputs)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.