Skip to content

Commit d266b8d

Browse files
committed
Merge branch 'devel'
2 parents 49459ff + 2a83526 commit d266b8d

File tree

10 files changed

+106
-41
lines changed

10 files changed

+106
-41
lines changed

NEWS.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
If you are viewing this file on CRAN, please check [the latest news on GitHub](https://github.com/r-lidar/lidR/blob/master/NEWS.md) where the formatting is also better
22

3-
## lidR v4.2.2 (Release date: 2025-06-02)
3+
## lidR v4.2.2 (Release date: ...)
44

5-
- Use `nanoflann` for knn computation
5+
- Use `nanoflann` for knn computation. Numerous functions are faster.
6+
- Fix #824 `readLAScatalog` with a VPC files parse absolute paths with "/" instead of "\\" properly.
7+
- Fix #825 `plot()` with `color = "Classification"`is now as fast as plotting other attributes
68

79
## lidR v4.2.1 (Release date: 2025-06-02)
810

R/RcppExports.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ C_eigen_metrics <- function(las, k, r, coeffs, filter, ncpu) {
113113
.Call(`_lidR_C_eigen_metrics`, las, k, r, coeffs, filter, ncpu)
114114
}
115115

116-
C_fast_knn_eigen_decomposition <- function(las, k, coeffs, ncpu) {
117-
.Call(`_lidR_C_fast_knn_eigen_decomposition`, las, k, coeffs, ncpu)
116+
C_fast_eigen_decomposition <- function(las, k, r, coeffs, ncpu) {
117+
.Call(`_lidR_C_fast_eigen_decomposition`, las, k, r, coeffs, ncpu)
118118
}
119119

120120
C_connected_component <- function(las, res) {

R/io_readLAScatalog.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ read_vpc <- function(f)
301301
is_absolute <- function(path)
302302
{
303303
if (.Platform$OS.type == "windows") {
304-
grepl("^[a-zA-Z]:\\\\|^\\\\", path) # Absolute paths start with "C:\" or "\\" on Windows
304+
grepl("^(?:[A-Za-z]:[\\\\/]|\\\\\\\\)", path) # Absolute paths start with "C:\" or "\\" on Windows
305305
} else {
306306
substr(path, 1, 1) == "/" # Absolute paths start with "/" on Unix-like systems
307307
}

R/metrics_point.R

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,17 @@ point_eigenvalues = function(las, k, r, xyz = FALSE, metrics = FALSE, coeffs = F
258258

259259
assert_is_a_bool(xyz)
260260

261-
filter <- parse_filter(las, filter)
262-
M <- C_eigen_metrics(las, k, r, coeffs, filter, getThreads())
261+
if (!is.null(filter))
262+
{
263+
filter <- parse_filter(las, filter)
264+
M <- C_eigen_metrics(las, k, r, coeffs, filter, getThreads())
265+
}
266+
else
267+
{
268+
M <- C_fast_eigen_decomposition(las, k, r, coeffs, getThreads())
269+
M <- cbind(pointID = 1:npoints(las), M)
270+
}
271+
263272
data.table::setDT(M)
264273
data.table::setorder(M, pointID)
265274

R/utils_colors.R

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -86,23 +86,26 @@ pastel.colors = function(n)
8686

8787
lasclass.colors = function(...)
8888
{
89-
return(c("lightgrey", # 0 never classifiied
90-
"lightgray", # 1 unclassified
91-
"blue", # 2 ground
92-
"limegreen", # 3 low vegetation
93-
"forestgreen", # 4 medium vegetation
94-
"darkgreen", # 5 high vegetation
95-
"red", # 6 building
96-
"yellow", # 7 low point (noise)
97-
"yellow", # 8 reserved
98-
"#6495ED", # 9 water
99-
"yellow", # 10 rail
100-
"gray20", # 11 Road surface
101-
"yellow", # 12 reserved
102-
"pink", # 13 wire
103-
"pink", # 14 wire
104-
"purple", # 15 transmission tower
105-
"pink", # 16 Wire connector
106-
"orange", # 17 bridge deck
107-
"yellow")) # +
89+
pal = c("lightgrey", # 0 never classifiied
90+
"lightgray", # 1 unclassified
91+
"blue", # 2 ground
92+
"limegreen", # 3 low vegetation
93+
"forestgreen", # 4 medium vegetation
94+
"darkgreen", # 5 high vegetation
95+
"red", # 6 building
96+
"yellow", # 7 low point (noise)
97+
"yellow", # 8 reserved
98+
"#6495ED", # 9 water
99+
"yellow", # 10 rail
100+
"gray20", # 11 Road surface
101+
"yellow", # 12 reserved
102+
"pink", # 13 wire
103+
"pink", # 14 wire
104+
"purple", # 15 transmission tower
105+
"pink", # 16 Wire connector
106+
"orange", # 17 bridge deck
107+
"yellow")
108+
109+
hex <- rgb(t(col2rgb(pal)), maxColorValue = 255)
110+
return(hex)
108111
}

src/LAS.cpp

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,7 +1434,6 @@ List LAS::point_metrics(unsigned int k, double r, DataFrame data, int nalloc, SE
14341434
// Query the knn neighborhood
14351435
PointXYZ p(X[i], Y[i], Z[i]);
14361436
tree.knn(p, k, pts);
1437-
14381437
// No need to reallocate the memory because it is always of size k
14391438
}
14401439
else
@@ -1572,9 +1571,21 @@ List LAS::point_metrics(unsigned int k, double r, DataFrame data, int nalloc, SE
15721571
}
15731572
#endif
15741573

1575-
DataFrame LAS::fast_knn_eigen_decomposition(int k, bool get_coef)
1574+
DataFrame LAS::fast_eigen_decomposition(int k, double r, bool get_coef)
15761575
{
15771576
int n = npoints;
1577+
k = std::min(k, n);
1578+
double rsq = r*r;
1579+
1580+
int mode = 0;
1581+
if (k == 0 && r > 0)
1582+
mode = 1;
1583+
else if (k > 0 && r == 0)
1584+
mode = 0;
1585+
else if (k > 0 && r > 0)
1586+
mode = 2;
1587+
else
1588+
Rcpp::stop("Internal error: invalid argument k or r");
15781589

15791590
NumericVector eigen_largest(n);
15801591
NumericVector eigen_medium(n);
@@ -1618,18 +1629,55 @@ DataFrame LAS::fast_knn_eigen_decomposition(int k, bool get_coef)
16181629
if (pb.check_interrupt()) abort = true;
16191630
pb.increment();
16201631

1621-
std::vector<uint32_t> indices(k);
1622-
std::vector<KDTree::DistanceType> dists(k);
16231632
double p[3] = { X[i], Y[i], Z[i] };
1624-
tree.knnSearch(p, k, indices.data(), dists.data());
16251633

1634+
std::vector<uint32_t> indices;
1635+
std::vector<KDTree::DistanceType> dists;
16261636

1627-
arma::mat A(k,3);
1637+
switch (mode)
1638+
{
1639+
case 2:
1640+
{
1641+
// ---- KNN with radius cap ----
1642+
std::vector<uint32_t> tmp_idx(k);
1643+
std::vector<KDTree::DistanceType> tmp_dist(k);
1644+
1645+
size_t found = tree.knnSearch(p, k, tmp_idx.data(), tmp_dist.data());
1646+
for (size_t j = 0; j < found; j++)
1647+
{
1648+
if (tmp_dist[j] <= rsq)
1649+
indices.push_back(tmp_idx[j]);
1650+
}
1651+
break;
1652+
}
1653+
case 0:
1654+
{
1655+
// ---- Pure KNN ----
1656+
indices.resize(k);
1657+
dists.resize(k);
1658+
tree.knnSearch(p, k, indices.data(), dists.data());
1659+
break;
1660+
}
1661+
case 1:
1662+
{
1663+
// ---- Pure Radius ----
1664+
std::vector<nanoflann::ResultItem<uint32_t, KDTree::DistanceType>> matches;
1665+
const size_t found = tree.radiusSearch(p, rsq+EPSILON, matches);
1666+
1667+
for (const auto& m : matches)
1668+
indices.push_back(m.first);
1669+
1670+
break;
1671+
}
1672+
}
1673+
1674+
// build matrix A
1675+
arma::mat A(indices.size(), 3);
16281676
arma::mat coeff; // Principle component matrix
16291677
arma::mat score;
16301678
arma::vec latent; // Eigenvalues in descending order
16311679

1632-
for (unsigned int j = 0 ; j < indices.size() ; j++)
1680+
for (size_t j = 0; j < indices.size(); j++)
16331681
{
16341682
A(j,0) = X[indices[j]];
16351683
A(j,1) = Y[indices[j]];

src/LAS.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class LAS
5050
NumericVector knn_distance(unsigned int k);
5151
NumericVector interpolate_knnidw(NumericVector x, NumericVector y, int k, double p, double rmax);
5252
DataFrame eigen_decomposition(int k, double r, bool get_coef);
53-
DataFrame fast_knn_eigen_decomposition(int k, bool get_coef);
53+
DataFrame fast_eigen_decomposition(int k, double r, bool get_coef);
5454

5555
private:
5656
static bool coplanar (arma::vec& latent, arma::mat& coeff, NumericVector& th) { return latent[1] > th[0]*latent[2] && th[1]*latent[1] > latent[0]; }

src/RcppExports.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -385,16 +385,17 @@ BEGIN_RCPP
385385
return rcpp_result_gen;
386386
END_RCPP
387387
}
388-
// C_fast_knn_eigen_decomposition
389-
DataFrame C_fast_knn_eigen_decomposition(S4 las, int k, bool coeffs, int ncpu);
390-
RcppExport SEXP _lidR_C_fast_knn_eigen_decomposition(SEXP lasSEXP, SEXP kSEXP, SEXP coeffsSEXP, SEXP ncpuSEXP) {
388+
// C_fast_eigen_decomposition
389+
DataFrame C_fast_eigen_decomposition(S4 las, int k, double r, bool coeffs, int ncpu);
390+
RcppExport SEXP _lidR_C_fast_eigen_decomposition(SEXP lasSEXP, SEXP kSEXP, SEXP rSEXP, SEXP coeffsSEXP, SEXP ncpuSEXP) {
391391
BEGIN_RCPP
392392
Rcpp::RObject rcpp_result_gen;
393393
Rcpp::traits::input_parameter< S4 >::type las(lasSEXP);
394394
Rcpp::traits::input_parameter< int >::type k(kSEXP);
395+
Rcpp::traits::input_parameter< double >::type r(rSEXP);
395396
Rcpp::traits::input_parameter< bool >::type coeffs(coeffsSEXP);
396397
Rcpp::traits::input_parameter< int >::type ncpu(ncpuSEXP);
397-
rcpp_result_gen = Rcpp::wrap(C_fast_knn_eigen_decomposition(las, k, coeffs, ncpu));
398+
rcpp_result_gen = Rcpp::wrap(C_fast_eigen_decomposition(las, k, r, coeffs, ncpu));
398399
return rcpp_result_gen;
399400
END_RCPP
400401
}
@@ -703,7 +704,7 @@ static const R_CallMethodDef CallEntries[] = {
703704
{"_lidR_C_isolated_voxel", (DL_FUNC) &_lidR_C_isolated_voxel, 3},
704705
{"_lidR_C_check_gpstime", (DL_FUNC) &_lidR_C_check_gpstime, 2},
705706
{"_lidR_C_eigen_metrics", (DL_FUNC) &_lidR_C_eigen_metrics, 6},
706-
{"_lidR_C_fast_knn_eigen_decomposition", (DL_FUNC) &_lidR_C_fast_knn_eigen_decomposition, 4},
707+
{"_lidR_C_fast_eigen_decomposition", (DL_FUNC) &_lidR_C_fast_eigen_decomposition, 5},
707708
{"_lidR_C_connected_component", (DL_FUNC) &_lidR_C_connected_component, 2},
708709
{"_lidR_C_voxel_id", (DL_FUNC) &_lidR_C_voxel_id, 2},
709710
{"_lidR_fast_table", (DL_FUNC) &_lidR_fast_table, 2},

src/RcppFunction.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,10 @@ DataFrame C_eigen_metrics(S4 las, int k, double r, bool coeffs, LogicalVector fi
237237
}
238238

239239
//[[Rcpp::export(rng = false)]]
240-
DataFrame C_fast_knn_eigen_decomposition(S4 las, int k, bool coeffs, int ncpu)
240+
DataFrame C_fast_eigen_decomposition(S4 las, int k, double r, bool coeffs, int ncpu)
241241
{
242242
LAS pt(las, ncpu);
243-
return pt.fast_knn_eigen_decomposition(k, coeffs);
243+
return pt.fast_eigen_decomposition(k, r, coeffs);
244244
}
245245

246246

src/knn.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ List cpp_knn(S4 data, int k, int ncpu)
4343
tree.buildIndex();
4444

4545
int npoints = X.size();
46+
k = std::min(k, npoints);
4647

4748
IntegerMatrix knn_idx(npoints, k);
4849
NumericMatrix knn_dist(npoints, k);
@@ -92,6 +93,7 @@ List cpp_knnx(S4 data, S4 query, int k, int ncpu)
9293
NumericVector Z = tmp["Z"];
9394

9495
int npoints = X.size();
96+
k = std::min(k, npoints);
9597

9698
IntegerMatrix knn_idx(npoints, k);
9799
NumericMatrix knn_dist(npoints, k);

0 commit comments

Comments
 (0)