2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
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.
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.
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
33 VikTrack *vik_track_new()
35 VikTrack *tr = g_malloc0 ( sizeof ( VikTrack ) );
40 void vik_track_set_comment_no_copy(VikTrack *tr, gchar *comment)
43 g_free ( tr->comment );
44 tr->comment = comment;
48 void vik_track_set_comment(VikTrack *tr, const gchar *comment)
51 g_free ( tr->comment );
53 if ( comment && comment[0] != '\0' )
54 tr->comment = g_strdup(comment);
59 void vik_track_ref(VikTrack *tr)
64 void vik_track_free(VikTrack *tr)
66 if ( tr->ref_count-- > 1 )
70 g_free ( tr->comment );
71 g_list_foreach ( tr->trackpoints, (GFunc) g_free, NULL );
72 g_list_free( tr->trackpoints );
76 VikTrack *vik_track_copy ( const VikTrack *tr )
78 VikTrack *new_tr = vik_track_new();
79 VikTrackpoint *new_tp;
80 GList *tp_iter = tr->trackpoints;
81 new_tr->visible = tr->visible;
82 new_tr->trackpoints = NULL;
85 new_tp = g_malloc ( sizeof ( VikTrackpoint ) );
86 *new_tp = *((VikTrackpoint *)(tp_iter->data));
87 new_tr->trackpoints = g_list_append ( new_tr->trackpoints, new_tp );
88 tp_iter = tp_iter->next;
90 vik_track_set_comment(new_tr,tr->comment);
94 VikTrackpoint *vik_trackpoint_new()
96 return g_malloc0(sizeof(VikTrackpoint));
99 void vik_trackpoint_free(VikTrackpoint *tp)
104 VikTrackpoint *vik_trackpoint_copy(VikTrackpoint *tp)
106 VikTrackpoint *rv = vik_trackpoint_new();
111 gdouble vik_track_get_length(const VikTrack *tr)
114 if ( tr->trackpoints )
116 GList *iter = tr->trackpoints->next;
119 if ( ! VIK_TRACKPOINT(iter->data)->newsegment )
120 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
121 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
128 gdouble vik_track_get_length_including_gaps(const VikTrack *tr)
131 if ( tr->trackpoints )
133 GList *iter = tr->trackpoints->next;
136 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
137 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
144 gulong vik_track_get_tp_count(const VikTrack *tr)
147 GList *iter = tr->trackpoints;
156 gulong vik_track_get_dup_point_count ( const VikTrack *tr )
159 GList *iter = tr->trackpoints;
162 if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
163 &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
170 void vik_track_remove_dup_points ( VikTrack *tr )
172 GList *iter = tr->trackpoints;
175 if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
176 &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
178 g_free ( iter->next->data );
179 tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next );
186 guint vik_track_get_segment_count(const VikTrack *tr)
189 GList *iter = tr->trackpoints;
192 while ( (iter = iter->next) )
194 if ( VIK_TRACKPOINT(iter->data)->newsegment )
200 VikTrack **vik_track_split_into_segments(VikTrack *t, guint *ret_len)
205 guint segs = vik_track_get_segment_count(t);
214 rv = g_malloc ( segs * sizeof(VikTrack *) );
215 tr = vik_track_copy ( t );
217 iter = tr->trackpoints;
220 while ( (iter = iter->next) )
222 if ( VIK_TRACKPOINT(iter->data)->newsegment )
224 iter->prev->next = NULL;
226 rv[i] = vik_track_new();
228 vik_track_set_comment ( rv[i], tr->comment );
229 rv[i]->visible = tr->visible;
230 rv[i]->trackpoints = iter;
238 void vik_track_reverse ( VikTrack *tr )
241 tr->trackpoints = g_list_reverse(tr->trackpoints);
243 /* fix 'newsegment' */
244 iter = g_list_last ( tr->trackpoints );
247 if ( ! iter->next ) /* last segment, was first, cancel newsegment. */
248 VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
249 if ( ! iter->prev ) /* first segment by convention has newsegment flag. */
250 VIK_TRACKPOINT(iter->data)->newsegment = TRUE;
251 else if ( VIK_TRACKPOINT(iter->data)->newsegment && iter->next )
253 VIK_TRACKPOINT(iter->next->data)->newsegment = TRUE;
254 VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
260 gdouble vik_track_get_average_speed(const VikTrack *tr)
264 if ( tr->trackpoints )
266 GList *iter = tr->trackpoints->next;
269 if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
270 VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
271 (! VIK_TRACKPOINT(iter->data)->newsegment) )
273 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
274 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
275 time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
280 return (time == 0) ? 0 : ABS(len/time);
283 gdouble vik_track_get_max_speed(const VikTrack *tr)
285 gdouble maxspeed = 0.0, speed = 0.0;
286 if ( tr->trackpoints )
288 GList *iter = tr->trackpoints->next;
291 if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
292 VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
293 (! VIK_TRACKPOINT(iter->data)->newsegment) )
295 speed = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) )
296 / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
297 if ( speed > maxspeed )
306 void vik_track_convert ( VikTrack *tr, VikCoordMode dest_mode )
308 GList *iter = tr->trackpoints;
311 vik_coord_convert ( &(VIK_TRACKPOINT(iter->data)->coord), dest_mode );
316 /* I understood this when I wrote it ... maybe ... Basically it eats up the
317 * proper amounts of length on the track and averages elevation over that. */
318 gdouble *vik_track_make_elevation_map ( const VikTrack *tr, guint16 num_chunks )
321 gdouble total_length, chunk_length, current_dist, current_area_under_curve, current_seg_length, dist_along_seg = 0.0;
322 gdouble altitude1, altitude2;
323 guint16 current_chunk;
324 gboolean ignore_it = FALSE;
326 GList *iter = tr->trackpoints;
328 { /* test if there's anything worth calculating */
329 gboolean okay = FALSE;
332 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
343 g_assert ( num_chunks < 16000 );
345 pts = g_malloc ( sizeof(gdouble) * num_chunks );
347 total_length = vik_track_get_length_including_gaps ( tr );
348 chunk_length = total_length / num_chunks;
351 current_area_under_curve = 0;
353 current_seg_length = 0;
355 current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
356 &(VIK_TRACKPOINT(iter->next->data)->coord) );
357 altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
358 altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
361 while ( current_chunk < num_chunks ) {
363 /* go along current seg */
364 if ( current_seg_length && (current_seg_length - dist_along_seg) > chunk_length ) {
365 dist_along_seg += chunk_length;
369 * /x altitude = alt_at_pt_1 + alt_at_pt_2 / 2 = altitude1 + slope * dist_value_of_pt_inbetween_pt1_and_pt2
370 * /xx avg altitude = area under curve / chunk len
371 *pt1 *xxx avg altitude = altitude1 + (altitude2-altitude1)/(current_seg_length)*(dist_along_seg + (chunk_len/2))
377 pts[current_chunk] = VIK_DEFAULT_ALTITUDE;
379 pts[current_chunk] = altitude1 + (altitude2-altitude1)*((dist_along_seg - (chunk_length/2))/current_seg_length);
383 /* finish current seg */
384 if ( current_seg_length ) {
385 gdouble altitude_at_dist_along_seg = altitude1 + (altitude2-altitude1)/(current_seg_length)*dist_along_seg;
386 current_dist = current_seg_length - dist_along_seg;
387 current_area_under_curve = current_dist*(altitude_at_dist_along_seg + altitude2)*0.5;
388 } else { current_dist = current_area_under_curve = 0; } /* should only happen if first current_seg_length == 0 */
390 /* get intervening segs */
392 while ( iter && iter->next ) {
393 current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
394 &(VIK_TRACKPOINT(iter->next->data)->coord) );
395 altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
396 altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
397 ignore_it = VIK_TRACKPOINT(iter->next->data)->newsegment;
399 if ( chunk_length - current_dist >= current_seg_length ) {
400 current_dist += current_seg_length;
401 current_area_under_curve += current_seg_length * (altitude1+altitude2) * 0.5;
409 dist_along_seg = chunk_length - current_dist;
410 if ( ignore_it || !iter->next ) {
411 pts[current_chunk] = current_area_under_curve / current_dist;
414 current_area_under_curve += dist_along_seg * (altitude1 + (altitude2 - altitude1)*dist_along_seg/current_seg_length);
415 pts[current_chunk] = current_area_under_curve / chunk_length;
427 void vik_track_get_total_elevation_gain(const VikTrack *tr, gdouble *up, gdouble *down)
431 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE )
433 GList *iter = tr->trackpoints->next;
436 diff = VIK_TRACKPOINT(iter->data)->altitude - VIK_TRACKPOINT(iter->prev->data)->altitude;
444 *up = *down = VIK_DEFAULT_ALTITUDE;
451 void compute_spline(int n, double *x, double *f, spline_coeff_t *p)
453 double *h, *alpha, *B, *m;
456 double new_x[3], new_f[3];
462 new_x[1] = x[0]+0.00001;
473 new_x[2] = x[1] + x[1]-x[0];
474 new_f[2] = f[1] + f[1]-f[0];
480 /* we're solving a linear system of equations of the form Ax = B.
481 * The matrix a is tridiagonal and consists of coefficients in
482 * the h[] and alpha[] arrays.
485 h = (double *)malloc(sizeof(double) * (n-1));
486 for (i=0; i<n-1; i++) {
490 alpha = (double *)malloc(sizeof(double) * (n-2));
491 for (i=0; i<n-2; i++) {
492 alpha[i] = 2 * (h[i] + h[i+1]);
495 /* B[] is the vector on the right hand side of the equation */
496 B = (double *)malloc(sizeof(double) * (n-2));
497 for (i=0; i<n-2; i++) {
498 B[i] = 6 * ((f[i+2] - f[i+1])/h[i+1] - (f[i+1] - f[i])/h[i]);
501 /* Now solve the n-2 by n-2 system */
502 m = (double *)malloc(sizeof(double) * (n-2));
503 for (i=1; i<=n-3; i++) {
509 di = di - (ai-1 / di-1) * ci-1
510 bi = bi - (ai-1 / di-1) * bi-1
513 alpha[i] = alpha[i] - (h[i]/alpha[i-1]) * h[i];
514 B[i] = B[i] - (h[i]/alpha[i-1]) * B[i-1];
516 /* xn-3 = bn-3 / dn-3; */
517 m[n-3] = B[n-3]/alpha[n-3];
518 for (i=n-4; i>=0; i--) {
519 m[i] = (B[i]-h[i+1]*m[i+1])/alpha[i];
522 for (i=0; i<orig_n-1; i++) {
524 mi = (i==(n-2)) ? 0 : m[i];
525 mi1 = (i==0) ? 0 : m[i-1];
528 p[i].b = (f[i+1] - f[i]) / h[i] + h[i] * (2*mi + mi1) / 6;
530 p[i].d = (mi-mi1)/(6*h[i]);
539 /* by Alex Foobarian */
540 gdouble *vik_track_make_speed_map ( const VikTrack *tr, guint16 num_chunks )
543 gdouble duration, chunk_dur, T, s_prev, s_now;
545 int i, pt_count, numpts, spline;
550 if ( ! tr->trackpoints )
553 g_assert ( num_chunks < 16000 );
555 #ifdef XXXXXXXXXXXXXXXXXX
556 iter = tr->trackpoints;
560 #endif /*XXXXXXXXXXXXXXXXXX*/
562 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
563 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
566 if ( !t1 || !t2 || !duration )
570 fprintf(stderr, "negative duration: unsorted trackpoint timestamps?\n");
573 pt_count = vik_track_get_tp_count(tr);
575 v = g_malloc ( sizeof(gdouble) * num_chunks );
576 chunk_dur = duration / num_chunks;
578 s = g_malloc(sizeof(double) * pt_count);
579 t = g_malloc(sizeof(double) * pt_count);
580 p = g_malloc(sizeof(spline_coeff_t) * (pt_count-1));
582 iter = tr->trackpoints->next;
585 t[0] = VIK_TRACKPOINT(iter->prev->data)->timestamp;
588 s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
589 t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
594 compute_spline(numpts, t, s, p);
597 printf("Got spline\n");
598 for (i=0; i<numpts-1; i++) {
599 printf("a = %15f b = %15f c = %15f d = %15f\n", p[i].a, p[i].b, p[i].c, p[i].d);
603 /* the spline gives us distances at chunk_dur intervals. from these,
604 * we obtain average speed in each interval.
609 p[spline].d * pow(T - t[spline+1], 3) +
610 p[spline].c * pow(T - t[spline+1], 2) +
611 p[spline].b * (T - t[spline+1]) +
613 for (i = 0; i < num_chunks; i++, T+=chunk_dur) {
614 while (T > t[spline+1]) {
618 p[spline].d * pow(T - t[spline+1], 3) +
619 p[spline].c * pow(T - t[spline+1], 2) +
620 p[spline].b * (T - t[spline+1]) +
622 v[i] = (s_now - s_prev) / chunk_dur;
625 * old method of averages
626 v[i] = (s[spline+1]-s[spline])/(t[spline+1]-t[spline]);
635 /* by Alex Foobarian */
636 VikTrackpoint *vik_track_get_closest_tp_by_percentage_dist ( VikTrack *tr, gdouble reldist )
638 gdouble dist = vik_track_get_length_including_gaps(tr) * reldist;
639 gdouble current_dist = 0.0;
640 gdouble current_inc = 0.0;
641 if ( tr->trackpoints )
643 GList *iter = tr->trackpoints->next;
644 GList *last_iter = NULL;
647 current_inc = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
648 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
649 current_dist += current_inc;
650 if ( current_dist >= dist )
655 if (!iter) /* passing the end the track */
656 return (last_iter ? last_iter->data : NULL);
657 /* we've gone past the dist already, was prev trackpoint closer? */
658 /* should do a vik_coord_average_weighted() thingy. */
659 if ( iter->prev && abs(current_dist-current_inc-dist) < abs(current_dist-dist) )
662 return VIK_TRACKPOINT(iter->data);
668 gboolean vik_track_get_minmax_alt ( const VikTrack *tr, gdouble *min_alt, gdouble *max_alt )
672 if ( tr && tr->trackpoints && tr->trackpoints->data && (VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE) ) {
673 GList *iter = tr->trackpoints->next;
677 tmp_alt = VIK_TRACKPOINT(iter->data)->altitude;
678 if ( tmp_alt > *max_alt )
680 if ( tmp_alt < *min_alt )
689 void vik_track_marshall ( VikTrack *tr, guint8 **data, guint *datalen)
692 GByteArray *b = g_byte_array_new();
696 g_byte_array_append(b, (guint8 *)tr, sizeof(*tr));
698 /* we'll fill out number of trackpoints later */
700 g_byte_array_append(b, (guint8 *)&len, sizeof(len));
702 tps = tr->trackpoints;
705 g_byte_array_append(b, (guint8 *)tps->data, sizeof(VikTrackpoint));
709 *(guint *)(b->data + intp) = ntp;
711 len = (tr->comment) ? strlen(tr->comment)+1 : 0;
712 g_byte_array_append(b, (guint8 *)&len, sizeof(len));
713 if (tr->comment) g_byte_array_append(b, (guint8 *)tr->comment, len);
717 g_byte_array_free(b, FALSE);
720 VikTrack *vik_track_unmarshall (guint8 *data, guint datalen)
723 VikTrack *new_tr = vik_track_new();
724 VikTrackpoint *new_tp;
728 /* only the visibility is needed */
729 new_tr->visible = ((VikTrack *)data)->visible;
730 data += sizeof(*new_tr);
732 ntp = *(guint *)data;
735 for (i=0; i<ntp; i++) {
736 new_tp = vik_trackpoint_new();
737 memcpy(new_tp, data, sizeof(*new_tp));
738 data += sizeof(*new_tp);
739 new_tr->trackpoints = g_list_append(new_tr->trackpoints, new_tp);
742 len = *(guint *)data;
745 new_tr->comment = g_strdup((gchar *)data);