-
-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathplugins.qmd
116 lines (87 loc) · 4.83 KB
/
plugins.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
```{r,echo=FALSE,message=FALSE,warning=FALSE}
library(lidR)
library(stars)
r3dDefaults = rgl::r3dDefaults
m = structure(c(0.921, -0.146, 0.362, 0, 0.386, 0.482, -0.787, 0,
-0.06, 0.864, 0.5, 0, 0, 0, 0, 1), .Dim = c(4L, 4L))
r3dDefaults$FOV = 50
r3dDefaults$userMatrix = m
r3dDefaults$zoom = 0.75
knitr::opts_chunk$set(
comment = "#>",
collapse = TRUE,
fig.align = "center")
rgl::setupKnitr(autoprint = TRUE)
```
# `lidR` plugin system {#sec-plugins}
We have seen that `lidR` has many functions capable of processing a collection of files. Most of these functions support more than one algorithm to achieve a given task.
- `rasterize_terrain()` supports `tin()`, `knnidw()`, `kriging()`
- `rasterize_canopy()` supports `p2r()`, `dsmtin()`, `pitfree()`
- `classify_ground()` support `csf()`, `pmf()`, `mcc()`
- `locate_trees()` supports `lmf()`
- `segment_trees()` supports `dalponte2016()`, `li2012()`, `watershed()`, `silva2016()`
- `track_sensor()` supports `roussel2020()`, `Gatziolis2019()`
- and so on
What if a user wanted to create a new algorithm integration? For example, what about a new algorithm for ground segmentation or tree segmentation or another interpolation method to create a digital terrain model using cubic spline interpolation? Sounds interesting.
With `catalog_apply()` one can more or less replicate the original functions and apply a new method over a catalog. After all, `lidR` functions actually use `catalog_apply()` internally. However this implies a lot of code and is error prone especially for users who are not fully comfortable with the engine.
There is another way to create new algorithms that are fully compatible with `lidR` functions. This is not documented in the package because the underlying mechanism is not yet fully consistent and is still subject to improvements.
Let's continue with the bicubic spline interpolation method for creating a digital terrain model. There is a package called [`MBA`](https://CRAN.R-project.org/package=MBA) that implements bicubic spline interpolation. We will create a function `mba()` that can be used like any other algorithm:
``` r
dtm <- rasterize_terrain(las, algorithm = mba(n = 1, h = 8))
```
## Understanding lidR algorithms {#sec-plugin-system}
In `lidR`, an algorithm such as `tin()`, `p2r()` or `lmf()` is a function factory. The output is functions with extra classes so regular users wont immediately recognize that they are functions.
```{r}
algo <- knnidw(k = 10)
algo
class(algo) # algo is a function
```
Removing the extra classes we can see its a function and we can see the source code.
```{r}
class(algo) <- "function"
algo
```
We can see how a function designed to be used in `rasterize_terrain()` is designed. The signature is
``` r
function(las, where)
```
When creating a new algorithm for spatial interpolation, the function factory must return a function similar to what you see above. In the case of spatial interpolation `las` is a `LAS` with X Y and Z coordinates of ground points (cleaned of duplicates). `where` is a `data.frame` with the X Y coordinates of the location where we want to interpolate Z.
## Creation of the `mba` algorithm {#sec-plugin-creation}
Now let's create our `mba` algorithm.
```{r}
# mba is our function factory
mba <- function(n = 1, m = 1, h = 8, extend = TRUE) {
# f is created inside mba and receive the ground points in a LAS (gnd)
# and the location where to compute the interpolation (where)
f <- function(gnd, where) {
# computation of the interpolation (see the documentation of MBA package)
res <- MBA::mba.points(gnd@data, where, n, m , h, extend)
return(res$xyz.est[,3])
}
# f is a function but we can set compatible classes. Here it is an
# algorithm for DTM
f <- plugin_dtm(f)
return(f)
}
```
Now let see what happens if we instantiate the `mba` algorithm:
```{r}
algo <- mba(h = 6)
algo
```
We can now use it like any other `lidR` algorithm:
```{r plot-raster-mab, fig.height=5, fig.width=6, warning=FALSE}
LASfile <- system.file("extdata", "Topography.laz", package="lidR")
las <- readLAS(LASfile)
dtm <- rasterize_terrain(las, algorithm = mba())
plot(dtm, col = gray(1:50/50))
```
```{r plot-raster-3d-mba, rgl = TRUE}
plot_dtm3d(dtm, bg = "white")
```
It will even fail nicely if used inappropriately!
```{r, error = TRUE}
rasterize_canopy(las, 1, mba())
```
## What about other algorithms? {#sec-plugin-extensions}
`lidR` has algorithms for canopy height models, individual tree segmentation, individual tree detection, sensor tracking, snag segmentation and so on. They all have different behaviours and this is why it's difficult to document. If you want to create a new algorithm the best first step is to communicate directly with `lidR` developers :). The [`lidRplugins`](https://github.com/Jean-Romain/lidRplugins) package makes heavy use of the plugins system to provide extra methods for diverse tasks.