From c2f916bb5aff3a81072e5f9dc2edc1b9171d0918 Mon Sep 17 00:00:00 2001 From: "Andrew G. Brown" Date: Wed, 18 May 2022 10:35:07 -0700 Subject: [PATCH] Coarse fractions and `.sieve`: Allow for custom `sieves` argument - Remove 76mm gravel warning - Adjust gravel threshold to 76mm --- R/AAAA.R | 16 ++-- R/get_extended_data_from_NASIS_db.R | 25 +++--- R/simplfyFragmentData.R | 107 +++++++++++------------- R/simplifyArtifactData.R | 94 +++++++-------------- data-raw/NASIS_SoilProfileCollections.R | 14 ++-- data-raw/spc-horizons.csv | 2 +- data/loafercreek.rda | Bin 30340 -> 30348 bytes man/simplifyFragmentData.Rd | 8 +- misc/run-all-NASIS-get-methods.R | 22 ++--- 9 files changed, 122 insertions(+), 166 deletions(-) diff --git a/R/AAAA.R b/R/AAAA.R index 425ac532e..6d77f4db4 100644 --- a/R/AAAA.R +++ b/R/AAAA.R @@ -6,18 +6,18 @@ soilDB.env <- new.env(hash=TRUE, parent = parent.frame()) .onLoad <- function(libname, pkgname) { # deprecated: soilDB 2.5.9 CRAN release - options(.soilDB_testNetworkFunctions=TRUE) - + options(.soilDB_testNetworkFunctions = TRUE) + # function verbosity - options(soilDB.verbose=FALSE) - + options(soilDB.verbose = FALSE) + # set default local nasis authentication - options(soilDB.NASIS.credentials="DSN=nasis_local;UID=NasisSqlRO;PWD=nasisRe@d0n1y") - + options(soilDB.NASIS.credentials = "DSN=nasis_local;UID=NasisSqlRO;PWD=nasisRe@d0n1y") + # update according to win 7 or 10 si <- Sys.info() - if( grepl('windows', si['sysname'], ignore.case = TRUE) & grepl('8|10', si['release'], ignore.case = TRUE) ) { - options(soilDB.NASIS.credentials="DSN=nasis_local;UID=NasisSqlRO;PWD=nasisRe@d0n1y365") + if (grepl('windows', si['sysname'], ignore.case = TRUE) & grepl('8|10', si['release'], ignore.case = TRUE)) { + options(soilDB.NASIS.credentials = "DSN=nasis_local;UID=NasisSqlRO;PWD=nasisRe@d0n1y365") } } diff --git a/R/get_extended_data_from_NASIS_db.R b/R/get_extended_data_from_NASIS_db.R index 71e14389c..3aae3527d 100644 --- a/R/get_extended_data_from_NASIS_db.R +++ b/R/get_extended_data_from_NASIS_db.R @@ -520,19 +520,21 @@ LEFT OUTER JOIN ( # summarize rock fragment data if (nrow(d.rf.data) > 0) { # keep track of UNIQUE original phiids so that we can optionally fill NA with 0 in a second pass - all.ids <- unique(d.rf.data[, 'phiid', drop=FALSE]) + all.ids <- unique(d.rf.data[, 'phiid', drop = FALSE]) # left join d.rf.summary <- merge(all.ids, d.rf.summary, by = 'phiid', sort = FALSE, all.x = TRUE, incomparables = NA) ## basic checks for problematic data + # 2022/05/18: removed this QC warning, the "change" to 75mm was a typographical error + # recent NSSH changes to gravel/cobble threshold 76mm -> 75mm - qc.idx <- which(d.rf.data$fragsize_h == 76) - if(length(qc.idx) > 0) { - msg <- sprintf('-> QC: some fragsize_h values == 76mm, may be mis-classified as cobbles [%i / %i records]', length(qc.idx), nrow(d.rf.data)) - message(msg) - } + # qc.idx <- which(d.rf.data$fragsize_h == 76) + # if (length(qc.idx) > 0) { + # msg <- sprintf('-> QC: some fragsize_h values == 75mm, may be mis-classified as cobbles [%i / %i records]', length(qc.idx), nrow(d.rf.data)) + # message(msg) + # } } @@ -552,15 +554,9 @@ LEFT OUTER JOIN ( if (nrow(d.art.data) > 0) { - art.all.ids <- unique(d.art.data[, 'phiid', drop=FALSE]) + art.all.ids <- unique(d.art.data[, 'phiid', drop = FALSE]) d.art.summary <- merge(art.all.ids, d.art.summary, by = 'phiid', sort = FALSE, all.x = TRUE, incomparables = NA) - # recent NSSH changes to gravel/cobble threshold 76mm -> 75mm - qc.idx <- which(d.art.data$huartsize_h == 76) - if (length(qc.idx) > 0) { - msg <- sprintf('-> QC: some huartsize_h values == 76mm, may be mis-classified as cobbles [%i / %i records]', length(qc.idx), nrow(d.art.data)) - message(msg) - } } if (nullFragsAreZero) { @@ -578,8 +574,7 @@ LEFT OUTER JOIN ( # if (nullFragsAreZero == TRUE) { # d.rf.data.v2[idx] <- lapply(d.rf.data.v2[idx], function(x) ifelse(is.na(x), 0, x)) # } - - + # return a list of results return(list(ecositehistory = d.ecosite, siteaoverlap = d.siteaoverlap, diff --git a/R/simplfyFragmentData.R b/R/simplfyFragmentData.R index 348c2348d..2572d4487 100644 --- a/R/simplfyFragmentData.R +++ b/R/simplfyFragmentData.R @@ -1,52 +1,52 @@ ## TODO: generalize, export, and make sieve sizes into an argument - -# latest NSSH part 618 -# https://directives.sc.egov.usda.gov/OpenNonWebContent.aspx?content=44371.wba +## 2022 I propose we move .sieve to aqp and export it there, see updates --agb # internally-used function to test size classes # diameter is in mm # NA diameter results in NA class -.sieve <- function(diameter, flat=FALSE, para=FALSE, new.names = NULL) { - - # flat fragments - if(flat == TRUE) - sieves <- c(channers=150, flagstones=380, stones=600, boulders=10000000000) - - # non-flat fragments - if(flat == FALSE) - sieves <- c(fine_gravel=5, gravel=75, cobbles=250, stones=600, boulders=10000000000) - - if(!is.null(new.names)) +.sieve <- function(diameter, flat = FALSE, para = FALSE, + sieves = c(fine_gravel = 5, gravel = 76, cobbles = 250, stones = 600, boulders = 1e10), + new.names = NULL) { + + if (flat && missing(sieves)) { + sieves <- c(channers = 150, flagstones = 380, stones = 600, boulders = 1e10) + } + + if (!is.null(new.names)) { names(sieves) <- new.names + } # test for NA, and filter-out - res <- vector(mode='character', length = length(diameter)) + res <- vector(mode = 'character', length = length(diameter)) res[which(is.na(diameter))] <- NA no.na.idx <- which(!is.na(diameter)) # only assign classes to non-NA diameters - if(length(no.na.idx) > 0) { + if (length(no.na.idx) > 0) { # pass diameters "through" sieves # 2020: latest part 618 uses '<' for all upper values of class range + # 2022: adjusted gravel upper threshold to 76 mm classes <- t(sapply(diameter[no.na.idx], function(i) i < sieves)) # determine largest passing sieve name res[no.na.idx] <- names(sieves)[apply(classes, 1, which.max)] # change names if we are working with parafrags - if(para == TRUE) + + if (para == TRUE) { res[no.na.idx] <- paste0('para', res[no.na.idx]) + } } return(res) } - -## TODO: this is NASIS-specific for now, generalize this to any data # x: uncoded contents of the phfrags table -.rockFragmentSieve <- function(x, vol.var = "fragvol", prefix = "frag") { +# vol.var: column name in x containing fragment volume (default (`"fragvol"`)) +# prefix: prefix used in common with hardness and shape var (default `"frag"`) +# ...: additional arguments to .sieve +.rockFragmentSieve <- function(x, vol.var = "fragvol", prefix = "frag", ...) { - xvar <- vol.var hardvar <- paste0(prefix, "hard") shpvar <- paste0(prefix, "shp") sizevar <- paste0(paste0(prefix, "size"), c("_l","_r","_h")) @@ -79,7 +79,6 @@ idx <- grep('strong|indurated', x[[hardvar]], ignore.case = TRUE, invert = TRUE) parafrags <- x[idx, ] - ## split flat / non-flat # frags idx <- which(frags[[shpvar]] == 'nonflat') @@ -97,18 +96,18 @@ ## sieve # non-flat fragments - frags.nonflat$class <- .sieve(frags.nonflat[[sizevar[2]]], flat = FALSE) + frags.nonflat$class <- .sieve(frags.nonflat[[sizevar[2]]], flat = FALSE, ...) # non-flat parafragments - parafrags.nonflat$class <- .sieve(parafrags.nonflat[[sizevar[2]]], flat = FALSE, para = TRUE) + parafrags.nonflat$class <- .sieve(parafrags.nonflat[[sizevar[2]]], flat = FALSE, para = TRUE, ...) # flat fragments - frags.flat$class <- .sieve(frags.flat[[sizevar[2]]], flat = TRUE) + frags.flat$class <- .sieve(frags.flat[[sizevar[2]]], flat = TRUE, ...) # flat parafragments - parafrags.flat$class <- .sieve(parafrags.flat[[sizevar[2]]], flat = TRUE, para = TRUE) + parafrags.flat$class <- .sieve(parafrags.flat[[sizevar[2]]], flat = TRUE, para = TRUE, ...) - # combine pieces, note may contain RF classes == NA + # combine pieces, note may contain RF classes == NA res <- rbind(frags.nonflat, frags.flat, parafrags.nonflat, parafrags.flat) # what does an NA fragment class mean? @@ -119,7 +118,7 @@ # keep track of these for QC in an 'unspecified' column # but only when there is a fragment volume specified idx <- which(is.na(res$class) & !is.na(res[[vol.var]])) - if( length(idx) > 0 ) { + if (length(idx) > 0) { res$class[idx] <- 'unspecified' } @@ -127,12 +126,6 @@ return(res) } - -# rf: un-coded contents of the phfrags table -# id.var: id column name -# nullFragsAreZero: convert NA to 0? - - #' Simplify Coarse Fraction Data #' #' Simplify multiple coarse fraction (>2mm) records by horizon. @@ -158,10 +151,12 @@ #' @param prefix a character vector prefix for input #' @param nullFragsAreZero should fragment volumes of NULL be interpreted as 0? (default: `TRUE`), see details #' @param msg Identifier of data being summarized. Default is `"rock fragment volume"` but this routine is also used for `"surface fragment cover"` +#' @param ... Additional arguments passed to sieving function (e.g. `sieves` a named numeric containing sieve size thresholds with class name) #' @author D.E. Beaudette, A.G Brown #' @keywords manip #' @export simplifyFragmentData -simplifyFragmentData <- function(rf, id.var, vol.var = "fragvol", prefix = "frag", nullFragsAreZero = TRUE, msg = "rock fragment volume") { +simplifyFragmentData <- function(rf, id.var, vol.var = "fragvol", prefix = "frag", + nullFragsAreZero = TRUE, msg = "rock fragment volume", ...) { fragvol <- NULL @@ -177,7 +172,7 @@ simplifyFragmentData <- function(rf, id.var, vol.var = "fragvol", prefix = "frag if (nrow(rf[which(!is.na(rf[[vol.var]])),]) == 0) { message(sprintf('NOTE: all records are missing %s', msg)) dat <- as.data.frame(t(rep(NA, length(result.columns)))) - for(i in 1:length(rf[[id.var]])) { + for (i in 1:length(rf[[id.var]])) { dat[i,] <- dat[1,] dat[i,which(result.columns == id.var)] <- rf[[id.var]][i] } @@ -195,14 +190,16 @@ simplifyFragmentData <- function(rf, id.var, vol.var = "fragvol", prefix = "frag ## NOTE: this is performed on the data, as-is: not over all possible classes as enforced by factor levels # sum volume by id and class # class cannot contain NA - rf.sums <- aggregate(rf.classes[[vol.var]], by=list(rf.classes[[id.var]], rf.classes[['class']]), FUN=sum, na.rm=TRUE) - # fix defualt names from aggregate() + rf.sums <- aggregate(rf.classes[[vol.var]], + by = list(rf.classes[[id.var]], rf.classes[['class']]), + FUN = sum, na.rm = TRUE) + # fix default names from aggregate() names(rf.sums) <- c(id.var, 'class', 'volume') ## NOTE: we set factor levels here because the reshaping (long->wide) needs to account for all possible classes ## NOTE: this must include all classes that related functions return # set levels of classes - rf.sums$class <- factor(rf.sums$class, levels=frag.classes) + rf.sums$class <- factor(rf.sums$class, levels = frag.classes) # convert to wide format if (nrow(rf.sums) == 0) { @@ -219,23 +216,19 @@ simplifyFragmentData <- function(rf, id.var, vol.var = "fragvol", prefix = "frag id.col.idx <- which(names(rf.wide) == id.var) ## optionally convert NULL frags -> 0 - if(nullFragsAreZero & ncol(rf.wide) > 1) { + if (nullFragsAreZero & ncol(rf.wide) > 1) { rf.wide <- as.data.frame( - cbind(rf.wide[, id.col.idx, drop=FALSE], + cbind(rf.wide[, id.col.idx, drop = FALSE], lapply(rf.wide[, -id.col.idx], function(i) ifelse(is.na(i), 0, i)) ), stringsAsFactors = FALSE) } - # final sanity check: are there any fractions or the total >= 100% - # note: sapply() was previously used here - # 1 row in rf.wide --> result is a vector - # >1 row in rf.wide --> result is a matrix - # solution: keep as a list + # are there any fractions or the total >= 100% gt.100 <- lapply(rf.wide[, -id.col.idx, drop = FALSE], FUN = function(i) i >= 100) # check each size fraction and report id.var if there are any - gt.100.matches <- sapply(gt.100, any, na.rm=TRUE) - if(any(gt.100.matches)) { + gt.100.matches <- sapply(gt.100, any, na.rm = TRUE) + if (any(gt.100.matches)) { # search within each fraction class.idx <- which(gt.100.matches) idx <- unique(unlist(lapply(gt.100[class.idx], which))) @@ -244,19 +237,16 @@ simplifyFragmentData <- function(rf, id.var, vol.var = "fragvol", prefix = "frag warning(sprintf("%s >= 100%%\n%s:\n%s", msg, id.var, paste(flagged.ids, collapse = "\n")), call. = FALSE) } - ## TODO: 0 is returned when all NA and nullFragsAreZero=FALSE - ## https://github.com/ncss-tech/soilDB/issues/57 # compute total fragments - # trap no frag condition - # includes unspecified class - if(ncol(rf.wide) > 1) { - # calculate another column for total RF, ignoring parafractions + if (ncol(rf.wide) > 1) { + # calculate another column for total RF, ignoring "parafractions" # index of columns to ignore, para* - idx.pf <- grep(names(rf.wide), pattern="para") + idx.pf <- grep(names(rf.wide), pattern = "para") + # also remove ID column idx <- c(id.col.idx, idx.pf) # this could result in an error if all fragments are para* - rf.wide$total_frags_pct_nopf <- rowSums(rf.wide[, -idx], na.rm=TRUE) + rf.wide$total_frags_pct_nopf <- rowSums(rf.wide[, -idx], na.rm = TRUE) # calculate total fragments (including para) # excluding ID and last columns @@ -264,12 +254,9 @@ simplifyFragmentData <- function(rf, id.var, vol.var = "fragvol", prefix = "frag rf.wide$total_frags_pct <- rowSums(rf.wide[, -idx], na.rm = TRUE) } - ## TODO: 0 is returned when all NA and nullFragsAreZero=FALSE - ## https://github.com/ncss-tech/soilDB/issues/57 - # corrections: # 1. fine gravel is a subset of gravel, therefore: gravel = gravel + fine_gravel rf.wide$gravel <- rowSums(cbind(rf.wide$gravel, rf.wide$fine_gravel), na.rm = TRUE) - rf.wide$paragravel <- rowSums(cbind(rf.wide$paragravel, rf.wide$parafine_gravel), na.rm=TRUE) + rf.wide$paragravel <- rowSums(cbind(rf.wide$paragravel, rf.wide$parafine_gravel), na.rm = TRUE) # done return(rf.wide) diff --git a/R/simplifyArtifactData.R b/R/simplifyArtifactData.R index e6b4c138e..829ac93bc 100644 --- a/R/simplifyArtifactData.R +++ b/R/simplifyArtifactData.R @@ -1,30 +1,25 @@ -## TODO: convert commentary from source material (fragments) and add manual page, thanks +# code for dealing with human artifacts; see simplifyFragmentData.R -# code for dealing with human artifacts - -.artifactSieve <- function(x, vol.var = "huartvol") { - # convert to lower case: NASIS metadata usese upper for labels, lower for values +.artifactSieve <- function(x, vol.var = "huartvol", ...) { x$huartco <- tolower(x$huartco) x$huartshp <- tolower(x$huartshp) ## assumptions - # missing hardness = rock fragment + # missing huartco = cohesive x$huartco[which(is.na(x$huartco))] <- 'cohesive' - # missing shape = Nonflat + + # missing huartshp = irregular x$huartshp[which(is.na(x$huartshp))] <- 'irregular' - ## the RV size is likely the safest estimate, - ## given the various upper bounds for GR (74mm, 75mm, 76mm) - # calculate if missing + ## the RV size is likely the safest estimate x$huartsize_r <- ifelse( is.na(x$huartsize_r), (x$huartsize_l + x$huartsize_h) / 2, x$huartsize_r ) - ## split flat/nonflat - idx <- grep('^flat', x$huartshp, ignore.case = TRUE, invert=TRUE) + idx <- grep('^flat', x$huartshp, ignore.case = TRUE, invert = TRUE) arts <- x[idx, ] idx <- grep('^flat', x$huartshp, ignore.case = TRUE) @@ -32,24 +27,14 @@ ## sieve using RV sizes # non-flat fragments - arts$class <- .sieve(arts$huartsize_r, new.names = c('art_fgr', 'art_gr', 'art_cb', - 'art_st', 'art_by')) + arts$class <- .sieve(arts$huartsize_r, new.names = c('art_fgr', 'art_gr', 'art_cb', 'art_st', 'art_by'), ...) # flat artifacts - farts$class <- .sieve(farts$huartsize_r, flat = TRUE, new.names = c('art_ch','art_fl', 'art_st', 'art_by')) + farts$class <- .sieve(farts$huartsize_r, flat = TRUE, new.names = c('art_ch','art_fl', 'art_st', 'art_by'), ...) - # combine pieces, note may contain RF classes == NA res <- rbind(arts, farts) - - # what does an NA fragment class mean? - # - # typically, fragment size missing - # or, worst-case, .sieve() rules are missing criteria - # - # keep track of these for QC in an 'unspecified' column - # but only when there is a fragment volume specified idx <- which(is.na(res$class) & !is.na(res[[vol.var]])) - if( length(idx) > 0 ) { + if (length(idx) > 0) { res$class[idx] <- 'art_unspecified' } @@ -60,14 +45,10 @@ #' @param art a \code{data.frame} object, typically returned from NASIS, see details #' @rdname simplifyFragmentData #' @export simplifyArtifactData -simplifyArtifactData <- function(art, id.var, vol.var = "huartvol", nullFragsAreZero = nullFragsAreZero) { - - huartvol <- NULL +simplifyArtifactData <- function(art, id.var, vol.var = "huartvol", nullFragsAreZero = nullFragsAreZero, ...) { # artifact size classes, using fragment breaks, are used in this function - # note that we are adding a catch-all for those strange phfrags records missing fragment size art.classes <- c('art_fgr', 'art_gr', 'art_cb', 'art_st', 'art_by', 'art_ch', 'art_fl', 'art_unspecified') - result.columns <- c(id.var, art.classes, "total_art_pct", "huartvol_cohesive", "huartvol_penetrable", "huartvol_innocuous", "huartvol_persistent") # warn the user and remove the NA records @@ -76,9 +57,9 @@ simplifyArtifactData <- function(art, id.var, vol.var = "huartvol", nullFragsAre if (nrow(art[which(!is.na(art[[vol.var]])),]) == 0) { message('NOTE: all records are missing artifact volume') dat <- as.data.frame(t(rep(NA, length(result.columns)))) - for(i in 1:length(art[[id.var]])) { - dat[i,] <- dat[1,] - dat[i,which(result.columns == id.var)] <- art[[id.var]][i] + for (i in 1:length(art[[id.var]])) { + dat[i, ] <- dat[1, ] + dat[i, which(result.columns == id.var)] <- art[[id.var]][i] } colnames(dat) <- result.columns return(dat) @@ -89,16 +70,14 @@ simplifyArtifactData <- function(art, id.var, vol.var = "huartvol", nullFragsAre # extract classes # note: these will put any fragments without fragsize into an 'unspecified' class - artifact.classes <- .artifactSieve(art, vol.var = vol.var) + artifact.classes <- .artifactSieve(art, vol.var = vol.var, ...) # sum volume by id and class - # class cannot contain NA - art.sums <- aggregate(artifact.classes[[vol.var]], by=list(artifact.classes[[id.var]], artifact.classes[['class']]), FUN=sum, na.rm=TRUE) - # fix defualt names from aggregate() + art.sums <- aggregate(artifact.classes[[vol.var]], + by = list(artifact.classes[[id.var]], artifact.classes[['class']]), + FUN = sum, na.rm = TRUE) names(art.sums) <- c(id.var, 'class', 'volume') - ## NOTE: we set factor levels here because the reshaping (long->wide) needs to account for all possible classes - ## NOTE: this must include all classes that related functions return # set levels of classes art.sums$class <- factor(art.sums$class, levels = art.classes) @@ -110,23 +89,17 @@ simplifyArtifactData <- function(art, id.var, vol.var = "huartvol", nullFragsAre id.col.idx <- which(names(art.wide) == id.var) ## optionally convert NULL frags -> 0 - if(nullFragsAreZero & ncol(art.wide) > 1) { + if (nullFragsAreZero & ncol(art.wide) > 1) { art.wide <- as.data.frame( - cbind(art.wide[, id.col.idx, drop=FALSE], + cbind(art.wide[, id.col.idx, drop = FALSE], lapply(art.wide[, -id.col.idx], function(i) ifelse(is.na(i), 0, i)) - ), stringsAsFactors=FALSE) + ), stringsAsFactors = FALSE) } - - # final sanity check: are there any fractions or the total >= 100% - # note: sapply() was previously used here - # 1 row in rf.wide --> result is a vector - # >1 row in rf.wide --> result is a matrix - # solution: keep as a list - gt.100 <- lapply(art.wide[, -id.col.idx, drop=FALSE], FUN=function(i) i >= 100) - - # check each size fraction and report id.var if there are any - gt.100.matches <- sapply(gt.100, any, na.rm=TRUE) - if(any(gt.100.matches)) { + + # are there any fractions or the total >= 100% + gt.100 <- lapply(art.wide[, -id.col.idx, drop = FALSE], FUN = function(i) i >= 100) + gt.100.matches <- sapply(gt.100, any, na.rm = TRUE) + if (any(gt.100.matches)) { # search within each fraction class.idx <- which(gt.100.matches) idx <- unique(unlist(lapply(gt.100[class.idx], which))) @@ -135,11 +108,7 @@ simplifyArtifactData <- function(art, id.var, vol.var = "huartvol", nullFragsAre warning(sprintf("artifact volume >= 100%%\n%s:\n%s", id.var, paste(flagged.ids, collapse = "\n")), call. = FALSE) } - ## TODO: 0 is returned when all NA and nullFragsAreZero=FALSE - ## https://github.com/ncss-tech/soilDB/issues/57 - # compute total fragments - # trap no frag condition - # includes unspecified class + # compute total artifacts if (ncol(art.wide) > 1) { # calculate another column for total RF, ignoring parafractions # index of columns to ignore, para* @@ -147,11 +116,9 @@ simplifyArtifactData <- function(art, id.var, vol.var = "huartvol", nullFragsAre # also remove ID column idx <- c(id.col.idx)#, idx.pf) # this could result in an error if all fragments are para* - art.wide$total_art_pct <- rowSums(art.wide[, -idx], na.rm=TRUE) + art.wide$total_art_pct <- rowSums(art.wide[, -idx], na.rm = TRUE) } - - ## TODO: 0 is returned when all NA and nullFragsAreZero=FALSE - ## https://github.com/ncss-tech/soilDB/issues/57 + # corrections: # 1. fine gravel is a subset of gravel, therefore: gravel = gravel + fine_gravel art.wide$art_gr <- rowSums(cbind(art.wide$art_gr, art.wide$art_fgr), na.rm = TRUE) @@ -161,12 +128,15 @@ simplifyArtifactData <- function(art, id.var, vol.var = "huartvol", nullFragsAre art.wide$huartvol_cohesive <- as.numeric(lapply(split(art, art[[id.var]]), function(art.sub) { sum(art.sub[[vol.var]][art.sub$huartco == "cohesive"], na.rm = TRUE) })) + art.wide$huartvol_penetrable <- as.numeric(lapply(split(art, art[[id.var]]), function(art.sub) { sum(art.sub[[vol.var]][art.sub$huartpen == "penetrable"], na.rm = TRUE) })) + art.wide$huartvol_noxious <- as.numeric(lapply(split(art, art[[id.var]]), function(art.sub) { sum(art.sub[[vol.var]][art.sub$huartsafety == "noxious artifacts"], na.rm = TRUE) })) + art.wide$huartvol_persistent <- as.numeric(lapply(split(art, art[[id.var]]), function(art.sub) { sum(art.sub[[vol.var]][art.sub$huartper == "persistent"], na.rm = TRUE) })) diff --git a/data-raw/NASIS_SoilProfileCollections.R b/data-raw/NASIS_SoilProfileCollections.R index 7605792f2..9fa9491ee 100644 --- a/data-raw/NASIS_SoilProfileCollections.R +++ b/data-raw/NASIS_SoilProfileCollections.R @@ -10,13 +10,13 @@ data("mineralKing", package = "soilDB") # # create CSVs (requires NASIS setup) # # query CA630 and CA792 w/ R08 PEDON/SITE by SSA ID or similar # # load source data sets (CA630 and CA792 pedons) -# nasis_pedons <- fetchNASIS(rmHzErrors = FALSE, SS = FALSE) -# -# p <- rebuildSPC(subset(nasis_pedons, siteiid %in% as.double(c(loafercreek, gopheridge, mineralKing)$siteiid))) -# write.csv(horizons(p), "data-raw/spc-horizons.csv", row.names = FALSE) -# write.csv(site(p), "data-raw/spc-site.csv", row.names = FALSE) -# write.csv(diagnostic_hz(p), "data-raw/spc-diagnostic_hz.csv", row.names = FALSE) -# write.csv(restrictions(p), "data-raw/spc-restrictions.csv", row.names = FALSE) +nasis_pedons <- fetchNASIS(rmHzErrors = FALSE, SS = FALSE) + +p <- rebuildSPC(subset(nasis_pedons, siteiid %in% as.double(c(loafercreek, gopheridge, mineralKing)$siteiid))) +write.csv(horizons(p), "data-raw/spc-horizons.csv", row.names = FALSE) +write.csv(site(p), "data-raw/spc-site.csv", row.names = FALSE) +write.csv(diagnostic_hz(p), "data-raw/spc-diagnostic_hz.csv", row.names = FALSE) +write.csv(restrictions(p), "data-raw/spc-restrictions.csv", row.names = FALSE) recent1822a <- read.csv("data-raw/spc-horizons.csv") depths(recent1822a) <- peiid ~ hzdept + hzdepb diff --git a/data-raw/spc-horizons.csv b/data-raw/spc-horizons.csv index 6391d992b..1dd95a117 100644 --- a/data-raw/spc-horizons.csv +++ b/data-raw/spc-horizons.csv @@ -860,7 +860,7 @@ "2977026","640607","Bt1","Bt1",6,39,"clear","smooth",17,49,34,10,"L","l",NA,NA,"none",NA,"slightly hard","friable",NA,"slightly sticky","slightly plastic",NA,"l","12533",NA,NA,NA,NA,NA,NA,NA,0.488397126185639,0.349362062820934,0.229243737407669,"7.5YR",4,4,NA,"#7D593A",NA,"#7D593A",0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,0,0,0,0,0,0,0,0,0,0,0,0,0 "2977027","640607","Bt2","Bt2",39,54,"clear","wavy",21,44,35,40,"GR-L","l",NA,NA,"none",NA,"slightly hard","friable",NA,"moderately sticky","moderately plastic",NA,"l","12534",NA,NA,NA,NA,NA,NA,NA,0.634523149830161,0.438851858628477,0.247247510397254,"7.5YR",5,6,NA,"#A2703F",NA,"#A2703F",0,20,10,0,0,0,0,0,10,0,0,0,0,0,0,30,40,0,0,0,0,0,0,0,0,0,0,0,0,0 "2977028","640607","Cr","Cr",54,79,NA,NA,NA,NA,NA,0,"BR",NA,"br",NA,NA,NA,NA,NA,NA,NA,NA,NA,"br","12535",NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -"2977037","640610","A","A",0,5,"clear","smooth",14,40,46,5,"L","l",NA,NA,"none",NA,"soft","friable",NA,"slightly sticky","slightly plastic",NA,"l","12544",NA,NA,NA,NA,NA,NA,NA,0.337643309877762,0.264793407575455,0.213042579872497,"7.5YR",3,2,NA,"#564436",NA,"#564436",0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0 +"2977037","640610","A","A",0,5,"clear","smooth",14,40,46,5,"L","l",NA,NA,"none",NA,"soft","friable",NA,"slightly sticky","slightly plastic",NA,"l","12544",NA,NA,NA,NA,NA,NA,NA,0.337643309877762,0.264793407575455,0.213042579872497,"7.5YR",3,2,NA,"#564436",NA,"#564436",0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0 "2977038","640610","Bt1","Bt1",5,34,"clear","smooth",18,40,42,5,"L","l",NA,NA,"none",NA,"slightly hard","friable",NA,"slightly sticky","slightly plastic",NA,"l","12545",NA,NA,NA,NA,NA,NA,NA,0.358300526259172,0.258532492699209,0.181154944486718,"7.5YR",3,3,NA,"#5B422E",NA,"#5B422E",0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0 "2977039","640610","Bt2","Bt2",34,49,"clear","smooth",23,36,41,10,"L","l",NA,NA,"none",NA,"slightly hard","firm",NA,"moderately sticky","moderately plastic",NA,"l","12546",NA,NA,NA,NA,NA,NA,NA,0.358300526259172,0.258532492699209,0.181154944486718,"7.5YR",3,3,NA,"#5B422E",NA,"#5B422E",0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,0,0,0,0,0,0,0,0,0,0,0,0,0 "2977040","640610","Bt3","Bt2",49,89,"abrupt","smooth",28,34,38,0,"CL","cl",NA,NA,"none",NA,"moderately hard","firm",NA,"moderately sticky","moderately plastic",NA,"cl","12547",NA,NA,NA,NA,NA,NA,NA,0.467514082120659,0.355962289511748,0.265795247113725,"7.5YR",4,3,NA,"#775B44",NA,"#775B44",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 diff --git a/data/loafercreek.rda b/data/loafercreek.rda index 19a2fa3522118a244d4a8a33cea791ae549014c1..8fa203c61e1088ac276cfc920c8e8f0b04cb197f 100644 GIT binary patch delta 13020 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 delta 13012 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