Preamble

What this code does:

  1. Code for prepping data for importation to Google Earth, especially for making videos of animal movement paths

  2. Code for generating GIFs of animal movement from tracking data

##### Clear Environment #####
remove(list=ls())


#### load libraries ####
library(sp)
library(rgdal)
library(rgeos)
library(adehabitatLT)
library(sf)
library(ggplot2)
library(raster)
library(move)
library(moveVis)
library(vembedr)


Make a video of animal movement

We’ll use Google Earth Pro to make a video of animal movement, but first we’ll prep our data in R.


Load and prep data

First, we’ll load and view our data. This is dispersal data for a young male deer, and we will evaluate just the period of this animal’s dispersal.

#### load data ####
pts <- read.csv("Tutorial_data/movement_video_tutorial_data.csv", header = T)
pts$date <- as.POSIXct(pts$date, tz = "America/Chicago")
head(pts)
##          x        y                date         dx        dy      dist    dt        R2n   abs.angle   rel.angle   id burst idcollar dop     FixType    rel.deg    flag b.screen  speed_kmh
## 1 520149.9 290377.2 2017-05-13 04:01:50   49.97703 -28.79398  57.67839 14367  34565.358 -0.52269380  1.49233812 5700  5700    24219 5.2 val. GPS-3D   85.50468 no_flag  no_flag 0.02385982
## 2 520199.9 290348.4 2017-05-13 08:01:17  169.92049 -13.81863 170.48146 14375  57686.322 -0.08114553  0.44154827 5700  5700    24219 2.4 val. GPS-3D   25.29885 no_flag  no_flag 0.01445272
## 3 520369.8 290334.6 2017-05-13 12:00:52  -49.97725  16.01088  52.47927 14394 146997.221  2.83156009  2.91270561 5700  5700    24219 4.0 val. GPS-3D  166.88574 no_flag  no_flag 0.04269449
## 4 520319.9 290350.6 2017-05-13 16:00:46 -296.86325  89.40999 310.03538 14445 110388.231  2.84905143  0.01749134 5700  5700    24219 4.8 val. GPS-3D    1.00218 no_flag  no_flag 0.01312529
## 5 520023.0 290440.0 2017-05-13 20:01:31  100.95296 -20.21001 102.95603 14366   7397.729 -0.19758051 -3.04663195 5700  5700    24219 1.2 val. GPS-3D -174.55915 no_flag  no_flag 0.07726738
## 6 520123.9 290419.8 2017-05-14 00:00:57  -38.98788  15.10220  41.81066 14410  18603.008  2.77203330  2.96961381 5700  5700    24219 1.8 val. GPS-3D  170.14634 no_flag  no_flag 0.02579992
# view plot of trajectory, with path colored by date of location
ggplot(pts, aes(x = x, y = y, color = date)) + geom_path() + theme_bw() + coord_fixed()

Next, we’ll convert our movement data to the correct coordinate reference systems (CRS).

## convert to an sf object to facilitate reprojecting coordinates
mov <- pts %>%
  sf::st_as_sf(coords = c("x", "y"), crs = 6610)


## Reproject coordinates ##
mov.latlong <- st_transform(mov, crs = 4326) # convert to lat/long coordinates (for Google Earth example)
mov.nlcd <- st_transform(mov, crs = 3857) # convert to match NLCD coordinate reference system (for GIF example)


## Extract reprojected lat/long coordinates ##
coord <- data.frame(st_coordinates(mov.latlong))
colnames(coord) <- c("x", "y")
head(coord)
##           x        y
## 1 -89.99816 43.08611
## 2 -89.99754 43.08585
## 3 -89.99546 43.08573
## 4 -89.99607 43.08587
## 5 -89.99972 43.08668
## 6 -89.99848 43.08649



Write out GPX file which we will load into Google Earth for tracing the “live” track

Now that we’ve prepped our data, we can write it out into the file format for use in Google Earth Pro.

We’ll write a function for creating a GPX file.

## function for writing out GPX files
writeGPX <- function(lat, lon, time, out_file) { 
  o <- c('<gpx version="1.1" creator="R">','<trk>','<trkseg>') 
  if (missing(time)) 
    o <- c(o, paste('<trkpt lat="',lat,'" lon="',lon,'" />', sep='')) 
  else 
    o <- c(o, paste('<trkpt lat="',lat,'" lon="',lon,'"><time>',paste(gsub(' ','T', as.character(time)), 'Z', sep=''),'</time></trkpt>', sep='')) 
  o <- c(o, '</trkseg>', '</trk>', '</gpx>') 
  if (is.character(out_file) || inherits(out_file, "connection")) 
    cat(o, file=out_file, sep='\n') }

Next, we’ll use that function to write out our track file. We can also control the time scale of the animal movement that goes into this file. This data formatting can ultimately be used to make videos with data from a variety of sources - you just need lat/long coordinate data and time stamps.

