]> git.street.me.uk Git - andy/viking.git/blame - src/viktrack.c
Alex Foobarian's v-vs-t and elevation map patch
[andy/viking.git] / src / viktrack.c
CommitLineData
50a14534
EB
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>
9903c388 24#include <stdio.h>
50a14534
EB
25#include "coords.h"
26#include "vikcoord.h"
27#include "viktrack.h"
28#include "globals.h"
29
30VikTrack *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
39void 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
47void 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
58void vik_track_ref(VikTrack *tr)
59{
60 tr->ref_count++;
61}
62
63void 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
75VikTrack *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
93VikTrackpoint *vik_trackpoint_new()
94{
95 return g_malloc0(sizeof(VikTrackpoint));
96}
97
98void vik_trackpoint_free(VikTrackpoint *tp)
99{
100 g_free(tp);
101}
102
103VikTrackpoint *vik_trackpoint_copy(VikTrackpoint *tp)
104{
105 VikTrackpoint *rv = vik_trackpoint_new();
106 *rv = *tp;
107 return rv;
108}
109
110gdouble 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
127gdouble 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
143gulong 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
155gulong 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
169void 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
185guint 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
199VikTrack **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
237void 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
259gdouble 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
282gdouble 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
305void 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. */
317gdouble *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
9903c388 363 pts[current_chunk] = altitude1 + (altitude2-altitude1)*((dist_along_seg - (chunk_length/2))/current_seg_length);
50a14534
EB
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;
9903c388 394 if ( ignore_it || !iter->next ) {
50a14534 395 pts[current_chunk] = current_area_under_curve / current_dist;
9903c388 396 }
50a14534
EB
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
411void 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}
25e44eac
AF
429
430gdouble *vik_track_make_speed_map ( const VikTrack *tr, guint16 num_chunks )
431{
432 gdouble *pts;
433 gdouble duration, chunk_dur, t;
434 time_t t1, t2;
435 int i;
436
437 GList *iter = tr->trackpoints;
438
439 g_assert ( num_chunks < 16000 );
440
441 pts = g_malloc ( sizeof(gdouble) * num_chunks );
442
443 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
444 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
445 duration = t2 - t1;
446 if (duration < 0) {
447 fprintf(stderr, "negative duration: unsorted trackpoint timestamps?\n");
448 return NULL;
449 }
450
451 chunk_dur = duration / num_chunks;
452 t = t1;
453 for (i = 0; i < num_chunks; i++, t+=chunk_dur) {
454 while (iter && VIK_TRACKPOINT(iter->data)->timestamp <= t) {
455 iter = iter->next;
456 }
457 pts[i] = vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord),
458 &(VIK_TRACKPOINT(iter->data)->coord) ) /
459 (gdouble)(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
460 }
461
462 return pts;
463}