pacman::p_load(
"curl",
"dplyr",
"fontawesome",
"htmltools", "httr2",
"janitor",
"kableExtra", "knitr",
"lidR", "lutz",
"openxlsx",
"PROJ",
"raster", "rasterVis", "reproj",
"sf",
"tinytex", "tmap", "tmaptools", "terra",
"useful", "usethis"
)Canopy Height
3.0 Overview
Canopy Height Models (CHM) represent vertical distance from ground to canopy top across the landscape. CHMs derived from normalised LiDAR point clouds serve as primary inputs for tree detection, biomass estimation, and forest inventory. This chapter documents operational workflows for CHM aggregation, dominant-height extraction, and quality control using the Carr Fire 1 ha AOI normalised in Chapter 1.
Environment Setup
3.1 Prepare CHM
Create 1 m resolution CHM from the normalized point cloud using triangulation.
# Import outputs from previous chapter
las <- lidR::readALSLAS("../data/las_1ha.laz")ttops <- sf::read_sf("../data/ttops_1ha.gpkg", quiet = TRUE)
# Generate 1 m CHM
chm <- lidR::rasterize_canopy(las, res = 1, dsmtin(max_edge = 8))
terra::plot(chm, col = height.colors(50))
3.2 Extract Dominant Height
Points Extraction
Extract dominant height directly from point clouds without rasterizing. This method is slower but preserves full point information. The ttops object is derived in Chapter 2: Stem Detection.
# Create a 1 m grid
grid <- sf::st_make_grid(ttops, cellsize = 1, square = TRUE)
grid_sf <- sf::st_sf(grid_id = 1:length(grid), geometry = grid)
ttops_grid <- sf::st_join(ttops, grid_sf)
ttops_95h <- ttops_grid |>
sf::st_drop_geometry() |>
dplyr::group_by(grid_id) |>
dplyr::summarise(
count = dplyr::n(),
`95thQuantile` = quantile(Z, 0.95, na.rm = TRUE),
Max = max(Z, na.rm = TRUE),
.groups = "drop"
) |>
dplyr::right_join(grid_sf, by = "grid_id") |>
sf::st_as_sf()
# Convert to raster
ttops_95h_rast <- terra::rasterize(
terra::vect(ttops_95h),
terra::rast(terra::ext(ttops_95h), res = 1, crs = terra::crs(ttops_95h)),
field = "95thQuantile"
)
terra::plot(ttops_95h_rast, main = "95th Percentile Height")