## Google Earth will move along the track based on the time stamps in the imported file. Using the original time stamps makes the track go very 
# slowly, so we'll scale it to a faster rate. You could modify this line to aim for, for example, a constant movement rate along the track.
secs <- Sys.time() + c(0, cumsum(mov.latlong$dt[1:(nrow(coord)-1)]/10000)) # scale to movement rate
writeGPX(lat = coord$y, lon = coord$x, time = secs, out_file = "test_trk_zoomin.gpx")



Now we’ll open this file in Google Earth Pro

Open Google Earth Pro. To load data, click “File” –> “Open” and select the .gpx file we created in the last step. A pop-up box will appear; select “Create KML Tracks” and “Adjust altitudes to ground height” as shown below:



Your GPS data “track” should look like this once it’s loaded into Google Earth Pro:




Change the visual style of the track

You can easily change the visual style of the track. To start, expand the “GPS device” data so you can see “Tracks” - see the area in the red square in the photo below.



Next, follow the steps in the video below: right click on “Tracks” and select “Get info.” A pop-up window will appear. Click the button for “Style, Color” and then you can update the appearance of the track.



Change the appearance of a “tour.”

To make a video along the track, we’ll be making what Google Earth Pro calls a “tour.” Google Earth Pro has a variety of different settings to change the appearance of a tour, including the camera angle, viewing range, and speed. Follow along in the video below to see how to access and change tour preferences and how to watch a tour without recording it.



Record a “tour”

Once you are satisfied with the track and tour settings, you can record a video of the tour. The video below shows how to use Google Earth Pro’s “movie maker” function to make one of these videos. In brief, the steps are to:

  1. Start playing the tour of the selected track: With the track selected, click the folder icon (“Play tour”). This pops up a control panel in the bottom left of the map.

  2. Save the tour: Click the save button in the control panel from step 1 and give your tour an easy to remember name.

  3. Close the control panel: In order to access the “movie maker” function, you need to “x” out of the control panel from step 1.

  4. Record movie: Click “Tools” in the top menu and select “Movie Maker.” If you cannot click “Movie Maker” make sure you’ve closed the control panel from step 1. Select the tour you want to make a video of, then choose your output name, location, and other movie settings and create your movie! The video creation itself will take a little while to complete.



Edit your movie

After you’ve made your movie using Google Earth Pro, you can fine-tune elements using standard video editing software. This could include making clips of subsets of the video, adjusting the speed in different sections, adding text or audio to the video - and more! While I won’t include details of video editing here, some useful video editing software includes iMovie (Mac users) or Adobe Premiere Pro (Mac or Windows users).



Make a GIF of animal movement

In addition to making movies of animal movement in Google Earth Pro, we can also make gifs of animal movement in R.

Create a “move” object of the movement data

We want to use the movement data projected to the CRS that aligns with NLCD raster data (what we’ll use as a basemap).

dat.nlcd.co <- st_coordinates(mov.nlcd)
dat.nlcd <- st_drop_geometry(mov.nlcd)
dat.nlcd <- cbind(dat.nlcd, dat.nlcd.co)

With this data, we’ll create and align a “move” object.

# create the move object
deer_move <- move(dat.nlcd$X, dat.nlcd$Y, time = dat.nlcd$date, proj =  CRS("+init=epsg:3857"), animal = dat.nlcd$id)
# align movement data (uniform temporal resolution)
m <- align_move(deer_move, res = "mean", digit = "mean", unit = "hours", spaceMethod = "euclidean")

Load landscape layers

Next, we’ll load the landscape layers we want to use in our gif. In this case, we’re using a land cover raster (NLCD) and a layer of rivers/streams.

# load landscape layers
load("Tutorial_data/movement_video_tutorial_layers.Rdata")
nlcd <- layers$nlcd # nlcd raster for the focal area
rivs <- layers$rivs # rivers/streams SpatialLinesDataFrame for the study area
rf <- fortify(rivs) # convert for plotting

Create gif

We’ll use the MoveVis package to create the spatial frames for our gif.

