moveVis provides tools to visualize movement data (e.g. from GPS tracking) and temporal changes of environmental data (e.g. from remote sensing) by creating video animations. It works with move, sp and raster class inputs and turns them into ggplot2 frames that can be further customized. moveVis uses gifski (wraping the gifski cargo crate) and av (binding to FFmpeg) to render frames into animated GIF or video files.

Figure 1: Exemplary movement tracks nearby Lake of Constance and a gradient base layer faded over time

Figure 2: Exemplary movement tracks nearby Lake of Constance and a discrete base layer


With version 0.10.0, the package has been rewritten from the ground up with the goal to make it easier to customize the appearance of movement animations. Thus, the logic of the package, its functions and their syntax have changed.

moveVis 0.10.0 is not on CRAN yet (but will be soon). Until then, to install moveVis version 0.10.0, run:


Code written for moveVis version <=0.9.9 will not work with the newer versions, but it is quite simple and thus highly recommended to switch to the new syntax due to a variety of advantages. moveVis version <=0.9.9 can still be downloaded here and installed manually:

install.packages("moveVis-0.9.9.tar.gz", repos = NULL)

Function overview

moveVis includes the following functions, sorted by the order they would be applied to create an animation from movement and environmental data:

Prepare movement data

  • df2move() converts a data.frame into a move or moveStack object. This is useful if you do not usually work with the move classes and your tracks are present as data.frames.
  • align_move() aligns single and multi-individual movement data to a uniform time scale with a uniform temporal resolution needed for creating an animation from it. Use this function to prepare your movement data for animation depending on the temporal resolution that suits your data.
  • subset_move() subsets a move or moveStack by a given time span. This is useful if you want to create a movement animation of only a temporal subset of your data, e.g. a particular day.

Create animation frames

  • get_maptypes() returns a character vector of available map types that can be used with frames_spatial(). moveVis supports OpenStreetMaps and Mapbox basemap imergay. Alternatively, you can provide custom imagery to frames_spatial().
  • frames_spatial() creates a list of ggplot2 maps displaying movement. Each object represents a single frame. Each frame can be viewed or modified individually. The returned list of frames can be animated using animate_frames().
  • frames_graph() creates a list of ggplot2 graphs displaying movement-environment interaction. Each object represents a single frame. Each frame can be viewed or modified individually. The returned list of frames can be animated using animate_frames().

Adjust frames layout and appearacne

Animate frames as GIF or video

View movement tracks

  • view_spatial() displays movement tracks on an interactive mapview or leaflet map.

Get started

Example 1: Creating a simple animation

Create a simple animation using a default base map by first aligning your movement data to a uniform time scale, creating a list of ggplot2 frames and turning these frames into an animated GIF:


# align move_data to a uniform time scale
move_data <- align_move(move_data, res = 240, digit = 0, unit = "secs")

# create spatial frames with a OpenStreetMap watercolour map
frames <- frames_spatial(move_data, path_colours = c("red", "green", "blue"),
                         map_service = "osm", map_type = "watercolor", alpha = 0.5)
frames[[100]] # preview one of the frames

# animate frames
animate_frames(frames, out_file = "/full/path/to/output/folder/moveVis_example.gif")

Example 2: Example 1 explained in detail

First, load the required packages for this example and the moveVis example movement data:



move_data is a moveStack, containing three individual tracks. Let’s have a look at both timestamps and sampling rates:

timeLag(move_data, unit = "mins")

We can conclude that each track has a sampling rate of roughly 4 minutes, however sampling rates differ over time. Due to this, tracks do not share unique timestamps. For animation, unique frame times are needed, regardless if we want to animate a single track or multiple at once. Thus, we need to align move_data in order to

  • make all tracks share unique timestamps that can be assigned to frames
  • make all tracks share unique, steady sampling rates without gaps

You can use align_move() to align move_data to a sampling rate of 4 minutes (240 seconds) at the seconds digit “:00”:

move_data <- align_move(move_data, res = 240, digit = 0, unit = "secs")

Instead, you could apply your own functions for aligning your data, e.g. using more advanced interpolation methods.

Now, as the movement tracks are aligned, we can pair them with a base map to create frames that can be turned into an animation later on. You can use your own custom base map imagery or choose from default map types. To get a list of all available default map types, use get_maptypes(). moveVis supports both OpenStreetMap and mapbox as default map services.

Using OpenStreetMap, you can get open-source streets map imagery and maps derived thereof. Using mapbox, you can get a variety of map imagery, including satellite, hybrid, light, dark maps and more. For mapbox, you need to register (for free) at to get a token that grants you free access (50 000 map downloads/month) and that can be used with the map_token argument of frames_spatial() (see ?frames_spatial for details).

In this example, we want to use the OpenStreetMap ‘watercolour’ imagery with a transparency of 50% to start with something nice looking. To create a list of spatial frames from move_data using a map, we can use frames_spatial():

frames <- frames_spatial(move_data, path_colours = c("red", "green", "blue"),
                         map_service = "osm", map_type = "watercolor", alpha = 0.5)

Instead of using path_colours, you can add a colour column to your move or moveStack object. This allows you to colour your movement tracks as you want, e.g. not only by individual track, but by behavioral segment, time, age, speed or something different (see ?frames_spatial for details).

Have a look at the newly created frames list object and display a randomly selected frame to get a first impression, how your animation will look like:

length(frames) # number of frames
frames[[100]] # display one of the frames

You can pass any list of frames like the one we just created to animate_frames(). This function will turn your frames into an animation, written as a GIF image or a video file. For now, we du not want to add any customizations to frames and just create a GIF from it. If you are not sure, which output formats can be used, run suggest_formats() that returns you a vector of file suffixes that can be created on your system. For making a GIF from frames, just run:

