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