diff --git a/.Rbuildignore b/.Rbuildignore index 53c2d38..78721bf 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -6,3 +6,5 @@ ^\.git$ ^\.gitignore$ ^.*\.orig$ +^cran-comments\.md$ +^data-raw$ diff --git a/DESCRIPTION b/DESCRIPTION index 324d8f5..eca3adc 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -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")) @@ -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 diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..5aa1009 --- /dev/null +++ b/NEWS.md @@ -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/`. diff --git a/R/kublin_no.R b/R/kublin_no.R index 9f07e29..f730229 100644 --- a/R/kublin_no.R +++ b/R/kublin_no.R @@ -10,16 +10,25 @@ #' @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,...) { @@ -27,11 +36,12 @@ 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){ diff --git a/R/plot_taper.R b/R/plot_taper.R index 935d708..7cf7f0d 100644 --- a/R/plot_taper.R +++ b/R/plot_taper.R @@ -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 diff --git a/R/sysdata.rda b/R/sysdata.rda index d4648c6..398232d 100644 Binary files a/R/sysdata.rda and b/R/sysdata.rda differ diff --git a/cran-comments.md b/cran-comments.md new file mode 100644 index 0000000..0571df3 --- /dev/null +++ b/cran-comments.md @@ -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. diff --git a/data-raw/sysdata.R b/data-raw/sysdata.R new file mode 100644 index 0000000..11f9b5a --- /dev/null +++ b/data-raw/sysdata.R @@ -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" +) diff --git a/inst/CITATION b/inst/CITATION index 5b08d64..5982a05 100644 --- a/inst/CITATION +++ b/inst/CITATION @@ -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"), diff --git a/inst/WORDLIST b/inst/WORDLIST new file mode 100644 index 0000000..953708e --- /dev/null +++ b/inst/WORDLIST @@ -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 diff --git a/man/kublin_nor.Rd b/man/kublin_nor.Rd index b15cacd..5009816 100644 --- a/man/kublin_nor.Rd +++ b/man/kublin_nor.Rd @@ -15,11 +15,11 @@ kublin_nor(Hx = NULL, Hm, Dm, mHt, sp = 1, Dx = NULL, ...) \item{mHt}{measured tree height (m) above ground} -\item{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}.} +\item{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}.} \item{Dx}{or diameters (cm) for which to return a height} @@ -38,3 +38,13 @@ and code in Kublin & Breidenbach (https://CRAN.R-project.org/package=TapeR). \details{ Allows the use of multiple diameters measured at various points along the stem to estimate taper. } +\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 +} diff --git a/man/plot_taper.Rd b/man/plot_taper.Rd index 1d71e62..152abb4 100644 --- a/man/plot_taper.Rd +++ b/man/plot_taper.Rd @@ -15,6 +15,10 @@ plot_taper(dbh, h_top, sp = "spruce", with_bark = TRUE) \item{with_bark}{plot taper curve with (TRUE, default) or without bark (FALSE).} } +\value{ +No return value. Called for its side effect of drawing a taper-curve +plot on the current graphics device. +} \description{ Plots the taper curve } diff --git a/tests/spelling.R b/tests/spelling.R new file mode 100644 index 0000000..d60e024 --- /dev/null +++ b/tests/spelling.R @@ -0,0 +1,7 @@ +if (requireNamespace("spelling", quietly = TRUE)) { + spelling::spell_check_test( + vignettes = TRUE, + error = FALSE, + skip_on_cran = TRUE + ) +} diff --git a/tests/testthat/test-kublin_nor.R b/tests/testthat/test-kublin_nor.R index d22deb8..30908e9 100644 --- a/tests/testthat/test-kublin_nor.R +++ b/tests/testthat/test-kublin_nor.R @@ -56,16 +56,34 @@ 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" ) } }) @@ -73,6 +91,6 @@ test_that("kublin_nor errors for pine and birch (only spruce is fitted)", { 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" ) })