]> git.street.me.uk Git - andy/viking.git/blob - src/viktrack.c
Fix memory leak on re-downloading 'bad' map image tiles.
[andy/viking.git] / src / viktrack.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <glib.h>
27 #include <time.h>
28 #include <stdlib.h>
29 #ifdef HAVE_STRING_H
30 #include <string.h>
31 #endif
32 #ifdef HAVE_MATH_H
33 #include <math.h>
34 #endif
35
36 #include "coords.h"
37 #include "vikcoord.h"
38 #include "viktrack.h"
39 #include "globals.h"
40 #include "dems.h"
41 #include "settings.h"
42
43 VikTrack *vik_track_new()
44 {
45   VikTrack *tr = g_malloc0 ( sizeof ( VikTrack ) );
46   tr->ref_count = 1;
47   return tr;
48 }
49
50 #define VIK_SETTINGS_TRACK_NAME_MODE "track_draw_name_mode"
51 #define VIK_SETTINGS_TRACK_NUM_DIST_LABELS "track_number_dist_labels"
52
53 /**
54  * vik_track_set_defaults:
55  *
56  * Set some default values for a track.
57  * ATM This uses the 'settings' method to get values,
58  *  so there is no GUI way to control these yet...
59  */
60 void vik_track_set_defaults(VikTrack *tr)
61 {
62   gint tmp;
63   if ( a_settings_get_integer ( VIK_SETTINGS_TRACK_NAME_MODE, &tmp ) )
64     tr->draw_name_mode = tmp;
65
66   if ( a_settings_get_integer ( VIK_SETTINGS_TRACK_NUM_DIST_LABELS, &tmp ) )
67     tr->max_number_dist_labels = tmp;
68 }
69
70 void vik_track_set_comment_no_copy(VikTrack *tr, gchar *comment)
71 {
72   if ( tr->comment )
73     g_free ( tr->comment );
74   tr->comment = comment;
75 }
76
77
78 void vik_track_set_name(VikTrack *tr, const gchar *name)
79 {
80   if ( tr->name )
81     g_free ( tr->name );
82
83   tr->name = g_strdup(name);
84 }
85
86 void vik_track_set_comment(VikTrack *tr, const gchar *comment)
87 {
88   if ( tr->comment )
89     g_free ( tr->comment );
90
91   if ( comment && comment[0] != '\0' )
92     tr->comment = g_strdup(comment);
93   else
94     tr->comment = NULL;
95 }
96
97 void vik_track_set_description(VikTrack *tr, const gchar *description)
98 {
99   if ( tr->description )
100     g_free ( tr->description );
101
102   if ( description && description[0] != '\0' )
103     tr->description = g_strdup(description);
104   else
105     tr->description = NULL;
106 }
107
108 void vik_track_set_source(VikTrack *tr, const gchar *source)
109 {
110   if ( tr->source )
111     g_free ( tr->source );
112
113   if ( source && source[0] != '\0' )
114     tr->source = g_strdup(source);
115   else
116     tr->source = NULL;
117 }
118
119 void vik_track_set_type(VikTrack *tr, const gchar *type)
120 {
121   if ( tr->type )
122     g_free ( tr->type );
123
124   if ( type && type[0] != '\0' )
125     tr->type = g_strdup(type);
126   else
127     tr->type = NULL;
128 }
129
130 void vik_track_ref(VikTrack *tr)
131 {
132   tr->ref_count++;
133 }
134
135 void vik_track_set_property_dialog(VikTrack *tr, GtkWidget *dialog)
136 {
137   /* Warning: does not check for existing dialog */
138   tr->property_dialog = dialog;
139 }
140
141 void vik_track_clear_property_dialog(VikTrack *tr)
142 {
143   tr->property_dialog = NULL;
144 }
145
146 void vik_track_free(VikTrack *tr)
147 {
148   if ( tr->ref_count-- > 1 )
149     return;
150
151   if ( tr->name )
152     g_free ( tr->name );
153   if ( tr->comment )
154     g_free ( tr->comment );
155   if ( tr->description )
156     g_free ( tr->description );
157   if ( tr->source )
158     g_free ( tr->source );
159   if ( tr->type )
160     g_free ( tr->type );
161   g_list_foreach ( tr->trackpoints, (GFunc) vik_trackpoint_free, NULL );
162   g_list_free( tr->trackpoints );
163   if (tr->property_dialog)
164     if ( GTK_IS_WIDGET(tr->property_dialog) )
165       gtk_widget_destroy ( GTK_WIDGET(tr->property_dialog) );
166   g_free ( tr );
167 }
168
169 /**
170  * vik_track_copy:
171  * @tr: The Track to copy
172  * @copy_points: Whether to copy the track points or not
173  *
174  * Normally for copying the track it's best to copy all the trackpoints
175  * However for some operations such as splitting tracks the trackpoints will be managed separately, so no need to copy them.
176  *
177  * Returns: the copied VikTrack
178  */
179 VikTrack *vik_track_copy ( const VikTrack *tr, gboolean copy_points )
180 {
181   VikTrack *new_tr = vik_track_new();
182   new_tr->name = g_strdup(tr->name);
183   new_tr->visible = tr->visible;
184   new_tr->is_route = tr->is_route;
185   new_tr->draw_name_mode = tr->draw_name_mode;
186   new_tr->max_number_dist_labels = tr->max_number_dist_labels;
187   new_tr->has_color = tr->has_color;
188   new_tr->color = tr->color;
189   new_tr->bbox = tr->bbox;
190   new_tr->trackpoints = NULL;
191   if ( copy_points )
192   {
193     GList *tp_iter = tr->trackpoints;
194     while ( tp_iter )
195     {
196       VikTrackpoint *new_tp = vik_trackpoint_copy ( (VikTrackpoint*)(tp_iter->data) );
197       new_tr->trackpoints = g_list_prepend ( new_tr->trackpoints, new_tp );
198       tp_iter = tp_iter->next;
199     }
200     if ( new_tr->trackpoints )
201       new_tr->trackpoints = g_list_reverse ( new_tr->trackpoints );
202   }
203   vik_track_set_name(new_tr,tr->name);
204   vik_track_set_comment(new_tr,tr->comment);
205   vik_track_set_description(new_tr,tr->description);
206   vik_track_set_source(new_tr,tr->source);
207   return new_tr;
208 }
209
210 VikTrackpoint *vik_trackpoint_new()
211 {
212   VikTrackpoint *tp = g_malloc0(sizeof(VikTrackpoint));
213   tp->speed = NAN;
214   tp->course = NAN;
215   tp->altitude = VIK_DEFAULT_ALTITUDE;
216   tp->hdop = VIK_DEFAULT_DOP;
217   tp->vdop = VIK_DEFAULT_DOP;
218   tp->pdop = VIK_DEFAULT_DOP;
219   return tp;
220 }
221
222 void vik_trackpoint_free(VikTrackpoint *tp)
223 {
224   g_free(tp->name);
225   g_free(tp);
226 }
227
228 void vik_trackpoint_set_name(VikTrackpoint *tp, const gchar *name)
229 {
230   if ( tp->name )
231     g_free ( tp->name );
232
233   // If the name is blank then completely remove it
234   if ( name && name[0] == '\0' )
235     tp->name = NULL;
236   else if ( name )
237     tp->name = g_strdup(name);
238   else
239     tp->name = NULL;
240 }
241
242 VikTrackpoint *vik_trackpoint_copy(VikTrackpoint *tp)
243 {
244   VikTrackpoint *new_tp = vik_trackpoint_new();
245   memcpy ( new_tp, tp, sizeof(VikTrackpoint) );
246   if ( tp->name )
247     new_tp->name = g_strdup (tp->name);
248   return new_tp;
249 }
250
251 /**
252  * track_recalculate_bounds_last_tp:
253  * @trk:   The track to consider the recalculation on
254  *
255  * A faster bounds check, since it only considers the last track point
256  */
257 static void track_recalculate_bounds_last_tp ( VikTrack *trk )
258 {
259   GList *tpl = g_list_last ( trk->trackpoints );
260
261   if ( tpl ) {
262     struct LatLon ll;
263     // See if this trackpoint increases the track bounds and update if so
264     vik_coord_to_latlon ( &(VIK_TRACKPOINT(tpl->data)->coord), &ll );
265     if ( ll.lat > trk->bbox.north )
266       trk->bbox.north = ll.lat;
267     if ( ll.lon < trk->bbox.west )
268       trk->bbox.west = ll.lon;
269     if ( ll.lat < trk->bbox.south )
270       trk->bbox.south = ll.lat;
271     if ( ll.lon > trk->bbox.east )
272       trk->bbox.east = ll.lon;
273   }
274 }
275
276 /**
277  * vik_track_add_trackpoint:
278  * @tr:          The track to which the trackpoint will be added
279  * @tp:          The trackpoint to add
280  * @recalculate: Whether to perform any associated properties recalculations
281  *               Generally one should avoid recalculation via this method if adding lots of points
282  *               (But ensure calculate_bounds() is called after adding all points!!)
283  *
284  * The trackpoint is added to the end of the existing trackpoint list
285  */
286 void vik_track_add_trackpoint ( VikTrack *tr, VikTrackpoint *tp, gboolean recalculate )
287 {
288   // When it's the first trackpoint need to ensure the bounding box is initialized correctly
289   gboolean adding_first_point = tr->trackpoints ? FALSE : TRUE;
290   tr->trackpoints = g_list_append ( tr->trackpoints, tp );
291   if ( adding_first_point )
292     vik_track_calculate_bounds ( tr );
293   else if ( recalculate )
294     track_recalculate_bounds_last_tp ( tr );
295 }
296
297 /**
298  * vik_track_get_length_to_trackpoint:
299  *
300  */
301 gdouble vik_track_get_length_to_trackpoint (const VikTrack *tr, const VikTrackpoint *tp)
302 {
303   gdouble len = 0.0;
304   if ( tr->trackpoints )
305   {
306     // Is it the very first track point?
307     if ( VIK_TRACKPOINT(tr->trackpoints->data) == tp )
308       return len;
309
310     GList *iter = tr->trackpoints->next;
311     while (iter)
312     {
313       VikTrackpoint *tp1 = VIK_TRACKPOINT(iter->data);
314       if ( ! tp1->newsegment )
315         len += vik_coord_diff ( &(tp1->coord),
316                                 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
317
318       // Exit when we reach the desired point
319       if ( tp1 == tp )
320         break;
321
322       iter = iter->next;
323     }
324   }
325   return len;
326 }
327
328 gdouble vik_track_get_length(const VikTrack *tr)
329 {
330   gdouble len = 0.0;
331   if ( tr->trackpoints )
332   {
333     GList *iter = tr->trackpoints->next;
334     while (iter)
335     {
336       if ( ! VIK_TRACKPOINT(iter->data)->newsegment )
337         len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
338                                 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
339       iter = iter->next;
340     }
341   }
342   return len;
343 }
344
345 gdouble vik_track_get_length_including_gaps(const VikTrack *tr)
346 {
347   gdouble len = 0.0;
348   if ( tr->trackpoints )
349   {
350     GList *iter = tr->trackpoints->next;
351     while (iter)
352     {
353       len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
354                               &(VIK_TRACKPOINT(iter->prev->data)->coord) );
355       iter = iter->next;
356     }
357   }
358   return len;
359 }
360
361 gulong vik_track_get_tp_count(const VikTrack *tr)
362 {
363   return g_list_length(tr->trackpoints);
364 }
365
366 gulong vik_track_get_dup_point_count ( const VikTrack *tr )
367 {
368   gulong num = 0;
369   GList *iter = tr->trackpoints;
370   while ( iter )
371   {
372     if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
373                        &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
374       num++;
375     iter = iter->next;
376   }
377   return num;
378 }
379
380 /*
381  * Deletes adjacent points that have the same position
382  * Returns the number of points that were deleted
383  */
384 gulong vik_track_remove_dup_points ( VikTrack *tr )
385 {
386   gulong num = 0;
387   GList *iter = tr->trackpoints;
388   while ( iter )
389   {
390     if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
391                        &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
392     {
393       num++;
394       // Maintain track segments
395       if ( VIK_TRACKPOINT(iter->next->data)->newsegment && (iter->next)->next )
396         VIK_TRACKPOINT(((iter->next)->next)->data)->newsegment = TRUE;
397
398       vik_trackpoint_free ( iter->next->data );
399       tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next );
400     }
401     else
402       iter = iter->next;
403   }
404
405   // NB isn't really be necessary as removing duplicate points shouldn't alter the bounds!
406   vik_track_calculate_bounds ( tr );
407
408   return num;
409 }
410
411 /*
412  * Get a count of trackpoints with the same defined timestamp
413  * Note is using timestamps with a resolution with 1 second
414  */
415 gulong vik_track_get_same_time_point_count ( const VikTrack *tr )
416 {
417   gulong num = 0;
418   GList *iter = tr->trackpoints;
419   while ( iter ) {
420     if ( iter->next &&
421          ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
422            VIK_TRACKPOINT(iter->next->data)->has_timestamp ) &&
423          ( VIK_TRACKPOINT(iter->data)->timestamp ==
424            VIK_TRACKPOINT(iter->next->data)->timestamp) )
425       num++;
426     iter = iter->next;
427   }
428   return num;
429 }
430
431 /*
432  * Deletes adjacent points that have the same defined timestamp
433  * Returns the number of points that were deleted
434  */
435 gulong vik_track_remove_same_time_points ( VikTrack *tr )
436 {
437   gulong num = 0;
438   GList *iter = tr->trackpoints;
439   while ( iter ) {
440     if ( iter->next &&
441          ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
442            VIK_TRACKPOINT(iter->next->data)->has_timestamp ) &&
443          ( VIK_TRACKPOINT(iter->data)->timestamp ==
444            VIK_TRACKPOINT(iter->next->data)->timestamp) ) {
445
446       num++;
447       
448       // Maintain track segments
449       if ( VIK_TRACKPOINT(iter->next->data)->newsegment && (iter->next)->next )
450         VIK_TRACKPOINT(((iter->next)->next)->data)->newsegment = TRUE;
451
452       vik_trackpoint_free ( iter->next->data );
453       tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next );
454     }
455     else
456       iter = iter->next;
457   }
458
459   vik_track_calculate_bounds ( tr );
460
461   return num;
462 }
463
464 /**
465  * vik_track_remove_dodgy_first_point:
466  * @vt:            The track
467  * @speed:         Maximum speed in m/s between points allowed
468  * @recalc_bounds: Whether track bounds should be recalculated
469  *           (i.e. can be skipped if bounds will get calculated later on)
470  *
471  * Returns: Whether the first point was removed
472  *
473  * ATM Primarily to remove dodgy first point inserted back at previous
474  *  location by some Garmin GPS Etrex units after being significantly moved
475  * e.g. you've driven somewhere else and start recording a new cycle/walk etc...
476  *
477  * NB This function is limited to just handling first point issues,
478  *  rather than a more comprehensive attempt to remove any suspicious points
479  *  through-out the track.
480  */
481 gboolean vik_track_remove_dodgy_first_point ( VikTrack *vt, guint speed, gboolean recalc_bounds )
482 {
483   gboolean deleted = FALSE;
484
485   if ( vt->trackpoints ) {
486     GList *iter = g_list_first ( vt->trackpoints );
487     VikTrackpoint *tp1 = VIK_TRACKPOINT(iter->data);
488
489     if ( tp1->has_timestamp ) {
490       if ( iter->next ) {
491         VikTrackpoint *tp2 = VIK_TRACKPOINT(iter->next->data);
492         if ( tp2->has_timestamp ) {
493           gdouble dist_diff = vik_coord_diff ( &tp1->coord, &tp2->coord );
494           time_t time_diff = tp2->timestamp - tp1->timestamp;
495
496           gdouble spd = fabs(dist_diff / (gint)time_diff);
497           if ( spd > speed ) {
498             deleted = TRUE;
499             vik_trackpoint_free ( tp1 );
500             vt->trackpoints = g_list_delete_link ( vt->trackpoints, iter );
501             if ( recalc_bounds )
502               vik_track_calculate_bounds ( vt );
503           }
504         }
505       }
506     }
507   }
508
509   return deleted;
510 }
511
512 /*
513  * Deletes all 'extra' trackpoint information
514  *  such as time stamps, speed, course etc...
515  */
516 void vik_track_to_routepoints ( VikTrack *tr )
517 {
518   GList *iter = tr->trackpoints;
519   while ( iter ) {
520
521     // c.f. with vik_trackpoint_new()
522
523     VIK_TRACKPOINT(iter->data)->has_timestamp = FALSE;
524     VIK_TRACKPOINT(iter->data)->timestamp = 0;
525     VIK_TRACKPOINT(iter->data)->speed = NAN;
526     VIK_TRACKPOINT(iter->data)->course = NAN;
527     VIK_TRACKPOINT(iter->data)->hdop = VIK_DEFAULT_DOP;
528     VIK_TRACKPOINT(iter->data)->vdop = VIK_DEFAULT_DOP;
529     VIK_TRACKPOINT(iter->data)->pdop = VIK_DEFAULT_DOP;
530     VIK_TRACKPOINT(iter->data)->nsats = 0;
531     VIK_TRACKPOINT(iter->data)->fix_mode = VIK_GPS_MODE_NOT_SEEN;
532
533     iter = iter->next;
534   }
535 }
536
537 guint vik_track_get_segment_count(const VikTrack *tr)
538 {
539   guint num = 1;
540   GList *iter = tr->trackpoints;
541   if ( !iter )
542     return 0;
543   while ( (iter = iter->next) )
544   {
545     if ( VIK_TRACKPOINT(iter->data)->newsegment )
546       num++;
547   }
548   return num;
549 }
550
551 VikTrack **vik_track_split_into_segments(VikTrack *t, guint *ret_len)
552 {
553   VikTrack **rv;
554   VikTrack *tr;
555   guint i;
556   guint segs = vik_track_get_segment_count(t);
557   GList *iter;
558
559   if ( segs < 2 )
560   {
561     *ret_len = 0;
562     return NULL;
563   }
564
565   rv = g_malloc ( segs * sizeof(VikTrack *) );
566   tr = vik_track_copy ( t, TRUE );
567   rv[0] = tr;
568   iter = tr->trackpoints;
569
570   i = 1;
571   while ( (iter = iter->next) )
572   {
573     if ( VIK_TRACKPOINT(iter->data)->newsegment )
574     {
575       iter->prev->next = NULL;
576       iter->prev = NULL;
577       rv[i] = vik_track_copy ( tr, FALSE );
578       rv[i]->trackpoints = iter;
579
580       vik_track_calculate_bounds ( rv[i] );
581
582       i++;
583     }
584   }
585   *ret_len = segs;
586   return rv;
587 }
588
589 /*
590  * Simply remove any subsequent segment markers in a track to form one continuous track
591  * Return the number of segments merged
592  */
593 guint vik_track_merge_segments(VikTrack *tr)
594 {
595   guint num = 0;
596   GList *iter = tr->trackpoints;
597   if ( !iter )
598     return num;
599
600   // Always skip the first point as this should be the first segment
601   iter = iter->next;
602
603   while ( (iter = iter->next) )
604   {
605     if ( VIK_TRACKPOINT(iter->data)->newsegment ) {
606       VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
607       num++;
608     }
609   }
610   return num;
611 }
612
613 void vik_track_reverse ( VikTrack *tr )
614 {
615   if ( ! tr->trackpoints )
616     return;
617
618   tr->trackpoints = g_list_reverse(tr->trackpoints);
619
620   /* fix 'newsegment' */
621   GList *iter = g_list_last ( tr->trackpoints );
622   while ( iter )
623   {
624     if ( ! iter->next ) /* last segment, was first, cancel newsegment. */
625       VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
626     if ( ! iter->prev ) /* first segment by convention has newsegment flag. */
627       VIK_TRACKPOINT(iter->data)->newsegment = TRUE;
628     else if ( VIK_TRACKPOINT(iter->data)->newsegment && iter->next )
629     {
630       VIK_TRACKPOINT(iter->next->data)->newsegment = TRUE;
631       VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
632     }
633     iter = iter->prev;
634   }
635 }
636
637 /**
638  * vik_track_get_duration:
639  * @trk: The track
640  * @segment_gaps: Whether the duration should include gaps between segments
641  *
642  * Returns: The time in seconds
643  *  NB this may be negative particularly if the track has been reversed
644  */
645 time_t vik_track_get_duration(const VikTrack *trk, gboolean segment_gaps)
646 {
647   time_t duration = 0;
648   if ( trk->trackpoints ) {
649     // Ensure times are available
650     if ( vik_track_get_tp_first(trk)->has_timestamp ) {
651       // Get trkpt only once - as using vik_track_get_tp_last() iterates whole track each time
652       if (segment_gaps) {
653         // Simple duration
654         VikTrackpoint *trkpt_last = vik_track_get_tp_last(trk);
655         if ( trkpt_last->has_timestamp ) {
656           time_t t1 = vik_track_get_tp_first(trk)->timestamp;
657           time_t t2 = trkpt_last->timestamp;
658           duration = t2 - t1;
659         }
660       }
661       else {
662         // Total within segments
663         GList *iter = trk->trackpoints->next;
664         while (iter) {
665           if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
666                VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
667               (!VIK_TRACKPOINT(iter->data)->newsegment) ) {
668             duration += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
669           }
670           iter = iter->next;
671         }
672       }
673     }
674   }
675   return duration;
676 }
677
678 gdouble vik_track_get_average_speed(const VikTrack *tr)
679 {
680   gdouble len = 0.0;
681   guint32 time = 0;
682   if ( tr->trackpoints )
683   {
684     GList *iter = tr->trackpoints->next;
685     while (iter)
686     {
687       if ( VIK_TRACKPOINT(iter->data)->has_timestamp && 
688           VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
689           (! VIK_TRACKPOINT(iter->data)->newsegment) )
690       {
691         len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
692                                 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
693         time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
694       }
695       iter = iter->next;
696     }
697   }
698   return (time == 0) ? 0 : ABS(len/time);
699 }
700
701 /**
702  * Based on a simple average speed, but with a twist - to give a moving average.
703  *  . GPSs often report a moving average in their statistics output
704  *  . bicycle speedos often don't factor in time when stopped - hence reporting a moving average for speed
705  *
706  * Often GPS track will record every second but not when stationary
707  * This method doesn't use samples that differ over the specified time limit - effectively skipping that time chunk from the total time
708  *
709  * Suggest to use 60 seconds as the stop length (as the default used in the TrackWaypoint draw stops factor)
710  */
711 gdouble vik_track_get_average_speed_moving (const VikTrack *tr, int stop_length_seconds)
712 {
713   gdouble len = 0.0;
714   guint32 time = 0;
715   if ( tr->trackpoints )
716   {
717     GList *iter = tr->trackpoints->next;
718     while (iter)
719     {
720       if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
721           VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
722           (! VIK_TRACKPOINT(iter->data)->newsegment) )
723       {
724         if ( ( VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp ) < stop_length_seconds ) {
725           len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
726                                   &(VIK_TRACKPOINT(iter->prev->data)->coord) );
727         
728           time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
729         }
730       }
731       iter = iter->next;
732     }
733   }
734   return (time == 0) ? 0 : ABS(len/time);
735 }
736
737 gdouble vik_track_get_max_speed(const VikTrack *tr)
738 {
739   gdouble maxspeed = 0.0, speed = 0.0;
740   if ( tr->trackpoints )
741   {
742     GList *iter = tr->trackpoints->next;
743     while (iter)
744     {
745       if ( VIK_TRACKPOINT(iter->data)->has_timestamp && 
746           VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
747           (! VIK_TRACKPOINT(iter->data)->newsegment) )
748       {
749         speed =  vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) )
750                  / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
751         if ( speed > maxspeed )
752           maxspeed = speed;
753       }
754       iter = iter->next;
755     }
756   }
757   return maxspeed;
758 }
759
760 void vik_track_convert ( VikTrack *tr, VikCoordMode dest_mode )
761 {
762   GList *iter = tr->trackpoints;
763   while (iter)
764   {
765     vik_coord_convert ( &(VIK_TRACKPOINT(iter->data)->coord), dest_mode );
766     iter = iter->next;
767   }
768 }
769
770 /* I understood this when I wrote it ... maybe ... Basically it eats up the
771  * proper amounts of length on the track and averages elevation over that. */
772 gdouble *vik_track_make_elevation_map ( const VikTrack *tr, guint16 num_chunks )
773 {
774   gdouble *pts;
775   gdouble total_length, chunk_length, current_dist, current_area_under_curve, current_seg_length, dist_along_seg = 0.0;
776   gdouble altitude1, altitude2;
777   guint16 current_chunk;
778   gboolean ignore_it = FALSE;
779
780   GList *iter = tr->trackpoints;
781
782   if (!iter || !iter->next) /* zero- or one-point track */
783           return NULL;
784
785   { /* test if there's anything worth calculating */
786     gboolean okay = FALSE;
787     while ( iter )
788     {
789       // Sometimes a GPS device (or indeed any random file) can have stupid numbers for elevations
790       // Since when is 9.9999e+24 a valid elevation!!
791       // This can happen when a track (with no elevations) is uploaded to a GPS device and then redownloaded (e.g. using a Garmin Legend EtrexHCx)
792       // Some protection against trying to work with crazily massive numbers (otherwise get SIGFPE, Arithmetic exception)
793       if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE &&
794            VIK_TRACKPOINT(iter->data)->altitude < 1E9 ) {
795         okay = TRUE; break;
796       }
797       iter = iter->next;
798     }
799     if ( ! okay )
800       return NULL;
801   }
802
803   iter = tr->trackpoints;
804
805   g_assert ( num_chunks < 16000 );
806
807   pts = g_malloc ( sizeof(gdouble) * num_chunks );
808
809   total_length = vik_track_get_length_including_gaps ( tr );
810   chunk_length = total_length / num_chunks;
811
812   /* Zero chunk_length (eg, track of 2 tp with the same loc) will cause crash */
813   if (chunk_length <= 0) {
814     g_free(pts);
815     return NULL;
816   }
817
818   current_dist = 0.0;
819   current_area_under_curve = 0;
820   current_chunk = 0;
821   current_seg_length = 0;
822
823   current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
824       &(VIK_TRACKPOINT(iter->next->data)->coord) );
825   altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
826   altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
827   dist_along_seg = 0;
828
829   while ( current_chunk < num_chunks ) {
830
831     /* go along current seg */
832     if ( current_seg_length && (current_seg_length - dist_along_seg) > chunk_length ) {
833       dist_along_seg += chunk_length;
834
835       /*        /
836        *   pt2 *
837        *      /x       altitude = alt_at_pt_1 + alt_at_pt_2 / 2 = altitude1 + slope * dist_value_of_pt_inbetween_pt1_and_pt2
838        *     /xx   avg altitude = area under curve / chunk len
839        *pt1 *xxx   avg altitude = altitude1 + (altitude2-altitude1)/(current_seg_length)*(dist_along_seg + (chunk_len/2))
840        *   / xxx
841        *  /  xxx
842        **/
843
844       if ( ignore_it )
845         // Seemly can't determine average for this section - so use last known good value (much better than just sticking in zero)
846         pts[current_chunk] = altitude1;
847       else
848         pts[current_chunk] = altitude1 + (altitude2-altitude1)*((dist_along_seg - (chunk_length/2))/current_seg_length);
849
850       current_chunk++;
851     } else {
852       /* finish current seg */
853       if ( current_seg_length ) {
854         gdouble altitude_at_dist_along_seg = altitude1 + (altitude2-altitude1)/(current_seg_length)*dist_along_seg;
855         current_dist = current_seg_length - dist_along_seg;
856         current_area_under_curve = current_dist*(altitude_at_dist_along_seg + altitude2)*0.5;
857       } else { current_dist = current_area_under_curve = 0; } /* should only happen if first current_seg_length == 0 */
858
859       /* get intervening segs */
860       iter = iter->next;
861       while ( iter && iter->next ) {
862         current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
863             &(VIK_TRACKPOINT(iter->next->data)->coord) );
864         altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
865         altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
866         ignore_it = VIK_TRACKPOINT(iter->next->data)->newsegment;
867
868         if ( chunk_length - current_dist >= current_seg_length ) {
869           current_dist += current_seg_length;
870           current_area_under_curve += current_seg_length * (altitude1+altitude2) * 0.5;
871           iter = iter->next;
872         } else {
873           break;
874         }
875       }
876
877       /* final seg */
878       dist_along_seg = chunk_length - current_dist;
879       if ( ignore_it || ( iter && !iter->next ) ) {
880         pts[current_chunk] = current_area_under_curve / current_dist;
881         if (!iter->next) {
882           int i;
883           for (i = current_chunk + 1; i < num_chunks; i++)
884             pts[i] = pts[current_chunk];
885           break;
886         }
887       } 
888       else {
889         current_area_under_curve += dist_along_seg * (altitude1 + (altitude2 - altitude1)*dist_along_seg/current_seg_length);
890         pts[current_chunk] = current_area_under_curve / chunk_length;
891       }
892
893       current_dist = 0;
894       current_chunk++;
895     }
896   }
897
898   return pts;
899 }
900
901
902 void vik_track_get_total_elevation_gain(const VikTrack *tr, gdouble *up, gdouble *down)
903 {
904   gdouble diff;
905   *up = *down = 0;
906   if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE )
907   {
908     GList *iter = tr->trackpoints->next;
909     while (iter)
910     {
911       diff = VIK_TRACKPOINT(iter->data)->altitude - VIK_TRACKPOINT(iter->prev->data)->altitude;
912       if ( diff > 0 )
913         *up += diff;
914       else
915         *down -= diff;
916       iter = iter->next;
917     }
918   } else
919     *up = *down = VIK_DEFAULT_ALTITUDE;
920 }
921
922 gdouble *vik_track_make_gradient_map ( const VikTrack *tr, guint16 num_chunks )
923 {
924   gdouble *pts;
925   gdouble *altitudes;
926   gdouble total_length, chunk_length, current_gradient;
927   gdouble altitude1, altitude2;
928   guint16 current_chunk;
929
930   g_assert ( num_chunks < 16000 );
931
932   total_length = vik_track_get_length_including_gaps ( tr );
933   chunk_length = total_length / num_chunks;
934
935   /* Zero chunk_length (eg, track of 2 tp with the same loc) will cause crash */
936   if (chunk_length <= 0) {
937     return NULL;
938   }
939
940   altitudes = vik_track_make_elevation_map (tr, num_chunks);
941   if (altitudes == NULL) {
942     return NULL;
943   }
944
945   current_gradient = 0.0;
946   pts = g_malloc ( sizeof(gdouble) * num_chunks );
947   for (current_chunk = 0; current_chunk < (num_chunks - 1); current_chunk++) {
948     altitude1 = altitudes[current_chunk];
949     altitude2 = altitudes[current_chunk + 1];
950     current_gradient = 100.0 * (altitude2 - altitude1) / chunk_length;
951
952     pts[current_chunk] = current_gradient;
953   }
954
955   pts[current_chunk] = current_gradient;
956
957   g_free ( altitudes );
958
959   return pts;
960 }
961
962 /* by Alex Foobarian */
963 gdouble *vik_track_make_speed_map ( const VikTrack *tr, guint16 num_chunks )
964 {
965   gdouble *v, *s, *t;
966   gdouble duration, chunk_dur;
967   time_t t1, t2;
968   int i, pt_count, numpts, index;
969   GList *iter;
970
971   if ( ! tr->trackpoints )
972     return NULL;
973
974   g_assert ( num_chunks < 16000 );
975
976   t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
977   t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
978   duration = t2 - t1;
979
980   if ( !t1 || !t2 || !duration )
981     return NULL;
982
983   if (duration < 0) {
984     g_warning("negative duration: unsorted trackpoint timestamps?");
985     return NULL;
986   }
987   pt_count = vik_track_get_tp_count(tr);
988
989   v = g_malloc ( sizeof(gdouble) * num_chunks );
990   chunk_dur = duration / num_chunks;
991
992   s = g_malloc(sizeof(double) * pt_count);
993   t = g_malloc(sizeof(double) * pt_count);
994
995   iter = tr->trackpoints->next;
996   numpts = 0;
997   s[0] = 0;
998   t[0] = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
999   numpts++;
1000   while (iter) {
1001     s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
1002     t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
1003     numpts++;
1004     iter = iter->next;
1005   }
1006
1007   /* In the following computation, we iterate through periods of time of duration chunk_dur.
1008    * The first period begins at the beginning of the track.  The last period ends at the end of the track.
1009    */
1010   index = 0; /* index of the current trackpoint. */
1011   for (i = 0; i < num_chunks; i++) {
1012     /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
1013      * find the first trackpoint outside the current interval, averaging the speeds between intermediate trackpoints.
1014      */
1015     if (t[0] + i*chunk_dur >= t[index]) {
1016       gdouble acc_t = 0, acc_s = 0;
1017       while (t[0] + i*chunk_dur >= t[index]) {
1018         acc_s += (s[index+1]-s[index]);
1019         acc_t += (t[index+1]-t[index]);
1020         index++;
1021       }
1022       v[i] = acc_s/acc_t;
1023     } 
1024     else if (i) {
1025       v[i] = v[i-1];
1026     }
1027     else {
1028       v[i] = 0;
1029     }
1030   }
1031   g_free(s);
1032   g_free(t);
1033   return v;
1034 }
1035
1036 /**
1037  * Make a distance/time map, heavily based on the vik_track_make_speed_map method
1038  */
1039 gdouble *vik_track_make_distance_map ( const VikTrack *tr, guint16 num_chunks )
1040 {
1041   gdouble *v, *s, *t;
1042   gdouble duration, chunk_dur;
1043   time_t t1, t2;
1044   int i, pt_count, numpts, index;
1045   GList *iter;
1046
1047   if ( ! tr->trackpoints )
1048     return NULL;
1049
1050   t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1051   t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1052   duration = t2 - t1;
1053
1054   if ( !t1 || !t2 || !duration )
1055     return NULL;
1056
1057   if (duration < 0) {
1058     g_warning("negative duration: unsorted trackpoint timestamps?");
1059     return NULL;
1060   }
1061   pt_count = vik_track_get_tp_count(tr);
1062
1063   v = g_malloc ( sizeof(gdouble) * num_chunks );
1064   chunk_dur = duration / num_chunks;
1065
1066   s = g_malloc(sizeof(double) * pt_count);
1067   t = g_malloc(sizeof(double) * pt_count);
1068
1069   iter = tr->trackpoints->next;
1070   numpts = 0;
1071   s[0] = 0;
1072   t[0] = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1073   numpts++;
1074   while (iter) {
1075     s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
1076     t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
1077     numpts++;
1078     iter = iter->next;
1079   }
1080
1081   /* In the following computation, we iterate through periods of time of duration chunk_dur.
1082    * The first period begins at the beginning of the track.  The last period ends at the end of the track.
1083    */
1084   index = 0; /* index of the current trackpoint. */
1085   for (i = 0; i < num_chunks; i++) {
1086     /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
1087      * find the first trackpoint outside the current interval, averaging the distance between intermediate trackpoints.
1088      */
1089     if (t[0] + i*chunk_dur >= t[index]) {
1090       gdouble acc_s = 0; // No need for acc_t
1091       while (t[0] + i*chunk_dur >= t[index]) {
1092         acc_s += (s[index+1]-s[index]);
1093         index++;
1094       }
1095       // The only bit that's really different from the speed map - just keep an accululative record distance
1096       v[i] = i ? v[i-1]+acc_s : acc_s;
1097     }
1098     else if (i) {
1099       v[i] = v[i-1];
1100     }
1101     else {
1102       v[i] = 0;
1103     }
1104   }
1105   g_free(s);
1106   g_free(t);
1107   return v;
1108 }
1109
1110 /**
1111  * This uses the 'time' based method to make the graph, (which is a simpler compared to the elevation/distance)
1112  * This results in a slightly blocky graph when it does not have many trackpoints: <60
1113  * NB Somehow the elevation/distance applies some kind of smoothing algorithm,
1114  *   but I don't think any one understands it any more (I certainly don't ATM)
1115  */
1116 gdouble *vik_track_make_elevation_time_map ( const VikTrack *tr, guint16 num_chunks )
1117 {
1118   time_t t1, t2;
1119   gdouble duration, chunk_dur;
1120   GList *iter = tr->trackpoints;
1121
1122   if (!iter || !iter->next) /* zero- or one-point track */
1123     return NULL;
1124
1125   /* test if there's anything worth calculating */
1126   gboolean okay = FALSE;
1127   while ( iter ) {
1128     if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1129       okay = TRUE;
1130       break;
1131     }
1132     iter = iter->next;
1133   }
1134   if ( ! okay )
1135     return NULL;
1136
1137   t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1138   t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1139   duration = t2 - t1;
1140
1141   if ( !t1 || !t2 || !duration )
1142     return NULL;
1143
1144   if (duration < 0) {
1145     g_warning("negative duration: unsorted trackpoint timestamps?");
1146     return NULL;
1147   }
1148   gint pt_count = vik_track_get_tp_count(tr);
1149
1150   // Reset iterator back to the beginning
1151   iter = tr->trackpoints;
1152
1153   gdouble *pts = g_malloc ( sizeof(gdouble) * num_chunks ); // The return altitude values
1154   gdouble *s = g_malloc(sizeof(double) * pt_count); // calculation altitudes
1155   gdouble *t = g_malloc(sizeof(double) * pt_count); // calculation times
1156
1157   chunk_dur = duration / num_chunks;
1158
1159   s[0] = VIK_TRACKPOINT(iter->data)->altitude;
1160   t[0] = VIK_TRACKPOINT(iter->data)->timestamp;
1161   iter = tr->trackpoints->next;
1162   gint numpts = 1;
1163   while (iter) {
1164     s[numpts] = VIK_TRACKPOINT(iter->data)->altitude;
1165     t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
1166     numpts++;
1167     iter = iter->next;
1168   }
1169
1170  /* In the following computation, we iterate through periods of time of duration chunk_dur.
1171    * The first period begins at the beginning of the track.  The last period ends at the end of the track.
1172    */
1173   gint index = 0; /* index of the current trackpoint. */
1174   gint i;
1175   for (i = 0; i < num_chunks; i++) {
1176     /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
1177      * find the first trackpoint outside the current interval, averaging the heights between intermediate trackpoints.
1178      */
1179     if (t[0] + i*chunk_dur >= t[index]) {
1180       gdouble acc_s = s[index]; // initialise to first point
1181       while (t[0] + i*chunk_dur >= t[index]) {
1182         acc_s += (s[index+1]-s[index]);
1183         index++;
1184       }
1185       pts[i] = acc_s;
1186     }
1187     else if (i) {
1188       pts[i] = pts[i-1];
1189     }
1190     else {
1191       pts[i] = 0;
1192     }
1193   }
1194   g_free(s);
1195   g_free(t);
1196
1197   return pts;
1198 }
1199
1200 /**
1201  * Make a speed/distance map
1202  */
1203 gdouble *vik_track_make_speed_dist_map ( const VikTrack *tr, guint16 num_chunks )
1204 {
1205   gdouble *v, *s, *t;
1206   time_t t1, t2;
1207   gint i, pt_count, numpts, index;
1208   GList *iter;
1209   gdouble duration, total_length, chunk_length;
1210
1211   if ( ! tr->trackpoints )
1212     return NULL;
1213
1214   t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1215   t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1216   duration = t2 - t1;
1217
1218   if ( !t1 || !t2 || !duration )
1219     return NULL;
1220
1221   if (duration < 0) {
1222     g_warning("negative duration: unsorted trackpoint timestamps?");
1223     return NULL;
1224   }
1225
1226   total_length = vik_track_get_length_including_gaps ( tr );
1227   chunk_length = total_length / num_chunks;
1228   pt_count = vik_track_get_tp_count(tr);
1229
1230   if (chunk_length <= 0) {
1231     return NULL;
1232   }
1233
1234   v = g_malloc ( sizeof(gdouble) * num_chunks );
1235   s = g_malloc ( sizeof(double) * pt_count );
1236   t = g_malloc ( sizeof(double) * pt_count );
1237
1238   // No special handling of segments ATM...
1239   iter = tr->trackpoints->next;
1240   numpts = 0;
1241   s[0] = 0;
1242   t[0] = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1243   numpts++;
1244   while (iter) {
1245     s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
1246     t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
1247     numpts++;
1248     iter = iter->next;
1249   }
1250
1251   // Iterate through a portion of the track to get an average speed for that part
1252   // This will essentially interpolate between segments, which I think is right given the usage of 'get_length_including_gaps'
1253   index = 0; /* index of the current trackpoint. */
1254   for (i = 0; i < num_chunks; i++) {
1255     // Similar to the make_speed_map, but instead of using a time chunk, use a distance chunk
1256     if (s[0] + i*chunk_length >= s[index]) {
1257       gdouble acc_t = 0, acc_s = 0;
1258       while (s[0] + i*chunk_length >= s[index]) {
1259         acc_s += (s[index+1]-s[index]);
1260         acc_t += (t[index+1]-t[index]);
1261         index++;
1262       }
1263       v[i] = acc_s/acc_t;
1264     }
1265     else if (i) {
1266       v[i] = v[i-1];
1267     }
1268     else {
1269       v[i] = 0;
1270     }
1271   }
1272   g_free(s);
1273   g_free(t);
1274   return v;
1275 }
1276
1277 /**
1278  * vik_track_get_tp_by_dist:
1279  * @trk:                  The Track on which to find a Trackpoint
1280  * @meters_from_start:    The distance along a track that the trackpoint returned is near
1281  * @get_next_point:       Since there is a choice of trackpoints, this determines which one to return
1282  * @tp_metres_from_start: For the returned Trackpoint, returns the distance along the track
1283  *
1284  * TODO: Consider changing the boolean get_next_point into an enum with these options PREVIOUS, NEXT, NEAREST
1285  *
1286  * Returns: The #VikTrackpoint fitting the criteria or NULL
1287  */
1288 VikTrackpoint *vik_track_get_tp_by_dist ( VikTrack *trk, gdouble meters_from_start, gboolean get_next_point, gdouble *tp_metres_from_start )
1289 {
1290   gdouble current_dist = 0.0;
1291   gdouble current_inc = 0.0;
1292   if ( tp_metres_from_start )
1293     *tp_metres_from_start = 0.0;
1294
1295   if ( trk->trackpoints ) {
1296     GList *iter = g_list_next ( g_list_first ( trk->trackpoints ) );
1297     while (iter) {
1298       current_inc = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
1299                                      &(VIK_TRACKPOINT(iter->prev->data)->coord) );
1300       current_dist += current_inc;
1301       if ( current_dist >= meters_from_start )
1302         break;
1303       iter = g_list_next ( iter );
1304     }
1305     // passed the end of the track
1306     if ( !iter )
1307       return NULL;
1308
1309     if ( tp_metres_from_start )
1310       *tp_metres_from_start = current_dist;
1311
1312     // we've gone past the distance already, is the previous trackpoint wanted?
1313     if ( !get_next_point ) {
1314       if ( iter->prev ) {
1315         if ( tp_metres_from_start )
1316           *tp_metres_from_start = current_dist-current_inc;
1317         return VIK_TRACKPOINT(iter->prev->data);
1318       }
1319     }
1320     return VIK_TRACKPOINT(iter->data);
1321   }
1322
1323   return NULL;
1324 }
1325
1326 /* by Alex Foobarian */
1327 VikTrackpoint *vik_track_get_closest_tp_by_percentage_dist ( VikTrack *tr, gdouble reldist, gdouble *meters_from_start )
1328 {
1329   gdouble dist = vik_track_get_length_including_gaps(tr) * reldist;
1330   gdouble current_dist = 0.0;
1331   gdouble current_inc = 0.0;
1332   if ( tr->trackpoints )
1333   {
1334     GList *iter = tr->trackpoints->next;
1335     GList *last_iter = NULL;
1336     gdouble last_dist = 0.0;
1337     while (iter)
1338     {
1339       current_inc = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
1340                                      &(VIK_TRACKPOINT(iter->prev->data)->coord) );
1341       last_dist = current_dist;
1342       current_dist += current_inc;
1343       if ( current_dist >= dist )
1344         break;
1345       last_iter = iter;
1346       iter = iter->next;
1347     }
1348     if (!iter) { /* passing the end the track */
1349       if (last_iter) {
1350         if (meters_from_start)
1351           *meters_from_start = last_dist;
1352         return(VIK_TRACKPOINT(last_iter->data));
1353       }
1354       else
1355         return NULL;
1356     }
1357     /* we've gone past the dist already, was prev trackpoint closer? */
1358     /* should do a vik_coord_average_weighted() thingy. */
1359     if ( iter->prev && fabs(current_dist-current_inc-dist) < fabs(current_dist-dist) ) {
1360       if (meters_from_start)
1361         *meters_from_start = last_dist;
1362       iter = iter->prev;
1363     }
1364     else
1365       if (meters_from_start)
1366         *meters_from_start = current_dist;
1367
1368     return VIK_TRACKPOINT(iter->data);
1369
1370   }
1371   return NULL;
1372 }
1373
1374 VikTrackpoint *vik_track_get_closest_tp_by_percentage_time ( VikTrack *tr, gdouble reltime, time_t *seconds_from_start )
1375 {
1376   if ( !tr->trackpoints )
1377     return NULL;
1378
1379   time_t t_pos, t_start, t_end, t_total;
1380   t_start = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1381   t_end = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1382   t_total = t_end - t_start;
1383
1384   t_pos = t_start + t_total * reltime;
1385
1386   GList *iter = tr->trackpoints;
1387
1388   while (iter) {
1389     if (VIK_TRACKPOINT(iter->data)->timestamp == t_pos)
1390       break;
1391     if (VIK_TRACKPOINT(iter->data)->timestamp > t_pos) {
1392       if (iter->prev == NULL)  /* first trackpoint */
1393         break;
1394       time_t t_before = t_pos - VIK_TRACKPOINT(iter->prev->data)->timestamp;
1395       time_t t_after = VIK_TRACKPOINT(iter->data)->timestamp - t_pos;
1396       if (t_before <= t_after)
1397         iter = iter->prev;
1398       break;
1399     }
1400     else if ((iter->next == NULL) && (t_pos < (VIK_TRACKPOINT(iter->data)->timestamp + 3))) /* last trackpoint: accommodate for round-off */
1401       break;
1402     iter = iter->next;
1403   }
1404
1405   if (!iter)
1406     return NULL;
1407   if (seconds_from_start)
1408     *seconds_from_start = VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1409   return VIK_TRACKPOINT(iter->data);
1410 }
1411
1412 VikTrackpoint* vik_track_get_tp_by_max_speed ( const VikTrack *tr )
1413 {
1414   gdouble maxspeed = 0.0, speed = 0.0;
1415
1416   if ( !tr->trackpoints )
1417     return NULL;
1418
1419   GList *iter = tr->trackpoints;
1420   VikTrackpoint *max_speed_tp = NULL;
1421
1422   while (iter) {
1423     if (iter->prev) {
1424       if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
1425            VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
1426            (! VIK_TRACKPOINT(iter->data)->newsegment) ) {
1427         speed =  vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) )
1428           / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
1429         if ( speed > maxspeed ) {
1430           maxspeed = speed;
1431           max_speed_tp = VIK_TRACKPOINT(iter->data);
1432         }
1433       }
1434     }
1435     iter = iter->next;
1436   }
1437   
1438   if (!max_speed_tp)
1439     return NULL;
1440
1441   return max_speed_tp;
1442 }
1443
1444 VikTrackpoint* vik_track_get_tp_by_max_alt ( const VikTrack *tr )
1445 {
1446   gdouble maxalt = -5000.0;
1447   if ( !tr->trackpoints )
1448     return NULL;
1449
1450   GList *iter = tr->trackpoints;
1451   VikTrackpoint *max_alt_tp = NULL;
1452
1453   while (iter) {
1454     if ( VIK_TRACKPOINT(iter->data)->altitude > maxalt ) {
1455       maxalt = VIK_TRACKPOINT(iter->data)->altitude;
1456       max_alt_tp = VIK_TRACKPOINT(iter->data);
1457     }
1458     iter = iter->next;
1459   }
1460
1461   if (!max_alt_tp)
1462     return NULL;
1463
1464   return max_alt_tp;
1465 }
1466
1467 VikTrackpoint* vik_track_get_tp_by_min_alt ( const VikTrack *tr )
1468 {
1469   gdouble minalt = 25000.0;
1470   if ( !tr->trackpoints )
1471     return NULL;
1472
1473   GList *iter = tr->trackpoints;
1474   VikTrackpoint *min_alt_tp = NULL;
1475
1476   while (iter) {
1477     if ( VIK_TRACKPOINT(iter->data)->altitude < minalt ) {
1478       minalt = VIK_TRACKPOINT(iter->data)->altitude;
1479       min_alt_tp = VIK_TRACKPOINT(iter->data);
1480     }
1481     iter = iter->next;
1482   }
1483
1484   if (!min_alt_tp)
1485     return NULL;
1486
1487   return min_alt_tp;
1488 }
1489
1490 VikTrackpoint *vik_track_get_tp_first( const VikTrack *tr )
1491 {
1492   if ( !tr->trackpoints )
1493     return NULL;
1494
1495   return (VikTrackpoint*)g_list_first(tr->trackpoints)->data;
1496 }
1497
1498 VikTrackpoint *vik_track_get_tp_last ( const VikTrack *tr )
1499 {
1500   if ( !tr->trackpoints )
1501     return NULL;
1502
1503   return (VikTrackpoint*)g_list_last(tr->trackpoints)->data;
1504 }
1505
1506 VikTrackpoint *vik_track_get_tp_prev ( const VikTrack *tr, VikTrackpoint *tp )
1507 {
1508   if ( !tr->trackpoints )
1509     return NULL;
1510
1511   GList *iter = tr->trackpoints;
1512   VikTrackpoint *tp_prev = NULL;
1513
1514   while (iter) {
1515     if (iter->prev) {
1516       if ( VIK_TRACKPOINT(iter->data) == tp ) {
1517         tp_prev = VIK_TRACKPOINT(iter->prev->data);
1518         break;
1519       }
1520     }
1521     iter = iter->next;
1522   }
1523
1524   return tp_prev;
1525 }
1526
1527 gboolean vik_track_get_minmax_alt ( const VikTrack *tr, gdouble *min_alt, gdouble *max_alt )
1528 {
1529   *min_alt = 25000;
1530   *max_alt = -5000;
1531   if ( tr && tr->trackpoints && tr->trackpoints->data && (VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE) ) {
1532     GList *iter = tr->trackpoints->next;
1533     gdouble tmp_alt;
1534     while (iter)
1535     {
1536       tmp_alt = VIK_TRACKPOINT(iter->data)->altitude;
1537       if ( tmp_alt > *max_alt )
1538         *max_alt = tmp_alt;
1539       if ( tmp_alt < *min_alt )
1540         *min_alt = tmp_alt;
1541       iter = iter->next;
1542     }
1543     return TRUE;
1544   }
1545   return FALSE;
1546 }
1547
1548 void vik_track_marshall ( VikTrack *tr, guint8 **data, guint *datalen)
1549 {
1550   GList *tps;
1551   GByteArray *b = g_byte_array_new();
1552   guint len;
1553   guint intp, ntp;
1554
1555   g_byte_array_append(b, (guint8 *)tr, sizeof(*tr));
1556
1557   /* we'll fill out number of trackpoints later */
1558   intp = b->len;
1559   g_byte_array_append(b, (guint8 *)&len, sizeof(len));
1560
1561   // This allocates space for variant sized strings
1562   //  and copies that amount of data from the track to byte array
1563 #define vtm_append(s) \
1564   len = (s) ? strlen(s)+1 : 0; \
1565   g_byte_array_append(b, (guint8 *)&len, sizeof(len)); \
1566   if (s) g_byte_array_append(b, (guint8 *)s, len);
1567
1568   tps = tr->trackpoints;
1569   ntp = 0;
1570   while (tps) {
1571     g_byte_array_append(b, (guint8 *)tps->data, sizeof(VikTrackpoint));
1572     vtm_append(VIK_TRACKPOINT(tps->data)->name);
1573     tps = tps->next;
1574     ntp++;
1575   }
1576   *(guint *)(b->data + intp) = ntp;
1577
1578   vtm_append(tr->name);
1579   vtm_append(tr->comment);
1580   vtm_append(tr->description);
1581   vtm_append(tr->source);
1582
1583   *data = b->data;
1584   *datalen = b->len;
1585   g_byte_array_free(b, FALSE);
1586 }
1587
1588 /*
1589  * Take a byte array and convert it into a Track
1590  */
1591 VikTrack *vik_track_unmarshall (guint8 *data, guint datalen)
1592 {
1593   guint len;
1594   VikTrack *new_tr = vik_track_new();
1595   VikTrackpoint *new_tp;
1596   guint ntp;
1597   gint i;
1598
1599   /* basic properties: */
1600   new_tr->visible = ((VikTrack *)data)->visible;
1601   new_tr->is_route = ((VikTrack *)data)->is_route;
1602   new_tr->draw_name_mode = ((VikTrack *)data)->draw_name_mode;
1603   new_tr->max_number_dist_labels = ((VikTrack *)data)->max_number_dist_labels;
1604   new_tr->has_color = ((VikTrack *)data)->has_color;
1605   new_tr->color = ((VikTrack *)data)->color;
1606   new_tr->bbox = ((VikTrack *)data)->bbox;
1607
1608   data += sizeof(*new_tr);
1609
1610   ntp = *(guint *)data;
1611   data += sizeof(ntp);
1612
1613 #define vtu_get(s) \
1614   len = *(guint *)data; \
1615   data += sizeof(len); \
1616   if (len) { \
1617     (s) = g_strdup((gchar *)data); \
1618   } else { \
1619     (s) = NULL; \
1620   } \
1621   data += len;
1622
1623   for (i=0; i<ntp; i++) {
1624     new_tp = vik_trackpoint_new();
1625     memcpy(new_tp, data, sizeof(*new_tp));
1626     data += sizeof(*new_tp);
1627     vtu_get(new_tp->name);
1628     new_tr->trackpoints = g_list_prepend(new_tr->trackpoints, new_tp);
1629   }
1630   if ( new_tr->trackpoints )
1631     new_tr->trackpoints = g_list_reverse(new_tr->trackpoints);
1632
1633   vtu_get(new_tr->name);
1634   vtu_get(new_tr->comment);
1635   vtu_get(new_tr->description);
1636   vtu_get(new_tr->source);
1637
1638   return new_tr;
1639 }
1640
1641 /**
1642  * (Re)Calculate the bounds of the given track,
1643  *  updating the track's bounds data.
1644  * This should be called whenever a track's trackpoints are changed
1645  */
1646 void vik_track_calculate_bounds ( VikTrack *trk )
1647 {
1648   GList *tp_iter;
1649   tp_iter = trk->trackpoints;
1650   
1651   struct LatLon topleft, bottomright, ll;
1652   
1653   // Set bounds to first point
1654   if ( tp_iter ) {
1655     vik_coord_to_latlon ( &(VIK_TRACKPOINT(tp_iter->data)->coord), &topleft );
1656     vik_coord_to_latlon ( &(VIK_TRACKPOINT(tp_iter->data)->coord), &bottomright );
1657   }
1658   while ( tp_iter ) {
1659
1660     // See if this trackpoint increases the track bounds.
1661    
1662     vik_coord_to_latlon ( &(VIK_TRACKPOINT(tp_iter->data)->coord), &ll );
1663   
1664     if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
1665     if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
1666     if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
1667     if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
1668     
1669     tp_iter = tp_iter->next;
1670   }
1671  
1672   g_debug ( "Bounds of track: '%s' is: %f,%f to: %f,%f", trk->name, topleft.lat, topleft.lon, bottomright.lat, bottomright.lon );
1673
1674   trk->bbox.north = topleft.lat;
1675   trk->bbox.east = bottomright.lon;
1676   trk->bbox.south = bottomright.lat;
1677   trk->bbox.west = topleft.lon;
1678 }
1679
1680 /**
1681  * vik_track_anonymize_times:
1682  *
1683  * Shift all timestamps to be relatively offset from 1901-01-01
1684  */
1685 void vik_track_anonymize_times ( VikTrack *tr )
1686 {
1687   GTimeVal gtv;
1688   // Check result just to please Coverity - even though it shouldn't fail as it's a hard coded value here!
1689   if ( !g_time_val_from_iso8601 ( "1901-01-01T00:00:00Z", &gtv ) ) {
1690     g_critical ( "Calendar time value failure" );
1691     return;
1692   }
1693
1694   time_t anon_timestamp = gtv.tv_sec;
1695   time_t offset = 0;
1696
1697   GList *tp_iter;
1698   tp_iter = tr->trackpoints;
1699   while ( tp_iter ) {
1700     VikTrackpoint *tp = VIK_TRACKPOINT(tp_iter->data);
1701     if ( tp->has_timestamp ) {
1702       // Calculate an offset in time using the first available timestamp
1703       if ( offset == 0 )
1704         offset = tp->timestamp - anon_timestamp;
1705
1706       // Apply this offset to shift all timestamps towards 1901 & hence anonymising the time
1707       // Note that the relative difference between timestamps is kept - thus calculating speeds will still work
1708       tp->timestamp = tp->timestamp - offset;
1709     }
1710     tp_iter = tp_iter->next;
1711   }
1712 }
1713
1714 /**
1715  * vik_track_interpolate_times:
1716  *
1717  * Interpolate the timestamps between first and last trackpoint,
1718  * so that the track is driven at equal speed, regardless of the
1719  * distance between individual trackpoints.
1720  *
1721  * NB This will overwrite any existing trackpoint timestamps
1722  */
1723 void vik_track_interpolate_times ( VikTrack *tr )
1724 {
1725   gdouble tr_dist, cur_dist;
1726   time_t tsdiff, tsfirst;
1727
1728   GList *iter;
1729   iter = tr->trackpoints;
1730
1731   VikTrackpoint *tp = VIK_TRACKPOINT(iter->data);
1732   if ( tp->has_timestamp ) {
1733     tsfirst = tp->timestamp;
1734
1735     // Find the end of the track and the last timestamp
1736     while ( iter->next ) {
1737       iter = iter->next;
1738     }
1739     tp = VIK_TRACKPOINT(iter->data);
1740     if ( tp->has_timestamp ) {
1741       tsdiff = tp->timestamp - tsfirst;
1742
1743       tr_dist = vik_track_get_length_including_gaps ( tr );
1744       cur_dist = 0.0;
1745
1746       if ( tr_dist > 0 ) {
1747         iter = tr->trackpoints;
1748         // Apply the calculated timestamp to all trackpoints except the first and last ones
1749         while ( iter->next && iter->next->next ) {
1750           iter = iter->next;
1751           tp = VIK_TRACKPOINT(iter->data);
1752           cur_dist += vik_coord_diff ( &(tp->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) );
1753
1754           tp->timestamp = (cur_dist / tr_dist) * tsdiff + tsfirst;
1755           tp->has_timestamp = TRUE;
1756         }
1757         // Some points may now have the same time so remove them.
1758         vik_track_remove_same_time_points ( tr );
1759       }
1760     }
1761   }
1762 }
1763
1764 /**
1765  * vik_track_apply_dem_data:
1766  * @skip_existing: When TRUE, don't change the elevation if the trackpoint already has a value
1767  *
1768  * Set elevation data for a track using any available DEM information
1769  */
1770 gulong vik_track_apply_dem_data ( VikTrack *tr, gboolean skip_existing )
1771 {
1772   gulong num = 0;
1773   GList *tp_iter;
1774   gint16 elev;
1775   tp_iter = tr->trackpoints;
1776   while ( tp_iter ) {
1777     // Don't apply if the point already has a value and the overwrite is off
1778     if ( !(skip_existing && VIK_TRACKPOINT(tp_iter->data)->altitude != VIK_DEFAULT_ALTITUDE) ) {
1779       /* TODO: of the 4 possible choices we have for choosing an elevation
1780        * (trackpoint in between samples), choose the one with the least elevation change
1781        * as the last */
1782       elev = a_dems_get_elev_by_coord ( &(VIK_TRACKPOINT(tp_iter->data)->coord), VIK_DEM_INTERPOL_BEST );
1783
1784       if ( elev != VIK_DEM_INVALID_ELEVATION ) {
1785         VIK_TRACKPOINT(tp_iter->data)->altitude = elev;
1786         num++;
1787       }
1788     }
1789     tp_iter = tp_iter->next;
1790   }
1791   return num;
1792 }
1793
1794 /**
1795  * vik_track_apply_dem_data_last_trackpoint:
1796  * Apply DEM data (if available) - to only the last trackpoint
1797  */
1798 void vik_track_apply_dem_data_last_trackpoint ( VikTrack *tr )
1799 {
1800   gint16 elev;
1801   if ( tr->trackpoints ) {
1802     /* As in vik_track_apply_dem_data above - use 'best' interpolation method */
1803     elev = a_dems_get_elev_by_coord ( &(VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->coord), VIK_DEM_INTERPOL_BEST );
1804     if ( elev != VIK_DEM_INVALID_ELEVATION )
1805       VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->altitude = elev;
1806   }
1807 }
1808
1809
1810 /**
1811  * smoothie:
1812  *
1813  * Apply elevation smoothing over range of trackpoints between the list start and end points
1814  */
1815 static void smoothie ( GList *tp1, GList *tp2, gdouble elev1, gdouble elev2, guint points )
1816 {
1817   // If was really clever could try and weigh interpolation according to the distance between trackpoints somehow
1818   // Instead a simple average interpolation for the number of points given.
1819   gdouble change = (elev2 - elev1)/(points+1);
1820   gint count = 1;
1821   GList *tp_iter = tp1;
1822   while ( tp_iter != tp2 && tp_iter ) {
1823     VikTrackpoint *tp = VIK_TRACKPOINT(tp_iter->data);
1824
1825     tp->altitude = elev1 + (change*count);
1826
1827     count++;
1828     tp_iter = tp_iter->next;
1829   }
1830 }
1831
1832 /**
1833  * vik_track_smooth_missing_elevation_data:
1834  * @flat: Specify how the missing elevations will be set.
1835  *        When TRUE it uses a simple flat method, using the last known elevation
1836  *        When FALSE is uses an interpolation method to the next known elevation
1837  *
1838  * For each point with a missing elevation, set it to use the last known available elevation value.
1839  * Primarily of use for smallish DEM holes where it is missing elevation data.
1840  * Eg see Austria: around N47.3 & E13.8
1841  *
1842  * Returns: The number of points that were adjusted
1843  */
1844 gulong vik_track_smooth_missing_elevation_data ( VikTrack *tr, gboolean flat )
1845 {
1846   gulong num = 0;
1847
1848   GList *tp_iter;
1849   gdouble elev = VIK_DEFAULT_ALTITUDE;
1850
1851   VikTrackpoint *tp_missing = NULL;
1852   GList *iter_first = NULL;
1853   guint points = 0;
1854
1855   tp_iter = tr->trackpoints;
1856   while ( tp_iter ) {
1857     VikTrackpoint *tp = VIK_TRACKPOINT(tp_iter->data);
1858
1859     if ( VIK_DEFAULT_ALTITUDE == tp->altitude ) {
1860       if ( flat ) {
1861         // Simply assign to last known value
1862         if ( elev != VIK_DEFAULT_ALTITUDE ) {
1863           tp->altitude = elev;
1864           num++;
1865         }
1866       }
1867       else {
1868         if ( !tp_missing ) {
1869           // Remember the first trackpoint (and the list pointer to it) of a section of no altitudes
1870           tp_missing = tp;
1871           iter_first = tp_iter;
1872           points = 1;
1873         }
1874         else {
1875           // More missing altitudes
1876           points++;
1877         }
1878       }
1879     }
1880     else {
1881       // Altitude available (maybe again!)
1882       // If this marks the end of a section of altitude-less points
1883       //  then apply smoothing for that section of points
1884       if ( points > 0 && elev != VIK_DEFAULT_ALTITUDE )
1885         if ( !flat ) {
1886           smoothie ( iter_first, tp_iter, elev, tp->altitude, points );
1887           num = num + points;
1888         }
1889
1890       // reset
1891       points = 0;
1892       tp_missing = NULL;
1893
1894       // Store for reuse as the last known good value
1895       elev = tp->altitude;
1896     }
1897
1898     tp_iter = tp_iter->next;
1899   }
1900
1901   return num;
1902 }
1903
1904 /**
1905  * vik_track_steal_and_append_trackpoints:
1906  * 
1907  * appends t2 to t1, leaving t2 with no trackpoints
1908  */
1909 void vik_track_steal_and_append_trackpoints ( VikTrack *t1, VikTrack *t2 )
1910 {
1911   if ( t1->trackpoints ) {
1912     t1->trackpoints = g_list_concat ( t1->trackpoints, t2->trackpoints );
1913   } else
1914     t1->trackpoints = t2->trackpoints;
1915   t2->trackpoints = NULL;
1916
1917   // Trackpoints updated - so update the bounds
1918   vik_track_calculate_bounds ( t1 );
1919 }
1920
1921 /**
1922  * vik_track_cut_back_to_double_point:
1923  * 
1924  * starting at the end, looks backwards for the last "double point", a duplicate trackpoint.
1925  * If there is no double point, deletes all the trackpoints.
1926  * 
1927  * Returns: the new end of the track (or the start if there are no double points)
1928  */
1929 VikCoord *vik_track_cut_back_to_double_point ( VikTrack *tr )
1930 {
1931   GList *iter = tr->trackpoints;
1932   VikCoord *rv;
1933
1934   if ( !iter )
1935     return NULL;
1936   while ( iter->next )
1937     iter = iter->next;
1938
1939
1940   while ( iter->prev ) {
1941     VikCoord *cur_coord = &((VikTrackpoint*)iter->data)->coord;
1942     VikCoord *prev_coord = &((VikTrackpoint*)iter->prev->data)->coord;
1943     if ( vik_coord_equals(cur_coord, prev_coord) ) {
1944       GList *prev = iter->prev;
1945
1946       rv = g_malloc(sizeof(VikCoord));
1947       *rv = *cur_coord;
1948
1949       /* truncate trackpoint list */
1950       iter->prev = NULL; /* pretend it's the end */
1951       g_list_foreach ( iter, (GFunc) g_free, NULL );
1952       g_list_free( iter );
1953
1954       prev->next = NULL;
1955
1956       return rv;
1957     }
1958     iter = iter->prev;
1959   }
1960
1961   /* no double point found! */
1962   rv = g_malloc(sizeof(VikCoord));
1963   *rv = ((VikTrackpoint*) tr->trackpoints->data)->coord;
1964   g_list_foreach ( tr->trackpoints, (GFunc) g_free, NULL );
1965   g_list_free( tr->trackpoints );
1966   tr->trackpoints = NULL;
1967   return rv;
1968 }
1969
1970 /**
1971  * Function to compare two tracks by their first timestamp
1972  **/
1973 int vik_track_compare_timestamp (const void *x, const void *y)
1974 {
1975   VikTrack *a = (VikTrack *)x;
1976   VikTrack *b = (VikTrack *)y;
1977
1978   VikTrackpoint *tpa = NULL;
1979   VikTrackpoint *tpb = NULL;
1980
1981   if ( a->trackpoints )
1982     tpa = VIK_TRACKPOINT(g_list_first(a->trackpoints)->data);
1983
1984   if ( b->trackpoints )
1985     tpb = VIK_TRACKPOINT(g_list_first(b->trackpoints)->data);
1986
1987   if ( tpa && tpb ) {
1988     if ( tpa->timestamp < tpb->timestamp )
1989       return -1;
1990     if ( tpa->timestamp > tpb->timestamp )
1991       return 1;
1992   }
1993
1994   if ( tpa && !tpb )
1995     return 1;
1996
1997   if ( !tpa && tpb )
1998     return -1;
1999
2000   return 0;
2001 }