VNIR Spectra

library(aqp)
library(soilDB)
library(reshape2)
library(lattice)
library(tactile)

## gather some example spectra
x <- fetchRaCA('redding', get.vnir = TRUE)
x <- as.data.frame(x$spectra[1:pmin(25, nrow(x$spectra)), ])

y <- fetchRaCA('zook', get.vnir = TRUE)
y <- as.data.frame(y$spectra[1:pmin(25, nrow(y$spectra)), ])

z <- fetchRaCA('drummer', get.vnir = TRUE)
z <- as.data.frame(z$spectra[1:pmin(25, nrow(z$spectra)), ])

# combine
x <- rbind(x, y, z)

# add a temporary ID
x$.id <- 1:nrow(x)

# long format simpler to work with
m <- melt(x, id.vars = '.id')

# remove leading 'X' from wavelength
m$variable <- as.character(m$variable)
m$v <- as.numeric(gsub(pattern = 'X', replacement = '', x = m$variable))

# remove left-over columns
m$variable <- NULL

# check: ok
head(m)
##   .id    value   v
## 1   1 0.195590 350
## 2   2 0.126400 350
## 3   3 0.137835 350
## 4   4 0.157308 350
## 5   5 0.150098 350
## 6   6 0.185702 350
# subset to visible part of the spectrum that spec2Munsell can use
m.sub <- m[which(m$v >= 380 & m$v <= 730), ]

# "round" 1nm -> 10nm resolution
m.sub$v10 <- round(m.sub$v, -1)

# check: ok
head(m.sub)
##      .id    value   v v10
## 2251   1 0.162216 380 380
## 2252   2 0.082300 380 380
## 2253   3 0.096800 380 380
## 2254   4 0.120641 380 380
## 2255   5 0.109728 380 380
## 2256   6 0.149557 380 380
# aggregate to 10nm res via mean
a <- aggregate(value ~ .id + v10, data = m.sub, FUN = mean)

# factor
a$.id <- factor(a$.id)

# check: ok
head(a)
##   .id v10      value
## 1   1 380 0.16242617
## 2   2 380 0.08151667
## 3   3 380 0.09708333
## 4   4 380 0.11996617
## 5   5 380 0.10874017
## 6   6 380 0.14964150
z <- split(a, a$.id)

# TODO: may need some error handling of NA colors
z <- lapply(z, function(i) {
  # assumes 380--730nm at 10nm resolution
  sRGB <- spec2Munsell(i$value, convert = FALSE)
  # pack into df
  res <- data.frame(
    .id = i$.id[1],
    col = rgb(sRGB),
    stringsAsFactors = FALSE
  )
  #
  return(res)
})

z <- do.call('rbind', z)

###

## big assumption: colors are in the same order as levels of .id

# plot spectra
tps <- tactile.theme(
  superpose.line = list(lwd = 2, col = z$col)
)


# annotate with visible portion of the spectrum + colors
xyplot(
  value ~ v, 
  groups = .id, 
  data = m, 
  type = 'l', 
  par.settings = tps, 
  scales = list(x = list(tick.number = 20), y = list(tick.number = 10)), 
  xlab = 'Wavelength (nm)', 
  ylab = 'Reflectance', 
  panel = function(x, y, ...) {
    panel.grid(-1, -1)
    panel.abline(v = c(380, 730), lty = 2)
    panel.xyplot(x = x, y = y, ...)
    
    # position via panel geometry
    yb <- current.panel.limits()$ylim[1]
    yt <- yb + 0.01
    panel.rect(xleft = 380, xright = 435, ybottom = yb, ytop = yt, border = 'violet', col = 'violet')
    panel.rect(xleft = 435, xright = 500, ybottom = yb, ytop = yt, border = 'blue', col = 'blue')
    panel.rect(xleft = 500, xright = 520, ybottom = yb, ytop = yt, border = 'cyan', col = 'cyan')
    panel.rect(xleft = 520, xright = 565, ybottom = yb, ytop = yt, border = 'green', col = 'green')
    panel.rect(xleft = 565, xright = 590, ybottom = yb, ytop = yt, border = 'yellow', col = 'yellow')
    panel.rect(xleft = 590, xright = 625, ybottom = yb, ytop = yt, border = 'orange', col = 'orange')
    panel.rect(xleft = 625, xright = 730, ybottom = yb, ytop = yt, border = 'red', col = 'red')
    
  })