What this code does:
Code for prepping data for importation to Google Earth, especially for making videos of animal movement paths
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)
We’ll use Google Earth Pro to make a video of animal movement, but first we’ll prep our data in R.
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
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")
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:
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.
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.
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:
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.
Save the tour: Click the save button in the control panel from step 1 and give your tour an easy to remember name.
Close the control panel: In order to access the “movie maker” function, you need to “x” out of the control panel from step 1.
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.
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).
In addition to making movies of animal movement in Google Earth Pro, we can also make gifs of animal movement in R.
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")
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
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