The gapfraction package requires height-normalized LiDAR points clouds. Users can either provide the path to files or objects following the column order and naming conventions of the rLiDAR and lidR packages.

By far, the simplest option for pre-processing data is to follow the lidR package tutorial, which utilizes LAStools under the hood. Alternatively, for LiDAR data without ground point classifications, height-normalized point clouds can be produced either with two LAStools command line functions, lasground and lasheight, or with three functions in USDA Fusion, GroundFilter, GridSurfaceCreate, and CanopyModel. If the ground points are already classified then you only need to use the lasheight function of LAStools, while the process for Fusion still requires the same three functions. Hence, I recommend that you use LAStools, as its ground point classification algorithm is also superior to that of Fusion, producing more accurate height-normalized point clouds. This is because Fusion uses the Kraus and Pfeifer (1998) algorithm1, while LAStools implements an optimized version of the Axelsson (1999) algorithm2. For more information, read Maguya, Junttila, and Kauranne (2014)3. An example application of lasground and lasheight, implemented in Command Prompt or Bash, is provided below.

lasground -i lidar.las -o lidar_g.las 
lasheight -i lidar_g.las -o lidar_n.las -replace_z 

In order to run these functions, you need to istall LAStools. For Windows, don’t forget to add the LAStools bin directory to your system path4. For a single LiDAR plot, this is simple to run without leaving your R session. You can call these functions using the system function included in base R, as shown below.

system(lasground -i lidar.las -o lidar_g.las)
system(lasheight -i lidar_g.las -o lidar_n.las -replace_z)

To loop through LAS files in a folder, the syntax follows this pseudocode:

folder <- 'C:/lidar'
files  <- list.files(folder, pattern="\\.las$", full.names=TRUE)

for (i in 1:length(files)) {
  file   <- files[i]
  basenm <- basename(file)
  filenm <- strsplit(basenm,'.',fixed=TRUE)[[1]][1]
  ground <- paste(folder,filenm,'_ground.las',sep='')
  htnorm <- paste(folder,filenm,'_norm.las',sep='')
  system(paste('lasground -i ',file,' -o ',ground, sep=''))
  while (!file.exists(ground)) { Sys.sleep(1) }
  system(paste('lasheight -i ',ground,' -o ',htnorm,' -replace_z', sep=''))
  while (!file.exists(htnorm)) { Sys.sleep(1) }

This for loop reads each LAS file path, extracts the name of the file without extension, creates the filenames of the ground and height-normalized outputs, executes lasground, waits for the output, executes lasheight using the ground file as the input, waits for the output, then proceeds to the next iteration. The code should be simple to parallelize using the foreach package.

Again, the lidR package is recommended for its ease of use.

  1. Kraus and Pfeifer (1998) Determination of terrain models in wooded areas with airborne laser scanner data.

  2. Axelsson (1999) Processing of laser scanner data—algorithms and applications.

  3. Maguya, Junttila, and Kauranne (2014)