library(aqp)
library(soilDB)
library(sf)
library(sharpshootR)
library(SoilTaxonomy)
## possible AOIs defined using a bounding-box via SoilWeb
# WI: many ties
bb <- '-97.0983 39.3808,-97.0983 39.4127,-97.0282 39.4127,-97.0282 39.3808,-97.0983 39.3808'
# KS069
bb <- '-100.5534 37.9177,-100.5534 37.9822,-100.4389 37.9822,-100.4389 37.9177,-100.5534 37.9177'
## assemble AOI polygon into WKT
wkt <- sprintf('POLYGON((%s))', bb)
## init sf polygon
# GCS WGS84
x <- st_as_sfc(wkt, crs = 4326)
## get overlapping map unit keys
# could also use SDA_query() with more elaborate SQL
m <- SDA_spatialQuery(x, what = 'mukey')
## compose SQL to return component details for these map unit keys
# return only:
# * map units overlapping with BBOX
# * major components
# * no misc. areas that might share name with a poorly-named soil series
sql <- sprintf(
"SELECT mukey, cokey, compname, compkind, comppct_r
FROM component
WHERE mukey IN %s
-- AND majcompflag = 'Yes'
AND compkind != 'Miscellaneous area'
", format_SQL_in_statement(as.integer(m$mukey))
)
## send to SDA, result is a data.frame
s <- SDA_query(sql)
## get OSD morphology + extended summaries
osd <- fetchOSD(unique(s$compname), extended = TRUE)
## check out results
str(osd, 1)
## List of 18
## $ SPC :Formal class 'SoilProfileCollection' [package "aqp"] with 8 slots
## $ competing :'data.frame': 87 obs. of 3 variables:
## $ geog_assoc_soils:'data.frame': 74 obs. of 2 variables:
## $ geomcomp :'data.frame': 14 obs. of 9 variables:
## $ hillpos :'data.frame': 14 obs. of 8 variables:
## $ mtnpos : logi FALSE
## $ terrace :'data.frame': 4 obs. of 5 variables:
## $ flats :'data.frame': 7 obs. of 7 variables:
## $ shape_across :'data.frame': 14 obs. of 8 variables:
## $ shape_down :'data.frame': 14 obs. of 8 variables:
## $ pmkind :'data.frame': 32 obs. of 5 variables:
## $ pmorigin :'data.frame': 5 obs. of 5 variables:
## $ mlra :'data.frame': 107 obs. of 4 variables:
## $ ecoclassid :'data.frame': 67 obs. of 5 variables:
## $ climate.annual :'data.frame': 112 obs. of 12 variables:
## $ climate.monthly :'data.frame': 336 obs. of 14 variables:
## $ NCCPI :'data.frame': 14 obs. of 16 variables:
## $ soilweb.metadata:'data.frame': 23 obs. of 2 variables:
# single iteration of hydrologic ordering
h1 <- hydOrder(osd$shape_across, g = 'shape', clust = TRUE)
# return trace log for eval of objective function
# increase max iterations
h2 <- iterateHydOrder(osd$shape_across, 'shape', maxIter = 100, verbose = TRUE, trace = TRUE)
# compare
h1$match.rate
## [1] 1
h2$match.rate
## [1] 1
# inspect objective function evolution
tr <- h2$trace
obj <- sapply(tr, '[[', 'obj')
hist(obj, las = 1, xlab = 'Objective Function\nLower is Better')
match.rate <- sapply(tr, '[[', 'match.rate')
hist(match.rate, las = 1, xlab = 'Matching Rate')
Note that profiles deeper than 180cm are marked as truncated via ragged bottoms.
# provide additional arguments to aqp::plotSPC() via options
options(.aqp.plotSPC.args = list(max.depth = 190))
par(mar = c(1, 0, 1, 2))
plotGeomorphCrossSection(osd, type = 'line', maxIter = 100, j.amount = 0.05, verbose = TRUE)
plotGeomorphCrossSection(osd, type = 'bar', maxIter = 100, j.amount = 0.05, verbose = TRUE)
par(mar = c(1, 0, 1, 2))
plotGeomorphCrossSection(osd, type = 'line', clust = FALSE)
plotGeomorphCrossSection(osd, type = 'bar', clust = FALSE)
o <- reconcileOSDGeomorph(osd, 'hillpos')
res <- vizHillslopePosition(o$geom, verbose = TRUE)
print(res$fig)
par(mar = c(1, 0, 1, 1))
idx <- match(hydOrder(o$geom, g = 'hillpos', clust = FALSE), profile_id(o$SPC))
plotSPC(o$SPC, plot.order = idx, width = 0.35, name.style = 'center-center', cex.names = 0.65)
o <- reconcileOSDGeomorph(osd, 'geomcomp')
res <- vizGeomorphicComponent(o$geom, verbose = TRUE)
print(res$fig)
par(mar = c(1, 0, 1, 1))
idx <- match(hydOrder(o$geom, g = 'geomcomp', clust = FALSE), profile_id(o$SPC))
plotSPC(o$SPC, plot.order = idx, width = 0.35, name.style = 'center-center', cex.names = 0.65)
o <- reconcileOSDGeomorph(osd, 'flats')
res <- vizFlatsPosition(o$geom, verbose = TRUE)
print(res$fig)
par(mar = c(1, 0, 1, 2))
idx <- match(hydOrder(o$geom, g = 'flats', clust = FALSE), profile_id(o$SPC))
plotSPC(o$SPC, plot.order = idx, width = 0.35, name.style = 'center-center', cex.names = 0.65)
o <- reconcileOSDGeomorph(osd, 'terrace')
res <- vizTerracePosition(o$geom, verbose = TRUE)
print(res$fig)
par(mar = c(1, 0, 1, 1))
idx <- match(hydOrder(o$geom, g = 'terrace', clust = FALSE), profile_id(o$SPC))
plotSPC(o$SPC, plot.order = idx, width = 0.35, name.style = 'center-center', cex.names = 0.65)
o <- reconcileOSDGeomorph(osd, 'mtnpos')
res <- vizMountainPosition(o$geom, verbose = TRUE)
print(res$fig)
par(mar = c(1, 0, 1, 0))
idx <- match(hydOrder(o$geom, g = 'mtnpos', clust = FALSE), profile_id(o$SPC))
plotSPC(o$SPC, plot.order = idx, width = 0.35, name.style = 'center-center', cex.names = 0.65)
o <- reconcileOSDGeomorph(osd, 'shape_across')
res <- vizSurfaceShape(o$geom, title = 'Shape Across', verbose = TRUE)
print(res$fig)
par(mar = c(1, 0, 1, 1))
idx <- match(hydOrder(o$geom, g = 'shape', clust = FALSE), profile_id(o$SPC))
plotSPC(o$SPC, plot.order = idx, width = 0.35, name.style = 'center-center', cex.names = 0.65)
o <- reconcileOSDGeomorph(osd, 'shape_down')
res <- vizSurfaceShape(o$geom, title = 'Shape Down', verbose = TRUE)
print(res$fig)
par(mar = c(1, 0, 1, 1))
idx <- match(hydOrder(o$geom, g = 'shape', clust = FALSE), profile_id(o$SPC))
plotSPC(o$SPC, plot.order = idx, width = 0.35, name.style = 'center-center', cex.names = 0.65)
# use local data
data("OSDexamples")
o <- reconcileOSDGeomorph(OSDexamples, 'hillpos')
knitr::kable(o$geom, digits = 3)
series | Toeslope | Footslope | Backslope | Shoulder | Summit | n | shannon_entropy |
---|---|---|---|---|---|---|---|
AMADOR | 0.000 | 0.000 | 0.667 | 0.111 | 0.222 | 36 | 1.224 |
ARGONAUT | 0.062 | 0.125 | 0.458 | 0.312 | 0.042 | 48 | 1.856 |
CECIL | 0.000 | 0.000 | 0.307 | 0.355 | 0.338 | 1027 | 1.582 |
DRUMMER | 0.986 | 0.012 | 0.000 | 0.000 | 0.002 | 1617 | 0.116 |
HANFORD | 0.517 | 0.329 | 0.154 | 0.000 | 0.000 | 149 | 1.436 |
MOGLIA | 0.000 | 0.195 | 0.268 | 0.293 | 0.244 | 41 | 1.985 |
MUSICK | 0.000 | 0.000 | 0.753 | 0.206 | 0.041 | 73 | 0.966 |
PALAU | 0.200 | 0.200 | 0.200 | 0.200 | 0.200 | 100 | 2.322 |
PARDEE | 0.058 | 0.000 | 0.192 | 0.212 | 0.538 | 52 | 1.650 |
PENTZ | 0.013 | 0.000 | 0.625 | 0.112 | 0.250 | 80 | 1.357 |
REDDING | 0.176 | 0.010 | 0.157 | 0.029 | 0.627 | 102 | 1.498 |
SIERRA | 0.000 | 0.018 | 0.802 | 0.108 | 0.072 | 111 | 0.980 |
SYCAMORE | 1.000 | 0.000 | 0.000 | 0.000 | 0.000 | 17 | 0.000 |
VLECK | 0.000 | 1.000 | 0.000 | 0.000 | 0.000 | 2 | 0.000 |
WILLOWS | 1.000 | 0.000 | 0.000 | 0.000 | 0.000 | 47 | 0.000 |
YOLO | 0.593 | 0.322 | 0.085 | 0.000 | 0.000 | 59 | 1.275 |
ZOOK | 0.986 | 0.015 | 0.000 | 0.000 | 0.000 | 482 | 0.109 |
res <- vizHillslopePosition(o$geom, verbose = TRUE)
print(res$fig)
# truncate profiles at 180cm
options(.aqp.plotSPC.args = list(max.depth = 180))
par(mar = c(1, 0, 1, 2))
plotGeomorphCrossSection(OSDexamples, type = 'line', maxIter = 100, j.amount = 0.05, verbose = TRUE)
par(mar = c(1, 0, 1, 2))
plotGeomorphCrossSection(OSDexamples, type = 'line', clust = FALSE)
## TODO: ensure geomorph data + type aren't mixed up
hydOrder(osd$hillpos, g = 'hillpos', clust = FALSE)
## [1] "SPEARVILLE" "HARNEY" "RICHFIELD" "ULYSSES" "MANSKER" "ULY"
## [7] "COLY" "PENDEN" "BUFFALO PARK" "DUROC" "NESS" "LODGEPOLE"
## [13] "FETERITA" "PLEASANT"
hydOrder(osd$hillpos, g = 'hillpos', clust = TRUE)
## $clust
##
## Call:
## diana(x = daisy(x.prop, type = list(numeric = 1:n.prop)))
##
## Cluster method : NA
## Distance : euclidean
## Number of objects: 14
##
##
## $hyd.order
## [1] "SPEARVILLE" "HARNEY" "RICHFIELD" "ULYSSES" "MANSKER" "ULY"
## [7] "COLY" "PENDEN" "BUFFALO PARK" "DUROC" "NESS" "LODGEPOLE"
## [13] "FETERITA" "PLEASANT"
##
## $clust.hyd.order
## [1] "SPEARVILLE" "HARNEY" "RICHFIELD" "DUROC" "ULYSSES" "MANSKER"
## [7] "ULY" "COLY" "PENDEN" "BUFFALO PARK" "LODGEPOLE" "NESS"
## [13] "FETERITA" "PLEASANT"
##
## $match.rate
## [1] 0.3571429
##
## $obj
## [1] 452
hydOrder(osd$flats, g = 'flats', clust = FALSE)
## [1] "MANSKER" "ULYSSES" "RICHFIELD" "FETERITA" "DUROC" "LODGEPOLE" "NESS"
Reset options.
options(.aqp.plotSPC.args = NULL)
This document is based on aqp
version 2.2,
soilDB
version 2.8.8, and sharpshootR
version
2.3.3.