Time-class: sft_timestamp

One requirement for a movement object is it must have a measurement of time. This unit can be sequential numbers dictating the order in which points were taken, or actual time. For the sake of sftrack, we allow either measurement of time. Time objects are stored in a column as an sft_timestamp class. This class is at the column level and is a collection of time measurements for a set of spatial points or steps.

##   animal_id latitude longitude           timestamp height hdop vdop fix
## 1   TTP-058       NA        NA 2019-01-18 19:02:30     NA  0.0  0.0  NO
## 2   TTP-058 26.06945 -80.27906 2019-01-18 20:02:30      7  6.2  3.2  2D
## 3   TTP-058       NA        NA 2019-01-18 21:02:30     NA  0.0  0.0  NO
## 4   TTP-058       NA        NA 2019-01-18 22:02:30     NA  0.0  0.0  NO
## 5   TTP-058 26.06769 -80.27431 2019-01-18 23:02:30    858  5.1  3.2  2D
## 6   TTP-058 26.06867 -80.27930 2019-01-19 00:02:30    350  1.9  3.2  3D
time <- make_timestamp(raccoons$timestamp)
time[1:10]
##  [1] "2019-01-18 19:02:30 EST" "2019-01-18 20:02:30 EST"
##  [3] "2019-01-18 21:02:30 EST" "2019-01-18 22:02:30 EST"
##  [5] "2019-01-18 23:02:30 EST" "2019-01-19 00:02:30 EST"
##  [7] "2019-01-19 01:02:30 EST" "2019-01-19 02:02:04 EST"
##  [9] "2019-01-19 03:02:30 EST" "2019-01-19 12:02:30 EST"

When time is represented by a vector with one timestamp for each point, the time class acts just like a POSIXct or numeric object (and actually supersedes both).

time[1:10] + 60
##  [1] "2019-01-18 19:03:30 EST" "2019-01-18 20:03:30 EST"
##  [3] "2019-01-18 21:03:30 EST" "2019-01-18 22:03:30 EST"
##  [5] "2019-01-18 23:03:30 EST" "2019-01-19 00:03:30 EST"
##  [7] "2019-01-19 01:03:30 EST" "2019-01-19 02:03:04 EST"
##  [9] "2019-01-19 03:03:30 EST" "2019-01-19 12:03:30 EST"
class(time)
## [1] "sft_timestamp" "POSIXct"       "POSIXt"

The only difference is it retains a few extra attributes at the column level for ease of calculating upon the entire list. These include retaining the timezone information and a descriptor if the list contains POSIX or numeric measurement of time. These attributes are only used by sftrack functions. The real tzone attribute reamains in each row level object.

## $class
## [1] "sft_timestamp" "POSIXct"       "POSIXt"       
## 
## $tzone
## [1] "EST5EDT"
## 
## $type
## [1] "POSIX"
attributes(time[[1]])
## $class
## [1] "sft_timestamp" "POSIXct"       "POSIXt"       
## 
## $tzone
## [1] "EST5EDT"

Timestamp classes are automatically created in sftrack and sftraj objects, though they differ in the structure of time. An sftrack object only requires the start time (\(t_1\)) and thus stores only one measurement of time similar as seen above.

racc_track
## sftrack (*locations*) with 445 features and 10 fields
## geometry:   "geometry" (XY, CRS: WGS 84)
## timestamps: "timestamp" (integer)
## groupings:  "sft_group" (*id*, *month*)
## -------------------------------
##   animal_id latitude longitude           timestamp height hdop vdop fix
## 1   TTP-058       NA        NA 2019-01-18 19:02:30     NA  0.0  0.0  NO
## 2   TTP-058 26.06945 -80.27906 2019-01-18 20:02:30      7  6.2  3.2  2D
## 3   TTP-058       NA        NA 2019-01-18 21:02:30     NA  0.0  0.0  NO
## 4   TTP-058       NA        NA 2019-01-18 22:02:30     NA  0.0  0.0  NO
## 5   TTP-058 26.06769 -80.27431 2019-01-18 23:02:30    858  5.1  3.2  2D
## 6   TTP-058 26.06867 -80.27930 2019-01-19 00:02:30    350  1.9  3.2  3D
##                 sft_group                   geometry
## 1 (id: TTP-058, month: 1)                POINT EMPTY
## 2 (id: TTP-058, month: 1) POINT (-80.27906 26.06945)
## 3 (id: TTP-058, month: 1)                POINT EMPTY
## 4 (id: TTP-058, month: 1)                POINT EMPTY
## 5 (id: TTP-058, month: 1) POINT (-80.27431 26.06769)
## 6 (id: TTP-058, month: 1)  POINT (-80.2793 26.06867)

Trajectories, require a start and end time for each step. This requires a multi-dimensional time object.

