Demonstrate how to build animated tracking maps using tracking data in Movebank, environmental covariates in track and raster annotations from EnvDATA, and the moveVis package written by Jakob Schwalb-Willmann. Subsequent examples become more complete and cover more complicated situations.
Notes: * Consider exploring tracking data in Movebank, DynamoVis (https://doi.org/10.13020/D6PH49) and/or Google Maps first to evaluate datasets you’re not yet familiar with and plan out animations. * See more about the moveVis package at http://movevis.org.
library(ggplot2)
library(moveVis)
library(move)
library(magrittr)
library(raster)
options(max.print=100)
options(width=95)
Load the Svalbard geese dataset (Griffin, 2014, https://doi.org/10.5441/001/1.5k6b1364).
geese.move <- move("data/annotations/Svalbard geese 1k 16d NDVI-3281758398705165226/Svalbard geese 1k 16d NDVI-3281758398705165226.csv")
geese.move
## class : MoveStack
## features : 24488
## extent : -4.029, 22.421, 54.15566, 78.71467 (xmin, xmax, ymin, ymax)
## coord. ref. : +proj=longlat +ellps=WGS84 +datum=WGS84 +towgs84=0,0,0
## variables : 6
## names : event.id, timestamp, comments, height.raw, MODIS.Land.Vegetation.Indices.1km.16d.Aqua.NDVI, MODIS.Land.Vegetation.Indices.1km.16d.Terra.NDVI
## min values : 369331563, 2006-04-05 07:00:00, 170563-07, -0.1, 1.000006e-02, 1.000000e-02
## max values : 369356755, 2011-06-18 17:00:00, 86828-09, NULL, -9.997746e-03, -9.998666e-02
## timestamps : 2006-04-05 07:00:00 ... 2011-06-18 17:00:00 Time difference of 1900 days (start ... end, duration)
## sensors : gps
## indiv. data : visible, algorithm.marked.outlier, sensor.type, individual.taxon.canonical.name, tag.local.identifier, individual.local.identifier, study.name
## min ID Data : true, NA, gps, Branta leucopsis, 33102, X170563, Migration timing in barnacle geese (Svalbard) (data from Kölzsch et al. and Shariatinajafabadi et al. 2014)
## max ID Data : true, NA, gps, Branta leucopsis, 186827, X86828, Migration timing in barnacle geese (Svalbard) (data from Kölzsch et al. and Shariatinajafabadi et al. 2014)
## individuals : X33102, X33103, X33104, X33145, X33953, X33954, X64685, X64687, X70564, X70565, X70566, X70567, X70568, X70618, X70619
## study name : Migration timing in barnacle geese (Svalbard) (data from Kölzsch et al. and Shariatinajafabadi et al. 2014)
## date created: 2018-09-12 16:39:55
As shown in “timestamps” this study covers 5 years (4/5/2006 to 6/18/2011, 1900 days). Before building animations, check the spread of the data over time.
geese.df <- as.data.frame(geese.move)
ggplot() +
geom_point(data = geese.df, aes(x = timestamp, y = individual.local.identifier)) +
theme(legend.position = "none") # hide legend
Let’s reduce it to the migration with the largest number of birds (spring 2007).
geese.s07 <- subset(geese.move, timestamp >= as.POSIXct('2007-04-01 00:00:00.000')
& timestamp <= as.POSIXct('2007-06-15 00:00:00.000'))
Evaluate tracking data for sampling rates if unknown. Use this information to help decide the temporal resolution at which to align the data for the animation.
unique(timestamps(geese.s07))
## [1] "2007-04-01 10:00:00 UTC" "2007-04-01 12:00:00 UTC" "2007-04-01 14:00:00 UTC"
## [4] "2007-04-01 16:00:00 UTC" "2007-04-01 18:00:00 UTC" "2007-04-02 04:00:00 UTC"
## [7] "2007-04-02 06:00:00 UTC" "2007-04-02 08:00:00 UTC" "2007-04-02 10:00:00 UTC"
## [10] "2007-04-02 12:00:00 UTC" "2007-04-02 14:00:00 UTC" "2007-04-02 16:00:00 UTC"
## [13] "2007-04-02 18:00:00 UTC" "2007-04-02 20:00:00 UTC" "2007-04-03 06:00:00 UTC"
## [16] "2007-04-03 08:00:00 UTC" "2007-04-03 10:00:00 UTC" "2007-04-03 12:00:00 UTC"
## [19] "2007-04-03 14:00:00 UTC" "2007-04-03 16:00:00 UTC" "2007-04-03 18:00:00 UTC"
## [22] "2007-04-04 04:00:00 UTC" "2007-04-04 06:00:00 UTC" "2007-04-04 08:00:00 UTC"
## [25] "2007-04-04 10:00:00 UTC" "2007-04-04 12:00:00 UTC" "2007-04-04 14:00:00 UTC"
## [28] "2007-04-04 16:00:00 UTC" "2007-04-04 18:00:00 UTC" "2007-04-04 20:00:00 UTC"
## [31] "2007-04-05 04:00:00 UTC" "2007-04-05 06:00:00 UTC" "2007-04-05 08:00:00 UTC"
## [34] "2007-04-05 16:00:00 UTC" "2007-04-05 18:00:00 UTC" "2007-04-05 20:00:00 UTC"
## [37] "2007-04-06 04:00:00 UTC" "2007-04-06 06:00:00 UTC" "2007-04-06 08:00:00 UTC"
## [40] "2007-04-06 10:00:00 UTC" "2007-04-06 12:00:00 UTC" "2007-04-06 14:00:00 UTC"
## [43] "2007-04-06 16:00:00 UTC" "2007-04-06 18:00:00 UTC" "2007-04-07 04:00:00 UTC"
## [46] "2007-04-07 06:00:00 UTC" "2007-04-07 08:00:00 UTC" "2007-04-07 10:00:00 UTC"
## [49] "2007-04-07 12:00:00 UTC" "2007-04-07 14:00:00 UTC" "2007-04-08 04:00:00 UTC"
## [52] "2007-04-08 06:00:00 UTC" "2007-04-08 08:00:00 UTC" "2007-04-08 10:00:00 UTC"
## [55] "2007-04-08 12:00:00 UTC" "2007-04-08 14:00:00 UTC" "2007-04-08 16:00:00 UTC"
## [58] "2007-04-08 18:00:00 UTC" "2007-04-09 10:00:00 UTC" "2007-04-09 12:00:00 UTC"
## [61] "2007-04-09 14:00:00 UTC" "2007-04-09 16:00:00 UTC" "2007-04-09 18:00:00 UTC"
## [64] "2007-04-09 20:00:00 UTC" "2007-04-10 06:00:00 UTC" "2007-04-10 08:00:00 UTC"
## [67] "2007-04-10 10:00:00 UTC" "2007-04-10 12:00:00 UTC" "2007-04-10 14:00:00 UTC"
## [70] "2007-04-10 16:00:00 UTC" "2007-04-10 18:00:00 UTC" "2007-04-10 20:00:00 UTC"
## [73] "2007-04-11 10:00:00 UTC" "2007-04-11 12:00:00 UTC" "2007-04-11 14:00:00 UTC"
## [76] "2007-04-11 16:00:00 UTC" "2007-04-11 18:00:00 UTC" "2007-04-11 20:00:00 UTC"
## [79] "2007-04-12 04:00:00 UTC" "2007-04-12 06:00:00 UTC" "2007-04-12 08:00:00 UTC"
## [82] "2007-04-12 10:00:00 UTC" "2007-04-12 12:00:00 UTC" "2007-04-12 14:00:00 UTC"
## [85] "2007-04-12 16:00:00 UTC" "2007-04-12 18:00:00 UTC" "2007-04-12 20:00:00 UTC"
## [88] "2007-04-14 04:00:00 UTC" "2007-04-14 06:00:00 UTC" "2007-04-14 08:00:00 UTC"
## [91] "2007-04-14 16:00:00 UTC" "2007-04-14 18:00:00 UTC" "2007-04-14 20:00:00 UTC"
## [94] "2007-04-15 10:00:00 UTC" "2007-04-15 12:00:00 UTC" "2007-04-15 14:00:00 UTC"
## [97] "2007-04-15 16:00:00 UTC" "2007-04-15 18:00:00 UTC" "2007-04-15 20:00:00 UTC"
## [100] "2007-04-16 04:00:00 UTC"
## [ reached 'max' / getOption("max.print") -- omitted 1040 entries ]
timeLag(geese.s07, unit = "hours")
## $X64687
## [1] 2 2 2 2 10 2 2 2 2 2 2 2 2 10 2 2 2 2 2 2 10 2 2 2 2 2 2 2 2 8
## [31] 2 2 8 2 2 8 2 2 2 2 2 2 2 10 2 2 2 2 2 14 2 2 2 2 2 2 2 16 2 2
## [61] 2 2 2 10 2 2 2 2 2 2 2 14 2 2 2 2 2 8 2 2 2 2 2 2 2 2 32 2 2 8
## [91] 2 2 14 2 2 2 2 2 8 2
## [ reached getOption("max.print") -- omitted 333 entries ]
##
## $X70564
## [1] 1 1 1 1 1 3 2 8 2 2 2 2 2 2 2 20 2 2 14 2 2 2 2 2 2 8 2 8 2 2
## [31] 2 8 2 2 2 2 2 2 2 2 8 2 2 2 2 44 2 2 2 2 2 2 10 2 2 2 2 2 2 2
## [61] 8 2 2 2 2 2 2 2 14 2 2 8 8 2 2 2 2 2 2 2 14 2 2 2 2 2 2 8 2 2
## [91] 2 2 2 2 2 14 2 2 2 2
## [ reached getOption("max.print") -- omitted 405 entries ]
##
## $X70565
## [1] 2 2 2 2 2 2 2 8 2 2 2 2 2 2 2 2 14 30 2 2 10 2 2 2 2 2 2 2 10 2
## [31] 2 2 2 2 2 2 10 2 2 2 2 16 2 8 2 12 2 2 2 2 2 2 2 8 2 2 8 2 16 2
## [61] 2 2 2 2 10 2 2 2 2 2 2 2 8 2 2 2 2 2 2 2 2 10 2 2 2 2 2 2 2 10
## [91] 2 2 2 2 2 2 2 10 2 2
## [ reached getOption("max.print") -- omitted 471 entries ]
##
## $X70566
## [1] 2 2 68 2 2 8 2 2 2 2 2 2 2 2 8 2 2 44 2 2 8 2 2 8 2 2 2 2 2 2
## [31] 2 2 10 2 2 2 2 2 2 2 14 2 2 2 2 2 8 2 2 2 2 2 2 2 2 8 2 2 2 2
## [61] 2 2 2 2 8 2 2 2 2 2 14 2 2 26 2 2 2 2 2 8 2 4 2 2 2 2 2 10 2 8
## [91] 2 2 32 2 2 20 2 22 2 2
## [ reached getOption("max.print") -- omitted 413 entries ]
##
## $X70567
## [1] 2 2 2 2 40 2 2 2 2 2 8 2 2 2 2 2 2 2 2 8 2 2 26 2 2 44 2 2 2 2
## [31] 10 2 2 2 2 2 50 2 16 2 2 2 2 2 8 2 2 2 2 2 2 2 2 10 2 26 2 2 14 2
## [61] 2 2 2 2 2 2 2 14 2 2 2 2 2 8 2 2 56 2 2 32 2 2 50 2 2 2 2 2 20 2
## [91] 2 8 2 2 2 2 2 2 2 2
## [ reached getOption("max.print") -- omitted 410 entries ]
##
## $X70568
## [1] 2 2 2 2 2 14 2 2 2 2 2 8 2 2 2 2 2 2 2 2 62 2 2 2 2 2 8 2 2 2
## [31] 2 2 2 12 2 2 20 2 2 2 2 2 2 2 2 8 2 2 2 2 2 2 2 2 8 2 2 2 2 2
## [61] 2 2 2 8 2 2 26 2 2 2 2 2 8 2 2 2 2 2 2 2 2 8 6 2 2 2 2 2 20 2
## [91] 2 8 2 2 50 2 2 74 2 2
## [ reached getOption("max.print") -- omitted 215 entries ]
##
## $X70618
## [1] 2 2 2 2 2 2 2 2 8 2 2 20 2 2 2 2 2 14 2 2 2 2 2 2 2 2 8 2 2 2
## [31] 2 2 2 2 2 8 2 2 20 2 2 26 2 2 2 2 2 8 2 2 2 2 2 2 2 16 2 2 2 2
## [61] 2 10 2 8 2 2 8 2 2 2 2 2 2 2 2 8 2 2 2 2 2 14 2 2 2 2 2 20 2 2
## [91] 2 2 2 8 2 2 2 2 2 2
## [ reached getOption("max.print") -- omitted 524 entries ]
##
## $X70619
## [1] 2 2 2 2 2 2 2 10 2 2 2 2 2 14 2 2 2 2 2 2 2 2 8 2 2 2 2 2 2 2
## [31] 2 8 2 2 2 2 2 2 2 2 8 2 2 2 2 2 2 2 2 8 2 2 68 2 2 2 2 2 2 2
## [61] 2 10 2 32 2 2 8 2 2 2 2 2 2 2 2 8 2 2 2 2 2 2 2 2 8 2 2 8 2 2
## [91] 8 2 2 2 2 2 2 2 2 20
## [ reached getOption("max.print") -- omitted 471 entries ]
##
## $X170563
## [1] 4 1 1 2 2 8 2 2 2 8 2 8 2 2 2 2 2 2 20 2 2 2 10 2 2 2 2 2 2 2
## [31] 10 2 2 2 2 2 2 2 2 8 2 2 2 2 2 2 26 2 8 2 2 2 2 2 2 2 2 10 2 2
## [61] 2 2 2 2 2 10 2 2 32 2 8 2 2 2 8 2 22 2 8 8 2 2 38 2 2 8 2 8 2 2
## [91] 2 2 2 2 2 2 8 2 2 2
## [ reached getOption("max.print") -- omitted 439 entries ]
Align tracking data to uniform temporal resolution for interpretation by frames_spatial.
geese <- align_move(geese.s07, res = 12, unit = "hours", spaceMethod = "greatcircle")
Could use 2-hr resolution for a final product but 12-hr should speed up processing.
Create map frames for animation (see p.25 of MoveVis manual). Note that equidistant = TRUE does not mean that the map will be displayed in an equidistant projection (i.e. that preserves distances). Instead it causes frames_spatial to stretch the displayed area to an square extent. If equidistant = FALSE, the extent is displayed in the projection-native axis ratio.
frames <- frames_spatial(geese, map_service = "osm", map_type = "watercolor",
equidistant = FALSE, path_legend = T, path_legend_title = "Geese",
alpha = 0.5)
## Processing movement data...
## Retrieving and compositing basemap imagery...
## Assigning raster maps to frames...
## Creating frames...
Have a look at one of the frames.
frames[[100]]