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