]> git.street.me.uk Git - andy/viking.git/blob - src/viktrack.c
Guilhem's Autotools support
[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   g_assert ( num_chunks < 16000 );
328
329   pts = g_malloc ( sizeof(gdouble) * num_chunks );
330
331   total_length = vik_track_get_length_including_gaps ( tr );
332   chunk_length = total_length / num_chunks;
333
334   current_dist = 0.0;
335   current_area_under_curve = 0;
336   current_chunk = 0;
337   current_seg_length = 0;
338
339   current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
340       &(VIK_TRACKPOINT(iter->next->data)->coord) );
341   altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
342   altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
343   dist_along_seg = 0;
344
345   while ( current_chunk < num_chunks ) {
346
347     /* go along current seg */
348     if ( current_seg_length && (current_seg_length - dist_along_seg) > chunk_length ) {
349       dist_along_seg += chunk_length;
350
351       /*        /
352        *   pt2 *
353        *      /x       altitude = alt_at_pt_1 + alt_at_pt_2 / 2 = altitude1 + slope * dist_value_of_pt_inbetween_pt1_and_pt2
354        *     /xx   avg altitude = area under curve / chunk len
355        *pt1 *xxx   avg altitude = altitude1 + (altitude2-altitude1)/(current_seg_length)*(dist_along_seg + (chunk_len/2))
356        *   / xxx
357        *  /  xxx
358        **/
359
360       if ( ignore_it )
361         pts[current_chunk] = VIK_DEFAULT_ALTITUDE;
362       else
363         pts[current_chunk] = altitude1 + (altitude2-altitude1)*((dist_along_seg - (chunk_length/2))/current_seg_length);
364
365       current_chunk++;
366     } else {
367       /* finish current seg */
368       if ( current_seg_length ) {
369         gdouble altitude_at_dist_along_seg = altitude1 + (altitude2-altitude1)/(current_seg_length)*dist_along_seg;
370         current_dist = current_seg_length - dist_along_seg;
371         current_area_under_curve = current_dist*(altitude_at_dist_along_seg + altitude2)*0.5;
372       } else { current_dist = current_area_under_curve = 0; } /* should only happen if first current_seg_length == 0 */
373
374       /* get intervening segs */
375       iter = iter->next;
376       while ( iter && iter->next ) {
377         current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
378             &(VIK_TRACKPOINT(iter->next->data)->coord) );
379         altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
380         altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
381         ignore_it = VIK_TRACKPOINT(iter->next->data)->newsegment;
382
383         if ( chunk_length - current_dist >= current_seg_length ) {
384           current_dist += current_seg_length;
385           current_area_under_curve += current_seg_length * (altitude1+altitude2) * 0.5;
386           iter = iter->next;
387         } else {
388           break;
389         }
390       }
391
392       /* final seg */
393       dist_along_seg = chunk_length - current_dist;
394       if ( ignore_it || !iter->next ) {
395         pts[current_chunk] = current_area_under_curve / current_dist;
396       } 
397       else {
398         current_area_under_curve += dist_along_seg * (altitude1 + (altitude2 - altitude1)*dist_along_seg/current_seg_length);
399         pts[current_chunk] = current_area_under_curve / chunk_length;
400       }
401
402       current_dist = 0;
403       current_chunk++;
404     }
405   }
406
407   return pts;
408 }
409
410
411 void vik_track_get_total_elevation_gain(const VikTrack *tr, gdouble *up, gdouble *down)
412 {
413   gdouble diff;
414   *up = *down = 0;
415   if ( tr->trackpoints )
416   {
417     GList *iter = tr->trackpoints->next;
418     while (iter)
419     {
420       diff = VIK_TRACKPOINT(iter->data)->altitude - VIK_TRACKPOINT(iter->prev->data)->altitude;
421       if ( diff > 0 )
422         *up += diff;
423       else
424         *down -= diff;
425       iter = iter->next;
426     }
427   }
428 }