R/subset.R

## vim:textwidth=128:expandtab:shiftwidth=4:softtabstop=4

##OLD use_sf_package <- TRUE

#' Subset an argoFloats Object
#'
#' Return a subset of an [`argoFloats-class`] object.  This applies
#' only to objects of type `"index"`, as created with [getIndex()] or `subset()`
#' acting on such a value, or of type `"argos"`, as created with [readProfiles()].
#' (It cannot be used on objects of type `"profiles"`, as created with [getProfiles()].)
#' There are two ways to use `subset()`.  **Method 1.** supply
#' the `subset` argument.  This may be a logical vector indicating
#' which entries to keep (by analogy to the base-R `subset()`
#' function) or it may be an integer vector holding the indices of entries to
#' be retained. **Method 2.** do not supply `subset`.  In this
#' case, the action is determined by the third (`...`)
#' argument; see \sQuote{Details}.
#'
#' The possibilities for the `...` argument are as follows.
#'
#' 1. An integer vector giving indices to keep.
#'
#' 2. A list named `circle` with numeric elements named `longitude`,
#' `latitude` and `radius`.  The first two give the center of
#' the subset region, and the third gives the radius of
#' that region, in kilometers.
#'
#' 3. A list named `rectangle` that has elements named
#' `longitude` and `latitude`, two-element numeric vectors
#' giving the western and eastern, and southern and northern
#' limits of the selection region.
#'
#' 4. A list named `polygon` that has elements named `longitude` and `latitude`
#' that are numeric vectors specifying a polygon within which profiles
#' will be retained. The polygon must not be self-intersecting,
#' and an error message will be issued if it is.  If the polygon is not closed
#' (i.e. if the first and last points do not coincide) the first point is pasted
#' onto the end, to close it.
#'
#' 5. A vector or list named `parameter` that holds character values that
#' specify the names of measured parameters to keep. See section 3.3 of
#' Reference 1 for a list of parameters.
#'
#' 6. A list named `time` that has elements `from` and `to` that are either
#' POSIXt times, or character strings that `subset()` will convert to
#' POSIXt times using [as.POSIXct()] with `tz="UTC"`.
#'
#' 7. A list named `institution` that holds a single character element that
#' names the institution.  The permitted values are:
#' `"AO"` for Atlantic Oceanographic and Meteorological Laboratory;
#' `"BO"` for British Oceanographic Data Centre;
#' `"CS"` for Commonwealth Scientific and Industrial Research Organization;
#' `"HZ"` for China Second Institute of Oceanography;
#' `"IF"` for Ifremer, France;
#' `"IN"` for India National Centre for Ocean Information Services;
#' `"JA"` for Japan Meteorological Agency;
#' `"KM"` for Korea Meteorological Agency;
#' `"KO"` for Korea Ocean Research and Development Institute;
#' `"ME"` for Marine Environment Data Section; and
#' `"NM"` for National Marine Data & Information Service.
#'
#' 8. A list named `deep` that holds a logical value indicating whether argo floats
#' are deep argo (i.e. `profiler_type` 849, 862, and 864).
#'
#' 9. A list named `ID` that holds a character value specifying a float identifier.
#'
#' 10. A list named `ocean` that  holds a single character element that names the
#' ocean. The permitted values are:
#' `"A"` for Atlantic Ocean Area, from 70 W to 20 E,
#' `"P"` for Pacific Ocean Area, from 145 E to 70 W, and
#' `"I"` for Indian Ocean Area, from 20 E to 145 E.
#'
#' 11. A character value named `dataMode`, equal to either `realtime` or `delayed`,
#' that selects whether to retain real-time data or delayed data.  There are two
#' possibilities, depending on the `type` of the `x` argument.
#' **Case 1.** If `x` is of `type="index"`, then the subset is done by looking for the letters
#' `R` or `D` in the source filename. Note that a file in the
#' latter category may contain some profiles that are of delayed mode *and also*
#' some profiles that are of `realtime` or `adjusted` mode.
#' **Case 2.** If `x` is
#' of type `argos`, then the subset operation is done for each profile within
#' the dataset. Sometimes this will yield data arrays with zero columns.
#'
#' 12. An integer or character value named `cycle` that specifies which cycles are to be retained.
#' This is done by regular-expression matching of the filename, looking between the
#' underline character (`"_"`) and the suffix (`.nc`), but note that the expression
#' is made up of a compulsory component comprising 3 or 4 digits, and an optional
#' component that is either blank or the character `"D"` (which designates a
#' descending profile).  Thus, `001` will match both `*_001.nc` and `*_001D.nc`.
#' Note this can be used for both `"index"` and `"argos"` types.
#'
#' 13. A character value named `direction`, equal to either "descent" or "ascent",
#' that selects whether to retain data from the ascent or decent phase.
#'
#' 14. An integer value named `profile` that selects which profiles
#' to retain.  Note that this type of subset is possible only
#' for objects of type `"argos"`.
#'
#' 15. An integer value named `cycle` that selects which cycles
#' to retain.
#'
#' 16. A character value named `dataStateIndicator`, equal to either "0A", "1A",
#' "2B", "2B+", "2C", "2C+", "3B", or "3C" that selects which `dataStateIndicator`
#' to keep (see table 6 of Reference 1 for details of these codes).
#' This operation only works for objects of type `"argos"`.
#'
#' 17. A list named `section` that  has four elements:
#' `longitude`,`latitude`, `width`, and `segments`. The first two of these
#' are numeric vectors that define the spine of the section, in degrees East
#' and North, respectively.  These are both mandatory, while both `width` and `segments`
#' are optional. If given, `width` is taken as maximal distance between an Argo
#' and the spine, for that float to be retained. If `width` is not given, it
#' defaults to 100km.  If given, `segments` is either `NULL` or a positive
#' integer.  Setting `segments` to `NULL` will cause the float-spine distance
#' to be computed along a great-circle route.  By contrast, if `segments` is an
#' integer, then the spine is traced using `stats::approx()`, creating
#' `segments` new points.  If `segments` is not provided, it defaults to 100.
#'
#' In all cases, the notation is that longitude is positive
#' for degrees East and negative for degrees West, and that latitude
#' is positive for degrees North and negative for degrees South.
#' For more on this function, see Kelley et al. (2021).
#'
#' @param x an [`argoFloats-class`] object as created by [getIndex()].
#'
#' @param subset optional numerical or logical vector that indicates which
#' indices of `x@data$index` to keep (example 1).
#'
#' @param ... the first entry here must be either (a) a list named `circle`,
#' `rectangle`, `polygon`, `parameter`, `time`, `institution`,
#' `ID`,`ocean`,`dataMode`,`cycle`, `direction`, `profile`, or `section`.
#'  (examples 2 through 8, and 10 through 17),
#' or (b) a logical value named `deep` (example 9).  Optionally, this entry
#' may be followed by second entry named `silent`, which is a logical
#' value indicating whether to prevent the printing of messages that
#' indicate the number (and percentage) of data that are kept
#' during the subsetting operation.
#' See \dQuote{Details} and \dQuote{Examples}.
#'
#' @return An [`argoFloats-class`] object, restricted as indicated.
#'
#' @examples
#' library(argoFloats)
#' data(index)
#' data(indexSynthetic)
#'
#' # Subset to the first 3 profiles in the (built-in) index
#' indexFirst3 <- subset(index, 1:3)
#'
#' # Subset to a circle near Abaco Island
#' indexCircle <- subset(index, circle=list(longitude=-77.5, latitude=27.5, radius=50))
##
## # NOTE: this example was removed because it requires using tempdir, which
## # is not something we want to encourage, since it goes against the whole idea
## # of caching.
## # Example 2B: subset a 300 km radius around Panama using "maps" package
## # This example requires downloading.
## \donttest{
## library("maps")
## data(world.cities)
## ai <- getIndex()
## panama <- subset(world.cities, name=="Panama")
## index1 <- subset(ai, circle=list(longitude=panama$long, latitude=panama$lat, radius=200))
##}
#'
#' # Subset to a rectangle near Abaco Island
#' lonlim <- c(-76.5, -76)
#' latlim <- c(26.5, 27.5)
#' indexRectangle <- subset(index, rectangle=list(longitude=lonlim, latitude=latlim))
#'
#' # Subset to a polygon near Abaco Island
#' lonp <- c(-77.492, -78.219, -77.904, -77.213, -76.728, -77.492)
#' latp <- c( 26.244,  25.247,  24.749,  24.987,  25.421,  26.244)
#' indexPolygon <- subset(index, polygon=list(longitude=lonp, latitude=latp))
##
## # Show some of these subsets on a map
## plot(index, bathymetry=FALSE)
## points(index2[["longitude"]], index2[["latitude"]], col=2, pch=20, cex=1.4)
## points(index3[["longitude"]], index3[["latitude"]], col=3, pch=20, cex=1.4)
## rect(lonRect[1], latRect[1], lonRect[2], latRect[2], border=3, lwd=2)
## points(index4[["longitude"]], index4[["latitude"]], col=4, pch=20, cex=1.4)
## polygon(p$longitude, p$latitude, border=4)
#'
#' # Subset to year 2019
#' index2019 <- subset(index, time=list(from="2019-01-01", to="2019-12-31"))
#'
#' # Subset to Canadian MEDS data
#' indexMEDS <- subset(index, institution="ME")
##
## # NOTE: this example was removed because it requires using tempdir, which
## # is not something we want to encourage, since it goes against the whole idea
## # of caching.
## # Example 8: subset to a specific ID
## # This example requires downloading.
## \donttest{
## ai <- getIndex(filename="synthetic")
## index9 <- subset(ai, ID="1900722")
##}
##
## # NOTE: this example was removed because it requires using tempdir, which
## # is not something we want to encourage, since it goes against the whole idea
## # of caching.
## # Example 9: subset data to only include deep argo
## # This example requires downloading.
## \donttest{
## ai <- getIndex(filename="synthetic")
## index8 <- subset(ai, deep=TRUE)
##}
##
## # NOTE: this example was removed because it requires using tempdir, which
## # is not something we want to encourage, since it goes against the whole idea
## # of caching.
## # Example 10: subset data by ocean
## # This example requires downloading.
## \donttest{
## ai <- getIndex()
## index10 <- subset(ai, circle=list(longitude=-83, latitude=9, radius=500))
## plot(index10, which="map", bathymetry=FALSE)
## atlantic <- subset(index10, ocean="A") # Subsetting for Atlantic Ocean
## pacific <- subset(index10, ocean="P")
## points(atlantic[["longitude"]], atlantic[["latitude"]], pch=20, col=2)
## points(pacific[["longitude"]], pacific[["latitude"]], pch=20, col=3)
##}
##
## # NOTE: there is no example 11.
##
## # NOTE: this example was removed because it requires using tempdir, which
## # is not something we want to encourage, since it goes against the whole idea
## # of caching.
## # Example 11: subset by delayed time
## # This example requires downloading.
## \donttest{
## data(indexBgc)
## index11 <- subset(indexBgc, dataMode="delayed")
## profiles <- getProfiles(index11)
## argos <- readProfiles(profiles)
## oxygen <- argos[["oxygen"]][[3]]
## pressure <- argos[["pressure"]][[3]]
## plot(oxygen, pressure, ylim=rev(range(pressure, na.rm=TRUE)),
##      ylab="Pressure (dbar)", xlab="Oxygen (umol/kg)")
##}
##
## # Example 12: subset by cycle
## data(index)
## index12A <- subset(index, cycle="124")
## index12B <- subset(index, cycle=0:2)
## cat("File names with cycle number 124:", paste(index12A[["file"]]), "\n")
## cat("File names with cycle number between 0 and 2:", paste(index12B[["file"]]), "\n")
##
##
## # NOTE: this example was removed because it requires using tempdir, which
## # is not something we want to encourage, since it goes against the whole idea
## # of caching.
## # Example 13: subset by direction
## # This example requires downloading.
## \donttest{
## library(argoFloats)
## index13A <- subset(getIndex(), deep=TRUE)
## index13B <- subset(index13A, direction="descent")
## head(index13B[["file"]])
##}
##
## # NOTE: this example was removed because it requires using tempdir, which
## # is not something we want to encourage, since it goes against the whole idea
## # of caching.
## # Example 14: subset by profile (for argos type)
## # This example requires downloading.
## \donttest{
## library(argoFloats)
## index14A <- subset(getIndex(filename="synthetic"), ID="5903889")
## index14B <- subset(index14A, cycle="074")
## argos14A <- readProfiles(getProfiles(index14B))
## argos14B <- subset(argos14A, profile=1)
## D <- data.frame(Oxygen = argos14A[["oxygen"]],
## col1= argos14B[["oxygen"]][[1]])
##}
##
## # NOTE: this example was removed because it requires using tempdir, which
## # is not something we want to encourage, since it goes against the whole idea
## # of caching.
## # Example 15: subset by cycle (for argos type) to create TS diagram
## # This example requires downloading.
## \donttest{
## data("index")
## index15 <- subset(index, ID="1901584")
## profiles <- getProfiles(index15)
## argos <- readProfiles(profiles)
## plot(subset(argos, cycle="147"), which="TS")
##}
##
## # NOTE: this example was removed because it requires using tempdir, which
## # is not something we want to encourage, since it goes against the whole idea
## # of caching.
## # Example 16: subset by dataStateIndicator
## # This example requires downloading.
## \donttest{
## data("index")
## index16 <- subset(index, 1:40)
## argos <- readProfiles(getProfiles(index16))
## argos16A <- subset(argos, dataStateIndicator="2C")
## argos16B <- subset(argos, dataStateIndicator="2B")
##}
##
## # Example 17: subset by section to create a map plot
## if (requireNamespace("s2")) {
##     data("index")
##     lon <- c(-78, -77, -76)
##     lat <-c(27.5,27.5,26.5)
##     index17 <- subset(index,
##                       section=list(longitude=lon, latitude=lat, width=50))
##     plot(index17, bathymetry=FALSE)
##     points(lon, lat, pch=21, col="black", bg="red", type="o", lwd=3)
##}
#'
#' # Subset to profiles with oxygen data
#' indexOxygen <- subset(indexSynthetic, parameter="DOXY")
#'
#' # Subset to profiles with both salinity and 380-nm downward irradiance data
#' indexSalinityIrradiance <- subset(indexSynthetic, parameter=c("PSAL", "DOWN_IRRADIANCE380"))
#'
#' @references
#' 1. Carval, Thierry, Bob Keeley, Yasushi Takatsuki, Takashi Yoshida, Stephen Loch,
#' Claudia Schmid, and Roger Goldsmith. Argo User's Manual V3.3. Ifremer, 2019.
#' `doi:10.13155/29825`.
#' 2. Kelley, D. E., Harbin, J., & Richards, C. (2021). argoFloats: An R package for analyzing
#' Argo data. Frontiers in Marine Science, (8), 636922.
#' \doi{10.3389/fmars.2021.635922}
#'
#' @importFrom stats approx
#'
#' @importFrom oce geodDist
#'
#' @export
#'
#' @author Dan Kelley and Jaimie Harbin
setMethod(f="subset",
    signature="argoFloats",
    definition=function(x, subset=NULL, ...)
    {
        if (x@metadata$type == "profiles")
            stop("in subset,argoFloats-method() :\n  subset doesn't work for type = profiles", call.=FALSE)
        ## subsetString <- paste(deparse(substitute(subset)), collapse=" ")
        dots <- list(...)
        dotsNames <- names(dots)
        ## Clear the "debug" and "silent" entries from dots, because later we insist that length(dots) be 1.
        debug <- 0
        if ("debug" %in% dotsNames) {
            debug <- dots$debug
            dots$debug <- NULL
            dotsNames <- names(dots)
        }
        silent <- 0
        if ("silent" %in% dotsNames) {
            silent <- dots$silent
            dots$silent <- NULL
            dotsNames <- names(dots)
        }
        ## All done with manipulating dots now.
        argoFloatsDebug(debug, "subset,argoFloats-method() {\n", style="bold", sep="", unindent=1)
        ## message("type =", x@metadata$type)
        ## Step 1: handle "argo" type first. Note that "subset" is ignored; rather, we insist that either
        ## "profile" or "cycle" be provided.
        if (x@metadata$type == "argos") {
            N <- x[["length"]]
            if (!missing(subset)) {
                N <- x[["length"]]
                if (is.logical(subset)) {
                    if (length(subset) != x[["length"]])
                        stop("in subset,argoFloats-method() :\n  'subset' length (", length(subset), ") must match x length (", x[["length"]], ")",
                            call.=FALSE)
                    keep <- which(subset)
                } else if (is.numeric(subset)) {
                    if (!all(subset > 0))
                        stop("in subset,argoFloats-method() :\n  'subset' must consist only of positive integers",
                            call.=FALSE)
                    if (any(subset > x[["length"]]))
                        stop("in subset,argoFloats-method() :\n  'subset' must not contain indices beyond range of `x` (in this case, ", x[["length"]], ")",
                            call.=FALSE)
                    keep <- subset
                } else {
                    stop("in subset,argoFloats-method() :\n  'subset' must either be a logical vector or a list of indices",
                        call.=FALSE)
                }
                res <- x
                res@data$argos <- x@data$argos[keep]
                if (!silent)
                    message("Kept ", length(keep), " cycles (", sprintf("%.3g", 100*length(keep)/N), "%)")
                argoFloatsDebug(debug, "} # subset,argoFloats-method()\n", style="bold", sep="", unindent=1)
                return(res)
            }
            argoFloatsDebug(debug, "subsetting with type=\"argos\"\n")
            if (length(dotsNames) == 0)
                stop("in subset,argoFloats-method() :\n  must give \"profile\" , \"cycle\", or \"dataStateIndicator\" argument", call.=FALSE)
            if (dotsNames[1] == "profile") {
                argoFloatsDebug(debug, "subsetting by profile ", profile, "\n")
                profile <- dots[[1]]
                ## Loop over all objects within the data, and within that loop look at data within the object,
                ## and for each of them, if its a vactor subset according to profile and if its a matrix
                ## subset according to profile
                res <- x
                argos <- x[["argos"]]
                ## Loop over all objects
                ##message("profile=",paste(profile, collapse=" "))
                for (iargo in seq_along(argos)) {
                    argo <- argos[[iargo]]
                    ## Handle the metadata slot
                    for (name in names(argo@metadata)) {
                        ##message("name=",name)
                        argoFloatsDebug(debug, "subsetting metadata item named \"", name, "\"\n", sep="")
                        ## Pass some things through directly.
                        if (name %in% c("units", "filename", "flagScheme", "dataNamesOriginal"))
                            next
                        item <- argo@metadata[[name]]
                        ## Handle things that are encoded as characters in a string,
                        ## namely "direction", "juldQC", and "positionQC".
                        if (name == "direction" || grepl("QC$", name)) {
                            ## message("  -- character")
                            res@data$argos[[iargo]]@metadata[[name]] <- paste(strsplit(item,"")[[1]][profile],collapse="")
                        } else if (is.list(item)) {
                            ##message("list")
                            for (l in seq_along(item)) {
                                ##print(dim(item[[l]]))
                                D <- dim(item[[l]])
                                if (profile > D[2])
                                    stop("in subset,argoFloats-method() :\n cannot access profile ", profile, " of metadata item \"", name, "\" because its dimension is ", paste(D, collapse=" "), call.=FALSE)
                                ##cat("BEFORE:\n");print(dim(res@data$argos[[iargo]]@metadata[[name]][[l]]))
                                res@data$argos[[iargo]]@metadata[[name]][[l]] <- item[[l]][, profile, drop=FALSE]
                                ##cat("AFTER:\n");print(dim(res@data$argos[[iargo]]@metadata[[name]][[l]]))
                            }
                        } else if (is.vector(name)) {
                            ##message("vector")
                            res@data$argos[[iargo]]@metadata[[name]] <- item[profile]
                        } else if (is.matrix(name)) {
                            ##message("matrix")
                            res@data$argos[[iargo]]@metadata[[name]] <- item[, profile, drop=FALSE]
                        } else if (is.array(name)) {
                            argoFloatsDebug(debug, "name=", name, " has dim ", paste(dim(res@metadata[[name]]), collapse=" "), "\n")
                            if (length(dim(res@metadata[[name]])) <= 3) {
                                res@metadata[[name]] <- item[, , keep, drop=FALSE]
                            } else {
                                warning("not subsetting \"", name, "\" in metadata, because it is an array of rank > 3")
                            }
                        } else {
                            stop("cannot subset metadata item named \"", name, "\" because it is not a length-one string, a vector, or a matrix")
                        }
                    }
                    ## Handle the data slot
                    for (name in names(argo@data)) {
                        item <- argo@data[[name]]
                        if (is.matrix(item)) {
                            dim <- dim(item)
                            if (profile > dim[2])
                                stop("in subset,argoFloats-method() :\n  Only have ", dim[2], " profiles", call.=FALSE)
                            newItem <- item[, profile, drop=FALSE]
                            res@data$argos[[iargo]]@data[[name]] <- newItem
                        } else {
                            length <- length(item)
                            if (profile > length)
                                stop("in subset,argoFloats-method() :\n  Only have ", length, " profiles", call.=FALSE)
                            newItem <- item[profile, drop=FALSE]
                            res@data$argos[[iargo]]@data[[name]] <- newItem
                        }
                    }
                }
                argoFloatsDebug(debug, "} # subset,argoFloats-method()\n", style="bold", sep="", unindent=1)
                res@processingLog <- oce::processingLogAppend(res@processingLog,
                                                  paste("subset argos type for profile=", profile))
                return(res)
            } else if (dotsNames[1] == "ID") {
                istraj <- identical(x@metadata$subtype, "trajectories")
                argoFloatsDebug(debug, "subsetting an argos object by ID\n")
                ID <- as.character(dots[[1]]) # convert in case it is numeric
                xID <- x[["ID"]]
                keep <- rep(FALSE, length(xID))
                for (thisID in ID)
                    keep <- keep | grepl(thisID, xID)
                if (!silent && !istraj) {
                    message("Kept ", sum(keep), " cycles (", sprintf("%.3g", 100*sum(keep)/N), "%)")
                } else if (!silent && istraj) {
                    message("Kept ", sum(keep), " trajectories (", sprintf("%.3g", 100*sum(keep)/N), "%)")
                }
                x@data$argos <- x@data$argos[keep]
                x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                            paste("subset argos type by float ID", ID))
            } else if (dotsNames[1] == "cycle") {
                argoFloatsDebug(debug, "subsetting by cycle for 'argos' type\n")
                cycle <- dots[[1]]
                if (!is.character(cycle) & !is.numeric(cycle))
                    stop("in subset,argoFloats-method() : \"cycle\" must be character value or numeric value", call.=FALSE)
                ## Calculate 'keep', a logical vector that will be used for the actual subsetting.
                xcycle <- x[["cycle"]]
                argoFloatsDebug(debug, "x[[\"cycle\"]]: ", paste(xcycle, collapse=" "), "\n")
                argoFloatsDebug(debug, "cycle: ", cycle, "\n")
                if (is.character(cycle)) {
                    argoFloatsDebug(debug, "subsetting by cycle as a character value\n")
                    ## Here, keep is logical
                    keep <- rep(FALSE, length(xcycle))
                    for (thisCycle in cycle)
                        keep <- keep | grepl(thisCycle, xcycle)
                    nkeep <- sum(keep)
                } else if (is.numeric(cycle)) {
                    argoFloatsDebug(debug, "subsetting by cycle as a numeric value\n")
                    ## Here, keep is integer
                    keep <- NULL
                    xcycle <- as.integer(gsub("AD","",xcycle)) # change e.g. "123D" to "123"
                    for (thisCycle in cycle) {
                        keep <- c(keep, which(thisCycle == xcycle))
                        ## message("thisCycle=", thisCycle, " keep=", paste(keep, collapse=" "))
                    }
                    nkeep <- length(keep)
                }
                if (nkeep < 1)
                    warning("In subset,argoFloats-method(..., parameter) : found no profiles with given cycle(s)", call.=FALSE)
                if (!silent)
                    message("Kept ", nkeep, " cycles ")
                x@data[[1]] <- x@data[[1]][keep]
                x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste("subset argos type for cycle=", cycle))
            } else if (dotsNames[1] == "dataStateIndicator") {
                argoFloatsDebug(debug, "subsetting by dataStateIndicator\n")
                dataStateIndicator <- dots[[1]]
                if (is.list(dots[1]))
                    dataStateIndicator <- unlist(dataStateIndicator)
                ## Reference Table 6, in section 3.6, of
                ## Argo Data Management Team. “Argo User's Manual V3.3.”
                ## Ifremer, November 28, 2019. https://doi.org/10.13155/29825.
                if (!is.character(dataStateIndicator))
                    stop("in subset,argoFloats-method() :  \"dataStateIndicator\" must be character value", call.=FALSE)
                ## In next, [1] means we take the first element (first profile) of the cycle.
                dsi <- sapply(x[["argos"]], function(a) a[["dataStateIndicator"]][1])
                keep <- rep(FALSE, length(dsi))
                for (dataStateIndicatorThis in dataStateIndicator) {
                    keep <- keep | grepl(dataStateIndicatorThis, dsi , fixed=TRUE)
                    ##> message(dataStateIndicatorThis)
                    ##> print(keep)
                }
                x@data[[1]] <- x@data[[1]][keep]
                x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste("subset argos type by dataStateIndicator=", dataStateIndicator))
            } else if (dotsNames[1] == "dataMode") {
                seeking <- dots$dataMode
                # This testing of 'dataMode' may be overly complex
                if (length(seeking) != 1)
                    stop("in subset,argoFloats-method(): length of 'dataMode' must be 1", call.=FALSE)
                if (!seeking %in% c("realtime", "adjusted", "delayed"))
                    stop('in subset,argoFloats-method(): \'dataMode\' must be "realtime", "adjusted" or "delayed", not "', seeking, '"', call.=FALSE)
                seeking <- switch(seeking, realtime="R", adjusted="A", delayed="D")
                res <- x
                a <- x@data$argos
                for (i in seq_along(a)) {
                    A <- a[[i]]
                    keepProfile <- A[["dataMode"]] == seeking
                    #> message("i=", i, ", keepProfile=", paste(keepProfile, collapse=" "))
                    ## flags
                    for (flagName in names(A@metadata$flags)) {
                        A@metadata$flags[[flagName]] <- A@metadata$flags[[flagName]][, keepProfile, drop=FALSE]
                    }
                    # dataMode
                    A@metadata$dataMode <- A@metadata$dataMode[keepProfile, drop=FALSE]
                    # data
                    for (dataName in names(A@data)) {
                        if (is.vector(A@data[[dataName]])) {
                            #> message("subset ", dataName, " vector from ", paste(A@data[[dataName]], collapse=" "))
                            A@data[[dataName]] <- A@data[[dataName]][keepProfile]
                            #> message("   ... to ", paste(A@data[[dataName]], collapse=" "))
                        } else if (is.array(A@data[[dataName]])) {
                            A@data[[dataName]] <- A@data[[dataName]][, keepProfile, drop=FALSE]
                        }
                    }
                    res@data$argos[[i]] <- A
                }
                res@processingLog <- oce::processingLogAppend(res@processingLog, paste(deparse(match.call()), sep="", collapse=""))
                argoFloatsDebug(debug, "} # subset,argoFloats-method()\n", style="bold", sep="", unindent=1)
                return(res)
            } else {
                stop("in subset,argoFloats-method():\n  the \"...\" argument \"", dotsNames[1], "\" is not permitted for an argos-type object. The only valid choices are \"cycle\", \"dataMode\", \"ID\", \"dataSateIndicator\" and \"profile\".", call.=FALSE)
            }
        }
        ## Step 2: Now we know the type is either "index" or "profiles".  In either case,
        ## "subset" can be provided, so we check for its existence first.
        if (missing(subset)) {
            #argoFloatsDebug(debug, "no subset was given, so it must be circle=, rectangle=, or similar\n")
            if (length(dots) == 0)
                stop("in subset,argoFloats-method() :\n for indices, must specify the subset, with \"subset\" argument, \"circle\",\"rectangle\", \"parameter\",\"polygon\", \"time\", \"institution\", \"deep\", \"ID\", \"ocean\", dataMode\", \"cycle\",\"direction\", or \"section\"", call.=FALSE)
            if (length(dots) > 1)
                stop("in subset,argoFloats-method() :\n  cannot give more than one method in the \"...\" argument", call.=FALSE)
            N <- length(x@data$index[[1]]) # used in calculating percentages
            if (x@metadata$type == "index") {
                istraj <- identical(x@metadata$subtype, "trajectories")
                if (dotsNames[1] == "circle" && !istraj) {
                    argoFloatsDebug(debug, "subsetting an index by circle\n")
                    circle <- dots[[1]]
                    if (!is.list(dots[1]))
                        stop("in subset,argoFloats-method() :\n  \"circle\" must be a list containing \"longitude\", \"latitude\" and \"radius\".", call.=FALSE)
                    if (3 != sum(c("longitude", "latitude", "radius") %in% sort(names(circle))))
                        stop("in subset,argoFloats-method() :\n  \"circle\" must be a list containing \"longitude\", \"latitude\" and \"radius\"", call.=FALSE)
                    if (!requireNamespace("oce", quietly=TRUE))
                        stop("must install.packages(\"oce\") to subset by circle")
                    dist <- oce::geodDist(x[["longitude"]], x[["latitude"]], circle$longitude, circle$latitude)
                    keep <- dist < circle$radius
                    keep[is.na(keep)] <- FALSE
                    x@data$index <- x@data$index[keep, ]
                    x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste("subset index type by circle with longitude= ", circle$longitude, " , latitude= ", circle$latitude, ", and radius= ", circle$radius))
                    if (!silent)
                        message("Kept ", sum(keep), " cycles (", sprintf("%.3g", 100*sum(keep)/N), "%)")
                } else if (dotsNames[1] == "circle" && istraj) {
                        stop("in subset,argoFloats-method(): cannot subset circle for \"index\" type with subtype = trajectories", call.=FALSE)
                } else if (dotsNames[1] == "rectangle") {
                    argoFloatsDebug(debug, "subsetting an index by rectangle\n")
                    rectangle <- dots[[1]]
                    if (!is.list(dots[1]))
                        stop("in subset,argoFloats-method():\n  \"rectangle\" must be a list containing \"longitude\" and \"latitude\"", call.=FALSE)
                    if (2 != sum(c("longitude", "latitude") %in% sort(names(rectangle))))
                        stop("in subset,argoFloats-method():\n  \"rectangle\" must be a list containing \"longitude\" and \"latitude\"", call.=FALSE)
                    if (!istraj) {
                        keeplon <- rectangle$longitude[1] <=x[["longitude"]] & x[["longitude"]] <= rectangle$longitude[2]
                        keeplat <- rectangle$latitude[1] <= x[["latitude"]] & x[["latitude"]] <= rectangle$latitude[2]
                        ok <- is.finite(keeplon) & is.finite(keeplat)
                        keeplon[!ok] <- FALSE
                        keeplat[!ok] <- FALSE
                        keep <- keeplon & keeplat
                    } else if (istraj) {
                        if (!requireNamespace("sf", quietly=TRUE))
                            stop("must install sf package for subset(...,circle,...) to work with trajectory files")
                        sE <- rectangle$longitude[2]
                        sW <- rectangle$longitude[1]
                        sS <- rectangle$latitude[1]
                        sN <- rectangle$latitude[2]
                        s <- rbind(c(sW,sS), c(sW, sN), c(sE,sN), c(sE, sS), c(sW,sS))
                        S <- sf::st_polygon(list(s))
                        n <- nrow(x@data$index)
                        keep <- rep(FALSE, n)
                        for (i in seq_len(n)) {
                            #message("i equals ", i)
                            #print(x@data$index[i,])
                            tE <- as.numeric(x@data$index$longitude_max[i])
                            tW <- as.numeric(x@data$index$longitude_min[i])
                            tS <- as.numeric(x@data$index$latitude_min[i])
                            tN <- as.numeric(x@data$index$latitude_max[i])
                            t <- rbind(c(tW,tS), c(tW, tN), c(tE,tN), c(tE, tS), c(tW,tS))
                            T <- sf::st_polygon(list(t))
                            intersection <- sf::st_intersection(T, S)
                            keep[i] <- length(intersection) > 0
                            #print(intersects)
                            #message('intersects ', length(intersects) > 0)
                        }
                    }
                    x@data$index <- x@data$index[keep, ]
                    x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                                paste("subset index type by rectangle with longitude= ", rectangle$longitude, " and latitude= ", rectangle$latitude ))
                    if (!silent)
                        message("Kept ", sum(keep), " cycles (", sprintf("%.3g", 100*sum(keep)/N), "%)")
                } else if (dotsNames[1] == "parameter") {
                    argoFloatsDebug(debug, "subsetting an index by parameter\n")
                    parameter <- dots[[1]]
                    if (is.list(dots[1]))
                        parameters <- unlist(parameter)
                    nparameters <- length(parameters)
                    parametersList <- lapply(x[["parameters"]], function(p) strsplit(p, " ")[[1]])
                    keep <- unlist(lapply(parametersList, function(pl) nparameters == sum(parameters %in% pl)))
                    #if (sum(keep) < 1)
                    #warning("in subset,argoFloats-method(..., parameter):\n  found no profiles with given parameter", call.=FALSE)
                    if (!silent && !istraj) {
                        message("Kept ", sum(keep), " cycles (", sprintf("%.3g", 100*sum(keep)/N), "%)")
                    } else if (!silent && istraj) {
                        message("Kept ", sum(keep), " trajectories (", sprintf("%.3g", 100*sum(keep)/N), "%)")
                    }
                    x@data$index <- x@data$index[keep, ]
                    x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste("subset index type by parameter= ",parameter))
                } else if (dotsNames[1] == "polygon" && !istraj) {
                    argoFloatsDebug(debug, "subsetting an index by polygon\n")
                    if (!requireNamespace("sf", quietly=TRUE))
                        stop("must install.packages(\"sf\") for subset() by polygon to work")
                    polygon <- dots[[1]]
                    if(!is.list(dots[1]))
                        stop("in subset,argoFloats-method():\n  \"polygon\" must be a list", call.=FALSE)
                    if (length(polygon) != 2)
                        stop("in subset,argoFloats-method():\n  \"polygon\" must be a list of two elements", call.=FALSE)
                    if (2 != sum(c("longitude", "latitude") %in% names(polygon)))
                        stop("in subset,argoFloats-method():\n  \"polygon\" must be a list containing \"longitude\" and \"latitude\"", call.=FALSE)
                    plat <- polygon$latitude
                    plon <- polygon$longitude
                    if (!length(plat) > 2)
                        stop("Must give at least three latitude coordinates.")
                    if (!length(plon) > 2)
                        stop("Must give at least three longitude coordinates.")
                    if (length(plat) != length(plon))
                        stop("lengths of polygon$longitude and polygon$latitude must match, but they are ",
                            length(plat), " and ", length(plon))
                    if ((head(plon, 1) != tail(plon, 1)) || head(plat, 1) != tail(plat, 1)) {
                        #warning("In subset,argoFloats-method(): Closing the polygon, since the first and last points did not match.\n", call.=FALSE)
                        plon <- c(plon, plon[1])
                        plat <- c(plat, plat[1])
                    }
                    alon <- x[["longitude"]]
                    alat <- x[["latitude"]]
                    ## We need the *index* of points to keep, and not just a lon-lat subset of
                    ## points.  It is not too difficult to get the index with the sp
                    ## package, but the only solution I could come up with using the sf
                    ## package is to tack 1,2,3,etc onto the lon-lat points as a third
                    ## dimension, so that after we select for the points inside, we can skim
                    ## that third dimension and that gives us the keep value that we need. There
                    ## may be a more straightforward way, but my (admittedly shallow) reading
                    ## of the sf function list did not uncover anything promising, and my
                    ## tests show that this scheme works.
                    ##
                    ## See https://github.com/ArgoCanada/argoFloats/issues/86
                    ok <- is.finite(alon) & is.finite(alat)
                    if (!requireNamespace("sf", quietly=TRUE))
                        stop("must install sf package for subset(...,polygon,...) to work")
                    Polygon <- sf::st_polygon(list(outer=cbind(plon, plat, rep(0, length(plon)))))
                    ## DOES NOT WORK (REQUIRES OTHER SOFTWARE??): Polygon <- sf::st_make_valid(Polygon)
                    if (!is.finite(sf::st_is_valid(Polygon))) {
                        errorMessage <- sf::st_is_valid(Polygon, reason=TRUE)
                        stop(paste0("Error in subset,argoFloats-method():\n  polygon is invalid, because of ", errorMessage), call.=FALSE)
                    }
                    ## multipoint does not permit NA values, so we set them to zero and remove them later
                    Points <- sf::st_multipoint(cbind(ifelse(ok, alon, 0),
                            ifelse(ok, alat, 0),
                            seq_along(alon)))
                    if (!sf::st_is_valid(Points)) {
                        errorMessage <- sf::st_is_valid(Points, reason=TRUE)
                        stop(paste0("Error in subset,argoFloats-method():\n  \"Points\" is invalid, because of ", errorMessage), call.=FALSE)
                    }
                    Intersection <- sf::st_intersection(Points, Polygon)
                    keep <- Intersection[,3]
                    if (!silent)
                        message("Kept ", length(keep), " cycles (", sprintf("%.3g", 100*length(keep)/N), "%)")
                    x@data$index <- x@data$index[keep, ]
                    x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste("subset index type by polygon"))
                } else if (dotsNames[1] == "polygon" && istraj) {
                        stop("in subset,argoFloats-method(): cannot subset polygon for \"index\" type with subtype = trajectories", call.=FALSE)
                } else if (dotsNames[1] == "time") {
                    argoFloatsDebug(debug, "subsetting an index by time\n")
                    time <- dots[[1]]
                    if(!is.list(dots[1]))
                        stop("in subset,argoFloats-method():\n  \"time\" must be a list", call.=FALSE)
                    if (2 != sum(c("from", "to") %in% names(time)))
                        stop("in subset,argoFloats-method():\n  \"time\" must be a list containing \"to\"and \"from\"", call.=FALSE)
                    if (length(time$from) != 1)
                        stop("from must be of length 1")
                    if (length(time$to) != 1)
                        stop("to must be of length 1")
                    if (!inherits(time$from, "POSIXt")) {
                        time$from <- try(as.POSIXct(time$from, tz="UTC"))
                        if (inherits(time$from, "try-error"))
                            stop("in subset,argoFloats-method():\n  cannot convert \"time$from\" to a POSIX time", call.=FALSE)
                    }
                    if (!inherits(time$to, "POSIXt")) {
                        time$to <- try(as.POSIXct(time$to, tz="UTC"))
                        if (inherits(time$to, "try-error"))
                            stop("in subset,argoFloats-method():\n  cannot convert \"time$to\" to a POSIX time", call.=FALSE)
                    }
                    if (time$to <= time$from)
                        stop ("in subset,argoFloats-method():\n \"to\" must be greater than \"from\"", call.=FALSE)
                    argoFloatsDebug(debug, "from= ", format(time$from, "%Y-%m-%d %H:%M:%S %z"), "\n")
                    argoFloatsDebug(debug, "to= ", format(time$to, "%Y-%m-%d %H:%M:%S %z"), "\n")
                    keep <- time$from[1] <= x[["date"]] & x[["date"]] <= time$to[1]
                    keep[is.na(keep)] <- FALSE
                    if (!silent && !istraj) {
                        message("Kept ", sum(keep), " cycles (", sprintf("%.3g", 100*sum(keep)/N), "%)")
                    } else if (!silent &&istraj) {
                        message("Kept ", sum(keep), " trajectories (", sprintf("%.3g", 100*sum(keep)/N), "%)")
                    }
                    x@data$index <- x@data$index[keep, ]
                    x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste("subset index type by time from", time$from, "to ", time$to))
                } else if(dotsNames[1] == "institution") {
                    argoFloatsDebug(debug, "subsetting an index by institution\n")
                    institution <- dots[[1]]
                    if(!is.list(dots[1]))
                        stop("in subset,argoFloats-method():\n  \"institution\" must be a list")
                    if (length(institution) > 1)
                        stop("\"institution\" cannot hold more than one element")
                    keep <- grepl(institution, x@data$index$institution)
                    keep[is.na(keep)] <- FALSE
                    if (!silent && !istraj) {
                        message("Kept ", sum(keep), " cycles (", sprintf("%.3g", 100*sum(keep)/N), "%)")
                    } else if (!silent && istraj) {
                        message("Kept ", sum(keep), " trajectories (", sprintf("%.3g", 100*sum(keep)/N), "%)")
                    }
                    x@data$index <- x@data$index[keep, ]
                    x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste("subset index type by institution " ,institution))
                } else if (dotsNames[1] == "deep") {
                    argoFloatsDebug(debug, "subsetting an index by deep category\n")
                    deep <- dots[[1]]
                    if (!is.logical(deep))
                        stop("in subset,argoFloats-method():\n deep must be a logical vector indicating TRUE or FALSE", call.=FALSE)
                    if (deep) {
                        keep <- grep("849|862|864", x@data$index$profiler_type)
                    } else {
                        keep <- grep("849|862|864", x@data$index$profiler_type, invert=TRUE)
                    }
                    if (!silent && !istraj) {
                        message("Kept ", length(keep), " cycles (", sprintf("%.3g", 100*length(keep)/N), "%)")
                    } else if (!silent && istraj) {
                        message("Kept ", length(keep), " trajectories (", sprintf("%.3g", 100*length(keep)/N), "%)")
                    }
                    x@data$index <- x@data$index[keep, ]
                    x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste("subset index type to only include deep data"))
                } else if (dotsNames[1] == "ID") {
                    argoFloatsDebug(debug, "subsetting an index by ID\n")
                    ID <- as.character(dots[[1]]) # convert in case it is numeric
                    xID <- x[["ID"]]
                    keep <- rep(FALSE, length(xID))
                    file <- x@data$index$file
                    for (thisID in ID)
                        keep <- keep | grepl(thisID, xID)
                    if (!silent && !istraj) {
                        message("Kept ", sum(keep), " cycles (", sprintf("%.3g", 100*sum(keep)/N), "%)")
                    } else if (!silent && istraj) {
                        message("Kept ", sum(keep), " trajectories (", sprintf("%.3g", 100*sum(keep)/N), "%)")
                    }
                    x@data$index <- x@data$index[keep, ]
                    x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste("subset index type by float ID", ID))
                } else if (dotsNames[1] == "ocean" && !istraj) {
                    argoFloatsDebug(debug, "subsetting an index by ocean\n")
                    ocean <- dots[[1]]
                    if (!is.character(ocean))
                        stop("in subset,argoFloats-method() : \"ocean\" must be character value", call.=FALSE)
                    if (length(ocean) > 1)
                        stop("in subset,argoFloats-method():\n \"ocean\" cannot hold more than one element", call.=FALSE)
                    keep <- grepl(ocean, x@data$index$ocean)
                    keep[is.na(keep)] <- FALSE
                    if (!silent)
                        message("Kept ", sum(keep), " cycles (", sprintf("%.3g", 100.0*sum(keep)/N), "%)")
                    x@data$index <- x@data$index[keep, ]
                    x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste("subset index type by ocean", ocean))
                } else if (dotsNames[1] == "ocean" && istraj) {
                        stop("in subset,argoFloats-method(): cannot subset ocean for \"index\" type with subtype = trajectories", call.=FALSE)
                } else if (dotsNames[1] == "dataMode") {
                    argoFloatsDebug(debug, "subsetting an index by dataMode\n")
                    dataMode <- dots[[1]]
                    if (!is.character(dataMode))
                        stop("in subset,argoFloats-method():\n  \"dataMode\" must be character value",call.=FALSE)
                    if (dataMode == "delayed" && !istraj) {
                        keep <- grepl("^[a-z]*/[0-9]*/profiles/.{0,1}D.*$", x[["file"]])
                    } else if (dataMode == "delayed" && istraj) {
                        keep <- grepl("^[a-z]*/[0-9]*/[0-9]*_.{0,1}D.*$", x[["file"]])
                    } else if (dataMode == "realtime" && !istraj) {
                        keep <- grepl("^[a-z]*/[0-9]*/profiles/.{0,1}R.*$", x[["file"]])
                    } else if (dataMode == "realtime" && istraj) {
                        keep <- grepl("^[a-z]*/[0-9]*/[0-9]*_.{0,1}R.*$", x[["file"]])
                    } else {
                        stop("in subset,argoFloats-method():\n  \"dataMode\" must be either \"realtime\" or \"delayed\", not \"", dataMode, "\"", call.=FALSE)
                    }
                    if (!silent && !istraj) {
                        message("Kept ", sum(keep), " cycles (", sprintf("%.3g", 100.0*sum(keep)/N), "%)")
                    } else if (!silent && istraj) {
                        message("Kept ", sum(keep), " trajectories (", sprintf("%.3g", 100.0*sum(keep)/N), "%)")
                    }

                    x@data$index <- x@data$index[keep, ]
                    x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste("subset index type by dataMode", dataMode))
                } else if (dotsNames[1] == "cycle" && !istraj) {
                    cycle <- dots[[1]]
                    if (!is.character(cycle) & !is.numeric(cycle))
                        stop("in subset,argoFloats-method() : \"cycle\" must be character value or numeric value", call.=FALSE)
                    ## Calculate 'keep', a logical vector that will be used for the actual subsetting.
                    xcycle <- x[["cycle"]]
                    # If cycle is numeric, we must convert it to character (so e.g. 1 becomes "001")
                    if (is.numeric(cycle)) {
                        cycle <- sprintf("%03d", as.integer(cycle))
                        argoFloatsDebug(debug, "subsetting an index by cycle of numeric type\n")
                    } else if (is.character(cycle)) {
                        argoFloatsDebug(debug, "subsetting an index by cycle of character type\n")
                    } else {
                        stop("cycle must be character or numeric")
                    }
                    keep <- rep(FALSE, length(xcycle))
                    for (thisCycle in cycle)
                        keep <- keep | grepl(thisCycle, xcycle)
                    nkeep <- sum(keep)
                    if (nkeep < 1)
                        warning("In subset,argoFloats-method(..., parameter) : found no profiles with given cycle(s)", call.=FALSE)
                    if (!silent)
                        message("Kept ", nkeep, " cycles (", sprintf("%.3g", 100*nkeep/N), "%)")
                    x@data$index <- x@data$index[keep, ]
                    x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste("subset index type by cycle=", cycle))
                } else if (dotsNames[1] == "cycle" && istraj) {
                        stop("in subset,argoFloats-method(): cannot subset cycle for \"index\" type with subtype = trajectories", call.=FALSE)
                } else if (dotsNames[1] == "direction" && !istraj) {
                    argoFloatsDebug(debug, "subsetting an index by direction\n")
                    direction <- dots[[1]]
                    if (!is.character(direction))
                        stop("in subset,argoFloats-method():\n  \"direction\" must be character value of either \"ascent\" or \"descent\"", call.=FALSE)
                    if (direction == "ascent") {
                        keep <- grepl("^.*[^D].nc$", x@data$index$file)
                    } else if (direction == "descent") {
                        keep <- grepl("^.*D.nc$", x@data$index$file)
                    } else {
                        stop("in subset,argoFloats-method():\n  \"direction\" must be either \"ascent\" or \"descent\", not \"", direction, "\"", call.=FALSE)
                    }
                    x@data$index <- x@data$index[keep, ]
                    x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste("subset index type by direction=", direction))
                } else if (dotsNames[1] == "direction" && istraj) {
                        stop("in subset,argoFloats-method(): cannot subset direction for \"index\" type with subtype = trajectories", call.=FALSE)
                } else if (dotsNames[1] == "section" && !istraj) {
                    # The following link shows a test of this method.
                    # https://github.com/ArgoCanada/argoFloats/issues/397#issuecomment-798960312
                    if (!requireNamespace("s2", quietly=TRUE))
                        stop("must install.packages(\"s2\") to subset by section")
                    argoFloatsDebug(debug, "subsetting an index by section\n")
                    section <- dots[[1]]
                    if (!is.list(section))
                        stop("in subset,argoFloats-method() :\n  section must be a list containing \"longitude\", \"latitude\" and possibly \"width\" and \"segments\"", call.=FALSE)
                    # Handle mandatory components
                    if (!"longitude" %in% names(section))
                        stop("in subset,argoFloats-method() :\n  section must have a \"longitude\" entry", call.=FALSE)
                    if (!"latitude" %in% names(section))
                        stop("in subset,argoFloats-method() :\n  section must have a \"latitude\" entry", call.=FALSE)
                    if (length(section$longitude) != length(section$latitude))
                        stop("in subset,argoFloats-method() :\n  length of section$longitude (",
                             length(section$longitude), ") and section$latitude (", length(section$latitude), ") disagree", call.=FALSE)
                    # Handle optional components
                    if (!"width" %in% names(section))
                        section$width <- 100
                    if (!"segments" %in% names(section))
                        section$segments <- 100
                    if (!is.null(section$segments)) {
                        if (!is.numeric(section$segments) || section$segments > 0) {
                            argoFloatsDebug(debug, "subdividing lon,lat trace (of length ", length(section$longitude), ") to increase length to ", section$segments, "\n", sep="")
                            s <- seq(0, 1, length.out=length(section$longitude))
                            section$longitude <- stats::approx(s, section$longitude, seq(0, 1, length.out=section$segments))$y
                            section$latitude <- stats::approx(s, section$latitude, seq(0, 1, length.out=section$segments))$y
                        } else {
                            stop("in subset,argoFloats-method() :\n  section$segments must be NULL or a positive integer", call.=FALSE)
                        }
                    }
                    # Create a spine for the section.
                    sectionSpine <- s2::s2_make_line(section$longitude, section$latitude)
                    # Create points for float locations.  Blank the NAs, in case not allowed.
                    alon <- x[["longitude"]]
                    alat <- x[["latitude"]]
                    bad <- !is.finite(alon) | !is.finite(alat)
                    alon[bad] <- 0
                    alat[bad] <- 0
                    floatLocations <- s2::as_s2_geography(s2::s2_lnglat(alon, alat))
                    # Identify floats near the section spine
                    keep <- s2::s2_dwithin(floatLocations, sectionSpine, 1000*section$width)
                    argoFloatsDebug(debug, "sum(keep)=", sum(keep), " i.e. ",
                                    round(100*sum(keep)/length(keep), 3), "% (before accounting for NA values)\n", sep="")
                    keep <- keep & !bad
                    argoFloatsDebug(debug, "sum(keep)=", sum(keep), " i.e. ",
                                    round(100*sum(keep)/length(keep), 3), "% (after accounting for NA values)\n", sep="")
                    if (!silent)
                        message("Kept ", sum(keep), " cycles (", sprintf("%.3g", 100*sum(keep)/N), "%)")
                    x@data$index <- x@data$index[keep, ]
                    x@processingLog <- oce::processingLogAppend(x@processingLog,
                                                  paste0("subset index type by section with longitude=c(",
                                                        paste(section$longitude, collapse=", "), "), latitude=c(",
                                                        paste(section$latitude, collapse=", "), "), and width=",
                                                        section$width, " km"))
                } else if (dotsNames[1] == "section" && istraj) {
                        stop("in subset,argoFloats-method(): cannot subset section for \"index\" type with subtype = trajectories", call.=FALSE)
                } else {
                    stop("in subset,argoFloats-method():\n  the \"...\" argument \"", dotsNames[1], "\" is not permitted for an index-type object. The only valid choices are \"circle\", \"rectangle\", \"parameter\", \"polygon\", \"time\", \"institution\", \"deep\", \"ID\", \"ocean\", \"dataMode\", \"cycle\",\"direction\" and \"section\"", call.=FALSE)
                }
            }
        } else {
            ## subset is not missing
            if (length(dotsNames) != 0)
                stop("in subset,argoFloats-method():\n  cannot give both \"subset\" and \"...\" arguments", call.=FALSE)
            if (x@metadata$type == "index") {
                if (!silent) {
                    if (is.logical(subset)) # this simplifies the percentage count for the method
                        subset <- which(subset)
                    message("Kept ", length(subset), " cycles (", sprintf("%.3g", 100.0*length(subset)/dim(x@data$index)[1]), "%)")
                }
                x@data$index <- x@data$index[subset, ]
            } else {
                stop("in subset,argoFloats-method():\n  method not coded except for type=\"index\"", call.=FALSE)
            }
        }
        argoFloatsDebug(debug, "} # subset,argoFloats-method()\n", style="bold", sep="", unindent=1)
        x
    })
dankelley/argoFloats documentation built on April 18, 2024, 5:13 a.m.