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