diff --git a/NAMESPACE b/NAMESPACE index aba430db..e5b6a055 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -175,6 +175,7 @@ exportMethods("horizons<-") exportMethods("hzID<-") exportMethods("hzdesgnname<-") exportMethods("hzidname<-") +exportMethods("hzmetaname<-") exportMethods("hztexclname<-") exportMethods("initSpatial<-") exportMethods("metadata<-") @@ -207,6 +208,7 @@ exportMethods(hzID) exportMethods(hzMetadata) exportMethods(hzdesgnname) exportMethods(hzidname) +exportMethods(hzmetaname) exportMethods(hztexclname) exportMethods(idname) exportMethods(isEmpty) diff --git a/R/SoilProfileCollection-metadata.R b/R/SoilProfileCollection-metadata.R index 9aa1d201..7d834cb3 100644 --- a/R/SoilProfileCollection-metadata.R +++ b/R/SoilProfileCollection-metadata.R @@ -293,3 +293,74 @@ setReplaceMethod("GHL", allowednames = "horizon" ) }) + +setGeneric("hzmetaname", function(object, attr, required = FALSE) + standardGeneric("hzmetaname")) + +#' @title Get or Set Horizon Metadata Column Name +#' @name hzmetaname +#' @aliases hzmetaname hzmetaname,SoilProfileCollection-method hzmetaname<- hzmetaname,SoilProfileCollection-method +#' @details Store the column name containing a specific type of horizon data in the metadata slot of the SoilProfileCollection. +#' @description `hzmetaname()`: Get column name containing horizon data of interest +#' @param object a SoilProfileCollection +#' @param attr character. Base name for attribute to be stored in metadata. This is prefixed with `"aqp_hz"` for horizon-level metadata for column attributes. e.g. `attr="clay"` results in metadata value retrieved from `"aqp_hzclay"`. +#' @param required logical, is this attribute required? If it is, set to `TRUE` to trigger error on invalid result +#' @docType methods +#' @rdname hzmetaname +#' @export +setMethod("hzmetaname", signature(object = "SoilProfileCollection"), + function(object, attr, required = FALSE) { + .require.metadata.aqp(object, + attr = paste0("aqp_hz", attr), + attrlabel = paste0("Horizon metadata (", attr, ") column"), + message = "\nSee ??hzmetaname", + required = required) + }) + +setGeneric('hzmetaname<-', function(object, attr, required = FALSE, value) + standardGeneric('hzmetaname<-')) + +#' @description `hzmetaname<-`: Set horizon designation column name +#' @param object A _SoilProfileCollection_ +#' @param attr _character_. Base name for attribute to be stored in metadata. This is prefixed with `"aqp_hz"` for horizon-level metadata for column attributes. e.g. `attr="clay"` results in metadata value retrieved from `"aqp_hzclay"`. +#' @param value _character_. Name of horizon-level column containing data corresponding to `attr`. +#' @param required _logical_. Is this attribute required? If it is, set to `TRUE` to trigger error on invalid `value`. +#' @docType methods +#' @seealso [guessHzAttrName()] +#' @rdname hzmetaname +#' @export +#' @examples +#' +#' data(sp1) +#' +#' # promote to SPC +#' depths(sp1) <- id ~ top + bottom +#' +#' # set important metadata columns +#' hzdesgnname(sp1) <- "name" +#' hztexclname(sp1) <- "texture" +#' +#' # set custom horizon property (clay content) column +#' hzmetaname(sp1, "clay") <- "prop" +#' +#' # inspect metadata list +#' metadata(sp1) +#' +#' # get horizon clay content column +#' hzmetaname(sp1, "clay") +#' +#' # uses hzdesgname(), hztexclname(), hzmetaname(attr="clay") in function definition +#' estimatePSCS(sp1) +setReplaceMethod("hzmetaname", + signature(object = "SoilProfileCollection"), + function(object, attr, required = FALSE, value) { + .set.metadata.aqp( + object = object, + value = value, + required = required, + attr = paste0("aqp_hz", attr), + attrlabel = paste0("Horizon metadata (", attr, ") column"), + message = "\nSee ??hzmetaname", + allowednames = "horizon" + ) + }) \ No newline at end of file diff --git a/R/allocate.R b/R/allocate.R index 037b8305..7a8eabcc 100644 --- a/R/allocate.R +++ b/R/allocate.R @@ -697,6 +697,11 @@ allocate <- function(..., to = c("FAO Salt Severity", "FAO Black Soil", "ST Diag #' #' depths(h) <- id ~ top + bottom #' +#' # set required metadata for estimatePSCS() +#' hzdesgnname(h) <- "hzname" +#' hztexclname(h) <- "texcl" +#' hzmetaname(h, "clay") <- "clay" +#' #' pscs <- data.frame(id = h$id, rbind(estimatePSCS(h))) #' names(pscs)[2:3] <- c("top", "bottom") #' diff --git a/R/depthOf.R b/R/depthOf.R index 74b689d5..76a6f6e9 100644 --- a/R/depthOf.R +++ b/R/depthOf.R @@ -60,7 +60,7 @@ depthOf <- function(p, pattern, FUN = NULL, top = TRUE, - hzdesgn = guessHzDesgnName(p), + hzdesgn = hzdesgnname(p, required = TRUE), no.contact.depth = NULL, no.contact.assigned = NA_real_, na.rm = TRUE, @@ -89,14 +89,10 @@ depthOf <- function(p, id <- idname(p) hid <- hzidname(p) - hznames <- horizonNames(p) # if the user has not specified a column containing horizon designations - if (!hzdesgn %in% hznames) { - hzdesgn <- guessHzDesgnName(p, required = TRUE) - if (!hzdesgn %in% hznames) { - stop("depth estimation relies on a column containing horizon designations") - } + if (is.null(hzdesgn) || !hzdesgn %in% horizonNames(p)) { + stop("Horizon designation column (", hzdesgn, ") does not exist.") } # get horizons matching designation pattern @@ -167,7 +163,7 @@ depthOf <- function(p, pattern, FUN, top = TRUE, - hzdesgn = guessHzDesgnName(p), + hzdesgn = hzdesgnname(p, required = TRUE), no.contact.depth = NULL, no.contact.assigned = NA, na.rm = TRUE, @@ -216,7 +212,7 @@ depthOf <- function(p, maxDepthOf <- function(p, pattern, top = TRUE, - hzdesgn = guessHzDesgnName(p), + hzdesgn = hzdesgnname(p, required = TRUE), no.contact.depth = NULL, no.contact.assigned = NA, na.rm = TRUE, @@ -241,7 +237,7 @@ maxDepthOf <- function(p, minDepthOf <- function(p, pattern, top = TRUE, - hzdesgn = guessHzDesgnName(p), + hzdesgn = hzdesgnname(p, required = TRUE), no.contact.depth = NULL, no.contact.assigned = NA, na.rm = TRUE, diff --git a/R/estimatePSCS.R b/R/estimatePSCS.R index f29f6c69..fa95f0d0 100644 --- a/R/estimatePSCS.R +++ b/R/estimatePSCS.R @@ -48,15 +48,23 @@ #' depths(sp1) <- id ~ top + bottom #' site(sp1) <- ~ group #' -#' p <- sp1 -#' attr <- 'prop' # clay contents -#' foo <- estimatePSCS(p, hzdesgn='name', clay.attr = attr, texcl.attr="texture") -#' foo -#' -#' -estimatePSCS = function(p, hzdesgn = "hzname", clay.attr = "clay", - texcl.attr = "texcl", tax_order_field = "tax_order", - bottom.pattern='Cr|R|Cd', simplify = TRUE, ...) { +#' # set required metadata +#' hzdesgnname(sp1) <- 'name' +#' hztexclname(sp1) <- 'texture' +#' hzmetaname(sp1, 'clay') <- 'prop' +#' +#' x <- estimatePSCS(sp1) +#' x +estimatePSCS <- function( + p, + hzdesgn = hzdesgnname(p, required = TRUE), + clay.attr = hzmetaname(p, "clay", required = TRUE), + texcl.attr = hztexclname(p, required = TRUE), + tax_order_field = "tax_order", + bottom.pattern = 'Cr|R|Cd', + simplify = TRUE, + ... +) { .LAST <- NULL hz.depths <- horizonDepths(p) @@ -64,23 +72,17 @@ estimatePSCS = function(p, hzdesgn = "hzname", clay.attr = "clay", attr.len <- unlist(lapply(c(hzdesgn, clay.attr, texcl.attr), length)) if (any(attr.len > 1)) stop("horizon designation, clay attribute or texture class attribute must have length 1") - - if (is.null(hzdesgn) | (!hzdesgn %in% horizonNames(p))) { - hzdesgn <- guessHzDesgnName(p, required = TRUE) - if (hzdesgn == "") - stop("horizon designation column not correctly specified") + + if (is.null(hzdesgn) || !hzdesgn %in% horizonNames(p)) { + stop("Horizon designation column (", hzdesgn, ") does not exist.") } - - if (is.null(clay.attr) | (!clay.attr %in% horizonNames(p))) { - clay.attr <- guessHzAttrName(p, attr = "clay", optional = c("total","_r")) - if (clay.attr == "") - stop("horizon clay content column not correctly specified") + + if (is.null(texcl.attr) || !texcl.attr %in% horizonNames(p)) { + stop("Horizon texture class column (", texcl.attr, ") does not exist.") } - - if (is.null(texcl.attr) | (!texcl.attr %in% horizonNames(p))) { - texcl.attr <- guessHzTexClName(p) - if (texcl.attr == "") - stop("horizon texture class column not correctly specified") + + if (is.null(clay.attr) | (!clay.attr %in% horizonNames(p))) { + stop("Horizon clay content column (", clay.attr, ") does not exist.") } soildepth <- minDepthOf(p, hzdesgn = hzdesgn, pattern = bottom.pattern, simplify = FALSE)[[hz.depths[1]]] diff --git a/R/getArgillicBounds.R b/R/getArgillicBounds.R index f7e88384..51baf895 100644 --- a/R/getArgillicBounds.R +++ b/R/getArgillicBounds.R @@ -42,15 +42,18 @@ #' depths(sp1) <- id ~ top + bottom #' site(sp1) <- ~ group #' -#' p <- sp1 -#' attr <- 'prop' # clay contents -#' foo <- getArgillicBounds(p, hzdesgn='name', clay.attr = attr, texcl.attr="texture") -#' foo +#' # set required metadata +#' hzdesgnname(sp1) <- 'name' +#' hztexclname(sp1) <- 'texture' +#' hzmetaname(sp1, 'clay') <- 'prop' +#' +#' x <- getArgillicBounds(sp1) +#' x #' getArgillicBounds <- function(p, - hzdesgn = 'hzname', - clay.attr = 'clay', - texcl.attr = 'texcl', + hzdesgn = hzdesgnname(p, required = TRUE), + clay.attr = hzmetaname(p, 'clay', required = TRUE), + texcl.attr = hztexclname(p, required = TRUE), require_t = TRUE, bottom.pattern = "Cr|R|Cd", lower.grad.pattern = "^[2-9]*B*CB*[^rtd]*[1-9]*$", @@ -61,26 +64,18 @@ getArgillicBounds <- function(p, hz <- horizons(p) hzd <- horizonDepths(p) - # ease removal of attribute name arguments -- deprecate them later - # for now, just fix em if the defaults dont match the hzdesgn/texcl.attr - if (!hzdesgn %in% horizonNames(p)) { - hzdesgn <- guessHzDesgnName(p) - if (is.na(hzdesgn)) - stop("horizon designation column not correctly specified") + if (is.null(hzdesgn) || !hzdesgn %in% horizonNames(p)) { + stop("Horizon designation column (", hzdesgn, ") does not exist.") } - - if (!clay.attr %in% horizonNames(p)) { - clay.attr <- guessHzAttrName(p, attr = "clay", optional = c("total","_r")) - if (is.na(clay.attr)) - stop("horizon clay content column not correctly specified") + + if (is.null(clay.attr) | (!clay.attr %in% horizonNames(p))) { + stop("Horizon clay content column (", texcl.attr, ") does not exist.") } - - if (!texcl.attr %in% horizonNames(p)) { - texcl.attr <- guessHzTexClName(p) - if (is.na(texcl.attr)) - stop("horizon texture class column not correctly specified") + + if (is.null(texcl.attr) || !texcl.attr %in% horizonNames(p)) { + stop("Horizon texture class column (", texcl.attr, ") does not exist.") } - + # get upper bound... mss <- getMineralSoilSurfaceDepth(p, hzdesgn = hzdesgn, simplify = FALSE) pld <- getPlowLayerDepth(p, hzdesgn = hzdesgn, simplify = FALSE) diff --git a/R/getCambicBounds.R b/R/getCambicBounds.R index 9dbe6aed..58702caf 100644 --- a/R/getCambicBounds.R +++ b/R/getCambicBounds.R @@ -38,16 +38,19 @@ #' #' # 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 = guessHzDesgnName(p, required = TRUE), - texcl.attr = guessHzTexClName(p, required = TRUE), - clay.attr = guessHzAttrName(p, attr = 'clay', c("total", "_r")), + 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", @@ -55,9 +58,13 @@ getCambicBounds <- function(p, sandy.texture.pattern = "-S$|^S$|COS$|L[^V]FS$|[^L]VFS$|LS$|LFS$", ...) { - # sacrafice to CRAN gods in the name of NSE + # 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), diff --git a/R/getSurfaceHorizonDepth.R b/R/getSurfaceHorizonDepth.R index 94ac8296..8768f5e1 100644 --- a/R/getSurfaceHorizonDepth.R +++ b/R/getSurfaceHorizonDepth.R @@ -8,7 +8,7 @@ #' #' @param p a SoilProfileCollection #' @param pattern a regular expression pattern to match for all horizons to be considered part of the "surface". -#' @param hzdesgn column name containing horizon designation. Default: \code{guessHzDesgnName(p, required = TRUE)}. +#' @param hzdesgn column name containing horizon designation. Default: `hzdesgnname(p, required = TRUE)`. #' @param simplify logical. Return single profile results as vector (default: `TRUE`) or `data.frame` (`FALSE`) #' @return a numeric value corresponding to the bottom depth of the last horizon matching 'pattern' that is contiguous with other matching horizons up to the soil surface. If `length(p) > 1` then a _data.frame_ containing profile ID, horizon ID, top or bottom depths, horizon designation and pattern. #' @@ -57,12 +57,12 @@ #' getSurfaceHorizonDepth <- function(p, pattern, - hzdesgn = guessHzDesgnName(p), + hzdesgn = hzdesgnname(p, required = TRUE), simplify = TRUE) { - if (!hzdesgn[1] %in% horizonNames(p)) { - # error if no valid designation found or specified - hzdesgn <- guessHzDesgnName(p, required = TRUE) + + if (is.null(hzdesgn) || !hzdesgn %in% horizonNames(p)) { + stop("Horizon designation column (", hzdesgn, ") does not exist.") } hz <- data.table::as.data.table(horizons(p)) @@ -160,15 +160,20 @@ getSurfaceHorizonDepth <- function(p, #' @rdname getSurfaceHorizonDepth #' @export -getMineralSoilSurfaceDepth <- function(p, hzdesgn = guessHzDesgnName(p), pattern = "O", simplify = TRUE) { - #assumes OSM is given O designation; - #TODO: add support for lab-sampled organic measurements - # keep organic horizons with andic soil properties +getMineralSoilSurfaceDepth <- function(p, hzdesgn = hzdesgnname(p, required = TRUE), pattern = "O", simplify = TRUE) { + + if (is.null(hzdesgn) || !hzdesgn %in% horizonNames(p)) { + stop("Horizon designation column (", hzdesgn, ") does not exist.") + } + + # assumes OSM is given O horizon designation; + # TODO: add support for lab-sampled organic measurements + # keep organic horizons with andic soil properties return(getSurfaceHorizonDepth(p, hzdesgn = hzdesgn, pattern = pattern, simplify = simplify)) } #' @rdname getSurfaceHorizonDepth #' @export -getPlowLayerDepth <- function(p, hzdesgn = guessHzDesgnName(p), pattern = "^Ap[^b]*", simplify = TRUE) { +getPlowLayerDepth <- function(p, hzdesgn = hzdesgnname(p, required = TRUE), pattern = "^Ap[^b]*", simplify = TRUE) { return(getSurfaceHorizonDepth(p, hzdesgn = hzdesgn, pattern = pattern, simplify = simplify)) } diff --git a/R/guessColumnNames.R b/R/guessColumnNames.R index af635c9c..1c2f294e 100644 --- a/R/guessColumnNames.R +++ b/R/guessColumnNames.R @@ -6,10 +6,10 @@ #' #' e.g. \code{guessHzAttrName(x, attr="clay", optional=c("total", "_r"))} matches (\code{claytotal_r == totalclay_r}) over (\code{clay_r == claytotal == totalclay}) over \code{clay}. #' -#' @param x A SoilProfileCollection -#' @param attr A regular expression containing required formative element of attribute name. -#' @param optional A character vector of regular expression(s) containing optional formative elements of attribute name. -#' @param verbose A boolean value for whether to produce message output about guesses. +#' @param x A _SoilProfileCollection_ +#' @param attr _character_. A regular expression containing required formative element of attribute name. +#' @param optional _character_. Vector of regular expression(s) containing optional formative elements of attribute name. +#' @param verbose _logical_. Produce message output about guesses? Default: `TRUE` #' #' @return Character containing horizon attribute column name. Result is the first match in \code{horizonNames(x)} with the most required plus optional patterns matched. #' @@ -46,15 +46,15 @@ guessHzAttrName <- function(x, attr, optional = NULL, verbose = TRUE, required = FALSE) { nm <- horizonNames(x) - if(!inherits(x, 'SoilProfileCollection')) { + if (!inherits(x, 'SoilProfileCollection')) { stop("x must be a SoilProfileCollection") } # possible names include column names with name in the name - req <- grepl(attr, nm, ignore.case=TRUE) + req <- grepl(attr, nm, ignore.case = TRUE) - opt <- lapply(as.list(optional), function(i) grepl(i, nm, ignore.case=TRUE)) - if(is.null(optional) | length(optional) == 0) + opt <- lapply(as.list(optional), function(i) grepl(i, nm, ignore.case = TRUE)) + if (is.null(optional) | length(optional) == 0) opt <- as.list(req) opt <- rowSums(do.call('cbind', opt)) @@ -70,15 +70,15 @@ guessHzAttrName <- function(x, attr, optional = NULL, verbose = TRUE, required = # return first index matching in decreasing precedence # all optional met, some optional met, basic requirement met, no requirement met res <- NA - if(length(idx1)) { + if (length(idx1)) { res <- nm[idx1[1]] - } else if(length(idx2)) { + } else if (length(idx2)) { res <- nm[idx2[1]] - } else if(length(idx3)) { + } else if (length(idx3)) { res <- nm[idx3[1]] } - if(!is.na(res)) { - if(verbose) + if (!is.na(res)) { + if (verbose) message(sprintf('guessing horizon attribute \'%s\' is stored in `%s`', attr, res)) } else { msg <- sprintf('unable to guess column containing horizon attribute \'%s\'', attr) @@ -90,7 +90,7 @@ guessHzAttrName <- function(x, attr, optional = NULL, verbose = TRUE, required = return(res) } -#' @description `guessHzDesgnName()`: This follows the historic convention used by \code{aqp::plotSPC()} looking for "hzname" or other column names containing the regular expression "name". If the pattern "name" is not found, the pattern "desgn" is searched as a fallback, as "hzdesgn" or "hz_desgn" are other common column naming schemes for horizon designation name. +#' @description `guessHzDesgnName()`: **DEPRECATED** This follows the historic convention used by \code{aqp::plotSPC()} looking for "hzname" or other column names containing the regular expression "name". If the pattern "name" is not found, the pattern "desgn" is searched as a fallback, as "hzdesgn" or "hz_desgn" are other common column naming schemes for horizon designation name. #' #' @param x A SoilProfileCollection #' @param required logical Default: `FALSE`. Is this attribute required? If it is, set to `TRUE` to trigger error on invalid value. @@ -98,23 +98,14 @@ guessHzAttrName <- function(x, attr, optional = NULL, verbose = TRUE, required = #' @rdname guessHzAttrName #' #' @export -#' -#' @examples -#' -#' a <- data.frame(id = 1, top = c(0,10), bottom=c(10,40), horizonname=c("A","Bw")) -#' depths(a) <- id ~ top + bottom -#' -#' # store guess in metadata -#' hzdesgnname(a) <- guessHzDesgnName(a) -#' -#' # inspect result -#' hzdesgnname(a) -#' guessHzDesgnName <- function(x, required = FALSE) { + + .Deprecated(msg = "`guessHzDesgnName()` is deprecated. Use `hzdesgnname()` (with `required=TRUE` argument if needed), or use `guessHzAttrName()` with appropriate values for your use case") + nm <- horizonNames(x) name <- NA - if(!inherits(x, 'SoilProfileCollection')) { + if (!inherits(x, 'SoilProfileCollection')) { stop("x must be a SoilProfileCollection") } @@ -125,10 +116,10 @@ guessHzDesgnName <- function(x, required = FALSE) { } # possible names include column names with name in the name - possible.name <- nm[grep('name', nm, ignore.case=TRUE)] + possible.name <- nm[grep('name', nm, ignore.case = TRUE)] # use the first valid guess - if(length(possible.name) > 0) { + if (length(possible.name) > 0) { possible.name <- possible.name[1] name <- possible.name } else { @@ -148,24 +139,15 @@ guessHzDesgnName <- function(x, required = FALSE) { return(name) } -#' @description `guessHzTexClName()`: This function is used to provide a texture class attribute column name to functions. It will use regular expressions to match "texcl" which is typically the texture of the fine earth fraction, without modifiers or in-lieu textures. Alternately, it will match "texture" for cases where "texcl" is absent (e.g. in NASIS Component Horizon). +#' @description `guessHzTexClName()`: **DEPRECATED** This function is used to provide a texture class attribute column name to functions. It will use regular expressions to match "texcl" which is typically the texture of the fine earth fraction, without modifiers or in-lieu textures. Alternately, it will match "texture" for cases where "texcl" is absent (e.g. in NASIS Component Horizon). #' #' @rdname guessHzAttrName #' #' @export guessHzTexClName -#' -#' @examples -#' -#' a <- data.frame(id = 1, top = c(0,10), bottom=c(10,40), texture=c("A","Bw")) -#' depths(a) <- id ~ top + bottom -#' -#' # store guess in metadata -#' hztexclname(a) <- guessHzTexClName(a) -#' -#' # inspect result -#' hztexclname(a) -#' guessHzTexClName <- function(x, required = FALSE) { + + .Deprecated(msg = "`guessHzTexClName()` is deprecated. Use `hztexclname()` (with `required=TRUE` argument if needed), or use `guessHzAttrName()` with appropriate values for your use case") + nm <- horizonNames(x) if (!inherits(x, 'SoilProfileCollection')) { diff --git a/R/mollicEpipedon.R b/R/mollicEpipedon.R index fb67817f..11c21be8 100644 --- a/R/mollicEpipedon.R +++ b/R/mollicEpipedon.R @@ -37,9 +37,9 @@ #' clay.attr='prop', truncate=FALSE)) #' mollic.thickness.requirement <- function(p, - hzdesgn = guessHzDesgnName(p), - texcl.attr = guessHzTexClName(p), - clay.attr = guessHzAttrName(p, 'clay', c('total','_r')), + hzdesgn = hzdesgnname(p, required = TRUE), + texcl.attr = hztexclname(p, required = TRUE), + clay.attr = hzmetaname(p, "clay", required = TRUE), truncate = TRUE) { hzd <- horizonDepths(p) diff --git a/R/soilColorIndices.R b/R/soilColorIndices.R index 11d4b8f9..500c7bed 100644 --- a/R/soilColorIndices.R +++ b/R/soilColorIndices.R @@ -300,7 +300,7 @@ buntley.westin.index <- function(hue, chroma) { #' @rdname thompson.bell.darkness #' @export thompson.bell.darkness thompson.bell.darkness <- function(p, - name = guessHzDesgnName(p, required = TRUE), + name = hzdesgnname(p, required = TRUE), pattern = "^A", value = "m_value", chroma = "m_chroma") { @@ -310,8 +310,9 @@ thompson.bell.darkness <- function(p, hz <- horizons(p) depthz <- horizonDepths(p) - if (!all(name %in% horizonNames(p))) { - name <- guessHzDesgnName(p, required = TRUE) + + if (is.null(name) || !name %in% horizonNames(p)) { + stop("Horizon designation column (", name, ") does not exist.") } a.hz <- hz[grepl(hz[[name]], pattern = pattern),] diff --git a/man/depthOf.Rd b/man/depthOf.Rd index 1f72741f..b8c9d25a 100644 --- a/man/depthOf.Rd +++ b/man/depthOf.Rd @@ -11,7 +11,7 @@ depthOf( pattern, FUN = NULL, top = TRUE, - hzdesgn = guessHzDesgnName(p), + hzdesgn = hzdesgnname(p, required = TRUE), no.contact.depth = NULL, no.contact.assigned = NA_real_, na.rm = TRUE, @@ -22,7 +22,7 @@ maxDepthOf( p, pattern, top = TRUE, - hzdesgn = guessHzDesgnName(p), + hzdesgn = hzdesgnname(p, required = TRUE), no.contact.depth = NULL, no.contact.assigned = NA, na.rm = TRUE, @@ -33,7 +33,7 @@ minDepthOf( p, pattern, top = TRUE, - hzdesgn = guessHzDesgnName(p), + hzdesgn = hzdesgnname(p, required = TRUE), no.contact.depth = NULL, no.contact.assigned = NA, na.rm = TRUE, diff --git a/man/estimatePSCS.Rd b/man/estimatePSCS.Rd index 1b805577..0bb2241b 100644 --- a/man/estimatePSCS.Rd +++ b/man/estimatePSCS.Rd @@ -7,9 +7,9 @@ \usage{ estimatePSCS( p, - hzdesgn = "hzname", - clay.attr = "clay", - texcl.attr = "texcl", + hzdesgn = hzdesgnname(p, required = TRUE), + clay.attr = hzmetaname(p, "clay", required = TRUE), + texcl.attr = hztexclname(p, required = TRUE), tax_order_field = "tax_order", bottom.pattern = "Cr|R|Cd", simplify = TRUE, @@ -69,12 +69,13 @@ data(sp1, package = 'aqp') depths(sp1) <- id ~ top + bottom site(sp1) <- ~ group -p <- sp1 -attr <- 'prop' # clay contents -foo <- estimatePSCS(p, hzdesgn='name', clay.attr = attr, texcl.attr="texture") -foo - +# set required metadata +hzdesgnname(sp1) <- 'name' +hztexclname(sp1) <- 'texture' +hzmetaname(sp1, 'clay') <- 'prop' +x <- estimatePSCS(sp1) +x } \references{ Soil Survey Staff. 2014. Keys to Soil Taxonomy, 12th ed. diff --git a/man/getArgillicBounds.Rd b/man/getArgillicBounds.Rd index b3e74022..44c0fa43 100644 --- a/man/getArgillicBounds.Rd +++ b/man/getArgillicBounds.Rd @@ -6,9 +6,9 @@ \usage{ getArgillicBounds( p, - hzdesgn = "hzname", - clay.attr = "clay", - texcl.attr = "texcl", + hzdesgn = hzdesgnname(p, required = TRUE), + clay.attr = hzmetaname(p, "clay", required = TRUE), + texcl.attr = hztexclname(p, required = TRUE), require_t = TRUE, bottom.pattern = "Cr|R|Cd", lower.grad.pattern = "^[2-9]*B*CB*[^rtd]*[1-9]*$", @@ -65,10 +65,13 @@ data(sp1, package = 'aqp') depths(sp1) <- id ~ top + bottom site(sp1) <- ~ group -p <- sp1 -attr <- 'prop' # clay contents -foo <- getArgillicBounds(p, hzdesgn='name', clay.attr = attr, texcl.attr="texture") -foo +# set required metadata +hzdesgnname(sp1) <- 'name' +hztexclname(sp1) <- 'texture' +hzmetaname(sp1, 'clay') <- 'prop' + +x <- getArgillicBounds(sp1) +x } \author{ diff --git a/man/getCambicBounds.Rd b/man/getCambicBounds.Rd index b1358d29..70c9ec81 100644 --- a/man/getCambicBounds.Rd +++ b/man/getCambicBounds.Rd @@ -6,9 +6,9 @@ \usage{ getCambicBounds( p, - hzdesgn = guessHzDesgnName(p, required = TRUE), - texcl.attr = guessHzTexClName(p, required = TRUE), - clay.attr = guessHzAttrName(p, attr = "clay", c("total", "_r")), + 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", @@ -60,8 +60,11 @@ spc <- data.frame(id=1, taxsubgrp = "Lithic Haploxerepts", # 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) diff --git a/man/getSurfaceHorizonDepth.Rd b/man/getSurfaceHorizonDepth.Rd index 6424c69b..ba7af8aa 100644 --- a/man/getSurfaceHorizonDepth.Rd +++ b/man/getSurfaceHorizonDepth.Rd @@ -9,20 +9,20 @@ getSurfaceHorizonDepth( p, pattern, - hzdesgn = guessHzDesgnName(p), + hzdesgn = hzdesgnname(p, required = TRUE), simplify = TRUE ) getMineralSoilSurfaceDepth( p, - hzdesgn = guessHzDesgnName(p), + hzdesgn = hzdesgnname(p, required = TRUE), pattern = "O", simplify = TRUE ) getPlowLayerDepth( p, - hzdesgn = guessHzDesgnName(p), + hzdesgn = hzdesgnname(p, required = TRUE), pattern = "^Ap[^b]*", simplify = TRUE ) @@ -32,7 +32,7 @@ getPlowLayerDepth( \item{pattern}{a regular expression pattern to match for all horizons to be considered part of the "surface".} -\item{hzdesgn}{column name containing horizon designation. Default: \code{guessHzDesgnName(p, required = TRUE)}.} +\item{hzdesgn}{column name containing horizon designation. Default: \code{hzdesgnname(p, required = TRUE)}.} \item{simplify}{logical. Return single profile results as vector (default: \code{TRUE}) or \code{data.frame} (\code{FALSE})} } diff --git a/man/guessHzAttrName.Rd b/man/guessHzAttrName.Rd index 7d3247fa..0733b6e6 100644 --- a/man/guessHzAttrName.Rd +++ b/man/guessHzAttrName.Rd @@ -15,11 +15,11 @@ guessHzTexClName(x, required = FALSE) \arguments{ \item{x}{A SoilProfileCollection} -\item{attr}{A regular expression containing required formative element of attribute name.} +\item{attr}{\emph{character}. A regular expression containing required formative element of attribute name.} -\item{optional}{A character vector of regular expression(s) containing optional formative elements of attribute name.} +\item{optional}{\emph{character}. Vector of regular expression(s) containing optional formative elements of attribute name.} -\item{verbose}{A boolean value for whether to produce message output about guesses.} +\item{verbose}{\emph{logical}. Produce message output about guesses? Default: \code{TRUE}} \item{required}{logical Default: \code{FALSE}. Is this attribute required? If it is, set to \code{TRUE} to trigger error on invalid value.} } @@ -31,9 +31,9 @@ Character containing horizon attribute column name. Result is the first match in e.g. \code{guessHzAttrName(x, attr="clay", optional=c("total", "_r"))} matches (\code{claytotal_r == totalclay_r}) over (\code{clay_r == claytotal == totalclay}) over \code{clay}. -\code{guessHzDesgnName()}: This follows the historic convention used by \code{aqp::plotSPC()} looking for "hzname" or other column names containing the regular expression "name". If the pattern "name" is not found, the pattern "desgn" is searched as a fallback, as "hzdesgn" or "hz_desgn" are other common column naming schemes for horizon designation name. +\code{guessHzDesgnName()}: \strong{DEPRECATED} This follows the historic convention used by \code{aqp::plotSPC()} looking for "hzname" or other column names containing the regular expression "name". If the pattern "name" is not found, the pattern "desgn" is searched as a fallback, as "hzdesgn" or "hz_desgn" are other common column naming schemes for horizon designation name. -\code{guessHzTexClName()}: This function is used to provide a texture class attribute column name to functions. It will use regular expressions to match "texcl" which is typically the texture of the fine earth fraction, without modifiers or in-lieu textures. Alternately, it will match "texture" for cases where "texcl" is absent (e.g. in NASIS Component Horizon). +\code{guessHzTexClName()}: \strong{DEPRECATED} This function is used to provide a texture class attribute column name to functions. It will use regular expressions to match "texcl" which is typically the texture of the fine earth fraction, without modifiers or in-lieu textures. Alternately, it will match "texture" for cases where "texcl" is absent (e.g. in NASIS Component Horizon). } \examples{ @@ -61,26 +61,6 @@ depths(c) <- id ~ top + bottom guessHzAttrName(c, attr="clay", optional=c("total", "_r")) - -a <- data.frame(id = 1, top = c(0,10), bottom=c(10,40), horizonname=c("A","Bw")) -depths(a) <- id ~ top + bottom - -# store guess in metadata -hzdesgnname(a) <- guessHzDesgnName(a) - -# inspect result -hzdesgnname(a) - - -a <- data.frame(id = 1, top = c(0,10), bottom=c(10,40), texture=c("A","Bw")) -depths(a) <- id ~ top + bottom - -# store guess in metadata -hztexclname(a) <- guessHzTexClName(a) - -# inspect result -hztexclname(a) - } \author{ Andrew G. Brown diff --git a/man/hz_to_taxpartsize.Rd b/man/hz_to_taxpartsize.Rd index 2daefdf8..0e40c149 100644 --- a/man/hz_to_taxpartsize.Rd +++ b/man/hz_to_taxpartsize.Rd @@ -69,6 +69,11 @@ fragvoltot = h$frags depths(h) <- id ~ top + bottom +# set required metadata for estimatePSCS() +hzdesgnname(h) <- "hzname" +hztexclname(h) <- "texcl" +hzmetaname(h, "clay") <- "clay" + pscs <- data.frame(id = h$id, rbind(estimatePSCS(h))) names(pscs)[2:3] <- c("top", "bottom") diff --git a/man/hzmetaname.Rd b/man/hzmetaname.Rd new file mode 100644 index 00000000..e9350ff6 --- /dev/null +++ b/man/hzmetaname.Rd @@ -0,0 +1,57 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/SoilProfileCollection-metadata.R +\docType{methods} +\name{hzmetaname} +\alias{hzmetaname} +\alias{hzmetaname,SoilProfileCollection-method} +\alias{hzmetaname<-} +\alias{hzmetaname<-,SoilProfileCollection-method} +\title{Get or Set Horizon Metadata Column Name} +\usage{ +\S4method{hzmetaname}{SoilProfileCollection}(object, attr, required = FALSE) + +\S4method{hzmetaname}{SoilProfileCollection}(object, attr, required = FALSE) <- value +} +\arguments{ +\item{object}{A \emph{SoilProfileCollection}} + +\item{attr}{\emph{character}. Base name for attribute to be stored in metadata. This is prefixed with \code{"aqp_hz"} for horizon-level metadata for column attributes. e.g. \code{attr="clay"} results in metadata value retrieved from \code{"aqp_hzclay"}.} + +\item{required}{\emph{logical}. Is this attribute required? If it is, set to \code{TRUE} to trigger error on invalid \code{value}.} + +\item{value}{\emph{character}. Name of horizon-level column containing data corresponding to \code{attr}.} +} +\description{ +\code{hzmetaname()}: Get column name containing horizon data of interest + +\verb{hzmetaname<-}: Set horizon designation column name +} +\details{ +Store the column name containing a specific type of horizon data in the metadata slot of the SoilProfileCollection. +} +\examples{ + +data(sp1) + +# promote to SPC +depths(sp1) <- id ~ top + bottom + +# set important metadata columns +hzdesgnname(sp1) <- "name" +hztexclname(sp1) <- "texture" + +# set custom horizon property (clay content) column +hzmetaname(sp1, "clay") <- "prop" + +# inspect metadata list +metadata(sp1) + +# get horizon clay content column +hzmetaname(sp1, "clay") + +# uses hzdesgname(), hztexclname(), hzmetaname(attr="clay") in function definition +estimatePSCS(sp1) +} +\seealso{ +\code{\link[=guessHzAttrName]{guessHzAttrName()}} +} diff --git a/man/mollic.thickness.requirement.Rd b/man/mollic.thickness.requirement.Rd index c8edd7df..5b186b55 100644 --- a/man/mollic.thickness.requirement.Rd +++ b/man/mollic.thickness.requirement.Rd @@ -6,9 +6,9 @@ \usage{ mollic.thickness.requirement( p, - hzdesgn = guessHzDesgnName(p), - texcl.attr = guessHzTexClName(p), - clay.attr = guessHzAttrName(p, "clay", c("total", "_r")), + hzdesgn = hzdesgnname(p, required = TRUE), + texcl.attr = hztexclname(p, required = TRUE), + clay.attr = hzmetaname(p, "clay", required = TRUE), truncate = TRUE ) } diff --git a/man/thompson.bell.darkness.Rd b/man/thompson.bell.darkness.Rd index a6a95595..a772a326 100644 --- a/man/thompson.bell.darkness.Rd +++ b/man/thompson.bell.darkness.Rd @@ -6,7 +6,7 @@ \usage{ thompson.bell.darkness( p, - name = guessHzDesgnName(p, required = TRUE), + name = hzdesgnname(p, required = TRUE), pattern = "^A", value = "m_value", chroma = "m_chroma" diff --git a/tests/testthat/test-argillic.R b/tests/testthat/test-argillic.R index 62be4b2a..e1ea1d97 100644 --- a/tests/testthat/test-argillic.R +++ b/tests/testthat/test-argillic.R @@ -5,6 +5,8 @@ depths(sp1) <- id ~ top + bottom site(sp1) <- ~ group p <- sp1[1] +hzdesgnname(p) <- "name" +hztexclname(p) <- "texture" clay.attr <- 'prop' # clay contents % texcl.attr <- 'texture' # class containing textural class (for finding sandy textures) @@ -41,31 +43,14 @@ test_that("argillic.clay.increase() (used for getArgillicBounds())", { test_that("getArgillicBounds()", { - # name and texture class are guessable - d <- getArgillicBounds(p, clay.attr='prop') - + # name and texture class are determined from metadata + d <- getArgillicBounds(p, clay.attr = 'prop') + # this makes sure estimateSoilDepth() isn't broken... expect_equivalent(d, c(49, 89)) - - # no error when hzdesgn and texcl.attr are unknown, due to guessing function - d1 <- getArgillicBounds(p, hzdesgn='foo', clay.attr='prop', texcl.attr = 'bar') - expect_equivalent(d1, c(49, 89)) - - # deliberately break the reasoning guessing - # returns correct result because of slots - p$goo <- p$name - p$boo <- p$texture - p$name <- NULL - p$texture <- NULL - - expect_error(getArgillicBounds(p, hzdesgn='foo', clay.attr='prop', texcl.attr = 'bar')) - - # set the desgn name and texture class slots - hzdesgnname(p) <- "goo" - hztexclname(p) <- "boo" - - d2 <- getArgillicBounds(p, hzdesgn='foo', clay.attr='prop', texcl.attr = 'bar') - expect_equivalent(d2, c(49, 89)) + + # error when hzdesgn and texcl.attr are unknown; no guessing + expect_error(getArgillicBounds(p, hzdesgn = 'foo', clay.attr = 'prop', texcl.attr = 'bar')) }) test_that("getArgillicBounds - error conditions", { diff --git a/tests/testthat/test-cambic.R b/tests/testthat/test-cambic.R index 271b6239..6e5b2c66 100644 --- a/tests/testthat/test-cambic.R +++ b/tests/testthat/test-cambic.R @@ -16,6 +16,7 @@ spc <- data.frame(id = 1, taxsubgrp = "Lithic Haploxerepts", depths(spc) <- id ~ hzdept + hzdepb hzdesgnname(spc) <- 'hzname' hztexclname(spc) <- 'texcl' +hzmetaname(spc, "clay") <- 'clay' test_that("getCambicBounds - basic functionality", { dfbound <- getCambicBounds(spc) diff --git a/tests/testthat/test-estimatePSCS.R b/tests/testthat/test-estimatePSCS.R index 498d889d..85cc0302 100644 --- a/tests/testthat/test-estimatePSCS.R +++ b/tests/testthat/test-estimatePSCS.R @@ -77,8 +77,8 @@ test_that("estimatePSCS()", { }) test_that("estimatePSCS() thin soil profile with O horizon", { - expect_equal(estimatePSCS(x, clay.attr = 'prop', texcl.attr = "foo", hzdesgn = 'name'), c(13, 40)) - expect_equal(estimatePSCS(c(q,x), clay.attr = 'prop', texcl.attr = "foo", hzdesgn = 'name'), + expect_equal(estimatePSCS(x, clay.attr = 'prop', texcl.attr = "texture", hzdesgn = 'name'), c(13, 40)) + expect_equal(estimatePSCS(c(q,x), clay.attr = 'prop', texcl.attr = "texture", hzdesgn = 'name'), data.frame(id = c("706300", "P002"), pscs_top = c(13, 30), pscs_bottom = c(40, 59))) diff --git a/tests/testthat/test-guessColumnNames.R b/tests/testthat/test-guessColumnNames.R index c9cbf5dd..29328c95 100644 --- a/tests/testthat/test-guessColumnNames.R +++ b/tests/testthat/test-guessColumnNames.R @@ -8,7 +8,7 @@ depths(sp3) <- id ~ top + bottom test_that("basic functionality", { # historic horizon designation name (e.g. used by plotSPC) - expect_equal(guessHzDesgnName(sp3), "name") + expect_warning(expect_equal(guessHzDesgnName(sp3), "name")) # basic attribute name guessing expect_message(expect_equal(guessHzAttrName(sp3, "clay", ""), "clay"), @@ -25,32 +25,31 @@ test_that("basic functionality", { "guessing horizon attribute 'clay' is stored in `claytotal_r`") # basic attribute name guessing - expect_equal(guessHzTexClName(sp3), "") + expect_warning(expect_equal(guessHzTexClName(sp3), "")) # texcl horizons(sp3)$texcl <- "l" - expect_equal(guessHzTexClName(sp3), "texcl") + expect_warning(expect_equal(guessHzTexClName(sp3), "texcl")) # texture sp3$texture <- sp3$texcl sp3$texcl <- NULL - expect_equal(guessHzTexClName(sp3), "texture") + expect_warning(expect_equal(guessHzTexClName(sp3), "texture")) # descriptive name sp3$hzdesgn <- sp3$name sp3$name <- NULL sp3$desgn <- 1:nrow(sp3) - expect_equal(guessHzDesgnName(sp3), "hzdesgn") + expect_warning(expect_equal(guessHzDesgnName(sp3), "hzdesgn")) # unable to guess name sp3$foo <- sp3$hzdesgn sp3$hzdesgn <- NULL sp3$desgn <- NULL - expect_message(expect_equal(guessHzDesgnName(sp3), NA), - "unable to guess column containing horizon designations") + expect_warning(expect_equal(guessHzDesgnName(sp3), NA)) # custom name hzdesgnname(sp3) <- "foo" - expect_equal(guessHzDesgnName(sp3), "foo") + expect_warning(expect_equal(guessHzDesgnName(sp3), "foo")) }) diff --git a/tests/testthat/test-mollic.R b/tests/testthat/test-mollic.R index 4dbefd72..d8870998 100644 --- a/tests/testthat/test-mollic.R +++ b/tests/testthat/test-mollic.R @@ -28,10 +28,12 @@ spc2 <- data.frame( depths(spc) <- id ~ hzdept + hzdepb hzdesgnname(spc) <- 'hzname' hztexclname(spc) <- 'texcl' +hzmetaname(spc, "clay") <- 'prop' depths(spc2) <- id ~ hzdept + hzdepb hzdesgnname(spc2) <- 'hzname' hztexclname(spc2) <- 'texcl' +hzmetaname(spc2, "clay") <- 'claytotest' spc3 <- data.frame(pedon_key = c("10016", "10016", "10016", "10047", "10047", "10047", "10047", "10047", "10047", "10047"), @@ -46,6 +48,7 @@ spc3 <- data.frame(pedon_key = c("10016", "10016", "10016", "10047", "10047", depths(spc3) <-pedon_key ~ hzn_top + hzn_bot hzdesgnname(spc3) <- "hzn_desgn" hztexclname(spc3) <- "texture_lab" +hzmetaname(spc3, "clay") <- "clay_total" test_that("mollic.thickness.requirement", { expect_equal(mollic.thickness.requirement(spc, clay.attr = 'prop'), 18) diff --git a/tests/testthat/test-soil-depth.R b/tests/testthat/test-soil-depth.R index 8a582ebc..9459588d 100644 --- a/tests/testthat/test-soil-depth.R +++ b/tests/testthat/test-soil-depth.R @@ -30,7 +30,7 @@ d <- depths(d) <- id ~ top + bottom - +hzdesgnname(d) <- "name" ## tests @@ -39,6 +39,7 @@ test_that("error conditions", { # function will only accept a single profile expect_error(estimateSoilDepth(d, name='name')) + hzdesgnname(d) <- "" # not specified -> error expect_error(profileApply(d, estimateSoilDepth)) diff --git a/tests/testthat/test-surface-thickness.R b/tests/testthat/test-surface-thickness.R index 2f35ca4b..af63788b 100644 --- a/tests/testthat/test-surface-thickness.R +++ b/tests/testthat/test-surface-thickness.R @@ -5,6 +5,7 @@ context("surface horizon thickness, mineral soil surface, organic soil horizon") data(sp1, package = 'aqp') depths(sp1) <- id ~ top + bottom site(sp1) <- ~ group +hzdesgnname(sp1) <- "name" p <- sp1[1] attr <- 'prop' # clay contents %