| Title: | Calculate Tree Traits from Terrestrial Lidar |
|---|---|
| Description: | Measuring tree architecture from terrestrial lidar data, including tree-level properties, crown characteristics, and structural attributes derived from quantitative structure models (QSMs). |
| Authors: | Jeffery B. Cannon [aut, cre] |
| Maintainer: | Jeffery B. Cannon <[email protected]> |
| License: | GPL-3 |
| Version: | 0.1.2 |
| Built: | 2026-05-26 06:47:11 UTC |
| Source: | https://github.com/cran/tReeTraits |
Reads a PyTLidar-generated cylinder file and converts it into a tidy data frame with start/end coordinates, radius, length, volume, and branching order.
.read_qsm_raw(cyl_file).read_qsm_raw(cyl_file)
cyl_file |
Path to the PyTLidar cylinder output file (.txt). |
A data frame with columns startX, startY, startZ, endX, endY, endZ, cyl_ID, parent_ID, extension_ID, radius_cyl, length, volume, branching_order.
Computes a verticality metric (0-1) for each point in a LAS object based on the z-component of the dominant local PCA eigenvector.
add_verticality(las, k = 30, name = "verticality")add_verticality(las, k = 30, name = "verticality")
las |
A |
k |
Number of nearest neighbors for local PCA. |
name |
Name of the attribute to store. |
The input 'LAS' object with a new numeric attribute containing verticality values (0–1).
las = lidR::readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = add_verticality(las, k = 20) head(las@data)las = lidR::readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = add_verticality(las, k = 20) head(las@data)
Generates a diagnostic 2D plot showing basic tree measurements such as tree height, crown base height (CBH), crown width, and diameter at breast height (DBH) over a subsampled LAS point cloud.
basics_diagnostic_plot(las, height, cbh, crown_width, dbh, res = 0.1)basics_diagnostic_plot(las, height, cbh, crown_width, dbh, res = 0.1)
las |
A 'LAS' object (from the 'lidR' package) containing the tree point cloud. |
height |
Numeric. Total tree height. |
cbh |
Numeric. Crown base height. |
crown_width |
Numeric. Crown width. |
dbh |
Numeric. Diameter at breast height. |
res |
Numeric. Resolution for point cloud thinning; default is 0.1 m. |
The function first thins the point cloud using 'lidR::decimate_points()' to improve plotting speed. Then it overlays key measurement lines and markers for height, crown width, DBH, and crown base height.
A 'ggplot' object displaying thinned tree points and markers for key measurements.
library(lidR) path = system.file('extdata', 'tree_0744.laz', package='tReeTraits') las = clean_las(readLAS(path)) ht = get_height(las) dbh = get_dbh(las) wid = get_width(las)[1] cbh = get_crown_base(las) basics_diagnostic_plot(las, height = ht, cbh = cbh, crown_width = wid, dbh = dbh)library(lidR) path = system.file('extdata', 'tree_0744.laz', package='tReeTraits') las = clean_las(readLAS(path)) ht = get_height(las) dbh = get_dbh(las) wid = get_width(las)[1] cbh = get_crown_base(las) basics_diagnostic_plot(las, height = ht, cbh = cbh, crown_width = wid, dbh = dbh)
Generates a diagnostic plot showing the distribution of branch diameters in a QSM. This is a wrapper around 'branch_size_distribution()' which computes branch metrics.
branch_distribution_plot(qsm)branch_distribution_plot(qsm)
qsm |
A QSM object (e.g., data frame returned by 'run_treeqsm()') containing cylinder information. |
The function calls 'branch_size_distribution()' with 'plot = FALSE' to compute branch volumes at midpoints of diameter bins, then generates a bar plot showing total volume per diameter bin.
A 'ggplot' object displaying branch diameter (x-axis) versus total branch volume (y-axis).
qsm_file = system.file('extdata',"tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) branch_distribution_plot(qsm)qsm_file = system.file('extdata',"tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) branch_distribution_plot(qsm)
This function outputs the volume (in mL) distribution of branches across different branch diameter classes (in cm). Outputs a table that can be used in other functions to find branch_skewness or branch_volume_weighted_stats()
branch_size_distribution(qsm, breaks = NULL, plot = TRUE)branch_size_distribution(qsm, breaks = NULL, plot = TRUE)
qsm |
– a QSM loaded using '[load_qsm()]'. |
breaks |
numeric – a vector of diameter classes (in cm) by which to summarize branch volume. If 'NULL' the branch of branch sizes will be distributed across 1 cm bins. |
plot |
boolean – indicates whether the branch diameter distribution should be plotted as a histogram. |
A tibble summarizing branch volume by diameter class with columns:
Factor indicating the diameter class (cm).
Numeric midpoint (cm) of each diameter class.
Total branch volume (mL) within the class.
Returns 'NA' if fewer than two branch cylinders are present.
qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) branch_distribution = branch_size_distribution(qsm, plot=TRUE) print(branch_distribution) #volume-weighted mean branch_volume_weighted_stats(qsm, FUN = function(x) mean(x)) #volume-weighted median branch_volume_weighted_stats(qsm, FUN = function(x) median(x)) # volume-weighted skewness branch_volume_weighted_stats(qsm, FUN = function(x) 3*(mean(x) - median(x)) / sd(x))qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) branch_distribution = branch_size_distribution(qsm, plot=TRUE) print(branch_distribution) #volume-weighted mean branch_volume_weighted_stats(qsm, FUN = function(x) mean(x)) #volume-weighted median branch_volume_weighted_stats(qsm, FUN = function(x) median(x)) # volume-weighted skewness branch_volume_weighted_stats(qsm, FUN = function(x) 3*(mean(x) - median(x)) / sd(x))
This function calculates statistics on branch diameters weighted by the volume of branches of that size based on outputs from 'branch_size_distribution()'. The user defined function 'FUN' can take any form of f(x) where x is a vector of diameters of length 1 for every mL of volume of that branch size class. See Details for recommended values for 'FUN'. #Details Values of central tendency are recommended, but not variance since the weighted means are simulated.
branch_volume_weighted_stats(qsm, breaks = NULL, FUN = function(x) mean(x))branch_volume_weighted_stats(qsm, breaks = NULL, FUN = function(x) mean(x))
qsm |
a QSM loaded using '[load_qsm()]'. |
breaks |
numeric – a vector of diameter classes (in cm) by which to summarize branch volume. If 'NULL' the branch of branch sizes will be distributed across 1 cm bins. |
FUN |
function – central tendency function to be weighted based on branch volume. |
Recommended values of 'FUN' are:
Mean FUN = function(x) mean(x)
Median FUN = function(x) median(x)
Skewness FUN = function(x) 3*(mean(x) - median(x)) / sd(x)
A numeric value representing the volume-weighted statistic calculated by 'FUN' across branch diameter classes.
qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) branch_distribution = branch_size_distribution(qsm, plot=TRUE) print(branch_distribution) #volume-weighted mean branch_volume_weighted_stats(qsm, FUN = function(x) mean(x)) #volume-weighted median branch_volume_weighted_stats(qsm, FUN = function(x) median(x)) # volume-weighted skewness branch_volume_weighted_stats(qsm, FUN = function(x) 3*(mean(x) - median(x)) / sd(x))qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) branch_distribution = branch_size_distribution(qsm, plot=TRUE) print(branch_distribution) #volume-weighted mean branch_volume_weighted_stats(qsm, FUN = function(x) mean(x)) #volume-weighted median branch_volume_weighted_stats(qsm, FUN = function(x) median(x)) # volume-weighted skewness branch_volume_weighted_stats(qsm, FUN = function(x) 3*(mean(x) - median(x)) / sd(x))
Function to normalize, remove noise, remove vegetation, and recenter 'LAS' representing segmented tree. Vegetation cleaning is accomplished by identifying stem points (CrownScrochTLS::StemPoints) and removing all but the Stem below the 'z.threshold'.
clean_las(las, bole_height = 1, quantile = 0.001)clean_las(las, bole_height = 1, quantile = 0.001)
las |
'LAS' object from 'lidR' package representing individually segmented tree |
bole_height |
numeric, height threshold below which all stem points can be considered vegetation. |
quantile |
See 'normalize_las'. Z quantile at which grown level is specified since ground points may not be identifiable with common algorithms if ground points are removed during segmentation#' |
A cleaned 'LAS' object with vegetation and noise removed, normalized and recentered.
library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las_cleaned = clean_las(las) plot(las) plot(las_cleaned)library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las_cleaned = clean_las(las) plot(las) plot(las_cleaned)
This function generates an 'sf' object representing th vertical crown area of a 'LAS' object based using the convex hull of a 2D vertical projection.
convex_hull_2D(las, angle = 0)convex_hull_2D(las, angle = 0)
las |
'LAS' object from 'lidR' package representing the CROWN of a tree. Crowns must be segmented using [segment_crown()]. |
angle |
numeric - in degrees, rotation angle about Z axis. |
An 'sf' polygon representing the vertical convex hull of the crown projection.
las = lidR::readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) cbh = get_crown_base(las, threshold=0.25, sustain=2) las = segment_crown(las, cbh) get_crown_volume_voxel(las) get_crown_volume_alpha(las) sf::st_area(convex_hull_2D(las)) #profile area, convex hull sf::st_area(voxel_hull_2D(las)) #profile area, voxel hull get_lacunarity(las)las = lidR::readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) cbh = get_crown_base(las, threshold=0.25, sustain=2) las = segment_crown(las, cbh) get_crown_volume_voxel(las) get_crown_volume_alpha(las) sf::st_area(convex_hull_2D(las)) #profile area, convex hull sf::st_area(voxel_hull_2D(las)) #profile area, voxel hull get_lacunarity(las)
This function fits a taper equation to trunk sections of a QSM using Kozak the Model (2002, 2007).
fit_taper_Kozak( qsm, dbh, terminus_diam_cm = 4, segment_size = 0.25, plot = TRUE )fit_taper_Kozak( qsm, dbh, terminus_diam_cm = 4, segment_size = 0.25, plot = TRUE )
qsm |
a QSM loaded using '[load_qsm()]'. |
dbh |
numeric – required to fit Kozak model, not calculated from QSM, so as not to conflict with other more accurate means of measurement e.g., ‘get_DBH’ |
terminus_diam_cm |
numeric – the trunk diameter at which is no longer considered trunk |
segment_size |
numeric – the length of segments that QSM cylinders are grouped into |
plot |
boolean – indicates whether model output should be plotted. Plots are found in the output list as object$plot, regardless of this setting. |
$ d(h)/D = a0 (h/H) + a1 (h/H) + a2 (h/H)^2 + a3 (h/H)^3 $
The function groups QSM cylinders into segments of 'segment_size' up to 'terminus_diam' which is the maximum diameter at which the taper equation ends.
A list with components:
Tibble of trunk segment heights and observed diameters used in model fitting.
A 'ggplot2' object showing observed diameters and fitted taper curve.
Data frame containing fitted Kozak parameters ('a0'–'a3'), coefficient of determination ('r2'), and root mean squared error ('rmse').
qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) fit_taper_Kozak(qsm, dbh = 13.8)qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) fit_taper_Kozak(qsm, dbh = 13.8)
Generate a diagnostic plot to assess basic metrics and QSM output
full_diagnostic_plot(las, qsm, height, cbh, crown_width, dbh, res = 0.1)full_diagnostic_plot(las, qsm, height, cbh, crown_width, dbh, res = 0.1)
las |
'LAS' object from 'lidR' package representing the CROWN of a tree. Crowns can be segmented using [segment_crown()] |
qsm |
a QSM loaded using '[load_qsm()]'. |
height |
numeric - tree height, or generated from 'get_height()' |
cbh |
numeric - crown base heigth, or generated from 'get_crown_base()'. |
crown_width |
numeric - crown width height, or generated from 'get_width()' |
dbh |
numeric - in cm, tree diameter at breast height, or generated from 'get_dbh()' |
res |
numeric - resolution of voxelization to speed up plotting |
A multi-panel 'ggplot' object (class 'ggarrange') summarizing tree structural metrics and QSM diagnostics, suitable for printing or saving with 'ggplot2'.
library(lidR) las_file = system.file("extdata", "tree_0744.laz", package="tReeTraits") las = lidR::readLAS(las_file, filter = '-thin_with_voxel 0.1') las = clean_las(las, bole_height=3) height = get_height(las) crown_width = get_width(las) dbh = get_dbh(las, select_n=30) cbh = get_crown_base(las, threshold=0.25, sustain=2) las = segment_crown(las, cbh) qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) full_diagnostic_plot(las=las, qsm=qsm, height=height, cbh=cbh, crown_width=crown_width, dbh=dbh)library(lidR) las_file = system.file("extdata", "tree_0744.laz", package="tReeTraits") las = lidR::readLAS(las_file, filter = '-thin_with_voxel 0.1') las = clean_las(las, bole_height=3) height = get_height(las) crown_width = get_width(las) dbh = get_dbh(las, select_n=30) cbh = get_crown_base(las, threshold=0.25, sustain=2) las = segment_crown(las, cbh) qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) full_diagnostic_plot(las=las, qsm=qsm, height=height, cbh=cbh, crown_width=crown_width, dbh=dbh)
This function calculates the area of the tree profile by breaking it into segments of height 'segment_height' and estimating the width of each segement. Area profiles are useful for caluclating total area, but also used to detect crown base height.
get_area_profile(las, segment_height = 0.25, quantile = c(0.001), angle = 0)get_area_profile(las, segment_height = 0.25, quantile = c(0.001), angle = 0)
las |
'LAS' object from 'lidR' package representing individually segmented tree, with the crown labeled. |
segment_height |
numeric - height of each segment in which to calculate area |
quantile |
numeric - quantile at which width is measured Values in the interval approaching 0 (e.g., 0.001) are recommended to trim random noise |
angle |
numeric - angle at which to rotate the point cloud prior to estimating area. Useful in a loop if quantifying mulitple angles |
A tibble with columns 'bottom', 'top', 'width', 'area', and 'angle' describing the vertical area profile.
This function calculates the center of mass/volume from a QSM by estimating the centroid of cylinder locations, each weighted by their volume. Only trunk sections are included (e.g., 'branching_order == 0'). For center of mass, assumes constant density within segments.
get_center_of_mass(qsm)get_center_of_mass(qsm)
qsm |
qsm object loaded from '[load_qsm]'. |
A tibble with one row and three numeric columns:
Volume-weighted X coordinate of the trunk center of mass (m).
Volume-weighted Y coordinate of the trunk center of mass (m).
Volume-weighted Z coordinate of the trunk center of mass (m).
qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) print(get_center_of_mass(qsm))qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) print(get_center_of_mass(qsm))
This function extracts the horizontal distance between the base of a tree and the center of a tree from a QSM. The function takes the coordinate of the lowest QSM segment, and the center of mass, and finds the horizontal distance between them.
get_com_offset(qsm)get_com_offset(qsm)
qsm |
a QSM loaded using '[load_qsm()]'. |
A single numeric value giving the horizontal distance (m) between the base of the tree and the volume-weighted center of mass.
qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) print(get_center_of_mass(qsm)) print(get_com_offset(qsm))qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) print(get_center_of_mass(qsm)) print(get_com_offset(qsm))
This function estimates the crown base height by analyzing the vertical profile of the tree using [get_area_profile()] which breaks the profile into segments of height 'segment_height'. The function estimates segments exceed a threshold specified by 'threshold' which must be exceeded 'sustain' times.
get_crown_base( las, threshold = 0.5, sustain = 2, segment_height = 0.25, quantile = 0.01 )get_crown_base( las, threshold = 0.5, sustain = 2, segment_height = 0.25, quantile = 0.01 )
las |
'LAS' object from 'lidR' package representing individually segmented tree |
threshold |
numeric - threshold width at which crown becomes apparent. Recommend a width ~2X greater than anticipated DBH. |
sustain |
numeric - number of segments in a row that treshold must be exceeded before identifying start of crown. This is to exclude small segments of crown isolated from larger continuous crown. |
segment_height |
numeric - height of each segment in which to calculate area |
quantile |
numeric - quantile at which width is measured Values in the interval approaching 0 (e.g., 0.001) are recommended to trim random noise |
A named numeric vector with element 'crown_base_height' (m).
# example code library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package = "tReeTraits")) las = clean_las(las) # Estimate crown base height cbh = get_crown_base(las) print(cbh)# example code library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package = "tReeTraits")) las = clean_las(las) # Estimate crown base height cbh = get_crown_base(las) print(cbh)
This function calculates the lever arm of canopies. The function #' is a simple wrapper for 'get_area_profile()'. It calculates the crown area in segments defined by 'segement_height', multiplies the area of each of those segments by their height, and then returns the sum of all segments. This is proportaionl to drag calculations on the tree assuming windspeed is invariant with height.
get_crown_lever_arm(las, segment_height = 0.25, quantile = c(0.001), angle = 0)get_crown_lever_arm(las, segment_height = 0.25, quantile = c(0.001), angle = 0)
las |
'LAS' object from 'lidR' package representing individually segmented tree, with the crown labeled. See 'segment_crown()' |
segment_height |
numeric - height of each segment in which to calculate area |
quantile |
numeric - quantile at which width is measured Values in the interval approaching 0 (e.g., 0.001) are recommended to trim random noise |
angle |
numeric - angle at which to rotate the point cloud prior to estimating area. Useful in a loop if quantifying mulitple angles |
A numeric value representing the crown lever arm.
library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) las = segment_crown(las) print(get_crown_lever_arm(las))library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) las = segment_crown(las) print(get_crown_lever_arm(las))
This function volume of a 'LAS' object by thinning to a resolution specified by 'resolution', and estimating volume by fitting a alpha shape volume. Crowns must be segmented using [segment_crown()].
get_crown_volume_alpha(las, resolution = 0.1, alpha = 0.5)get_crown_volume_alpha(las, resolution = 0.1, alpha = 0.5)
las |
'LAS' object from 'lidR' package representing a tree. Crowns must be segmented using [segment_crown()]. |
resolution |
numeric - resolution of initial voxelization to increase speed |
alpha |
numeric - alpha for the computation of the 3D alpha-shape of the point cloud. See [alphashape3d::ashape3d]. |
A named numeric vector with element 'crown_volume_alpha' (m^3).
las = lidR::readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) cbh = get_crown_base(las, threshold=0.25, sustain=2) las = segment_crown(las, cbh) get_crown_volume_voxel(las) get_crown_volume_alpha(las) sf::st_area(convex_hull_2D(las)) #profile area, convex hull sf::st_area(voxel_hull_2D(las)) #profile area, voxel hull get_lacunarity(las)las = lidR::readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) cbh = get_crown_base(las, threshold=0.25, sustain=2) las = segment_crown(las, cbh) get_crown_volume_voxel(las) get_crown_volume_alpha(las) sf::st_area(convex_hull_2D(las)) #profile area, convex hull sf::st_area(voxel_hull_2D(las)) #profile area, voxel hull get_lacunarity(las)
This function volume of a 'LAS' object by thinning to a resolution specified by 'resolution', and estimating volume using the equation
get_crown_volume_voxel(las, resolution = 0.1)get_crown_volume_voxel(las, resolution = 0.1)
las |
'LAS' object from 'lidR' package representing a tree. Crowns must be segmented using [segment_crown()]. |
resolution |
numeric - resolution of voxelization |
A named numeric vector with element 'crown_volume_vox' (m^3).
las = lidR::readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) cbh = get_crown_base(las, threshold=0.25, sustain=2) las = segment_crown(las, cbh) get_crown_volume_voxel(las) get_crown_volume_alpha(las) sf::st_area(convex_hull_2D(las)) #profile area, convex hull sf::st_area(voxel_hull_2D(las)) #profile area, voxel hull get_lacunarity(las)las = lidR::readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) cbh = get_crown_base(las, threshold=0.25, sustain=2) las = segment_crown(las, cbh) get_crown_volume_voxel(las) get_crown_volume_alpha(las) sf::st_area(convex_hull_2D(las)) #profile area, convex hull sf::st_area(voxel_hull_2D(las)) #profile area, voxel hull get_lacunarity(las)
Function to extract diameter at breast height (1.37 m) from LAS object. Function filters LAS keeping only points with Intensity greater than specified threshold. Function calculates verticality eigenvalue and filters based on verticality threshold. Last, diameter is calculated using a RANSAC cylinder fitting algorithm.
get_dbh( las, intensity_threshold = 41000, select_n = 10, verticality_threshold = 0.9 )get_dbh( las, intensity_threshold = 41000, select_n = 10, verticality_threshold = 0.9 )
las |
'LAS' object from 'lidR' package representing individually segmented tree |
intensity_threshold |
numeric - filter value for Intensity to help remove vegetation |
select_n |
numeric - number of points selected on every RANSAC iteration. |
verticality_threshold |
numeric - filter value for Verticality threshold to remove horizontal branches. |
A named numeric vector with element 'dbh' (m).
library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) print(get_dbh(las))library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) print(get_dbh(las))
Function to extract height from LAS object. Function calculates difference between two specified quantiles from the 'Z' attribute.
get_height(las, quantiles = c(0, 1))get_height(las, quantiles = c(0, 1))
las |
'LAS' object from 'lidR' package representing individually segmented tree |
quantiles |
Z quantiles at which ground level and highest point are measured. Values in the interval (0,1) are recommended to trim random noise. |
A named numeric vector with element 'height' (m).
library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) print(get_height(las))library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) print(get_height(las))
This function calculates the lacunarity or "porosity" of a tree crown by defined as 1 - the ratio of a voxelized crown hull and a convex hull. See 'voxel_hull_2D()' and 'convex_hull_2D()'
get_lacunarity(las, res = 0.1, angle = 0)get_lacunarity(las, res = 0.1, angle = 0)
las |
'LAS' object from 'lidR' package representing the CROWN of a tree. Crowns must be segmented using [segment_crown()]. |
res |
numeric - resolution of voxelization |
angle |
numeric - in degrees, rotation angle about Z axis. |
A numeric value representing crown lacunarity (unitless).
las = lidR::readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) cbh = get_crown_base(las, threshold=0.25, sustain=2) las = segment_crown(las, cbh) get_crown_volume_voxel(las) get_crown_volume_alpha(las) sf::st_area(convex_hull_2D(las)) #profile area, convex hull sf::st_area(voxel_hull_2D(las)) #profile area, voxel hull get_lacunarity(las)las = lidR::readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) cbh = get_crown_base(las, threshold=0.25, sustain=2) las = segment_crown(las, cbh) get_crown_volume_voxel(las) get_crown_volume_alpha(las) sf::st_area(convex_hull_2D(las)) #profile area, convex hull sf::st_area(voxel_hull_2D(las)) #profile area, voxel hull get_lacunarity(las)
Extract primary branches from a QSM by filtering out the the trunk, and identifying all cylinders where 'branching_order == 1' and are attached to the trunk. Returns a tibble containing the basal diameter and height of attachment points.
get_primary_branches(qsm)get_primary_branches(qsm)
qsm |
a QSM loaded using '[load_qsm()]'. |
A tibble with one row per primary branch containing:
Character string ("branches").
Basal diameter of the branch (cm).
Height of branch attachment (m).
Placeholder column (currently 'NA').
qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) primary_branches = get_primary_branches(qsm) #number of primary branches nrow(primary_branches)qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) primary_branches = get_primary_branches(qsm) #number of primary branches nrow(primary_branches)
This function calculates tree sweep from a QSM. Starting with an idealized vector of a straight tree (straight line from top to bottom QSM segment) the function caclucates devations of points along the trunk from the idealized vector. It resturns sweep from each QSM segment so that summary statistics can be computed by the user. See also 'get_stem_deflection()'
get_stem_sweep(qsm, terminus_diam_cm = 4, plot = TRUE)get_stem_sweep(qsm, terminus_diam_cm = 4, plot = TRUE)
qsm |
QSM loaded using '[load_qsm()]'. |
terminus_diam_cm |
numeric – the trunk diameter at which is no longer considered trunk |
plot |
boolean – indicates whether graph of sweep should be plotted. |
A data frame with columns:
Height (m) of each trunk segment midpoint.
Perpendicular deviation (m) from the idealized straight stem line.
qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) print(get_center_of_mass(qsm)) print(get_com_offset(qsm))qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) print(get_center_of_mass(qsm)) print(get_com_offset(qsm))
This function calculates tilt of a tree from a QSM. The function identifies the upper and lower extreme segments of the QMS (trunk sections only) and computes a vector between them, and returns the devation of that angle from directly vertical.
get_stem_tilt(qsm, terminus_diam_cm = 4)get_stem_tilt(qsm, terminus_diam_cm = 4)
qsm |
a QSM loaded using '[load_qsm()]'. |
terminus_diam_cm |
numeric - trunk diameter at which it is treated as a branch. |
A single numeric value giving stem tilt in degrees from vertical.
qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) get_stem_tilt(qsm)qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) get_stem_tilt(qsm)
Function to extract width from LAS object. Function calculates difference between two specified quantiles from the 'X' and 'Y' attributes and returns both widths and their average.
get_width(las, quantiles = c(0.001, 0.999))get_width(las, quantiles = c(0.001, 0.999))
las |
'LAS' object from 'lidR' package representing individually segmented tree |
quantiles |
Z quantiles at which widths are measured are measured. Values in the interval (0,1) are recommended to trim random noise. |
A named numeric vector with elements 'mean_width', 'x_width', and 'y_width' (m).
library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) print(get_width(las))library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) print(get_width(las))
Generates a 2D diagnostic plot to visualize the convex hulls and voxelized hulls of tree crowns in a LAS point cloud. Useful for checking results of crown segmentation.
hull_diagnostic_plot(las, res = 0.1)hull_diagnostic_plot(las, res = 0.1)
las |
A 'LAS' object from the 'lidR' package. Must contain a column named 'Crown'. |
res |
Numeric. Resolution for voxelization in 'voxel_hull_2D()'. Default is 0.1. |
The function first filters points marked as crown ('Crown == 1') and then computes both the 2D convex hull and a voxelized 2D hull. The resulting plot overlays the voxel hull in color and the convex hull as a dashed outline.
A 'ggplot' object displaying convex hulls (dashed) and voxel hulls (filled).
library(lidR) file = system.file('extdata', file='tree_0744.laz', package='tReeTraits') las <- readLAS(file) las <- segment_crown(las) # adds `Crown` column hull_diagnostic_plot(las)library(lidR) file = system.file('extdata', file='tree_0744.laz', package='tReeTraits') las <- readLAS(file) las <- segment_crown(las) # adds `Crown` column hull_diagnostic_plot(las)
This function estimates the internode distance between primary branches from a QSM. It filters out all primary branches 'branching_order == 1' calculates their attachment points (Z) to the trunk, and then returns the distances betweent the branches
internode_distances(qsm, min_diam = 2)internode_distances(qsm, min_diam = 2)
qsm |
a QSM loaded using '[load_qsm()]'. |
min_diam |
numeric - minimum diameter (in cm) to include branch |
A numeric vector of internode distances (m) between consecutive primary branches that meet the diameter threshold. Returns 'NA' if fewer than two qualifying primary branches are present.
qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) inodes = internode_distances(qsm) print(inodes) median(inodes)qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) inodes = internode_distances(qsm) print(inodes) median(inodes)
Reads a Quantitative Structure Model (QSM) file from disk and checks that it includes all required columns. If any expected columns are missing, the function stops with an informative error message.
load_qsm(path)load_qsm(path)
path |
Character string giving the path to a QSM text file. |
A tibble containing the QSM data.
A tibble containing the QSM data with validated required columns: 'startX', 'startY', 'startZ', 'endX', 'endY', 'endZ', 'cyl_ID', 'parent_ID', 'extension_ID', 'radius_cyl', 'length', 'volume', and 'branching_order'. An error is thrown if any required columns are missing.
qsm_path = system.file('extdata', 'tree_0744_qsm.txt', package='tReeTraits') qsm <- load_qsm(qsm_path) plot_qsm2d(qsm, scale=50)qsm_path = system.file('extdata', 'tree_0744_qsm.txt', package='tReeTraits') qsm <- load_qsm(qsm_path) plot_qsm2d(qsm, scale=50)
Function to normalize LAS object. Function calculates ground level based on the parameter specified by 'quantile', subtracts it from all 'Z'.
normalize_las(las, quantile = c(0.001))normalize_las(las, quantile = c(0.001))
las |
'LAS' object from 'lidR' package representing individually segmented tree |
quantile |
Z quantile at which grown level is specified since ground points may not be identifiable with common algorithms if ground points are removed during segmentation |
A 'LAS' object with Z values normalized to ground level.
library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) # view histogram of Z values ranging from -18 to -7 m hist(las$Z) las = normalize_las(las) # view histogram of Z values now ranging from 0 to 11 m hist(las$Z)library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) # view histogram of Z values ranging from -18 to -7 m hist(las$Z) las = normalize_las(las) # view histogram of Z values now ranging from 0 to 11 m hist(las$Z)
Simple function to create a diagnostic plot to view QSMs colored by branching order.
plot_qsm2d(qsm, scale = 150, rotation = TRUE)plot_qsm2d(qsm, scale = 150, rotation = TRUE)
qsm |
a QSM loaded using '[load_qsm()]'. |
scale |
a factor by which to multiply the 'radius_cyl' column to give line segments the appearance of volume |
rotation |
boolean - indicates whether the plot should display the tree from 2 angles TRUE, or just one FALSE. |
'NULL', invisibly. Produces a base R plot as a side effect.
qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) plot_qsm2d(qsm)qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) plot_qsm2d(qsm)
Renders a 3D visualization of tree cylinders (from a Quantitative Structure Model, QSM) using actual geometric radii. Each cylinder is drawn between its start and end coordinates with the radius provided in the QSM data.
plot_qsm3d(qsm, bg = "white", color = "black", alpha = 0.7)plot_qsm3d(qsm, bg = "white", color = "black", alpha = 0.7)
qsm |
A data frame or tibble containing QSM cylinder data with columns:
|
bg |
Background color for the 3D plot. Defaults to |
color |
Cylinder color. Defaults to |
alpha |
Transparency level for cylinders, between 0 (fully transparent) and 1 (fully opaque).
Defaults to |
This function uses rgl to draw 3D cylinders representing each segment of a tree model. It is intended for visualizing QSM output such as that produced by PyTLidar or other tree reconstruction algorithms.
For large models, rendering may be slow because each cylinder is drawn as a separate mesh. Consider downsampling or filtering before plotting.
Opens an interactive 3D rgl window with rendered cylinders.
Returns NULL invisibly.
A 'ggplot' object combining multiple diagnostic panels.
# Load QSM output (example path) qsm_file = system.file('extdata',"tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) # Plot with real radii plot_qsm3d(qsm, color = "forestgreen", alpha = 0.6)# Load QSM output (example path) qsm_file = system.file('extdata',"tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) # Plot with real radii plot_qsm3d(qsm, color = "forestgreen", alpha = 0.6)
Plots 2 profiles X, Y, and and overhead Z view of a point cloud to allow users to identify stray points, or errors in segmentations.
plot_tree(las, res = 0.05, plot = TRUE)plot_tree(las, res = 0.05, plot = TRUE)
las |
'LAS' object from 'lidR' package representing the CROWN of a tree. Crowns can be segmented using [segment_crown()]. |
res |
numeric - resolution of voxelization to speed up plotting |
plot |
boolean - indicates whether to print the output plot, in both cases a ggplot object is returned in the output. |
A 'ggplot' object containing the arranged diagnostic panels.
# example code library(lidR) file = system.file("extdata", "tree_0744.laz", package="tReeTraits") las = readLAS(file, filter = '-thin_with_voxel 0.1') las = clean_las(las) plot_tree(las)# example code library(lidR) file = system.file("extdata", "tree_0744.laz", package="tReeTraits") las = readLAS(file, filter = '-thin_with_voxel 0.1') las = clean_las(las) plot_tree(las)
This function estimates tree volume and its vertical distribution from a QSM. The function separates the QSM into (1) trunk sections (2) terminus (top of trunk < 4 cm dbh), and (3) primary branches. The function divides trunk into segments defined by 'segment_size', calculates QSM volume, For tree portions identified as branches the function only returns the diameter. Both of these can be used in mass-volume equations as needed.
qsm_volume_distribution(qsm, terminus_diam_cm = 4, segment_size = 0.5)qsm_volume_distribution(qsm, terminus_diam_cm = 4, segment_size = 0.5)
qsm |
a QSM loaded using '[load_qsm()]'. |
terminus_diam_cm |
numeric - trunk diameter at which it is treated as a branch. |
segment_size |
numeric length of trunk segments in which to summarize volume. |
A tibble describing vertical volume distribution with columns:
Tree component ("trunk", "terminus", or "branches").
Diameter (cm) of the segment or branch.
Height (m) of the segment midpoint or branch attachment.
Total volume (m^3) of the segment (trunk and terminus only; 'NA' for branches).
qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) volume = qsm_volume_distribution(qsm) print(volume) plot(volume~ht_m, data=volume, type='l', xlab='height (m)', ylab='Volume (m3)')qsm_file = system.file("extdata", "tree_0744_qsm.txt", package='tReeTraits') qsm = load_qsm(qsm_file) volume = qsm_volume_distribution(qsm) print(volume) plot(volume~ht_m, data=volume, type='l', xlab='height (m)', ylab='Volume (m3)')
Function calculates the tree location using points below specified 'height' and recenters on 'X=0 Y=0'
recenter_las(las, height = 1)recenter_las(las, height = 1)
las |
'LAS' object from 'lidR' package representing |
height |
consider only points where Z < height, if specified. Useful for considering only the tree bole, for centering. individually segmented tree. Set 'height = NULL' to recenter using all points. |
A 'LAS' object with X and Y coordinates recentered to (0, 0).
library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) # view histogram of original X/Y values par(mfrow=c(1,2)) hist(las$X) hist(las$Y) las = recenter_las(las) # view histogram of X/Y values centered on 0,0 hist(las$X) hist(las$Y)library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) # view histogram of original X/Y values par(mfrow=c(1,2)) hist(las$X) hist(las$Y) las = recenter_las(las) # view histogram of X/Y values centered on 0,0 hist(las$X) hist(las$Y)
Rotate 'LAS' object about the 'Z' axis for specified angle.
rotate_las_z(las, angle)rotate_las_z(las, angle)
las |
'LAS' object from 'lidR' package representing individually segmented tree |
angle |
numeric - in degrees, rotation angle about Z axis. |
A 'LAS' object rotated about the Z axis.
library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las_rotated = rotate_las_z(las, 90) plot(las) plot(las_rotated)library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las_rotated = rotate_las_z(las, 90) plot(las) plot(las_rotated)
Processes a point cloud, filters and normalizes it, then runs the PyTLidar TreeQSM model to reconstruct cylinders representing the tree structure.
run_treeqsm( file, output_dir = NULL, intensity_threshold = 40000, resolution = 0.02, patch_diam1 = c(0.05, 0.1), patch_diam2min = c(0.04, 0.05), patch_diam2max = c(0.12, 0.14), optimizing_metrics = c("TrunkMean", "Branch1Mean"), verbose = TRUE )run_treeqsm( file, output_dir = NULL, intensity_threshold = 40000, resolution = 0.02, patch_diam1 = c(0.05, 0.1), patch_diam2min = c(0.04, 0.05), patch_diam2max = c(0.12, 0.14), optimizing_metrics = c("TrunkMean", "Branch1Mean"), verbose = TRUE )
file |
Path to the input LAS or LAZ file. |
output_dir |
Directory to save output files; temporary if NULL. |
intensity_threshold |
Minimum point intensity to retain. |
resolution |
Thinning voxel size (m). |
patch_diam1 |
Numeric vector of patch diameter 1 parameters. |
patch_diam2min |
Numeric vector of minimum patch diameter 2. |
patch_diam2max |
Numeric vector of maximum patch diameter 2. |
optimizing_metrics |
Character vector of metric names to average and minimize when selecting the best QSM fit. See Details |
verbose |
Logical; whether to print details during processing. |
The optimizing_metrics argument controls which point-to-cylinder distance
summaries are used to evaluate TreeQSM fits. These metrics quantify how closely
reconstructed cylinders match the underlying point cloud and are computed for
different structural components of the tree.
Available metrics include:
median, mean, max, std: Overall point–to–cylinder distances.
TrunkMedian, TrunkMean, TrunkMax, TrunkStd: Trunk-only distances.
BranchMedian, BranchMean, BranchMax, BranchStd: All branch distances.
Branch1Median, Branch1Mean, Branch1Max, Branch1Std: First-order branch distances.
Branch2Median, Branch2Mean, Branch2Max, Branch2Std: Second-order branch distances.
When multiple metrics are supplied, their row-wise mean is computed and minimized to select the best-fitting QSM, allowing users to balance fit quality across different tree components.
A list with elements:
qsm_pars: Data frame of selected patch parameters and fit metrics.
qsm: Data frame of cylinder-level QSM output.
A list with elements:
Data frame of patch parameters and fit metrics.
Data frame of cylinder-level QSM output.
## Not run: file <- system.file("extdata", "tree_0744.laz", package="tReeTraits") run_treeqsm(file) ## End(Not run)## Not run: file <- system.file("extdata", "tree_0744.laz", package="tReeTraits") run_treeqsm(file) ## End(Not run)
This function labels all points with $Z > 'crown_base_height'$ and returns a labled LAS. If 'crown_base_height' is not specified, it is estimated with [get_crown_base()] using default parameters.
segment_crown(las, crown_base_height = NULL)segment_crown(las, crown_base_height = NULL)
las |
'LAS' object from 'lidR' package representing individually segmented tree |
crown_base_height |
numeric - height of crown base for segmentation. 'NULL', it is estimated with [get_crown_base()] using default parameters. |
The input 'LAS' object with a new attribute 'Crown' (1 = crown, 0 = non-crown).
library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) las = segment_crown(las) #Plot with color based on crown plot(las, color='Crown')library(lidR) las = readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) las = segment_crown(las) #Plot with color based on crown plot(las, color='Crown')
Ensures Python 3.11 is available via conda, creates 'r-reticulate-pytlidar' environment if needed, activates it, and installs required Python modules for PyTLidar.
setup_pytlidar()setup_pytlidar()
This function **must be called manually** before using any Python-dependent functions.
'TRUE' if the environment is successfully activated and all required Python modules are available, 'FALSE' if installation of Miniconda was required and R should be restarted.
## Not run: setup_pytlidar() ## End(Not run)## Not run: setup_pytlidar() ## End(Not run)
Generates a diagnostic plot showing the fitted taper of a tree using the Kozak model. This is a simple wrapper around 'fit_taper_Kozak()' to extract its plot output.
taper_diagnostic_plot(qsm, dbh)taper_diagnostic_plot(qsm, dbh)
qsm |
A QSM object (e.g., data frame returned by 'run_treeqsm()') containing cylinder information. |
dbh |
Numeric. Diameter at breast height of the tree, used as input to the taper function. |
The function calls 'fit_taper_Kozak()' with 'plot = FALSE' and returns the plot component. This allows quick visualization of the taper without modifying the underlying QSM.
A 'ggplot' object showing the fitted taper along the tree stem.
path = system.file('extdata', 'tree_0744_qsm.txt', package='tReeTraits') qsm = load_qsm(path) taper_diagnostic_plot(qsm, dbh = 0.25)path = system.file('extdata', 'tree_0744_qsm.txt', package='tReeTraits') qsm = load_qsm(path) taper_diagnostic_plot(qsm, dbh = 0.25)
This function generates an 'sf' object representing th vertical crown area of a 'LAS' object by voxelizing a 2D vertical projection.
voxel_hull_2D(las, resolution = 0.1, angle = 0)voxel_hull_2D(las, resolution = 0.1, angle = 0)
las |
'LAS' object from 'lidR' package representing the CROWN of a tree. Crowns must be segmented using [segment_crown()]. |
resolution |
numeric - resolution of voxelization |
angle |
numeric - in degrees, rotation angle about Z axis. |
An 'sf' polygon representing the voxel-based vertical crown hull.
las = lidR::readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) cbh = get_crown_base(las, threshold=0.25, sustain=2) las = segment_crown(las, cbh) get_crown_volume_voxel(las) get_crown_volume_alpha(las) sf::st_area(convex_hull_2D(las)) #profile area, convex hull sf::st_area(voxel_hull_2D(las)) #profile area, voxel hull get_lacunarity(las)las = lidR::readLAS(system.file("extdata", "tree_0744.laz", package="tReeTraits")) las = clean_las(las) cbh = get_crown_base(las, threshold=0.25, sustain=2) las = segment_crown(las, cbh) get_crown_volume_voxel(las) get_crown_volume_alpha(las) sf::st_area(convex_hull_2D(las)) #profile area, convex hull sf::st_area(voxel_hull_2D(las)) #profile area, voxel hull get_lacunarity(las)
Writes QSM cylinder data and parameter summaries to tab-delimited text files.
write_qsm(qsm, name, output_dir = getwd())write_qsm(qsm, name, output_dir = getwd())
qsm |
Output from |
name |
Base name to use for output files. |
output_dir |
Directory where files are written (defaults to current working directory). |
Invisibly returns a list with file paths:
qsm: Path to the QSM cylinder file.
pars: Path to the parameter summary file.
## Not run: # Run and Load a qsm from a laz file. # ---- Step 1. Define input file ---- # Input file file <- system.file("extdata", "tree_0744.laz", package = "tReeTraits") tree_id <- tools::file_path_sans_ext(basename(file)) # ---- Step 2. Run TreeQSM ---- # Multiple parameter combinations can be supplied; TreeQSM optimizes across them qsm_result <- run_treeqsm( file = file, intensity_threshold = 40000, resolution = 0.02, patch_diam1 = c(0.05, 0.1), patch_diam2min = c(0.04, 0.05), patch_diam2max = c(0.12, 0.14), verbose = TRUE ) # ---- Step 3. Save results ---- write_qsm( qsm_result, name = tree_id, output_dir = tempdir() ) # ---- Step 4. Reload QSM ---- qsm_path <- file.path(tempdir(), paste0(tree_id, "_qsm.txt")) qsm <- load_qsm(qsm_path) # ---- Step 5. Visualize ---- plot_qsm2d(qsm, scale = 50) plot_qsm3d(qsm) ## End(Not run)## Not run: # Run and Load a qsm from a laz file. # ---- Step 1. Define input file ---- # Input file file <- system.file("extdata", "tree_0744.laz", package = "tReeTraits") tree_id <- tools::file_path_sans_ext(basename(file)) # ---- Step 2. Run TreeQSM ---- # Multiple parameter combinations can be supplied; TreeQSM optimizes across them qsm_result <- run_treeqsm( file = file, intensity_threshold = 40000, resolution = 0.02, patch_diam1 = c(0.05, 0.1), patch_diam2min = c(0.04, 0.05), patch_diam2max = c(0.12, 0.14), verbose = TRUE ) # ---- Step 3. Save results ---- write_qsm( qsm_result, name = tree_id, output_dir = tempdir() ) # ---- Step 4. Reload QSM ---- qsm_path <- file.path(tempdir(), paste0(tree_id, "_qsm.txt")) qsm <- load_qsm(qsm_path) # ---- Step 5. Visualize ---- plot_qsm2d(qsm, scale = 50) plot_qsm3d(qsm) ## End(Not run)