After implementing timestamp as M-value on SpatiaLite + QGIS as described in the previous article,I tried making moving point animation with QGIS and TimeManager plugin following Anita Graser's another article.
However, point doesn't move in real-time. When I move TimeManager's slider, I have to wait a few seconds to see a point.
So, I implemented more effective algorithm.
Here's the real-time movie of both 2 algorithms. The first (red point) is Anita's and the second (blue point) is mine. The whole trajectories shown are tracks of 10 day travel.
Codes
Here are the codes behind the moving points. The source data is "timestamp as M-value" track lines.
One layer of geometry generator is set as right.
Below is the text version of the expression to copy & paste.
CASE
WHEN ( m(end_point($geometry)) > second(age(animation_datetime(), to_datetime('1970-01-01 00:00')))
AND m(start_point($geometry)) <= second(age(animation_datetime(), to_datetime('1970-01-01 00:00'))))
THEN
line_lookup_m( $geometry, second(age(animation_datetime(), to_datetime('1970-01-01 00:00'))))
END
(2) Custom function definition
In this expression, custom function line_lookup_m()
is called. This function is defind as right.
Below is the text version of the line_lookup_m()
.
Explanation of the algorithm
Time can never be reversed. So, we can reasonably assume each GPS track is ordered by timestamp, which is m-value (it seems even enforceable in PostGIS using ST_IsValidTrajectory()
function.)
So for any given time target_m
, we check every track's start time and end time and see whether target_m
is in-between.
If it is, we pick timestamp of the middle point and see whether it is larger than target_m
or not. If it is larger, we can say that point to plot is in the latter half of the track, and see whether the middle point of that half is larger than target_m
or not. We repeat this halving process until we find a point to plot.
This is just a standard binary search algorithm.
For animation purpose, I modified the algorithm when reaching down to one point without finding exact match to target_m
to interpolate neighboring points based on timestamp.
Notes on QGIS custom expression
QGIS expression doesn't support neither loop functionality or recursive function call, which is necessary to implement binary search. So, we have to write a custom function.
Unfortunately, QGIS custom function is not so well documented. The following points will be helpful, if you are interested in writing your own.
- Logs can be output with
QgsMessageLog.logMessage()
to log message panel. This enables good oldprintf
debugging. - Editings on function builder is always automatically saved but not evaluated until you click
"save and load functions"
button. - Returning points have to be converted to
QgsGeometry
.
Source codes of TimeManager plugin and QGIS (expression related parts) will be also helpful.