R/Methods-plot.R

# For plotting templates
# Modified: 6 Sept 2015

setMethod('plot', signature(x = 'TemplateList', y = 'ANY'), 
  function(
    x,                             # Complete template list
    which.one = names(x@templates),  # If template list is provided, which template(s) should be used?
    click = FALSE,                   # Set to FALSE for no interaction
    ask = if(length(which.one)>1) TRUE else FALSE, # Set to FALSE for no pause between plots
    spec.col = gray.3(),             # Color palette for spectrogram
    on.col = '#FFA50075',            # Color for on points in binary templates
    off.col = '#0000FF75',           # Color for off points in binary templates
    pt.col = '#FFA50075',            # Color for correlation template points
    line.col = 'black'               #
  ) {
  
    # Set mai and get oldask
    oldpar <- par(mai = c(1.02, 0.82, 0.82, 0.82))
    oldask <- par(ask = par('ask'))
    on.exit(par(c(oldpar, oldask)))
    
    # Template loop
    for(i in which.one) {
      template <- x@templates[[i]]
  
      # Pull out clip name
      clip.path <- template@clip.path
      file.ext <- tolower(gsub(".*\\.", "", clip.path))
      if(file.ext == 'wav') clip <- tuneR::readWave(filename = clip.path) else if(file.ext == 'mp3') clip.path <- readMP3(filename = clip) else stop('File extension must be wav or mp3, got ', file.ext)
  
      # Get time and frequency limits
      # t.lim is for cutting the clip, and needs to include samples (i.e., wave@left) that are hanging at the end when ovlp > 0
      # The next line actually should return one extra point
      t.lim <- template@first.t.bin + c(0, template@duration + template@t.step*100/(100-template@ovlp))
      frq.lim <- template@frq.lim + c(-1, 1)*template@frq.step/2
  
      wave <- cutWave(clip, from = t.lim[1], to = t.lim[2]) 
  
      # Fourier transform
      fspec <- spectro(wave = wave, wl = template@wl, ovlp = template@ovlp, wn = template@wn)
      # Filter amplitudes 
      t.bins <- fspec$time
      n.t.bins <- length(t.bins)
      which.t.bins <- 1:n.t.bins
      which.frq.bins <- which(fspec$freq >= frq.lim[1] & fspec$freq <= frq.lim[2])
      frq.bins <- fspec$freq[which.frq.bins]
      n.frq.bins <- length(frq.bins)
      amp <- fspec$amp[which.frq.bins, ]
      t.step <- t.bins[2]-t.bins[1]
      frq.step <- frq.bins[2]-frq.bins[1]
  
      # Create plot
      image(x = which.t.bins, y = which.frq.bins, t(amp), col = spec.col, xlab = 'Time (s)', ylab = 'Frequency (kHz)', las = 1, useRaster = TRUE, axes = FALSE, las = 1)
      t.bin.ticks <- pretty(t.bins+t.lim[1], n = 6);axis(1, at = (t.bin.ticks-t.lim[1])/t.step, labels = t.bin.ticks)
      frq.bin.ticks <- pretty(frq.bins, n = 6);axis(2, at = frq.bin.ticks/frq.step, labels = frq.bin.ticks, las = 1)
      axis(3);axis(4, las = 1);box()
      mtext(paste('Template', i), 3,line = 3, cex = 1.2)
   
      # Plot template points
      if(class(x) == 'binTemplateList') {
  
        pt.on <- template@pt.on
        pt.off <- template@pt.off
  
        pt.on[, 'frq'] <- pt.on[, 'frq'] - min(which.frq.bins) + 1
        pt.off[, 'frq'] <- pt.off[, 'frq'] - min(which.frq.bins) + 1
        bin.amp <- 0*amp
        bin.amp[pt.on[, c(2, 1)]] <- 1
        bin.amp[pt.off[, c(2, 1)]] <- 2
  
        image(x = which.t.bins, y = which.frq.bins, t(bin.amp), zlim = c(0, 2), col = c('transparent', on.col, off.col), add = TRUE)
  
      } else if (class(x) == 'corTemplateList') {
  
        pts <- template@pts
        pts[, 'frq'] <- pts[, 'frq'] - min(which.frq.bins) + 1
        bin.amp <- 0*amp
        bin.amp[pts[, c(2, 1)]] <- 1
  
        image(x = which.t.bins, y = which.frq.bins, t(bin.amp), zlim = c(0, 1), col = c('transparent', pt.col), add = TRUE)
  
      } else stop('Template list class not recognized.')
  
      # Start loop for identifying plot locations 
      if(click) {
        i <- 0
        click.pts <- NULL
        message('Identify plot locations with left mouse click. To exit, right click.')
        pos <- NULL
        while(!is.null(pos)|i == 0) {
          i <- i + 1
          pos <- locator(n = 1)
          if(!is.null(pos)) {
            pos <- lapply(pos, round)
            abline(h = pos$y, col = line.col)
            abline(v = pos$x, col = line.col)
            text(pos$x, pos$y, i,col = 'red')
  
            # locator returned bin positions, so time and frequency values need to be determined from indexing
            t.pos <- t.lim[1] + t.bins[pos$x] + t.step # + t.step there because first time bin is zero. . .IS THIS A PROBLEM ELSEWHERE?
            frq.pos <- frq.bins[pos$y - min(which.frq.bins) + 1]
            amp.pos <- amp[pos$y - min(which.frq.bins) + 1, pos$x]
            click.pts <- rbind(click.pts, c(t.pos, frq.pos, amp.pos))
  
            x.mid <- mean(par()$usr[1:2])
            y.lim <- par()$usr[3:4]
  
            text(x.mid, y.lim[2]-i/20*diff(y.lim), paste(i, '.time = ', signif(t.pos, 3), ',frq = ', signif(frq.pos, 3), ',amp = ', signif(amp.pos, 3), sep = ''), cex = 0.8, col = line.col)
          }
        } 
      }
      par(ask = ask)
    }

    if(click) {
      colnames(click.pts) <- c('t', 'frq', 'amp')
      invisible(click.pts)
    }

  }
)