Raster Extraction
Aggregate CHM using 95th percentile statistics. This method is faster for large catalogs.
# Define 95th percentile function
quant95 <- function(x, ...) {
quantile(x, probs = 0.95, na.rm = TRUE)
}
# Aggregate using q95 (2x means a 2 m output cell from 1 m input)
chm_95h <- terra::aggregate(
chm,
fact = 2,
fun = quant95
)
terra::plot(chm_95h, main = "95th Percentile Height")
terra::writeRaster(chm_95h, "../data/chm_95h.tif", overwrite = TRUE)3.3 Validation
Compare 95th percentile CHM against detected tree heights from Chapter 2.
# Extract CHM values at tree locations
ttops$chm_95h <- terra::extract(terra::rast(chm_95h), terra::vect(ttops))[, 2]
## Error:
## ! [extract] raster has no values
# Statistics
validation <- ttops |>
dplyr::summarise(
correlation = cor(Z, chm_95h, use = "complete.obs"),
rmse = sqrt(mean((Z - chm_95h)^2, na.rm = TRUE)),
mae = mean(abs(Z - chm_95h), na.rm = TRUE),
bias = mean(Z - chm_95h, na.rm = TRUE)
)
## Error in `dplyr::summarise()`:
## ℹ In argument: `correlation = cor(Z, chm_95h, use =
## "complete.obs")`.
## Caused by error in `cor()`:
## ! 'y' must be numeric
print(validation)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'print': object 'validation' not found3.4 Build Mosaic
Merge all operating areas into a single product for Chapter 4. For the Carr demonstration the AOI is a single tile, so the 95th-percentile raster from §3.2 is the input to the biomass model in Chapter 4.
# With multi-area datasets the mosaic step loops over per-area CHM rasters.
# area_chms <- lapply(areas, function(a) raster(sprintf("../data/chm-tiles/%s.tif", a)))
# chm_95h_final <- do.call(merge, c(area_chms, tolerance = 1))
# writeRaster(chm_95h_final, "../data/chm_95h.tif", overwrite = TRUE)This raster is the dominant height input for biomass models in Chapter 4.
Runtime Log
devtools::session_info()
## ─ Session info ───────────────────────────────────────────────────
## setting value
## version R version 4.4.1 (2024-06-14)
## os macOS 15.7.5
## system aarch64, darwin20
## ui X11
## language (EN)
## collate en_CA.UTF-8
## ctype en_CA.UTF-8
## tz America/Vancouver
## date 2026-04-23
## pandoc 3.9.0.2 @ /opt/local/bin/ (via rmarkdown)
## quarto 1.7.33 @ /usr/local/bin/quarto
##
## ─ Packages ───────────────────────────────────────────────────────
## ! package * version date (UTC) lib source
## abind 1.4-8 2024-09-12 [1] CRAN (R 4.4.1)
## base64enc 0.1-6 2026-02-02 [1] CRAN (R 4.4.3)
## cachem 1.1.0 2024-05-16 [1] CRAN (R 4.4.1)
## P class 7.3-22 2023-05-03 [?] CRAN (R 4.4.1)
## classInt 0.4-11 2025-01-08 [1] CRAN (R 4.4.1)
## cli 3.6.5 2025-04-23 [1] CRAN (R 4.4.1)
## P codetools 0.2-20 2024-03-31 [?] CRAN (R 4.4.1)
## colorspace 2.1-2 2025-09-22 [1] CRAN (R 4.4.1)
## cols4all 0.10 2025-10-27 [1] CRAN (R 4.4.1)
## crosstalk 1.2.2 2025-08-26 [1] CRAN (R 4.4.1)
## curl * 7.0.0 2025-08-19 [1] CRAN (R 4.4.1)
## data.table 1.18.2.1 2026-01-27 [1] CRAN (R 4.4.3)
## DBI 1.3.0 2026-02-25 [1] CRAN (R 4.4.3)
## deldir 2.0-4 2024-02-28 [1] CRAN (R 4.4.1)
## devtools 2.5.0 2026-03-14 [1] CRAN (R 4.4.3)
## digest 0.6.39 2025-11-19 [1] CRAN (R 4.4.3)
## dplyr * 1.2.0 2026-02-03 [1] CRAN (R 4.4.3)
## e1071 1.7-17 2025-12-18 [1] CRAN (R 4.4.3)
## ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.4.1)
## evaluate 1.0.5 2025-08-27 [1] CRAN (R 4.4.1)
## farver 2.1.2 2024-05-13 [1] CRAN (R 4.4.1)
## fastmap 1.2.0 2024-05-15 [1] CRAN (R 4.4.1)
## fontawesome * 0.5.3 2024-11-16 [1] CRAN (R 4.4.1)
## fs 1.6.7 2026-03-06 [1] CRAN (R 4.4.3)
## generics 0.1.4 2025-05-09 [1] CRAN (R 4.4.1)
## ggplot2 * 4.0.2 2026-02-03 [1] CRAN (R 4.4.3)
## glue 1.8.0 2024-09-30 [1] CRAN (R 4.4.1)
## gtable 0.3.6 2024-10-25 [1] CRAN (R 4.4.1)
## hexbin 1.28.5 2024-11-13 [1] CRAN (R 4.4.1)
## htmltools * 0.5.9 2025-12-04 [1] CRAN (R 4.4.3)
## htmlwidgets 1.6.4 2023-12-06 [1] CRAN (R 4.4.0)
## httr2 * 1.2.2 2025-12-08 [1] CRAN (R 4.4.3)
## interp 1.1-6 2024-01-26 [1] CRAN (R 4.4.1)
## janitor * 2.2.1 2024-12-22 [1] CRAN (R 4.4.1)
## jpeg 0.1-11 2025-03-21 [1] CRAN (R 4.4.1)
## jsonlite 2.0.0 2025-03-27 [1] CRAN (R 4.4.1)
## kableExtra * 1.4.0 2024-01-24 [1] CRAN (R 4.4.0)
## P KernSmooth 2.23-24 2024-05-17 [?] CRAN (R 4.4.1)
## knitr * 1.51 2025-12-20 [1] CRAN (R 4.4.3)
## P lattice * 0.22-6 2024-03-20 [?] CRAN (R 4.4.1)
## latticeExtra 0.6-31 2025-09-10 [1] CRAN (R 4.4.1)
## lazyeval 0.2.2 2019-03-15 [1] CRAN (R 4.4.1)
## leafem 0.2.5 2025-08-28 [1] CRAN (R 4.4.1)
## leaflegend 1.2.1 2024-05-09 [1] CRAN (R 4.4.0)
## leaflet 2.2.3 2025-09-04 [1] CRAN (R 4.4.1)
## leafsync 0.1.0 2019-03-05 [1] CRAN (R 4.4.0)
## lidR * 4.2.3 2026-01-08 [1] CRAN (R 4.4.3)
## lifecycle 1.0.5 2026-01-08 [1] CRAN (R 4.4.3)
## logger 0.4.1 2025-09-11 [1] CRAN (R 4.4.1)
## lubridate 1.9.5 2026-02-04 [1] CRAN (R 4.4.3)
## lutz * 0.3.2 2023-10-17 [1] CRAN (R 4.4.0)
## lwgeom 0.2-15 2026-01-12 [1] CRAN (R 4.4.3)
## magrittr 2.0.4 2025-09-12 [1] CRAN (R 4.4.1)
## maptiles 0.11.0 2025-12-12 [1] CRAN (R 4.4.3)
## memoise 2.0.1 2021-11-26 [1] CRAN (R 4.4.0)
## openxlsx * 4.2.8.1 2025-10-31 [1] CRAN (R 4.4.1)
## otel 0.2.0 2025-08-29 [1] CRAN (R 4.4.1)
## P pacman 0.5.1 2019-03-11 [?] CRAN (R 4.4.0)
## pillar 1.11.1 2025-09-17 [1] CRAN (R 4.4.1)
## pkgbuild 1.4.8 2025-05-26 [1] CRAN (R 4.4.1)
## pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.4.1)
## pkgload 1.5.0 2026-02-03 [1] CRAN (R 4.4.3)
## plyr 1.8.9 2023-10-02 [1] CRAN (R 4.4.1)
## png 0.1-9 2026-03-15 [1] CRAN (R 4.4.3)
## PROJ * 0.6.0 2025-04-03 [1] CRAN (R 4.4.1)
## proj4 1.0-15 2025-03-21 [1] CRAN (R 4.4.1)
## proxy 0.4-29 2025-12-29 [1] CRAN (R 4.4.3)
## purrr 1.2.1 2026-01-09 [1] CRAN (R 4.4.3)
## R6 2.6.1 2025-02-15 [1] CRAN (R 4.4.1)
## rappdirs 0.3.4 2026-01-17 [1] CRAN (R 4.4.3)
## raster * 3.6-32 2025-03-28 [1] CRAN (R 4.4.1)
## rasterVis * 0.51.7 2025-09-01 [1] CRAN (R 4.4.1)
## RColorBrewer 1.1-3 2022-04-03 [1] CRAN (R 4.4.1)
## Rcpp 1.1.1 2026-01-10 [1] CRAN (R 4.4.3)
## renv 1.1.5 2025-07-24 [1] CRAN (R 4.4.1)
## reproj * 0.7.0 2024-06-11 [1] CRAN (R 4.4.0)
## rlang 1.1.7 2026-01-09 [1] CRAN (R 4.4.3)
## rlas 1.8.4 2026-01-29 [1] CRAN (R 4.4.3)
## rmarkdown 2.30 2025-09-28 [1] CRAN (R 4.4.1)
## rstudioapi 0.18.0 2026-01-16 [1] CRAN (R 4.4.3)
## s2 1.1.9 2025-05-23 [1] CRAN (R 4.4.1)
## S7 0.2.1 2025-11-14 [1] CRAN (R 4.4.3)
## scales 1.4.0 2025-04-24 [1] CRAN (R 4.4.1)
## sessioninfo 1.2.3 2025-02-05 [1] CRAN (R 4.4.1)
## sf * 1.1-0 2026-02-24 [1] CRAN (R 4.4.3)
## snakecase 0.11.1 2023-08-27 [1] CRAN (R 4.4.0)
## sp * 2.2-1 2026-02-13 [1] CRAN (R 4.4.3)
## spacesXYZ 1.6-0 2025-06-06 [1] CRAN (R 4.4.1)
## stars 0.7-1 2026-02-13 [1] CRAN (R 4.4.3)
## stringi 1.8.7 2025-03-27 [1] CRAN (R 4.4.1)
## stringr 1.6.0 2025-11-04 [1] CRAN (R 4.4.1)
## svglite 2.2.2 2025-10-21 [1] CRAN (R 4.4.1)
## systemfonts 1.3.2 2026-03-05 [1] CRAN (R 4.4.3)
## terra * 1.9-1 2026-03-08 [1] CRAN (R 4.4.3)
## textshaping 1.0.5 2026-03-06 [1] CRAN (R 4.4.3)
## tibble 3.3.1 2026-01-11 [1] CRAN (R 4.4.3)
## tidyselect 1.2.1 2024-03-11 [1] CRAN (R 4.4.0)
## timechange 0.4.0 2026-01-29 [1] CRAN (R 4.4.3)
## tinytex * 0.58 2025-11-19 [1] CRAN (R 4.4.3)
## tmap * 4.2 2025-09-10 [1] CRAN (R 4.4.1)
## tmaptools * 3.3 2025-07-24 [1] CRAN (R 4.4.1)
## units 1.0-1 2026-03-11 [1] CRAN (R 4.4.3)
## useful * 1.2.7 2026-02-26 [1] CRAN (R 4.4.3)
## usethis * 3.2.1 2025-09-06 [1] CRAN (R 4.4.1)
## vctrs 0.7.2 2026-03-21 [1] CRAN (R 4.4.3)
## viridisLite 0.4.3 2026-02-04 [1] CRAN (R 4.4.3)
## withr 3.0.2 2024-10-28 [1] CRAN (R 4.4.1)
## wk 0.9.5 2025-12-18 [1] CRAN (R 4.4.3)
## xfun 0.57 2026-03-20 [1] CRAN (R 4.4.3)
## XML 3.99-0.23 2026-03-20 [1] CRAN (R 4.4.3)
## xml2 1.5.2 2026-01-17 [1] CRAN (R 4.4.3)
## yaml 2.3.12 2025-12-10 [1] CRAN (R 4.4.3)
## zip 2.3.3 2025-05-13 [1] CRAN (R 4.4.1)
## zoo 1.8-15 2025-12-15 [1] CRAN (R 4.4.3)
##
## [1] /Users/seamus/repos/lidar-forestry/renv/library/macos/R-4.4/aarch64-apple-darwin20
## [2] /Users/seamus/Library/Caches/org.R-project.R/R/renv/sandbox/macos/R-4.4/aarch64-apple-darwin20/93405863
##
## * ── Packages attached to the search path.
## P ── Loaded and on-disk path mismatch.
##
## ──────────────────────────────────────────────────────────────────