]> git.street.me.uk Git - andy/viking.git/blob - src/viktrack.c
Minimum GTK+2.14 required for the build.
[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
42 VikTrack *vik_track_new()
43 {
44   VikTrack *tr = g_malloc0 ( sizeof ( VikTrack ) );
45   tr->ref_count = 1;
46   return tr;
47 }
48
49 void vik_track_set_comment_no_copy(VikTrack *tr, gchar *comment)
50 {
51   if ( tr->comment )
52     g_free ( tr->comment );
53   tr->comment = comment;
54 }
55
56
57 void vik_track_set_name(VikTrack *tr, const gchar *name)
58 {
59   if ( tr->name )
60     g_free ( tr->name );
61
62   tr->name = g_strdup(name);
63 }
64
65 void vik_track_set_comment(VikTrack *tr, const gchar *comment)
66 {
67   if ( tr->comment )
68     g_free ( tr->comment );
69
70   if ( comment && comment[0] != '\0' )
71     tr->comment = g_strdup(comment);
72   else
73     tr->comment = NULL;
74 }
75
76 void vik_track_set_description(VikTrack *tr, const gchar *description)
77 {
78   if ( tr->description )
79     g_free ( tr->description );
80
81   if ( description && description[0] != '\0' )
82     tr->description = g_strdup(description);
83   else
84     tr->description = NULL;
85 }
86
87 void vik_track_ref(VikTrack *tr)
88 {
89   tr->ref_count++;
90 }
91
92 void vik_track_set_property_dialog(VikTrack *tr, GtkWidget *dialog)
93 {
94   /* Warning: does not check for existing dialog */
95   tr->property_dialog = dialog;
96 }
97
98 void vik_track_clear_property_dialog(VikTrack *tr)
99 {
100   tr->property_dialog = NULL;
101 }
102
103 void vik_track_free(VikTrack *tr)
104 {
105   if ( tr->ref_count-- > 1 )
106     return;
107
108   if ( tr->name )
109     g_free ( tr->name );
110   if ( tr->comment )
111     g_free ( tr->comment );
112   if ( tr->description )
113     g_free ( tr->description );
114   g_list_foreach ( tr->trackpoints, (GFunc) g_free, NULL );
115   g_list_free( tr->trackpoints );
116   if (tr->property_dialog)
117     if ( GTK_IS_WIDGET(tr->property_dialog) )
118       gtk_widget_destroy ( GTK_WIDGET(tr->property_dialog) );
119   g_free ( tr );
120 }
121
122 /**
123  * vik_track_copy:
124  * @tr: The Track to copy
125  * @copy_points: Whether to copy the track points or not
126  *
127  * Normally for copying the track it's best to copy all the trackpoints
128  * However for some operations such as splitting tracks the trackpoints will be managed separately, so no need to copy them.
129  *
130  * Returns: the copied VikTrack
131  */
132 VikTrack *vik_track_copy ( const VikTrack *tr, gboolean copy_points )
133 {
134   VikTrack *new_tr = vik_track_new();
135   VikTrackpoint *new_tp;
136   GList *tp_iter = tr->trackpoints;
137   new_tr->visible = tr->visible;
138   new_tr->is_route = tr->is_route;
139   new_tr->has_color = tr->has_color;
140   new_tr->color = tr->color;
141   new_tr->bbox = tr->bbox;
142   new_tr->trackpoints = NULL;
143   if ( copy_points )
144   {
145     while ( tp_iter )
146     {
147       new_tp = g_malloc ( sizeof ( VikTrackpoint ) );
148       *new_tp = *((VikTrackpoint *)(tp_iter->data));
149       new_tr->trackpoints = g_list_append ( new_tr->trackpoints, new_tp );
150       tp_iter = tp_iter->next;
151     }
152   }
153   vik_track_set_name(new_tr,tr->name);
154   vik_track_set_comment(new_tr,tr->comment);
155   vik_track_set_description(new_tr,tr->description);
156   return new_tr;
157 }
158
159 VikTrackpoint *vik_trackpoint_new()
160 {
161   VikTrackpoint *tp = g_malloc0(sizeof(VikTrackpoint));
162   tp->speed = NAN;
163   tp->course = NAN;
164   tp->altitude = VIK_DEFAULT_ALTITUDE;
165   tp->hdop = VIK_DEFAULT_DOP;
166   tp->vdop = VIK_DEFAULT_DOP;
167   tp->pdop = VIK_DEFAULT_DOP;
168   return tp;
169 }
170
171 void vik_trackpoint_free(VikTrackpoint *tp)
172 {
173   g_free(tp);
174 }
175
176 VikTrackpoint *vik_trackpoint_copy(VikTrackpoint *tp)
177 {
178   VikTrackpoint *rv = vik_trackpoint_new();
179   *rv = *tp;
180   return rv;
181 }
182
183 gdouble vik_track_get_length(const VikTrack *tr)
184 {
185   gdouble len = 0.0;
186   if ( tr->trackpoints )
187   {
188     GList *iter = tr->trackpoints->next;
189     while (iter)
190     {
191       if ( ! VIK_TRACKPOINT(iter->data)->newsegment )
192         len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
193                                 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
194       iter = iter->next;
195     }
196   }
197   return len;
198 }
199
200 gdouble vik_track_get_length_including_gaps(const VikTrack *tr)
201 {
202   gdouble len = 0.0;
203   if ( tr->trackpoints )
204   {
205     GList *iter = tr->trackpoints->next;
206     while (iter)
207     {
208       len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
209                               &(VIK_TRACKPOINT(iter->prev->data)->coord) );
210       iter = iter->next;
211     }
212   }
213   return len;
214 }
215
216 gulong vik_track_get_tp_count(const VikTrack *tr)
217 {
218   gulong num = 0;
219   GList *iter = tr->trackpoints;
220   while ( iter )
221   {
222     num++;
223     iter = iter->next;
224   }
225   return num;
226 }
227
228 gulong vik_track_get_dup_point_count ( const VikTrack *tr )
229 {
230   gulong num = 0;
231   GList *iter = tr->trackpoints;
232   while ( iter )
233   {
234     if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
235                        &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
236       num++;
237     iter = iter->next;
238   }
239   return num;
240 }
241
242 /*
243  * Deletes adjacent points that have the same position
244  * Returns the number of points that were deleted
245  */
246 gulong vik_track_remove_dup_points ( VikTrack *tr )
247 {
248   gulong num = 0;
249   GList *iter = tr->trackpoints;
250   while ( iter )
251   {
252     if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
253                        &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
254     {
255       num++;
256       // Maintain track segments
257       if ( VIK_TRACKPOINT(iter->next->data)->newsegment && (iter->next)->next )
258         VIK_TRACKPOINT(((iter->next)->next)->data)->newsegment = TRUE;
259
260       vik_trackpoint_free ( iter->next->data );
261       tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next );
262     }
263     else
264       iter = iter->next;
265   }
266
267   // NB isn't really be necessary as removing duplicate points shouldn't alter the bounds!
268   vik_track_calculate_bounds ( tr );
269
270   return num;
271 }
272
273 /*
274  * Get a count of trackpoints with the same defined timestamp
275  * Note is using timestamps with a resolution with 1 second
276  */
277 gulong vik_track_get_same_time_point_count ( const VikTrack *tr )
278 {
279   gulong num = 0;
280   GList *iter = tr->trackpoints;
281   while ( iter ) {
282     if ( iter->next &&
283          ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
284            VIK_TRACKPOINT(iter->next->data)->has_timestamp ) &&
285          ( VIK_TRACKPOINT(iter->data)->timestamp ==
286            VIK_TRACKPOINT(iter->next->data)->timestamp) )
287       num++;
288     iter = iter->next;
289   }
290   return num;
291 }
292
293 /*
294  * Deletes adjacent points that have the same defined timestamp
295  * Returns the number of points that were deleted
296  */
297 gulong vik_track_remove_same_time_points ( VikTrack *tr )
298 {
299   gulong num = 0;
300   GList *iter = tr->trackpoints;
301   while ( iter ) {
302     if ( iter->next &&
303          ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
304            VIK_TRACKPOINT(iter->next->data)->has_timestamp ) &&
305          ( VIK_TRACKPOINT(iter->data)->timestamp ==
306            VIK_TRACKPOINT(iter->next->data)->timestamp) ) {
307
308       num++;
309       
310       // Maintain track segments
311       if ( VIK_TRACKPOINT(iter->next->data)->newsegment && (iter->next)->next )
312         VIK_TRACKPOINT(((iter->next)->next)->data)->newsegment = TRUE;
313
314       vik_trackpoint_free ( iter->next->data );
315       tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next );
316     }
317     else
318       iter = iter->next;
319   }
320
321   vik_track_calculate_bounds ( tr );
322
323   return num;
324 }
325
326 /*
327  * Deletes all 'extra' trackpoint information
328  *  such as time stamps, speed, course etc...
329  */
330 void vik_track_to_routepoints ( VikTrack *tr )
331 {
332   GList *iter = tr->trackpoints;
333   while ( iter ) {
334
335     // c.f. with vik_trackpoint_new()
336
337     VIK_TRACKPOINT(iter->data)->has_timestamp = FALSE;
338     VIK_TRACKPOINT(iter->data)->timestamp = 0;
339     VIK_TRACKPOINT(iter->data)->speed = NAN;
340     VIK_TRACKPOINT(iter->data)->course = NAN;
341     VIK_TRACKPOINT(iter->data)->hdop = VIK_DEFAULT_DOP;
342     VIK_TRACKPOINT(iter->data)->vdop = VIK_DEFAULT_DOP;
343     VIK_TRACKPOINT(iter->data)->pdop = VIK_DEFAULT_DOP;
344     VIK_TRACKPOINT(iter->data)->nsats = 0;
345     VIK_TRACKPOINT(iter->data)->fix_mode = VIK_GPS_MODE_NOT_SEEN;
346
347     iter = iter->next;
348   }
349 }
350
351 guint vik_track_get_segment_count(const VikTrack *tr)
352 {
353   guint num = 1;
354   GList *iter = tr->trackpoints;
355   if ( !iter )
356     return 0;
357   while ( (iter = iter->next) )
358   {
359     if ( VIK_TRACKPOINT(iter->data)->newsegment )
360       num++;
361   }
362   return num;
363 }
364
365 VikTrack **vik_track_split_into_segments(VikTrack *t, guint *ret_len)
366 {
367   VikTrack **rv;
368   VikTrack *tr;
369   guint i;
370   guint segs = vik_track_get_segment_count(t);
371   GList *iter;
372
373   if ( segs < 2 )
374   {
375     *ret_len = 0;
376     return NULL;
377   }
378
379   rv = g_malloc ( segs * sizeof(VikTrack *) );
380   tr = vik_track_copy ( t, TRUE );
381   rv[0] = tr;
382   iter = tr->trackpoints;
383
384   i = 1;
385   while ( (iter = iter->next) )
386   {
387     if ( VIK_TRACKPOINT(iter->data)->newsegment )
388     {
389       iter->prev->next = NULL;
390       iter->prev = NULL;
391       rv[i] = vik_track_copy ( tr, FALSE );
392       rv[i]->trackpoints = iter;
393
394       vik_track_calculate_bounds ( rv[i] );
395
396       i++;
397     }
398   }
399   *ret_len = segs;
400   return rv;
401 }
402
403 /*
404  * Simply remove any subsequent segment markers in a track to form one continuous track
405  * Return the number of segments merged
406  */
407 guint vik_track_merge_segments(VikTrack *tr)
408 {
409   guint num = 0;
410   GList *iter = tr->trackpoints;
411   if ( !iter )
412     return num;
413
414   // Always skip the first point as this should be the first segment
415   iter = iter->next;
416
417   while ( (iter = iter->next) )
418   {
419     if ( VIK_TRACKPOINT(iter->data)->newsegment ) {
420       VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
421       num++;
422     }
423   }
424   return num;
425 }
426
427 void vik_track_reverse ( VikTrack *tr )
428 {
429   if ( ! tr->trackpoints )
430     return;
431
432   tr->trackpoints = g_list_reverse(tr->trackpoints);
433
434   /* fix 'newsegment' */
435   GList *iter = g_list_last ( tr->trackpoints );
436   while ( iter )
437   {
438     if ( ! iter->next ) /* last segment, was first, cancel newsegment. */
439       VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
440     if ( ! iter->prev ) /* first segment by convention has newsegment flag. */
441       VIK_TRACKPOINT(iter->data)->newsegment = TRUE;
442     else if ( VIK_TRACKPOINT(iter->data)->newsegment && iter->next )
443     {
444       VIK_TRACKPOINT(iter->next->data)->newsegment = TRUE;
445       VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
446     }
447     iter = iter->prev;
448   }
449 }
450
451 gdouble vik_track_get_average_speed(const VikTrack *tr)
452 {
453   gdouble len = 0.0;
454   guint32 time = 0;
455   if ( tr->trackpoints )
456   {
457     GList *iter = tr->trackpoints->next;
458     while (iter)
459     {
460       if ( VIK_TRACKPOINT(iter->data)->has_timestamp && 
461           VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
462           (! VIK_TRACKPOINT(iter->data)->newsegment) )
463       {
464         len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
465                                 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
466         time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
467       }
468       iter = iter->next;
469     }
470   }
471   return (time == 0) ? 0 : ABS(len/time);
472 }
473
474 /**
475  * Based on a simple average speed, but with a twist - to give a moving average.
476  *  . GPSs often report a moving average in their statistics output
477  *  . bicycle speedos often don't factor in time when stopped - hence reporting a moving average for speed
478  *
479  * Often GPS track will record every second but not when stationary
480  * This method doesn't use samples that differ over the specified time limit - effectively skipping that time chunk from the total time
481  *
482  * Suggest to use 60 seconds as the stop length (as the default used in the TrackWaypoint draw stops factor)
483  */
484 gdouble vik_track_get_average_speed_moving (const VikTrack *tr, int stop_length_seconds)
485 {
486   gdouble len = 0.0;
487   guint32 time = 0;
488   if ( tr->trackpoints )
489   {
490     GList *iter = tr->trackpoints->next;
491     while (iter)
492     {
493       if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
494           VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
495           (! VIK_TRACKPOINT(iter->data)->newsegment) )
496       {
497         if ( ( VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp ) < stop_length_seconds ) {
498           len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
499                                   &(VIK_TRACKPOINT(iter->prev->data)->coord) );
500         
501           time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
502         }
503       }
504       iter = iter->next;
505     }
506   }
507   return (time == 0) ? 0 : ABS(len/time);
508 }
509
510 gdouble vik_track_get_max_speed(const VikTrack *tr)
511 {
512   gdouble maxspeed = 0.0, speed = 0.0;
513   if ( tr->trackpoints )
514   {
515     GList *iter = tr->trackpoints->next;
516     while (iter)
517     {
518       if ( VIK_TRACKPOINT(iter->data)->has_timestamp && 
519           VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
520           (! VIK_TRACKPOINT(iter->data)->newsegment) )
521       {
522         speed =  vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) )
523                  / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
524         if ( speed > maxspeed )
525           maxspeed = speed;
526       }
527       iter = iter->next;
528     }
529   }
530   return maxspeed;
531 }
532
533 void vik_track_convert ( VikTrack *tr, VikCoordMode dest_mode )
534 {
535   GList *iter = tr->trackpoints;
536   while (iter)
537   {
538     vik_coord_convert ( &(VIK_TRACKPOINT(iter->data)->coord), dest_mode );
539     iter = iter->next;
540   }
541 }
542
543 /* I understood this when I wrote it ... maybe ... Basically it eats up the
544  * proper amounts of length on the track and averages elevation over that. */
545 gdouble *vik_track_make_elevation_map ( const VikTrack *tr, guint16 num_chunks )
546 {
547   gdouble *pts;
548   gdouble total_length, chunk_length, current_dist, current_area_under_curve, current_seg_length, dist_along_seg = 0.0;
549   gdouble altitude1, altitude2;
550   guint16 current_chunk;
551   gboolean ignore_it = FALSE;
552
553   GList *iter = tr->trackpoints;
554
555   if (!iter || !iter->next) /* zero- or one-point track */
556           return NULL;
557
558   { /* test if there's anything worth calculating */
559     gboolean okay = FALSE;
560     while ( iter )
561     {
562       // Sometimes a GPS device (or indeed any random file) can have stupid numbers for elevations
563       // Since when is 9.9999e+24 a valid elevation!!
564       // 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)
565       // Some protection against trying to work with crazily massive numbers (otherwise get SIGFPE, Arithmetic exception)
566       if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE &&
567            VIK_TRACKPOINT(iter->data)->altitude < 1E9 ) {
568         okay = TRUE; break;
569       }
570       iter = iter->next;
571     }
572     if ( ! okay )
573       return NULL;
574   }
575
576   iter = tr->trackpoints;
577
578   g_assert ( num_chunks < 16000 );
579
580   pts = g_malloc ( sizeof(gdouble) * num_chunks );
581
582   total_length = vik_track_get_length_including_gaps ( tr );
583   chunk_length = total_length / num_chunks;
584
585   /* Zero chunk_length (eg, track of 2 tp with the same loc) will cause crash */
586   if (chunk_length <= 0) {
587     g_free(pts);
588     return NULL;
589   }
590
591   current_dist = 0.0;
592   current_area_under_curve = 0;
593   current_chunk = 0;
594   current_seg_length = 0;
595
596   current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
597       &(VIK_TRACKPOINT(iter->next->data)->coord) );
598   altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
599   altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
600   dist_along_seg = 0;
601
602   while ( current_chunk < num_chunks ) {
603
604     /* go along current seg */
605     if ( current_seg_length && (current_seg_length - dist_along_seg) > chunk_length ) {
606       dist_along_seg += chunk_length;
607
608       /*        /
609        *   pt2 *
610        *      /x       altitude = alt_at_pt_1 + alt_at_pt_2 / 2 = altitude1 + slope * dist_value_of_pt_inbetween_pt1_and_pt2
611        *     /xx   avg altitude = area under curve / chunk len
612        *pt1 *xxx   avg altitude = altitude1 + (altitude2-altitude1)/(current_seg_length)*(dist_along_seg + (chunk_len/2))
613        *   / xxx
614        *  /  xxx
615        **/
616
617       if ( ignore_it )
618         // Seemly can't determine average for this section - so use last known good value (much better than just sticking in zero)
619         pts[current_chunk] = altitude1;
620       else
621         pts[current_chunk] = altitude1 + (altitude2-altitude1)*((dist_along_seg - (chunk_length/2))/current_seg_length);
622
623       current_chunk++;
624     } else {
625       /* finish current seg */
626       if ( current_seg_length ) {
627         gdouble altitude_at_dist_along_seg = altitude1 + (altitude2-altitude1)/(current_seg_length)*dist_along_seg;
628         current_dist = current_seg_length - dist_along_seg;
629         current_area_under_curve = current_dist*(altitude_at_dist_along_seg + altitude2)*0.5;
630       } else { current_dist = current_area_under_curve = 0; } /* should only happen if first current_seg_length == 0 */
631
632       /* get intervening segs */
633       iter = iter->next;
634       while ( iter && iter->next ) {
635         current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
636             &(VIK_TRACKPOINT(iter->next->data)->coord) );
637         altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
638         altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
639         ignore_it = VIK_TRACKPOINT(iter->next->data)->newsegment;
640
641         if ( chunk_length - current_dist >= current_seg_length ) {
642           current_dist += current_seg_length;
643           current_area_under_curve += current_seg_length * (altitude1+altitude2) * 0.5;
644           iter = iter->next;
645         } else {
646           break;
647         }
648       }
649
650       /* final seg */
651       dist_along_seg = chunk_length - current_dist;
652       if ( ignore_it || ( iter && !iter->next ) ) {
653         pts[current_chunk] = current_area_under_curve / current_dist;
654         if (!iter->next) {
655           int i;
656           for (i = current_chunk + 1; i < num_chunks; i++)
657             pts[i] = pts[current_chunk];
658           break;
659         }
660       } 
661       else {
662         current_area_under_curve += dist_along_seg * (altitude1 + (altitude2 - altitude1)*dist_along_seg/current_seg_length);
663         pts[current_chunk] = current_area_under_curve / chunk_length;
664       }
665
666       current_dist = 0;
667       current_chunk++;
668     }
669   }
670
671   return pts;
672 }
673
674
675 void vik_track_get_total_elevation_gain(const VikTrack *tr, gdouble *up, gdouble *down)
676 {
677   gdouble diff;
678   *up = *down = 0;
679   if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE )
680   {
681     GList *iter = tr->trackpoints->next;
682     while (iter)
683     {
684       diff = VIK_TRACKPOINT(iter->data)->altitude - VIK_TRACKPOINT(iter->prev->data)->altitude;
685       if ( diff > 0 )
686         *up += diff;
687       else
688         *down -= diff;
689       iter = iter->next;
690     }
691   } else
692     *up = *down = VIK_DEFAULT_ALTITUDE;
693 }
694
695 gdouble *vik_track_make_gradient_map ( const VikTrack *tr, guint16 num_chunks )
696 {
697   gdouble *pts;
698   gdouble *altitudes;
699   gdouble total_length, chunk_length, current_gradient;
700   gdouble altitude1, altitude2;
701   guint16 current_chunk;
702
703   g_assert ( num_chunks < 16000 );
704
705   total_length = vik_track_get_length_including_gaps ( tr );
706   chunk_length = total_length / num_chunks;
707
708   /* Zero chunk_length (eg, track of 2 tp with the same loc) will cause crash */
709   if (chunk_length <= 0) {
710     return NULL;
711   }
712
713   altitudes = vik_track_make_elevation_map (tr, num_chunks);
714   if (altitudes == NULL) {
715     return NULL;
716   }
717
718   current_gradient = 0.0;
719   pts = g_malloc ( sizeof(gdouble) * num_chunks );
720   for (current_chunk = 0; current_chunk < (num_chunks - 1); current_chunk++) {
721     altitude1 = altitudes[current_chunk];
722     altitude2 = altitudes[current_chunk + 1];
723     current_gradient = 100.0 * (altitude2 - altitude1) / chunk_length;
724
725     pts[current_chunk] = current_gradient;
726   }
727
728   pts[current_chunk] = current_gradient;
729
730   return pts;
731 }
732
733 /* by Alex Foobarian */
734 gdouble *vik_track_make_speed_map ( const VikTrack *tr, guint16 num_chunks )
735 {
736   gdouble *v, *s, *t;
737   gdouble duration, chunk_dur;
738   time_t t1, t2;
739   int i, pt_count, numpts, index;
740   GList *iter;
741
742   if ( ! tr->trackpoints )
743     return NULL;
744
745   g_assert ( num_chunks < 16000 );
746
747   t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
748   t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
749   duration = t2 - t1;
750
751   if ( !t1 || !t2 || !duration )
752     return NULL;
753
754   if (duration < 0) {
755     g_warning("negative duration: unsorted trackpoint timestamps?");
756     return NULL;
757   }
758   pt_count = vik_track_get_tp_count(tr);
759
760   v = g_malloc ( sizeof(gdouble) * num_chunks );
761   chunk_dur = duration / num_chunks;
762
763   s = g_malloc(sizeof(double) * pt_count);
764   t = g_malloc(sizeof(double) * pt_count);
765
766   iter = tr->trackpoints->next;
767   numpts = 0;
768   s[0] = 0;
769   t[0] = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
770   numpts++;
771   while (iter) {
772     s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
773     t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
774     numpts++;
775     iter = iter->next;
776   }
777
778   /* In the following computation, we iterate through periods of time of duration chunk_dur.
779    * The first period begins at the beginning of the track.  The last period ends at the end of the track.
780    */
781   index = 0; /* index of the current trackpoint. */
782   for (i = 0; i < num_chunks; i++) {
783     /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
784      * find the first trackpoint outside the current interval, averaging the speeds between intermediate trackpoints.
785      */
786     if (t[0] + i*chunk_dur >= t[index]) {
787       gdouble acc_t = 0, acc_s = 0;
788       while (t[0] + i*chunk_dur >= t[index]) {
789         acc_s += (s[index+1]-s[index]);
790         acc_t += (t[index+1]-t[index]);
791         index++;
792       }
793       v[i] = acc_s/acc_t;
794     } 
795     else if (i) {
796       v[i] = v[i-1];
797     }
798     else {
799       v[i] = 0;
800     }
801   }
802   g_free(s);
803   g_free(t);
804   return v;
805 }
806
807 /**
808  * Make a distance/time map, heavily based on the vik_track_make_speed_map method
809  */
810 gdouble *vik_track_make_distance_map ( const VikTrack *tr, guint16 num_chunks )
811 {
812   gdouble *v, *s, *t;
813   gdouble duration, chunk_dur;
814   time_t t1, t2;
815   int i, pt_count, numpts, index;
816   GList *iter;
817
818   if ( ! tr->trackpoints )
819     return NULL;
820
821   t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
822   t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
823   duration = t2 - t1;
824
825   if ( !t1 || !t2 || !duration )
826     return NULL;
827
828   if (duration < 0) {
829     g_warning("negative duration: unsorted trackpoint timestamps?");
830     return NULL;
831   }
832   pt_count = vik_track_get_tp_count(tr);
833
834   v = g_malloc ( sizeof(gdouble) * num_chunks );
835   chunk_dur = duration / num_chunks;
836
837   s = g_malloc(sizeof(double) * pt_count);
838   t = g_malloc(sizeof(double) * pt_count);
839
840   iter = tr->trackpoints->next;
841   numpts = 0;
842   s[0] = 0;
843   t[0] = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
844   numpts++;
845   while (iter) {
846     s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
847     t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
848     numpts++;
849     iter = iter->next;
850   }
851
852   /* In the following computation, we iterate through periods of time of duration chunk_dur.
853    * The first period begins at the beginning of the track.  The last period ends at the end of the track.
854    */
855   index = 0; /* index of the current trackpoint. */
856   for (i = 0; i < num_chunks; i++) {
857     /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
858      * find the first trackpoint outside the current interval, averaging the distance between intermediate trackpoints.
859      */
860     if (t[0] + i*chunk_dur >= t[index]) {
861       gdouble acc_s = 0; // No need for acc_t
862       while (t[0] + i*chunk_dur >= t[index]) {
863         acc_s += (s[index+1]-s[index]);
864         index++;
865       }
866       // The only bit that's really different from the speed map - just keep an accululative record distance
867       v[i] = i ? v[i-1]+acc_s : acc_s;
868     }
869     else if (i) {
870       v[i] = v[i-1];
871     }
872     else {
873       v[i] = 0;
874     }
875   }
876   g_free(s);
877   g_free(t);
878   return v;
879 }
880
881 /**
882  * This uses the 'time' based method to make the graph, (which is a simpler compared to the elevation/distance)
883  * This results in a slightly blocky graph when it does not have many trackpoints: <60
884  * NB Somehow the elevation/distance applies some kind of smoothing algorithm,
885  *   but I don't think any one understands it any more (I certainly don't ATM)
886  */
887 gdouble *vik_track_make_elevation_time_map ( const VikTrack *tr, guint16 num_chunks )
888 {
889   time_t t1, t2;
890   gdouble duration, chunk_dur;
891   GList *iter = tr->trackpoints;
892
893   if (!iter || !iter->next) /* zero- or one-point track */
894     return NULL;
895
896   /* test if there's anything worth calculating */
897   gboolean okay = FALSE;
898   while ( iter ) {
899     if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
900       okay = TRUE;
901       break;
902     }
903     iter = iter->next;
904   }
905   if ( ! okay )
906     return NULL;
907
908   t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
909   t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
910   duration = t2 - t1;
911
912   if ( !t1 || !t2 || !duration )
913     return NULL;
914
915   if (duration < 0) {
916     g_warning("negative duration: unsorted trackpoint timestamps?");
917     return NULL;
918   }
919   gint pt_count = vik_track_get_tp_count(tr);
920
921   // Reset iterator back to the beginning
922   iter = tr->trackpoints;
923
924   gdouble *pts = g_malloc ( sizeof(gdouble) * num_chunks ); // The return altitude values
925   gdouble *s = g_malloc(sizeof(double) * pt_count); // calculation altitudes
926   gdouble *t = g_malloc(sizeof(double) * pt_count); // calculation times
927
928   chunk_dur = duration / num_chunks;
929
930   s[0] = VIK_TRACKPOINT(iter->data)->altitude;
931   t[0] = VIK_TRACKPOINT(iter->data)->timestamp;
932   iter = tr->trackpoints->next;
933   gint numpts = 1;
934   while (iter) {
935     s[numpts] = VIK_TRACKPOINT(iter->data)->altitude;
936     t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
937     numpts++;
938     iter = iter->next;
939   }
940
941  /* In the following computation, we iterate through periods of time of duration chunk_dur.
942    * The first period begins at the beginning of the track.  The last period ends at the end of the track.
943    */
944   gint index = 0; /* index of the current trackpoint. */
945   gint i;
946   for (i = 0; i < num_chunks; i++) {
947     /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
948      * find the first trackpoint outside the current interval, averaging the heights between intermediate trackpoints.
949      */
950     if (t[0] + i*chunk_dur >= t[index]) {
951       gdouble acc_s = s[index]; // initialise to first point
952       while (t[0] + i*chunk_dur >= t[index]) {
953         acc_s += (s[index+1]-s[index]);
954         index++;
955       }
956       pts[i] = acc_s;
957     }
958     else if (i) {
959       pts[i] = pts[i-1];
960     }
961     else {
962       pts[i] = 0;
963     }
964   }
965   g_free(s);
966   g_free(t);
967
968   return pts;
969 }
970
971 /**
972  * Make a speed/distance map
973  */
974 gdouble *vik_track_make_speed_dist_map ( const VikTrack *tr, guint16 num_chunks )
975 {
976   gdouble *v, *s, *t;
977   time_t t1, t2;
978   gint i, pt_count, numpts, index;
979   GList *iter;
980   gdouble duration, total_length, chunk_length;
981
982   if ( ! tr->trackpoints )
983     return NULL;
984
985   t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
986   t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
987   duration = t2 - t1;
988
989   if ( !t1 || !t2 || !duration )
990     return NULL;
991
992   if (duration < 0) {
993     g_warning("negative duration: unsorted trackpoint timestamps?");
994     return NULL;
995   }
996
997   total_length = vik_track_get_length_including_gaps ( tr );
998   chunk_length = total_length / num_chunks;
999   pt_count = vik_track_get_tp_count(tr);
1000
1001   if (chunk_length <= 0) {
1002     return NULL;
1003   }
1004
1005   v = g_malloc ( sizeof(gdouble) * num_chunks );
1006   s = g_malloc ( sizeof(double) * pt_count );
1007   t = g_malloc ( sizeof(double) * pt_count );
1008
1009   // No special handling of segments ATM...
1010   iter = tr->trackpoints->next;
1011   numpts = 0;
1012   s[0] = 0;
1013   t[0] = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1014   numpts++;
1015   while (iter) {
1016     s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
1017     t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
1018     numpts++;
1019     iter = iter->next;
1020   }
1021
1022   // Iterate through a portion of the track to get an average speed for that part
1023   // This will essentially interpolate between segments, which I think is right given the usage of 'get_length_including_gaps'
1024   index = 0; /* index of the current trackpoint. */
1025   for (i = 0; i < num_chunks; i++) {
1026     // Similar to the make_speed_map, but instead of using a time chunk, use a distance chunk
1027     if (s[0] + i*chunk_length >= s[index]) {
1028       gdouble acc_t = 0, acc_s = 0;
1029       while (s[0] + i*chunk_length >= s[index]) {
1030         acc_s += (s[index+1]-s[index]);
1031         acc_t += (t[index+1]-t[index]);
1032         index++;
1033       }
1034       v[i] = acc_s/acc_t;
1035     }
1036     else if (i) {
1037       v[i] = v[i-1];
1038     }
1039     else {
1040       v[i] = 0;
1041     }
1042   }
1043   g_free(s);
1044   g_free(t);
1045   return v;
1046 }
1047
1048 /* by Alex Foobarian */
1049 VikTrackpoint *vik_track_get_closest_tp_by_percentage_dist ( VikTrack *tr, gdouble reldist, gdouble *meters_from_start )
1050 {
1051   gdouble dist = vik_track_get_length_including_gaps(tr) * reldist;
1052   gdouble current_dist = 0.0;
1053   gdouble current_inc = 0.0;
1054   if ( tr->trackpoints )
1055   {
1056     GList *iter = tr->trackpoints->next;
1057     GList *last_iter = NULL;
1058     gdouble last_dist = 0.0;
1059     while (iter)
1060     {
1061       current_inc = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
1062                                      &(VIK_TRACKPOINT(iter->prev->data)->coord) );
1063       last_dist = current_dist;
1064       current_dist += current_inc;
1065       if ( current_dist >= dist )
1066         break;
1067       last_iter = iter;
1068       iter = iter->next;
1069     }
1070     if (!iter) { /* passing the end the track */
1071       if (last_iter) {
1072         if (meters_from_start)
1073           *meters_from_start = last_dist;
1074         return(VIK_TRACKPOINT(last_iter->data));
1075       }
1076       else
1077         return NULL;
1078     }
1079     /* we've gone past the dist already, was prev trackpoint closer? */
1080     /* should do a vik_coord_average_weighted() thingy. */
1081     if ( iter->prev && abs(current_dist-current_inc-dist) < abs(current_dist-dist) ) {
1082       if (meters_from_start)
1083         *meters_from_start = last_dist;
1084       iter = iter->prev;
1085     }
1086     else
1087       if (meters_from_start)
1088         *meters_from_start = current_dist;
1089
1090     return VIK_TRACKPOINT(iter->data);
1091
1092   }
1093   return NULL;
1094 }
1095
1096 VikTrackpoint *vik_track_get_closest_tp_by_percentage_time ( VikTrack *tr, gdouble reltime, time_t *seconds_from_start )
1097 {
1098   time_t t_pos, t_start, t_end, t_total;
1099   t_start = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1100   t_end = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1101   t_total = t_end - t_start;
1102
1103   t_pos = t_start + t_total * reltime;
1104
1105   if ( !tr->trackpoints )
1106     return NULL;
1107
1108   GList *iter = tr->trackpoints;
1109
1110   while (iter) {
1111     if (VIK_TRACKPOINT(iter->data)->timestamp == t_pos)
1112       break;
1113     if (VIK_TRACKPOINT(iter->data)->timestamp > t_pos) {
1114       if (iter->prev == NULL)  /* first trackpoint */
1115         break;
1116       time_t t_before = t_pos - VIK_TRACKPOINT(iter->prev)->timestamp;
1117       time_t t_after = VIK_TRACKPOINT(iter->data)->timestamp - t_pos;
1118       if (t_before <= t_after)
1119         iter = iter->prev;
1120       break;
1121     }
1122     else if ((iter->next == NULL) && (t_pos < (VIK_TRACKPOINT(iter->data)->timestamp + 3))) /* last trackpoint: accommodate for round-off */
1123       break;
1124     iter = iter->next;
1125   }
1126
1127   if (!iter)
1128     return NULL;
1129   if (seconds_from_start)
1130     *seconds_from_start = VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1131   return VIK_TRACKPOINT(iter->data);
1132 }
1133
1134 VikTrackpoint* vik_track_get_tp_by_max_speed ( const VikTrack *tr )
1135 {
1136   gdouble maxspeed = 0.0, speed = 0.0;
1137
1138   if ( !tr->trackpoints )
1139     return NULL;
1140
1141   GList *iter = tr->trackpoints;
1142   VikTrackpoint *max_speed_tp = NULL;
1143
1144   while (iter) {
1145     if (iter->prev) {
1146       if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
1147            VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
1148            (! VIK_TRACKPOINT(iter->data)->newsegment) ) {
1149         speed =  vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) )
1150           / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
1151         if ( speed > maxspeed ) {
1152           maxspeed = speed;
1153           max_speed_tp = VIK_TRACKPOINT(iter->data);
1154         }
1155       }
1156     }
1157     iter = iter->next;
1158   }
1159   
1160   if (!max_speed_tp)
1161     return NULL;
1162
1163   return max_speed_tp;
1164 }
1165
1166 VikTrackpoint* vik_track_get_tp_by_max_alt ( const VikTrack *tr )
1167 {
1168   gdouble maxalt = -5000.0;
1169   if ( !tr->trackpoints )
1170     return NULL;
1171
1172   GList *iter = tr->trackpoints;
1173   VikTrackpoint *max_alt_tp = NULL;
1174
1175   while (iter) {
1176     if ( VIK_TRACKPOINT(iter->data)->altitude > maxalt ) {
1177       maxalt = VIK_TRACKPOINT(iter->data)->altitude;
1178       max_alt_tp = VIK_TRACKPOINT(iter->data);
1179     }
1180     iter = iter->next;
1181   }
1182
1183   if (!max_alt_tp)
1184     return NULL;
1185
1186   return max_alt_tp;
1187 }
1188
1189 VikTrackpoint* vik_track_get_tp_by_min_alt ( const VikTrack *tr )
1190 {
1191   gdouble minalt = 25000.0;
1192   if ( !tr->trackpoints )
1193     return NULL;
1194
1195   GList *iter = tr->trackpoints;
1196   VikTrackpoint *min_alt_tp = NULL;
1197
1198   while (iter) {
1199     if ( VIK_TRACKPOINT(iter->data)->altitude < minalt ) {
1200       minalt = VIK_TRACKPOINT(iter->data)->altitude;
1201       min_alt_tp = VIK_TRACKPOINT(iter->data);
1202     }
1203     iter = iter->next;
1204   }
1205
1206   if (!min_alt_tp)
1207     return NULL;
1208
1209   return min_alt_tp;
1210 }
1211
1212 gboolean vik_track_get_minmax_alt ( const VikTrack *tr, gdouble *min_alt, gdouble *max_alt )
1213 {
1214   *min_alt = 25000;
1215   *max_alt = -5000;
1216   if ( tr && tr->trackpoints && tr->trackpoints->data && (VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE) ) {
1217     GList *iter = tr->trackpoints->next;
1218     gdouble tmp_alt;
1219     while (iter)
1220     {
1221       tmp_alt = VIK_TRACKPOINT(iter->data)->altitude;
1222       if ( tmp_alt > *max_alt )
1223         *max_alt = tmp_alt;
1224       if ( tmp_alt < *min_alt )
1225         *min_alt = tmp_alt;
1226       iter = iter->next;
1227     }
1228     return TRUE;
1229   }
1230   return FALSE;
1231 }
1232
1233 void vik_track_marshall ( VikTrack *tr, guint8 **data, guint *datalen)
1234 {
1235   GList *tps;
1236   GByteArray *b = g_byte_array_new();
1237   guint len;
1238   guint intp, ntp;
1239
1240   g_byte_array_append(b, (guint8 *)tr, sizeof(*tr));
1241
1242   /* we'll fill out number of trackpoints later */
1243   intp = b->len;
1244   g_byte_array_append(b, (guint8 *)&len, sizeof(len));
1245
1246   tps = tr->trackpoints;
1247   ntp = 0;
1248   while (tps) {
1249     g_byte_array_append(b, (guint8 *)tps->data, sizeof(VikTrackpoint));
1250     tps = tps->next;
1251     ntp++;
1252   }
1253   *(guint *)(b->data + intp) = ntp;
1254
1255   // This allocates space for variant sized strings
1256   //  and copies that amount of data from the track to byte array
1257 #define vtm_append(s) \
1258   len = (s) ? strlen(s)+1 : 0; \
1259   g_byte_array_append(b, (guint8 *)&len, sizeof(len)); \
1260   if (s) g_byte_array_append(b, (guint8 *)s, len);
1261
1262   vtm_append(tr->name);
1263   vtm_append(tr->comment);
1264   vtm_append(tr->description);
1265
1266   *data = b->data;
1267   *datalen = b->len;
1268   g_byte_array_free(b, FALSE);
1269 }
1270
1271 /*
1272  * Take a byte array and convert it into a Track
1273  */
1274 VikTrack *vik_track_unmarshall (guint8 *data, guint datalen)
1275 {
1276   guint len;
1277   VikTrack *new_tr = vik_track_new();
1278   VikTrackpoint *new_tp;
1279   guint ntp;
1280   gint i;
1281
1282   /* basic properties: */
1283   new_tr->visible = ((VikTrack *)data)->visible;
1284   new_tr->is_route = ((VikTrack *)data)->is_route;
1285   new_tr->has_color = ((VikTrack *)data)->has_color;
1286   new_tr->color = ((VikTrack *)data)->color;
1287   new_tr->bbox = ((VikTrack *)data)->bbox;
1288
1289   data += sizeof(*new_tr);
1290
1291   ntp = *(guint *)data;
1292   data += sizeof(ntp);
1293
1294   for (i=0; i<ntp; i++) {
1295     new_tp = vik_trackpoint_new();
1296     memcpy(new_tp, data, sizeof(*new_tp));
1297     data += sizeof(*new_tp);
1298     new_tr->trackpoints = g_list_append(new_tr->trackpoints, new_tp);
1299   }
1300
1301 #define vtu_get(s) \
1302   len = *(guint *)data; \
1303   data += sizeof(len); \
1304   if (len) { \
1305     (s) = g_strdup((gchar *)data); \
1306   } else { \
1307     (s) = NULL; \
1308   } \
1309   data += len;
1310
1311   vtu_get(new_tr->name);
1312   vtu_get(new_tr->comment);
1313   vtu_get(new_tr->description);
1314
1315   return new_tr;
1316 }
1317
1318 /**
1319  * (Re)Calculate the bounds of the given track,
1320  *  updating the track's bounds data.
1321  * This should be called whenever a track's trackpoints are changed
1322  */
1323 void vik_track_calculate_bounds ( VikTrack *trk )
1324 {
1325   GList *tp_iter;
1326   tp_iter = trk->trackpoints;
1327   
1328   struct LatLon topleft, bottomright, ll;
1329   
1330   // Set bounds to first point
1331   if ( tp_iter ) {
1332     vik_coord_to_latlon ( &(VIK_TRACKPOINT(tp_iter->data)->coord), &topleft );
1333     vik_coord_to_latlon ( &(VIK_TRACKPOINT(tp_iter->data)->coord), &bottomright );
1334   }
1335   while ( tp_iter ) {
1336
1337     // See if this trackpoint increases the track bounds.
1338    
1339     vik_coord_to_latlon ( &(VIK_TRACKPOINT(tp_iter->data)->coord), &ll );
1340   
1341     if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
1342     if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
1343     if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
1344     if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
1345     
1346     tp_iter = tp_iter->next;
1347   }
1348  
1349   g_debug ( g_strdup_printf("Bounds of track: '%s' is: %f,%f to: %f,%f", trk->name, topleft.lat, topleft.lon, bottomright.lat, bottomright.lon ) );
1350
1351   trk->bbox.north = topleft.lat;
1352   trk->bbox.east = bottomright.lon;
1353   trk->bbox.south = bottomright.lat;
1354   trk->bbox.west = topleft.lon;
1355 }
1356
1357 /**
1358  *
1359  */
1360 void vik_track_apply_dem_data ( VikTrack *tr )
1361 {
1362   GList *tp_iter;
1363   gint16 elev;
1364   tp_iter = tr->trackpoints;
1365   while ( tp_iter ) {
1366     /* TODO: of the 4 possible choices we have for choosing an elevation
1367      * (trackpoint in between samples), choose the one with the least elevation change
1368      * as the last */
1369     elev = a_dems_get_elev_by_coord ( &(VIK_TRACKPOINT(tp_iter->data)->coord), VIK_DEM_INTERPOL_BEST );
1370     if ( elev != VIK_DEM_INVALID_ELEVATION )
1371       VIK_TRACKPOINT(tp_iter->data)->altitude = elev;
1372     tp_iter = tp_iter->next;
1373   }
1374 }
1375
1376 /**
1377  * vik_track_apply_dem_data_last_trackpoint:
1378  * Apply DEM data (if available) - to only the last trackpoint
1379  */
1380 void vik_track_apply_dem_data_last_trackpoint ( VikTrack *tr )
1381 {
1382   gint16 elev;
1383   if ( tr->trackpoints ) {
1384     /* As in vik_track_apply_dem_data above - use 'best' interpolation method */
1385     elev = a_dems_get_elev_by_coord ( &(VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->coord), VIK_DEM_INTERPOL_BEST );
1386     if ( elev != VIK_DEM_INVALID_ELEVATION )
1387       VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->altitude = elev;
1388   }
1389 }
1390
1391 /**
1392  * vik_track_steal_and_append_trackpoints:
1393  * 
1394  * appends t2 to t1, leaving t2 with no trackpoints
1395  */
1396 void vik_track_steal_and_append_trackpoints ( VikTrack *t1, VikTrack *t2 )
1397 {
1398   if ( t1->trackpoints ) {
1399     t1->trackpoints = g_list_concat ( t1->trackpoints, t2->trackpoints );
1400   } else
1401     t1->trackpoints = t2->trackpoints;
1402   t2->trackpoints = NULL;
1403
1404   // Trackpoints updated - so update the bounds
1405   vik_track_calculate_bounds ( t1 );
1406 }
1407
1408 /**
1409  * vik_track_cut_back_to_double_point:
1410  * 
1411  * starting at the end, looks backwards for the last "double point", a duplicate trackpoint.
1412  * If there is no double point, deletes all the trackpoints.
1413  * 
1414  * Returns: the new end of the track (or the start if there are no double points)
1415  */
1416 VikCoord *vik_track_cut_back_to_double_point ( VikTrack *tr )
1417 {
1418   GList *iter = tr->trackpoints;
1419   VikCoord *rv;
1420
1421   if ( !iter )
1422     return NULL;
1423   while ( iter->next )
1424     iter = iter->next;
1425
1426
1427   while ( iter->prev ) {
1428     if ( vik_coord_equals((VikCoord *)iter->data, (VikCoord *)iter->prev->data) ) {
1429       GList *prev = iter->prev;
1430
1431       rv = g_malloc(sizeof(VikCoord));
1432       *rv = *((VikCoord *) iter->data);
1433
1434       /* truncate trackpoint list */
1435       iter->prev = NULL; /* pretend it's the end */
1436       g_list_foreach ( iter, (GFunc) g_free, NULL );
1437       g_list_free( iter );
1438
1439       prev->next = NULL;
1440
1441       return rv;
1442     }
1443     iter = iter->prev;
1444   }
1445
1446   /* no double point found! */
1447   rv = g_malloc(sizeof(VikCoord));
1448   *rv = *((VikCoord *) tr->trackpoints->data);
1449   g_list_foreach ( tr->trackpoints, (GFunc) g_free, NULL );
1450   g_list_free( tr->trackpoints );
1451   tr->trackpoints = NULL;
1452   return rv;
1453 }
1454