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