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
41 VikTrack
*vik_track_new()
43 VikTrack
*tr
= g_malloc0 ( sizeof ( VikTrack
) );
48 void vik_track_set_comment_no_copy(VikTrack
*tr
, gchar
*comment
)
51 g_free ( tr
->comment
);
52 tr
->comment
= comment
;
56 void vik_track_set_comment(VikTrack
*tr
, const gchar
*comment
)
59 g_free ( tr
->comment
);
61 if ( comment
&& comment
[0] != '\0' )
62 tr
->comment
= g_strdup(comment
);
67 void vik_track_ref(VikTrack
*tr
)
72 void vik_track_set_property_dialog(VikTrack
*tr
, GtkWidget
*dialog
)
74 /* Warning: does not check for existing dialog */
75 tr
->property_dialog
= dialog
;
78 void vik_track_clear_property_dialog(VikTrack
*tr
)
80 tr
->property_dialog
= NULL
;
83 void vik_track_free(VikTrack
*tr
)
85 if ( tr
->ref_count
-- > 1 )
89 g_free ( tr
->comment
);
90 g_list_foreach ( tr
->trackpoints
, (GFunc
) g_free
, NULL
);
91 g_list_free( tr
->trackpoints
);
92 if (tr
->property_dialog
)
93 gtk_widget_destroy ( GTK_WIDGET(tr
->property_dialog
) );
97 VikTrack
*vik_track_copy ( const VikTrack
*tr
)
99 VikTrack
*new_tr
= vik_track_new();
100 VikTrackpoint
*new_tp
;
101 GList
*tp_iter
= tr
->trackpoints
;
102 new_tr
->visible
= tr
->visible
;
103 new_tr
->trackpoints
= NULL
;
106 new_tp
= g_malloc ( sizeof ( VikTrackpoint
) );
107 *new_tp
= *((VikTrackpoint
*)(tp_iter
->data
));
108 new_tr
->trackpoints
= g_list_append ( new_tr
->trackpoints
, new_tp
);
109 tp_iter
= tp_iter
->next
;
111 vik_track_set_comment(new_tr
,tr
->comment
);
115 VikTrackpoint
*vik_trackpoint_new()
117 VikTrackpoint
*tp
= g_malloc0(sizeof(VikTrackpoint
));
118 tp
->extended
= FALSE
;
124 void vik_trackpoint_free(VikTrackpoint
*tp
)
129 VikTrackpoint
*vik_trackpoint_copy(VikTrackpoint
*tp
)
131 VikTrackpoint
*rv
= vik_trackpoint_new();
136 gdouble
vik_track_get_length(const VikTrack
*tr
)
139 if ( tr
->trackpoints
)
141 GList
*iter
= tr
->trackpoints
->next
;
144 if ( ! VIK_TRACKPOINT(iter
->data
)->newsegment
)
145 len
+= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
146 &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
) );
153 gdouble
vik_track_get_length_including_gaps(const VikTrack
*tr
)
156 if ( tr
->trackpoints
)
158 GList
*iter
= tr
->trackpoints
->next
;
161 len
+= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
162 &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
) );
169 gulong
vik_track_get_tp_count(const VikTrack
*tr
)
172 GList
*iter
= tr
->trackpoints
;
181 gulong
vik_track_get_dup_point_count ( const VikTrack
*tr
)
184 GList
*iter
= tr
->trackpoints
;
187 if ( iter
->next
&& vik_coord_equals ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
188 &(VIK_TRACKPOINT(iter
->next
->data
)->coord
) ) )
195 void vik_track_remove_dup_points ( VikTrack
*tr
)
197 GList
*iter
= tr
->trackpoints
;
200 if ( iter
->next
&& vik_coord_equals ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
201 &(VIK_TRACKPOINT(iter
->next
->data
)->coord
) ) )
203 g_free ( iter
->next
->data
);
204 tr
->trackpoints
= g_list_delete_link ( tr
->trackpoints
, iter
->next
);
211 guint
vik_track_get_segment_count(const VikTrack
*tr
)
214 GList
*iter
= tr
->trackpoints
;
217 while ( (iter
= iter
->next
) )
219 if ( VIK_TRACKPOINT(iter
->data
)->newsegment
)
225 VikTrack
**vik_track_split_into_segments(VikTrack
*t
, guint
*ret_len
)
230 guint segs
= vik_track_get_segment_count(t
);
239 rv
= g_malloc ( segs
* sizeof(VikTrack
*) );
240 tr
= vik_track_copy ( t
);
242 iter
= tr
->trackpoints
;
245 while ( (iter
= iter
->next
) )
247 if ( VIK_TRACKPOINT(iter
->data
)->newsegment
)
249 iter
->prev
->next
= NULL
;
251 rv
[i
] = vik_track_new();
253 vik_track_set_comment ( rv
[i
], tr
->comment
);
254 rv
[i
]->visible
= tr
->visible
;
255 rv
[i
]->trackpoints
= iter
;
263 void vik_track_reverse ( VikTrack
*tr
)
266 tr
->trackpoints
= g_list_reverse(tr
->trackpoints
);
268 /* fix 'newsegment' */
269 iter
= g_list_last ( tr
->trackpoints
);
272 if ( ! iter
->next
) /* last segment, was first, cancel newsegment. */
273 VIK_TRACKPOINT(iter
->data
)->newsegment
= FALSE
;
274 if ( ! iter
->prev
) /* first segment by convention has newsegment flag. */
275 VIK_TRACKPOINT(iter
->data
)->newsegment
= TRUE
;
276 else if ( VIK_TRACKPOINT(iter
->data
)->newsegment
&& iter
->next
)
278 VIK_TRACKPOINT(iter
->next
->data
)->newsegment
= TRUE
;
279 VIK_TRACKPOINT(iter
->data
)->newsegment
= FALSE
;
285 gdouble
vik_track_get_average_speed(const VikTrack
*tr
)
289 if ( tr
->trackpoints
)
291 GList
*iter
= tr
->trackpoints
->next
;
294 if ( VIK_TRACKPOINT(iter
->data
)->has_timestamp
&&
295 VIK_TRACKPOINT(iter
->prev
->data
)->has_timestamp
&&
296 (! VIK_TRACKPOINT(iter
->data
)->newsegment
) )
298 len
+= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
299 &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
) );
300 time
+= ABS(VIK_TRACKPOINT(iter
->data
)->timestamp
- VIK_TRACKPOINT(iter
->prev
->data
)->timestamp
);
305 return (time
== 0) ? 0 : ABS(len
/time
);
308 gdouble
vik_track_get_max_speed(const VikTrack
*tr
)
310 gdouble maxspeed
= 0.0, speed
= 0.0;
311 if ( tr
->trackpoints
)
313 GList
*iter
= tr
->trackpoints
->next
;
316 if ( VIK_TRACKPOINT(iter
->data
)->has_timestamp
&&
317 VIK_TRACKPOINT(iter
->prev
->data
)->has_timestamp
&&
318 (! VIK_TRACKPOINT(iter
->data
)->newsegment
) )
320 speed
= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
), &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
) )
321 / ABS(VIK_TRACKPOINT(iter
->data
)->timestamp
- VIK_TRACKPOINT(iter
->prev
->data
)->timestamp
);
322 if ( speed
> maxspeed
)
331 void vik_track_convert ( VikTrack
*tr
, VikCoordMode dest_mode
)
333 GList
*iter
= tr
->trackpoints
;
336 vik_coord_convert ( &(VIK_TRACKPOINT(iter
->data
)->coord
), dest_mode
);
341 /* I understood this when I wrote it ... maybe ... Basically it eats up the
342 * proper amounts of length on the track and averages elevation over that. */
343 gdouble
*vik_track_make_elevation_map ( const VikTrack
*tr
, guint16 num_chunks
)
346 gdouble total_length
, chunk_length
, current_dist
, current_area_under_curve
, current_seg_length
, dist_along_seg
= 0.0;
347 gdouble altitude1
, altitude2
;
348 guint16 current_chunk
;
349 gboolean ignore_it
= FALSE
;
351 GList
*iter
= tr
->trackpoints
;
353 if (!iter
|| !iter
->next
) /* zero- or one-point track */
356 { /* test if there's anything worth calculating */
357 gboolean okay
= FALSE
;
360 if ( VIK_TRACKPOINT(iter
->data
)->altitude
!= VIK_DEFAULT_ALTITUDE
) {
369 iter
= tr
->trackpoints
;
371 g_assert ( num_chunks
< 16000 );
373 pts
= g_malloc ( sizeof(gdouble
) * num_chunks
);
375 total_length
= vik_track_get_length_including_gaps ( tr
);
376 chunk_length
= total_length
/ num_chunks
;
378 /* Zero chunk_length (eg, track of 2 tp with the same loc) will cause crash */
379 if (chunk_length
<= 0)
383 current_area_under_curve
= 0;
385 current_seg_length
= 0;
387 current_seg_length
= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
388 &(VIK_TRACKPOINT(iter
->next
->data
)->coord
) );
389 altitude1
= VIK_TRACKPOINT(iter
->data
)->altitude
;
390 altitude2
= VIK_TRACKPOINT(iter
->next
->data
)->altitude
;
393 while ( current_chunk
< num_chunks
) {
395 /* go along current seg */
396 if ( current_seg_length
&& (current_seg_length
- dist_along_seg
) > chunk_length
) {
397 dist_along_seg
+= chunk_length
;
401 * /x altitude = alt_at_pt_1 + alt_at_pt_2 / 2 = altitude1 + slope * dist_value_of_pt_inbetween_pt1_and_pt2
402 * /xx avg altitude = area under curve / chunk len
403 *pt1 *xxx avg altitude = altitude1 + (altitude2-altitude1)/(current_seg_length)*(dist_along_seg + (chunk_len/2))
409 pts
[current_chunk
] = VIK_DEFAULT_ALTITUDE
;
411 pts
[current_chunk
] = altitude1
+ (altitude2
-altitude1
)*((dist_along_seg
- (chunk_length
/2))/current_seg_length
);
415 /* finish current seg */
416 if ( current_seg_length
) {
417 gdouble altitude_at_dist_along_seg
= altitude1
+ (altitude2
-altitude1
)/(current_seg_length
)*dist_along_seg
;
418 current_dist
= current_seg_length
- dist_along_seg
;
419 current_area_under_curve
= current_dist
*(altitude_at_dist_along_seg
+ altitude2
)*0.5;
420 } else { current_dist
= current_area_under_curve
= 0; } /* should only happen if first current_seg_length == 0 */
422 /* get intervening segs */
424 while ( iter
&& iter
->next
) {
425 current_seg_length
= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
426 &(VIK_TRACKPOINT(iter
->next
->data
)->coord
) );
427 altitude1
= VIK_TRACKPOINT(iter
->data
)->altitude
;
428 altitude2
= VIK_TRACKPOINT(iter
->next
->data
)->altitude
;
429 ignore_it
= VIK_TRACKPOINT(iter
->next
->data
)->newsegment
;
431 if ( chunk_length
- current_dist
>= current_seg_length
) {
432 current_dist
+= current_seg_length
;
433 current_area_under_curve
+= current_seg_length
* (altitude1
+altitude2
) * 0.5;
441 dist_along_seg
= chunk_length
- current_dist
;
442 if ( ignore_it
|| !iter
->next
) {
443 pts
[current_chunk
] = current_area_under_curve
/ current_dist
;
446 for (i
= current_chunk
+ 1; i
< num_chunks
; i
++)
447 pts
[i
] = pts
[current_chunk
];
452 current_area_under_curve
+= dist_along_seg
* (altitude1
+ (altitude2
- altitude1
)*dist_along_seg
/current_seg_length
);
453 pts
[current_chunk
] = current_area_under_curve
/ chunk_length
;
465 void vik_track_get_total_elevation_gain(const VikTrack
*tr
, gdouble
*up
, gdouble
*down
)
469 if ( tr
->trackpoints
&& VIK_TRACKPOINT(tr
->trackpoints
->data
)->altitude
!= VIK_DEFAULT_ALTITUDE
)
471 GList
*iter
= tr
->trackpoints
->next
;
474 diff
= VIK_TRACKPOINT(iter
->data
)->altitude
- VIK_TRACKPOINT(iter
->prev
->data
)->altitude
;
482 *up
= *down
= VIK_DEFAULT_ALTITUDE
;
486 /* by Alex Foobarian */
487 gdouble
*vik_track_make_speed_map ( const VikTrack
*tr
, guint16 num_chunks
)
490 gdouble duration
, chunk_dur
;
492 int i
, pt_count
, numpts
, index
;
495 if ( ! tr
->trackpoints
)
498 g_assert ( num_chunks
< 16000 );
500 t1
= VIK_TRACKPOINT(tr
->trackpoints
->data
)->timestamp
;
501 t2
= VIK_TRACKPOINT(g_list_last(tr
->trackpoints
)->data
)->timestamp
;
504 if ( !t1
|| !t2
|| !duration
)
508 g_warning("negative duration: unsorted trackpoint timestamps?");
511 pt_count
= vik_track_get_tp_count(tr
);
513 v
= g_malloc ( sizeof(gdouble
) * num_chunks
);
514 chunk_dur
= duration
/ num_chunks
;
516 s
= g_malloc(sizeof(double) * pt_count
);
517 t
= g_malloc(sizeof(double) * pt_count
);
519 iter
= tr
->trackpoints
->next
;
522 t
[0] = VIK_TRACKPOINT(iter
->prev
->data
)->timestamp
;
525 s
[numpts
] = s
[numpts
-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
), &(VIK_TRACKPOINT(iter
->data
)->coord
) );
526 t
[numpts
] = VIK_TRACKPOINT(iter
->data
)->timestamp
;
531 /* In the following computation, we iterate through periods of time of duration chunk_dur.
532 * The first period begins at the beginning of the track. The last period ends at the end of the track.
534 index
= 0; /* index of the current trackpoint. */
535 for (i
= 0; i
< num_chunks
; i
++) {
536 /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
537 * find the first trackpoint outside the current interval, averaging the speeds between intermediate trackpoints.
539 if (t
[0] + i
*chunk_dur
>= t
[index
]) {
540 gdouble acc_t
= 0, acc_s
= 0;
542 while (t
[0] + i
*chunk_dur
>= t
[index
]) {
543 acc_s
+= (s
[index
+1]-s
[index
]);
544 acc_t
+= (t
[index
+1]-t
[index
]);
562 /* by Alex Foobarian */
563 VikTrackpoint
*vik_track_get_closest_tp_by_percentage_dist ( VikTrack
*tr
, gdouble reldist
, gdouble
*meters_from_start
)
565 gdouble dist
= vik_track_get_length_including_gaps(tr
) * reldist
;
566 gdouble current_dist
= 0.0;
567 gdouble current_inc
= 0.0;
568 if ( tr
->trackpoints
)
570 GList
*iter
= tr
->trackpoints
->next
;
571 GList
*last_iter
= NULL
;
572 gdouble last_dist
= 0.0;
575 current_inc
= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
576 &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
) );
577 last_dist
= current_dist
;
578 current_dist
+= current_inc
;
579 if ( current_dist
>= dist
)
584 if (!iter
) { /* passing the end the track */
586 if (meters_from_start
)
587 *meters_from_start
= last_dist
;
588 return(VIK_TRACKPOINT(last_iter
->data
));
593 /* we've gone past the dist already, was prev trackpoint closer? */
594 /* should do a vik_coord_average_weighted() thingy. */
595 if ( iter
->prev
&& abs(current_dist
-current_inc
-dist
) < abs(current_dist
-dist
) ) {
596 if (meters_from_start
)
597 *meters_from_start
= last_dist
;
601 if (meters_from_start
)
602 *meters_from_start
= current_dist
;
604 return VIK_TRACKPOINT(iter
->data
);
610 VikTrackpoint
*vik_track_get_closest_tp_by_percentage_time ( VikTrack
*tr
, gdouble reltime
, time_t *seconds_from_start
)
612 time_t t_pos
, t_start
, t_end
, t_total
;
613 t_start
= VIK_TRACKPOINT(tr
->trackpoints
->data
)->timestamp
;
614 t_end
= VIK_TRACKPOINT(g_list_last(tr
->trackpoints
)->data
)->timestamp
;
615 t_total
= t_end
- t_start
;
617 t_pos
= t_start
+ t_total
* reltime
;
619 if ( !tr
->trackpoints
)
622 GList
*iter
= tr
->trackpoints
;
625 if (VIK_TRACKPOINT(iter
->data
)->timestamp
== t_pos
)
627 if (VIK_TRACKPOINT(iter
->data
)->timestamp
> t_pos
) {
628 if (iter
->prev
== NULL
) /* first trackpoint */
630 time_t t_before
= t_pos
- VIK_TRACKPOINT(iter
->prev
)->timestamp
;
631 time_t t_after
= VIK_TRACKPOINT(iter
->data
)->timestamp
- t_pos
;
632 if (t_before
<= t_after
)
636 else if ((iter
->next
== NULL
) && (t_pos
< (VIK_TRACKPOINT(iter
->data
)->timestamp
+ 3))) /* last trackpoint: accommodate for round-off */
643 if (seconds_from_start
)
644 *seconds_from_start
= VIK_TRACKPOINT(iter
->data
)->timestamp
- VIK_TRACKPOINT(tr
->trackpoints
->data
)->timestamp
;
645 return VIK_TRACKPOINT(iter
->data
);
648 gboolean
vik_track_get_minmax_alt ( const VikTrack
*tr
, gdouble
*min_alt
, gdouble
*max_alt
)
652 if ( tr
&& tr
->trackpoints
&& tr
->trackpoints
->data
&& (VIK_TRACKPOINT(tr
->trackpoints
->data
)->altitude
!= VIK_DEFAULT_ALTITUDE
) ) {
653 GList
*iter
= tr
->trackpoints
->next
;
657 tmp_alt
= VIK_TRACKPOINT(iter
->data
)->altitude
;
658 if ( tmp_alt
> *max_alt
)
660 if ( tmp_alt
< *min_alt
)
669 void vik_track_marshall ( VikTrack
*tr
, guint8
**data
, guint
*datalen
)
672 GByteArray
*b
= g_byte_array_new();
676 g_byte_array_append(b
, (guint8
*)tr
, sizeof(*tr
));
678 /* we'll fill out number of trackpoints later */
680 g_byte_array_append(b
, (guint8
*)&len
, sizeof(len
));
682 tps
= tr
->trackpoints
;
685 g_byte_array_append(b
, (guint8
*)tps
->data
, sizeof(VikTrackpoint
));
689 *(guint
*)(b
->data
+ intp
) = ntp
;
691 len
= (tr
->comment
) ? strlen(tr
->comment
)+1 : 0;
692 g_byte_array_append(b
, (guint8
*)&len
, sizeof(len
));
693 if (tr
->comment
) g_byte_array_append(b
, (guint8
*)tr
->comment
, len
);
697 g_byte_array_free(b
, FALSE
);
700 VikTrack
*vik_track_unmarshall (guint8
*data
, guint datalen
)
703 VikTrack
*new_tr
= vik_track_new();
704 VikTrackpoint
*new_tp
;
708 /* only the visibility is needed */
709 new_tr
->visible
= ((VikTrack
*)data
)->visible
;
710 data
+= sizeof(*new_tr
);
712 ntp
= *(guint
*)data
;
715 for (i
=0; i
<ntp
; i
++) {
716 new_tp
= vik_trackpoint_new();
717 memcpy(new_tp
, data
, sizeof(*new_tp
));
718 data
+= sizeof(*new_tp
);
719 new_tr
->trackpoints
= g_list_append(new_tr
->trackpoints
, new_tp
);
722 len
= *(guint
*)data
;
725 new_tr
->comment
= g_strdup((gchar
*)data
);
730 void vik_track_apply_dem_data ( VikTrack
*tr
)
734 tp_iter
= tr
->trackpoints
;
736 /* TODO: of the 4 possible choices we have for choosing an elevation
737 * (trackpoint in between samples), choose the one with the least elevation change
739 elev
= a_dems_get_elev_by_coord ( &(VIK_TRACKPOINT(tp_iter
->data
)->coord
), VIK_DEM_INTERPOL_BEST
);
740 if ( elev
!= VIK_DEM_INVALID_ELEVATION
)
741 VIK_TRACKPOINT(tp_iter
->data
)->altitude
= elev
;
742 tp_iter
= tp_iter
->next
;
746 /* appends t2 to t1, leaving t2 with no trackpoints */
747 void vik_track_steal_and_append_trackpoints ( VikTrack
*t1
, VikTrack
*t2
)
749 if ( t1
->trackpoints
) {
750 GList
*tpiter
= t1
->trackpoints
;
751 while ( tpiter
->next
)
752 tpiter
= tpiter
->next
;
753 tpiter
->next
= t2
->trackpoints
;
754 t2
->trackpoints
->prev
= tpiter
;
756 t1
->trackpoints
= t2
->trackpoints
;
757 t2
->trackpoints
= NULL
;
760 /* starting at the end, looks backwards for the last "double point", a duplicate trackpoint.
761 * this is indicative of magic scissors continued use. If there is no double point,
762 * deletes all the trackpoints. Returns the new end of the track (or the start if
763 * there are no double points
765 VikCoord
*vik_track_cut_back_to_double_point ( VikTrack
*tr
)
767 GList
*iter
= tr
->trackpoints
;
776 while ( iter
->prev
) {
777 if ( vik_coord_equals((VikCoord
*)iter
->data
, (VikCoord
*)iter
->prev
->data
) ) {
778 GList
*prev
= iter
->prev
;
780 rv
= g_malloc(sizeof(VikCoord
));
781 *rv
= *((VikCoord
*) iter
->data
);
783 /* truncate trackpoint list */
784 iter
->prev
= NULL
; /* pretend it's the end */
785 g_list_foreach ( iter
, (GFunc
) g_free
, NULL
);
795 /* no double point found! */
796 rv
= g_malloc(sizeof(VikCoord
));
797 *rv
= *((VikCoord
*) tr
->trackpoints
->data
);
798 g_list_foreach ( tr
->trackpoints
, (GFunc
) g_free
, NULL
);
799 g_list_free( tr
->trackpoints
);
800 tr
->trackpoints
= NULL
;