Playing with animal tracking data in QGIS

open data
Using the Temporal Controller in QGIS to visualize the movements of cattle in the Logone Floodplain in Cameroon. Based on data from the Movebank, a free, online database of animal tracking data. tags: movement data, data visualisation

Paulo van Breugel


August 30, 2021

I am currently looking for data I can use in my classes about spatial data analysis. A great source of data I found is the Movebank, a free, online database of animal tracking data hosted by the Max Planck Institute of Animal Behavior. The aim is to help animal tracking researchers to manage, share, protect, analyze and archive their data. This short article provides a nice short introduction to the type of data available on this site.

The data is great for something else I have wanted to do for some time now. That is, trying out the temporal controller in QGIS. This tool offers native temporal support to QGIS. It is the successor of the celebrated TimeManager plugin, and is available since version 3.14. The main developer, Nyal Dawson, made a video demonstrating some of the capabilities of this new tool. Definitely something to check out. Below you can find the steps I followed to create the animated map (you can find the map at the end of this post). Note, click on an image to enlarge it.

Cattle movements

To use the temporal controller, you obviously need temporal data. This can be raster or vector data. The animal tracking data available in the Movebank database is a .csv data layer with a number of columns, including the coordinates and the date-time column. This can be imported as a vector point layer in QGIS.

Figure 1: Study area with the 33 animal tracks. Background image, Bing satellite via the Quickmap services plugin for QGIS. In this study, we’ll focus on the eastern tracks along the main river.

For this post, we use a dataset collected by Moritz et al.1 for a study about grazing pressure in pastoral systems in the Logone Floodplain in Cameroon2. I particularly liked this study because of how it combines different approaches and methods (GPS/GIS, video recordings of animal behavior, and ethnographic methods). But that is something for another time.

The GPS data set consists of 33 tracks, representing the daily movement of individual animals during the day (21 tracks) or night (12 tracks) in three different locations. As mentioned above, the data comes as a .csv file, which we import and subsequently save as a vector layer in a new Geopackage.

Import csv and save in geopackage

Import the csv file in QGIS.

Save the data as the layer cattlemovement in the (new) Geopackage cattlemovementdata.gpkg.

“Define the symbology, assigning random colors to each of the tracks. Tracks are identified by the categories in the ‘individual-local-identifier’ column.

Data preparation

The locations of the animals were recorded at 3-second intervals (only if the animal was moving)2. However, the date-time stamps provided in the Movebank data file are rounded to whole minutes. Information about the seconds between consecutive measurements is provided separately in the column ‘study-specific measurement’.

Because there are some missing observations, creating complete time-stamps based on this information is a bit tricky. So I will leave that as a challenge for you. For the visualizing of the animal movements, a one-minute time interval is good enough. It does mean we need to compute the average locations per minute first.

Calculate average position per minute

First step is to create a new column tracktime with the combined track ID and date-time stamp. This makes for a unique ID that can be used to group the location points that need to be averaged. Combining the two columns can be done using the expression "individual-local-identifier" || '_' || to_string( "timestamp" ) in the Field calculator. Note that we add the underscore to make it easier later on to split the created strings back in two columns with the original track identifiers and the date-time stamps.

Concatenate the columns with the track ID and the timestamp.

To speed up subsequent calculations, we create an index on the newly created field, using the function Create attribute index in the processing toolbox.

Create an index on the column track_time.

To calculate the arithmetic mean of the coordinates of each tracktime category, we use the Mean coordinates function from the processing toolbox.

Create new layer with average position per track_time category.

The resulting layer does not contain the track ID and date-times. To get this ‘back’, we need to split the tracktime ID’s we created earlier back into two columns with the track ID’s and the date-time records. To do so, we use the string_to_array() function in the Field calculator; string_to_array("tracktime", "_")[0] to get the track ID’s and string_to_array("tracktime", "_")[1] to get the date-time values.

Extract the track ID’s from the column tracktime.

Extract the date-time records from the column tracktime.

We now have a vector layer cattlemovement_perminute with the location data per minute. The tracks were recorded over a 15-day period, with some longer periods between tracks. To speed things up, and to reduce the length of the animated map, we use a subset of the recorded tracks, e.g., those for the tracks in the Cubuna.

Select and save tracking data for Cubunu

Select features using the ‘select features’ option.

Save the selected features as a new layer using the context menu: right click on the layer cattle movement, and in the context menu, select ‘Export > Save selected Features as’.

Save the new vector layer ‘Cubuna’ in the (existing) Geopackage ‘cattlemovementdata.gpkg’ (of course, you can also save it in a new Geopackage if you prefer that).


In the Layers windows, we duplicate this layer (right-click the layer, and in the context menu, select duplicate layer) and rename this Cubuna_background. The idea is to use the Cubuna layer to show the moving points, and the Cubuna_background layer to keep the points visible, but in another color. To this end, we assigned random fill colors to each of the tracks in Cubuna layer, with white borders for the day tracks, and black borders for the night tracks. For the Cubuna_background day tracks, we assign a semi-transparent white color to the day tracks and a semi-transparent black color to the night tracks.

Define the symbology

Symbology for the point layer that symbolizing the point locations.

Symbology for the point layer symbolizing the tracks

Temporal controller

The next step is to enable the Dynamic temporal control under the Temporal tab of the Layer Properties window. With the dynamic temporal control enabled, QGIS will display features/points at certain times or time intervals. Depending on the data, there are different configuration options. We use the Single field with Date/Time option with the date_time column as input. We furthermore set the event duration to 1 minute. This defines how long the points will be visible after the start.

Enable the temporal control

