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
));
120 tp
->altitude
= VIK_DEFAULT_ALTITUDE
;
121 tp
->hdop
= VIK_DEFAULT_DOP
;
122 tp
->vdop
= VIK_DEFAULT_DOP
;
123 tp
->pdop
= VIK_DEFAULT_DOP
;
127 void vik_trackpoint_free(VikTrackpoint
*tp
)
132 VikTrackpoint
*vik_trackpoint_copy(VikTrackpoint
*tp
)
134 VikTrackpoint
*rv
= vik_trackpoint_new();
139 gdouble
vik_track_get_length(const VikTrack
*tr
)
142 if ( tr
->trackpoints
)
144 GList
*iter
= tr
->trackpoints
->next
;
147 if ( ! VIK_TRACKPOINT(iter
->data
)->newsegment
)
148 len
+= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
149 &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
) );
156 gdouble
vik_track_get_length_including_gaps(const VikTrack
*tr
)
159 if ( tr
->trackpoints
)
161 GList
*iter
= tr
->trackpoints
->next
;
164 len
+= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
165 &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
) );
172 gulong
vik_track_get_tp_count(const VikTrack
*tr
)
175 GList
*iter
= tr
->trackpoints
;
184 gulong
vik_track_get_dup_point_count ( const VikTrack
*tr
)
187 GList
*iter
= tr
->trackpoints
;
190 if ( iter
->next
&& vik_coord_equals ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
191 &(VIK_TRACKPOINT(iter
->next
->data
)->coord
) ) )
198 void vik_track_remove_dup_points ( VikTrack
*tr
)
200 GList
*iter
= tr
->trackpoints
;
203 if ( iter
->next
&& vik_coord_equals ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
204 &(VIK_TRACKPOINT(iter
->next
->data
)->coord
) ) )
206 g_free ( iter
->next
->data
);
207 tr
->trackpoints
= g_list_delete_link ( tr
->trackpoints
, iter
->next
);
214 guint
vik_track_get_segment_count(const VikTrack
*tr
)
217 GList
*iter
= tr
->trackpoints
;
220 while ( (iter
= iter
->next
) )
222 if ( VIK_TRACKPOINT(iter
->data
)->newsegment
)
228 VikTrack
**vik_track_split_into_segments(VikTrack
*t
, guint
*ret_len
)
233 guint segs
= vik_track_get_segment_count(t
);
242 rv
= g_malloc ( segs
* sizeof(VikTrack
*) );
243 tr
= vik_track_copy ( t
);
245 iter
= tr
->trackpoints
;
248 while ( (iter
= iter
->next
) )
250 if ( VIK_TRACKPOINT(iter
->data
)->newsegment
)
252 iter
->prev
->next
= NULL
;
254 rv
[i
] = vik_track_new();
256 vik_track_set_comment ( rv
[i
], tr
->comment
);
257 rv
[i
]->visible
= tr
->visible
;
258 rv
[i
]->trackpoints
= iter
;
266 void vik_track_reverse ( VikTrack
*tr
)
269 tr
->trackpoints
= g_list_reverse(tr
->trackpoints
);
271 /* fix 'newsegment' */
272 iter
= g_list_last ( tr
->trackpoints
);
275 if ( ! iter
->next
) /* last segment, was first, cancel newsegment. */
276 VIK_TRACKPOINT(iter
->data
)->newsegment
= FALSE
;
277 if ( ! iter
->prev
) /* first segment by convention has newsegment flag. */
278 VIK_TRACKPOINT(iter
->data
)->newsegment
= TRUE
;
279 else if ( VIK_TRACKPOINT(iter
->data
)->newsegment
&& iter
->next
)
281 VIK_TRACKPOINT(iter
->next
->data
)->newsegment
= TRUE
;
282 VIK_TRACKPOINT(iter
->data
)->newsegment
= FALSE
;
288 gdouble
vik_track_get_average_speed(const VikTrack
*tr
)
292 if ( tr
->trackpoints
)
294 GList
*iter
= tr
->trackpoints
->next
;
297 if ( VIK_TRACKPOINT(iter
->data
)->has_timestamp
&&
298 VIK_TRACKPOINT(iter
->prev
->data
)->has_timestamp
&&
299 (! VIK_TRACKPOINT(iter
->data
)->newsegment
) )
301 len
+= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
302 &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
) );
303 time
+= ABS(VIK_TRACKPOINT(iter
->data
)->timestamp
- VIK_TRACKPOINT(iter
->prev
->data
)->timestamp
);
308 return (time
== 0) ? 0 : ABS(len
/time
);
311 gdouble
vik_track_get_max_speed(const VikTrack
*tr
)
313 gdouble maxspeed
= 0.0, speed
= 0.0;
314 if ( tr
->trackpoints
)
316 GList
*iter
= tr
->trackpoints
->next
;
319 if ( VIK_TRACKPOINT(iter
->data
)->has_timestamp
&&
320 VIK_TRACKPOINT(iter
->prev
->data
)->has_timestamp
&&
321 (! VIK_TRACKPOINT(iter
->data
)->newsegment
) )
323 speed
= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
), &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
) )
324 / ABS(VIK_TRACKPOINT(iter
->data
)->timestamp
- VIK_TRACKPOINT(iter
->prev
->data
)->timestamp
);
325 if ( speed
> maxspeed
)
334 void vik_track_convert ( VikTrack
*tr
, VikCoordMode dest_mode
)
336 GList
*iter
= tr
->trackpoints
;
339 vik_coord_convert ( &(VIK_TRACKPOINT(iter
->data
)->coord
), dest_mode
);
344 /* I understood this when I wrote it ... maybe ... Basically it eats up the
345 * proper amounts of length on the track and averages elevation over that. */
346 gdouble
*vik_track_make_elevation_map ( const VikTrack
*tr
, guint16 num_chunks
)
349 gdouble total_length
, chunk_length
, current_dist
, current_area_under_curve
, current_seg_length
, dist_along_seg
= 0.0;
350 gdouble altitude1
, altitude2
;
351 guint16 current_chunk
;
352 gboolean ignore_it
= FALSE
;
354 GList
*iter
= tr
->trackpoints
;
356 if (!iter
|| !iter
->next
) /* zero- or one-point track */
359 { /* test if there's anything worth calculating */
360 gboolean okay
= FALSE
;
363 if ( VIK_TRACKPOINT(iter
->data
)->altitude
!= VIK_DEFAULT_ALTITUDE
) {
372 iter
= tr
->trackpoints
;
374 g_assert ( num_chunks
< 16000 );
376 pts
= g_malloc ( sizeof(gdouble
) * num_chunks
);
378 total_length
= vik_track_get_length_including_gaps ( tr
);
379 chunk_length
= total_length
/ num_chunks
;
381 /* Zero chunk_length (eg, track of 2 tp with the same loc) will cause crash */
382 if (chunk_length
<= 0) {
388 current_area_under_curve
= 0;
390 current_seg_length
= 0;
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
;
398 while ( current_chunk
< num_chunks
) {
400 /* go along current seg */
401 if ( current_seg_length
&& (current_seg_length
- dist_along_seg
) > chunk_length
) {
402 dist_along_seg
+= chunk_length
;
406 * /x altitude = alt_at_pt_1 + alt_at_pt_2 / 2 = altitude1 + slope * dist_value_of_pt_inbetween_pt1_and_pt2
407 * /xx avg altitude = area under curve / chunk len
408 *pt1 *xxx avg altitude = altitude1 + (altitude2-altitude1)/(current_seg_length)*(dist_along_seg + (chunk_len/2))
414 pts
[current_chunk
] = VIK_DEFAULT_ALTITUDE
;
416 pts
[current_chunk
] = altitude1
+ (altitude2
-altitude1
)*((dist_along_seg
- (chunk_length
/2))/current_seg_length
);
420 /* finish current seg */
421 if ( current_seg_length
) {
422 gdouble altitude_at_dist_along_seg
= altitude1
+ (altitude2
-altitude1
)/(current_seg_length
)*dist_along_seg
;
423 current_dist
= current_seg_length
- dist_along_seg
;
424 current_area_under_curve
= current_dist
*(altitude_at_dist_along_seg
+ altitude2
)*0.5;
425 } else { current_dist
= current_area_under_curve
= 0; } /* should only happen if first current_seg_length == 0 */
427 /* get intervening segs */
429 while ( iter
&& iter
->next
) {
430 current_seg_length
= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
431 &(VIK_TRACKPOINT(iter
->next
->data
)->coord
) );
432 altitude1
= VIK_TRACKPOINT(iter
->data
)->altitude
;
433 altitude2
= VIK_TRACKPOINT(iter
->next
->data
)->altitude
;
434 ignore_it
= VIK_TRACKPOINT(iter
->next
->data
)->newsegment
;
436 if ( chunk_length
- current_dist
>= current_seg_length
) {
437 current_dist
+= current_seg_length
;
438 current_area_under_curve
+= current_seg_length
* (altitude1
+altitude2
) * 0.5;
446 dist_along_seg
= chunk_length
- current_dist
;
447 if ( ignore_it
|| !iter
->next
) {
448 pts
[current_chunk
] = current_area_under_curve
/ current_dist
;
451 for (i
= current_chunk
+ 1; i
< num_chunks
; i
++)
452 pts
[i
] = pts
[current_chunk
];
457 current_area_under_curve
+= dist_along_seg
* (altitude1
+ (altitude2
- altitude1
)*dist_along_seg
/current_seg_length
);
458 pts
[current_chunk
] = current_area_under_curve
/ chunk_length
;
470 void vik_track_get_total_elevation_gain(const VikTrack
*tr
, gdouble
*up
, gdouble
*down
)
474 if ( tr
->trackpoints
&& VIK_TRACKPOINT(tr
->trackpoints
->data
)->altitude
!= VIK_DEFAULT_ALTITUDE
)
476 GList
*iter
= tr
->trackpoints
->next
;
479 diff
= VIK_TRACKPOINT(iter
->data
)->altitude
- VIK_TRACKPOINT(iter
->prev
->data
)->altitude
;
487 *up
= *down
= VIK_DEFAULT_ALTITUDE
;
491 /* by Alex Foobarian */
492 gdouble
*vik_track_make_speed_map ( const VikTrack
*tr
, guint16 num_chunks
)
495 gdouble duration
, chunk_dur
;
497 int i
, pt_count
, numpts
, index
;
500 if ( ! tr
->trackpoints
)
503 g_assert ( num_chunks
< 16000 );
505 t1
= VIK_TRACKPOINT(tr
->trackpoints
->data
)->timestamp
;
506 t2
= VIK_TRACKPOINT(g_list_last(tr
->trackpoints
)->data
)->timestamp
;
509 if ( !t1
|| !t2
|| !duration
)
513 g_warning("negative duration: unsorted trackpoint timestamps?");
516 pt_count
= vik_track_get_tp_count(tr
);
518 v
= g_malloc ( sizeof(gdouble
) * num_chunks
);
519 chunk_dur
= duration
/ num_chunks
;
521 s
= g_malloc(sizeof(double) * pt_count
);
522 t
= g_malloc(sizeof(double) * pt_count
);
524 iter
= tr
->trackpoints
->next
;
527 t
[0] = VIK_TRACKPOINT(iter
->prev
->data
)->timestamp
;
530 s
[numpts
] = s
[numpts
-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
), &(VIK_TRACKPOINT(iter
->data
)->coord
) );
531 t
[numpts
] = VIK_TRACKPOINT(iter
->data
)->timestamp
;
536 /* In the following computation, we iterate through periods of time of duration chunk_dur.
537 * The first period begins at the beginning of the track. The last period ends at the end of the track.
539 index
= 0; /* index of the current trackpoint. */
540 for (i
= 0; i
< num_chunks
; i
++) {
541 /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
542 * find the first trackpoint outside the current interval, averaging the speeds between intermediate trackpoints.
544 if (t
[0] + i
*chunk_dur
>= t
[index
]) {
545 gdouble acc_t
= 0, acc_s
= 0;
547 while (t
[0] + i
*chunk_dur
>= t
[index
]) {
548 acc_s
+= (s
[index
+1]-s
[index
]);
549 acc_t
+= (t
[index
+1]-t
[index
]);
567 /* by Alex Foobarian */
568 VikTrackpoint
*vik_track_get_closest_tp_by_percentage_dist ( VikTrack
*tr
, gdouble reldist
, gdouble
*meters_from_start
)
570 gdouble dist
= vik_track_get_length_including_gaps(tr
) * reldist
;
571 gdouble current_dist
= 0.0;
572 gdouble current_inc
= 0.0;
573 if ( tr
->trackpoints
)
575 GList
*iter
= tr
->trackpoints
->next
;
576 GList
*last_iter
= NULL
;
577 gdouble last_dist
= 0.0;
580 current_inc
= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
581 &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
) );
582 last_dist
= current_dist
;
583 current_dist
+= current_inc
;
584 if ( current_dist
>= dist
)
589 if (!iter
) { /* passing the end the track */
591 if (meters_from_start
)
592 *meters_from_start
= last_dist
;
593 return(VIK_TRACKPOINT(last_iter
->data
));
598 /* we've gone past the dist already, was prev trackpoint closer? */
599 /* should do a vik_coord_average_weighted() thingy. */
600 if ( iter
->prev
&& abs(current_dist
-current_inc
-dist
) < abs(current_dist
-dist
) ) {
601 if (meters_from_start
)
602 *meters_from_start
= last_dist
;
606 if (meters_from_start
)
607 *meters_from_start
= current_dist
;
609 return VIK_TRACKPOINT(iter
->data
);
615 VikTrackpoint
*vik_track_get_closest_tp_by_percentage_time ( VikTrack
*tr
, gdouble reltime
, time_t *seconds_from_start
)
617 time_t t_pos
, t_start
, t_end
, t_total
;
618 t_start
= VIK_TRACKPOINT(tr
->trackpoints
->data
)->timestamp
;
619 t_end
= VIK_TRACKPOINT(g_list_last(tr
->trackpoints
)->data
)->timestamp
;
620 t_total
= t_end
- t_start
;
622 t_pos
= t_start
+ t_total
* reltime
;
624 if ( !tr
->trackpoints
)
627 GList
*iter
= tr
->trackpoints
;
630 if (VIK_TRACKPOINT(iter
->data
)->timestamp
== t_pos
)
632 if (VIK_TRACKPOINT(iter
->data
)->timestamp
> t_pos
) {
633 if (iter
->prev
== NULL
) /* first trackpoint */
635 time_t t_before
= t_pos
- VIK_TRACKPOINT(iter
->prev
)->timestamp
;
636 time_t t_after
= VIK_TRACKPOINT(iter
->data
)->timestamp
- t_pos
;
637 if (t_before
<= t_after
)
641 else if ((iter
->next
== NULL
) && (t_pos
< (VIK_TRACKPOINT(iter
->data
)->timestamp
+ 3))) /* last trackpoint: accommodate for round-off */
648 if (seconds_from_start
)
649 *seconds_from_start
= VIK_TRACKPOINT(iter
->data
)->timestamp
- VIK_TRACKPOINT(tr
->trackpoints
->data
)->timestamp
;
650 return VIK_TRACKPOINT(iter
->data
);
653 gboolean
vik_track_get_minmax_alt ( const VikTrack
*tr
, gdouble
*min_alt
, gdouble
*max_alt
)
657 if ( tr
&& tr
->trackpoints
&& tr
->trackpoints
->data
&& (VIK_TRACKPOINT(tr
->trackpoints
->data
)->altitude
!= VIK_DEFAULT_ALTITUDE
) ) {
658 GList
*iter
= tr
->trackpoints
->next
;
662 tmp_alt
= VIK_TRACKPOINT(iter
->data
)->altitude
;
663 if ( tmp_alt
> *max_alt
)
665 if ( tmp_alt
< *min_alt
)
674 void vik_track_marshall ( VikTrack
*tr
, guint8
**data
, guint
*datalen
)
677 GByteArray
*b
= g_byte_array_new();
681 g_byte_array_append(b
, (guint8
*)tr
, sizeof(*tr
));
683 /* we'll fill out number of trackpoints later */
685 g_byte_array_append(b
, (guint8
*)&len
, sizeof(len
));
687 tps
= tr
->trackpoints
;
690 g_byte_array_append(b
, (guint8
*)tps
->data
, sizeof(VikTrackpoint
));
694 *(guint
*)(b
->data
+ intp
) = ntp
;
696 len
= (tr
->comment
) ? strlen(tr
->comment
)+1 : 0;
697 g_byte_array_append(b
, (guint8
*)&len
, sizeof(len
));
698 if (tr
->comment
) g_byte_array_append(b
, (guint8
*)tr
->comment
, len
);
702 g_byte_array_free(b
, FALSE
);
705 VikTrack
*vik_track_unmarshall (guint8
*data
, guint datalen
)
708 VikTrack
*new_tr
= vik_track_new();
709 VikTrackpoint
*new_tp
;
713 /* only the visibility is needed */
714 new_tr
->visible
= ((VikTrack
*)data
)->visible
;
715 data
+= sizeof(*new_tr
);
717 ntp
= *(guint
*)data
;
720 for (i
=0; i
<ntp
; i
++) {
721 new_tp
= vik_trackpoint_new();
722 memcpy(new_tp
, data
, sizeof(*new_tp
));
723 data
+= sizeof(*new_tp
);
724 new_tr
->trackpoints
= g_list_append(new_tr
->trackpoints
, new_tp
);
727 len
= *(guint
*)data
;
730 new_tr
->comment
= g_strdup((gchar
*)data
);
735 void vik_track_apply_dem_data ( VikTrack
*tr
)
739 tp_iter
= tr
->trackpoints
;
741 /* TODO: of the 4 possible choices we have for choosing an elevation
742 * (trackpoint in between samples), choose the one with the least elevation change
744 elev
= a_dems_get_elev_by_coord ( &(VIK_TRACKPOINT(tp_iter
->data
)->coord
), VIK_DEM_INTERPOL_BEST
);
745 if ( elev
!= VIK_DEM_INVALID_ELEVATION
)
746 VIK_TRACKPOINT(tp_iter
->data
)->altitude
= elev
;
747 tp_iter
= tp_iter
->next
;
751 /* appends t2 to t1, leaving t2 with no trackpoints */
752 void vik_track_steal_and_append_trackpoints ( VikTrack
*t1
, VikTrack
*t2
)
754 if ( t1
->trackpoints
) {
755 GList
*tpiter
= t1
->trackpoints
;
756 while ( tpiter
->next
)
757 tpiter
= tpiter
->next
;
758 tpiter
->next
= t2
->trackpoints
;
759 t2
->trackpoints
->prev
= tpiter
;
761 t1
->trackpoints
= t2
->trackpoints
;
762 t2
->trackpoints
= NULL
;
765 /* starting at the end, looks backwards for the last "double point", a duplicate trackpoint.
766 * this is indicative of magic scissors continued use. If there is no double point,
767 * deletes all the trackpoints. Returns the new end of the track (or the start if
768 * there are no double points
770 VikCoord
*vik_track_cut_back_to_double_point ( VikTrack
*tr
)
772 GList
*iter
= tr
->trackpoints
;
781 while ( iter
->prev
) {
782 if ( vik_coord_equals((VikCoord
*)iter
->data
, (VikCoord
*)iter
->prev
->data
) ) {
783 GList
*prev
= iter
->prev
;
785 rv
= g_malloc(sizeof(VikCoord
));
786 *rv
= *((VikCoord
*) iter
->data
);
788 /* truncate trackpoint list */
789 iter
->prev
= NULL
; /* pretend it's the end */
790 g_list_foreach ( iter
, (GFunc
) g_free
, NULL
);
800 /* no double point found! */
801 rv
= g_malloc(sizeof(VikCoord
));
802 *rv
= *((VikCoord
*) tr
->trackpoints
->data
);
803 g_list_foreach ( tr
->trackpoints
, (GFunc
) g_free
, NULL
);
804 g_list_free( tr
->trackpoints
);
805 tr
->trackpoints
= NULL
;