1 Introduction

Soil Taxonomy is a theoretical framework for organizing soil information, typically at scales coarser than most land management decisions. Soil series are a practical (but limited in geographic scope) framework for organizing soil/landscape combinations close to the scale of land management decisions.

So, what is the practical manifestation of a single subgroup from Soil Taxonomy (abruptic durixeralfs), in terms of soil series concepts?

The following code, links, and notes may serve as a starting point for those interested in learning more about Soil Taxonomy. This is a work in progress.

# AQP suite, be sure to use the development versions from GitHub
library(aqp)
library(soilDB)
library(sharpshootR)

# used for visualization
library(latticeExtra)
library(tactile)
library(cluster)
library(ape)
library(FactoMineR)

# mapping a subgroup
library(sf)
library(terra)
library(spData) 
library(rasterVis)
library(viridisLite)

# subgroup "explainer"
library(SoilTaxonomy)

library(data.tree)

2 Examples

Get a snapshot of the Soil Classification database. This contains the current classification for all soil series.

# latest version, synced quarterly
u <- 'https://github.com/ncss-tech/SoilWeb-data/raw/main/files/SC-database.csv.gz'
tf <- tempfile()
download.file(u, destfile = tf)
SC <- read.csv(tf, stringsAsFactors = FALSE)

Identify all of the soil series associated with a specific subgroup taxa: abruptic durixeralfs and download basic morphology and climate summaries via fetchOSD.

# select series names for a single subgroup
SC <- SC[which(SC$taxsubgrp == 'abruptic durixeralfs'), ]
s <- SC$soilseriesname

# get OSD morphology and extended summaries
osd <- fetchOSD(soils = s, extended = TRUE)

2.1 ST Explainer

Explain subgroup taxa via SoilTaxonomy package.

cat(explainST('abruptic durixeralfs'))
abruptic durixeralfs
|        |   |  |                                                                                   
abrupt textural change                                                                              
         |   |  |                                                                                   
         presence of a duripan                                                                      
             |  |                                                                                   
             xeric soil moisture regime                                                             
                |                                                                                   
                soils with an argillic, kandic, or natric horizon                                   

2.2 ST Hierarchy to the Subgroup Level

Automatic to the subgroup, via SoilTaxonomy package.

taxonTree(c('durixeralfs', 'rhodoxeralfs'), special.chars = c("\u251c","\u2502", "\u2514", "\u2500 "))
Soil Taxonomy                              
 └─ alfisols                               
     └─ xeralfs                            
         ├─ durixeralfs                    
         │   ├─ natric durixeralfs         
         │   ├─ vertic durixeralfs         
         │   ├─ aquic durixeralfs          
         │   ├─ abruptic haplic durixeralfs
         │   ├─ abruptic durixeralfs       
         │   ├─ haplic durixeralfs         
         │   └─ typic durixeralfs          
         └─ rhodoxeralfs                   
             ├─ lithic rhodoxeralfs        
             ├─ vertic rhodoxeralfs        
             ├─ petrocalcic rhodoxeralfs   
             ├─ calcic rhodoxeralfs        
             ├─ inceptic rhodoxeralfs      
             └─ typic rhodoxeralfs         

2.3 ST Hierarchy with Family and Series

We have to do this manually.

# subset SC database to required columns
SC <- SC[, c('soilseriesname', 'taxorder', 'taxsuborder', 'taxgrtgroup', 'taxsubgrp', 'taxclname')]

# normalization via lower case
SC$taxclname <- tolower(SC$taxclname)
SC$soilseriesname <- tolower(SC$soilseriesname)

# remove subgroup component of family spec
SC$f <- NA_character_
for(i in 1:nrow(SC)) {
  SC$f[i] <- gsub(pattern = SC$taxsubgrp[i], replacement = '', SC$taxclname[i], fixed = TRUE)
}

# remove white space
SC$f <- trimws(SC$f, which = 'both')

# required columns only, smaller data.tree
v <- c('taxorder', 'taxsuborder', 'taxgrtgroup', 'taxsubgrp', 'f', 'soilseriesname', 'path')

