ncss-tech / aqp

Algorithms for Quantitative Pedology
http://ncss-tech.github.io/aqp/
54 stars 15 forks source link

plotSPC: missing horizon boundaries w/ missing data #189

Closed brownag closed 1 year ago

brownag commented 3 years ago

Here is a minimal reproducible example of what happens in plotSPC if a horizon is missing.

library(aqp, warn.conflicts = FALSE)
#> This is aqp 1.27
library(daff)
#> Warning: package 'daff' was built under R version 4.0.3

data(jacobs2000)

p <- jacobs2000[4,]
h <- horizons(p)

# use glom to create some fake missing data (REMOVE 50-100cm zone)
input.spc <- glom(p, 50, 100, truncate = TRUE, invert = TRUE)

daff::diff_data(h, horizons(input.spc))
#> Daff Comparison: 'h' vs. 'horizons(input.spc)' 
#>     id   name sand clay matrix_color time_saturated top     bottom
#> ->  92-4 Ap   88   2    #83796FFF    0              0       20    
#> ->  92-4 C1   93   3    #AE9069FF    0              20      53->50
#> --- 92-4 C2   93   2    #C9AB81FF    0              53      79    
#> ->  92-4 C3   93   3    #C9AB81FF    1              79->100 130   
#> ->  92-4 Cg1  94   0    #D2C9BDFF    18             130     165   
#> ->  92-4 Cg2  96   1    #D6C9AEFF    41             165     185   
#> ->  92-4 2C   83   3    #BEAD98FF    61             185     203   
#>     matrix_color_munsell ... concentration_pct hzID 
#> ->  10YR 5/1             ... 0                 21->1
#> ->  10YR 6/4             ... 0                 22->2
#> --- 10YR 7/4             ... 2                 23   
#> ->  10YR 7/4             ... 15                24->3
#> ->  10YR 8/1             ... 5                 25->4
#> ->  2.5Y 8/2             ... 5                 26->5
#> ->  10YR 7/2             ... 2                 27->6

checkHzDepthLogic(input.spc)
#>     id valid depthLogic sameDepth missingDepth overlapOrGap
#> 1 92-4 FALSE      FALSE     FALSE        FALSE         TRUE
# inspect on white -- difference in sRGB color of image is extremely subtle w/ no horizon boundary
#  - no apparent boundary at 100cm
par(mfrow = c(1, 2), mar = c(0, 0, 2, 1))
plotSPC(p, max.depth = 225, plot.depth.axis = FALSE)
plotSPC(input.spc, max.depth = 225)

# inspect w/ filled rect - difference is clear and that there is no horizon boundary
#  - truncated upper boundary of C3 at 100cm apparent
par(mfrow = c(1, 2), mar = c(0, 0, 2, 1))
plotSPC(p, max.depth = 225, plot.depth.axis = FALSE, color = "matrix_color")
plotSPC(input.spc, max.depth = 225, color = "matrix_color")

dylanbeaudette commented 3 years ago

That is strange! I'll have to investigate further, thanks for making the reprex.

dylanbeaudette commented 3 years ago

Most definitely related, but not addressed by the following bug/fix. Both have to do with indexing of adjacent horizon depths when building polygon fill/outlines. I'll investigate further.

"single-horizon" glitch:

library(aqp)

x <- data.frame(
  id = 'A',
  top = 0,
  bottom = 25,
  clay = 15,
  stringsAsFactors = FALSE
)

depths(x) <- id ~ top + bottom

plotSPC(x, color = 'clay')

image

fixed in 621df647d2af6dd3231f810a45d5e414bebd63b9

dylanbeaudette commented 3 years ago

More useful "data" in the http://ncss-tech.github.io/AQP/aqp/dealing-with-bad-data.html tutorial. This is likely due to the use of pmin and pmax in the presence of NA.

dylanbeaudette commented 3 years ago

Not a good example (and not fixing), severe horizon logic errors:

      y1  y0
 [1,]   0   5
 [2,]   5  12
 [3,]  12  21
 [4,]  21  47
 [5,]  47  71
 [6,]  71 109
 [7,] 109 131
 [8,] 131 172
 [9,] 135 150
[10,] 172 210
library(aqp)
library(soilDB)
# pedons indexed by "pedlabsampnum" ID
pls <- c("04N0610", "04N0611", "04N0612", "04N0613")

# vectorization = implicit iteration
res <- fetchKSSL(pedlabsampnum = pls, returnMorphologicData = TRUE, simplifyColors = TRUE)

# check: OK
str(res, 2)

# extract SPC
pedons <- res$SPC

# check: looks good
plotSPC(pedons, color='estimated_ph_h2o', name='hzn_desgn')

# note that `32427` contains overlapOrGap
checkHzDepthLogic(pedons)

image

dylanbeaudette commented 3 years ago

Another example. 2/3 fixes are in place and now part of #199. Still need a reasonable solution to the missing "top" boundary in the presence of NA.

library(aqp)

data(sp4)
depths(sp4) <- id ~ top + bottom

par(mar = c(0, 0, 0, 0))
plotSPC(sp4, width = 0.3)

# copy to break
x <- sp4

## legal (deepest) horizons to repair:
# introduce NA
x$bottom[4] <- NA
# top == bottom
x$bottom[6] <- x$top[6]

## not legal horizons to repair
x$bottom[12] <- NA

# marked as invalid
checkHzDepthLogic(x)

plotSPC(x, width = 0.3)

image

dylanbeaudette commented 1 year ago

Closing this, as errors in graphical output assist with identification of bad data.