Movement data in GIS #7: animated trajectories with TimeManager

In this post, we use TimeManager to visualize the position of a moving object over time along a trajectory. This is another example of what is possible thanks to QGIS’ geometry generator feature. The result can look like this:

What makes this approach interesting is that the trajectory is stored in PostGIS as a LinestringM instead of storing individual trajectory points. So there is only one line feature loaded in QGIS:

(In part 2 of this series, we already saw how a geometry generator can be used to visualize speed along a trajectory.)

The layer is added to TimeManager using t_start and t_end attributes to define the trajectory’s temporal extent.

TimeManager exposes an animation_datetime() function which returns the current animation timestamp, that is, the timestamp that is also displayed in the TimeManager dock, as well as on the map (if we don’t explicitly disable this option).

Once TimeManager is set up, we can edit the line style to add a point marker to visualize the position of the moving object at the current animation timestamp. To do that, we interpolate the position along the trajectory segments. The first geometry generator expression splits the trajectory in its segments:

The second geometry generator expression interpolates the position on the segment that contains the current TimeManager animation time:

The WHEN statement compares the trajectory segment’s start and end times to the current TimeManager animation time. Afterwards, the line_interpolate_point function is used to draw the point marker at the correct position along the segment:

CASE 
WHEN (
m(end_point(geometry_n($geometry,@geometry_part_num)))
> second(age(animation_datetime(),to_datetime('1970-01-01 00:00')))
AND
m(start_point(geometry_n($geometry,@geometry_part_num)))
<= second(age(animation_datetime(),to_datetime('1970-01-01 00:00')))
)
THEN
line_interpolate_point( 
  geometry_n($geometry,@geometry_part_num),
  1.0 * (
    second(age(animation_datetime(),to_datetime('1970-01-01 00:00')))
	- m(start_point(geometry_n($geometry,@geometry_part_num)))
  ) / (
    m(end_point(geometry_n($geometry,@geometry_part_num)))
	- m(start_point(geometry_n($geometry,@geometry_part_num)))
  ) 
  * length(geometry_n($geometry,@geometry_part_num))
)
END

Here is the animation result for a part of the trajectory between 08:00 and 09:00:


This post is part of a series. Read more about movement data in GIS.

