context("segment_trees") las <- clip_rectangle(mixedconifer, 481250, 3812980, 481300, 3813030) ctg <- mixedconifer_ctg opt_progress(ctg) = FALSE opt_chunk_size(ctg) = 100 opt_chunk_buffer(ctg) = 20 opt_chunk_alignment(ctg) <- c(0, 20) chm <- rasterize_canopy(las, 0.5, pitfree()) ker <- matrix(1,3,3) chm <- terra::focal(chm, w = ker, fun = mean) chm <- stars::st_as_stars(chm) ttops <- locate_trees(chm, lmf(3, 2)) n <- nrow(ttops) ttops_shifted500 <- ttops sf::st_geometry(ttops_shifted500) <- sf::st_geometry(ttops_shifted500) + sf::st_sfc(sf::st_point(c(0, 500, 0))) sf::st_crs(ttops_shifted500) <- sf::st_crs(chm) test_that("segment_tree propagates tree IDs and preserves storage mode", { ttops1 <- ttops ttops1$treeID <- ttops1$treeID*2L las1 <- segment_trees(las, dalponte2016(chm, ttops1)) ttops2 <- ttops ttops2$treeID <- ttops2$treeID + runif(n, -0.1, 0.1) las2 <- segment_trees(las, dalponte2016(chm, ttops2)) expect_equal(sort(unique(las1$treeID)), ttops1$treeID) expect_equal(sort(unique(las2$treeID)), ttops2$treeID) expect_equal(storage.mode(las1$treeID), storage.mode(ttops1$treeID)) expect_equal(storage.mode(las2$treeID), storage.mode(ttops2$treeID)) }) test_that("segment_trees can store in a user defined column", { las <- segment_trees(las, li2012(speed_up = 5), attribute = "plop") expect_true("plop" %in% names(las)) }) test_that("segment_trees supports different unicity strategies", { las <- segment_trees(las, li2012(speed_up = 5), uniqueness = "incremental", attribute = "ID1") las <- segment_trees(las, li2012(speed_up = 5), uniqueness = "gpstime", attribute = "ID2") las <- segment_trees(las, li2012(speed_up = 5), uniqueness = "bitmerge", attribute = "ID3") expect_equal(length(na.omit(unique(las$ID1))), 48L) expect_equal(length(na.omit(unique(las$ID2))), 48L) expect_equal(length(na.omit(unique(las$ID3))), 48L) u <- las@data[, if (!anyNA(.BY)) .I[which(Z == max(Z))], by = "ID1"] s <- las@data[u$V1] expectedID2 <- s$gpstime expectedID3 <- lidR:::bitmerge(s$X*100, s$Y*100) id42 = s$ID1 == 42 expect_equal(s$ID2[!id42], expectedID2[!id42]) expect_equal(s$ID3[!id42], expectedID3[!id42]) expect_equal(s$ID2[id42][1], min(s$gpstime[id42])) expect_equal(s$ID3[id42][1], expectedID3[id42][which.min(s$X[id42])]) }) test_that("segment_trees supports a LAScatalog", { # Skipping this test temporarily to be able to have a build on MacOS. Then I will be # able to test that on somebody' computer. skip_on_cran() opt_output_files(ctg) <- "{tempdir()}/{ID}" new_ctg <- segment_trees(ctg, li2012(speed_up = 5), uniqueness = 'gpstime') las <- readLAS(new_ctg) las <- segment_trees(las, li2012(speed_up = 5), uniqueness = 'gpstime', attribute = "treeID2") expect_equal(length(na.omit(unique(las$treeID))), 274) # 272 on Fedora and MacOS. Impossible to reproduce. expect_equal(length(na.omit(unique(las$treeID2))), 274) # 272 on Fedora and MacOS. Impossible to reproduce. skip("Need investigation") expect_equal(las$treeID, las$treeID2) }) test_that("Dalponte algorithm works", { las <- segment_trees(las, dalponte2016(chm, ttops)) expected_table <- c(46, 80, 143, 98, 175, 15, 175, 196, 222, 132, 91, 124, 77, 147, 100, 52, 35, 81, 102, 81, 49, 95, 170, 89, 72, 60, 123, 91, 98, 125, 101, 69, 159, 118, 134, 128, 136, 99, 47, 58) expected_table <- table(las$treeID) expect_true("treeID" %in% names(las)) expect_equal(sort(unique(las$treeID)), 1:n) expect_equivalent(expected_table, expected_table) }) test_that("Silva algorithm works", { las <- segment_trees(las, silva2016(chm, ttops)) expected_table <- c(96, 64, 71, 131, 134, 126, 141, 110, 163, 93, 134, 104, 145, 92, 68, 81, 183, 93, 100, 65, 98, 71, 96, 44, 35, 80, 155, 95, 146, 92, 144, 196, 301, 177, 14, 180, 111, 186, 40, 89) expected_table <- table(las$treeID) expect_true("treeID" %in% names(las)) expect_equal(sort(unique(las$treeID)), 1:n) expect_equivalent(expected_table, expected_table) }) test_that("Watershed algorithm works", { skip_if_not_installed("EBImage") las <- segment_trees(las, watershed(chm)) expected_table <- c(336, 271, 208, 254, 198, 289, 140, 35, 195, 258, 287, 156, 167, 174, 233, 150, 251, 225, 121, 44, 103, 180, 109, 87, 198, 92, 43, 78, 15) expected_table <- table(las$treeID) expect_true("treeID" %in% names(las)) expect_equal(sort(unique(las$treeID)), 1:29) expect_equivalent(expected_table, expected_table) }) test_that("Dalponte algorithm works with sfc", { ttops <- sf::st_geometry(ttops) las <- segment_trees(las, dalponte2016(chm, ttops)) expected_table <- c(46, 80, 143, 98, 175, 15, 175, 196, 222, 132, 91, 124, 77, 147, 100, 52, 35, 81, 102, 81, 49, 95, 170, 89, 72, 60, 123, 91, 98, 125, 101, 69, 159, 118, 134, 128, 136, 99, 47, 58) expected_table <- table(las$treeID) expect_true("treeID" %in% names(las)) expect_equal(sort(unique(las$treeID)), 1:n) expect_equivalent(expected_table, expected_table) }) test_that("Silva algorithm works with sfc", { ttops <- sf::st_geometry(ttops) las <- segment_trees(las, silva2016(chm, ttops)) expected_table <- c(96, 64, 71, 131, 134, 126, 141, 110, 163, 93, 134, 104, 145, 92, 68, 81, 183, 93, 100, 65, 98, 71, 96, 44, 35, 80, 155, 95, 146, 92, 144, 196, 301, 177, 14, 180, 111, 186, 40, 89) expected_table <- table(las$treeID) expect_true("treeID" %in% names(las)) expect_equal(sort(unique(las$treeID)), 1:n) expect_equivalent(expected_table, expected_table) }) test_that("Dalponte algorithm catches invalid IDs in input", { ttops$treeID[2:3] <- 1L expect_error(segment_trees(las, dalponte2016(chm, ttops3), "Duplicated tree IDs found")) }) test_that("Silva algorithm catches invalid IDs in input", { ttops$treeID[2:3] <- 1L expect_error(segment_trees(las, silva2016(chm, ttops3), "Duplicated tree IDs found")) }) test_that("Dalponte algorithm returns NAs if no seed in chm", { expect_warning(las <- segment_trees(las, dalponte2016(chm, ttops_shifted500)), "No tree can be used as seed") expect_true("treeID" %in% names(las)) expect_true(all(is.na(las$treeID))) expect_equal(storage.mode(las$treeID), storage.mode(ttops_shifted500$treeID)) }) test_that("Silva algorithm returns NAs if no seed in chm", { expect_warning(las <- segment_trees(las, silva2016(chm, ttops_shifted500)), "No tree can be used as seed") expect_true("treeID" %in% names(las)) expect_true(all(is.na(las$treeID))) expect_equal(storage.mode(las$treeID), storage.mode(ttops_shifted500$treeID)) }) test_that("Dalponte algorithm does not fail with no seed at all", { ttops <- ttops[0,] expect_message(segment_trees(las, dalponte2016(chm, ttops)), "No tree found") }) test_that("Silva algorithm does not fail with no seed at all", { ttops <- ttops[0,] expect_message(segment_trees(las, silva2016(chm, ttops)), "No tree found") }) test_that("Dalponte algorithm works standalone", { expect_error(trees <- dalponte2016(chm, ttops)(), NA) expect_true(is(trees, "stars")) expect_equal(sort(unique(as.numeric(trees[[1]]))), ttops$treeID) expect_warning(trees <- dalponte2016(chm, ttops_shifted500)(), "No tree can be used as seed") expect_true(is(trees, "stars")) expect_equal(sort(unique(as.numeric(trees[[1]]))), numeric(0)) }) test_that("Silva algorithm works standalone", { expect_error(trees <- silva2016(chm, ttops)(), NA) expect_true(is(trees, "stars")) expect_equal(sort(unique(as.numeric(trees[[1]]))), ttops$treeID) expect_warning(trees <- silva2016(chm, ttops_shifted500)(), "No tree can be used as seed") expect_true(is(trees, "stars")) expect_equal(sort(unique(as.numeric(trees[[1]]))), numeric(0)) }) test_that("Watershed algorithm works standalone", { skip_if_not_installed("EBImage") expect_error(trees <- watershed(chm)(), NA) expect_true(is(trees, "stars")) expect_equal(sort(unique(as.numeric(trees[[1]]))), 1:29) }) test_that("Dalponte algorithm works standalone and is backward compatible", { skip_if_not_installed("raster") ttops <- locate_trees(chm, lmf(3, 2)) chm <- as(chm, "Raster") trees <- dalponte2016(chm, ttops)() expect_true(is(trees, "RasterLayer")) }) test_that("Silva algorithm works standalone and is backward compatible", { skip_if_not_installed("raster") ttops <- locate_trees(chm, lmf(3, 2)) chm <- as(chm, "Raster") trees <- silva2016(chm, ttops)() expect_true(is(trees, "RasterLayer")) }) test_that("Watershed algorithm works standalone and is backward compatible", { skip_if_not_installed("EBImage") skip_if_not_installed("raster") chm <- as(chm, "Raster") trees <- watershed(chm)() expect_true(is(trees, "RasterLayer")) }) test_that("Li algorithm method works", { las <- segment_trees(las, li2012(speed_up = 5)) expect_equal(sort(unique(las$treeID)), 1:48L) expect_true(is.integer(las$treeID)) expect_warning(segment_trees(las, li2012(hmin = 100)), "No tree segmented") })