racc_traj
## sftraj (*steps*) with 445 features and 10 fields
## geometry:   "geometry" (XY, CRS: WGS 84)
## timestamps: "timestamp" (integer)
## groupings:  "sft_group" (*id*, *month*)
## -------------------------------
##   animal_id latitude longitude                                     timestamp
## 1   TTP-058       NA        NA (2019-01-18 19:02:30 --> 2019-01-18 20:02:30)
## 2   TTP-058 26.06945 -80.27906 (2019-01-18 20:02:30 --> 2019-01-18 21:02:30)
## 3   TTP-058       NA        NA (2019-01-18 21:02:30 --> 2019-01-18 22:02:30)
## 4   TTP-058       NA        NA (2019-01-18 22:02:30 --> 2019-01-18 23:02:30)
## 5   TTP-058 26.06769 -80.27431 (2019-01-18 23:02:30 --> 2019-01-19 00:02:30)
## 6   TTP-058 26.06867 -80.27930 (2019-01-19 00:02:30 --> 2019-01-19 01:02:30)
##   height hdop vdop fix               sft_group                       geometry
## 1     NA  0.0  0.0  NO (id: TTP-058, month: 1)                    POINT EMPTY
## 2      7  6.2  3.2  2D (id: TTP-058, month: 1)     POINT (-80.27906 26.06945)
## 3     NA  0.0  0.0  NO (id: TTP-058, month: 1)                    POINT EMPTY
## 4     NA  0.0  0.0  NO (id: TTP-058, month: 1)                    POINT EMPTY
## 5    858  5.1  3.2  2D (id: TTP-058, month: 1) LINESTRING (-80.27431 26.06...
## 6    350  1.9  3.2  3D (id: TTP-058, month: 1) LINESTRING (-80.2793 26.068...

Each row is a time interval that is independent of the other rows. So deleting a step (for instance with the same start time as a previous entries end time) does not affect the previous step in any way.

racc_traj[-2, ]
## sftraj (*steps*) with 444 features and 10 fields
## geometry:   "geometry" (XY, CRS: WGS 84)
## timestamps: "timestamp" (integer)
## groupings:  "sft_group" (*id*, *month*)
## -------------------------------
##   animal_id latitude longitude                                     timestamp
## 1   TTP-058       NA        NA (2019-01-18 19:02:30 --> 2019-01-18 20:02:30)
## 3   TTP-058       NA        NA (2019-01-18 21:02:30 --> 2019-01-18 22:02:30)
## 4   TTP-058       NA        NA (2019-01-18 22:02:30 --> 2019-01-18 23:02:30)
## 5   TTP-058 26.06769 -80.27431 (2019-01-18 23:02:30 --> 2019-01-19 00:02:30)
## 6   TTP-058 26.06867 -80.27930 (2019-01-19 00:02:30 --> 2019-01-19 01:02:30)
## 7   TTP-058 26.06962 -80.27908 (2019-01-19 01:02:30 --> 2019-01-19 02:02:04)
##   height hdop vdop fix               sft_group                       geometry
## 1     NA  0.0  0.0  NO (id: TTP-058, month: 1)                    POINT EMPTY
## 3     NA  0.0  0.0  NO (id: TTP-058, month: 1)                    POINT EMPTY
## 4     NA  0.0  0.0  NO (id: TTP-058, month: 1)                    POINT EMPTY
## 5    858  5.1  3.2  2D (id: TTP-058, month: 1) LINESTRING (-80.27431 26.06...
## 6    350  1.9  3.2  3D (id: TTP-058, month: 1) LINESTRING (-80.2793 26.068...
## 7     11  2.3  4.5  3D (id: TTP-058, month: 1) LINESTRING (-80.27908 26.06...

Functions to help access time

t1 and t2

t1 is a basic function to grab the starting point of any sftrack or sftraj object, as well as sft_timestamp classes directly. In most cases this is simply lapply(timestamp, function(x) x[[1]]).

# sftrack
t1(racc_track[1:5, ])
## [1] "2019-01-18 19:02:30 EST" "2019-01-18 20:02:30 EST"
## [3] "2019-01-18 21:02:30 EST" "2019-01-18 22:02:30 EST"
## [5] "2019-01-18 23:02:30 EST"
# sftraj
t1(racc_traj[1:5, ])
## [1] "2019-01-18 19:02:30 EST" "2019-01-18 20:02:30 EST"
## [3] "2019-01-18 21:02:30 EST" "2019-01-18 22:02:30 EST"
## [5] "2019-01-18 23:02:30 EST"
# sft_timestamp
t1(racc_traj$timestamp[1:5])
## [1] "2019-01-18 19:02:30 EST" "2019-01-18 20:02:30 EST"
## [3] "2019-01-18 21:02:30 EST" "2019-01-18 22:02:30 EST"
## [5] "2019-01-18 23:02:30 EST"