# init data.tree object
SC$path <- sprintf("ST/%s/%s/%s/%s/%s/%s", SC$taxorder, SC$taxsuborder, SC$taxgrtgroup, SC$taxsubgrp, SC$f, SC$soilseriesname)
n <- as.Node(SC[, v], pathName = 'path')

## missing family / series result in an ugly tree, prune accordingly
# prune missing family / series
pf <- function(i) {
  
  # NA due to left join
  # note odd approach required, matching to 'NA' vs. is.na()
  if(GetAttribute(i, 'name') == 'NA') {
    return(FALSE)
  } else {
    return(TRUE)
  }
  
}

## print
# print(n, limit = NULL, pruneFun = pf)
                                                     levelName
1  ST                                                         
2   °--alfisols                                               
3       °--xeralfs                                            
4           °--durixeralfs                                    
5               °--abruptic durixeralfs                       
6                   ¦--fine-loamy, mixed, superactive, thermic
7                   ¦   °--bellota                            
8                   ¦--ashy, glassy, frigid                   
9                   ¦   °--borealis                           
10                  ¦--fine, kaolinitic, thermic              
11                  ¦   °--chesterton                         
12                  ¦--clayey-skeletal, kaolinitic, thermic   
13                  ¦   °--clough                             
14                  ¦--very-fine, smectitic, frigid           
15                  ¦   °--deunah                             
16                  ¦--fine, mixed, active, thermic           
17                  ¦   ¦--eastbiggs                          
18                  ¦   ¦--redding                            
19                  ¦   ¦--san joaquin                        
20                  ¦   °--yuvas                              
21                  ¦--fine, smectitic, mesic                 
22                  ¦   °--fairylawn                          
23                  ¦--clayey, smectitic, frigid, shallow     
24                  ¦   ¦--furshur                            
25                  ¦   ¦--nicholflat                         
26                  ¦   °--ponina                             
27                  ¦--fine, illitic, thermic                 
28                  ¦   °--gloria                             
29                  ¦--clayey, mixed, active, thermic, shallow
30                  ¦   ¦--keyes                              
31                  ¦   °--vistarobles                        
32                  ¦--fine, smectitic, thermic               
33                  ¦   °--madera                             
34                  ¦--fine, vermiculitic, thermic            
35                  ¦   °--moda                               
36                  ¦--fine, smectitic, frigid                
37                  ¦   °--thacker                            
38                  °--fine-loamy, mixed, active, thermic     
39                      °--thermalito                         

2.4 SoilWeb seriesTree Application

seriesTree figure Link to SoilWeb subgroup taxa tree for abruptic durixeralfs.

2.5 Taxa Extent Maps

# get 800m extent map
e <- taxaExtent('abruptic durixeralfs', level = 'subgroup')

# aggregate via focal mean using 5x5 moving window
# visualization purposes only
a <- terra::aggregate(e, fact = 5, fun = mean, na.rm = TRUE)

data("us_states")
us_states <- st_transform(us_states, 5070)
us_states <- st_crop(us_states, e)

# simple figure
plot(a, axes = FALSE, col = mako(50), mar = c(3, 1, 3, 3))
plot(st_geometry(us_states), add = TRUE)
title('Abruptic Durixeralfs')
mtext('pixel values represent percent of area\nwithin 800m grid cells', side = 1, line = 1.5)

2.6 Data via soilDB::fetchOSD

Cluster series based on annual climate summaries but don’t plot it yet.

# control centers symbol and size here
res <- vizAnnualClimate(osd$climate.annual, s = 'SAN JOAQUIN', IQR.cex = 1.1, cex=1.1, pch=18)

Series associated with abruptic durixeralfs subgroup, arranged according to annual climate summaries.

par(mar = c(0,0,1,2))
plotProfileDendrogram(osd$SPC, clust = res$clust, scaling.factor = 0.075, width = 0.25, y.offset = 0.5, name.style = 'center-center', shrink = TRUE, max.depth = 150, depth.axis = list(cex = 0.8))
mtext('Abruptic Durixeralfs', side = 1, at = 0.5, adj = 0, line = -1.5, font=4)
mtext('sorted by annual climate summaries', side = 3, at = 0.5, adj = 0, line = -1.5, font = 1, cex = 1.1)