]> git.street.me.uk Git - andy/viking.git/blame - src/viktrack.c
Use configure.ac version
[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>
bf35388d
EB
25#include <stdlib.h>
26#include <math.h>
50a14534
EB
27#include "coords.h"
28#include "vikcoord.h"
29#include "viktrack.h"
30#include "globals.h"
31
32VikTrack *vik_track_new()
33{
8c4f1350 34 VikTrack *tr = g_malloc0 ( sizeof ( VikTrack ) );
50a14534
EB
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
c79f0206
EB
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
50a14534
EB
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
9903c388 378 pts[current_chunk] = altitude1 + (altitude2-altitude1)*((dist_along_seg - (chunk_length/2))/current_seg_length);
50a14534
EB
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;
9903c388 409 if ( ignore_it || !iter->next ) {
50a14534 410 pts[current_chunk] = current_area_under_curve / current_dist;
9903c388 411 }
50a14534
EB
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
426void vik_track_get_total_elevation_gain(const VikTrack *tr, gdouble *up, gdouble *down)
427{
428 gdouble diff;
429 *up = *down = 0;
8c4f1350 430 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE )
50a14534
EB
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 }
bf35388d
EB
442 } else
443 *up = *down = VIK_DEFAULT_ALTITUDE;
444}
445
446typedef struct {
447 double a, b, c, d;
448} spline_coeff_t;
449
450void compute_spline(int n, double *x, double *f, spline_coeff_t *p)
451{
452 double *h, *alpha, *B, *m;
453 int i;
454
455 /* we're solving a linear system of equations of the form Ax = B.
456 * The matrix a is tridiagonal and consists of coefficients in
457 * the h[] and alpha[] arrays.
458 */
459
460 h = (double *)malloc(sizeof(double) * (n-1));
461 for (i=0; i<n-1; i++) {
462 h[i] = x[i+1]-x[i];
463 }
464
465 alpha = (double *)malloc(sizeof(double) * (n-2));
466 for (i=0; i<n-2; i++) {
467 alpha[i] = 2 * (h[i] + h[i+1]);
50a14534 468 }
bf35388d
EB
469
470 /* B[] is the vector on the right hand side of the equation */
471 B = (double *)malloc(sizeof(double) * (n-2));
472 for (i=0; i<n-2; i++) {
473 B[i] = 6 * ((f[i+2] - f[i+1])/h[i+1] - (f[i+1] - f[i])/h[i]);
474 }
475
476 /* Now solve the n-2 by n-2 system */
477 m = (double *)malloc(sizeof(double) * (n-2));
478 for (i=1; i<=n-3; i++) {
479 /*
480 d0 = alpha 0
481 a0 = h1
482 c0 = h1
483
484 di = di - (ai-1 / di-1) * ci-1
485 bi = bi - (ai-1 / di-1) * bi-1
486 ;
487 */
488 alpha[i] = alpha[i] - (h[i]/alpha[i-1]) * h[i];
489 B[i] = B[i] - (h[i]/alpha[i-1]) * B[i-1];
490 }
491 /* xn-3 = bn-3 / dn-3; */
492 m[n-3] = B[n-3]/alpha[n-3];
493 for (i=n-4; i>=0; i--) {
494 m[i] = (B[i]-h[i+1]*m[i+1])/alpha[i];
495 }
496
497 for (i=0; i<n-1; i++) {
498 double mi, mi1;
499 mi = (i==(n-2)) ? 0 : m[i];
500 mi1 = (i==0) ? 0 : m[i-1];
501
502 p[i].a = f[i+1];
503 p[i].b = (f[i+1] - f[i]) / h[i] + h[i] * (2*mi + mi1) / 6;
504 p[i].c = mi/2;
505 p[i].d = (mi-mi1)/(6*h[i]);
506 }
507
508 free(alpha);
509 free(B);
510 free(h);
511 free(m);
50a14534 512}
25e44eac 513
bf35388d 514/* by Alex Foobarian */
25e44eac
AF
515gdouble *vik_track_make_speed_map ( const VikTrack *tr, guint16 num_chunks )
516{
bf35388d
EB
517 gdouble *v, *s, *t;
518 gdouble duration, chunk_dur, T, s_prev, s_now;
25e44eac 519 time_t t1, t2;
bf35388d
EB
520 int i, pt_count, numpts, spline;
521 GList *iter;
522 spline_coeff_t *p;
25e44eac 523
24d5c7e2
EB
524 if ( ! tr->trackpoints )
525 return NULL;
25e44eac 526
24d5c7e2 527 g_assert ( num_chunks < 16000 );
25e44eac
AF
528
529 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
530 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
531 duration = t2 - t1;
c79f0206
EB
532
533 if ( !t1 || !t2 || !duration )
534 return NULL;
535
25e44eac
AF
536 if (duration < 0) {
537 fprintf(stderr, "negative duration: unsorted trackpoint timestamps?\n");
538 return NULL;
539 }
bf35388d 540 pt_count = vik_track_get_tp_count(tr);
24d5c7e2 541
bf35388d 542 v = g_malloc ( sizeof(gdouble) * num_chunks );
25e44eac 543 chunk_dur = duration / num_chunks;
bf35388d
EB
544
545 s = g_malloc(sizeof(double) * pt_count);
546 t = g_malloc(sizeof(double) * pt_count);
547 p = g_malloc(sizeof(spline_coeff_t) * (pt_count-1));
548
549 iter = tr->trackpoints->next;
550 numpts = 0;
551 s[0] = 0;
552 t[0] = VIK_TRACKPOINT(iter->prev->data)->timestamp;
553 numpts++;
554 while (iter) {
555 s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
556 t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
557 numpts++;
558 iter = iter->next;
25e44eac
AF
559 }
560
bf35388d
EB
561 compute_spline(numpts, t, s, p);
562
563 /*
564 printf("Got spline\n");
565 for (i=0; i<numpts-1; i++) {
566 printf("a = %15f b = %15f c = %15f d = %15f\n", p[i].a, p[i].b, p[i].c, p[i].d);
567 }
568 */
569
570 /* the spline gives us distances at chunk_dur intervals. from these,
571 * we obtain average speed in each interval.
572 */
573 spline = 0;
574 T = t[spline];
575 s_prev =
576 p[spline].d * pow(T - t[spline+1], 3) +
577 p[spline].c * pow(T - t[spline+1], 2) +
578 p[spline].b * (T - t[spline+1]) +
579 p[spline].a;
580 for (i = 0; i < num_chunks; i++, T+=chunk_dur) {
581 while (T > t[spline+1]) {
582 spline++;
583 }
584 s_now =
585 p[spline].d * pow(T - t[spline+1], 3) +
586 p[spline].c * pow(T - t[spline+1], 2) +
587 p[spline].b * (T - t[spline+1]) +
588 p[spline].a;
589 v[i] = (s_now - s_prev) / chunk_dur;
590 s_prev = s_now;
591 /*
592 * old method of averages
593 v[i] = (s[spline+1]-s[spline])/(t[spline+1]-t[spline]);
594 */
595 }
596 g_free(s);
597 g_free(t);
598 g_free(p);
599 return v;
25e44eac 600}
24d5c7e2 601
bf35388d 602/* by Alex Foobarian */
24d5c7e2
EB
603VikCoord *vik_track_get_closest_tp_by_percentage_dist ( VikTrack *tr, gdouble reldist )
604{
605 gdouble dist = vik_track_get_length_including_gaps(tr) * reldist;
606 gdouble current_dist = 0.0;
607 gdouble current_inc = 0.0;
608 VikCoord *rv;
609 if ( tr->trackpoints )
610 {
611 GList *iter = tr->trackpoints->next;
612 while (iter)
613 {
614 current_inc = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
615 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
616 current_dist += current_inc;
617 if ( current_dist >= dist )
618 break;
619 iter = iter->next;
620 }
621 /* we've gone past the dist already, was prev trackpoint closer? */
622 /* should do a vik_coord_average_weighted() thingy. */
623 if ( iter->prev && abs(current_dist-current_inc-dist) < abs(current_dist-dist) )
624 iter = iter->prev;
625
626
627
628 rv = g_malloc(sizeof(VikCoord));
629 *rv = VIK_TRACKPOINT(iter->data)->coord;
630
631 return rv;
632
633 }
634 return NULL;
635}
b42a25ba
EB
636
637gboolean vik_track_get_minmax_alt ( const VikTrack *tr, gdouble *min_alt, gdouble *max_alt )
638{
639 *min_alt = 25000;
640 *max_alt = -5000;
641 if ( tr && tr->trackpoints && tr->trackpoints->data && (VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE) ) {
642 GList *iter = tr->trackpoints->next;
643 gdouble tmp_alt;
644 while (iter)
645 {
646 tmp_alt = VIK_TRACKPOINT(iter->data)->altitude;
647 if ( tmp_alt > *max_alt )
648 *max_alt = tmp_alt;
649 if ( tmp_alt < *min_alt )
650 *min_alt = tmp_alt;
651 iter = iter->next;
652 }
653 return TRUE;
654 }
655 return FALSE;
656}