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