]> git.street.me.uk Git - andy/viking.git/blob - src/viktrack.c
Elevation map click and bugfixes
[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 "coords.h"
26 #include "vikcoord.h"
27 #include "viktrack.h"
28 #include "globals.h"
29
30 VikTrack *vik_track_new()
31 {
32   VikTrack *tr = g_malloc ( sizeof ( VikTrack ) );
33   tr->trackpoints = NULL;
34   tr->comment = NULL;
35   tr->ref_count = 1;
36   return tr;
37 }
38
39 void vik_track_set_comment_no_copy(VikTrack *tr, gchar *comment)
40 {
41   if ( tr->comment )
42     g_free ( tr->comment );
43   tr->comment = comment;
44 }
45
46
47 void vik_track_set_comment(VikTrack *tr, const gchar *comment)
48 {
49   if ( tr->comment )
50     g_free ( tr->comment );
51
52   if ( comment && comment[0] != '\0' )
53     tr->comment = g_strdup(comment);
54   else
55     tr->comment = NULL;
56 }
57
58 void vik_track_ref(VikTrack *tr)
59 {
60   tr->ref_count++;
61 }
62
63 void vik_track_free(VikTrack *tr)
64 {
65   if ( tr->ref_count-- > 1 )
66     return;
67
68   if ( tr->comment )
69     g_free ( tr->comment );
70   g_list_foreach ( tr->trackpoints, (GFunc) g_free, NULL );
71   g_list_free( tr->trackpoints );
72   g_free ( tr );
73 }
74
75 VikTrack *vik_track_copy ( const VikTrack *tr )
76 {
77   VikTrack *new_tr = vik_track_new();
78   VikTrackpoint *new_tp;
79   GList *tp_iter = tr->trackpoints;
80   new_tr->visible = tr->visible;
81   new_tr->trackpoints = NULL;
82   while ( tp_iter )
83   {
84     new_tp = g_malloc ( sizeof ( VikTrackpoint ) );
85     *new_tp = *((VikTrackpoint *)(tp_iter->data));
86     new_tr->trackpoints = g_list_append ( new_tr->trackpoints, new_tp );
87     tp_iter = tp_iter->next;
88   }
89   vik_track_set_comment(new_tr,tr->comment);
90   return new_tr;
91 }
92
93 VikTrackpoint *vik_trackpoint_new()
94 {
95   return g_malloc0(sizeof(VikTrackpoint));
96 }
97
98 void vik_trackpoint_free(VikTrackpoint *tp)
99 {
100   g_free(tp);
101 }
102
103 VikTrackpoint *vik_trackpoint_copy(VikTrackpoint *tp)
104 {
105   VikTrackpoint *rv = vik_trackpoint_new();
106   *rv = *tp;
107   return rv;
108 }
109
110 gdouble vik_track_get_length(const VikTrack *tr)
111 {
112   gdouble len = 0.0;
113   if ( tr->trackpoints )
114   {
115     GList *iter = tr->trackpoints->next;
116     while (iter)
117     {
118       if ( ! VIK_TRACKPOINT(iter->data)->newsegment )
119         len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
120                                 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
121       iter = iter->next;
122     }
123   }
124   return len;
125 }
126
127 gdouble vik_track_get_length_including_gaps(const VikTrack *tr)
128 {
129   gdouble len = 0.0;
130   if ( tr->trackpoints )
131   {
132     GList *iter = tr->trackpoints->next;
133     while (iter)
134     {
135       len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
136                               &(VIK_TRACKPOINT(iter->prev->data)->coord) );
137       iter = iter->next;
138     }
139   }
140   return len;
141 }
142
143 gulong vik_track_get_tp_count(const VikTrack *tr)
144 {
145   gulong num = 0;
146   GList *iter = tr->trackpoints;
147   while ( iter )
148   {
149     num++;
150     iter = iter->next;
151   }
152   return num;
153 }
154
155 gulong vik_track_get_dup_point_count ( const VikTrack *tr )
156 {
157   gulong num = 0;
158   GList *iter = tr->trackpoints;
159   while ( iter )
160   {
161     if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
162                        &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
163       num++;
164     iter = iter->next;
165   }
166   return num;
167 }
168
169 void vik_track_remove_dup_points ( VikTrack *tr )
170 {
171   GList *iter = tr->trackpoints;
172   while ( iter )
173   {
174     if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
175                        &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
176     {
177       g_free ( iter->next->data );
178       tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next );
179     }
180     else
181       iter = iter->next;
182   }
183 }
184
185 guint vik_track_get_segment_count(const VikTrack *tr)
186 {
187   guint num = 1;
188   GList *iter = tr->trackpoints;
189   if ( !iter )
190     return 0;
191   while ( (iter = iter->next) )
192   {
193     if ( VIK_TRACKPOINT(iter->data)->newsegment )
194       num++;
195   }
196   return num;
197 }
198
199 VikTrack **vik_track_split_into_segments(VikTrack *t, guint *ret_len)
200 {
201   VikTrack **rv;
202   VikTrack *tr;
203   guint i;
204   guint segs = vik_track_get_segment_count(t);
205   GList *iter;
206
207   if ( segs < 2 )
208   {
209     *ret_len = 0;
210     return NULL;
211   }
212
213   rv = g_malloc ( segs * sizeof(VikTrack *) );
214   tr = vik_track_copy ( t );
215   rv[0] = tr;
216   iter = tr->trackpoints;
217
218   i = 1;
219   while ( (iter = iter->next) )
220   {
221     if ( VIK_TRACKPOINT(iter->data)->newsegment )
222     {
223       iter->prev->next = NULL;
224       iter->prev = NULL;
225       rv[i] = vik_track_new();
226       if ( tr->comment )
227         vik_track_set_comment ( rv[i], tr->comment );
228       rv[i]->visible = tr->visible;
229       rv[i]->trackpoints = iter;
230       i++;
231     }
232   }
233   *ret_len = segs;
234   return rv;
235 }
236
237 void vik_track_reverse ( VikTrack *tr )
238 {
239   GList *iter;
240   tr->trackpoints = g_list_reverse(tr->trackpoints);
241
242   /* fix 'newsegment' */
243   iter = g_list_last ( tr->trackpoints );
244   while ( iter )
245   {
246     if ( ! iter->next ) /* last segment, was first, cancel newsegment. */
247       VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
248     if ( ! iter->prev ) /* first segment by convention has newsegment flag. */
249       VIK_TRACKPOINT(iter->data)->newsegment = TRUE;
250     else if ( VIK_TRACKPOINT(iter->data)->newsegment && iter->next )
251     {
252       VIK_TRACKPOINT(iter->next->data)->newsegment = TRUE;
253       VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
254     }
255     iter = iter->prev;
256   }
257 }
258
259 gdouble vik_track_get_average_speed(const VikTrack *tr)
260 {
261   gdouble len = 0.0;
262   guint32 time = 0;
263   if ( tr->trackpoints )
264   {
265     GList *iter = tr->trackpoints->next;
266     while (iter)
267     {
268       if ( VIK_TRACKPOINT(iter->data)->has_timestamp && 
269           VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
270           (! VIK_TRACKPOINT(iter->data)->newsegment) )
271       {
272         len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
273                                 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
274         time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
275       }
276       iter = iter->next;
277     }
278   }
279   return (time == 0) ? 0 : ABS(len/time);
280 }
281
282 gdouble vik_track_get_max_speed(const VikTrack *tr)
283 {
284   gdouble maxspeed = 0.0, speed = 0.0;
285   if ( tr->trackpoints )
286   {
287     GList *iter = tr->trackpoints->next;
288     while (iter)
289     {
290       if ( VIK_TRACKPOINT(iter->data)->has_timestamp && 
291           VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
292           (! VIK_TRACKPOINT(iter->data)->newsegment) )
293       {
294         speed =  vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) )
295                  / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
296         if ( speed > maxspeed )
297           maxspeed = speed;
298       }
299       iter = iter->next;
300     }
301   }
302   return maxspeed;
303 }
304
305 void vik_track_convert ( VikTrack *tr, VikCoordMode dest_mode )
306 {
307   GList *iter = tr->trackpoints;
308   while (iter)
309   {
310     vik_coord_convert ( &(VIK_TRACKPOINT(iter->data)->coord), dest_mode );
311     iter = iter->next;
312   }
313 }
314
315 /* I understood this when I wrote it ... maybe ... Basically it eats up the
316  * proper amounts of length on the track and averages elevation over that. */
317 gdouble *vik_track_make_elevation_map ( const VikTrack *tr, guint16 num_chunks )
318 {
319   gdouble *pts;
320   gdouble total_length, chunk_length, current_dist, current_area_under_curve, current_seg_length, dist_along_seg = 0.0;
321   gdouble altitude1, altitude2;
322   guint16 current_chunk;
323   gboolean ignore_it = FALSE;
324
325   GList *iter = tr->trackpoints;
326
327   { /* test if there's anything worth calculating */
328     gboolean okay = FALSE;
329     while ( iter )
330     {
331       if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
332         okay = TRUE; break;
333       }
334       iter = iter->next;
335     }
336     if ( ! okay )
337       return NULL;
338   }
339
340
341
342   g_assert ( num_chunks < 16000 );
343
344   pts = g_malloc ( sizeof(gdouble) * num_chunks );
345
346   total_length = vik_track_get_length_including_gaps ( tr );
347   chunk_length = total_length / num_chunks;
348
349   current_dist = 0.0;
350   current_area_under_curve = 0;
351   current_chunk = 0;
352   current_seg_length = 0;
353
354   current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
355       &(VIK_TRACKPOINT(iter->next->data)->coord) );
356   altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
357   altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
358   dist_along_seg = 0;
359
360   while ( current_chunk < num_chunks ) {
361
362     /* go along current seg */
363     if ( current_seg_length && (current_seg_length - dist_along_seg) > chunk_length ) {
364       dist_along_seg += chunk_length;
365
366       /*        /
367        *   pt2 *
368        *      /x       altitude = alt_at_pt_1 + alt_at_pt_2 / 2 = altitude1 + slope * dist_value_of_pt_inbetween_pt1_and_pt2
369        *     /xx   avg altitude = area under curve / chunk len
370        *pt1 *xxx   avg altitude = altitude1 + (altitude2-altitude1)/(current_seg_length)*(dist_along_seg + (chunk_len/2))
371        *   / xxx
372        *  /  xxx
373        **/
374
375       if ( ignore_it )
376         pts[current_chunk] = VIK_DEFAULT_ALTITUDE;
377       else
378         pts[current_chunk] = altitude1 + (altitude2-altitude1)*((dist_along_seg - (chunk_length/2))/current_seg_length);
379
380       current_chunk++;
381     } else {
382       /* finish current seg */
383       if ( current_seg_length ) {
384         gdouble altitude_at_dist_along_seg = altitude1 + (altitude2-altitude1)/(current_seg_length)*dist_along_seg;
385         current_dist = current_seg_length - dist_along_seg;
386         current_area_under_curve = current_dist*(altitude_at_dist_along_seg + altitude2)*0.5;
387       } else { current_dist = current_area_under_curve = 0; } /* should only happen if first current_seg_length == 0 */
388
389       /* get intervening segs */
390       iter = iter->next;
391       while ( iter && iter->next ) {
392         current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
393             &(VIK_TRACKPOINT(iter->next->data)->coord) );
394         altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
395         altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
396         ignore_it = VIK_TRACKPOINT(iter->next->data)->newsegment;
397
398         if ( chunk_length - current_dist >= current_seg_length ) {
399           current_dist += current_seg_length;
400           current_area_under_curve += current_seg_length * (altitude1+altitude2) * 0.5;
401           iter = iter->next;
402         } else {
403           break;
404         }
405       }
406
407       /* final seg */
408       dist_along_seg = chunk_length - current_dist;
409       if ( ignore_it || !iter->next ) {
410         pts[current_chunk] = current_area_under_curve / current_dist;
411       } 
412       else {
413         current_area_under_curve += dist_along_seg * (altitude1 + (altitude2 - altitude1)*dist_along_seg/current_seg_length);
414         pts[current_chunk] = current_area_under_curve / chunk_length;
415       }
416
417       current_dist = 0;
418       current_chunk++;
419     }
420   }
421
422   return pts;
423 }
424
425
426 void vik_track_get_total_elevation_gain(const VikTrack *tr, gdouble *up, gdouble *down)
427 {
428   gdouble diff;
429   *up = *down = 0;
430   if ( tr->trackpoints )
431   {
432     GList *iter = tr->trackpoints->next;
433     while (iter)
434     {
435       diff = VIK_TRACKPOINT(iter->data)->altitude - VIK_TRACKPOINT(iter->prev->data)->altitude;
436       if ( diff > 0 )
437         *up += diff;
438       else
439         *down -= diff;
440       iter = iter->next;
441     }
442   }
443 }
444
445 gdouble *vik_track_make_speed_map ( const VikTrack *tr, guint16 num_chunks )
446 {
447   gdouble *pts;
448   gdouble duration, chunk_dur, t;
449   time_t t1, t2;
450   int i;
451
452   GList *iter = tr->trackpoints;
453
454   if ( ! tr->trackpoints )
455     return NULL;
456
457   g_assert ( num_chunks < 16000 );
458
459   t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
460   t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
461   duration = t2 - t1;
462
463   if ( !t1 || !t2 || !duration )
464     return NULL;
465
466   if (duration < 0) {
467     fprintf(stderr, "negative duration: unsorted trackpoint timestamps?\n");
468     return NULL;
469   }
470   
471   pts = g_malloc ( sizeof(gdouble) * num_chunks );
472
473   chunk_dur = duration / num_chunks;
474   t = t1;
475   for (i = 0; i < num_chunks; i++, t+=chunk_dur) {
476     while (iter && VIK_TRACKPOINT(iter->data)->timestamp <= t) {
477       iter = iter->next;
478     }
479     pts[i] = vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord),
480                                           &(VIK_TRACKPOINT(iter->data)->coord) ) / 
481       (gdouble)(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
482   }
483
484   return pts;
485 }
486
487 VikCoord *vik_track_get_closest_tp_by_percentage_dist ( VikTrack *tr, gdouble reldist )
488 {
489   gdouble dist = vik_track_get_length_including_gaps(tr) * reldist;
490   gdouble current_dist = 0.0;
491   gdouble current_inc = 0.0;
492   VikCoord *rv;
493   if ( tr->trackpoints )
494   {
495     GList *iter = tr->trackpoints->next;
496     while (iter)
497     {
498       current_inc = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
499                                      &(VIK_TRACKPOINT(iter->prev->data)->coord) );
500       current_dist += current_inc;
501       if ( current_dist >= dist )
502         break;
503       iter = iter->next;
504     }
505     /* we've gone past the dist already, was prev trackpoint closer? */
506     /* should do a vik_coord_average_weighted() thingy. */
507     if ( iter->prev && abs(current_dist-current_inc-dist) < abs(current_dist-dist) )
508       iter = iter->prev;
509
510
511
512     rv = g_malloc(sizeof(VikCoord));
513     *rv = VIK_TRACKPOINT(iter->data)->coord;
514
515     return rv;
516
517   }
518   return NULL;
519 }