The creation of maps is essential to understanding animal movement patterns over space and time, but these maps nearly always show spatial patterns at the expense of time. The “flattening” of temporal patterns of animal movement over geographic space can preclude a detailed understanding of how animals move across a landscape (or seascape). This is particularly true when individuals exhibit high site fidelity or there are many overlapping tracks from multiple individuals.

During the data exploration stage, it is particularly useful to become familiar with relationships between movement patterns and covariates of interest. However, this process can be difficult when practitioners only rely on static maps that obscure the temporal pattern exhibited by an organism.

As an example, below is a static plot of the sese (southern elephant seal) dataset (Jonsen et al., 2019) included within the foieGras package (Jonsen and Patterson, 2020).


# Load data

# Change column names to match required names for shiny_tracks()
# Change projection to calculate step lengths, turning angles, and net-squared displacement metrics
sese.sf<- sese %>%
  rename(x = lon, y = lat) %>%
  st_as_sf(., coords = c("x","y"), crs = 4326) %>%
  st_transform(crs = "+proj=laea +lat_0=-90 +lon_0=75 +ellps=WGS84 +units=km +no_defs") %>%
  dplyr::mutate(x = unlist(purrr::map(.data$geometry, 1)),
                      y = unlist(purrr::map(.data$geometry, 2)))

# Convert back to data.frame w/ projected coordinates
sese2<- sese.sf %>%

# Convert sf object to MULTILINESTRING
sese.sf<- sese.sf %>%
  group_by(id) %>%
  summarize(do_union = FALSE) %>%

# Define bounding box of seal tracks
bbox<- st_bbox(sese.sf)

# Calculate step lengths, turning angles, and net squared displacement
sese3<- prep_data(dat = sese2, coord.names = c("x","y"), id = "id")

# Load land spatial data
world<- ne_countries(scale = 110, returnclass = "sf") %>%
  st_transform("+proj=laea +lat_0=-90 +lon_0=75 +ellps=WGS84 +units=km +no_defs")

# View static tracks
ggplot() +
  geom_sf(data = world, fill = "grey45") +
  geom_sf(data = sese.sf, aes(color = id), size = 1) +
  scale_color_brewer(palette = "Dark2", guide = F) +
  theme_minimal() +
  coord_sf(xlim = bbox[c(1,3)], ylim = bbox[c(2,4)])

It is apparent that these southern elephant seals are central-place foragers and are undertaking a looping migration. However, it is unclear how long these individuals were tracked, if they all performed migratory movements at the exact same time, and how long they spent at foraging locations before continuing to travel in a fast and directed behavior.

While we could potentially subset these data to create a grid of plots at some time interval of interest (e.g., days, months, seasons, years), this can take unnecessarily long to try different combinations for one or multiple tagged individuals. Additionally, it would be difficult to explore patterns in the intrinsic properties of movement itself (e.g., identify episodic periods of high site fidelity, looping movements, or relationships in spatiotemporal patterns between individual animals). Therefore, we have created a R Shiny application within the bayesmove package to dynamically explore animal movement patterns.

Dynamic exploration of animal movement patterns

Users can quickly render their data in the Shiny app using the shiny_tracks() function, which only requires a data frame and an EPSG code (or PROJ string) to map the tracks. At a minimum, this data frame must have columns labeled ‘id’, ‘x’, ‘y’, and ‘date’ where ‘date’ is of class POSIXct. Any other variables can also be included within the data frame, but only time series of numeric values can be currently be visualized and explored.

In this example, the Lambert Azimuthal Equal Area, WGS84 projection (“+proj=laea +lat_0=-90 +lon_0=75 +ellps=WGS84 +units=km +no_defs”) is used since the coordinate units are in kilometers, which provides an intuitive set of units for step lengths and net-squared displacement. Upon first running the function, users are brought to a page that lists instructions and information for using the app (in the ‘About’ tab). Clicking on the ‘Explore data’ tab allows dynamic exploration of movement patterns from multiple individuals:

