If you are viewing this file on CRAN, please check the latest news on GitHub where the formatting is also better

lidR v3.2.3 (Release date: …)

lidR v3.2.2 (Release date: 2021-10-18)

lidR v3.2.1 (Release date: 2021-09-27)

lidR v3.2.0 (Release date: 2021-09-26)


rgdal and rgeos will be retired on Jan 1st 2024. raster and sp are based on rgdal/rgeos. lidR is based on raster and sp because it was created before sf, terra and stars. This means that sooner or later lidR will run into trouble (actually it has already started to be the case). So, it is time to fully embrace sf, terra/stars and to leave sp and raster. This will require an in-depth rebase of lidR. We have started the work and we plan to release lidR 4.0.0 that will no longer have any internal code that uses sp and raster. This version already no longer uses rgdal. We hope make these changes with minimal breakage in backward compatibility by maintaining the conversion to sp/raster for functions from v < 4.0.0, but some backward incompatibilities will necessarily arise. In particular, LAS will no longer inherit the sp::Spatial class and will no longer contain a sp::CRS but a sf::crs and LAScatalog will no longer be sp::SpatialPolygonDataFrame. Our plan is (hopefully) to rebase lidR in such a way that nobody will notice the changes expect users who dig a little deeper into the objects.


  1. hexbin_metrics() was an unused function and has been removed from lidR. It can be retrieved in lidRplugins

  2. Functions using the former namespace such as lassomething() that were renamed into verb_noun() in version 3.0.0 now throw a warning. In v3.0.0 they were still usable for backward compatibility but not documented. In v3.1.0 they printed a message saying to move on to the new namespace. Now in 3.2.0 they throw a formal warning saying to move on to the new namespace. They will throw an error in the next version.


  1. classify_poi(). New function capable of attributing a class of choice to any points that meet a logical criterion (e.g. Z > 2) and/or a spatial criterion (e.g. inside a polygon). For example, the following will attribute the class “high vegetation” to each non-ground point that is not in the lake polygon. r las <- classify_poi(las, LASHIGHVEGETATION, poi = ~Classification != 2, roi = lakes, inverse = TRUE)

  2. LAScatalog

  3. LASheader

  4. LAS

  5. Full waveform: with most recent versions of the rlas package, full waveform (FWF) can be read and lidR provides some compatible functions. However the support of FWF is still a work in progress in the rlas package. How it is read, interpreted and represented in R may change. Consequently, tools provided by lidR may also change until the support of FWF becomes mature and stable in rlas.

  6. catalog_intersect() now supports sf, sfc, Extent and bbox objects

  7. Concave hull: lidR now includes its own C++ code to compute concave hulls using concaveman-cpp.

  8. New function catalog_boundary() to compute the actual shape of the point-cloud

  9. In find_trees() and segment_trees() the bitmerge strategy to generate robust unique IDs was not actually a valid and robust procedure. It had the advantage of generating integers but was not 100% unique. The probability to generate duplicates was low but we changed the strategy to use a true bit-merging procedure anyway. The new IDs thus generated are weird decimal number such as 5.001120e-310 but are guaranteed to be unique. The documentation has been updated to explain the method.

  10. New algorithm random_per_voxel() for decimate_points that keep n points per voxel (#406).

  11. 3D rendering:

  12. New function plot_metrics() that wraps several other functions into one seamless function that extracts ground inventory plots, computes metrics for each plot and returns a ready to use data.frame for statistical modelling.

  13. New function point_eigenvalue() that is equivalent to point_metrics(las, .stdshapemetrics) but specialized, optimized and parallelized to be 10 times faster.

  14. grid_metrics() gains a new parameters by_echo allowing users to compute the metrics for different types of echos independently. It is now possible to map e.g. mean(Intensity) for first returns only + multiple return only + single return only. All metrics are computed in a single run and returned in a raster stack.

  15. merge_spatial() supports sfc


  1. grid_density() is 10 times faster


  1. Fix: quantize() now preserves NaN values instead of converting them into minus infinity (#460).
  2. Fix: stdmetrics_i() now fails with an informative message when the sum of intensities is greater than .Machine$integer.max and becomes double (#463)
  3. Fix: find_localmaxima() respects the filter argument. It was previously not considered.


  1. Remove crayon and hexbin dependencies
  2. Packages RCSF and rgeos are now only suggested and they are consequently no longer installed by default with lidR
  3. Change: rgdal will be retired in 2024. Code using rgdal internally has been removed. In many cases this will not change anything for users but in some cases it may fail when assigning an EPSG code to the LAS file. Also, old versions of rgdal built with old versions of gdal and proj are no longer supported (#466)

lidR v3.1.4 (Release date: 2021-06-22)

lidR v3.1.3 (Release date: 2021-05-20)

lidR v3.1.2 (Release date: 2021-03-11)

lidR v3.1.1 (Release date: 2021-01-22)

lidR v3.1.0 (Release date: 2021-01-15)


The release of lidR 3.1.0 comes with major internal modifications enabling users to chose different kinds of spatial indexes to process the point-clouds, including Quadtrees and Octrees, plus others. Previous releases were optimized to process ALS data but were suboptimal for TLS data (for example) because the spatial index in use was specialized for ALS. With 3 new spatial indexes, version 3.1.0 brings the capability to process TLS (but not only) data more efficiently. For the time being, however, lidR is still mainly focused on ALS and does not include many functions for TLS processing, but the existing functions that be used on all kinds of point-cloud, such as point_metrics(), detect_shape(), and classify_noise() are already much faster for TLS data.

  1. The class LAS has a new slot @index that registers the source of the point cloud (e.g. ALS, TLS, UAV, DAP) and the spatial index that must be used (e.g. grid partition, voxel partition, quadtree, octree). See help("lidR-spatial-index").
  2. This comes with several new read*LAS() functions, such as readTLSLAS(), which registers the point-cloud type and a default spatial index. Registering the correct point type improves the performance of some functions. This is particularly visible in functions that perform 3D knn searches, such as point_metrics(). Computing point_metrics() on a TLS point-cloud tagged as TLS is much faster than if it is not tagged. If performance is not improved in this release the future versions of the package may bring enhancements transparently.
  3. New functions index() and sensor() to manually modify the spatial indexing-related information. help("lidR-spatial-index").
  4. New C++ API: the C++ classes for spatial indexing are header-only and stored in inst/include, meaning that other packages can link to lidR to uses the spatial index at C++ level. The classes are not documented yet but the source code is simple and commented, and the lidR book contains (or will contain) a chapter on spatial indexing.


  1. The use of old deprecated namespaces (such as lassomething()) now triggers a message inviting users to move on the new namespace.
  2. The construction of a LAS object with LAS() now triggers warnings with incorrectly quantized coordinates according to the information in the header.
  3. grid_terrain() now has a parameter ... after algorithm that invalidates code that uses too many parameters without naming them. This no longer works:
grid_terrain(las, 1, tin(), TRUE, TRUE, 8)
# Use instead
grid_terrain(las, 1, tin(), keep_lowest = TRUE, full_raster = TRUE, use_class = 8)
  1. opt_cores() and opt_cores<-() are now defunct. These functions did not have any effect because they only throw a warning to alert about deprecation since v2.1.0 (July 2019).
  2. The LAS* classes have a new slot @index (see above). This should not break anything expect when a LAS* object is saved in an Rds file and loaded as an R object instead of being read with readLAS.


  1. classify_noise()
  2. Quantization of the coordinates. LAS objects in lidR closely respect the ASPRS standard. When modified manually by users, some inadequate practices may generate invalid LAS objects. We thus decided to export some internal functions to help in creating valid LAS objects and we modified the behavior of the [[<- and $<- operators to ensure that it is more difficult to create LAS objects that are not ASPRS compliant.
    las$X # Original values
    #> [1] 0.755 0.286 0.100 0.954 0.416 0.455 0.971 0.584 0.962 0.762
    las$X + 5/3 # Many decimals because 5/3 = 1.666666...
    #> [1] 2.421667 1.952667 1.766667 2.620667 2.082667 2.121667 2.637667 2.250667 2.628667 2.428667
    las$X <- las$X + 5/3 # Updates X with these numbers
    las$X # Values were quantized (and header updated)
    #> [1] 2.422 1.953 1.767 2.621 2.083 2.122 2.638 2.251 2.629 2.429
  3. metrics
  4. grid_terrain()
    grid_terrain(las, 1, tin(), TRUE, TRUE, 8)
    # Use instead
    grid_terrain(las, 1, tin(), keep_lowest = TRUE, full_raster = TRUE, use_class = 8)


  1. In clip_transect() the polygon generated to extract the transect defined by points p1, p2 was created by buffering the line p1-p2 with a SQUARE cap style meaning that the transect was extended beyond points p1, p2. It now uses a FLAT cap style meaning that the transect is no longer extended beyond the limits of the user input.
  2. In segment_trees() when using a raster-based algorithm, some points may have been misclassified as NAs at the edges of the point cloud instead of getting the correct tree ID found in the raster because of some edge effects. Now, all points are correctly classified and there are no longer false positive NAs.
  3. normalize_intensity() was previously not working with a LAScatalog. Now fixed. See #388
  4. In grid_*() functions when a RasterLayer is given as layout, the computation was performed for all the cells no matter if the extent of the loaded point-cloud was much smaller than the raster. For large rasters this dramatically increased the workload with redundant computation and saturated the RAM to a point that the computation was no longer possible.
  5. In track_sensor() pulse IDs could be wrongly attributed for multi-beam sensors if the number of points is very low. See #392
  6. In track_sensor(), if thin_pulses_with_time = 0 a single pulse was loaded with a LAScatalog. However it worked as expected with a LAS object. This behaviour has been fixed.
  7. Fixed some new warnings coming from future and related to RNG.
  8. clip_*() in a region with no points from a LAScatalog + an output file no longer fails. See #400.


lidR v3.0.4 (Release date: 2020-10-08)

lidR v3.0.3 (Release date: 2020-08-05)

lidR v3.0.2 (Release date: 2020-07-05)

lidR v3.0.1 (Release date: 2020-06-18)

lidR v3.0.0 (Release date: 2020-06-08)



In lidR version 3.0.0, 80% of the functions were renamed. Old functions were soft-deprecated, meaning that they still exist so version 3 is fully compatible with version 2, at least for 1 year. Users should start to use the new names. See ?lidR::deprecated for the list of deprecated functions and their new names. The plan is to remove these functions in 1 year so they will progressively print a message, then throw a warning, then throw an error, after which they will be definitively removed.

Full explanation

At the very beginning of the development of lidR we started to name the functions that return a LAS object lassomething(). At that point there were 5 functions and ~10 users. As lidR grew up, we kept going with this naming convention but now lidR is used worldwide and this naming convention now overlaps with the LAStools software suite created by Martin Isenburg. This creates confusion for users which is problematic both for Martin and for us. This situation is likely to get worse as more tools are released into LAStools. We discussed the issue with Martin Isenburg and we took the decision to rename the functions in the lidR package so that the overlaps in namespace will progressively disappear.

The new naming convention follows the currently trending verb_noun syntax initiated by the tidyverse. For example, lasnormalize() becomes normalize_height(), while lasground() becomes classify_ground(). The full list of changes can be found in ?lidR::deprecated.

In efforts to avoid breaking users’ scripts version 3 is fully backwards-compatible. For example, the function lasground() still exists and can be used without throwing a warning or error message. But this will progressively change with versions 3.1.0, 3.2.0 and 3.3.0. First a message will be displayed to invite users to change to using the new names, then a warning, then finally an error. After a year, maybe 18 months, the function will no longer exist. So users are invited to adopt the new naming convention as soon as possible.


  1. readLAScatalog() has new parameters to tune the processing options at read time without using the functions opt_*().

    readLAScatalog("folder/", chunk_buffer = 60, filter = "-drop_z_below 2")
  2. New function clip_transect() to extract a transect between two points. The function has the capability to reorient the point cloud to put it on XZ coordinates and easily create some 2D rendering of the transects in e.g. ggplot2.

  3. New function readMSLAS() to read multisprectral data from 3 different files.

    readMSLAS("channel1.las", "channel2.las", "channel3.las", filter = "-keep_first")
  4. delineate_crowns() (formerly named tree_hulls()) now returns 3 metrics: XTOP, YTOP and ZTOP, that contain the coordinates of the apices of the trees.

  5. segment_trees() (formerly named lastrees()) and find_trees() (formerly tree_detection()) can now perform the computation on a LAScatalog using two strategies to ensure that tree IDs are always unique on a coverage and that trees that belong on the edge of two tiles will independently get the same IDs.

  6. point_metrics()

  7. normalize_height() (formerly named lasnormalize())

  8. New function find_localmaxima() to find local maxima with different windows. This function is designed for programming purposes, not to find individual trees. This latter task is still performed by find_trees() (formerly called tree_detection()). Instead, find_localmaxima() may help with finding other human-made structures.


    filter_poi(las, !Classification %in% c(LASWIRECONDUCTOR, LASTRANSMISSIONTOWER))
  10. The internal function catalog_makechunks() has been exported. It is not actually intended to be used by regular users but might be useful in some specific cases for debugging purposes.

  11. lasmetrics(), grid_metrics3d(), grid_hexametrics() were deprecated in previous versions. They are now defunct.

  12. las_check() (formerly named lascheck()):

    las_check(las, FALSE)
    #> $warnings
    #> [1] "1 points are duplicated and share XYZ coordinates with other points"                                         
    #> [2] "There were 1 degenerated ground points. Some X Y Z coordinates were repeated."                               
    #> [3] "There were 1 degenerated ground points. Some X Y coordinates were repeated but with different Z coordinates."
    #> $errors
    #> [1] "Invalid header: X scale factors should be factor ten of 0.1 or 0.5 or 0.25 not 0.123"                      
    #> [2] "Invalid file: the data contains a 'gpstime' attribute but point data format is not set to 1, 3, 6, 7 or 8."
  13. merge_spatial() (formerly named lasmergespatial()) now supports sf POLYGON objects.

  14. plot()

    las = readLAS("classified.las")
    nonveg = filter_poi(las, Classification != LASHIGHVEGETATION)
    veg = filter_poi(las, Classification == LASHIGHVEGETATION)
    x = plot(nonveg, color = "Classification")
    plot(veg, add = x)
  15. New function add_lasrgb() to add RGB attributes. The function updates the header in such a way that the LAS object has a valid point format that supports RGB.

  16. LAScatalog processing engine

  17. New function get_range().

  18. knnidw() gains an argument rmax to set a maximum radius search in which to find the knn. This fixes computation time issues with non-convex point clouds.

  19. track_sensor() (formerly sensor_tracking())

  20. writeLAS() gains a parameter index = TRUE to automatically write a lax file along with the las/laz file.


  1. readLAS() now warns when reading incompatible files. Point coordinates are recomputed on-the-fly as it has always been done but now the user is aware of potential problems or precision loss.

  2. A new vignette named LAScatalog processing engine has been added and documents in-depth the catalog_apply() engine of lidR.

  3. In clip_*() several lines of codes were removed because they were not used. We suspected these lines covered old cases from lidR v1.x.y that are no longer relevant. If a user encounters problems, please report.

  4. The arguments select and filter from readLAS() are not expected to be used with a LAScluster when processing a LAScatalog. The options are carried by the LAScatalog itself with opt_select() and opt_filter(). If used, a warning is now thrown.

  5. Enhancements made here and there to improve the support of the CRS when reading and checking a LAS file.

  6. When processing by file with a raster output, automatic chunk extension to match with a raster resolution now performs a tighter extension.

  7. Minor modification of print() methods to enhance information displayed.

  8. All algorithms such as tin(), p2r(), knnidw(), li2012(), and so on, now have the classes c("lidRAlgorithm", "something") and a dedicated print function. The source code is no longer displayed when printing these objects

    f = lmf(2)
    #> Object of class lidR algorithm
    #> Algorithm for: individual tree detection 
    #> Designed to be used with: find_trees 
    #> Native C++ parallelization: yes 
    #> Parameters: 
    #>  - circ = TRUE <logical>
    #>  - hmin = 2 <numeric>
    #>  - shape = circular <character>
    #>  - ws = 2 <numeric>
  9. In grid_metrics() the RasterBrick is built much faster.


  1. In delineate_crowns(), formerly named tree_hull(), when applied to a LAScatalog the buffer was not properly removed. The polygons were simply clipped using the bounding box of the chunk. Now the trees that have an apex in the buffer are removed and the trees that have an apex outside the buffer are retained. Thus, when merging, everything is smooth and continuous.

  2. catalog_retile() returns a LAScatalog with only the newly created files even if the folder contains other las files. It formerly read every las file in the folder leading to an invalid catalog if the folder already contained las files.

  3. Previously in automatic filename generation the template YCENTER was not actually recognized. However, XCENTER was recognized but actually contained the value for YCENTER. This was working for lasclip() thanks to a previous fix but was still a problem in other functions when processing chunks.

  4. Function wkt() no longer masks the new function wkt() in sp.

  5. merge_spatial() (formerly named lasmergespatial()) no longer fails with a LAS object containing a single point.

lidR v 2.x.y

lidR v2.x.y changelog has been moved to NEWS_v2.md