Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
^\.git$
^\.gitignore$
^.*\.orig$
^cran-comments\.md$
^data-raw$
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: taperNOR
Type: Package
Title: Taper, Volume and Bark Thickness Models for Spruce, Pine and Birch in Norway
Version: 0.1.0
Version: 0.2.0
Authors@R: c(
person("Johannes", "Rahlf", email = "johannes.rahlf@nibio.no", role = c("aut", "cre")),
person(c("Endre", "Hofstad"), "Hansen", role = "aut"))
Expand All @@ -17,11 +17,13 @@ License: MIT + file LICENSE
URL: https://github.com/SmartForest-no/taperNOR
BugReports: https://github.com/SmartForest-no/taperNOR/issues
Encoding: UTF-8
Language: en-US
Imports:
TapeR
RoxygenNote: 7.3.2
Depends:
R (>= 2.10)
Suggests:
spelling,
testthat (>= 3.0.0)
Config/testthat/edition: 3
22 changes: 22 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# taperNOR 0.2.0

* `kublin_nor()` now supports Scots pine in addition to Norway spruce, using a
pine-specific Kublin (TapeR) mixed-effects model. Birch is still not fitted
and continues to raise an informative error.

# taperNOR 0.1.0

* `taperNOR()`, `barkNOR()`, and `volume()` implement the taper, bark
thickness, and volume models of Hansen et al. (2023) for Norway spruce,
Scots pine, and birch.
* `hfromd()` and `dlocation()` invert the taper curve to estimate tree height
and diameter at breast height from measured diameters, and to locate the
height at which a given diameter occurs.
* `kublin_nor()` provides the Kublin (2013) mixed-effects taper model via
`TapeR`. The model is currently fitted for spruce only; pine and birch now
raise an informative error instead of silently using the spruce model.
* `kublin_nor()` accepts a vector of diameters in `Dx`, returning one height
per element.
* `volume()` accepts integer inputs and allows per-tree `sp` and `with_bark`,
and the `h_vol_lower`/`h_vol_upper` clamping no longer affects valid trees.
* Added a Python port of the calculator functions under `python/`.
28 changes: 19 additions & 9 deletions R/kublin_no.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,38 @@
#' @param Hm height above ground of measured diameters (m)
#' @param Dm measured diameters (cm)
#' @param mHt measured tree height (m) above ground
#' @param sp species. \strong{Only spruce is currently supported}
#' ("spruce", "s", "gran", "g", "1"); the Kublin (TapeR) mixed-effects model
#' has only been fitted for spruce in this package. Pine and birch raise an
#' error - use \code{\link{taperNOR}} for those species, or supply a fitted
#' \code{par.lme}.
#' @param sp species. \strong{Spruce and pine are currently supported}
#' (spruce: "spruce", "s", "gran", "g", "1"; pine: "pine", "p", "furu", "f",
#' "2"). The Kublin (TapeR) mixed-effects model has not yet been fitted for
#' birch in this package, so birch raises an error - use \code{\link{taperNOR}}
#' for birch, or supply a fitted \code{par.lme}.
#' @param ... parameters handed over to E_DHx_HmDm_HT.f or E_HDx_HmDm_HT.f
#' @return When Hx is given: diameters at Hx (cm). When Dx is given: heights where d=Dx (m).
#' \code{TapeR::E_HDx_HmDm_HT.f()} only root-finds a single height per call, so when
#' \code{Dx} has length > 1 it is called once per element of \code{Dx} and the
#' resulting heights are combined into a vector of the same length as \code{Dx}.
#' @examples
#' # Spruce stem with diameters of 30 cm at 1.3 m and 22 cm at 5 m above
#' # ground, and a total height of 25 m. (Spruce and pine are supported.)
#'
#' # Height(s) (m) at which the stem reaches 25 and 15 cm in diameter:
#' kublin_nor(Dx = c(25, 15), Hm = c(1.3, 5), Dm = c(30, 22), mHt = 25, sp = "spruce")
#'
#' # Diameters (cm) predicted at 1.3, 5 and 10 m above ground (pine):
#' kublin_nor(Hx = c(1.3, 5, 10), Hm = c(1.3, 5), Dm = c(30, 22), mHt = 25, sp = "pine")$DHx
#' @export

