Nothing
# finds all horizons that are possibly part of a cambic horizon
# excluding those that are part of an argillic horizon
# (defined either by depth interval or getArgillicBounds)
#' Find all intervals that are potentially part of a Cambic horizon
#' @description Find all intervals that are potentially part of a Cambic horizon excluding those that are part of an argillic horizon (defined either by depth interval or \code{getArgillicBounds()}).
#'
#' There may be multiple cambic horizons (indexes) in a profile. Each cambic index has a top and bottom depth associated: cambic_top and cambic_bottom. This result is designed to be used for single profiles, or with \code{profileApply(..., frameify = TRUE)}
#'
#' @param p A single-profile SoilProfileCollection
#' @param hzdesgn Column name containing horizon designations.
#' @param texcl.attr Arguments to \code{getArgillicBounds()}
#' @param clay.attr Arguments to \code{getArgillicBounds()}
#' @param ... Arguments to \code{getArgillicBounds()}
#' @param argi_bounds Optional: numeric vector of length 2 with top and bottom of argillic; (Default: NULL)
#' @param d_value Column name containing dry value. Default: d_value
#' @param m_value Column name containing moist value. Default: m_value
#' @param m_chroma Column name containing moist chroma Default: m_chroma
#' @param sandy.texture.pattern this is a pattern for matching sandy textural classes: `-S$|^S$|COS$|L[^V]FS$|[^L]VFS$|LS$|LFS$`
#'
#' @return A \code{data.frame} containing profile, cambic indexes, along with top and bottom depths.
#'
#' @author Andrew G. Brown
#'
#' @export
#'
#' @examples
#' # construct a fake profile
#' spc <- data.frame(id=1, taxsubgrp = "Lithic Haploxerepts",
#' hzname = c("A","AB","Bw","BC","R"),
#' hzdept = c(0, 20, 32, 42, 49),
#' hzdepb = c(20, 32, 42, 49, 200),
#' clay = c(19, 22, 22, 21, NA),
#' texcl = c("l","l","l", "l","br"),
#' d_value = c(5, 5, 5, 6, NA),
#' m_value = c(2.5, 3, 3, 4, NA),
#' m_chroma = c(2, 3, 4, 4, NA))
#'
#' # promote to SoilProfileCollection
#' depths(spc) <- id ~ hzdept + hzdepb
#'
#' # set required metadata
#' hzdesgnname(spc) <- 'hzname'
#' hztexclname(spc) <- 'texcl'
#' hzmetaname(spc, 'clay') <- 'clay'
#'
#' # print results in table
#' getCambicBounds(spc)
#'
getCambicBounds <- function(p,
hzdesgn = hzdesgnname(p, required = TRUE),
texcl.attr = hztexclname(p, required = TRUE),
clay.attr = hzmetaname(p, "clay", required = TRUE),
argi_bounds = NULL,
d_value = "d_value",
m_value = "m_value",
m_chroma = "m_chroma",
sandy.texture.pattern = "-S$|^S$|COS$|L[^V]FS$|[^L]VFS$|LS$|LFS$",
...) {
# sacrifice to CRAN gods in the name of NSE
id <- NULL
if (is.null(hzdesgn) || !hzdesgn %in% horizonNames(p)) {
stop("Horizon designation column (", hzdesgn, ") does not exist.")
}
# construct data.frame result for no-cambic-found (NA)
empty_frame <- data.frame(id = character(0),
cambic_id = numeric(0),
cambic_top = numeric(0),
cambic_bottom = numeric(0))
empty_frame_names <- names(empty_frame)
empty_frame_names[1] <- idname(p)
names(empty_frame) <- empty_frame_names
hzd <- horizonDepths(p)
if (is.null(argi_bounds)) {
argi_bounds <- getArgillicBounds(p, hzdesgn = hzdesgn,
clay.attr = clay.attr, texcl.attr = texcl.attr,
...,
simplify = FALSE)
} else if (is.vector(argi_bounds)) {
if (length(p) > 1) {
stop("`argi_bounds` should be a data.frame if `p` has more than one profile", call. = FALSE)
}
argi_bounds <- data.frame(id = profile_id(p), ubound = argi_bounds[1], lbound = argi_bounds[2])
colnames(argi_bounds)[1] <- idname(p)
}
cambictop <- minDepthOf(p, pattern = "B", hzdesgn = hzdesgn, no.contact.assigned = NA, simplify = FALSE)
cambicbottom <- maxDepthOf(p, pattern = "B", hzdesgn = hzdesgn, top = FALSE, no.contact.assigned = NA, simplify = FALSE)
cambic <- glom(p, cambictop[[hzd[1]]], cambicbottom[[hzd[2]]], truncate = TRUE, fill = TRUE)
if (any(!is.na(argi_bounds$ubound), !is.na(argi_bounds$lbound))) {
# if an argillic is present, remove with glom truncate+invert
cab <- argi_bounds[complete.cases(argi_bounds),]
non.argillic <- glom(cambic,
argi_bounds$ubound, argi_bounds$lbound,
truncate = TRUE, invert = TRUE, fill = TRUE)
} else {
non.argillic <- cambic
}
dark.colors <- hasDarkColors(non.argillic)
non.argillic$w <- rep(1, nrow(non.argillic))
textures <- non.argillic[[texcl.attr]]
sandy.textures <- grepl(sandy.texture.pattern, textures, ignore.case = TRUE)
nhz <- horizons(non.argillic)
# remove horizons that are sandy or have dark colors
if (any(sandy.textures | dark.colors, na.rm = TRUE)) {
nhz <- nhz[-which(sandy.textures | dark.colors),]
}
final <- data.frame(id = NA_character_, cambic_top = NA_real_, cambic_bottom = NA_real_)[0,]
# iterate through combinations of horizons, check topology and thickness
# finds multiple occurrences of cambic horizons, excluding argillics
nhzs <- split(nhz, nhz[[idname(p)]])
for (k in seq_along(nhzs)){
for (j in 1:nrow(nhzs[[k]])) {
for (i in j:nrow(nhzs[[k]])) {
ftop <- nhzs[[k]][j:i, hzd[1]]
fbot <- nhzs[[k]][j:i, hzd[2]]
if (any(suppressWarnings(hzDepthTests(ftop, fbot)))) {
i <- i - 1
break;
}
}
if (is.numeric(fbot) & is.numeric(ftop)) {
pcamb.thickness <- fbot - ftop
if (length(pcamb.thickness) > 0 & sum(pcamb.thickness, na.rm = TRUE) >= 15) {
final <- rbind(final, data.frame(id = as.character(nhzs[[k]][[idname(p)]][1]),
cambic_top = min(ftop, na.rm = TRUE),
cambic_bottom = max(fbot, na.rm = TRUE)))
if (i == nrow(nhzs[[k]])) {
k = k + 1
break
}
}
}
if (k > length(nhzs)) {
break
}
}
}
.N <- NULL
iddf <- data.table::data.table(id = as.character(final[["id"]]))[, list(cambic_index = 1:.N), by = "id"]
nadf <- data.frame(id = profile_id(p)[!profile_id(p) %in% final[["id"]]])
if (nrow(nadf) > 0) {
nadf$cambic_index <- rep(NA_real_, nrow(nadf))
nadf$cambic_top <- rep(NA_real_, nrow(nadf))
nadf$cambic_bottom <- rep(NA_real_, nrow(nadf))
}
iddf <- iddf[,-1]
res <- cbind(final, iddf)
# note: NSE used to specifiy ordering condition for data.table object
# much faster than ordering on the data.frame representation used next
res <- data.table::rbindlist(list(res, nadf), fill = TRUE)[order(id)]
colnames(res)[1] <- idname(p)
.as.data.frame.aqp(res, aqp_df_class(p))
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.