The Temporal tab can be found in the Layer properties 1. The date and time for each location is determined by the values in the field date_time. We select the Single Field with Data/Time from the configuration drop down menu 2, and the field date-time to define the date and time 4. Finally, we set the event duration to 1 minute 3.

Endable the dynamic temporal control for the ‘Cubuna’ layer in the Layer properties Window.

We do the same for the ‘Cubuna_background’ layer. Only this time, we select the ‘Accumulate features over time’ option 5. With this option enables, the points will remain visible after activation.

Endable the dynamic temporal control for the ‘Cubuna’ layer in the Layer properties Window.

We can now open the Temporal Control Panel to preview the animated map, change the animation speed, or change the temporal range to be animated.

Preview animated map

The layers under temporal control have a clock symbol next to the layer name in the Layer window 1. The Temporal Control Panel can be opened by clicking on the Clock icon on the Map Navigation Toolbar 2 or through View → Panels → Temporal Controller. In the Temporal Controller window, we can now click on the Animated Temporal Navigation (play icon 3) to activate the animation controls. Click the Set to Full Range (refresh icon 4) to automatically set the time range to the match the dataset. Set the step to 1 minute 5. Now we can preview the animation by clicking the Play button 6.

Preview of the animated map with the Temporal controller.

Note that if the animation is too fast, or too slow, you can adjust the frame rate by clicking Temporal Settings (yellow gear icon 7). Decreasing the the frame rate (frames per second) will slow down the animation.

It would be nice if we can add a label that displays the time frame on the map. We can do that using the built in Title Decoration.

Add label with time stamp

The following is based on the tutorial by Ujaval Gandhi3. Go to View → Decorations → Title Label.

Adding a title label to the map.

Click the checkbox to enable it and click Insert an Expression button and enter the following expression to display the date and time.

[%format_date(@map_start_time, 'dd MMMM yyyy hh:mm:ss')%]

Here, the variable @map_start_time contains the timestamp of the current time slice being displayed. So we can use that timestamp and format it to display the date and time of occurrence. See the QGIS documentation for more information about the syntax and options.

Define the title label text.

The font type and size, background color and placement can all be adapted to your likening. In the example, I used a white font (Noto Sans Georgian, 22pt) on a 43% transparent black background.

Export the animated map

So now we have an animated map that we can view and explore in QGIS. But what if we want to share the map? The Time Controller has the option to export the frames of the animations as individual png files.

Export the animated map

The first step is to export the map animation. To do so, we select the Export Animation (save icon) in the Temporal control window. In the Export Map Animation dialog, we click on the ... Output directory 1 to choose the directory in which the images will be saved. We use Calculate from layer 2 to set the extent to match that of the Cubuna point layer. Activate the little lock 4, and set the output height (the width will be automatically adjusted) 3. Optionally, the time range can be set, e.g., to limit the video to the first day.

Export map animation.

Once the export finishes, we have a large number of PNG images, each representing a 1-minute step, in the output directory. We can now convert these into an animated image (gif) or a video. Given the large number of png files we just created, the gif file will be very large, and it takes a long time to generate. Creating a video is therefore the better option.

There are various tools, but below we are going to use FFmpeg, a cross-platform solution to record, convert and stream audio and video.

From pngs to video

FFmpeg is a command-line tool. If you are on Windows, you can use the command line (cmd) or Windows PowerShell. Either way, first we need to go to the working directory with the png images. In the console, you type in:

cd C:\users\brp\Desktop\movementdata

Now we use the FFmpeg function to convert the png files in that folder to a video. With the command below, we create two video’s, one in the popular mp4 format, the other in the mkv format. The most important parameter is the -i, which stands for input, followed by the cattlemovement%04d.png. This part of the code identifies the input files as all the png files in the folder which names start with ‘cattlemovement’, followed by a 4 digit number.

ffmpeg -framerate 60 -i cattlemovement%04d.png -crf 20 cattlemovement2.mp4
ffmpeg -i cattlemovement%04d.png -c:v libx264 -preset slow -crf 22 -c:a copy cattlemovement.mkv

I leave it up to you to find out more about the other parameters. This page provides an overview of some of the main options. And on this page is more specifically about the various encoding options. And there is of course the official documentation.

The resulting video is around 8 MB. Which is pretty good, given that all the input png files together were 6.45 GB. An in case you are curious, a gif file would have been around 218 MB large, and it takes a lot more time to create. So, yes, creating a video definitely is a good choice here. But the video, that was what it was all about. Check it out below (if it doesn’t automatically play, hit the play button).

So how useful is a video like the one above? Not sure to be honest. It does raise some interesting questions. Like, how do the pastoralists decide where to go? They must know the area pretty well given that they need to water their animals (i.e., reach the water holes, here marked by blue dots) on time. And what about those night tracks? I guess that is exactly what I like about this kind of maps, they make you curious about what is going on. And check out the article for some answers.


Hope you enjoyed reading the post. Want to see more examples? Check out this post by Topi Tjukanov and this tutorial by Ujaval Gandhi, both of which served as inspiration for this post. And there are a lot of inspiring video tutorials on Youtube. And last but not least, thanks to Moritz et al. for providing the data and providing feedback on questions, and the Movebank for the answers to my inquiries.


Moritz M. Data from: An integrated approach to modeling grazing pressure in pastoral systems: the case of the Logone Floodplain (Cameroon). Published online 2018. doi:10.5441/001/1.J682DS56
Moritz M, Soma E, Scholte P, et al. An Integrated Approach to Modeling Grazing Pressure in Pastoral Systems: The Case of the Logone Floodplain (Cameroon). Human Ecology. 2010;38(6):775-789. doi:10.1007/s10745-010-9361-z
Gandhi U. Animating time series data (QGIS3). Published online 2019.