kublin_nor <- function(Hx=NULL,Hm,Dm,mHt,sp=1,Dx=NULL,...) {

sp_norm<-tolower(as.character(sp))
if(sp_norm%in%c("spruce","s","gran","g","1")){
par_lme<-kublin_par_lme_spruce
} else if(sp_norm%in%c("pine","p","furu","f","2",
"birch","b","bj\u00f8rk","bjork","bj","lauv","l","3")){
stop("kublin_nor() currently only supports spruce. The Kublin (TapeR) model has not been fitted for pine or birch in this package; use taperNOR() for those species, or supply a fitted par.lme.")
} else if(sp_norm%in%c("pine","p","furu","f","2")){
par_lme<-kublin_par_lme_pine
} else if(sp_norm%in%c("birch","b","bj\u00f8rk","bjork","bj","lauv","l","3")){
stop("kublin_nor() does not yet support birch. The Kublin (TapeR) model has not been fitted for birch in this package; use taperNOR() for birch, or supply a fitted par.lme.")
} else {
stop("sp must indicate spruce, the only species currently supported by kublin_nor().")
stop("sp must indicate spruce or pine, the species currently supported by kublin_nor().")
}
if(is.null(Hx)&!is.null(Dx)){
if(length(Dx)>1){
Expand Down
2 changes: 2 additions & 0 deletions R/plot_taper.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#' @param h_top tree height above ground (m).
#' @param sp species
#' @param with_bark plot taper curve with (TRUE, default) or without bark (FALSE).
#' @return No return value. Called for its side effect of drawing a taper-curve
#' plot on the current graphics device.
#' @examples
#'
#' #one tree
Expand Down
Binary file modified R/sysdata.rda
Binary file not shown.
22 changes: 22 additions & 0 deletions cran-comments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## R CMD check results

0 errors | 0 warnings | 1 note

* This is a new submission.

## Test environments

* local Windows 11, R 4.6.0
* GitHub Actions: ubuntu-latest, macos-latest, windows-latest (R release)

## Notes

* The package depends on 'TapeR' (CRAN) for the `kublin_nor()` function.
* The Kublin (2013) mixed-effects taper model is currently fitted for Norway
spruce only; pine and birch raise an informative error. The Kozak-style
taper, bark, and volume functions (`taperNOR()`, `barkNOR()`, `volume()`,
`hfromd()`, `dlocation()`) support all three species.

## Reverse dependencies

There are currently no reverse dependencies.
32 changes: 32 additions & 0 deletions data-raw/sysdata.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Provenance script for R/sysdata.rda (internal package data).
#
# R/sysdata.rda holds the fitted Kublin (TapeR) mixed-effects taper models
# used by kublin_nor(), one `par.lme` object per species:
# - kublin_par_lme_spruce
# - kublin_par_lme_pine
#
# Each object is the `$par.lme` element returned by TapeR::TapeR_FIT_LME.f().
# This script rebuilds R/sysdata.rda from the per-species fit output. Point the
# paths below at the relevant fit objects (kept outside the package).

# --- pine -------------------------------------------------------------------
# Full TapeR_FIT_LME.f() output for Scots pine (list with $fit.lme and $par.lme).
pine_fit_rds <- "../../pine_model.rds"
kublin_par_lme_pine <- readRDS(pine_fit_rds)$par.lme

# --- spruce -----------------------------------------------------------------
# The spruce par.lme predates this script; reuse the object already shipped in
# R/sysdata.rda. Replace with the original fit output if it is re-fitted.
.e <- new.env()
load("R/sysdata.rda", envir = .e)
kublin_par_lme_spruce <- .e$kublin_par_lme_spruce

# --- write ------------------------------------------------------------------
# version = 2 keeps the file readable by R (>= 2.10) (see DESCRIPTION Depends).
save(
kublin_par_lme_spruce,
kublin_par_lme_pine,
file = "R/sysdata.rda",
version = 2,
compress = "bzip2"
)
18 changes: 16 additions & 2 deletions inst/CITATION
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
citHeader(
"To cite taperNOR in publications, please cite the underlying models:"
## Publication year of the current package version (0.2.0, 2026).
## Update both the year below and this note when a new version is released.
vers <- if (!is.null(meta$Version)) meta$Version else "0.2.0"

bibentry(
bibtype = "Manual",
header = "To cite the taperNOR software package:",
title = "{taperNOR}: Taper, Volume and Bark Thickness Models for Spruce, Pine and Birch in Norway",
author = c(
person("Johannes", "Rahlf"),
person(c("Endre", "Hofstad"), "Hansen")
),
year = "2026",
note = paste("R package version", vers),
url = "https://github.com/SmartForest-no/taperNOR"
)

bibentry(
bibtype = "Article",
header = "To cite the underlying taper, volume and bark thickness models:",
title = "Taper, volume, and bark thickness models for spruce, pine, and birch in Norway",
author = c(
person("Endre", "Hansen"),
Expand Down
41 changes: 41 additions & 0 deletions inst/WORDLIST
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Arbetsrapport
Astrup
Breidenbach
CTL
DHx
Dx
Funktioner
Gobakken
HDx
Hannrup
HmDm
Hx
Kozak
Kozak’s
Kublin
Picea
Rasmus
Skogforsk
Stängle
TapeR
Terje
Uppsala
abies
al
avverkning
barkens
bj
bjork
bjørk
doi
et
furu
för
https
lauv
ln
optim
skattning
skördare
tjocklek
vid
20 changes: 15 additions & 5 deletions man/kublin_nor.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions man/plot_taper.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions tests/spelling.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
if (requireNamespace("spelling", quietly = TRUE)) {
spelling::spell_check_test(
vignettes = TRUE,
error = FALSE,
skip_on_cran = TRUE
)
}
36 changes: 27 additions & 9 deletions tests/testthat/test-kublin_nor.R
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,41 @@ test_that("kublin_nor accepts spruce via its aliases", {
}
})

test_that("kublin_nor errors for pine and birch (only spruce is fitted)", {
# Regression: pine/birch previously fell through to the spruce model
# silently. They must now error rather than return spruce-based results.
# "bjork" (ASCII) covers the birch path; the non-ASCII Norwegian alias is
# exercised by the function but kept out of this test to keep the source ASCII.
for (alias in c("pine", "p", "furu", "f", "2",
"birch", "b", "bjork", "bj", "lauv", "l", "3")) {
test_that("kublin_nor uses the pine model for pine aliases", {
ref <- kublin_nor(Dx = 25, Hm = c(1.3, 5), Dm = c(30, 22), mHt = 25, sp = "pine")

expect_length(ref, 1)
expect_type(ref, "double")

# All pine aliases resolve to the same (pine) model.
for (alias in c("pine", "p", "furu", "f", "2")) {
expect_equal(
kublin_nor(Dx = 25, Hm = c(1.3, 5), Dm = c(30, 22), mHt = 25, sp = alias),
ref
)
}

# Pine and spruce use different fitted models, so they must not coincide.
spruce <- kublin_nor(Dx = 25, Hm = c(1.3, 5), Dm = c(30, 22), mHt = 25, sp = "spruce")
expect_false(isTRUE(all.equal(ref, spruce)))
})

test_that("kublin_nor errors for birch (not yet fitted)", {
# birch has no fitted Kublin model yet, so it must error rather than fall
# back to another species. "bjork" (ASCII) covers the birch path; the
# non-ASCII Norwegian alias is exercised by the function but kept out of
# this test to keep the source ASCII.
for (alias in c("birch", "b", "bjork", "bj", "lauv", "l", "3")) {
expect_error(
kublin_nor(Dx = 25, Hm = c(1.3, 5), Dm = c(30, 22), mHt = 25, sp = alias),
"only supports spruce"
"does not yet support birch"
)
}
})

test_that("kublin_nor errors for an unrecognized species", {
expect_error(
kublin_nor(Dx = 25, Hm = c(1.3, 5), Dm = c(30, 22), mHt = 25, sp = "oak"),
"sp must indicate spruce"
"sp must indicate spruce or pine"
)
})
Loading