# Run Shiny app
shiny_tracks(data = sese3, epsg = "+proj=laea +lat_0=-90 +lon_0=75 +ellps=WGS84 +units=km +no_defs")

This screenshot shows the general layout of the Shiny app, which let’s users select a variable to dynamically explore, filter variables from their dataset, and visualize a time series plot as well as an interactive map of these data. Additionally, users can toggle between visualizing their set of locations as lines or points. To provide a concrete example, let’s explore the southern elephant seal data further.

We can see on the sidebar that there are two main headers that break up this panel: 1) Explore the data, and 2) Filter the data. Under the ‘Explore the data’ header, you can select any variables from your data frame (besides id or date) to visualize an interactive time series. Additionally, a radio button is provided to toggle between mapping your spatial coordinates as a trajectory (line) or as a set of points on the interactive Leaflet map.

Let’s say we were interested in exploring the East-West movement patterns of all 5 individuals: we would click on the “Select a variable” dropdown menu at the top and change this to x.

From this time series plot, we can see that two IDs (“ct36-E-09” and “ct36-F-09”) were transmitting only during 2009, whereas the remaining three IDs were all transmitting over the same period of time in 2015. Additionally, it is also very obvious that these seals are moving in a variety of directions from the island of Kerguelen. The line plot and the tracks on the Leaflet map are both colored by ID, where green circles denote starting locations and red circles denote ending locations.

To dynamically explore a more interesting variable, such as net-squared displacement (NSD), we can change the variable in the dropdown menu and then click and drag on the time series plot to only highlight a particular time period of interest on the map (e.g., 2015).

We now see a zoomed in version of the time series plot of NSD and only the three IDs tagged in 2015 showing up on the Leaflet map. This example demonstrates that updating the time window of the time series plot will impact the map, but does not impact the table that displays the dataset below (which can be confirmed if you scroll down and observe the total number of entries reported beneath the table). If interested in filtering this dataset to export only the tracks from 2015, you would have to either 1) remove the two IDs tagged in 2009, or 2) adjust the date range slider under the “Filter the data” header. An example where the data is filtered by ID is shown below, where the number of retained (vs removed) observations is shown circled in red. Clicking on the id window will bring up options to include the previously removed IDs.

When inspecting the plot of NSD over time, we can see a pattern where NSD is essentially zero at the beginning (February) and end (September) of the time series, but in between it is gradually increasing to its maximum value during the austral fall/winter before gradually decreasing to zero by the austral spring. This is useful to know, but we can inspect even finer temporal resolution patterns of these movements, particularly at their greatest extent (i.e., April to June). To do so, all we have to do is click and drag on the plot for this time window and the Leaflet map will update the extent of the zoomed-in tracks, as well as showing the full tracks in grey while the “highlighted” time window is shown in color.

With this new time window selected, we can pan around and zoom-in to each ID. The green and red circles denote the first and last location for the selected time window, which has now been updated.

Additionally, the basemap can be adjusted and the tracks can be toggled on and off (including whether you prefer to view only the highlighted portion of the track, the full track length, or both).

If you wanted to filter a subset of your data (using the sliders and inputs under the ‘Filter the data’ header of the sidebar) to export for later use, you can either copy the entire table by clicking on the “Copy” button, or download a CSV file of the filtered data by clicking the “CSV” button.

An online version of this app can be found here. If any other features are desired (e.g., loading raster layers, choosing among multiple color palettes, etc), feel free to send requests by e-mail (joshcullen10 [at] gmail [dot] com) or submit as an issue on the GitHub page for bayesmove.


Jonsen ID, McMahon CR, Patterson TA, Auger-Methe M, Harcourt R, Hindell MA, Bestley S. (2019). Movement responses to environment: fast inference of variation among southern elephant seals with a mixed effects model. Ecology, 100:e02566. doi:10.1002/ecy.2566.

Jonsen I, Patterson T. (2020). foieGras: fit latent variable movement models to animal tracking data for location quality control and behavioural inference. Zenodo. doi:10.5281/zenodo.3899972