How to animate Strava .gpx tracks in QGIS

Andriy Yaremenko
Geospatial Analytics
6 min readNov 24, 2021

--

In this short tutorial, I am gonna show how I animated my Strava tracks. As usual, my workflow might be not ideal. If you have any tips or comments, please reach out on Twitter.

In nutshell, you need to create a series of maps via QGIS Atlas and then animate them with any convenient tool for you. This idea is not new. For example, check out this nice tutorial from Topi Tjukanov.

Another thing that made this GIF so catchy — show all tracks, like they start at the same time. To achieve this, you need to calculate the time relatively to start for each record.

It might be done for any data, but in this particular case, I`ll use my .gpx tracks from Strava. You are free to apply this idea to whatever you want. GIS is creative stuff =)

1. Get your data

To download data from Strava please follow this instruction.

2. Import data to QGIS with the “Batch GSP Importer” plugin.

If you don`t have this plugin — go to Plugins —> Manage and Install Plugins, and search for the mentioned one.

In the plugin dialog window browse to the folder with your .gpx files. For Geometry type select point and hit Import button.

After the import, you`ll have a temporary merged point vector layer that contains information from all your .gpx tracks. - “id”, “elevation”, “capture_time”, and “file_name”. Strava app recording one point each 1–2 seconds in the format yyyy-mm-dd T hh:mm:ssThe most important here “capture_time” column since we gonna extract the “time_difference” from it.

Save this layer and if you want to, get rid of empty columns.

3. Add date and time_difference columns

To show all tracks like its starts at the same time, we need to calculate the relative time difference for each point in the track. Like how much time has passed since the beginning of the record(track).
For this, we can use a field calculator with the expression below. Don`t forget to toggle editing.

second(  
age
(to_datetime("capture_time"), to_datetime (minimum("capture_time", "file_name"))
)
)

Let’s break it down. But I strongly advise skipping the next paragraph as I am really bad at explaining this.

Function age returns the difference between two dates or datetimes.
The first argument representing the later datetime, the second one representing the earlier datetime. As we want this result in seconds we wrapping age into second function.
For the first argument, we just use to_datetime function to convert a string into a datetime object.
For the second argument, we need to find the most earlier/the first point in each track. For this, we use function minimum with two arguments: “capture_time” — for the field to aggregate and “file_name” — for the field to group by (since we compare each point to the earliest point in the track, not to the earliest point in the entire file that contains multiply tracks).

After calculating, we have a column (in my case it`s called “time_difference”) with the number of seconds passed from the beginning of the track record. Using this column we can show all tracks like they start at the same time.

4. Create a coverage layer for the atlas.

As I mentioned above, I used QGIS Atlas to generate images for GIF. When you create the Atlas, you need a “coverage layer”, something to create and manage atlases pages.
It`s possible to use a copy of the point layer with tracks info for this, but it will generate as many pages as points in the longest track. In my case, it was something around 7000 points. Generating and animating so many maps may take a lot of time. So, I just create a .csv file with the column that contains numbers multiplied by 20 in the range from -20 (to have one empty map) to maximum(time_difference)+few extra rows(to cover all points). You can use any spreadsheet software for this (LibreOffice, MS Excel, etc.).
And of course, you can change the time interval from 20 to whatever suits your case.

After saving the .csv file, add it to the QGIS as a table without geometry.

5. Create the atlas

Let`s go to the Print composer. Create a page in a size that you want and add a map to it. After, generate an Atlas using our table with numbers multiplied by 20 as coverage layer. Turn on Atlas preview and select any page, not from the beginning of the list (I`ll explain why later). Now we can start styling the point layer.

6. Styling

Let’s say we want to create a visual effect where newly added points have another color. It helps to track changes and make visualization more interesting.
For this purpose, we can use Rule Based symbology. In this particular case, I created 3 types of symbology (and that’s why we need to have Atlas, not on the first page).
For the first ten points:

(@atlas_pagename - 10) < "time_difference" 
and
"time_difference" <= @atlas_pagename

For the second ten points:

(@atlas_pagename - 20) < "time_difference" 
and
"time_difference" <= (@atlas_pagename -10)

For the rest of the points:

"time_difference" <= (@atlas_pagename-20)

I set different opacity and colors for groups. The first one should be the most visible. The second one might be more transparent, and the rest of the points might be almost transparent. You can experiment with number of the groups, colors, opacity, size, and all other settings looking for what works best for you.

Also, apply Symbol levels. All groups should be arranged sequentially, with the first on the top.

7. Export

Now we can go back to Print Composer. Set suitable scale and extent. If you want, change the background color.
Also, we can add “stopwatch” to give an understanding of the time.
For this, add a label with followed expression.

format_date( (make_time(0,0,0)+make_interval(seconds:=@atlas_pagename)), 'HH:mm:ss')

When all is done, click “Export Atlas as Images” and select a folder to save the output. Depending on the number of pages and your computer performance, it might take some time.

8. Animate

You can create GIF with any convenient tool for you like online services, FFmpeg, or GIMP. For this case I prefer GIMP. It`s an open-source raster graphics editor. You can learn about it more and download it here.
In GIMP go to the Files -> Open Image as Layers and add all your maps.
Then go to Filters -> Animation -> Optimize (for GIF).
After optimization, go to File -> Export as and save it as .gif.
In the dialog window choose parameters for output.

Congrats on your visualization!
P.S. Thanks to Reddit user matos4df for pointing to a typo in a filter expression.

For some reasons Medium drop FPS =/
For some reasons Medium drop FPS =/
Image for thumbnail

--

--