Custom metric sets
Custom_metric_sets.Rmd
The lidRmetrics package is designed to provide a collection of functions for calculating point cloud metrics. Each function is specialized in computing metrics that characterize a specific aspect of the point cloud structure. This modular approach allows users to select and combine functions to create a custom set of metrics tailored to their specific needs.
The package includes three sets of predefined metrics, but users are encouraged to develop their own. The following examples demonstrate how to do this.
We’ll begin with a simple example by combining metrics_basic() and metrics_percentiles():
my_metric_set <- function(z) {
m_basic <- lidRmetrics::metrics_basic(z=z)
m_prctls <- lidRmetrics::metrics_percentiles(z=z)
m <- c(m_basic, m_prctls)
return(m)
}
It’s important to note that each function is explicitly called with the lidRmetrics package prefix. This practice is crucial when processing data on multiple cores using the future package.
Let’s test the function and examine the outputs:
LASfile <- system.file("extdata", "Megaplot.laz", package="lidR")
las <- readLAS(LASfile, select = "*", filter = "-keep_random_fraction 0.5")
my_metrics <- pixel_metrics(las, ~my_metric_set(Z), res = 20)
head(as.data.frame(my_metrics),1)
#> n zmax zmin zmean zvar zsd zcv zskew zkurt zq1
#> 1 103 22 0 11.95777 48.78116 6.984351 58.40849 -0.4572028 1.741565 0
#> zq5 zq10 zq15 zq20 zq25 zq30 zq35 zq40 zq45 zq50 zq55 zq60
#> 1 0.202 1.28 2.267 3.438 4.68 6.414 10.405 11.846 13.623 14.29 15.058 16.088
#> zq65 zq70 zq75 zq80 zq85 zq90 zq95 zq99
#> 1 17.041 17.518 17.68 18.062 18.421 19.178 20.813 21.7698
Now, let’s consider a more complex scenario. Suppose we want to calculate metrics_basic() and metrics_percentiles(), but for each metric, we want to consider two different scenarios: with and without a height cutoff of 2 meters (zmin). We can easily adapt the previous function to calculate both variants of each metric:
my_metric_set <- function(z) {
# set the height threshold
zmin <- 2
# define the text that will be appended to metric names calculated with height threshold. This is to avoid duplicated metric names.
zmin_label <- paste0(".above",zmin)
# metrics_basic without height threshold
m_basic <- lidRmetrics::metrics_basic(z=z)
# metrics_basic with height threshold. To make metric names unique, `zmin_label` is appended.
m_basic_2 <- lidRmetrics::metrics_basic(z=z, zmin=zmin)
names(m_basic_2) <- paste0(names(m_basic_2),zmin_label)
# the same for metrics_percentiles
m_prctls <- lidRmetrics::metrics_percentiles(z=z)
m_prctls_2 <- lidRmetrics::metrics_percentiles(z=z, zmin=zmin)
names(m_prctls_2) <- paste0(names(m_prctls_2),zmin_label)
m <- c(m_basic, m_prctls, m_basic_2, m_prctls_2)
return(m)
}
The output of the function above will look as follows:
head(as.data.frame(my_metrics),1)
#> n zmax zmin zmean zvar zsd zcv zskew zkurt zq1
#> 1 102 21.97 0 12.37745 55.39565 7.442826 60.13214 -0.5287653 1.725683 0
#> zq5 zq10 zq15 zq20 zq25 zq30 zq35 zq40 zq45 zq50 zq55
#> 1 0.0075 0.314 1.46 3.22 4.3925 6.917 10.513 12.262 13.767 15.815 16.9395
#> zq60 zq65 zq70 zq75 zq80 zq85 zq90 zq95 zq99 n.above2
#> 1 17.228 17.5625 18.183 18.44 18.7 19.101 19.768 21.1785 21.77 86
#> zmax.above2 zmin.above2 zmean.above2 zvar.above2 zsd.above2 zcv.above2
#> 1 21.97 2.82 14.61674 33.44941 5.783546 39.56795
#> zskew.above2 zkurt.above2 zq1.above2 zq5.above2 zq10.above2 zq15.above2
#> 1 -0.8554666 2.438011 2.8285 3.2225 4.055 6.04
#> zq20.above2 zq25.above2 zq30.above2 zq35.above2 zq40.above2 zq45.above2
#> 1 9.48 11.305 13.13 13.785 15.43 16.62
#> zq50.above2 zq55.above2 zq60.above2 zq65.above2 zq70.above2 zq75.above2
#> 1 17.065 17.475 17.62 18.2625 18.44 18.695
#> zq80.above2 zq85.above2 zq90.above2 zq95.above2 zq99.above2
#> 1 18.87 19.215 20.72 21.43 21.8
Finally, metrics can be calculated for different echo types (e.g., for first returns only, for all returns, etc.). This can be achieved using the built-in functionality of the lidR package:
my_metrics <- pixel_metrics(las, ~my_metric_set(Z), by_echo=c("first", "all"))
The output will include the results of metrics_basic() and metrics_percentiles(), calculated for the following scenarios: all returns with no height threshold, all returns above 2 meters, first returns only with no height threshold, and finally, first returns above 2 meters. Since point cloud subsetting is performed during metric calculation, it is important to avoid applying any filtering when loading the point cloud data or creating the catalog.
head(as.data.frame(my_metrics),1)
#> n zmax zmin zmean zvar zsd zcv zskew zkurt zq1
#> 1 102 21.97 0 12.37745 55.39565 7.442826 60.13214 -0.5287653 1.725683 0
#> zq5 zq10 zq15 zq20 zq25 zq30 zq35 zq40 zq45 zq50 zq55
#> 1 0.0075 0.314 1.46 3.22 4.3925 6.917 10.513 12.262 13.767 15.815 16.9395
#> zq60 zq65 zq70 zq75 zq80 zq85 zq90 zq95 zq99 n.above2
#> 1 17.228 17.5625 18.183 18.44 18.7 19.101 19.768 21.1785 21.77 86
#> zmax.above2 zmin.above2 zmean.above2 zvar.above2 zsd.above2 zcv.above2
#> 1 21.97 2.82 14.61674 33.44941 5.783546 39.56795
#> zskew.above2 zkurt.above2 zq1.above2 zq5.above2 zq10.above2 zq15.above2
#> 1 -0.8554666 2.438011 2.8285 3.2225 4.055 6.04
#> zq20.above2 zq25.above2 zq30.above2 zq35.above2 zq40.above2 zq45.above2
#> 1 9.48 11.305 13.13 13.785 15.43 16.62
#> zq50.above2 zq55.above2 zq60.above2 zq65.above2 zq70.above2 zq75.above2
#> 1 17.065 17.475 17.62 18.2625 18.44 18.695
#> zq80.above2 zq85.above2 zq90.above2 zq95.above2 zq99.above2 n.first
#> 1 18.87 19.215 20.72 21.43 21.8 63
#> zmax.first zmin.first zmean.first zvar.first zsd.first zcv.first zskew.first
#> 1 21.97 0.7 16.66556 21.5526 4.642478 27.85672 -1.809631
#> zkurt.first zq1.first zq5.first zq10.first zq15.first zq20.first zq25.first
#> 1 5.970935 2.405 4.403 10.444 13.758 14.75 16.26
#> zq30.first zq35.first zq40.first zq45.first zq50.first zq55.first zq60.first
#> 1 16.8 17.132 17.482 17.575 17.87 18.331 18.44
#> zq65.first zq70.first zq75.first zq80.first zq85.first zq90.first zq95.first
#> 1 18.679 18.752 19.08 19.236 20.331 20.956 21.735
#> zq99.first n.above2.first zmax.above2.first zmin.above2.first
#> 1 21.846 62 21.97 3.45
#> zmean.above2.first zvar.above2.first zsd.above2.first zcv.above2.first
#> 1 16.92306 17.65985 4.202363 24.83216
#> zskew.above2.first zkurt.above2.first zq1.above2.first zq5.above2.first
#> 1 -1.759413 5.98208 3.7245 6.307
#> zq10.above2.first zq15.above2.first zq20.above2.first zq25.above2.first
#> 1 12.786 13.8945 15.15 16.325
#> zq30.above2.first zq35.above2.first zq40.above2.first zq45.above2.first
#> 1 16.992 17.208 17.518 17.598
#> zq50.above2.first zq55.above2.first zq60.above2.first zq65.above2.first
#> 1 17.96 18.3355 18.44 18.6895
#> zq70.above2.first zq75.above2.first zq80.above2.first zq85.above2.first
#> 1 18.761 19.095 19.248 20.4405
#> zq90.above2.first zq95.above2.first zq99.above2.first
#> 1 20.958 21.7475 21.848