LidR: “Argument is not a LAS object” when using catalog_apply

cliplidrnormalizepoint cloudr

I am using the lidR package to read lidar data (LAZ format), then using clip_roi() and normalize_height() to process the data for the segment trees function. These functions are being executed within a function (tree_pos()) which the catalog_apply() tool is using against a LAScatalog.

If I dont run the clip_roi() tool the process works however since the clip_roi() saves data to a list not a LAScatalog then normalize_height() tool flags this error:

"Argument is not a LAS object". Here is the code:

comp <- readOGR("comps")

tree_pos = function(cluster)
{
  las = readLAS(cluster)
  if (is.empty(las)) return(NULL)
  
  #clip current las with comp boundary
  las<-clip_roi(las,cmp)
  if (is.empty(las)) return(NULL)
  
  #normalize currnet comp
  las <- normalize_height(las, tin())
  
  #extract individual trees
  las<-segment_trees(las,li2012())
  return(las)
}

#use tree_pos function below

for (i in comp$lid) 
{
  cmp<-subset(comp,lid==i)
  plot(cmp)
  cmp<-subset(comp,lid==i)
  cmp<-gBuffer(cmp, byid=TRUE, width=0)
  project <- readLAScatalog("folder")
  crs(cmp) <- crs(project)
  project<-catalog_intersect(project,cmp)
  opt_chunk_buffer(project) <- 15
  opt_chunk_size(project) <- 250
  opt <- list(raster_alignment = 20, stop_early=F)
  output <- catalog_apply(project, tree_pos, .options = opt)
}

Best Answer

There are many ways to achieve what you are trying to do but your code has numerous flaws. So many actually that I can't explain them all. I hope the following code will help you to clarify how it works.

If I want to stay close to your original code it'd look like that

tree_pos = function(ctg, cmp)
{
  las <- clip_roi(ctg, cmp)
  if (is.empty(las)) return(NULL)
  las <- normalize_height(las, tin())
  las<-segment_trees(las,li2012())
  return(las)
}

project <- readLAScatalog("folder")

for (i in comp$lid) 
{
  cmp<-subset(comp,lid==i)
  tree_pos(project, cmp)
}

If I want to stay close to the design of the package it would look like that:

tree_pos = function(cluster)
{
  las <- readLAS(cluster)
  if (is.empty(las)) return(NULL)
  
  las <- normalize_height(las, tin())
  las<-segment_trees(las,li2012())
  return(las)
}

project <- readLAScatalog("folder")

opt_output_file(project) = "folder/plot_{ATTRFROMCOMP}"
ctg_plots = clip_roi(project, comp)

opt_output_file(ctg_plot) = "folder/*_segmented"
opt_independent_file(ctg_plots) = TRUE
ctg_segmented = catalog_apply(ctg_plots, tree_pos)
Related Question