]> git.street.me.uk Git - andy/viking.git/blob - src/viktrack.c
Remove dependencies to gob2
[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  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include <glib.h>
23 #include <time.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <math.h>
27 #include "coords.h"
28 #include "vikcoord.h"
29 #include "viktrack.h"
30 #include "globals.h"
31 #include "dems.h"
32
33 VikTrack *vik_track_new()
34 {
35   VikTrack *tr = g_malloc0 ( sizeof ( VikTrack ) );
36   tr->ref_count = 1;
37   return tr;
38 }
39
40 void vik_track_set_comment_no_copy(VikTrack *tr, gchar *comment)
41 {
42   if ( tr->comment )
43     g_free ( tr->comment );
44   tr->comment = comment;
45 }
46
47
48 void vik_track_set_comment(VikTrack *tr, const gchar *comment)
49 {
50   if ( tr->comment )
51     g_free ( tr->comment );
52
53   if ( comment && comment[0] != '\0' )
54     tr->comment = g_strdup(comment);
55   else
56     tr->comment = NULL;
57 }
58
59 void vik_track_ref(VikTrack *tr)
60 {
61   tr->ref_count++;
62 }
63
64 void vik_track_set_property_dialog(VikTrack *tr, GtkWidget *dialog)
65 {
66   /* Warning: does not check for existing dialog */
67   tr->property_dialog = dialog;
68 }
69
70 void vik_track_clear_property_dialog(VikTrack *tr)
71 {
72   tr->property_dialog = NULL;
73 }
74
75 void vik_track_free(VikTrack *tr)
76 {
77   if ( tr->ref_count-- > 1 )
78     return;
79
80   if ( tr->comment )
81     g_free ( tr->comment );
82   g_list_foreach ( tr->trackpoints, (GFunc) g_free, NULL );
83   g_list_free( tr->trackpoints );
84   if (tr->property_dialog)
85     gtk_widget_destroy ( GTK_WIDGET(tr->property_dialog) );
86   g_free ( tr );
87 }
88
89 VikTrack *vik_track_copy ( const VikTrack *tr )
90 {
91   VikTrack *new_tr = vik_track_new();
92   VikTrackpoint *new_tp;
93   GList *tp_iter = tr->trackpoints;
94   new_tr->visible = tr->visible;
95   new_tr->trackpoints = NULL;
96   while ( tp_iter )
97   {
98     new_tp = g_malloc ( sizeof ( VikTrackpoint ) );
99     *new_tp = *((VikTrackpoint *)(tp_iter->data));
100     new_tr->trackpoints = g_list_append ( new_tr->trackpoints, new_tp );
101     tp_iter = tp_iter->next;
102   }
103   vik_track_set_comment(new_tr,tr->comment);
104   return new_tr;
105 }
106
107 VikTrackpoint *vik_trackpoint_new()
108 {
109   VikTrackpoint *tp = g_malloc0(sizeof(VikTrackpoint));
110   tp->extended = FALSE;
111   tp->speed = NAN;
112   tp->course = NAN;
113   return tp;
114 }
115
116 void vik_trackpoint_free(VikTrackpoint *tp)
117 {
118   g_free(tp);
119 }
120
121 VikTrackpoint *vik_trackpoint_copy(VikTrackpoint *tp)
122 {
123   VikTrackpoint *rv = vik_trackpoint_new();
124   *rv = *tp;
125   return rv;
126 }
127
128 gdouble vik_track_get_length(const VikTrack *tr)
129 {
130   gdouble len = 0.0;
131   if ( tr->trackpoints )
132   {
133     GList *iter = tr->trackpoints->next;
134     while (iter)
135     {
136       if ( ! VIK_TRACKPOINT(iter->data)->newsegment )
137         len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
138                                 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
139       iter = iter->next;
140     }
141   }
142   return len;
143 }
144
145 gdouble vik_track_get_length_including_gaps(const VikTrack *tr)
146 {
147   gdouble len = 0.0;
148   if ( tr->trackpoints )
149   {
150     GList *iter = tr->trackpoints->next;
151     while (iter)
152     {
153       len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
154                               &(VIK_TRACKPOINT(iter->prev->data)->coord) );
155       iter = iter->next;
156     }
157   }
158   return len;
159 }
160
161 gulong vik_track_get_tp_count(const VikTrack *tr)
162 {
163   gulong num = 0;
164   GList *iter = tr->trackpoints;
165   while ( iter )
166   {
167     num++;
168     iter = iter->next;
169   }
170   return num;
171 }
172
173 gulong vik_track_get_dup_point_count ( const VikTrack *tr )
174 {
175   gulong num = 0;
176   GList *iter = tr->trackpoints;
177   while ( iter )
178   {
179     if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
180                        &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
181       num++;
182     iter = iter->next;
183   }
184   return num;
185 }
186
187 void vik_track_remove_dup_points ( VikTrack *tr )
188 {
189   GList *iter = tr->trackpoints;
190   while ( iter )
191   {
192     if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
193                        &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
194     {
195       g_free ( iter->next->data );
196       tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next );
197     }
198     else
199       iter = iter->next;
200   }
201 }
202
203 guint vik_track_get_segment_count(const VikTrack *tr)
204 {
205   guint num = 1;
206   GList *iter = tr->trackpoints;
207   if ( !iter )
208     return 0;
209   while ( (iter = iter->next) )
210   {
211     if ( VIK_TRACKPOINT(iter->data)->newsegment )
212       num++;
213   }
214   return num;
215 }
216
217 VikTrack **vik_track_split_into_segments(VikTrack *t, guint *ret_len)
218 {
219   VikTrack **rv;
220   VikTrack *tr;
221   guint i;
222   guint segs = vik_track_get_segment_count(t);
223   GList *iter;
224
225   if ( segs < 2 )
226   {
227     *ret_len = 0;
228     return NULL;
229   }
230
231   rv = g_malloc ( segs * sizeof(VikTrack *) );
232   tr = vik_track_copy ( t );
233   rv[0] = tr;
234   iter = tr->trackpoints;
235
236   i = 1;
237   while ( (iter = iter->next) )
238   {
239     if ( VIK_TRACKPOINT(iter->data)->newsegment )
240     {
241       iter->prev->next = NULL;
242       iter->prev = NULL;
243       rv[i] = vik_track_new();
244       if ( tr->comment )
245         vik_track_set_comment ( rv[i], tr->comment );
246       rv[i]->visible = tr->visible;
247       rv[i]->trackpoints = iter;
248       i++;
249     }
250   }
251   *ret_len = segs;
252   return rv;
253 }
254
255 void vik_track_reverse ( VikTrack *tr )
256 {
257   GList *iter;
258   tr->trackpoints = g_list_reverse(tr->trackpoints);
259
260   /* fix 'newsegment' */
261   iter = g_list_last ( tr->trackpoints );
262   while ( iter )
263   {
264     if ( ! iter->next ) /* last segment, was first, cancel newsegment. */
265       VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
266     if ( ! iter->prev ) /* first segment by convention has newsegment flag. */
267       VIK_TRACKPOINT(iter->data)->newsegment = TRUE;
268     else if ( VIK_TRACKPOINT(iter->data)->newsegment && iter->next )
269     {
270       VIK_TRACKPOINT(iter->next->data)->newsegment = TRUE;
271       VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
272     }
273     iter = iter->prev;
274   }
275 }
276
277 gdouble vik_track_get_average_speed(const VikTrack *tr)
278 {
279   gdouble len = 0.0;
280   guint32 time = 0;
281   if ( tr->trackpoints )
282   {
283     GList *iter = tr->trackpoints->next;
284     while (iter)
285     {
286       if ( VIK_TRACKPOINT(iter->data)->has_timestamp && 
287           VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
288           (! VIK_TRACKPOINT(iter->data)->newsegment) )
289       {
290         len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
291                                 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
292         time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
293       }
294       iter = iter->next;
295     }
296   }
297   return (time == 0) ? 0 : ABS(len/time);
298 }
299
300 gdouble vik_track_get_max_speed(const VikTrack *tr)
301 {
302   gdouble maxspeed = 0.0, speed = 0.0;
303   if ( tr->trackpoints )
304   {
305     GList *iter = tr->trackpoints->next;
306     while (iter)
307     {
308       if ( VIK_TRACKPOINT(iter->data)->has_timestamp && 
309           VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
310           (! VIK_TRACKPOINT(iter->data)->newsegment) )
311       {
312         speed =  vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) )
313                  / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
314         if ( speed > maxspeed )
315           maxspeed = speed;
316       }
317       iter = iter->next;
318     }
319   }
320   return maxspeed;
321 }
322
323 void vik_track_convert ( VikTrack *tr, VikCoordMode dest_mode )
324 {
325   GList *iter = tr->trackpoints;
326   while (iter)
327   {
328     vik_coord_convert ( &(VIK_TRACKPOINT(iter->data)->coord), dest_mode );
329     iter = iter->next;
330   }
331 }
332
333 /* I understood this when I wrote it ... maybe ... Basically it eats up the
334  * proper amounts of length on the track and averages elevation over that. */
335 gdouble *vik_track_make_elevation_map ( const VikTrack *tr, guint16 num_chunks )
336 {
337   gdouble *pts;
338   gdouble total_length, chunk_length, current_dist, current_area_under_curve, current_seg_length, dist_along_seg = 0.0;
339   gdouble altitude1, altitude2;
340   guint16 current_chunk;
341   gboolean ignore_it = FALSE;
342
343   GList *iter = tr->trackpoints;
344
345   if (!iter || !iter->next) /* zero- or one-point track */
346           return NULL;
347
348   { /* test if there's anything worth calculating */
349     gboolean okay = FALSE;
350     while ( iter )
351     {
352       if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
353         okay = TRUE; break;
354       }
355       iter = iter->next;
356     }
357     if ( ! okay )
358       return NULL;
359   }
360
361   iter = tr->trackpoints;
362
363   g_assert ( num_chunks < 16000 );
364
365   pts = g_malloc ( sizeof(gdouble) * num_chunks );
366
367   total_length = vik_track_get_length_including_gaps ( tr );
368   chunk_length = total_length / num_chunks;
369
370   /* Zero chunk_length (eg, track of 2 tp with the same loc) will cause crash */
371   if (chunk_length <= 0)
372     return NULL;
373
374   current_dist = 0.0;
375   current_area_under_curve = 0;
376   current_chunk = 0;
377   current_seg_length = 0;
378
379   current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
380       &(VIK_TRACKPOINT(iter->next->data)->coord) );
381   altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
382   altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
383   dist_along_seg = 0;
384
385   while ( current_chunk < num_chunks ) {
386
387     /* go along current seg */
388     if ( current_seg_length && (current_seg_length - dist_along_seg) > chunk_length ) {
389       dist_along_seg += chunk_length;
390
391       /*        /
392        *   pt2 *
393        *      /x       altitude = alt_at_pt_1 + alt_at_pt_2 / 2 = altitude1 + slope * dist_value_of_pt_inbetween_pt1_and_pt2
394        *     /xx   avg altitude = area under curve / chunk len
395        *pt1 *xxx   avg altitude = altitude1 + (altitude2-altitude1)/(current_seg_length)*(dist_along_seg + (chunk_len/2))
396        *   / xxx
397        *  /  xxx
398        **/
399
400       if ( ignore_it )
401         pts[current_chunk] = VIK_DEFAULT_ALTITUDE;
402       else
403         pts[current_chunk] = altitude1 + (altitude2-altitude1)*((dist_along_seg - (chunk_length/2))/current_seg_length);
404
405       current_chunk++;
406     } else {
407       /* finish current seg */
408       if ( current_seg_length ) {
409         gdouble altitude_at_dist_along_seg = altitude1 + (altitude2-altitude1)/(current_seg_length)*dist_along_seg;
410         current_dist = current_seg_length - dist_along_seg;
411         current_area_under_curve = current_dist*(altitude_at_dist_along_seg + altitude2)*0.5;
412       } else { current_dist = current_area_under_curve = 0; } /* should only happen if first current_seg_length == 0 */
413
414       /* get intervening segs */
415       iter = iter->next;
416       while ( iter && iter->next ) {
417         current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
418             &(VIK_TRACKPOINT(iter->next->data)->coord) );
419         altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
420         altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
421         ignore_it = VIK_TRACKPOINT(iter->next->data)->newsegment;
422
423         if ( chunk_length - current_dist >= current_seg_length ) {
424           current_dist += current_seg_length;
425           current_area_under_curve += current_seg_length * (altitude1+altitude2) * 0.5;
426           iter = iter->next;
427         } else {
428           break;
429         }
430       }
431
432       /* final seg */
433       dist_along_seg = chunk_length - current_dist;
434       if ( ignore_it || !iter->next ) {
435         pts[current_chunk] = current_area_under_curve / current_dist;
436         if (!iter->next) {
437           int i;
438           for (i = current_chunk + 1; i < num_chunks; i++)
439             pts[i] = pts[current_chunk];
440           break;
441         }
442       } 
443       else {
444         current_area_under_curve += dist_along_seg * (altitude1 + (altitude2 - altitude1)*dist_along_seg/current_seg_length);
445         pts[current_chunk] = current_area_under_curve / chunk_length;
446       }
447
448       current_dist = 0;
449       current_chunk++;
450     }
451   }
452
453   return pts;
454 }
455
456
457 void vik_track_get_total_elevation_gain(const VikTrack *tr, gdouble *up, gdouble *down)
458 {
459   gdouble diff;
460   *up = *down = 0;
461   if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE )
462   {
463     GList *iter = tr->trackpoints->next;
464     while (iter)
465     {
466       diff = VIK_TRACKPOINT(iter->data)->altitude - VIK_TRACKPOINT(iter->prev->data)->altitude;
467       if ( diff > 0 )
468         *up += diff;
469       else
470         *down -= diff;
471       iter = iter->next;
472     }
473   } else
474     *up = *down = VIK_DEFAULT_ALTITUDE;
475 }
476
477
478 /* by Alex Foobarian */
479 gdouble *vik_track_make_speed_map ( const VikTrack *tr, guint16 num_chunks )
480 {
481   gdouble *v, *s, *t;
482   gdouble duration, chunk_dur;
483   time_t t1, t2;
484   int i, pt_count, numpts, index;
485   GList *iter;
486
487   if ( ! tr->trackpoints )
488     return NULL;
489
490   g_assert ( num_chunks < 16000 );
491
492   t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
493   t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
494   duration = t2 - t1;
495
496   if ( !t1 || !t2 || !duration )
497     return NULL;
498
499   if (duration < 0) {
500     g_warning("negative duration: unsorted trackpoint timestamps?");
501     return NULL;
502   }
503   pt_count = vik_track_get_tp_count(tr);
504
505   v = g_malloc ( sizeof(gdouble) * num_chunks );
506   chunk_dur = duration / num_chunks;
507
508   s = g_malloc(sizeof(double) * pt_count);
509   t = g_malloc(sizeof(double) * pt_count);
510
511   iter = tr->trackpoints->next;
512   numpts = 0;
513   s[0] = 0;
514   t[0] = VIK_TRACKPOINT(iter->prev->data)->timestamp;
515   numpts++;
516   while (iter) {
517     s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
518     t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
519     numpts++;
520     iter = iter->next;
521   }
522
523   /* In the following computation, we iterate through periods of time of duration chunk_dur.
524    * The first period begins at the beginning of the track.  The last period ends at the end of the track.
525    */
526   index = 0; /* index of the current trackpoint. */
527   for (i = 0; i < num_chunks; i++) {
528     /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
529      * find the first trackpoint outside the current interval, averaging the speeds between intermediate trackpoints.
530      */
531     if (t[0] + i*chunk_dur >= t[index]) {
532       gdouble acc_t = 0, acc_s = 0;
533       numpts = 0;
534       while (t[0] + i*chunk_dur >= t[index]) {
535         acc_s += (s[index+1]-s[index]);
536         acc_t += (t[index+1]-t[index]);
537         index++;
538         numpts++;
539       }
540       v[i] = acc_s/acc_t;
541     } 
542     else if (i) {
543       v[i] = v[i-1];
544     }
545     else {
546       v[i] = 0;
547     }
548   }
549   g_free(s);
550   g_free(t);
551   return v;
552 }
553
554 /* by Alex Foobarian */
555 VikTrackpoint *vik_track_get_closest_tp_by_percentage_dist ( VikTrack *tr, gdouble reldist, gdouble *meters_from_start )
556 {
557   gdouble dist = vik_track_get_length_including_gaps(tr) * reldist;
558   gdouble current_dist = 0.0;
559   gdouble current_inc = 0.0;
560   if ( tr->trackpoints )
561   {
562     GList *iter = tr->trackpoints->next;
563     GList *last_iter = NULL;
564     gdouble last_dist = 0.0;
565     while (iter)
566     {
567       current_inc = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
568                                      &(VIK_TRACKPOINT(iter->prev->data)->coord) );
569       last_dist = current_dist;
570       current_dist += current_inc;
571       if ( current_dist >= dist )
572         break;
573       last_iter = iter;
574       iter = iter->next;
575     }
576     if (!iter) { /* passing the end the track */
577       if (last_iter) {
578         if (meters_from_start)
579           *meters_from_start = last_dist;
580         return(VIK_TRACKPOINT(last_iter->data));
581       }
582       else
583         return NULL;
584     }
585     /* we've gone past the dist already, was prev trackpoint closer? */
586     /* should do a vik_coord_average_weighted() thingy. */
587     if ( iter->prev && abs(current_dist-current_inc-dist) < abs(current_dist-dist) ) {
588       if (meters_from_start)
589         *meters_from_start = last_dist;
590       iter = iter->prev;
591     }
592     else
593       if (meters_from_start)
594         *meters_from_start = current_dist;
595
596     return VIK_TRACKPOINT(iter->data);
597
598   }
599   return NULL;
600 }
601
602 VikTrackpoint *vik_track_get_closest_tp_by_percentage_time ( VikTrack *tr, gdouble reltime, time_t *seconds_from_start )
603 {
604   time_t t_pos, t_start, t_end, t_total;
605   t_start = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
606   t_end = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
607   t_total = t_end - t_start;
608
609   t_pos = t_start + t_total * reltime;
610
611   if ( !tr->trackpoints )
612     return NULL;
613
614   GList *iter = tr->trackpoints;
615
616   while (iter) {
617     if (VIK_TRACKPOINT(iter->data)->timestamp == t_pos)
618       break;
619     if (VIK_TRACKPOINT(iter->data)->timestamp > t_pos) {
620       if (iter->prev == NULL)  /* first trackpoint */
621         break;
622       time_t t_before = t_pos - VIK_TRACKPOINT(iter->prev)->timestamp;
623       time_t t_after = VIK_TRACKPOINT(iter->data)->timestamp - t_pos;
624       if (t_before <= t_after)
625         iter = iter->prev;
626       break;
627     }
628     else if ((iter->next == NULL) && (t_pos < (VIK_TRACKPOINT(iter->data)->timestamp + 3))) /* last trackpoint: accommodate for round-off */
629       break;
630     iter = iter->next;
631   }
632
633   if (!iter)
634     return NULL;
635   if (seconds_from_start)
636     *seconds_from_start = VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
637   return VIK_TRACKPOINT(iter->data);
638 }
639
640 gboolean vik_track_get_minmax_alt ( const VikTrack *tr, gdouble *min_alt, gdouble *max_alt )
641 {
642   *min_alt = 25000;
643   *max_alt = -5000;
644   if ( tr && tr->trackpoints && tr->trackpoints->data && (VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE) ) {
645     GList *iter = tr->trackpoints->next;
646     gdouble tmp_alt;
647     while (iter)
648     {
649       tmp_alt = VIK_TRACKPOINT(iter->data)->altitude;
650       if ( tmp_alt > *max_alt )
651         *max_alt = tmp_alt;
652       if ( tmp_alt < *min_alt )
653         *min_alt = tmp_alt;
654       iter = iter->next;
655     }
656     return TRUE;
657   }
658   return FALSE;
659 }
660
661 void vik_track_marshall ( VikTrack *tr, guint8 **data, guint *datalen)
662 {
663   GList *tps;
664   GByteArray *b = g_byte_array_new();
665   guint len;
666   guint intp, ntp;
667
668   g_byte_array_append(b, (guint8 *)tr, sizeof(*tr));
669
670   /* we'll fill out number of trackpoints later */
671   intp = b->len;
672   g_byte_array_append(b, (guint8 *)&len, sizeof(len));
673
674   tps = tr->trackpoints;
675   ntp = 0;
676   while (tps) {
677     g_byte_array_append(b, (guint8 *)tps->data, sizeof(VikTrackpoint));
678     tps = tps->next;
679     ntp++;
680   }
681   *(guint *)(b->data + intp) = ntp;
682
683   len = (tr->comment) ? strlen(tr->comment)+1 : 0; 
684   g_byte_array_append(b, (guint8 *)&len, sizeof(len)); 
685   if (tr->comment) g_byte_array_append(b, (guint8 *)tr->comment, len);
686
687   *data = b->data;
688   *datalen = b->len;
689   g_byte_array_free(b, FALSE);
690 }
691
692 VikTrack *vik_track_unmarshall (guint8 *data, guint datalen)
693 {
694   guint len;
695   VikTrack *new_tr = vik_track_new();
696   VikTrackpoint *new_tp;
697   guint ntp;
698   gint i;
699
700   /* only the visibility is needed */
701   new_tr->visible = ((VikTrack *)data)->visible;
702   data += sizeof(*new_tr);
703
704   ntp = *(guint *)data;
705   data += sizeof(ntp);
706
707   for (i=0; i<ntp; i++) {
708     new_tp = vik_trackpoint_new();
709     memcpy(new_tp, data, sizeof(*new_tp));
710     data += sizeof(*new_tp);
711     new_tr->trackpoints = g_list_append(new_tr->trackpoints, new_tp);
712   }
713
714   len = *(guint *)data;
715   data += sizeof(len);
716   if (len) {
717     new_tr->comment = g_strdup((gchar *)data);
718   }
719   return new_tr;
720 }
721
722 void vik_track_apply_dem_data ( VikTrack *tr )
723 {
724   GList *tp_iter;
725   gint16 elev;
726   tp_iter = tr->trackpoints;
727   while ( tp_iter ) {
728     /* TODO: of the 4 possible choices we have for choosing an elevation
729      * (trackpoint in between samples), choose the one with the least elevation change
730      * as the last */
731     elev = a_dems_get_elev_by_coord ( &(VIK_TRACKPOINT(tp_iter->data)->coord), VIK_DEM_INTERPOL_BEST );
732     if ( elev != VIK_DEM_INVALID_ELEVATION )
733       VIK_TRACKPOINT(tp_iter->data)->altitude = elev;
734     tp_iter = tp_iter->next;
735   }
736 }
737
738 /* appends t2 to t1, leaving t2 with no trackpoints */
739 void vik_track_steal_and_append_trackpoints ( VikTrack *t1, VikTrack *t2 )
740 {
741   if ( t1->trackpoints ) {
742     GList *tpiter = t1->trackpoints;
743     while ( tpiter->next )
744       tpiter = tpiter->next;
745     tpiter->next = t2->trackpoints;
746     t2->trackpoints->prev = tpiter;
747   } else
748     t1->trackpoints = t2->trackpoints;
749   t2->trackpoints = NULL;
750 }
751
752 /* starting at the end, looks backwards for the last "double point", a duplicate trackpoint.
753  * this is indicative of magic scissors continued use. If there is no double point,
754  * deletes all the trackpoints. Returns the new end of the track (or the start if
755  * there are no double points
756  */
757 VikCoord *vik_track_cut_back_to_double_point ( VikTrack *tr )
758 {
759   GList *iter = tr->trackpoints;
760   VikCoord *rv;
761
762   if ( !iter )
763     return NULL;
764   while ( iter->next )
765     iter = iter->next;
766
767
768   while ( iter->prev ) {
769     if ( vik_coord_equals((VikCoord *)iter->data, (VikCoord *)iter->prev->data) ) {
770       GList *prev = iter->prev;
771
772       rv = g_malloc(sizeof(VikCoord));
773       *rv = *((VikCoord *) iter->data);
774
775       /* truncate trackpoint list */
776       iter->prev = NULL; /* pretend it's the end */
777       g_list_foreach ( iter, (GFunc) g_free, NULL );
778       g_list_free( iter );
779
780       prev->next = NULL;
781
782       return rv;
783     }
784     iter = iter->prev;
785   }
786
787   /* no double point found! */
788   rv = g_malloc(sizeof(VikCoord));
789   *rv = *((VikCoord *) tr->trackpoints->data);
790   g_list_foreach ( tr->trackpoints, (GFunc) g_free, NULL );
791   g_list_free( tr->trackpoints );
792   tr->trackpoints = NULL;
793   return rv;
794 }
795