19 comments
  1. This is wonderful. Great work

  2. K said:

    How can I create a LinestringM out of a line and a separate point layer with points exactly on the line and a timestamp as attribute?

    • Interesting … where does the line come from? Is it a routing network? If not, wouldn’t it be enough to just deal with, i.e. connect, the points?

      • K said:

        I used QuickOSM to download a bus route relation, then I filtered the way layer so that it only shows the roads and then i dissolved that so that I have a simple continuous line from bus route start to bus route end (theres a roundabout in it though, not sure how it affects that).

        I also got a point layer with the points “on” that line, I filtered it so that the layer now only contains the stops that the bus I’m looking at stops at. Then I joined a csv file to it that contains the overall time spent up to each stop in seconds (first stop 0 seconds, next stop 240, etc. up to the last stop 1440 seconds).
        Example: http://puu.sh/xazze/d04aae0e9c.png

        Here are the two shapefiles: http://puu.sh/xazEV/57a5467156.zip

        What should I do next?

  3. Mike said:

    Hi, I’d really like to be able to replicate this but having trouble displaying the marker on the screen when TimeManager runs.

    My layer looks like this…

    id;t_start;t_end;newline
    100;2016-01-01 08:00:00;2016-01-01 08:02:00;LINESTRING(546805 261201, 545883 260197, 544799 261344, 544343 261491)

    I add the layer to Time Manager no problem and I think I’ve followed your instructions to the letter re applying the two geomertry generator steps.

    When I click play TimeManager runs at specified intervals from 08:00 to 08:02 but the marker is not displayed on screen.

    Anything obvious I could be missing?

    Thanks

    • Hi Mike! If your file contains ordinary linestring features instead of linestringM, it can not work.

  4. Thanks for responding. I couldn’t see how the measure value in the linestringM was being used so wrongly assumed a regular linestring was fine. Please could you explain why it needs to be a linestringM and how the measure value is used by this example? I’ve read back through your earlier posts on this subject and can see how the measure value is used when producing the plot which breaks down the trajectory into segments then colours them by speed, but from what I can tell for this example it assumes a constant speed between the start and end of the entire trajectory – the speed of the marker in the animation doesn’t reflect the specified speed on each individual segment of the trajectory.

    I changed my layer to the below but am still not seeing the marker when I run Time Manager.

    id;t_start;t_end;newline
    100;2016-01-01 08:00;2016-01-01 08:02;LINESTRINGM(546805 261201 10, 545883 260197 10, 544799 261344 10, 544343 261491 10)

    • from what I can tell for this example it assumes a constant speed between the start and end of the entire trajectory

      That’s incorrect. The expression that you see above is evaluated on the level of each individual trajectory segment and not on the entire trajectory.

      In your example, the m value is always 10, so it’s always 1970-01-01 00:00:10. That’s inconsistent with the t_start and t_end values you specified.

      • Mike said:

        I understand now and have it working – thanks!!

  5. Mike said:

    Hi, is it possible to include the fadeout effect in this example as per your ‘Trajectory animations with fadeout effect’ post?

    For the fadeout feature to work I understand you have to activate ‘accumulate features’ in the layer settings, but does this work if an end time is specified as required to interpolate the position in this example?

    • That’s a good question! I’ll have to think about how this can be achieved. I don’t doubt that it can, but will have to try how it performs.

  6. Akos said:

    Hello!
    Please help me! I already install the plugin, but it’s don’t work.
    After i add a layer in the Settings the plugin recognize the “Start time” and the “End time” automatically, but if i click on the play button nothing happens. The time is’t spinning. What can i do?!
    Thx for you answ!

    • It’s hard to guess what might be wrong without more details about the data and settings used.

  7. Leo France said:

    What if I have multiple GPS vehicle traces that I would like to visualise ?

    • There should be no changes necessary if there is one LinestringM feature for each vehicle

  8. Phil said:

    Hi!
    I try to make this fantastic example to work with my dataset but unsuccessfully. I have a table with one LineStringM in PostGIS and I follow the example step by step. Can someone pinpoint what’s wrong with my data? I have exactly 25 minutes between the start and the end. The M values go from 0 to 1500. They represents seconds (1500 / 60 = 25)

    gid;id;t_start;t_end;st_asewkt
    1;0;2017-11-15 08:00:00;2017-11-15 08:25:00;SRID=32188;LINESTRINGM(336414.416934886 5099396.98635722 0,335775.178872311 5098597.938779 60,332419.179043793 5099077.36732593 120,331460.321949929 5097479.2721695 180,330980.893402999 5094283.08185662 240,331300.512434286 5092525.17718454 300,329382.79824656 5089169.17735602 360,329222.988730918 5083895.46333978 420,325707.179386752 5078302.13029224 480,325227.750839822 5076384.41610452 540,322625.380801855 5074083.32464015 600,320423.37969994 5070073.91115475 660,318073.125920946 5068459.09267257 720,316053.806206565 5065460.88646253 780,313562.152366213 5059707.24503851 840,314550.591438974 5056003.15839969 900,314360.703776044 5052732.60778924 960,314772.067241304 5049730.26008475 1020,314680.322807332 5044742.13200705 1080,314680.322807332 5041386.13217853 1140,310525.275400594 5040906.7036316 1200,305890.799446924 5040267.46556902 1260,304612.323321775 5041545.94169417 1320,303014.228165336 5042025.3702411 1380,300617.08543068 5042824.41781932 1440,299658.228336818 5039947.84653774 1500)

    Thank you!

    • Hi Phil! An M value of 0 would equal 1970-01-01 00:00:00 (or similar, not checked). The M values and t_start and t_end have to be in sync

      • Phil said:

        Thank you. It’s now working perfectly.