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