animate_frames(frames, out_file = "/full/path/to/output/folder/moveVis_example.gif")

Example 3: Customizing frames

moveVis is entierly based on the ggplot2 grammar of graphics. Each list element in frames is a ggplot2 object that represents a single animation frame. Thus, it is possible to customize each frame individually using ggplot2 functions. Instead, moveVis provides a set of functions for making it simpler to cutomize frames. We will use some of them in the following to customize frames that we created in the prior section:

frames <- add_labels(frames, x = "Longitude", y = "Latitude") # add labels, e.g. axis labels
frames <- add_progress(frames) # add a progress bar
frames <- add_scalebar(frames, height = 0.015) # add a scale bar
frames <- add_northarrow(frames) # add a north arrow
frames <- add_timestamps(frames, move_data, type = "label") # add timestamps

## Have a look at one of the frames:

For further details on these functions, please see their help files. If you want to apply your own ggplot2 syntax to frames, e.g. for drawing polygons, lines or points that are static or even change with time, you can do this frame-wise. In the following example, we customize one individual frame just as if you would work with a single ggplot2 object:

data <- data.frame(x = c(8.917, 8.924, 8.924, 8.916, 8.917),
                   y = c(47.7678, 47.7675, 47.764, 47.7646, 47.7678))

# just customize a single frame and have a look at it
frame_test <- frames[[100]] + geom_path(aes(x = x, y = y), data = data,
                                        colour = "red", linetype = "dashed")

If you just want to change one or a small selection of frames, you could just manipulate those frames like shown above and assign the cusomized ggplot2 frames to the equivalent elements in your frames list.

If you want to edit all frames, you can use the add_gg() function. Here, we want to mark a field on the map on all frames. For this, we use the geom_path() function of ggplot2 with add_gg():

# or customize all frames at once using add_gg:
frames = add_gg(frames, gg = expr(geom_path(aes(x = x, y = y), data = data,
                                  colour = "red", linetype = "dashed")), data = data)

The field marking is now added to all frames. Let’s add some text to describe the field marking:

frames <- add_text(frames, "Static feature", x = 8.9205, y = 47.7633,
                   colour = "black", size = 3)

## Have a look at one of the frames:

add_gg() can also be used to customize each frame consecutively, e.g. to add dynamic marks that move or change with time. Both arguments gg and data can take lists of the same length as frames. If one of these arguments or both are lists, each list element is applied to the according element in frames. Let’s add a another field mark that is slightly changing with each frame:

## create data.frame containing corner coordinates
data <- data.frame(x = c(8.96, 8.955, 8.959, 8.963, 8.968, 8.963, 8.96),
                   y = c(47.725, 47.728, 47.729, 47.728, 47.725, 47.723, 47.725))
## make a list from it by replicating it by the length of frames
data <- rep(list(data), length.out = length(frames))

## now alter the coordinates to make them shift
data <- lapply(data, function(x){
  y <- rnorm(nrow(x)-1, mean = 0.00001, sd = 0.0001) 
  x + c(y, y[1])

## draw each individual polygon to each frame
frames = add_gg(frames, gg = expr(geom_path(aes(x = x, y = y), data = data,
                                  colour = "black")), data = data)

## add a text label
frames <- add_text(frames, "Dynamic feature", x = 8.959, y = 47.7305,
                   colour = "black", size = 3)

## Have a look at one of the frames:

Animate the the customized frames as we did in the prior section using animate_frames(). This time, let’s make a .mov video:

animate_frames(frames, "/full/path/to/output/folder/")

Example 4: Base maps: using mapbox with moveVis

example code to be added soon, see ?frames_spatial for details.

Example 5: Custom maps: gradient, discrete and RGB raster data

example code to be added soon, see ?frames_spatial for details.

Example 6: Interaction graphs

example code to be added soon, see ?frames_graph for details.

Example 7: Joining frames side by side

example code to be added soon, see ?join_frames for details.

Example 8: View movement tracks

With the simple view_spatial() wrapper, movement tracks can be displayed on an interactive map using the very handy mapview or leaflet packages. This may be helpful if you want to explore data before animating them or check the effect of applying correction methods as done by align_move().

# in case, mapview or leaflet is not installed:
install.packages(c("leaflet", "mapview"))

# return a mapview map

An interactive map is returned. If you use RStudio, it will be displayed on the RStudio viewer pane:

When hovering a point with the cursor, the timestamps of that point is displayed. Thanks to mapview, you may click on a point to open a pop-up box containing the point’s attributes:

Features to be added

Things and features that should be added in future versions of moveVis (feel free to contribute to this list using a pull request):

Next version:

  • “keep tracks” setting to force paths to not disappear
  • follow population mode
  • follow individual mode
  • day-/night-time visualization

Some day:

  • 3D animations, e.g. for including altitude data

Contact & bug reports

For bug reports, please use Feature requests and other contributions are welcome!

What else are we doing?

The Department of Remote Sensing of the University of Würzburg has developed other R packages that might interest you:

  • getSpatialData, a package to query, preview and download satellite data,
  • RStoolbox, a package providing a wide range of tools for every-day remote sensing processing needs,
  • rsMove, a package providing tools to query and analyze movement data using remote sensing.

For other news on the work at at the Department of Remote Sensing of the University of Würzburg, click here.


This initiative is part of the Opt4Environment project and was funded by the German Aerospace Center (DLR) on behalf of the Federal Ministry for Economic Affairs and Energy (BMWi) with the research grant 50 EE 1403.