t2 is a more complex function. t2 is inherently easy in an sftraj where the true end point is stored in the timestamp, and in this case t2 is thus simply lapply(timestamp, function(x) x[[2]]).

t2(racc_traj[1:5,])
## [1] "2019-01-18 20:02:30 EST" "2019-01-18 21:02:30 EST"
## [3] "2019-01-18 22:02:30 EST" "2019-01-18 23:02:30 EST"
## [5] "2019-01-19 00:02:30 EST"

sftrack, POSIXt, or numeric vectors do not obviously have a \(t_2\). In these cases, because it is useful internally, and possibly to the user, t2 computes a lag of “time1” (i.e. takes the next record). When supplied with a sftrack the grouping is used to compute a more accurate \(t_2\) with the grouping considered. When a single vector is supplied with either sft_timestamp, POSIX, or numeric a simple lag of 1 is computed with time.

t2(racc_track[1:5, ])
## [1] "2019-01-18 20:02:30 EST" "2019-01-18 21:02:30 EST"
## [3] "2019-01-18 22:02:30 EST" "2019-01-18 23:02:30 EST"
## [5] NA
t2(Sys.time() + 1:10)
##  [1] "2021-07-01 16:00:07 CEST" "2021-07-01 16:00:08 CEST"
##  [3] "2021-07-01 16:00:09 CEST" "2021-07-01 16:00:10 CEST"
##  [5] "2021-07-01 16:00:11 CEST" "2021-07-01 16:00:12 CEST"
##  [7] "2021-07-01 16:00:13 CEST" "2021-07-01 16:00:14 CEST"
##  [9] "2021-07-01 16:00:15 CEST" NA
t2(1:10)
##  [1]  2  3  4  5  6  7  8  9 10 NA

If you’d like to calculate \(t_2\) with grouping considered on these 1 dimensional vectors, you can use t2_by_group, which requires \(t_1\) and the grouping.

grouping <- make_c_grouping(list(id = rep(1:2, 5)))
t2 <- t2_by_group(1:10, grouping)

data.frame(t1 = 1:10, t2 = t2, group = grouping)
##    t1 t2      id
## 1   1  3 (id: 1)
## 2   2  4 (id: 2)
## 3   3  5 (id: 1)
## 4   4  6 (id: 2)
## 5   5  7 (id: 1)
## 6   6  8 (id: 2)
## 7   7  9 (id: 1)
## 8   8 10 (id: 2)
## 9   9 NA (id: 1)
## 10 10 NA (id: 2)

Recalculate time in an sftraj object

While a step model of a sftraj generally assumes that both \(t_1\) and \(t_2\) of a step are independent of other steps, occasionally \(t_2\) may need to be recalculated in an sftraj (for instance because of outliers or subsetting). In order to do this you can use time_recalc(). This function only works on sftraj objects:

sub_traj <- racc_traj[-2, ]
(new_traj <- time_recalc(sub_traj))
## sftraj (*steps*) with 444 features and 10 fields
## geometry:   "geometry" (XY, CRS: WGS 84)
## timestamps: "timestamp" (integer)
## groupings:  "sft_group" (*id*, *month*)
## -------------------------------
##   animal_id latitude longitude                                     timestamp
## 1   TTP-058       NA        NA (2019-01-18 19:02:30 --> 2019-01-18 21:02:30)
## 3   TTP-058       NA        NA (2019-01-18 21:02:30 --> 2019-01-18 22:02:30)
## 4   TTP-058       NA        NA (2019-01-18 22:02:30 --> 2019-01-18 23:02:30)
## 5   TTP-058 26.06769 -80.27431 (2019-01-18 23:02:30 --> 2019-01-19 00:02:30)
## 6   TTP-058 26.06867 -80.27930 (2019-01-19 00:02:30 --> 2019-01-19 01:02:30)
## 7   TTP-058 26.06962 -80.27908 (2019-01-19 01:02:30 --> 2019-01-19 02:02:04)
##   height hdop vdop fix               sft_group                       geometry
## 1     NA  0.0  0.0  NO (id: TTP-058, month: 1)                    POINT EMPTY
## 3     NA  0.0  0.0  NO (id: TTP-058, month: 1)                    POINT EMPTY
## 4     NA  0.0  0.0  NO (id: TTP-058, month: 1)                    POINT EMPTY
## 5    858  5.1  3.2  2D (id: TTP-058, month: 1) LINESTRING (-80.27431 26.06...
## 6    350  1.9  3.2  3D (id: TTP-058, month: 1) LINESTRING (-80.2793 26.068...
## 7     11  2.3  4.5  3D (id: TTP-058, month: 1) LINESTRING (-80.27908 26.06...

This does not recalculate the geometries, that must be done separately with step_recalc.