R/plot.R

#' Plot a glider Object
#'
#' This is a limited function that is intended for quick views of a
#' dataset. More serious analysis is best done by extracting data and
#' using whatever graphical methods best suit the task at hand.
#'
#' The form of the plot is set by the `which` argument, as follows.
#'
#' \itemize{
#'
#' \item `which=0` or `which="map"`: plot a map of sampling locations.
#' This can be quite slow with the default plot type (using points),
#' so you may find it helpful to use `plot(g, type="l")` to get a
#' quick plot. If you want to change the view, e.g. expanding it so
#' coastline are visible, start by drawing a coastline using the
#' \CRANpkg{oce} package, and then add dots with
#' `points(g[["longitude"]], g[["latitude"]]` or similar.  This method
#' is more flexible than the present `plot()` function.
#'
#' \item `which=1` or `which="p"`: time-series plot
#' of pressure, produced with [oce::oce.plot.ts()].
#'
#' \item `which=2` or `which="T"`: time-series plot
#' of temperature , produced with [oce::oce.plot.ts()].
#'
#' \item `which=3` or `which="S"`: time-series plot
#' of salinity, produced with [oce::oce.plot.ts()].
#'
#' \item `which=4` or `which="TS"`: temperature-salinity diagram, with
#' dots for data produced with [oce::plotTS()].
#'
#' \item `which=5` or `which="navState"`: ignored except for
#' seaexplorer data, this means to plot a time-series of the
#' navigation state, stored as the `navState` item within the
#' `payload1` element of the `data` slot. The meanings of the
#' `navState` values for `seaexplorer` data are:
#'
#' \itemize{
#'
#' \item `105`: glider is not navigating yet
#'
#' \item `115`: glider is surfacing, with ballast and centre of
#' gravity being adjusted to put antenna out of the water
#'
#' \item `116`: glider is at the surface, acquiring a GPS signal, and
#' communicating
#'
#' \item `110`: ballast and centre of mass are adjusted to cause
#' glider to inflect downward
#'
#' \item `100`: ballast is in diving position; adjustments may be made
#' to adjust pitch and heading
#'
#' \item `118`: target depth or altitude has been achieved, so ballast
#' and centre of mass are adjusted to inflect glider upwards
#'
#' \item `117`: glider is ascending, with controls being
#' adjusted for desired pitch and heading
#'
#' }
#'
#' Lines and notes in the plot border indicate these states, both
#' numerically and with phrases, as inferred by [navStateCodes()].
#'
#' }
#'
#' @param x a [glider-class object.
#'
#' @param which either an integer or character value specifying which
#' style is to be used; see \dQuote{Details}.
#'
#' @param col colour to be used for lines or characters. Note that if
#' `colorby` is provided, then it will be used for point plots,
#' instead of `col`.
#'
#' @param colorby character value, ignored for line plots, that names
#' a data variable to be indicated on the plot through the
#' colourization of individual plotted points (i.e. `type="p"` must be
#' governing the plot for `colorby` to have an effect). For example, a
#' form of a temperature section plot can be created by plotting
#' glider depth versus time, coloured by temperature. For reference, a
#' colour palette (using [oce::oceColorsTurbo()] is displayed to the right
#' of the plot.  See Example 3.
#'
#' @param colorbylim optional value, used only if `colorby` is
#' provided, to set the limits of the colorizing limits.  It does this
#' by being provided as the `zlim` argument to [oce::colormap()].
#'
#' @param simplify either NA or an integer value, that will be supplied
#' to [oce::oce.plot.ts()] for time-series plots. The default value of NA
#' instructs [oce::oce.plot.ts()] not to try to simplify the plot by breaking
#' it up into shorter time intervals and finding ranges therein. This can
#' cause problems with the CRAN version of oce (1.8-3, as of early 2025)
#' but it will cease to cause those problems once oce 1.8-4 is released,
#' perhaps in mid 2025.
#'
#' @template debug
#'
#' @param ... ignored.
#'
#' @importFrom oce as.ctd colormap drawPalette oceColorsTurbo oce.plot.ts plotTS resizableLabel
#' @importFrom graphics abline lines par plot text
#'
#' @examples
#' library(oceglider)
#'
#' # Example 1: various plot types, using defaults
#' directory <- system.file("extdata/sea_explorer/delayed_raw", package = "oceglider")
#' g <- read.glider.seaexplorer.raw(directory, "pld1.raw", progressBar = FALSE)
#' plot(g, which = "p")
#' plot(g, which = "S")
#' plot(g, which = "T")
#' plot(g, which = "TS")
#' plot(g, which = "map")
#' plot(g, which = "navState")
#'
#' # Example 2: colour-code p by temperature, autoscaled
#' plot(g, which = "p", type = "p", pch = 20, colorby = "temperature")
#'
#' # Example 3: colour-code p by temperature, with a colour palette,
#' # and more aesthetic control, e.g. setting limits using quantiles.
#' temperature <- g[["temperature"]]
#' cm <- colormap(temperature,
#'     zlim = quantile(temperature, c(0.01, 0.99), na.rm = TRUE),
#'     col = oceColorsTurbo
#' )
#' par(mar = c(2, 3.5, 2, 4))
#' drawPalette(colormap = cm)
#' plot(g, which = "p", type = "p", col = cm$zcol, mar = c(2, 3.5, 2, 4), pch = 20)
#'
#' @md
#'
#' @aliases plot.glider
#'
#' @export
setMethod(
    f = "plot",
    signature = signature("glider"),
    definition = function(x, which, col = 1, colorby = NULL, colorbylim, simplify = NA, debug, ...) # plot,glider-method
    {
        debug <- if (!missing(debug)) debug else getOption("gliderDebug", 0)
        gliderDebug(debug, "plot,glider-method {\n", sep = "", unindent = 1)
        dots <- list(...)
        dotsNames <- names(dots)
        gliderDebug(debug, oce::vectorShow(dots))
        cm <- NULL
        if (!is.null(colorby)) {
            z <- x[[colorby]]
            if (is.null(z)) {
                warning("In plot,glider-method() : there is no \"", colorby,
                    "\" field, so ignoring 'colorby'",
                    call. = FALSE
                )
                colorby <- NULL
            } else {
                cm <- if (missing(colorbylim)) {
                    oce::colormap(x[[colorby]], col = oce::oceColorsTurbo, debug = debug - 1)
                } else {
                    oce::colormap(x[[colorby]], col = oce::oceColorsTurbo, zlim = colorbylim, debug = debug - 1)
                }
                gliderDebug(debug, "set col to indicate values of \"", colorby, "\"\n", sep = "")
            }
        }
        if (which == 0 || which == "map") {
            gliderDebug(debug, "map plot\n", sep = "")
            longitude <- x[["longitude"]]
            latitude <- x[["latitude"]]
            args <- list(
                x = longitude,
                y = latitude,
                asp = 1.0 / cos(mean(range(latitude, na.rm = TRUE) * pi / 180)),
                xlab = resizableLabel("longitude"),
                ylab = resizableLabel("latitude")
            )
            if (!"type" %in% dotsNames) {
                dots$type <- "p"
            }
            args <- c(args, dots)
            omar <- par("mar")
            if (!is.null(colorby)) {
                if (length(cm$zcol) != length(longitude)) {
                    stop(
                        "cannot colour-code location by ", colorby, "because there are",
                        length(latitude), "locations, but ", length(cm$zcol), "values for", colorby
                    )
                } else {
                    oce::drawPalette(colormap = cm)
                    args$col <- cm$zcol
                    par(mar = omar)
                }
            }
            mar <- omar
            mar[4] <- mar[4] + 2 # FIXME: why do this if no palette?
            par(mar = mar)
            do.call("plot", args)
            par(mar = omar)
        } else if (which == 1 || which == "p") {
            gliderDebug(debug, "pressure time-series plot\n", sep = "")
            t <- x[["time"]]
            p <- x[["pressure"]]
            args <- list(
                x = t, y = p, xlab = "",
                ylab = "Pressure [dbar]", simplify = simplify, col = col
            )
            if (!"ylim" %in% dotsNames) {
                dots$ylim <- rev(range(p, na.rm = TRUE))
            }
            if (!"xlim" %in% dotsNames) {
                dots$xlim <- range(t, na.rm = TRUE)
            }
            omar <- par("mar")
            if (!is.null(colorby)) { # we know 'col' cannot be in dots, from earlier tests
                oce::drawPalette(colormap = cm)
                args$col <- cm$zcol
                args$marginsAsImage <- TRUE
            }
            args <- c(args, dots)
            par(mar = omar)
            oldwarn <- options()$warn
            options(warn = -1)
            do.call("oce.plot.ts", args)
            options(warn = oldwarn)
            par(mar = omar)
        } else if (which == 2 || which == "T") {
            gliderDebug(debug, "temperature time-series plot\n", sep = "")
            t <- x[["time"]]
            TT <- x[["temperature"]]
            args <- list(
                x = t, y = TT, xlab = "",
                ylab = expression("Temperature [" * degree * "C]"),
                simplify = simplify, col = col
            )
            omar <- par("mar")
            if (!is.null(colorby)) { # we know 'col' cannot be in dots, from earlier tests
                oce::drawPalette(colormap = cm)
                args$col <- cm$zcol
                args$marginsAsImage <- TRUE
            }
            args <- c(args, dots)
            par(mar = omar)
            oldwarn <- options()$warn
            options(warn = -1)
            do.call("oce.plot.ts", args)
            options(warn = oldwarn)
            par(mar = omar)
        } else if (which == 3 || which == "S") {
            gliderDebug(debug, "salinity time-series plot\n", sep = "")
            t <- x[["time"]]
            S <- x[["salinity"]]
            args <- list(
                x = t, y = S, xlab = "", ylab = "Practical Salinity",
                simplify = simplify, col = col
            )
            omar <- par("mar")
            if (!is.null(colorby)) { # we know 'col' cannot be in dots, from earlier tests
                oce::drawPalette(colormap = cm)
                args$col <- cm$zcol
                args$marginsAsImage <- TRUE
            }
            args <- c(args, dots)
            par(mar = omar)
            oldwarn <- options()$warn
            options(warn = -1)
            do.call("oce.plot.ts", args)
            options(warn = oldwarn)
            par(mar = omar)
        } else if (which == 4 || which == "TS") {
            gliderDebug(debug, "TS plot\n", sep = "")
            salinity <- x[["salinity"]]
            temperature <- x[["temperature"]]
            pressure <- x[["pressure"]]
            longitude <- x[["longitude"]]
            latitude <- x[["latitude"]]
            ctd <- oce::as.ctd(salinity, temperature, pressure, longitude = longitude, latitude = latitude)
            # FIXME for issue https://github.com/dankelley/oce/issues/2295
            args <- list(x = ctd)
            omar <- par("mar")
            if (!is.null(colorby)) { # we know 'col' cannot be in dots, from earlier tests
                oce::drawPalette(colormap = cm)
                args$col <- cm$zcol
                args$mar <- omar + c(0, 0, 0, 2)
                par(mar = omar)
            }
            args <- c(args, dots)
            do.call("plotTS", args)
            par(mar = omar)
        } else if (which == 5 || which == "navState") {
            gliderDebug(debug, "navState plot\n", sep = "")
            ns <- navStateCodes(x)
            # We actually draw the data after the guiding lines
            oldwarn <- options()$warn
            options(warn = -1)
            oce.plot.ts(x[["time"]], x[["navState"]],
                type = "n",
                ylab = "navState", mar = c(2, 3, 1, 9),
                simplify = simplify,
                ...
            )
            options(warn = oldwarn)
            for (ii in seq_along(ns)) {
                abline(h = ns[[ii]], col = "blue", lwd = 0.75)
            }
            # redraw navState on top of the guiding lines
            lines(x[["time"]], x[["navState"]], lwd = 2)
            oxpd <- par("xpd")
            par(xpd = NA)
            tmax <- par("usr")[2] + 0.00 * diff(par("usr")[1:2])
            for (ii in seq_along(ns)) {
                text(tmax, ns[[ii]],
                    sprintf(" %d: %s", ns[[ii]], names(ns[ii])),
                    col = "blue", cex = 0.75, xpd = TRUE, pos = 4
                )
            }
            par(xpd = oxpd)
        } else {
            stop("which=", which, " is not permitted; see ?\"plot,glider-method\"")
        }
        gliderDebug(debug, "} # plot,glider-method\n", sep = "", unindent = 1)
    }
)
dankelley/oceanglider documentation built on June 8, 2025, 4:20 a.m.