|
6 | 6 | #' |
7 | 7 | #' @inheritParams frames_spatial |
8 | 8 | #' @param m \code{move2} object, which is allowed to contain irregular timestamps and diverging temporal resolutions. |
9 | | -#' @param res either a \code{units} object representing the temporal resolution \code{m} should be aligned to, or a character being one of 'min', 'max', 'mean' or 'median' to indicate how the target resolution should be derived from \code{m}. |
| 9 | +#' @param res either a \code{units} object representing the temporal resolution \code{m} should be aligned to, or a character being one of 'minimum', 'maximum', 'mean' or 'median' to indicate how the target resolution should be derived from \code{m}. |
10 | 10 | #' @param start_end_time \code{NULL} (default) or a vector of two POSIXct times (one start time and one end time for alignment). If \code{NULL}, the start and end time are retrieved from \code{m} and used for alignment. |
11 | 11 | #' \itemize{ |
12 | 12 | #' \item \code{"minimum"} to use the smallest temporal resolution of \code{m} (default) |
13 | 13 | #' \item \code{"maximum"} to use the largest temporal resolution of \code{m} |
14 | 14 | #' \item \code{"mean"} to use the rounded average temporal resolution of \code{m} |
15 | 15 | #' \item \code{"median"} to use the rounded median temporal resolution of \code{m} |
16 | 16 | #' } |
| 17 | +#' @param fill_na_values logical, whether to fill empty (\code{NA}) values of columns of \code{m} at interpolated locations (defaults to \code{TRUE}). Column values at interpolated locations are filled with the value of the temporally closest location. |
| 18 | +#' |
17 | 19 | #' @param ... deprecated arguments, including \code{digit}, \code{unit} and \code{spaceMethod}. |
18 | 20 | #' |
19 | 21 | #' @return \code{move2} object, with aligned positions at uniform temporal scale computed from \code{m}, ready to be used by \code{\link{frames_spatial}}. |
|
70 | 72 | #' |
71 | 73 | #' @export |
72 | 74 |
|
73 | | -align_move <- function(m, res = "minimum", start_end_time = NULL, ..., verbose = TRUE){ |
| 75 | +align_move <- function(m, res = "minimum", start_end_time = NULL, fill_na_values = TRUE, ..., verbose = TRUE){ |
74 | 76 | if(inherits(verbose, "logical")) options(moveVis.verbose = verbose) |
75 | 77 |
|
76 | 78 | extras <- list(...) |
77 | 79 | if(!is.null(extras$digit)) out("Argument 'digit' is deprecated. See ?moveVis::align_move for details.", type = 2) |
78 | 80 | if(!is.null(extras$unit)) out("Argument 'unit' is deprecated. See ?moveVis::align_move for details.", type = 2) |
79 | 81 | if(!is.null(extras$spaceMethod)) out("Argument 'spaceMethod' is deprecated. See ?moveVis::align_move for details.", type = 2) |
80 | 82 |
|
| 83 | + if(!is.logical(fill_na_values)) fill_na_values <- TRUE |
| 84 | + |
81 | 85 | # check inputs |
82 | 86 | .check_move2(m) |
83 | 87 | m_tracks <- split(m, mt_track_id(m)) |
84 | 88 | m_length <- if(mt_n_tracks(m) > 1) sapply(split(m, mt_track_id(m)), nrow) else nrow(m) |
85 | 89 | if(any(m_length < 2)) out(paste0("Individual track(s) ", paste0(which(m_length < 2), collapse = ", "), " of 'm' consist(s) of less than 2 locations. A minimum of 2 locations per indvidual track is required for alignment."), type = 3) |
86 | 90 |
|
87 | 91 | # check resolution and define resolution |
88 | | - if(all(!c(inherits(res, "units"), inherits(res, "character")))) out("Argument 'res' must either be a 'units' object or one of c('min', 'max', 'mean', 'median').", type = 3) |
| 92 | + if(all(!c(inherits(res, "units"), inherits(res, "character")))) out("Argument 'res' must either be a 'units' object or one of c('minimum', 'maximum', 'mean', 'median').", type = 3) |
89 | 93 | if(inherits(res, "units")){ |
90 | 94 | time_unit <- as_units("s") |
91 | 95 | is_time_unit <- ud_are_convertible(deparse_unit(res), deparse_unit(time_unit)) |
@@ -177,6 +181,43 @@ align_move <- function(m, res = "minimum", start_end_time = NULL, ..., verbose = |
177 | 181 | m_aligned <- m_aligned[order(m_aligned$timestamp),] |
178 | 182 | m_aligned <- m_aligned[order(mt_track_id(m_aligned)),] |
179 | 183 |
|
| 184 | + # fill variables |
| 185 | + if(isTRUE(fill_na_values)){ |
| 186 | + m_aligend_filled <- lapply(split(m_aligned, mt_track_id(m_aligned)), function(m_track){ |
| 187 | + for(x in names_attr){ |
| 188 | + this_attr <- m_track[[x]] |
| 189 | + |
| 190 | + m_track[[x]] <- sapply(1:length(this_attr), function(i){ |
| 191 | + if(!is.na(this_attr[i])){ |
| 192 | + this_attr[i] |
| 193 | + } else{ |
| 194 | + left <- if(i == 1) NULL else 1:(i-1) |
| 195 | + right <- if(i == length(this_attr)) NULL else (i+1):length(this_attr) |
| 196 | + |
| 197 | + if(!is.null(left)){ |
| 198 | + non_na <- left[which(!is.na(this_attr[left]))[1]] |
| 199 | + } else non_na <- NULL |
| 200 | + if(!is.null(right)){ |
| 201 | + non_na <- c(non_na, right[which(!is.na(this_attr[right]))[1]]) |
| 202 | + } |
| 203 | + |
| 204 | + non_na_diff <- abs(sapply(non_na, function(.non_na){ |
| 205 | + difftime( |
| 206 | + m_track[[mt_time_column(m_track)]][.non_na], |
| 207 | + m_track[[mt_time_column(m_track)]][i], |
| 208 | + units = "secs" |
| 209 | + ) |
| 210 | + })) |
| 211 | + |
| 212 | + this_attr[non_na[which.min(non_na_diff)]] |
| 213 | + } |
| 214 | + }) |
| 215 | + } |
| 216 | + return(m_track) |
| 217 | + }) |
| 218 | + m_aligned <- do.call(rbind, m_aligend_filled) |
| 219 | + } |
| 220 | + |
180 | 221 | # for now, we just return the aligned data |
181 | 222 | m_aligned <- m_aligned[m_aligned$interpolated,] |
182 | 223 | m_aligned$interpolated <- NULL |
|
0 commit comments