* Oct, 2020: checked compatibility with lidR 3.0.3

+ Jan, 2020: field names changed to english

+ Oct, 2019: added projection info / updated points clouds in rda archive

+ Aug, 2018: first version

## Field inventory

### Import tree-level inventory

96 plots of 15 m radius have been inventoried in the Quatre Montagnes area (Vercors Mountain, France). Plots are aggregated in clusters of 4. Data are provided in:

+ one file per plot for tree information ("Verc-CLUSTERID-PLOTID-ArbresTerrain.csv)

- one file per cluster for plot center location ("Verc-CLUSTERID-PiquetsTerrain.csv)

The first step is to import all tree information in a single data.frame.

```{r treeInventory, include=TRUE}

# set inventory parameters

# plot radius (m)

p.radius <- 15

# DBH threshold (cm) for inventory, trees smaller than this value are not inventoried

### Import meta data about meridian convergence and declination

In case tree positions have to be precisely calculated in a given projected coordinates system, it is necessary to correct magnetic azimut with magnetic declination at the time of inventory and meridian convergence at the location.

```{r metaData, include=TRUE}

# get meta data about meridian convergence and declination by importing the meta file.

meta <- read.table(file="./data.ABA.model/placettes.csv",sep=";",header=T)

# keep only required attributes

meta <- meta[,c("Id","Convergence_gr","Declinaison_gr")]

# merge to add convergence and declination in plots data.frame

plots <- merge(plots, meta)

head(plots, n=3)

```

### Compute tree coordinates

Tree coordinates can be computed from the plot center coordinates and from the azimut, slope and ground distance measurements. In case ground distance is measured to the tree edge, the tree diameter has to be taken into account to compute the position of the tree center.

```{r treeCoordinates, include=TRUE}

# merge tree and plots data.frames to import plot center

trees <- merge(trees, plots[,c("X","Y","Convergence.gr","Declination.gr","plotId")],

Field data have to be checked to correct potential errors. Regarding all trees :

+ Horizontal distances between the plot center and the tree center have to be checked to make sure the tree is included in the 15 meter radius.

- Diameters can also be checked to avoid that trees below the DBH limit remain in the inventory.

Once values have been checked for potential writing errors, remaining outliers should be removed.

```{r checkData, include=TRUE}

# keep only trees inside plot

trees <- trees[trees$Horiz.Distance.m <=p.radius , ]

# keep trees above the DBH limit

trees <- trees[trees$Diameter.cm >= dbh.min , ]

# keep only live trees

trees <- trees[is.element(trees$Appearance, c(1,2)), ]

```

For each plot, if the slope and azimut are constant on the whole surface, then plotting the tree slope from the plot center as function of the tree azimut from the plot center should draw a sinusoid-shaped point cloud. Outliers are likely to be measurement errors. For trees located close to the plot center, slope measurement precision is lower.

Displaying the histogram of trees, colored by species is also informative to make sure no abnormal measures remain.

In this area, public forests are generally managed in a different way compared to private forests. This classification could be used as a stratification for subsequent analysis. Plots will be attributed to strata based on external GIS layers.

The code below presents a workflow to calibrate prediction models for the estimation of forest parameters from ALS-derived metrics, using the area-based approach (ABA).

## Load required data

The "Quatre Montagnes" dataset from France, prepared as described in https://gitlab.irstea.fr/jean-matthieu.monnet/lidaRtRee/wikis/ABA-data-preparation is loaded from R archives (rda files).

### Field data

Field data should be organised in a data.frame named "plots" with at least two fields: plotID (unique plot identifier) and a forest stand parameter. A factor variable "stratum" is required for stratified models. Each line in the data.frame corresponds to a field plot.

The provided dataset includes three forest stand variables :

* basal area in m^2/ha (G.ha)

+ stem density in /ha (N.ha)

+ mean diameter at breast height in cm (D.mean.cm).

Scatterplots are presented below.

```{r loadFieldData, include=TRUE}

# load plot-level data

load(file="./data.ABA.model/plots.rda")

summary(plots)

# display forest variables

plot(plots[,c("G.ha", "N.ha", "D.mean.cm")])

```

### ALS data

Normalized ALS point clouds are loaded, as well as terrain statistics previously computed from the ALS ground points of each cloud. The dataset is loaded from R archives (rda files) previously prepared in https://gitlab.irstea.fr/jean-matthieu.monnet/lidaRtRee/wikis/ABA-data-preparation. Point clouds corresponding to each field plot are organized in a list of LAS objects from package lidR.

```{r loadFieldData, include=TRUE}

# list of LAS objects normalized point cloud inside plot extent

load("./data.ABA.model/llasn.rda")

# display one point cloud

lidR::plot(llasn[[1]])

#

# terrain statistics previously computed with (non-normalized) ground points inside each plot extent

load("./data.ABA.model/terrain.stats.rda")

head(terrain.stats, n=3)

# check that plots are ordered in the same way in all data.frames

## Computation of ALS metrics from the point clouds

Two types of metrics can be computed :

* Point cloud metrics are directly computed from the point cloud or from the derived surface models on the whole plot extent.

+ Tree-based metrics are computed from the characteristics of trees detected in the point cloud (or in the derived surface models).

Point cloud metrics are computed with the function `cloudMetrics` of lidaRtRee package, which applies the `cloud_metrics` function of lidR package to all point clouds in the list. Default computed metrics are those proposed by the function `stdmetrics` of the lidR package. Additionnal metrics are available with the `ABAmodelMetrics` of package lidaRtRee.

Tree metrics rely on a preliminary detection of trees, which is performed with the `treeSegmentation` function of package lidaRtRee. For more information on tree detection, please refer to https://gitlab.irstea.fr/jean-matthieu.monnet/lidaRtRee/-/wikis/tree%20detection%20workflow. Tree segmentation requires point clouds or canopy height models with an additionnal buffer in order to avoid border effects when computing tree characteristics. Once trees are detected, metrics are derived with the function `stdTreeMetrics` of package lidaRtRee.

```{r computeTreeMetrics, include=TRUE}

# load point clouds with 15m buffer

# load("./data.ABA.model/llasn.buffer.Rdata")

# resolution of canopy height model

resCHM <- 0.5

# specifiy plot radius to exclude trees located outside plots

# print("terrain stats are not used in calibration because function to compute them on grid is not implemented yet")

```

## Model calibration

Once a dependant variable (forest parameter of interest) has been chosen, the function `ABAmodel` of package lidaRtRee is used to select the linear regression model that yields the highest adjusted-R^2, while checking that some model assumptions remain verified. A box-Cox transformation of the dependant variable can be done to normalize its distribution, or a log transformation of all variables. Model details and cross-validation statistics are available from the returned object.

```{r model.calibration, include=TRUE}

###########################

# calibrate one model : for whole dataset or subset

modell <- lidaRtRee::ABAmodelCombineStrata(modells, placettes$plotID)

modell$stats

# example of combination of stratum-specific models obtained with different transformations (requires previous computation of modellsbox for boxcox et modellslog for log)

# modell <- combine.stratified.ABA.models(list(public=modellsbox[[1]], private=modellslog[[2]]), placettes)

This workflow compares a canopy height model (CHM) derived from airborne laser scanning (ALS) data and inventoried tree positions, and proposes a translation in plot position for better matching. It works with circular plots. The method is described in @Monnet2014. The workflow uses field and ALS data acquired in the forest of Lac des Rouges Truites. It is based on functions from packages `lidaRtRee` and `lidR`.

* Jan, 2021: checked compatibility with lidR 3.1.0 and lidaRtRee 3.0.0

+ Oct, 2020: checked compatibility with lidR 3.0.3

+ Feb, 2019: checked compatibility with lidR 2.0.0 and lidaRtRee 2.0.0

+ May, 2018: initial version

```{r include = FALSE}

library(lidR)

```

## Material

### Field data

The study area is a part of the forest of Lac des Rouges Truites. 44 plots have been inventoried, 15 are available for testing. Plots have a 14.10 m radius. A data.frame `p` contains the positions of the center of plots. Attributes are:

* `placette`: plot id

- `Xtheo` and `Ytheo`: XY coordinates initially sampled when preparing the field inventory

- `XGPS` and `YGPS`: XY coordinates recorded in the field with a GNSS receiver during the field inventory

- `Prec`: GNSS precision in meter specified by the receiver

- `dist`: horizontal distance between the sample and recorded coordinates.

```{r plots, include = TRUE}

# load plot coordinates (data.frame with lines corresponding to the las objects)

On each plot, five trees which were considered suitable for coregistration (vertical trunk, high and peak-shaped crown, well separated from neighboring trees) have been positioned relatively to the plot center. From the XY coordinates recorded by the GNSS receiver and the relative coordinates (slope, distance, azimuth), the XY coordinates of those trees are computed. Data.frame `ap` contains the following attributes:

* `plac`: plot id

- `n`: tree id

- `dia`: tree diameter in cm

- `distR`: slope distance from the plot center to tree center, in m

- `azimutG`: azimuth from the plot center to the tree center, in grades

- `pente.`: slope from plot center to tree center, in grades

- `XYZGPS`: coordinates of the plot center

- `xyz`: coordinates of the tree center

- `d`: horizontal distance between plot center and tree in m

```{r trees, include = TRUE}

# load inventoried trees (data.frame with plot id info )

Airborne laser scanning data on the study area is part of a campaign acquired in 2016 with an airborne RIEGL LMS Q680i sensor. Acquisition was funded by the Région Franche-Comté.

ALS data over the plots is provided as a list of LAS objects in rda file.

```{r las, include = TRUE}

# load point cloud over reference plots (list of las objects)

The code to extract LAS objects from a directory containing the LAS files is (code corresponding to parameters in the next paragraph has to be run beforehand):

Several parameters have to be specified for optimal results.

```{r parameters, include = TRUE}

# vegetation height threshold for removal of high values

hmax <- 50

# field plot radius for computation of pseudo-CHM on the inventory area

p.radius <- 14.105

# raster resolution for image processing

r.res <- 0.5

# maximum distance around initial position to search for coregistration

b.size <- 5

# increment step to search for coregistration (should be a multiple of resolution)

s.step <- 0.5

```

## Coregistration of one plot

### Canopy height model

The first step is to compute the canopy height model from the ALS data, and remove artefacts by thresholding extreme values and applying a median filter.

raster::plot(r.mask, main="Plot mask and tree positions")

# add tree positions

points(trees[,c("x","y")], cex=trees[,"dia"]/40)

# add plot center

points(centre, pch=3)

```

### Compute correlation and identify optimal translation

First the function computes the correlation for different possible translations of the plot center inside the buffer specified by the user, and outputs an image of correlation values between the ALS CHM and the pseudo CHM. Second this image is analyzed to identify which translation yields the highest correlation. The function outputs a list which first element is the correlation image, and the second one the corresponding statistics.