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