library(ForestElementsR) # Attach ForestElementsR
# Other packages required in the code examples below
library(dplyr)
library(ggplot2)
library(purrr)
Many methods from the fields of forest growth and yield, as well as forest inventory, that belong to the core contents of forest related education were not widely available in a contemporary form so far. To cover this deficit and to continue doing so in the long run is the driving idea behind the creation and development of the package ForestElementsR. The concept of the package is mainly based on experiences and procedures made and developed at the former Chair of Forest Growth and Yield at the Technical University of Munich. Therefore, the method and object collection covered by the current version of ForestElementsR is slightly biased towards German and Bavarian conditions. This is, however, not intended to be a limitation for the the package’s future development. For this reason - too much pre-definition leads to restriction - ForestElementsR does not provide readily defined workflows. It does, in contrast, offer a collection of elementary (hence the name of the package) building blocks that can be combined into any workflow required by users for their specific requirements. If you are a developer, feel encouraged to use the methods and objects provided by ForestElementsR in your own forest-related R packages, e.g. for evaluating specific types of forest inventories, research plots, or the output of forest growth models. This is exactly what we, the authors of ForestElementsR, are currently using the package for.
Most of the functionality we provide with the package is focused on producing information that is typically connected to the fields of forest growth and yield, forest mensuration, and forest inventory. Therefore, there is a certain bias towards goal variables which are related to the production of wood in one way or the other. However, even data from classic forest surveys can be evaluated in many ways that allow conclusions about forest structure and diversity. A few such methods are implemented in the current version of ForestElementsR, and we intend to add more in the future. We deem this important, because such approaches make a better use of forest data with regard to multifunctional forest management and planning.
It is an important design requirement of ForestElementsR not to be just a conglomerate of functions and objects that exist independently of each other. We hope users will see overarching concepts when they find similarities in the naming of our functions and objects, and the composition of function calls. Other, even more important bundling features are the package’s open Species Coding System and the hierarchically organized family of objects that represent (samples from) forest stands
With ForestElementsR we would like to address a broad audience. Practitioners might want to apply it for many purposes from using single functions in a “forest pocket calculator” style to evaluate forest stand surveys by arranging functions in a workchain way. Scientists will hopefully find the package useful for carrying out routine evaluations of forest data (like volume estimates, site indexing) that are regular prerequisites for in-dephth scientific analyzes. Students could profit from applying ForestElementR’s functionality to the extensive set of example data that comes with the package. We are confident that this would help them to develop an understanding and intuition about quantitative key methods in forestry as well as about the quantities themselves.
We should add that, besides our own developments, we also have quite shamelessly implemented methods developed and published by other authors that have proven themselves useful in our daily work. We have tried our best to find and cite the original publications correctly in order to give credit to whom it belongs.
Before we proceed, let us put out an Important Warning: While the functions and objects we provide with ForestElemensR behave exactly as described in the documentation, professional training in the fields of forestry, forest management, forest science is required to apply them correctly and to understand what their output actually means. If you are a hobby forester, you are more than welcome to make use of this package, but be sure to consult a professional before you draw important conclusions from what you get out of your evaluations. We also would like to point out another trivial wisdom that, however, seems to be ignored on a regular basis: If your data are bad, don’t expect to get good results.
In the following sections we give an overview about the most important
features of ForestElementsR. We do not delve deeply into all
details as these can be taken from the specific documentations provided
for each function. For two major features, namely the species coding system, and the
representation of yield tables, we
provide their own vignettes.
ForestElementsR comprises three main families of S3 objects that are intended to facilitate the users’ as well as the developers’ lives. These relate to the representations of forest plots and stands, the species coding system, and ForestElementR’s yield table implementation.
The basic and most versatile class for representing plots or stands
is fe_stand
. For how to make such an object from data call
the documentation ?fe_stand
. As each tree in an fe_stand
object can have its own representation number (i.e. an information about
how many trees it represents per ha), it can represent (a cutout from) a
forest stand as well as any kind of sample with varying representation
like, e.g., angle count samples (where the representation number of a
tree depends on its dbh) or n-tree samples (where typically, the
outermost tree is only half represented). Also a concentric circle based
design can be represented; however, we provide specialized objects for
these. Let’s have a closer look at such an object:
# spruce_beech_1_fe_stand is one of the included example objects,
# for an overview, type "?example_data"
spruce_beech_1_fe_stand
#> $stand_id
#> [1] "spruce_beech_1"
#>
#> $area_ha
#> [1] 0.49
#>
#> $trees
#> # A tibble: 225 × 12
#> tree_id species_id layer_key time_yr age_yr dbh_cm height_m
#> <chr> <tm_wwk_shrt> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 1 1 1 2022 75 26.3 27.5
#> 2 2 1 1 2022 75 34.8 30.9
#> 3 3 1 1 2022 75 34.1 32.1
#> 4 4 1 1 2022 75 33.6 32.7
#> 5 5 1 1 2022 75 27.5 29.7
#> 6 6 1 1 2022 75 30 30
#> 7 7 1 1 2022 75 32.8 31.2
#> 8 8 1 1 2022 75 34.8 30.9
#> 9 9 1 1 2022 75 33.6 29.9
#> 10 10 1 1 2022 75 38.5 31.5
#> # ℹ 215 more rows
#> # ℹ 5 more variables: crown_base_height_m <dbl>, crown_radius_m <dbl>,
#> # removal <lgl>, ingrowth <lgl>, n_rep_ha <dbl>
#>
#> $small_trees
#> Dataframe mit 0 Spalten und 0 Zeilen
#>
#> attr(,"class")
#> [1] "fe_stand"
An fe_stand object is evidently a list-like object; its most important element is the data frame (tibble) “trees”, which contains all trees that were measured individually and have a dbh. Note, that the element small_trees is still experimental and not yet used by any function we provide with ForestElementsR. It is intended to, in the future, provide a technical home for trees so small that they do not have a dbh (i.e. trees with heights < 1.3 m) or are under the size threshold for being measured individually, but are counted in size classes. Due to the multitude existing approaches, we haven’t decided on a representation in fe_stand objects, yet. The only current requirement for the small_trees element is that it has to be a data frame. In our example above, this data frame is empty. We have written fe_stand versions for the S3 generic functions summary and plot:
oo <- options(fe_spec_lang = "eng") # display species names in English
spruce_beech_1_fe_stand |> summary() # Give a summary of stand-level values
#> # A tibble: 2 × 9
#> # Groups: time_yr [1]
#> time_yr species_id stem_number_ha basal_area_m2_ha d_q_cm d_dom_cm h_q_m
#> <dbl> <tm_wwk_shrt> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 2022 Norway spruce 306. 25.9 32.8 38.3 30.8
#> 2 2022 European beech 153. 9.85 28.6 36.0 28.1
#> # ℹ 2 more variables: h_dom_m <dbl>, v_m3_ha <dbl>
spruce_beech_1_fe_stand |> plot() # Make an appropriate plot
options(oo) # Reset species name display option
If spatial information about the location of the stand (sample)
and/or the single trees is available, the class
fe_stand_spatial
can be used. It is a child of fe_stand.
All spatial functionality is taken from the package sf, therefore,
fe_stand_spatial accepts a broad range of coordinate definitions, from
simple cartesian coordinates to more or less all common geocoordinate
definitions. The output of summary
and plot
for such an object is virtually the same as for its parent class
fe_stand
:
oo <- options(fe_spec_lang = "sci") # display scientific species names
# Example: A mixed mountain forest plot in the Bavarian Alps;
# for an overview, type "?example_data"
mm_forest_1_fe_stand_spatial |> summary() # Give a summary of stand-level values
#> # A tibble: 22 × 9
#> # Groups: time_yr [5]
#> time_yr species_id stem_number_ha basal_area_m2_ha d_q_cm d_dom_cm
#> <int> <tm_wwk_lng> <dbl> <dbl> <dbl> <dbl>
#> 1 1975 Picea abies 143. 21.4 43.6 60.3
#> 2 1975 Abies alba 101. 6.16 27.8 42.5
#> 3 1975 Fagus sylvatica 161. 3.83 17.4 29.5
#> 4 1975 Acer pseudoplatanus 101. 3.55 21.1 27.2
#> 5 1984 Picea abies 71.6 14.1 50.2 64.0
#> 6 1984 Abies alba 53.7 3.47 28.7 46.6
#> 7 1984 Fagus sylvatica 89.5 3.22 21.4 36.7
#> 8 1984 Acer pseudoplatanus 59.7 2.54 23.3 31.4
#> 9 1995 Picea abies 71.6 15.8 52.9 67.6
#> 10 1995 Abies alba 41.8 3.87 34.4 50.2
#> # ℹ 12 more rows
#> # ℹ 3 more variables: h_q_m <dbl>, h_dom_m <dbl>, v_m3_ha <dbl>
mm_forest_1_fe_stand_spatial |> plot() # Make an appropriate plot
options(oo) # Reset species name display option
However, the spatial information about the outline of the stand (plot) and the positions of the trees can also be accessed graphically. In our example, the object represents a rectangular research plot where the coordinates are not geolocated:
mm_forest_1_fe_stand_spatial$outline |>
ggplot() +
geom_sf(fill = NA) +
geom_sf(data = mm_forest_1_fe_stand_spatial$tree_positions) +
xlab("x (m)") + ylab("y (m)")
An important special child of fe_stand_spatial
is ´the
fe_ccircle_spatial
object. It is a generic container for
sample plots with a concentric circle design. A multitude of such
objects can be used for representing entire sample forest inventories.
The latter is actually the intended use of
fe_ccircle_spatial
which can be done e.g. in R packages
that build up on ForestElementsR. The summary
and
plot
functions are also available for these objects:
oo <- options(fe_spec_lang = "ger") # display German species names
spruce_pine_ccircle_spatial |> summary()
#> # A tibble: 2 × 9
#> # Groups: time_yr [1]
#> time_yr species_id stem_number_ha basal_area_m2_ha d_q_cm d_dom_cm h_q_m
#> <dbl> <tm_wwk_lng> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 2020 Fichte 207. 13.8 29.1 31.4 23.8
#> 2 2020 Kiefer 642. 32.0 25.2 43.8 33.3
#> # ℹ 2 more variables: h_dom_m <dbl>, v_m3_ha <dbl>
spruce_pine_ccircle_spatial |> plot(dbh_scale = 3) # oversize dbh x3
options(oo) # Reset species name display option
We provide user friendly functions for generating all the objects
described above. These functions have the same name as the class of the
object to be created. E.g. with fe_stand()
you can generate
an object of class fe_stand
from your own data. This will
only be successful if the candidate object passes a validation that
makes sure it is not ill-defined. For details, check the documentations
of the functions fe_stand()
,
fe_stand_spatial()
, fe_ccircle_spatial()
, and
fe_ccircle_spatial_notrees()
(a class designed for special
cases e.g, permanent forest inventory samples where one plot does not
have any trees at a given inventory period).
Unfortunately, the different forest research institutions in Central Europa are typically using different tree species codings, which are typically not or only partly compatible with each other. This creates considerable friction e.g. in cross sectional analyses of data that come from different institutions. Therefore, ForestElementsR comes with a generic open species coding system that allows to cast between different codings. As we provide its own vignette, which covers this species coding system in detail, we give only an outline here.
The system is designed in a way that coding casts work as long as there is no logical problem in the specific situation to solve, even when the two codings of interest are not entirely compatible. That means, e.g., when incompatibilities between two codings exist for very uncommon species or species groups only, they will not hamper the majority of casts. When a species coding cast loses information, a warning will be issued, but the cast will be executed. This is the case when a species cast leads from an individually coded species in the original coding into a species group code in the goal coding. Currently, six species codings are implemented in ForestElementsR. Five of them are codings that are in use in practice, probably most prominently the coding of the German National Forest Inventory (Riedel et al. 2017). All specific codings are linked to an internal master coding, which guarantees consistency. While the authors have a faint hope that this master coding could eventually become a common standard, they are also realists and understand the longevity of traditions.
Many functions in ForestElementsR that utilize species
codings, e.g. the volume function v_gri()
, will try to
convert any given species coding into the most similar supported one for
which parameters are available. In this way, useful functions that were
originally developed at one institution and are therefore compatible
with a specific species coding, become more widely applicable without
forcing users to do tedious technical pull-ups beforehand. When
examining the code examples in the previous
section, you may have noticed that R’s options()
mechanism is used there to select the language which is used to display
species names. Let us demonstrate this here again:
# Define a vector of species ids compatible with the coding used by the
# Bavarian Forest Service
species_ids <- as_fe_species_bavrn_state(c(10, 20, 21, 24, 68, 62, 60))
# How they are displayed depends on the current setting of the option
# fe_spec_lang
species_ids
#> <fe_species_bavrn_state[7]>
#> [1] 10 20 21 24 68 62 60
# Check the option
options("fe_spec_lang") # if NULL or "code" the number codes are displayed
#> $fe_spec_lang
#> NULL
# Set to English (and keep the prevous option settings in oo)
oo <- options(fe_spec_lang = "eng")
species_ids
#> <fe_species_bavrn_state[7]>
#> [1] Norway spruce Scots pine eastern white pine Arolla pine
#> [5] sweet cherry small-leaved lime European beech
# Set to scientific names
options(fe_spec_lang = "sci")
species_ids
#> <fe_species_bavrn_state[7]>
#> [1] Picea abies Pinus sylvestris Pinus strobus Pinus cembra
#> [5] Prunus avium Tilia cordata Fagus sylvatica
# Set to German
options(fe_spec_lang = "ger")
species_ids
#> <fe_species_bavrn_state[7]>
#> [1] Fichte Kiefer Strobe Zirbe Vogelkirsche
#> [6] Winterlinde Buche
# Set to codes
options(fe_spec_lang = "code")
species_ids
#> <fe_species_bavrn_state[7]>
#> [1] 10 20 21 24 68 62 60
# Reset to the previous setting before enforcing English
options(oo)
See the vignette Tree Species Codings in ForestElementsR for an in-detail description of the species coding system of ForestElementsR
Yield tables might be the most important tools forest science has
ever provided to forest practice. They represent an invaluable wealth of
empirical data that has been condensed into tabular descriptions of
forest stand growth that are easy to work with. For about three decades,
however, they have been increasingly frowned upon for several reason,
including their limited applicability to mixed species stands, their
inherent restriction to even aged stands only, and their representation
of forest growth under environmental conditions that have since changed,
and cannot be considered as stable as they were when the data behind the
tables were collected.Nevertheless, when handled wisely, yield tables
can still be highly valuable assets in our tool kit. Therefore, we
developed a generic implementation of yield tables in
ForestElementsR that is designed to work equally well with
yield tables of different origin with different specifications and
descriptive variables. An initial collection of yield tables for
important tree species in Central Europe has already been implemented
and comes readily with ForestElements; this collection will
certainly keep growing. Currently, it covers the yield tables contained
in BayMinELF (1990). All readily available
tables are listed in the ForestElementsR’s documentation. You
can look them up with ?yield_tables
. It is also possible
and intended that users can make their own preferred yield tables usable
with ForestElementsR even though they are not part of the
collection that comes with the package. The key object here is
fe_yield_table
, and the function
fe_yield_table()
was designed for generating such objects
in a user friendly way. See the vignette Yield Tables in ForestElementsR for a
detailed description of ForestElementR’s yield table
system.
The yield table system allows for an easy visualization of yield
tables. We demonstrate that by example of the widely known yield table
for Norway spruce by Wiedemann (1942).
Applying plot()
to an object of class
fe_yield_table
displays the ‘site index fan’, i.e. the mean
stand height over age curves for the site indexes (si) covered by the
table.
fe_ytable_spruce_wiedemann_moderate_1936_42 |> plot()