lidR R Package – Fixing Error “$ Operator Not Defined for S4 Class”

lidarlidrr

I am trying to write a function to calculate specific metrics which I can write to a data frame and then a .csv file to write 'reports' on each las file in a directory. I have tried the following code:

calculate_las_metrics <- function(chunk) 
{
  las <- readLAS(chunk)
  if (is.empty(las)) return(NULL)

# Calculate point density for las file
  total_point_density <- length(las$X)/st_area(las)

# extract unique classifications for las file
  classes <- unique(las$Classification)

# extract other information/descriptive data
  num_total_points <- length(las$X)
  num_returns_total <- sum(las$NumberOfReturns)
  num_of_1st_return <- chunk@data$Number.of.1st.return

# Create data frame of stats
  metrics_df_total <- data.frame(
  Classification = classes,
  Num_Points = num_points_total,
  Num_Returns = num_returns_total,
  Points_Per_Return = points_per_return_total,
  Point_Density = total_point_density,
  File = chunk$filename
  )
  return(metrics_df)
}
ctg <- readLAScatalog("path/to/las/files")
plan(multisession, workers = 8)
opt_chunk_buffer(ctg) <- 0
opt_output_files(ctg) <- paste0(tempdir(), "/{*}_metrics")
metrics <- catalog_apply(ctg, calculate_las_metrics)

This is currently returning:

Processing [-------------]   0% (0/856) eta:  ?s
Error: $ operator not defined for this S4 class

When I try and use las@data$X it returns this error:

Processing [-------------]   0% (0/856) eta:  ?s
Error: no applicable method for `@` applied to an object of class "data.table"

And when I have tried to just access it through chunk@data$X or chunk@data@X it returns this:

Processing [-------------]   0% (0/856) eta:  ?s
Error: no slot of name "data" for this object of class "LAScluster"

When I have checked the ctg or used summary(ctg) it has been fine and able to access the headers etc, and there isn't anything apparently wrong with the data when I access it using these methods. When I try and access the data and information on individual files in the catalog it appears fine. I have tried subsetting in a similar way to the answer for this problem Apply a function to a catalog but again it returns the "no slot of name data for this object of class LAScluster".

Is it that I am combining how I access the information by using the individual las file and the 'chunk' for the ctg?

I have tried to just use one or the other and it has still returned one of the previous errors.

I realise it could be my misinterpretation of how to use catalog_apply or use of the S4 class, but looking through the vignettes I can't see anything that would indicate I am access the data wrongly, and I am able to access the information in the console when I try, just not as a function.


Found that if the terra package is loaded it masks is.empty in lidR so for the function to work that line should be:
if (lidR::is.empty(las)) return(NULL)

Best Answer

You cannot use chunk@data$Number.of.1st.return. Chunk is a LAscluster. This class is not documented and you know nothing about it. You only know that you can write las = readLAS(chunk) and use st_bbox(chunk). I also fixed many typos, error, missing variables and so on.

calculate_las_metrics <- function(chunk) 
{
  las <- readLAS(chunk)
  if (is.empty(las)) return(NULL)
  
  
  # Calculate point density for las file
  total_point_density <- density(las)
  
  # extract unique classifications for las file
  classes <- unique(las$Classification)
  
  # extract other information/descriptive data
  num_total_points <- npoints(las)
  #num_returns_total <- sum(las$NumberOfReturns) does not make sense. 
  num_of_1st_return <- las$`Number of points by return`[1]
  
  # Create data frame of stats
  metrics_df_total <- data.frame(
    Classification = classes,
    Num_Points = num_total_points,
    #Num_Returns = num_returns_total,
    #Points_Per_Return = points_per_return_total,
    num_of_1st_return = num_of_1st_return,
    Point_Density = total_point_density
  )
  
  return(metrics_df_total)
}