2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
42 VikTrack *vik_track_new()
44 VikTrack *tr = g_malloc0 ( sizeof ( VikTrack ) );
49 void vik_track_set_comment_no_copy(VikTrack *tr, gchar *comment)
52 g_free ( tr->comment );
53 tr->comment = comment;
57 void vik_track_set_name(VikTrack *tr, const gchar *name)
62 tr->name = g_strdup(name);
65 void vik_track_set_comment(VikTrack *tr, const gchar *comment)
68 g_free ( tr->comment );
70 if ( comment && comment[0] != '\0' )
71 tr->comment = g_strdup(comment);
76 void vik_track_set_description(VikTrack *tr, const gchar *description)
78 if ( tr->description )
79 g_free ( tr->description );
81 if ( description && description[0] != '\0' )
82 tr->description = g_strdup(description);
84 tr->description = NULL;
87 void vik_track_ref(VikTrack *tr)
92 void vik_track_set_property_dialog(VikTrack *tr, GtkWidget *dialog)
94 /* Warning: does not check for existing dialog */
95 tr->property_dialog = dialog;
98 void vik_track_clear_property_dialog(VikTrack *tr)
100 tr->property_dialog = NULL;
103 void vik_track_free(VikTrack *tr)
105 if ( tr->ref_count-- > 1 )
111 g_free ( tr->comment );
112 if ( tr->description )
113 g_free ( tr->description );
114 g_list_foreach ( tr->trackpoints, (GFunc) g_free, NULL );
115 g_list_free( tr->trackpoints );
116 if (tr->property_dialog)
117 if ( GTK_IS_WIDGET(tr->property_dialog) )
118 gtk_widget_destroy ( GTK_WIDGET(tr->property_dialog) );
124 * @tr: The Track to copy
125 * @copy_points: Whether to copy the track points or not
127 * Normally for copying the track it's best to copy all the trackpoints
128 * However for some operations such as splitting tracks the trackpoints will be managed separately, so no need to copy them.
130 * Returns: the copied VikTrack
132 VikTrack *vik_track_copy ( const VikTrack *tr, gboolean copy_points )
134 VikTrack *new_tr = vik_track_new();
135 VikTrackpoint *new_tp;
136 GList *tp_iter = tr->trackpoints;
137 new_tr->visible = tr->visible;
138 new_tr->is_route = tr->is_route;
139 new_tr->has_color = tr->has_color;
140 new_tr->color = tr->color;
141 new_tr->bbox = tr->bbox;
142 new_tr->trackpoints = NULL;
147 new_tp = g_malloc ( sizeof ( VikTrackpoint ) );
148 *new_tp = *((VikTrackpoint *)(tp_iter->data));
149 new_tr->trackpoints = g_list_append ( new_tr->trackpoints, new_tp );
150 tp_iter = tp_iter->next;
153 vik_track_set_name(new_tr,tr->name);
154 vik_track_set_comment(new_tr,tr->comment);
155 vik_track_set_description(new_tr,tr->description);
159 VikTrackpoint *vik_trackpoint_new()
161 VikTrackpoint *tp = g_malloc0(sizeof(VikTrackpoint));
164 tp->altitude = VIK_DEFAULT_ALTITUDE;
165 tp->hdop = VIK_DEFAULT_DOP;
166 tp->vdop = VIK_DEFAULT_DOP;
167 tp->pdop = VIK_DEFAULT_DOP;
171 void vik_trackpoint_free(VikTrackpoint *tp)
176 VikTrackpoint *vik_trackpoint_copy(VikTrackpoint *tp)
178 VikTrackpoint *rv = vik_trackpoint_new();
183 gdouble vik_track_get_length(const VikTrack *tr)
186 if ( tr->trackpoints )
188 GList *iter = tr->trackpoints->next;
191 if ( ! VIK_TRACKPOINT(iter->data)->newsegment )
192 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
193 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
200 gdouble vik_track_get_length_including_gaps(const VikTrack *tr)
203 if ( tr->trackpoints )
205 GList *iter = tr->trackpoints->next;
208 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
209 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
216 gulong vik_track_get_tp_count(const VikTrack *tr)
219 GList *iter = tr->trackpoints;
228 gulong vik_track_get_dup_point_count ( const VikTrack *tr )
231 GList *iter = tr->trackpoints;
234 if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
235 &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
243 * Deletes adjacent points that have the same position
244 * Returns the number of points that were deleted
246 gulong vik_track_remove_dup_points ( VikTrack *tr )
249 GList *iter = tr->trackpoints;
252 if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
253 &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
256 // Maintain track segments
257 if ( VIK_TRACKPOINT(iter->next->data)->newsegment && (iter->next)->next )
258 VIK_TRACKPOINT(((iter->next)->next)->data)->newsegment = TRUE;
260 vik_trackpoint_free ( iter->next->data );
261 tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next );
267 // NB isn't really be necessary as removing duplicate points shouldn't alter the bounds!
268 vik_track_calculate_bounds ( tr );
274 * Get a count of trackpoints with the same defined timestamp
275 * Note is using timestamps with a resolution with 1 second
277 gulong vik_track_get_same_time_point_count ( const VikTrack *tr )
280 GList *iter = tr->trackpoints;
283 ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
284 VIK_TRACKPOINT(iter->next->data)->has_timestamp ) &&
285 ( VIK_TRACKPOINT(iter->data)->timestamp ==
286 VIK_TRACKPOINT(iter->next->data)->timestamp) )
294 * Deletes adjacent points that have the same defined timestamp
295 * Returns the number of points that were deleted
297 gulong vik_track_remove_same_time_points ( VikTrack *tr )
300 GList *iter = tr->trackpoints;
303 ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
304 VIK_TRACKPOINT(iter->next->data)->has_timestamp ) &&
305 ( VIK_TRACKPOINT(iter->data)->timestamp ==
306 VIK_TRACKPOINT(iter->next->data)->timestamp) ) {
310 // Maintain track segments
311 if ( VIK_TRACKPOINT(iter->next->data)->newsegment && (iter->next)->next )
312 VIK_TRACKPOINT(((iter->next)->next)->data)->newsegment = TRUE;
314 vik_trackpoint_free ( iter->next->data );
315 tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next );
321 vik_track_calculate_bounds ( tr );
327 * Deletes all 'extra' trackpoint information
328 * such as time stamps, speed, course etc...
330 void vik_track_to_routepoints ( VikTrack *tr )
332 GList *iter = tr->trackpoints;
335 // c.f. with vik_trackpoint_new()
337 VIK_TRACKPOINT(iter->data)->has_timestamp = FALSE;
338 VIK_TRACKPOINT(iter->data)->timestamp = 0;
339 VIK_TRACKPOINT(iter->data)->speed = NAN;
340 VIK_TRACKPOINT(iter->data)->course = NAN;
341 VIK_TRACKPOINT(iter->data)->hdop = VIK_DEFAULT_DOP;
342 VIK_TRACKPOINT(iter->data)->vdop = VIK_DEFAULT_DOP;
343 VIK_TRACKPOINT(iter->data)->pdop = VIK_DEFAULT_DOP;
344 VIK_TRACKPOINT(iter->data)->nsats = 0;
345 VIK_TRACKPOINT(iter->data)->fix_mode = VIK_GPS_MODE_NOT_SEEN;
351 guint vik_track_get_segment_count(const VikTrack *tr)
354 GList *iter = tr->trackpoints;
357 while ( (iter = iter->next) )
359 if ( VIK_TRACKPOINT(iter->data)->newsegment )
365 VikTrack **vik_track_split_into_segments(VikTrack *t, guint *ret_len)
370 guint segs = vik_track_get_segment_count(t);
379 rv = g_malloc ( segs * sizeof(VikTrack *) );
380 tr = vik_track_copy ( t, TRUE );
382 iter = tr->trackpoints;
385 while ( (iter = iter->next) )
387 if ( VIK_TRACKPOINT(iter->data)->newsegment )
389 iter->prev->next = NULL;
391 rv[i] = vik_track_copy ( tr, FALSE );
392 rv[i]->trackpoints = iter;
394 vik_track_calculate_bounds ( rv[i] );
404 * Simply remove any subsequent segment markers in a track to form one continuous track
405 * Return the number of segments merged
407 guint vik_track_merge_segments(VikTrack *tr)
410 GList *iter = tr->trackpoints;
414 // Always skip the first point as this should be the first segment
417 while ( (iter = iter->next) )
419 if ( VIK_TRACKPOINT(iter->data)->newsegment ) {
420 VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
427 void vik_track_reverse ( VikTrack *tr )
429 if ( ! tr->trackpoints )
432 tr->trackpoints = g_list_reverse(tr->trackpoints);
434 /* fix 'newsegment' */
435 GList *iter = g_list_last ( tr->trackpoints );
438 if ( ! iter->next ) /* last segment, was first, cancel newsegment. */
439 VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
440 if ( ! iter->prev ) /* first segment by convention has newsegment flag. */
441 VIK_TRACKPOINT(iter->data)->newsegment = TRUE;
442 else if ( VIK_TRACKPOINT(iter->data)->newsegment && iter->next )
444 VIK_TRACKPOINT(iter->next->data)->newsegment = TRUE;
445 VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
451 gdouble vik_track_get_average_speed(const VikTrack *tr)
455 if ( tr->trackpoints )
457 GList *iter = tr->trackpoints->next;
460 if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
461 VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
462 (! VIK_TRACKPOINT(iter->data)->newsegment) )
464 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
465 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
466 time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
471 return (time == 0) ? 0 : ABS(len/time);
475 * Based on a simple average speed, but with a twist - to give a moving average.
476 * . GPSs often report a moving average in their statistics output
477 * . bicycle speedos often don't factor in time when stopped - hence reporting a moving average for speed
479 * Often GPS track will record every second but not when stationary
480 * This method doesn't use samples that differ over the specified time limit - effectively skipping that time chunk from the total time
482 * Suggest to use 60 seconds as the stop length (as the default used in the TrackWaypoint draw stops factor)
484 gdouble vik_track_get_average_speed_moving (const VikTrack *tr, int stop_length_seconds)
488 if ( tr->trackpoints )
490 GList *iter = tr->trackpoints->next;
493 if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
494 VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
495 (! VIK_TRACKPOINT(iter->data)->newsegment) )
497 if ( ( VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp ) < stop_length_seconds ) {
498 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
499 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
501 time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
507 return (time == 0) ? 0 : ABS(len/time);
510 gdouble vik_track_get_max_speed(const VikTrack *tr)
512 gdouble maxspeed = 0.0, speed = 0.0;
513 if ( tr->trackpoints )
515 GList *iter = tr->trackpoints->next;
518 if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
519 VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
520 (! VIK_TRACKPOINT(iter->data)->newsegment) )
522 speed = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) )
523 / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
524 if ( speed > maxspeed )
533 void vik_track_convert ( VikTrack *tr, VikCoordMode dest_mode )
535 GList *iter = tr->trackpoints;
538 vik_coord_convert ( &(VIK_TRACKPOINT(iter->data)->coord), dest_mode );
543 /* I understood this when I wrote it ... maybe ... Basically it eats up the
544 * proper amounts of length on the track and averages elevation over that. */
545 gdouble *vik_track_make_elevation_map ( const VikTrack *tr, guint16 num_chunks )
548 gdouble total_length, chunk_length, current_dist, current_area_under_curve, current_seg_length, dist_along_seg = 0.0;
549 gdouble altitude1, altitude2;
550 guint16 current_chunk;
551 gboolean ignore_it = FALSE;
553 GList *iter = tr->trackpoints;
555 if (!iter || !iter->next) /* zero- or one-point track */
558 { /* test if there's anything worth calculating */
559 gboolean okay = FALSE;
562 // Sometimes a GPS device (or indeed any random file) can have stupid numbers for elevations
563 // Since when is 9.9999e+24 a valid elevation!!
564 // This can happen when a track (with no elevations) is uploaded to a GPS device and then redownloaded (e.g. using a Garmin Legend EtrexHCx)
565 // Some protection against trying to work with crazily massive numbers (otherwise get SIGFPE, Arithmetic exception)
566 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE &&
567 VIK_TRACKPOINT(iter->data)->altitude < 1E9 ) {
576 iter = tr->trackpoints;
578 g_assert ( num_chunks < 16000 );
580 pts = g_malloc ( sizeof(gdouble) * num_chunks );
582 total_length = vik_track_get_length_including_gaps ( tr );
583 chunk_length = total_length / num_chunks;
585 /* Zero chunk_length (eg, track of 2 tp with the same loc) will cause crash */
586 if (chunk_length <= 0) {
592 current_area_under_curve = 0;
594 current_seg_length = 0;
596 current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
597 &(VIK_TRACKPOINT(iter->next->data)->coord) );
598 altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
599 altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
602 while ( current_chunk < num_chunks ) {
604 /* go along current seg */
605 if ( current_seg_length && (current_seg_length - dist_along_seg) > chunk_length ) {
606 dist_along_seg += chunk_length;
610 * /x altitude = alt_at_pt_1 + alt_at_pt_2 / 2 = altitude1 + slope * dist_value_of_pt_inbetween_pt1_and_pt2
611 * /xx avg altitude = area under curve / chunk len
612 *pt1 *xxx avg altitude = altitude1 + (altitude2-altitude1)/(current_seg_length)*(dist_along_seg + (chunk_len/2))
618 // Seemly can't determine average for this section - so use last known good value (much better than just sticking in zero)
619 pts[current_chunk] = altitude1;
621 pts[current_chunk] = altitude1 + (altitude2-altitude1)*((dist_along_seg - (chunk_length/2))/current_seg_length);
625 /* finish current seg */
626 if ( current_seg_length ) {
627 gdouble altitude_at_dist_along_seg = altitude1 + (altitude2-altitude1)/(current_seg_length)*dist_along_seg;
628 current_dist = current_seg_length - dist_along_seg;
629 current_area_under_curve = current_dist*(altitude_at_dist_along_seg + altitude2)*0.5;
630 } else { current_dist = current_area_under_curve = 0; } /* should only happen if first current_seg_length == 0 */
632 /* get intervening segs */
634 while ( iter && iter->next ) {
635 current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
636 &(VIK_TRACKPOINT(iter->next->data)->coord) );
637 altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
638 altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
639 ignore_it = VIK_TRACKPOINT(iter->next->data)->newsegment;
641 if ( chunk_length - current_dist >= current_seg_length ) {
642 current_dist += current_seg_length;
643 current_area_under_curve += current_seg_length * (altitude1+altitude2) * 0.5;
651 dist_along_seg = chunk_length - current_dist;
652 if ( ignore_it || ( iter && !iter->next ) ) {
653 pts[current_chunk] = current_area_under_curve / current_dist;
656 for (i = current_chunk + 1; i < num_chunks; i++)
657 pts[i] = pts[current_chunk];
662 current_area_under_curve += dist_along_seg * (altitude1 + (altitude2 - altitude1)*dist_along_seg/current_seg_length);
663 pts[current_chunk] = current_area_under_curve / chunk_length;
675 void vik_track_get_total_elevation_gain(const VikTrack *tr, gdouble *up, gdouble *down)
679 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE )
681 GList *iter = tr->trackpoints->next;
684 diff = VIK_TRACKPOINT(iter->data)->altitude - VIK_TRACKPOINT(iter->prev->data)->altitude;
692 *up = *down = VIK_DEFAULT_ALTITUDE;
695 gdouble *vik_track_make_gradient_map ( const VikTrack *tr, guint16 num_chunks )
699 gdouble total_length, chunk_length, current_gradient;
700 gdouble altitude1, altitude2;
701 guint16 current_chunk;
703 g_assert ( num_chunks < 16000 );
705 total_length = vik_track_get_length_including_gaps ( tr );
706 chunk_length = total_length / num_chunks;
708 /* Zero chunk_length (eg, track of 2 tp with the same loc) will cause crash */
709 if (chunk_length <= 0) {
713 altitudes = vik_track_make_elevation_map (tr, num_chunks);
714 if (altitudes == NULL) {
718 current_gradient = 0.0;
719 pts = g_malloc ( sizeof(gdouble) * num_chunks );
720 for (current_chunk = 0; current_chunk < (num_chunks - 1); current_chunk++) {
721 altitude1 = altitudes[current_chunk];
722 altitude2 = altitudes[current_chunk + 1];
723 current_gradient = 100.0 * (altitude2 - altitude1) / chunk_length;
725 pts[current_chunk] = current_gradient;
728 pts[current_chunk] = current_gradient;
733 /* by Alex Foobarian */
734 gdouble *vik_track_make_speed_map ( const VikTrack *tr, guint16 num_chunks )
737 gdouble duration, chunk_dur;
739 int i, pt_count, numpts, index;
742 if ( ! tr->trackpoints )
745 g_assert ( num_chunks < 16000 );
747 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
748 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
751 if ( !t1 || !t2 || !duration )
755 g_warning("negative duration: unsorted trackpoint timestamps?");
758 pt_count = vik_track_get_tp_count(tr);
760 v = g_malloc ( sizeof(gdouble) * num_chunks );
761 chunk_dur = duration / num_chunks;
763 s = g_malloc(sizeof(double) * pt_count);
764 t = g_malloc(sizeof(double) * pt_count);
766 iter = tr->trackpoints->next;
769 t[0] = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
772 s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
773 t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
778 /* In the following computation, we iterate through periods of time of duration chunk_dur.
779 * The first period begins at the beginning of the track. The last period ends at the end of the track.
781 index = 0; /* index of the current trackpoint. */
782 for (i = 0; i < num_chunks; i++) {
783 /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
784 * find the first trackpoint outside the current interval, averaging the speeds between intermediate trackpoints.
786 if (t[0] + i*chunk_dur >= t[index]) {
787 gdouble acc_t = 0, acc_s = 0;
788 while (t[0] + i*chunk_dur >= t[index]) {
789 acc_s += (s[index+1]-s[index]);
790 acc_t += (t[index+1]-t[index]);
808 * Make a distance/time map, heavily based on the vik_track_make_speed_map method
810 gdouble *vik_track_make_distance_map ( const VikTrack *tr, guint16 num_chunks )
813 gdouble duration, chunk_dur;
815 int i, pt_count, numpts, index;
818 if ( ! tr->trackpoints )
821 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
822 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
825 if ( !t1 || !t2 || !duration )
829 g_warning("negative duration: unsorted trackpoint timestamps?");
832 pt_count = vik_track_get_tp_count(tr);
834 v = g_malloc ( sizeof(gdouble) * num_chunks );
835 chunk_dur = duration / num_chunks;
837 s = g_malloc(sizeof(double) * pt_count);
838 t = g_malloc(sizeof(double) * pt_count);
840 iter = tr->trackpoints->next;
843 t[0] = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
846 s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
847 t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
852 /* In the following computation, we iterate through periods of time of duration chunk_dur.
853 * The first period begins at the beginning of the track. The last period ends at the end of the track.
855 index = 0; /* index of the current trackpoint. */
856 for (i = 0; i < num_chunks; i++) {
857 /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
858 * find the first trackpoint outside the current interval, averaging the distance between intermediate trackpoints.
860 if (t[0] + i*chunk_dur >= t[index]) {
861 gdouble acc_s = 0; // No need for acc_t
862 while (t[0] + i*chunk_dur >= t[index]) {
863 acc_s += (s[index+1]-s[index]);
866 // The only bit that's really different from the speed map - just keep an accululative record distance
867 v[i] = i ? v[i-1]+acc_s : acc_s;
882 * This uses the 'time' based method to make the graph, (which is a simpler compared to the elevation/distance)
883 * This results in a slightly blocky graph when it does not have many trackpoints: <60
884 * NB Somehow the elevation/distance applies some kind of smoothing algorithm,
885 * but I don't think any one understands it any more (I certainly don't ATM)
887 gdouble *vik_track_make_elevation_time_map ( const VikTrack *tr, guint16 num_chunks )
890 gdouble duration, chunk_dur;
891 GList *iter = tr->trackpoints;
893 if (!iter || !iter->next) /* zero- or one-point track */
896 /* test if there's anything worth calculating */
897 gboolean okay = FALSE;
899 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
908 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
909 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
912 if ( !t1 || !t2 || !duration )
916 g_warning("negative duration: unsorted trackpoint timestamps?");
919 gint pt_count = vik_track_get_tp_count(tr);
921 // Reset iterator back to the beginning
922 iter = tr->trackpoints;
924 gdouble *pts = g_malloc ( sizeof(gdouble) * num_chunks ); // The return altitude values
925 gdouble *s = g_malloc(sizeof(double) * pt_count); // calculation altitudes
926 gdouble *t = g_malloc(sizeof(double) * pt_count); // calculation times
928 chunk_dur = duration / num_chunks;
930 s[0] = VIK_TRACKPOINT(iter->data)->altitude;
931 t[0] = VIK_TRACKPOINT(iter->data)->timestamp;
932 iter = tr->trackpoints->next;
935 s[numpts] = VIK_TRACKPOINT(iter->data)->altitude;
936 t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
941 /* In the following computation, we iterate through periods of time of duration chunk_dur.
942 * The first period begins at the beginning of the track. The last period ends at the end of the track.
944 gint index = 0; /* index of the current trackpoint. */
946 for (i = 0; i < num_chunks; i++) {
947 /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
948 * find the first trackpoint outside the current interval, averaging the heights between intermediate trackpoints.
950 if (t[0] + i*chunk_dur >= t[index]) {
951 gdouble acc_s = s[index]; // initialise to first point
952 while (t[0] + i*chunk_dur >= t[index]) {
953 acc_s += (s[index+1]-s[index]);
972 * Make a speed/distance map
974 gdouble *vik_track_make_speed_dist_map ( const VikTrack *tr, guint16 num_chunks )
978 gint i, pt_count, numpts, index;
980 gdouble duration, total_length, chunk_length;
982 if ( ! tr->trackpoints )
985 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
986 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
989 if ( !t1 || !t2 || !duration )
993 g_warning("negative duration: unsorted trackpoint timestamps?");
997 total_length = vik_track_get_length_including_gaps ( tr );
998 chunk_length = total_length / num_chunks;
999 pt_count = vik_track_get_tp_count(tr);
1001 if (chunk_length <= 0) {
1005 v = g_malloc ( sizeof(gdouble) * num_chunks );
1006 s = g_malloc ( sizeof(double) * pt_count );
1007 t = g_malloc ( sizeof(double) * pt_count );
1009 // No special handling of segments ATM...
1010 iter = tr->trackpoints->next;
1013 t[0] = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1016 s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
1017 t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
1022 // Iterate through a portion of the track to get an average speed for that part
1023 // This will essentially interpolate between segments, which I think is right given the usage of 'get_length_including_gaps'
1024 index = 0; /* index of the current trackpoint. */
1025 for (i = 0; i < num_chunks; i++) {
1026 // Similar to the make_speed_map, but instead of using a time chunk, use a distance chunk
1027 if (s[0] + i*chunk_length >= s[index]) {
1028 gdouble acc_t = 0, acc_s = 0;
1029 while (s[0] + i*chunk_length >= s[index]) {
1030 acc_s += (s[index+1]-s[index]);
1031 acc_t += (t[index+1]-t[index]);
1048 /* by Alex Foobarian */
1049 VikTrackpoint *vik_track_get_closest_tp_by_percentage_dist ( VikTrack *tr, gdouble reldist, gdouble *meters_from_start )
1051 gdouble dist = vik_track_get_length_including_gaps(tr) * reldist;
1052 gdouble current_dist = 0.0;
1053 gdouble current_inc = 0.0;
1054 if ( tr->trackpoints )
1056 GList *iter = tr->trackpoints->next;
1057 GList *last_iter = NULL;
1058 gdouble last_dist = 0.0;
1061 current_inc = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
1062 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
1063 last_dist = current_dist;
1064 current_dist += current_inc;
1065 if ( current_dist >= dist )
1070 if (!iter) { /* passing the end the track */
1072 if (meters_from_start)
1073 *meters_from_start = last_dist;
1074 return(VIK_TRACKPOINT(last_iter->data));
1079 /* we've gone past the dist already, was prev trackpoint closer? */
1080 /* should do a vik_coord_average_weighted() thingy. */
1081 if ( iter->prev && abs(current_dist-current_inc-dist) < abs(current_dist-dist) ) {
1082 if (meters_from_start)
1083 *meters_from_start = last_dist;
1087 if (meters_from_start)
1088 *meters_from_start = current_dist;
1090 return VIK_TRACKPOINT(iter->data);
1096 VikTrackpoint *vik_track_get_closest_tp_by_percentage_time ( VikTrack *tr, gdouble reltime, time_t *seconds_from_start )
1098 time_t t_pos, t_start, t_end, t_total;
1099 t_start = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1100 t_end = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1101 t_total = t_end - t_start;
1103 t_pos = t_start + t_total * reltime;
1105 if ( !tr->trackpoints )
1108 GList *iter = tr->trackpoints;
1111 if (VIK_TRACKPOINT(iter->data)->timestamp == t_pos)
1113 if (VIK_TRACKPOINT(iter->data)->timestamp > t_pos) {
1114 if (iter->prev == NULL) /* first trackpoint */
1116 time_t t_before = t_pos - VIK_TRACKPOINT(iter->prev)->timestamp;
1117 time_t t_after = VIK_TRACKPOINT(iter->data)->timestamp - t_pos;
1118 if (t_before <= t_after)
1122 else if ((iter->next == NULL) && (t_pos < (VIK_TRACKPOINT(iter->data)->timestamp + 3))) /* last trackpoint: accommodate for round-off */
1129 if (seconds_from_start)
1130 *seconds_from_start = VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1131 return VIK_TRACKPOINT(iter->data);
1134 VikTrackpoint* vik_track_get_tp_by_max_speed ( const VikTrack *tr )
1136 gdouble maxspeed = 0.0, speed = 0.0;
1138 if ( !tr->trackpoints )
1141 GList *iter = tr->trackpoints;
1142 VikTrackpoint *max_speed_tp = NULL;
1146 if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
1147 VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
1148 (! VIK_TRACKPOINT(iter->data)->newsegment) ) {
1149 speed = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) )
1150 / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
1151 if ( speed > maxspeed ) {
1153 max_speed_tp = VIK_TRACKPOINT(iter->data);
1163 return max_speed_tp;
1166 VikTrackpoint* vik_track_get_tp_by_max_alt ( const VikTrack *tr )
1168 gdouble maxalt = -5000.0;
1169 if ( !tr->trackpoints )
1172 GList *iter = tr->trackpoints;
1173 VikTrackpoint *max_alt_tp = NULL;
1176 if ( VIK_TRACKPOINT(iter->data)->altitude > maxalt ) {
1177 maxalt = VIK_TRACKPOINT(iter->data)->altitude;
1178 max_alt_tp = VIK_TRACKPOINT(iter->data);
1189 VikTrackpoint* vik_track_get_tp_by_min_alt ( const VikTrack *tr )
1191 gdouble minalt = 25000.0;
1192 if ( !tr->trackpoints )
1195 GList *iter = tr->trackpoints;
1196 VikTrackpoint *min_alt_tp = NULL;
1199 if ( VIK_TRACKPOINT(iter->data)->altitude < minalt ) {
1200 minalt = VIK_TRACKPOINT(iter->data)->altitude;
1201 min_alt_tp = VIK_TRACKPOINT(iter->data);
1212 gboolean vik_track_get_minmax_alt ( const VikTrack *tr, gdouble *min_alt, gdouble *max_alt )
1216 if ( tr && tr->trackpoints && tr->trackpoints->data && (VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE) ) {
1217 GList *iter = tr->trackpoints->next;
1221 tmp_alt = VIK_TRACKPOINT(iter->data)->altitude;
1222 if ( tmp_alt > *max_alt )
1224 if ( tmp_alt < *min_alt )
1233 void vik_track_marshall ( VikTrack *tr, guint8 **data, guint *datalen)
1236 GByteArray *b = g_byte_array_new();
1240 g_byte_array_append(b, (guint8 *)tr, sizeof(*tr));
1242 /* we'll fill out number of trackpoints later */
1244 g_byte_array_append(b, (guint8 *)&len, sizeof(len));
1246 tps = tr->trackpoints;
1249 g_byte_array_append(b, (guint8 *)tps->data, sizeof(VikTrackpoint));
1253 *(guint *)(b->data + intp) = ntp;
1255 // This allocates space for variant sized strings
1256 // and copies that amount of data from the track to byte array
1257 #define vtm_append(s) \
1258 len = (s) ? strlen(s)+1 : 0; \
1259 g_byte_array_append(b, (guint8 *)&len, sizeof(len)); \
1260 if (s) g_byte_array_append(b, (guint8 *)s, len);
1262 vtm_append(tr->name);
1263 vtm_append(tr->comment);
1264 vtm_append(tr->description);
1268 g_byte_array_free(b, FALSE);
1272 * Take a byte array and convert it into a Track
1274 VikTrack *vik_track_unmarshall (guint8 *data, guint datalen)
1277 VikTrack *new_tr = vik_track_new();
1278 VikTrackpoint *new_tp;
1282 /* basic properties: */
1283 new_tr->visible = ((VikTrack *)data)->visible;
1284 new_tr->is_route = ((VikTrack *)data)->is_route;
1285 new_tr->has_color = ((VikTrack *)data)->has_color;
1286 new_tr->color = ((VikTrack *)data)->color;
1287 new_tr->bbox = ((VikTrack *)data)->bbox;
1289 data += sizeof(*new_tr);
1291 ntp = *(guint *)data;
1292 data += sizeof(ntp);
1294 for (i=0; i<ntp; i++) {
1295 new_tp = vik_trackpoint_new();
1296 memcpy(new_tp, data, sizeof(*new_tp));
1297 data += sizeof(*new_tp);
1298 new_tr->trackpoints = g_list_append(new_tr->trackpoints, new_tp);
1301 #define vtu_get(s) \
1302 len = *(guint *)data; \
1303 data += sizeof(len); \
1305 (s) = g_strdup((gchar *)data); \
1311 vtu_get(new_tr->name);
1312 vtu_get(new_tr->comment);
1313 vtu_get(new_tr->description);
1319 * (Re)Calculate the bounds of the given track,
1320 * updating the track's bounds data.
1321 * This should be called whenever a track's trackpoints are changed
1323 void vik_track_calculate_bounds ( VikTrack *trk )
1326 tp_iter = trk->trackpoints;
1328 struct LatLon topleft, bottomright, ll;
1330 // Set bounds to first point
1332 vik_coord_to_latlon ( &(VIK_TRACKPOINT(tp_iter->data)->coord), &topleft );
1333 vik_coord_to_latlon ( &(VIK_TRACKPOINT(tp_iter->data)->coord), &bottomright );
1337 // See if this trackpoint increases the track bounds.
1339 vik_coord_to_latlon ( &(VIK_TRACKPOINT(tp_iter->data)->coord), &ll );
1341 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
1342 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
1343 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
1344 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
1346 tp_iter = tp_iter->next;
1349 g_debug ( g_strdup_printf("Bounds of track: '%s' is: %f,%f to: %f,%f", trk->name, topleft.lat, topleft.lon, bottomright.lat, bottomright.lon ) );
1351 trk->bbox.north = topleft.lat;
1352 trk->bbox.east = bottomright.lon;
1353 trk->bbox.south = bottomright.lat;
1354 trk->bbox.west = topleft.lon;
1360 void vik_track_apply_dem_data ( VikTrack *tr )
1364 tp_iter = tr->trackpoints;
1366 /* TODO: of the 4 possible choices we have for choosing an elevation
1367 * (trackpoint in between samples), choose the one with the least elevation change
1369 elev = a_dems_get_elev_by_coord ( &(VIK_TRACKPOINT(tp_iter->data)->coord), VIK_DEM_INTERPOL_BEST );
1370 if ( elev != VIK_DEM_INVALID_ELEVATION )
1371 VIK_TRACKPOINT(tp_iter->data)->altitude = elev;
1372 tp_iter = tp_iter->next;
1377 * vik_track_apply_dem_data_last_trackpoint:
1378 * Apply DEM data (if available) - to only the last trackpoint
1380 void vik_track_apply_dem_data_last_trackpoint ( VikTrack *tr )
1383 if ( tr->trackpoints ) {
1384 /* As in vik_track_apply_dem_data above - use 'best' interpolation method */
1385 elev = a_dems_get_elev_by_coord ( &(VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->coord), VIK_DEM_INTERPOL_BEST );
1386 if ( elev != VIK_DEM_INVALID_ELEVATION )
1387 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->altitude = elev;
1392 * vik_track_steal_and_append_trackpoints:
1394 * appends t2 to t1, leaving t2 with no trackpoints
1396 void vik_track_steal_and_append_trackpoints ( VikTrack *t1, VikTrack *t2 )
1398 if ( t1->trackpoints ) {
1399 t1->trackpoints = g_list_concat ( t1->trackpoints, t2->trackpoints );
1401 t1->trackpoints = t2->trackpoints;
1402 t2->trackpoints = NULL;
1404 // Trackpoints updated - so update the bounds
1405 vik_track_calculate_bounds ( t1 );
1409 * vik_track_cut_back_to_double_point:
1411 * starting at the end, looks backwards for the last "double point", a duplicate trackpoint.
1412 * If there is no double point, deletes all the trackpoints.
1414 * Returns: the new end of the track (or the start if there are no double points)
1416 VikCoord *vik_track_cut_back_to_double_point ( VikTrack *tr )
1418 GList *iter = tr->trackpoints;
1423 while ( iter->next )
1427 while ( iter->prev ) {
1428 if ( vik_coord_equals((VikCoord *)iter->data, (VikCoord *)iter->prev->data) ) {
1429 GList *prev = iter->prev;
1431 rv = g_malloc(sizeof(VikCoord));
1432 *rv = *((VikCoord *) iter->data);
1434 /* truncate trackpoint list */
1435 iter->prev = NULL; /* pretend it's the end */
1436 g_list_foreach ( iter, (GFunc) g_free, NULL );
1437 g_list_free( iter );
1446 /* no double point found! */
1447 rv = g_malloc(sizeof(VikCoord));
1448 *rv = *((VikCoord *) tr->trackpoints->data);
1449 g_list_foreach ( tr->trackpoints, (GFunc) g_free, NULL );
1450 g_list_free( tr->trackpoints );
1451 tr->trackpoints = NULL;