When creating a Digital Terrain Models from LIDAR data, there are discontinuities (absence) of points over areas with water. Algorithms try to approximate missing points, but the result is generally inaccurate. In the case of the method based on triangular irregular network (TIN), such areas are usually represented by relatively large triangles (see Figure 2). Below is a method to smooth out such areas.
My idea was to find a water areas with help of lidRplugins::delineate_lakes()
, calculate the mean
(or min
) values and merge it with original DTM file. The workflow is shown on Figure 1:
Let’s read the DTM already crated. It’s shown on Figure 2.
<- terra::vrt("data/dtm/rasterize_terrain.vrt") r
rasterize_terrain()
function.To find and delineate/vectorize the water areas we will use delineate_lakes()
function from lidRplugins
package (Roussel 2022). In result we will get a vector layer with areas of similar altitude. The tol
and tol2
parameters are used to set the tolerances.
<- lidR::readLAScatalog("data/las/") |>
l ::delineate_lakes(tol = 1/1000, tol2 = c(1/10^6, 2/10^4)) |>
lidRplugins::vect(l) |>
terra::project(terra::crs(r)) terra
As the output of delineate_lakes()
can contain the flat terrain areas as well, we will filter out just water (-keep_class 9
) points from LAScatalog
, rasterize them and convert raster to polygon.
<- lidR::readLAScatalog("data/las/", filter = "-keep_class 9") |>
v ::rasterize_density() |>
lidR::as.polygons() |>
terra::subset(density > 0, NSE = TRUE) |>
terra::project(terra::crs(r)) terra
Now, having two polygons: 1st with delineated areas, and 2 only with water areas, lets intersect them to have only delineated water:
<- l |>
w subset(terra::is.related(l, v, relation = "intersects"))
Now we are going to extract the mean
(or you can use any other function) values of altitude for the vector areas and bind it to our vector layer.
<- terra::extract(r, w, fun=min, bind = TRUE, na.rm=TRUE)
w <- setNames(w, "mean_height")
w ::writeVector(w, "data/w.gpkg", overwrite = TRUE) terra
::plot(r, col = grDevices::gray.colors(50))
terra::plot(w, add = TRUE) terra
And finally produce the output raster:
<- r
ra <- NA
ra[] <- terra::rasterize(w,ra, field = w$mean_height)
ra
<- !is.na(ra)
indx <- r
rb <- ra[indx]
rb[indx] ::writeRaster(rb, file = "data/rb.tif", overwrite = TRUE) terra