# Changes of Note

- The Zstandard compression library is no longer required in order to build PDAL.
- The Ubuntu conda image now installs all of PDAL under `/usr`.
- The default type of the ClusterID dimension has changed from `uint64_t` to `int64_t`.
- Most operations now store data using `ColumnPointTable`, which stores data in arrays of dimensions, rather than arrays of point data. This can improve the performance of some operations substantially. Generally, this change should not be noticeable to users.
- Normal refinement (`refine` option) in `filters.normal` is now off by default. (#3133)
- All filters now accept a `where` option to allow points to skip the filters. (#3118)
- PDAL now searches for plugins in the same directory containing the PDAL library. (#3181)

# API User Notes

- `ColumnPointTable` is now the default PDAL point table. If you have written code that expects row-major data rather than column-major data, be sure to use `RowPointTable` instead.

# New Stages

- `filters.lloydkmeans` labels points with a `ClusterID` based on Lloyd's algorithm. (#3075)
- `filters.litree` provides segmentation of points into trees and non-trees. (#3111)
- `filters.faceraster` creates a raster from an existing triangulation. (#3152)
- `filters.optimalneighborhood` computes the optimal number of neighbors and corresponding radii (#3180)
- `reader.obj` allows reading of OBJ files.

# Removed Stages

- `filters.hag` has been removed. Use `filters.hag_dem`, `filter.hag_delaunay` or `filters.hag_nn` instead.

# Deprecated Stages

- `filters.ferry`.  Use the `value` option of `filters.assign` instead.
- `writers.oci`. OCI support will be removed in a future release.
- `readers.oci`. OCI support will be removed in a future release.

# Enhancements

- `filters.colorinterp` now provides the `clamp` option to force values to be in the range [min, max]. (#2729 - Thanks to Matt Csencsits)
- Long options can now be specified on the command line without and equal sign ('=') separating the option name and value. (#3016)
- Added `seed` option `readers.faux` in order to allow consistent generation of random data. (#3014)
- Improvements have been made to cmake installation to better support downstream projects. (#2262)
- Added `color_vertices` option to `writers.gltf` to allow writing of RGB and normals. (#3043 - Thanks to Emma Krantz)
- Added `classbits` option to `filters.smrf` to specify points to be ignored by the SMRF algorithm. (#3055)
- Delaunay triangulation performance has been improved on systems using libstdc++. (#3076)
- `filters.covariancefeatures` now provides the options `min_k` and `radius` to support radius search of neighbors in addition to k-nearest. (#3083)
- `filters.cluster` now provide the option `is3d` to support 2-dimensional clustering. (#3084)
- `filters.icp` now emits a fully composed transformation matrix as metadata that can be provided to `filters.transformation` to modify point locations using a single operation. (#2939, #2962)
- `filters.eigenvalues` added the options `stride`, `radius` and `min_k` to allow radius and strided neighbor searching. (#3132)
- `writers.las` now accepts the `filename` key in the `vlrs` option to import VLR data from files. (#3146)
- `filters.transformation` now provides the `invert` option to apply the inverse of the provided `matrix`. (#3126)
- `readers.ept` now returns a clipped boundary in the summary to match the behavior of the point count. (#3168)
- `filters.head` is now streamable.
- `readers.i3s` is now streamable.
- `readers.slpk` no longer expands the source file into a temporary directory.
- `filters.assign` added the `value` option to allow standard expression-based conditional assignment of values.(#1468)
- `readers.tiledb` and `writers.tiledb` now support TileDB version 2 and duplicate points when using version 2 of TileDB. (#3030, #3047)

# Bug Fixes

- A problem where the classifications of voting points were being changed in `filters.neighborclassifier` has been fixed. (#3210)
- Fixed an issue where NITF-wrapped LAS files may fail to open (#3010)
- Reading and writing of E57 files over 2GB in size on Windows has been fixed. (#3000)
- Fixed various compilations issues in `filters.matlab`. (#3013)
- Added better handling for some cases of coordinate transformation failure. (#3009)
- An error in calculating position for some data in `filters.smrf` has been fixed. (#3069)
- An error in calculating neighbors for isolated points in `filters.smrf` has been fixed. (#3071)
- `readers.las` now creates dimensions `ScanChannel` and `ClassFlags` only when reading point formats 6-10. It used to create them for any version 1.4 file. (#3092)
- `filters.poisson` now throws exceptions instead of calling `exit()` when it encounters a fatal error. (#3094)
- Errors that could occur in `gdal::Raster::statistics()` are now returned to the caller, rather than resulting in a crash. (#3097)
- `pdal tindex` now ensures that traditional GIS axis order is used. (#3099)
- `readers.tindex` now uses the file SRS rather than the tile index column SRS unless overridden by the `srs_column` option. (#3012)
- An error in `readers.ept` that could create bad data when both the `bounds` and `addons` options were specified has been fixed. (#3054)
- `filters.hag_nn` and `filter.hag_delaunay` now log an error rather than terminate if no ground points are found. (#3112)
- An error in reporting point count was when the number of points exceeded the maximum 32-bit value in `readers.ept` has been fixed. (#3120)
- `pdal tindex` now stores WKT instead of EPSG codes to handle vertical SRS instances. (#3100)
- A bug in `filters.crop` that didn't properly handle 2D cropping has been fixed. (#3115)
- `readers.text` now properly handles quoted dimension names in the header. (#2201)
- Windows-style directory separators are now properly handled in cases using relative filenames. (#2936)
- An exception reading E57 points with a transform has been fixed. (#3163)
- Fix a failure in `readers.pcd` when preview() was called before execute(). (#3169)
- The compression level for `writers.tiledb` is now properly set to 7. (#3198)
- An issue where `filters.neighborclassifier` would modify classifications as it calculated them has been fixed. (#3210)
- An extra comma was written using `readers.text` when creating GEOJson output. This has been fixed. (#3204)
