library(FeNEU) # Attach FeNEU
library(ForestElementsR) # FeNEU builds on it, see the note below
library(dplyr) # used in the code examples below
options(fe_spec_lang = "eng") # display species names in EnglishFeNEU is an R package for evaluating inventory data from larger forest management units in order to support professional forest planning in practice. The package is designed to cover sample inventories as well as stand-wise forest inventories, and it is by design open for new data sources and evaluation methods. This first version does not claim to be able to do “everything” but provides a first reliable collection of central functions and workflows; i.e. a solid fundament, on which we can build up further development. FeNEU makes extensive use of the objects and functions provided by the package ForestElementsR (Biber and Toraño Caicoya 2026). We recommend attaching that package alongside FeNEU, as we do in the setup above. FeNEU calls it internally either way, but the inventory plots themselves are ForestElementsR objects, and the methods that display and plot them become available only once the package is attached.
Before we proceed, let us put out the same Important Warning as we do with ForestElementsR: While the functions and objects we provide with FeNEU 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.
Building on the object model and functions provided by ForestElementsR, FeNEU adds the evaluation and reporting layer that larger forest inventories require: i) Standardised import of several data formats, ii) reports of the status quo covered by an inventory (called ‘base tables’, ‘structure tables’), and iii) increment evaluations and estimates with different methods. In addition, the package provides functions for generating supporting information, e.g. inventory plot information sheets for supporting surveys in the field.
This vignette provides a brief overview of the most important
concepts of the package, how to obtain it and how to work with it. For
more detailed information we provide two additional vignettes, namely
vignette("data_formats") and
vignette("increment_estimation"). All examples shown below
can be run as they stand; they use the example datasets that come with
FeNEU (see ?data_examples_overview for the full
list).
Most evaluations in FeNEU end in a formatted PDF report,
produced by the family of *_pdf() functions. These render
through rmarkdown and kableExtra on
top of two tools that live outside R and are therefore not
installed along with the package: pandoc and a
LaTeX distribution. All other functionality of
FeNEU works without them; only the *_pdf()
functions require them.
To find out where you stand, ask the package:
The function reports each component separately and, for anything that
is missing, prints what to do about it. If a LaTeX distribution is what
you lack, setup_pdf_toolchain() installs TinyTeX for you (a small LaTeX
distribution of about 300 MB; any pre-existing TeX Live or MiKTeX
installation works equally well and is left untouched). Pandoc must be
installed by hand from https://pandoc.org/installing.html — unless you work in
RStudio, which ships its own copy and needs no further attention.
Setting this up is a one-time job per machine. Should you forget it,
the *_pdf() functions stop early with a clear message
instead of failing later somewhere deep inside the rendering
toolchain.
Basically FeNEU covers two types of forest inventories, i.e. sample inventories and stand-wise inventories. The former are typically designed as a grid overlaying the forest area, and a sample plot positioned at each gridpoint. Often, the plot locations are permanently marked in order to allow for repeated inventories of the sample plots. Standwise inventories, in contrast, collect information based on the pre-defined stands that together make up the forest area to be surveyed. Typically, several samples are taken at different positions in the stand without keeping their exact location. Usually, the measurements are less detailed compared to sample inventories. While both inventory types can be done with different methods, sample inventories do very often use a concentric circle plot design while stand-wise inventories often use angle count samples. Thus, the current version of FeNEU supports sample inventories with a concentric circle design and stand-wise inventories based on angle count sampling. This is, however, not a permanent restriction. In the contrary, FeNEU is technically not limited to these concepts, and more will be added if required.
At the input side FeNEU can deal with raw data and preprocessed data. Raw data are data in the shape as they have been collected in the field. Typically, at that stage, they are not yet in a format that is ideal for further evaluations, and they have undergone no or only sparse plausibility checks. FeNEU provides functions that read different raw data formats and transform them into corresponding preprocessed formats.
Preprocessed data, however, do not have to originate from FeNEU. The preprocessed formats are defined formats in their own right, and FeNEU reads them whichever way they were produced — by its own raw data import, by another program, or by an inventory service provider who delivers data in that shape already. This is why the import is deliberately split into two steps that can be used together or separately.
Whichever way the data enter, they all lead to the same destination: an object of the class fe_inventory. This is the uniform representation in which FeNEU holds inventory data internally, no matter which inventory type, survey method or input format they came from. Everything the package does afterwards works on that object rather than on the original files. The consequence is a useful guarantee: once the data have been successfully converted into an fe_inventory object, all subsequent evaluations are technically sound. Whether they are also sensible remains, as always, a question of the data and of professional judgement.
Currently, FeNEU supports three raw data formats and two preprocessed formats:
Note that the format numbers are assigned within an inventory type: the “Format 1” of the sample inventories has nothing to do with the “Format 1” of the stand-wise ones. The complete, column-level definition of all five formats — which columns are mandatory, which are optional, and what they mean — is the subject of the vignette Data Formats.
Before we get into the workflows, we should mention the central
object class in FeNEU, the S3 class fe_inventory. This
class defines how a forest inventory is represented in FeNEU.
Thus, the first and most crucial step when working with FeNEU
is to transform the raw data into an fe_inventory object. After
this has been accomplished, i.e. an fe_inventory object has
been successfully constructed, it is guaranteed that all subsequent
evaluations will (technically) work. In essence, an
fe_inventory object is a tibble where every row represents an
inventory unit. Let us have a look at an example fe_inventory
object (data_ex3_sample_fe_inventory) that comes with the
package FeNEU. Note that this example comprises only ten
inventory units (plots) for quick demonstration purposes, while real
inventories often come with several hundred up to several thousand
plots. Also note that the example data_ex3_sample_fe_inventory
is realistic, but not real. Do not expect to find anything related to
the dummy geocoordinates stored in this object.
data_ex3_sample_fe_inventory |> is_fe_inventory()
#> [1] TRUE
data_ex3_sample_fe_inventory
#> # A tibble: 10 × 3
#> plot_id plot area_rep_ha
#> * <chr> <list> <dbl>
#> 1 900000002_1_99900002 <f_ccrcl_> 2.97
#> 2 900000015_1_99900015 <f_ccrcl_> 5.14
#> 3 900000020_1_99900020 <f_ccrcl_> 4.43
#> 4 900000029_1_99900029 <f_ccrcl_> 4.69
#> 5 900000043_1_99900043 <f_ccrcl_> 3.97
#> 6 900000046_1_99900046 <f_ccrcl_> 7.05
#> 7 900000061_1_99900061 <f_ccrcl_> 6.98
#> 8 900000075_1_99900075 <f_ccrcl_> 3.08
#> 9 900000086_1_99900086 <f_ccrcl_> 3.80
#> 10 900000100_1_99900100 <f_ccrcl_> 4.63The object has three columns, the central column plot being the most important, because it contains the inventory units themselves. Each element of this column is an object of ForestElementsR’s fe_stand family. The column plot_id provides a unique id for every inventory unit, and the column area_rep_ha shows how much forest area (in hectares) is represented by each inventory unit. Let us check out the first inventory unit:
inv_unit <- data_ex3_sample_fe_inventory$plot[[1]]
class(inv_unit)
#> [1] "fe_ccircle_spatial" "fe_stand_spatial" "fe_stand"
names(inv_unit)
#> [1] "stand_id" "time_yr" "circle_definition"
#> [4] "tree_positions" "trees" "small_trees"An inventory unit is itself a list of slots: an identifier, the
survey year, the geometry of the sample circles, the positions of the
trees, and the two tree tables. The heart of it is trees,
which holds one row per tree:
inv_unit$trees
#> # A tibble: 14 × 14
#> tree_id species_id layer_key time_yr age_yr dbh_cm height_m
#> <chr> <bavrn_state> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 3_1 Scots pine 4 2020 91 36.5 25.5
#> 2 4_1 Norway spruce 1 2020 53 36.5 NA
#> 3 4_2 Norway spruce 1 2020 53 31.5 NA
#> 4 4_3 Norway spruce 1 2020 53 23.5 NA
#> 5 4_4 Norway spruce 1 2020 53 35.5 NA
#> 6 4_5 Norway spruce 1 2020 53 13.5 NA
#> 7 4_6 Norway spruce 1 2020 53 30.5 NA
#> 8 4_7 Norway spruce 1 2020 53 31.5 NA
#> 9 4_8 Norway spruce 1 2020 53 24.5 NA
#> 10 4_9 Norway spruce 1 2020 53 31.5 NA
#> 11 4_10 Norway spruce 1 2020 53 20.5 NA
#> 12 4_11 Norway spruce 1 2020 53 33.5 NA
#> 13 4_12 Norway spruce 1 2020 53 31.5 NA
#> 14 4_13 Norway spruce 1 2020 53 20.5 NA
#> # ℹ 7 more variables: crown_base_height_m <dbl>, crown_radius_m <dbl>,
#> # removal <lgl>, ingrowth <lgl>, n_rep_ha <dbl>, tree_count <dbl>,
#> # h_m_tree <lgl>We can also obtain a graphical representation of the inventory plot.
The dbh_scale argument draws the stems oversized, which
makes them easier to see on a small map:
Two details of this map are worth knowing. The stems are shown in their true positions, but only trees from the second circle outwards carry coordinates at all — for the innermost circle, positions are not recorded. And that innermost circle is not concentric with the other two: it sits five metres north of the inventory point. That is the field convention these data follow, not a defect of the plot.
Evidently, this inventory unit is an object of class fe_ccircle_spatial which represents a sample inventory plot with a concentric circle design. This class is a child of fe_stand_spatial which in turn is a child of fe_stand (see the documentation of ForestElementsR for details about these classes). While it is not required that all elements of an fe_inventory object’s plot column have exactly the same class, as long as they are children of fe_stand, such a mix would be quite unusual and is not recommended. Some evaluations provided by FeNEU are only possible with special subtypes of fe_inventory objects. E.g. increment calculations from two repeated inventories are currently only possible when both inventories exclusively contain plots with a concentric circle design (i.e. fe_ccircle_spatial objects). In the following section, we will demonstrate how field data can be transformed into an fe_inventory object.
Field survey exports rarely come in the exact shape an fe_inventory object needs. The import therefore proceeds in two steps, each with its own function per inventory type. The first step, raw to pre, standardises the format; the second, pre to fe_inventory, builds the object. The following sketch shows how the pieces fit together:
stage sample inventory, concentric circles stand-wise inventory, relascope
-------------------------------------------------------------------------------------
raw Format 1 Format 2 Format 1
| | |
v v v
import_sample_concentric_ import_standwise_relascope_
format1_raw_to_pre() format2_raw_to_pre() format1_raw_to_pre()
| | |
v v v
pre --- BaySF style format --- Silvarith style format
(five tab-delimited files) (one file, WZP_Daten.txt)
| |
import_sample_concentric_ import_standwise_relascope_
pre_to_fe_inventory() pre_to_fe_inventory()
| |
v v
fe_inventory ------------------ fe_inventory ---------------------
Read the sketch from top to bottom, but note that you may enter it at either level. If your data are already in one of the preprocessed formats, you skip the upper half entirely and start at pre.
The example datasets that come with FeNEU are placed in exactly this scheme. Each of them covers the stages that are useful for demonstrating it, which is why not every set exists at every stage:
| Set | Inventory type | raw | pre | fe_inventory | Scope |
|---|---|---|---|---|---|
| ex1 | sample, concentric | Format 1 | 10 points | ||
| ex2 | sample, concentric | Format 2 | 10 points | ||
| ex3 | sample, concentric | yes | yes | 10 points, plus an earlier survey | |
| ex4 | sample, concentric | yes | yes | 100 points, plus an earlier survey | |
| ex5 | sample, concentric | yes | yes | 10 simulated points, plus an earlier survey | |
| ex6 | stand-wise, relascope | Format 1 | yes | yes | 2 stands, 17 points |
| ex7 | stand-wise, relascope | yes | yes | 2 stands, 15 points |
The sets that carry an earlier survey (ex3, ex4, ex5) are the ones we
use for increment estimation from repeated inventories later on. Data at
the fe_inventory stage are ready-made R objects, named like
data_ex3_sample_fe_inventory; data at the raw and
pre stages are text files, and you obtain the directory holding
them with system.file(), as the examples below show.
?data_examples_overview describes every set in detail, down
to the individual file.
The *_raw_to_pre() functions read the field data as
tab-delimited text files, check them, and write the preprocessed files
into a directory of your choosing. They are called for that side effect,
and return a short report telling you how it went.
Let us convert the raw stand-wise example (ex6) that comes with the package:
raw_dir <- system.file("extdata", "data_ex6_standwise_raw", package = "FeNEU")
dir(raw_dir)
#> [1] "EingabedatenGesamt.txt" "Inventuraufnahmen.txt"
pre_dir <- file.path(tempdir(), "ex6_pre")
report <- import_standwise_relascope_format1_raw_to_pre(
input_path = raw_dir,
output_dir = pre_dir
)
report$ok
#> [1] TRUE
dir(pre_dir)
#> [1] "WZP_Daten.txt"For a sample inventory it takes a little more, because the geometry
of the concentric circles is not part of the field data — it belongs to
the inventory design. It has to be supplied, as a pair of vectors giving
the lower DBH threshold and the radius of each circle (as we do below).
Alternatively, it can also be provided as a text file. This option is
especially useful when differing circle definitions were applied
throughout an inventory (see documentation of the import functions). Get
it wrong and every figure is wrong with it, since these radii are what
the representation factors of the sampled trees are computed from. The
values used below mirror the design of inventory example ex1;
?data_examples_overview lists them for the raw example
sets. Here is ex1, in raw Format 1:
raw_dir_ex1 <- system.file("extdata", "data_ex1_sample_raw", package = "FeNEU")
dir(raw_dir_ex1)
#> [1] "Baumschicht.txt" "Inv_punkt.txt" "Verjuengung.txt"
pre_dir_ex1 <- file.path(tempdir(), "ex1_pre")
report_ex1 <- import_sample_concentric_format1_raw_to_pre(
input_path = raw_dir_ex1,
output_dir = pre_dir_ex1,
small_trees_filename = "Verjuengung", # optional
dbh_cm_from = c(0, 12, 30, 48),
radiuses_m = c(2.82, 5.64, 11.28, 17.84),
coord_sys = "gk4",
species_guess = TRUE
)
report_ex1$ok
#> [1] TRUE
dir(pre_dir_ex1)
#> [1] "fcbestku.txt" "fdinvba.txt" "fdinvbhd.txt" "fdinvkrs.txt" "fdvikrs.txt"Note what the function has to say about these data:
report_ex1$warnings
#> [1] "Stand-age data are missing or incomplete. Analyses that need the (stand) age are therefore not possible (see the function documentation)."
#> [2] "4 species abbreviation(s) were matched only by fuzzy/approximate rules and should be checked manually (see report$species_mapping, tier == 'review')."Neither remark is fatal, and both are worth having. The first tells
us that we will not be able to run some age-based evaluation on this
inventory, especially increment estimates with yield tables or single
tree growth functions (see vignette Increment Estimation). Age
information might be incomplete due to errors, but also due to
uneven-aged stands where an age is not defined. The second is a
consequence of species_guess = TRUE, which lets the import
resolve species abbreviations it does not know verbatim: four of them
were matched only by approximate rules, and
report_ex1$species_mapping shows which, so that the guesses
can be checked rather than trusted. We recommend to use the species
coding supported by the data format of choice instead of species namme
abbreviations that have to be guessed afterwards. The report is a list
with the elements ok, errors,
warnings and paths. It is worth reading rather
than discarding: ok is FALSE whenever the data
could not be converted, and warnings is where the functions
tell you what they had to do with your data in order to proceed.
Three properties of this step deserve mention, because they are what makes the difference between an import that works on real-world data and one that only works on tidy data:
Plausibility checks. The raw data import is the place where the data are scrutinised, and it distinguishes carefully between two kinds of problem. A defect that invalidates a whole inventory point — a duplicated plot identifier, or a plot whose centre coordinate is missing — stops the import immediately and with a clear message, because there is no defensible way to carry on. A defect confined to a single tree — a tree above the innermost circle that carries no position, a tree with only half a position, a living tree without a stand layer — leads to that one tree being excluded or its position dropped, while the rest of the plot survives. Whenever this happens, the affected trees are named individually in the warnings, by tree number where one exists and by DBH otherwise, so that you can look them up in your own data. What FeNEU never does is guess: the package does not invent a plausible value to keep going quietly.
Encoding robustness. Field data arrive in whatever
encoding the exporting program happened to use, and the difference
between UTF-8 and ISO-8859-1 usually shows up in exactly the places that
matter — species abbreviations and column names carrying umlauts. Every
import function takes an encoding argument that defaults to
"auto", and the species and header matching additionally
recognises the characteristic garbling that results when UTF-8 text is
read as Latin-1, and repairs it.
Decimal separator robustness. German-language
exports write decimals with a comma. Reading such a file with automatic
type detection silently turns a DBH of 23,4 into
234, which is a data error no plausibility check further
downstream can recognise as one. FeNEU therefore reads every
input file as plain text and converts only those columns it actually
uses, tolerant of both the comma and the point.
The second step reads the preprocessed files (tab-delimited text) and assembles the fe_inventory object, which includes validating it. Continuing with the files we have just written:
inv_ex6 <- import_standwise_relascope_pre_to_fe_inventory(pre_dir)
inv_ex6
#> # A tibble: 17 × 3
#> plot_id plot area_rep_ha
#> * <chr> <list> <dbl>
#> 1 1_7- 2- 0- - 0 <fe_stand> 0.416
#> 2 1_7- 5- 0- - 0 <fe_stand> 0.546
#> 3 2_7- 2- 0- - 0 <fe_stand> 0.416
#> 4 2_7- 5- 0- - 0 <fe_stand> 0.546
#> 5 3_7- 2- 0- - 0 <fe_stand> 0.416
#> 6 3_7- 5- 0- - 0 <fe_stand> 0.546
#> 7 4_7- 2- 0- - 0 <fe_stand> 0.416
#> 8 4_7- 5- 0- - 0 <fe_stand> 0.546
#> 9 5_7- 2- 0- - 0 <fe_stand> 0.416
#> 10 5_7- 5- 0- - 0 <fe_stand> 0.546
#> 11 6_7- 2- 0- - 0 <fe_stand> 0.416
#> 12 6_7- 5- 0- - 0 <fe_stand> 0.546
#> 13 7_7- 2- 0- - 0 <fe_stand> 0.416
#> 14 7_7- 5- 0- - 0 <fe_stand> 0.546
#> 15 8_7- 2- 0- - 0 <fe_stand> 0.416
#> 16 8_7- 5- 0- - 0 <fe_stand> 0.546
#> 17 9_7- 5- 0- - 0 <fe_stand> 0.546For a sample inventory it works exactly the same way. The package ships the preprocessed files of example ex3 in BaySF style format:
pre_path <- system.file("extdata", "data_ex3_sample_pre", package = "FeNEU")
dir(pre_path)
#> [1] "fcbestku.txt" "fdinvba.txt" "fdinvbhd.txt" "fdinvkrs.txt" "fdvikrs.txt"
fe_inv <- import_sample_concentric_pre_to_fe_inventory(pre_path)
fe_inv
#> # A tibble: 10 × 3
#> plot_id plot area_rep_ha
#> * <chr> <list> <dbl>
#> 1 900000002_1_99900002 <f_ccrcl_> 2.97
#> 2 900000015_1_99900015 <f_ccrcl_> 5.14
#> 3 900000020_1_99900020 <f_ccrcl_> 4.43
#> 4 900000029_1_99900029 <f_ccrcl_> 4.69
#> 5 900000043_1_99900043 <f_ccrcl_> 3.97
#> 6 900000046_1_99900046 <f_ccrcl_> 7.05
#> 7 900000061_1_99900061 <f_ccrcl_> 6.98
#> 8 900000075_1_99900075 <f_ccrcl_> 3.08
#> 9 900000086_1_99900086 <f_ccrcl_> 3.80
#> 10 900000100_1_99900100 <f_ccrcl_> 4.63The result is the very object that FeNEU also ships
ready-made as data_ex3_sample_fe_inventory, and which we
reuse in the workflows below:
Note that we did not have to tell the function anything about the
coordinate reference system. Preprocessed data in BaySF style format
declare their own coordinate system, and FeNEU uses that
declaration. Should your files not carry it, the function will ask you
for it explicitly through its coord_sys argument rather
than assuming one — an assumption about coordinates is a particularly
expensive way to be wrong.
For those cases in which the inventory type is not known until run
time — a graphical user interface built on top of FeNEU is the
typical example — the package also offers
read_and_convert_data(). It covers the pre to
fe_inventory step for all inventory types and dispatches on its
inventory_type argument:
fe_inv_2 <- read_and_convert_data(pre_path, inventory_type = "sample_concentric")
isTRUE(all.equal(fe_inv_2, fe_inv))
#> [1] TRUEThis is an expert function, and the two-step route shown above remains the recommended one for everyday work: it names the inventory type in the function you call, which makes both your script and its error messages easier to read.
A status-quo evaluation describes what an inventory currently holds: stem number, basal area and standing volume, broken down by species group and by size or age. FeNEU offers two such evaluations, base tables and structure tables, and produces each of them in three separate steps:
aggregation restructuring rendering
base_table_*() -> output_base_table() -> output_base_table_pdf()
structure_table_*() output_structure_table() output_structure_table_pdf()
the numbers the table layout the formatted report
(R objects) (an R data frame) (a PDF file)
This separation is deliberate, and it matters more than it may look. The figures are computed first and handed back as plain R objects, so that you can work with them further — check them, combine them, feed them into your own graphics or into a report of your own making. Nothing forces you through the PDF, and the intermediate table stays language-neutral; only the last step commits to a language and a layout. If all you want is a number, you never have to render anything.
All evaluation functions work on a flat tree table rather than on the
nested fe_inventory object. Preparing that table is the one
step every evaluation in the rest of this vignette shares:
# The h_q fallback warning is expected here: a few species x layer groups in the
# example data carry no measured height, so their mean height is modelled.
trees_with_heights <- suppressWarnings(
data_ex3_sample_fe_inventory |>
pull_trees() |>
height_complete_inventory()
)
trees <- data_ex3_sample_fe_inventory |>
fill_heights_back(trees_with_heights) |>
pull_trees() |>
trees_add_essentials(method = "BaySF")
#> | | | 0% | |========= | 12% | |================== | 25% | |========================== | 38% | |=================================== | 50% | |============================================ | 62% | |==================================================== | 75% | |============================================================= | 88% | |======================================================================| 100%
trees
#> # A tibble: 139 × 23
#> plot_id area_rep_ha layer_key species_id species_group tree_id time_yr
#> <chr> <dbl> <dbl> <bavrn_state> <bav_st_shrt> <chr> <dbl>
#> 1 900000002_… 2.97 4 Scots pine pine 3_1 2020
#> 2 900000002_… 2.97 1 Norway spruce spruce 4_1 2020
#> 3 900000002_… 2.97 1 Norway spruce spruce 4_2 2020
#> 4 900000002_… 2.97 1 Norway spruce spruce 4_3 2020
#> 5 900000002_… 2.97 1 Norway spruce spruce 4_4 2020
#> 6 900000002_… 2.97 1 Norway spruce spruce 4_5 2020
#> 7 900000002_… 2.97 1 Norway spruce spruce 4_6 2020
#> 8 900000002_… 2.97 1 Norway spruce spruce 4_7 2020
#> 9 900000002_… 2.97 1 Norway spruce spruce 4_8 2020
#> 10 900000002_… 2.97 1 Norway spruce spruce 4_9 2020
#> # ℹ 129 more rows
#> # ℹ 16 more variables: age_yr <dbl>, age_class <ord>, dbh_cm <dbl>,
#> # dbh_class <ord>, height_m <dbl>, crown_base_height_m <dbl>,
#> # crown_radius_m <dbl>, removal <lgl>, ingrowth <lgl>, n_rep_ha <dbl>,
#> # h_m_tree <lgl>, dbh_trshld <dbl>, g_m2 <dbl>, v_m3 <dbl>, v_hub_m3 <dbl>,
#> # standing_area_m2 <dbl>Three things happen here. pull_trees() unnests the
inventory into one row per tree.
height_complete_inventory() estimates the heights of all
trees that were not measured — in a typical inventory that is the large
majority — and fill_heights_back() writes those estimates
into the inventory object, so that the second pull_trees()
delivers a tree table whose heights are complete. Finally
trees_add_essentials() derives what the evaluations need:
basal area, single-tree volume, species groups and size classes.
The detour through fill_heights_back() and a second
pull_trees() is worth keeping to. It leaves the heights in
the inventory object rather than in a loose table, and it is the form
the rest of the package expects.
A base table breaks the inventory down by species group and by one class variable, for which there is a choice: age classes or mean diameter classes. Which of the two to use is not a matter of taste. Age classes are the traditional grouping and work well as long as stands are reasonably even aged, so that a single stand age is a meaningful statement. In an enterprise that consists largely of structurally rich, uneven-aged stands, it is not: there is no age one could sensibly assign to such a stand. Mean diameter classes describe those forests better, because a diameter can always be measured. This is why FeNEU offers both throughout.
The second choice concerns the tree cohort: the whole inventory collective, or the main stand only. This distinction runs through the entire package:
base_table_age_class(),
base_table_d_q_class()) covers every living tree the
inventory recorded, understorey and advance regeneration included. It
answers what is standing there.base_table_age_class_main_stand(),
base_table_d_q_class_main_stand()) restricts the evaluation
to the main stand layer. This is typically the most important cohort of
trees that carries the stand.The difference is not merely one of a filter. If per-hectare figures of species, age classes and other cohorts are desired, it is necessary to estimate the area share covered by the cohort of interest. This is only meaningful for the main stand. The all-layers tables therefore report absolute figures but no per-hectare values, while the main stand tables report both. Here is the main stand of ex3 by age class:
bt <- base_table_age_class_main_stand(trees)
names(bt)
#> [1] "detail" "total" "all_species" "all_total"The result is a list of four data frames, and they are the levels of
aggregation the printed table is later assembled from:
detail holds each species group by class,
total each species group across all classes,
all_species each class across all species groups, and
all_total the grand total. Confidence intervals and mean
diameters cannot be summed, which is why these levels are computed
rather than added up:
bt$detail |>
select(species_group, age_class, v_hub_m3, n, d_q_cm, n_plot, conf_95_per)
#> # A tibble: 18 × 7
#> # Groups: species_group, age_class [18]
#> species_group age_class v_hub_m3 n d_q_cm n_plot conf_95_per
#> <bav_st_shrt> <ord> <dbl> <dbl> <dbl> <int> <dbl>
#> 1 spruce (20,40] 151. 86594. 3.72 1 NA
#> 2 spruce (40,60] 150. 99.6 43.5 1 NA
#> 3 spruce (60,80] 63.5 88.8 31.5 1 NA
#> 4 spruce (80,100] 1827. 1496. 35.6 3 165.
#> 5 spruce (140,160] 553. 672. 28.1 1 NA
#> 6 spruce (160,999] 2071. 1112. 41.0 1 NA
#> 7 pine (60,80] 1653. 2076. 32.9 2 212.
#> 8 pine (80,100] 2000. 2206. 33.5 3 288.
#> 9 pine (100,120] 431. 309. 39.8 1 NA
#> 10 pine (160,999] 1167. 648. 43.9 1 NA
#> 11 Douglas fir (80,100] 173. 76.0 48.5 1 NA
#> 12 beech (80,100] 143. 61.9 51.5 1 NA
#> 13 oak (0,20] 0 2793. 5.5 1 NA
#> 14 oak (40,60] 54.8 99.6 30.5 1 NA
#> 15 other hardwood (0,20] 168. 5587. 10.5 1 NA
#> 16 other hardwood (40,60] 226. 498. 28.5 1 NA
#> 17 other hardwood (80,100] 124. 61.9 47.5 1 NA
#> 18 noble hardwood (40,60] 104. 99.6 35.5 1 NATwo figures in this output deserve a comment, because both say something about reading inventory results.
The stem number of the youngest age class looks enormous next to the
others. Extreme values often come with very small sample sizes as we
have here (our demo example comprises ten plots only). As we can see in
the n_plot column, the stem number and the other variables
in the age class are upscaled from one single plot only. However,
despite being very high, such a number is not impossible on a single
plot. The trees are very small on average, and per hectare they amount
to some 14 000 stems, which is a possible density for small trees coming
from natural regeneration.
The column conf_95_per is the more important one. It
gives the width of the 95 % confidence interval of the volume, as a
percentage of the volume itself, and in this example it is wide, clearly
due to the unrealistically small demo sample size of just ten plots. For
the inventory as a whole it comes to roughly ± 70 %, and for individual
cells it is even wider. Clearly, it cannot be computed if there is only
one plot (therefore the NA values), and for small samples its value is
itself imprecise. Typical sample inventories comprise several hundred to
several thousand inventory points. The package also ships a
hundred-point example, data_ex4_sample_fe_inventory, and
the same figure there is about ± 14 %.
Nothing about the method changes between the two — only the sample size. It is worth keeping in view whenever an inventory is broken down into ever finer classes: every subdivision spends precision, and the confidence interval is where you can watch it being spent.
output_base_table() turns the four into the one table
that gets printed:
bt |>
output_base_table() |>
head(12)
#> # A tibble: 12 × 12
#> species_group variable `(0,20]` `(20,40]` `(40,60]` `(60,80]` `(80,100]`
#> <bav_st_shrt> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 spruce v_hub_m3 NA 151. 150. 63.5 1827.
#> 2 spruce g_m2 NA 94.3 14.8 6.92 149.
#> 3 spruce n NA 86594. 99.6 88.8 1496.
#> 4 spruce d_q_cm NA 3.72 43.5 31.5 35.6
#> 5 spruce h_q_m NA 9.60 27 23.5 33.1
#> 6 spruce n_plot NA 1 1 1 3
#> 7 spruce conf_95_per NA NA NA NA 165.
#> 8 spruce area_ha NA 6.05 0.509 0.257 5.77
#> 9 spruce v_hub_m3_ha NA 25.0 294. 247. 316.
#> 10 spruce g_m2_ha NA 15.6 29.0 27.0 25.7
#> 11 spruce n_ha NA 14316. 195. 346. 259.
#> 12 spruce tot_area_cla… NA 6.05 3.97 11.5 14.9
#> # ℹ 5 more variables: `(100,120]` <dbl>, `(120,140]` <dbl>, `(140,160]` <dbl>,
#> # `(160,999]` <dbl>, total <dbl>For the diameter variant, the class limits are prepared once by
back_table_dclass() and passed in:
d_classes <- back_table_dclass(trees)
base_table_d_q_class_main_stand(trees, dclass_back = d_classes) |>
output_base_table() |>
head(8)
#> # A tibble: 8 × 10
#> species_group variable `(0,10]` `(10,20]` `(20,30]` `(30,40]` `(40,50]`
#> <bav_st_shrt> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 spruce v_hub_m3 151. NA 1770. 2745. 150.
#> 2 spruce g_m2 94.3 NA 143. 201. 14.8
#> 3 spruce n 86594. NA 1434. 1934. 99.6
#> 4 spruce d_q_cm 3.72 NA 35.6 36.4 43.5
#> 5 spruce h_q_m 9.60 NA 33.4 35.8 27
#> 6 spruce n_plot 1 NA 2 3 1
#> 7 spruce conf_95_per NA NA 232. 373. NA
#> 8 spruce area_ha 6.05 NA 5.51 3.22 0.509
#> # ℹ 3 more variables: `(50,60]` <dbl>, `(60,999]` <dbl>, total <dbl>The final step writes the formatted report. It needs the PDF toolchain of section 1.2, and it is the only step that does:
bt |>
output_base_table() |>
output_base_table_pdf(
tab_title = "Example forest enterprise",
output_dir = tempdir(),
inventory = data_ex3_sample_fe_inventory
)The table describes itself: which cohort and which class variable it
holds is recorded when the table is built and travels with it, so the
renderer names the file accordingly and states the cohort on the page.
Passing inventory adds the survey year, the total area and
the number of inventory points to the header.
A structure table adds one further level to the base table: within each species group and class, the trees are additionally broken down by the diameter of the individual tree. Where the base table states that a species group holds so much volume in a given age class, the structure table shows how that volume is distributed over the diameter range.
The functions mirror the base tables exactly, in both choices, so everything said above applies unchanged:
st <- structure_table_age_class_main_stand(trees)
st |>
output_structure_table() |>
head(10)
#> # A tibble: 10 × 17
#> # Groups: species_group, age_class [1]
#> species_group age_class variable `(0,5]` `(5,10]` `(10,15]` `(15,20]`
#> <bav_st_shrt> <ord> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 spruce (20,40] v_hub_m3 0 41.5 110. NA
#> 2 spruce (20,40] g_m2 42.8 22.5 29.0 NA
#> 3 spruce (20,40] n 78214. 5587. 2793. NA
#> 4 spruce (20,40] d_q_cm 2.64 7.16 11.5 NA
#> 5 spruce (20,40] h_q_m 7.88 11.1 11 NA
#> 6 spruce (20,40] n_plot 1 1 1 NA
#> 7 spruce (20,40] conf_95_per NA NA NA NA
#> 8 spruce (20,40] area_ha 5.27 0.463 0.311 NA
#> 9 spruce (20,40] v_hub_m3_ha 0 89.5 353. NA
#> 10 spruce (20,40] g_m2_ha 8.11 48.5 93.4 NA
#> # ℹ 10 more variables: `(20,25]` <dbl>, `(25,30]` <dbl>, `(30,35]` <dbl>,
#> # `(35,40]` <dbl>, `(40,45]` <dbl>, `(45,50]` <dbl>, `(50,55]` <dbl>,
#> # `(55,60]` <dbl>, `(60,999]` <dbl>, total <dbl>and, as before, output_structure_table_pdf() renders
it.
One restriction is worth stating clearly, because it is a matter of meaning rather than of technique. Structure tables are built for inventories that carry individual tree sizes. Currently, only the sample inventory formats we support provide that information. Stand-wise inventories can in principle provide it as well, but the formats we support convey mean tree sizes only. Structure tables will also run on such data, but the result should not be used. The diameter breakdown would then be an artefact of a single mean value, and would suggest a structure that was never measured. FeNEU warns when it suspects this case.
Volume increment cannot be read off an inventory the way standing volume can. FeNEU estimates it in three ways, and which of them is available depends on the data at hand:
| Data required | Reports | |
|---|---|---|
| Repeated survey | two surveys of the same plots | any tree collective, with a confidence interval |
| BWI 3 growth functions | one survey, with tree ages | any tree collective |
| Yield tables | one survey, with tree ages | main stand only |
The repeated survey is the most informative way: it rests on measurements taken twice, so the increment is observed rather than modelled, and it is the only one of the three that yields a confidence interval. It is currently supported for sample inventories only. The other two use models, and both need the age of the trees/stands. Where an inventory carries no or incomplete ages — which happens, and is legitimate, since age is an optional attribute — these two are not available, and entering a pragmatic figure might be a workaround.
The three are treated in detail in the vignette Increment Estimation. This section shows what each of them looks like from the outside.
The package bundles the two ex3 surveys — the one used above and an earlier one — together with their prepared tree tables, so the example starts where section 4 left off:
inc <- inv_increment_repeated_survey(
inv_1st = data_ex3_previous_sample_fe_inventory,
inv_2nd = data_ex3_sample_fe_inventory,
inv_1st_trees = data_ex3_previous_sample_trees_essentials,
inv_2nd_trees = data_ex3_sample_trees_essentials,
method = "rep_classic",
fill_option = "standard",
progress_bar = FALSE
)
inc
#> <fe_increment_repsurv>
#> Surveys : 2009 -> 2020 (period 11 yr)
#> Area / plots : 46.7 ha / 10
#> Method : rep_classic / fill: standard
#> Trees : 139
#> Increment : 9.57 m3/ha/yr (summaric balance)
#> Steps kept : no
#> Use increment_base_table() / output_increment_overall() on this object.One call covers the whole chain: the plots of the two surveys are matched, the trees within them are matched, an increment is derived for every tree, and the trees that could not be matched are either ingrown or removed/mortal and are dealt with accordingly. Each step is also available as a function of its own; the increment vignette lists them. For most purposes this single call is sufficient.
Two arguments deserve attention. method selects how a
tree’s increment is derived from two measurements — there are four
variants, and they differ in how they treat trees that changed sample
circle between the surveys. fill_option governs what
happens to trees that could not be matched at all. Both are subjects of
the Increment Estimation
vignette.
The result carries everything the later steps need:
trees holds the per-tree increments,
overview the aggregation by plot and enterprise,
summaric the increment balance of the enterprise,
meta the figures the reports print in their header, and
steps the intermediate results of the chain, for anyone who
wants to look inside.
From here there are two directions. Broken down by species group and class, in the layout of the status-quo tables:
increment_base_table_main_stand(inc, by_class = "age") |>
output_increment_base_table() |>
head(10)
#> # A tibble: 10 × 12
#> species_group variable `(0,20]` `(20,40]` `(40,60]` `(60,80]` `(80,100]`
#> <bav_st_shrt> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 spruce n_plot_class NA 1 2 1 3
#> 2 spruce n_iv_total NA 7 14 1 21
#> 3 spruce n_iv_filled NA 7 14 0 10
#> 4 spruce iv_m3_yr_tot… NA 12.0 34.4 5.77 64.7
#> 5 spruce ci95_iv_m3_y… NA 25.7 69.3 13.2 92.0
#> 6 spruce iv_m3_ha_yr NA 0.256 0.737 0.124 1.39
#> 7 spruce ci95_iv_m3_h… NA 0.549 1.48 0.282 1.97
#> 8 spruce area_ha NA 6.05 3.48 0.198 5.78
#> 9 spruce iv_m3_ha_yr_… NA 1.98 9.89 29.2 11.2
#> 10 pine n_plot_class NA NA NA 2 3
#> # ℹ 5 more variables: `(100,120]` <dbl>, `(120,140]` <dbl>, `(140,160]` <dbl>,
#> # `(160,999]` <dbl>, total <dbl>Or as the enterprise-level summary, which is the figure to quote when the question is what the whole forest grows:
ovr <- output_increment_overall(inc)
ovr$table_combined |>
select(species_group, is_total_row, iv_m3_ha_yr_st, iv_m3_ha_yr_sum)
#> # A tibble: 8 × 4
#> species_group is_total_row iv_m3_ha_yr_st iv_m3_ha_yr_sum
#> <bav_st_shrt> <lgl> <dbl> <dbl>
#> 1 spruce FALSE 3.94 4.15
#> 2 pine FALSE 4.81 4.80
#> 3 larch FALSE 0.139 0.130
#> 4 Douglas fir FALSE 0.168 0.168
#> 5 beech FALSE 0.115 0.115
#> 6 oak FALSE 0.0476 0.0476
#> 7 other hardwood FALSE 0.503 0.532
#> 8 NA TRUE 9.35 9.57The last row is the enterprise as a whole. The two columns are two
ways of arriving at it, and they differ slightly, here 9.35 against 9.57
m³ per hectare and year: _st adds up the increments of the
individual trees, _sum is the balance of the two surveys
over the whole measured population — standing volume of the second
survey, minus that of the first, plus what was removed in between. For
the enterprise level, that balance is the reference figure; the per-tree
route is what allows the increment to be broken down into species and
classes at all.
Besides table_combined, which covers all inventory
points, the result holds table_matched for the points
measured in both surveys and table_2nd_only for those the
later survey added, plus a meta element for the report
header. The confidence interval is reported in
table_matched — for this example 9.80 ± 2.29 m³ per hectare
and year. It is derived from the scatter between the inventory points,
which requires an inventory point measured twice; the points the later
survey added contribute an estimate instead, so the combined table
carries no confidence interval.
Both have their *_pdf() counterpart,
output_increment_base_table_pdf() and
output_increment_overall_pdf(), and both follow the
three-step pattern of section 4.
Where only one survey exists, the increment has to be estimated from the current state of the trees. FeNEU offers two independent approaches.
The growth functions of the third German National Forest
Inventory (BWI 3) (Riedel et al.
2017) estimate the increment of each tree from its own dimensions
and age. They yield the same per-tree quantity the repeated survey
produces, which is why the same aggregation functions apply from there
on. inv_increment_gnfi3() covers the chain and returns the
same kind of object as inv_increment_repeated_survey():
inc_bwi3 <- inv_increment_gnfi3(
inv = data_ex3_sample_fe_inventory,
inv_trees = data_ex3_sample_trees_essentials,
dt = 5
)
inc_bwi3
#> <fe_increment_gnfi3>
#> Survey : 2020
#> Area / plots : 46.7 ha / 10
#> Method : BWI 3 growth functions, single inventory
#> Trees : 108
#> Increment : 7.33 m3/ha/yr
#> Steps kept : no
#> Use increment_base_table() on this object.dt is the span the estimate covers, in years. A positive
value looks forward, a negative one back; five years is a deliberate
default rather than a number worth changing lightly, since the further
such an estimate reaches, the more it is affected by removals, mortality
and ingrowth it cannot know about.
From here the routes of section 5.1 apply unchanged — the class tables, and the species-group overview:
increment_base_table(inc_bwi3, by_class = "age") |>
output_increment_overview_gnfi3()
#> # A tibble: 9 × 4
#> species_group is_total_row zv_m3_yr zv_m3_ha_yr
#> <bav_st_shrt> <lgl> <dbl> <dbl>
#> 1 spruce FALSE 163. 3.49
#> 2 pine FALSE 118. 2.53
#> 3 larch FALSE 7.35 0.157
#> 4 Douglas fir FALSE 5.91 0.126
#> 5 beech FALSE 3.88 0.0829
#> 6 oak FALSE 6.92 0.148
#> 7 other hardwood FALSE 34.0 0.728
#> 8 noble hardwood FALSE 3.46 0.0741
#> 9 NA TRUE 343. 7.33This is an estimate for the forest evaluated with the repeated survey above, arrived at independently and from one survey alone: 7.3 against 9.4 m³ per hectare and year. Holding the two against each other is one of the reasons to have both.
Since the estimate ages every tree, it needs the age of every tree.
Where a stand layer carries no ages — which happens, and is legitimate —
the function stops rather than evaluating the part of the population
that happens to have one. If ages exist for the main stand only,
restricting the tree list to it beforehand
(dplyr::filter(trees, layer_key == 1)) yields a main-stand
evaluation of the whole enterprise; the reference area stays the full
inventory area.
Yield tables take the other route. They work at
stand level rather than on individual trees, which requires the area to
be split into the shares of virtual monospecific stands. Unlike the
other two, this approach therefore applies to the main stand only.
FeNEU uses the yield table system of ForestElementsR,
described in its own vignette
(vignette("yield_tables", package = "ForestElementsR")).
inc_yt <- inv_increment_ytables(
inv = data_ex3_sample_fe_inventory,
inv_trees = data_ex3_sample_trees_essentials,
ytable_selection = ytables_bavrn_state_var_1_feneu
)
inc_yt
#> <fe_increment_ytables>
#> Survey : 2020
#> Area / plots : 46.7 ha / 10
#> Method : yield tables, single inventory
#> Collective : Hauptbestand
#> Cohorts : 22 (plot x species)
#> Increment : 4.19 m3/ha/yr
#> Use increment_ytables_base_table() on this object.
inc_yt$overview
#> # A tibble: 9 × 7
#> species_group area_ha site_index zv_m3_ha_yr zv_m3_yr is_total_row
#> <bav_st_shrt> <dbl> <dbl> <dbl> <dbl> <lgl>
#> 1 spruce 15.3 0.895 6.58 101. FALSE
#> 2 pine 21.6 0.729 3.12 67.2 FALSE
#> 3 Douglas fir 0.687 1.36 2.70 1.85 FALSE
#> 4 beech 0.812 2.48 3.61 2.93 FALSE
#> 5 oak 0.610 3 4.99 3.04 FALSE
#> 6 other hardwood 4.24 1.30 3.84 16.3 FALSE
#> 7 noble hardwood 0.563 -0.531 6.78 3.81 FALSE
#> 8 NA 2.97 NA NA NA FALSE
#> 9 NA 46.7 NA 4.19 196. TRUE
#> # ℹ 1 more variable: is_no_main_stand_row <lgl>The estimate is made for each cohort: one species in the main stand of one inventory plot, on the virtual area it occupies there. Its age and mean height give the site index, age and site index give the increment the yield table holds, and that figure is corrected by the stocking level — the cohort’s basal area over the basal area the table expects. A cohort has one age and one mean diameter, so grouping a report by age class or by diameter class labels the same estimate rather than changing it.
The overview reads accordingly: area_ha is the virtual
area of each species group, site_index its mean site index,
and zv_m3_ha_yr the increment on that virtual area. The
total row is not an average of those figures — it relates the whole
increment to the entire inventory area, which is the larger reference.
Its site_index is empty because the index scales of
different yield tables cannot be averaged. The row without a species
group carries the area of inventory points that hold no living main
stand, so that the areas add up to the inventory total.
ytables_bavrn_state_var_1_feneu assigns a yield table to
each species code. It is the set FeNEU ships, not a fixed part
of the method — the assignment can be replaced by one of your own, built
from the yield tables ForestElementsR provides. The Increment Estimation vignette shows
how, and what the choice implies.
Beside the chains of the previous chapters, FeNEU holds a few functions that are useful in their own right.
Section 4 used pull_trees() to
get at the trees of an fe_inventory. Three further
functions pull the other parts of it into a flat table:
pull_centers() the plot centre coordinates,
pull_circle_definitions() the circle geometry of each plot,
and pull_sums() the stand sums and mean values per plot and
layer.
pull_centers(data_ex3_sample_fe_inventory)
#> Simple feature collection with 10 features and 1 field
#> Geometry type: POINT
#> Dimension: XY
#> Bounding box: xmin: 729780 ymin: 5427422 xmax: 752949 ymax: 5457197
#> Projected CRS: ETRS89 / UTM zone 32N
#> plot_id geometry
#> 1 900000002_1_99900002 POINT (729780 5427422)
#> 2 900000015_1_99900015 POINT (735935 5438876)
#> 3 900000020_1_99900020 POINT (742855 5438138)
#> 4 900000029_1_99900029 POINT (744306 5437209)
#> 5 900000043_1_99900043 POINT (752853 5457197)
#> 6 900000046_1_99900046 POINT (748622 5431646)
#> 7 900000061_1_99900061 POINT (752949 5445687)
#> 8 900000075_1_99900075 POINT (745965 5453085)
#> 9 900000086_1_99900086 POINT (742935 5443759)
#> 10 900000100_1_99900100 POINT (749610 5435061)inventory_meta() returns the survey year, the inventory
area and the number of plots. These are the three figures the PDF
reports print below their title, and they are read off the inventory
object itself, so they also cover plots that carry no trees:
inventory_meta(data_ex3_sample_fe_inventory)
#> $year
#> [1] 2020
#>
#> $area_ha
#> [1] 46.73489
#>
#> $n_plots
#> [1] 10get_inv_year() returns the survey year alone. For a pair
of surveys, inv_period() adds the period between them — the
reference period of a repeated-survey increment:
plot_info_sheet_pdf() renders one inventory plot as a
PDF sheet. It takes a single plot rather than the whole inventory, so it
is applied to an element of the plot column:
Like every *_pdf() function it needs the toolchain of section 1.2. dbh_scale oversizes the
tree symbols, which keeps them visible on a plot drawn to scale.
The concentric importers of section 3 require a
circle definition. Where none is at hand,
generate_circle_definition() writes one:
generate_circle_definition(
output_dir = tempdir(),
dbh_cm_from = c(0, 12, 30),
radiuses_m = c(2.82, 5.64, 12.62)
)It produces a uniform definition — the same circles for
every plot. There is deliberately no default geometry: the circle sizes
determine the representation factors and therefore every per-hectare
figure that follows. An inventory whose plots fall into several circle
classes needs a file written by hand, with one fe_ikl per
class.
Inventory plots represent different areas, so a mean over them is a
ratio estimator rather than an arithmetic mean.
se_area_weighted() computes it together with its standard
error and confidence interval, from the per-hectare values of the plots
and the areas they represent. It is what the confidence intervals of the
increment reports rest on, and it is exported so that the same figure
can be obtained for an aggregation of your own.
se_area_weighted_grouped() does the same within groups.
FeNEU attaches ForestElementsR, so its functions
are available directly after library(FeNEU). One of them is
worth a section of its own here, because it answers a question that
comes up on every survey: which code stands for which species.
fe_species_get_field_table() returns a coding as a compact
key, one row per code, with the scientific, English and German name
beside it:
fe_species_get_field_table("bavrn_state") |> head(10)
#> # A tibble: 10 × 6
#> species_id name_sci name_eng name_ger level is_tree
#> <chr> <chr> <chr> <chr> <int> <lgl>
#> 1 10 Picea abies Norway spruce Fichte 0 TRUE
#> 2 11 Picea omorika Serbian spruce Omorika… 0 TRUE
#> 3 12 Picea sitchensis Sitka spruce Sitka-F… 0 TRUE
#> 4 20 Pinus sylvestris Scots pine Kiefer 0 TRUE
#> 5 21 Pinus strobus eastern white p… Strobe 0 TRUE
#> 6 22 Pinus nigra black pine Schwarz… 0 TRUE
#> 7 23 Pinus mugo subsp. uncinata Swiss mountain … Spirke 0 TRUE
#> 8 24 Pinus cembra Arolla pine Zirbe 0 TRUE
#> 9 25 Pinus mugo subsp. mugo dwarf maountain… Latsche 0 TRUE
#> 10 30 Abies alba silver fir Tanne 0 TRUEThis is the view to print for field work, and it is not the same as
the full coding table: that one carries one row per elementary species,
so a group code appears once per member species. All three name columns
are returned whatever options("fe_spec_lang") is set to.
The rows come in the coding’s own order, the finest codes first and the
group codes after; level says which is which (0 = finest)
and is_tree marks the categories that are not trees.
The function works for every coding ForestElementsR implements, so the key can be produced for whichever coding an inventory is recorded in.
The species codings and the yield tables themselves belong to
ForestElementsR; see
vignette("tree_species_codings", package = "ForestElementsR")
and
vignette("yield_tables", package = "ForestElementsR").
FeNEU adds one dataset of its own,
ytables_bavrn_state_var_1_feneu: the yield-table assignment
of section 5.2, which covers all current
bavrn_state species codes.
?data_examples_overview lists the example datasets used
throughout this vignette, at all three levels of processing.
The authors would like to thank the Bavarian Ministry for Nutrition, Agriculture, Forestry, and Tourism for funding the projects FeNEU: Ein innovatives Instrument für die forstliche Planung in Bayern (E062) and Ertragskundliche Betreuung der langfristigen Versuche (W007). We also wish to thank our colleagues at the Bayerische Staatsforsten (BaySF) for their friendly cooperation. The development of FeNEU has benefited especially from exchange with experienced practitioners. We sincerely thank Sabine Andersen, Stefan Esser, Markus Fritsch, Christian Winter, Hans Stark, and Daniel Kraus, representing a much larger group. Among them is Rainer Dürr, author of the Silvarith software, who sadly died before the release of FeNEU.