### create spatial frames with a base raster map ###
frames <- frames_spatial(m, path_colours = c("red"),
                         # map_service = "osm", map_type = "terrain", alpha = 0.5, # can alternatively use in-package base maps (e.g., open street maps)
                         r_list = nlcd, crop_raster = T, r_times = Sys.time(), r_type = "discrete",
                         equidistant = F,
                         path_legend = F) %>% 
  add_gg(gg = expr(geom_path(data = rf, aes(x = long, y = lat, group = group), colour = "#476BA0")), data = rf) %>% # add rivers (though they'll plot on top of the track)
  # add other customizations
  add_gg(gg = expr(theme(legend.position = "none", axis.text.x = element_text(angle = 90)))) %>%
  add_gg(gg = expr(xlab("Easting (m)") )) %>%
  add_gg(gg = expr(ylab("Northing (m)") )) %>%
  add_northarrow() %>%
  add_scalebar(units = "km", colour = "black") %>%
  add_timestamps(m, type = "label") %>%
  # this controls the colors of the raster basemap
  add_colourscale(type = "discrete", 
                  colours = c("11" = "#476BA0",
                              "21" = "#D89382",
                              "22" = "#D89382",
                              "23" = "#D89382",
                              "24" = "#D89382",
                              "31" = "#B2ADA3",
                              "41" = "#68AA63",
                              "42" = "#68AA63",
                              "43" = "#68AA63",
                              "52" = "#CCBA7C",
                              "71" = "#B2ADA3",
                              "81" = "#fff7bc",
                              "82" = "#fff7bc",
                              "90" = "#B2ADA3",
                              "95" = "#B2ADA3"),
                  legend_title = "Land Cover") %>%
  add_progress()
## Checking temporal alignment...
## Processing movement data...
## Approximated animation duration: ≈ 1.2s at 25 fps for 30 frames
## Assigning raster maps to frames...
## Creating frames...
frames[[15]] # preview one of the frames, e.g. the 100th frame

If we’re satisfied with the product, we can write out our frames as a gif.

animate_frames(frames, out_file = "moveDeer_test_nlcd.gif") # this will take a couple of minutes




Session Info

sessionInfo()
## R version 4.2.0 (2022-04-22)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS Big Sur 11.7.7
## 
## Matrix products: default
## LAPACK: /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] ggforce_0.4.1         spatstat_2.3-4        spatstat.linnet_2.3-2 spatstat.core_2.4-4   rpart_4.1.16          nlme_3.1-157          spatstat.random_3.1-3 spatstat.geom_3.0-5   spatstat.data_3.0-0  
## [10] smacpod_2.5           ape_5.6-2             terra_1.6-41          vembedr_0.1.5         moveVis_0.10.5        move_4.1.8            geosphere_1.5-14      raster_3.6-11         rgeos_0.5-9          
## [19] rgdal_1.6-2           doRNG_1.8.2           rngtools_1.5.2        foreach_1.5.2         crawl_2.3.0           lubridate_1.9.2       adehabitatHR_0.4.19   adehabitatLT_0.3.25   CircStats_0.2-6      
## [28] boot_1.3-28           MASS_7.3-56           adehabitatMA_0.3.14   ade4_1.7-19           deldir_1.0-6          sp_1.6-0              momentuHMM_1.5.5      RColorBrewer_1.1-3    ggplot2_3.4.0        
## [37] sf_1.0-9             
## 
## loaded via a namespace (and not attached):
##  [1] colorspace_2.0-3      class_7.3-20          rstudioapi_0.14       proxy_0.4-27          farver_2.1.1          fansi_1.0.3           mvtnorm_1.1-3         xml2_1.3.3            splines_4.2.0        
## [10] codetools_0.2-18      doParallel_1.0.17     cachem_1.0.6          knitr_1.39            polyclip_1.10-0       jsonlite_1.8.3        spatstat.sparse_3.0-0 compiler_4.2.0        httr_1.4.4           
## [19] assertthat_0.2.1      Matrix_1.4-1          fastmap_1.1.0         cli_3.6.0             tweenr_2.0.2          htmltools_0.5.3       tools_4.2.0           gtable_0.3.1          glue_1.6.2           
## [28] dplyr_1.0.10          smerc_1.7.2           Rcpp_1.0.10           jquerylib_0.1.4       vctrs_0.5.2           av_0.8.3              iterators_1.0.14      lwgeom_0.2-10         xfun_0.35            
## [37] stringr_1.5.0         timechange_0.2.0      lifecycle_1.0.3       goftest_1.2-3         scales_1.2.1          spatstat.utils_3.0-1  Brobdingnag_1.2-7     slippymath_0.3.1      parallel_4.2.0       
## [46] yaml_2.3.6            curl_4.3.3            memoise_2.0.1         pbapply_1.6-0         sass_0.4.4            stringi_1.7.12        highr_0.9             plotrix_3.8-2         e1071_1.7-12         
## [55] gifski_1.6.6-1        rlang_1.0.6           pkgconfig_2.0.3       evaluate_0.15         lattice_0.20-45       tensor_1.5            labeling_0.4.2        cowplot_1.1.1         tidyselect_1.2.0     
## [64] magrittr_2.0.3        R6_2.5.1              magick_2.7.4          generics_0.1.3        DBI_1.1.3             pillar_1.8.1          withr_2.5.0           mgcv_1.8-40           units_0.8-0          
## [73] abind_1.4-5           tibble_3.1.8          KernSmooth_2.23-20    utf8_1.2.2            rmarkdown_2.16        grid_4.2.0            digest_0.6.30         classInt_0.4-8        numDeriv_2016.8-1.1  
## [82] munsell_0.5.0         bslib_0.4.1