# templateScores

setMethod('plot', signature(x = 'detectionList', y = 'ANY'), 
  function(
  x,  
  flim = c(0, 12),                 # Frequency limits for the spectrogram.
  scorelim,                     # Plot limits for scores
  which.one = names(x@templates), # Name(s) of templates to plot
  box = TRUE,                     # Set to FALSE to surpress boxes in spectrogram showing hits
  spec.col = gray.2(),            # Color palette for spectrogram
  t.each = 30,                    # Time shown for each individual plot (s)
  hit.marker = 'lines',           # Markers for hits in score plot
  color = c('red', 'blue', 'green', 'orange', 'purple', 'pink', 'darkgreen', 'turquoise', 'royalblue', 'orchid4', 'brown', 'salmon2'), # Colors for individual templates
  legend = TRUE,                  # Set to FALSE to surpress legend
  all.peaks = FALSE,              # Set to TRUE to indicate locations of all peaks
  ask = if(dev.list() == 2) TRUE else FALSE 
  ) {

    survey <- x@survey
    t.survey <- length(survey@left)/survey@samp.rate 
    n.plots <- ceiling(t.survey/t.each)
    t.start <- 1:n.plots*t.each - t.each
    if(n.plots == 1) t.each <- t.survey
    t.end <- t.start + t.each
    t.end[t.end>t.survey] <- t.survey
    t.start[n.plots] <- t.end[n.plots] - t.each # Adjust start of last plot back so it has the same length as the others

    # Pull out spectrogram data from scores object
    # Based on first template
    amp <- x@survey.data[[1]]$amp
    t.bins <- x@survey.data[[1]]$t.bins
    frq.bins <- x@survey.data[[1]]$frq.bins
 
    # Sort out colors for lines and boxes
    names.t <- names(x@templates)
    n.templates <- length(names.t)
    color <- c(rep(color, n.templates %/% length(color)), color[1:n.templates%%length(color)])
    names(color) <- names.t

    # Get scorelim
    if(missing(scorelim)) {
      upr <- 0
      for(i in seq(length(x@scores))) {
        upr <- max(upr, x@scores[[i]]$score)
      }
      scorelim <- c(0, upr)
    }

    oldpar <- par(mar = c(1, 4,1, 1), oma = c(6, 0,0, 0), mfrow = c(2, 1))
    oldask <- par(ask = par('ask'))
    on.exit(par(c(oldpar, oldask)))

    # Loop through time windows, plotting a spectrogram for each time
    for(i in 1:length(t.start)) {

      message(paste(t.start[i], 'to', t.end[i], 'seconds'))
    
      times <- t.bins[t.bins>= t.start[i] & t.bins<= t.end[i]]
      amp.clip <- amp[, t.bins %in% times]
      image(x = times, y = frq.bins, t(amp.clip), ylim = flim, col = spec.col, xlab = '', ylab = 'Frequency (kHz)', xaxt = 'n', las = 1)
 
      # Loop through templates and add boxes around detections
      for(j in which.one) {
        template <- x@templates[[j]]
        if(all.peaks) pks <- x@peaks[[j]] else pks <- x@detections[[j]]
        pks.clip <- pks[pks$time + template@duration >= t.start[i] & pks$time - template@duration <= t.end[i], ]

        if(box & nrow(pks.clip)>0) {
          for(k in 1:nrow(pks.clip)) { 
            xleft <- pks.clip$time[k] - template@duration/2
            xright <- pks.clip$time[k] + template@duration/2
            ylwr <- template@frq.lim[1]
            yupr <- template@frq.lim[2]
            polygon(x = c(xleft, xleft, xright, xright), y = c(ylwr, yupr, yupr, ylwr), border = color[j], lwd = 1)
          }
        }
      }

      # Make plot of scores. Can't sort out xlab for some reason.
      plot(NULL, xlim = c(t.start[i], t.end[i]), ylim = scorelim, xlab = '', ylab = 'Score', type = 'n', xaxs = 'i', las = 1, mgp = c(3, 1,0))
      mtext("Time (s or min:sec)", 1,2.5, outer = TRUE)

      # Add x axis as mm:ss
      xaxp.sec <- par('xaxp')
      labs.sec <- seq(xaxp.sec[1], xaxp.sec[2], length.out = xaxp.sec[3]+1)
      labs.mmss <- paste(sprintf('%02d', labs.sec%/%60), ':', sprintf('%02d', labs.sec%%60), sep = '')
      axis(1, at = labs.sec, labels = labs.mmss, mgp = c(3, 1.9, 0))

      if(legend) legend('topright', which.one, lty = 1, col = color[which.one], cex = 0.7)

      # Loop through templates 
      for(j in which.one) {
        template <- x@templates[[j]]
        score <- x@scores[[j]]         # score output from sccDetect. The correlation coefficients within which hits were found.
        if(all.peaks) pks <- x@peaks[[j]] else pks <- x@detections[[j]]
        cutoff <- template@score.cutoff      # If given, will plot a horizontal line at the correlation coefficient cutoff.

        score.clip <- score[score$time>= t.start[i] & score$time<= t.end[i], ]
        pks.clip <- pks[pks$time + template@duration >= t.start[i] & pks$time - template@duration <= t.end[i], ]

        lines(score.clip$time, score.clip$score, col = color[j])
        if(hit.marker == 'points') points(pks.clip$time, pks.clip$score, col = color[j]) else
          if(hit.marker == 'lines') abline(v = pks.clip$time, col = color[j]) 
        if(is.vector(cutoff)) abline(h = cutoff, lty = 2, col = color[j])
      }
      par(ask = ask)
    }
  
  }
)  
jonkatz2/monitoR documentation built on March 27, 2024, 4:39 p.m.