Animating telemetry data

James E Paterson

One of my favourite ways to visualize wildlife tracking data is to animate paths. Using ggmap and gganimate, it has never been easier to show-off your hard-earned tracking data with animated maps you can use in presentations, meetings, and at dinner parties.

The code and data I use in this tutorial are available on my on my GitHub repo.

To create the animated plot above, I will use simulated data tracking five turtles.

# Read the csv file
# The file should be in your working directory.
turtles <- read.csv("tracking_sample.csv", 
                    stringsAsFactors = FALSE) 

I’m going to animate animal paths using the gganimate package, and plot the paths on top of a base map from Stamen using ggmap.

# First time only, you will need to install any missing packages
# install.packages("missingpackagename")
library(sp)
library(gganimate)
library(ggmap)
library(dplyr)
library(magrittr)

The sample data include coordinates in UTM, but ggmap uses latitude and longitude decimal degrees for the bounding box of your map (EPSG: 4326). Let’s convert our points into a SpatialPointsDataFrame (in package sp) and convert the coordinate reference system (CRS). You will need to know what CRS the coordinates you’re working with were recorded in. More information on CRS

# Make sure points are in order (in case they weren't before)
turtles <- turtles %>%
  arrange(id, date) %>% # arrange by animal, and ascending date
  filter(!is.na(x), !is.na(y)) # remove NA's

# Make spatial
turtlessp <- turtles
coordinates(turtlessp) <- c("x", "y")

# Set coordinate system. The sample data are in UTM zone 18, WGS84
proj4string(turtlessp) <- CRS( "+proj=utm +zone=18 +datum=WGS84 
                            +ellps=WGS84" )

# Transform to decimal degrees (from UTM)
turtlesspgeo <- spTransform(turtlessp, CRS("+init=EPSG:4326"))

# Make back into dataframe (but include date for our animation)
# ggmap and gganimate use dataframes for plotting
turtlesgeo <- as.data.frame(turtlesspgeo@coords)
turtlesgeo$id <- turtlesspgeo@data$id # add individual identifier
turtlesgeo$date <- as.Date(turtlesspgeo@data$date) # Important! the variable for revealing in the animation must be
# either integer, numberic, POSIXct, Date, difftime, or orhms. Here I made sure it is a date.

Now the data are in the correct format, we are ready to buid our map. The basic approach is:

  1. Build a static map using ggmap. Alternatively, you can use ggplot2 and not have a base map layer.
  2. Add animation for the map by revealing points and paths by the date.
  3. Save the animated version as a .gif to embed in presentations and websites.

For more information on navigating ggmap tile downloading, you can check out my tutorial on spatial formatting and basic mapping. If you want to download Google satellite imagery, you need to register an account and get a Google key. More information

# Plot study site using ggmap
# Using stamen tiles because Google tiles now require an account to download
mybasemap <- get_stamenmap(bbox = c(left = min(turtlesspgeo@coords[,1])-0.005, 
                                    bottom = min(turtlesspgeo@coords[,2])-0.005, 
                                    right = max(turtlesspgeo@coords[,1])+0.005, 
                                    top = max(turtlesspgeo@coords[,2])+0.005), 
                           zoom = 13)

# Plot static imagery + points + paths
mymap.paths <-ggmap(mybasemap) + 
  geom_point(data = turtlesgeo, aes(x = x, y = y, colour = id)) +
  geom_path(data = turtlesgeo, aes(x = x, y = y, colour = id, group = id)) +
  labs(x = "Longitude", y = "Latitude") +
  scale_colour_manual(name = "Turtle number",
                      # Adjust the number of values for how many animals you have
                      values = c("red", "blue", "purple", "green", "orange"), 
                      # Enough breaks for every animal in the data set
                      breaks = unique(turtles$id)) + 
 theme(legend.position = "bottom") 

# Static plot
mymap.paths

Next, we animate the plot by using transition_reveal and revealing the points and paths by date. Some transitions appear “slow” because there are large differences in time. For example, these animals were tracked less frequently over the winter when they are barely moving. The animation interpolates the differences in date between each data point. More regular data points will result in a smoother and more regular animation.

# Update plot to animate. I used 'transition_reveal' so that the path builds from the beginning to the end. Use 'transition_states' to show only one point at a time
path.animate.plot <- mymap.paths +
  transition_reveal(along = date) +
  labs(title = 'Date: {frame_along}')  # Add a label on top to say what date each frame is

# To display the animation, use `animate`.
# When using your own data, adjust frames per second (fps) to be as fast or slow as you like.
# Be patient at this stage! It will eventually render in your plotting window
animate(path.animate.plot,
        fps = 3, # frames per second
        nframes = 200) # default is 100 frames

Pretty cool! The last step is to save our animation as a .gif file.

The anim_save function is easy-to-use and works similarly to ggsave. Afterwards, you can embed your .gif file in presentations, documents, websites, etc.

# Save as gif. This may be a large file, depending on your data set! 
anim_save(path.animate.plot,
          fps = 3,
          nframes = 200,
          file = "animatedpaths.gif")

I hope you found this tutorial useful! For a more in-depth look at the gganimate package, check out this post