]> git.street.me.uk Git - andy/viking.git/blame - src/viktrack.c
Only allow JPGs in the file selector for Geotagging.
[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>
0d2b891f 5 * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
50a14534
EB
6 *
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.
11 *
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.
16 *
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
20 *
21 */
8c00358d
GB
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
50a14534
EB
25
26#include <glib.h>
27#include <time.h>
bf35388d 28#include <stdlib.h>
8c00358d 29#ifdef HAVE_STRING_H
ddc47a46 30#include <string.h>
8c00358d
GB
31#endif
32#ifdef HAVE_MATH_H
bf35388d 33#include <math.h>
8c00358d
GB
34#endif
35
50a14534
EB
36#include "coords.h"
37#include "vikcoord.h"
38#include "viktrack.h"
39#include "globals.h"
ad0a8c2d 40#include "dems.h"
387ff7ac 41#include "settings.h"
50a14534
EB
42
43VikTrack *vik_track_new()
44{
8c4f1350 45 VikTrack *tr = g_malloc0 ( sizeof ( VikTrack ) );
50a14534
EB
46 tr->ref_count = 1;
47 return tr;
48}
49
387ff7ac
RN
50#define VIK_SETTINGS_TRACK_NAME_MODE "track_draw_name_mode"
51#define VIK_SETTINGS_TRACK_NUM_DIST_LABELS "track_number_dist_labels"
52
53/**
54 * vik_track_set_defaults:
55 *
56 * Set some default values for a track.
57 * ATM This uses the 'settings' method to get values,
58 * so there is no GUI way to control these yet...
59 */
60void vik_track_set_defaults(VikTrack *tr)
61{
62 gint tmp;
63 if ( a_settings_get_integer ( VIK_SETTINGS_TRACK_NAME_MODE, &tmp ) )
64 tr->draw_name_mode = tmp;
65
66 if ( a_settings_get_integer ( VIK_SETTINGS_TRACK_NUM_DIST_LABELS, &tmp ) )
67 tr->max_number_dist_labels = tmp;
68}
69
50a14534
EB
70void vik_track_set_comment_no_copy(VikTrack *tr, gchar *comment)
71{
72 if ( tr->comment )
73 g_free ( tr->comment );
74 tr->comment = comment;
75}
76
77
ce4bd1cf
RN
78void vik_track_set_name(VikTrack *tr, const gchar *name)
79{
80 if ( tr->name )
81 g_free ( tr->name );
82
27d267f4 83 tr->name = g_strdup(name);
ce4bd1cf
RN
84}
85
50a14534
EB
86void vik_track_set_comment(VikTrack *tr, const gchar *comment)
87{
88 if ( tr->comment )
89 g_free ( tr->comment );
90
91 if ( comment && comment[0] != '\0' )
92 tr->comment = g_strdup(comment);
93 else
94 tr->comment = NULL;
95}
96
6b2f262e
RN
97void vik_track_set_description(VikTrack *tr, const gchar *description)
98{
99 if ( tr->description )
100 g_free ( tr->description );
101
102 if ( description && description[0] != '\0' )
103 tr->description = g_strdup(description);
104 else
105 tr->description = NULL;
106}
107
50a14534
EB
108void vik_track_ref(VikTrack *tr)
109{
110 tr->ref_count++;
111}
112
21700912
QT
113void vik_track_set_property_dialog(VikTrack *tr, GtkWidget *dialog)
114{
115 /* Warning: does not check for existing dialog */
116 tr->property_dialog = dialog;
117}
118
119void vik_track_clear_property_dialog(VikTrack *tr)
120{
121 tr->property_dialog = NULL;
122}
123
50a14534
EB
124void vik_track_free(VikTrack *tr)
125{
126 if ( tr->ref_count-- > 1 )
127 return;
128
ce4bd1cf
RN
129 if ( tr->name )
130 g_free ( tr->name );
50a14534
EB
131 if ( tr->comment )
132 g_free ( tr->comment );
6b2f262e
RN
133 if ( tr->description )
134 g_free ( tr->description );
f629b00b 135 g_list_foreach ( tr->trackpoints, (GFunc) vik_trackpoint_free, NULL );
50a14534 136 g_list_free( tr->trackpoints );
21700912 137 if (tr->property_dialog)
63b31477
RN
138 if ( GTK_IS_WIDGET(tr->property_dialog) )
139 gtk_widget_destroy ( GTK_WIDGET(tr->property_dialog) );
50a14534
EB
140 g_free ( tr );
141}
142
03817fbf
RN
143/**
144 * vik_track_copy:
145 * @tr: The Track to copy
146 * @copy_points: Whether to copy the track points or not
147 *
148 * Normally for copying the track it's best to copy all the trackpoints
149 * However for some operations such as splitting tracks the trackpoints will be managed separately, so no need to copy them.
150 *
151 * Returns: the copied VikTrack
152 */
153VikTrack *vik_track_copy ( const VikTrack *tr, gboolean copy_points )
50a14534
EB
154{
155 VikTrack *new_tr = vik_track_new();
b45865b4 156 new_tr->name = g_strdup(tr->name);
50a14534 157 new_tr->visible = tr->visible;
0d2b891f 158 new_tr->is_route = tr->is_route;
387ff7ac
RN
159 new_tr->draw_name_mode = tr->draw_name_mode;
160 new_tr->max_number_dist_labels = tr->max_number_dist_labels;
b1453c16
RN
161 new_tr->has_color = tr->has_color;
162 new_tr->color = tr->color;
20981fd6 163 new_tr->bbox = tr->bbox;
50a14534 164 new_tr->trackpoints = NULL;
03817fbf 165 if ( copy_points )
50a14534 166 {
f629b00b 167 GList *tp_iter = tr->trackpoints;
03817fbf
RN
168 while ( tp_iter )
169 {
f629b00b
RN
170 VikTrackpoint *new_tp = vik_trackpoint_copy ( (VikTrackpoint*)(tp_iter->data) );
171 new_tr->trackpoints = g_list_prepend ( new_tr->trackpoints, new_tp );
03817fbf
RN
172 tp_iter = tp_iter->next;
173 }
f629b00b
RN
174 if ( new_tr->trackpoints )
175 new_tr->trackpoints = g_list_reverse ( new_tr->trackpoints );
50a14534 176 }
ce4bd1cf 177 vik_track_set_name(new_tr,tr->name);
50a14534 178 vik_track_set_comment(new_tr,tr->comment);
6b2f262e 179 vik_track_set_description(new_tr,tr->description);
50a14534
EB
180 return new_tr;
181}
182
183VikTrackpoint *vik_trackpoint_new()
184{
a2817d3c 185 VikTrackpoint *tp = g_malloc0(sizeof(VikTrackpoint));
a2817d3c
QT
186 tp->speed = NAN;
187 tp->course = NAN;
8541f2cf
T
188 tp->altitude = VIK_DEFAULT_ALTITUDE;
189 tp->hdop = VIK_DEFAULT_DOP;
190 tp->vdop = VIK_DEFAULT_DOP;
191 tp->pdop = VIK_DEFAULT_DOP;
a2817d3c 192 return tp;
50a14534
EB
193}
194
195void vik_trackpoint_free(VikTrackpoint *tp)
196{
b45865b4 197 g_free(tp->name);
50a14534
EB
198 g_free(tp);
199}
200
b45865b4
RN
201void vik_trackpoint_set_name(VikTrackpoint *tp, const gchar *name)
202{
203 if ( tp->name )
204 g_free ( tp->name );
205
206 // If the name is blank then completely remove it
207 if ( name && name[0] == '\0' )
208 tp->name = NULL;
209 else if ( name )
210 tp->name = g_strdup(name);
211 else
212 tp->name = NULL;
213}
214
50a14534
EB
215VikTrackpoint *vik_trackpoint_copy(VikTrackpoint *tp)
216{
f629b00b
RN
217 VikTrackpoint *new_tp = vik_trackpoint_new();
218 memcpy ( new_tp, tp, sizeof(VikTrackpoint) );
219 if ( tp->name )
220 new_tp->name = g_strdup (tp->name);
221 return new_tp;
50a14534
EB
222}
223
9bc95d58
RN
224/**
225 * track_recalculate_bounds_last_tp:
226 * @trk: The track to consider the recalculation on
227 *
228 * A faster bounds check, since it only considers the last track point
229 */
230static void track_recalculate_bounds_last_tp ( VikTrack *trk )
231{
232 GList *tpl = g_list_last ( trk->trackpoints );
233
234 if ( tpl ) {
235 struct LatLon ll;
236 // See if this trackpoint increases the track bounds and update if so
237 vik_coord_to_latlon ( &(VIK_TRACKPOINT(tpl->data)->coord), &ll );
238 if ( ll.lat > trk->bbox.north )
239 trk->bbox.north = ll.lat;
240 if ( ll.lon < trk->bbox.west )
241 trk->bbox.west = ll.lon;
242 if ( ll.lat < trk->bbox.south )
243 trk->bbox.south = ll.lat;
244 if ( ll.lon > trk->bbox.east )
245 trk->bbox.east = ll.lon;
246 }
247}
248
249/**
250 * vik_track_add_trackpoint:
251 * @tr: The track to which the trackpoint will be added
252 * @tp: The trackpoint to add
253 * @recalculate: Whether to perform any associated properties recalculations
254 * Generally one should avoid recalculation via this method if adding lots of points
255 * (But ensure calculate_bounds() is called after adding all points!!)
256 *
257 * The trackpoint is added to the end of the existing trackpoint list
258 */
259void vik_track_add_trackpoint ( VikTrack *tr, VikTrackpoint *tp, gboolean recalculate )
260{
261 tr->trackpoints = g_list_append ( tr->trackpoints, tp );
262 if ( recalculate )
263 track_recalculate_bounds_last_tp ( tr );
264}
265
db478c57
RN
266/**
267 * vik_track_get_length_to_trackpoint:
268 *
269 */
270gdouble vik_track_get_length_to_trackpoint (const VikTrack *tr, const VikTrackpoint *tp)
271{
272 gdouble len = 0.0;
273 if ( tr->trackpoints )
274 {
275 GList *iter = tr->trackpoints->next;
276 while (iter)
277 {
278 VikTrackpoint *tp1 = VIK_TRACKPOINT(iter->data);
279 if ( ! tp1->newsegment )
280 len += vik_coord_diff ( &(tp1->coord),
281 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
282
283 // Exit when we reach the desired point
284 if ( tp1 == tp )
285 break;
286
287 iter = iter->next;
288 }
289 }
290 return len;
291}
292
50a14534
EB
293gdouble vik_track_get_length(const VikTrack *tr)
294{
295 gdouble len = 0.0;
296 if ( tr->trackpoints )
297 {
298 GList *iter = tr->trackpoints->next;
299 while (iter)
300 {
301 if ( ! VIK_TRACKPOINT(iter->data)->newsegment )
302 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
303 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
304 iter = iter->next;
305 }
306 }
307 return len;
308}
309
310gdouble vik_track_get_length_including_gaps(const VikTrack *tr)
311{
312 gdouble len = 0.0;
313 if ( tr->trackpoints )
314 {
315 GList *iter = tr->trackpoints->next;
316 while (iter)
317 {
318 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
319 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
320 iter = iter->next;
321 }
322 }
323 return len;
324}
325
326gulong vik_track_get_tp_count(const VikTrack *tr)
327{
a619ecac 328 return g_list_length(tr->trackpoints);
50a14534
EB
329}
330
331gulong vik_track_get_dup_point_count ( const VikTrack *tr )
332{
333 gulong num = 0;
334 GList *iter = tr->trackpoints;
335 while ( iter )
336 {
337 if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
338 &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
339 num++;
340 iter = iter->next;
341 }
342 return num;
343}
344
4f48c541
RN
345/*
346 * Deletes adjacent points that have the same position
347 * Returns the number of points that were deleted
348 */
349gulong vik_track_remove_dup_points ( VikTrack *tr )
50a14534 350{
4f48c541 351 gulong num = 0;
50a14534
EB
352 GList *iter = tr->trackpoints;
353 while ( iter )
354 {
355 if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
356 &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
357 {
4f48c541 358 num++;
d6caf4c0
RN
359 // Maintain track segments
360 if ( VIK_TRACKPOINT(iter->next->data)->newsegment && (iter->next)->next )
361 VIK_TRACKPOINT(((iter->next)->next)->data)->newsegment = TRUE;
362
363 vik_trackpoint_free ( iter->next->data );
2a0cbd46
RN
364 tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next );
365 }
366 else
367 iter = iter->next;
368 }
20981fd6
RN
369
370 // NB isn't really be necessary as removing duplicate points shouldn't alter the bounds!
371 vik_track_calculate_bounds ( tr );
372
2a0cbd46
RN
373 return num;
374}
375
376/*
377 * Get a count of trackpoints with the same defined timestamp
378 * Note is using timestamps with a resolution with 1 second
379 */
380gulong vik_track_get_same_time_point_count ( const VikTrack *tr )
381{
382 gulong num = 0;
383 GList *iter = tr->trackpoints;
384 while ( iter ) {
385 if ( iter->next &&
386 ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
387 VIK_TRACKPOINT(iter->next->data)->has_timestamp ) &&
388 ( VIK_TRACKPOINT(iter->data)->timestamp ==
389 VIK_TRACKPOINT(iter->next->data)->timestamp) )
390 num++;
391 iter = iter->next;
392 }
393 return num;
394}
395
396/*
397 * Deletes adjacent points that have the same defined timestamp
398 * Returns the number of points that were deleted
399 */
400gulong vik_track_remove_same_time_points ( VikTrack *tr )
401{
402 gulong num = 0;
403 GList *iter = tr->trackpoints;
404 while ( iter ) {
405 if ( iter->next &&
406 ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
407 VIK_TRACKPOINT(iter->next->data)->has_timestamp ) &&
408 ( VIK_TRACKPOINT(iter->data)->timestamp ==
409 VIK_TRACKPOINT(iter->next->data)->timestamp) ) {
410
411 num++;
412
413 // Maintain track segments
414 if ( VIK_TRACKPOINT(iter->next->data)->newsegment && (iter->next)->next )
415 VIK_TRACKPOINT(((iter->next)->next)->data)->newsegment = TRUE;
416
417 vik_trackpoint_free ( iter->next->data );
50a14534
EB
418 tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next );
419 }
420 else
421 iter = iter->next;
422 }
20981fd6
RN
423
424 vik_track_calculate_bounds ( tr );
425
4f48c541 426 return num;
50a14534
EB
427}
428
2f5d7ea1
RN
429/*
430 * Deletes all 'extra' trackpoint information
431 * such as time stamps, speed, course etc...
432 */
433void vik_track_to_routepoints ( VikTrack *tr )
434{
435 GList *iter = tr->trackpoints;
436 while ( iter ) {
437
438 // c.f. with vik_trackpoint_new()
439
440 VIK_TRACKPOINT(iter->data)->has_timestamp = FALSE;
441 VIK_TRACKPOINT(iter->data)->timestamp = 0;
442 VIK_TRACKPOINT(iter->data)->speed = NAN;
443 VIK_TRACKPOINT(iter->data)->course = NAN;
444 VIK_TRACKPOINT(iter->data)->hdop = VIK_DEFAULT_DOP;
445 VIK_TRACKPOINT(iter->data)->vdop = VIK_DEFAULT_DOP;
446 VIK_TRACKPOINT(iter->data)->pdop = VIK_DEFAULT_DOP;
447 VIK_TRACKPOINT(iter->data)->nsats = 0;
448 VIK_TRACKPOINT(iter->data)->fix_mode = VIK_GPS_MODE_NOT_SEEN;
449
450 iter = iter->next;
451 }
452}
453
50a14534
EB
454guint vik_track_get_segment_count(const VikTrack *tr)
455{
456 guint num = 1;
457 GList *iter = tr->trackpoints;
458 if ( !iter )
459 return 0;
460 while ( (iter = iter->next) )
461 {
462 if ( VIK_TRACKPOINT(iter->data)->newsegment )
463 num++;
464 }
465 return num;
466}
467
468VikTrack **vik_track_split_into_segments(VikTrack *t, guint *ret_len)
469{
470 VikTrack **rv;
471 VikTrack *tr;
472 guint i;
473 guint segs = vik_track_get_segment_count(t);
474 GList *iter;
475
476 if ( segs < 2 )
477 {
478 *ret_len = 0;
479 return NULL;
480 }
481
482 rv = g_malloc ( segs * sizeof(VikTrack *) );
03817fbf 483 tr = vik_track_copy ( t, TRUE );
50a14534
EB
484 rv[0] = tr;
485 iter = tr->trackpoints;
486
487 i = 1;
488 while ( (iter = iter->next) )
489 {
490 if ( VIK_TRACKPOINT(iter->data)->newsegment )
491 {
492 iter->prev->next = NULL;
493 iter->prev = NULL;
03817fbf 494 rv[i] = vik_track_copy ( tr, FALSE );
50a14534 495 rv[i]->trackpoints = iter;
20981fd6
RN
496
497 vik_track_calculate_bounds ( rv[i] );
498
50a14534
EB
499 i++;
500 }
501 }
502 *ret_len = segs;
503 return rv;
504}
505
ce1c0489
RN
506/*
507 * Simply remove any subsequent segment markers in a track to form one continuous track
508 * Return the number of segments merged
509 */
510guint vik_track_merge_segments(VikTrack *tr)
511{
512 guint num = 0;
513 GList *iter = tr->trackpoints;
514 if ( !iter )
515 return num;
516
517 // Always skip the first point as this should be the first segment
518 iter = iter->next;
519
520 while ( (iter = iter->next) )
521 {
522 if ( VIK_TRACKPOINT(iter->data)->newsegment ) {
523 VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
524 num++;
525 }
526 }
527 return num;
528}
529
50a14534
EB
530void vik_track_reverse ( VikTrack *tr )
531{
3b5e6737
RN
532 if ( ! tr->trackpoints )
533 return;
534
50a14534
EB
535 tr->trackpoints = g_list_reverse(tr->trackpoints);
536
537 /* fix 'newsegment' */
3b5e6737 538 GList *iter = g_list_last ( tr->trackpoints );
50a14534
EB
539 while ( iter )
540 {
541 if ( ! iter->next ) /* last segment, was first, cancel newsegment. */
542 VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
543 if ( ! iter->prev ) /* first segment by convention has newsegment flag. */
544 VIK_TRACKPOINT(iter->data)->newsegment = TRUE;
545 else if ( VIK_TRACKPOINT(iter->data)->newsegment && iter->next )
546 {
547 VIK_TRACKPOINT(iter->next->data)->newsegment = TRUE;
548 VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
549 }
550 iter = iter->prev;
551 }
552}
553
554gdouble vik_track_get_average_speed(const VikTrack *tr)
555{
556 gdouble len = 0.0;
557 guint32 time = 0;
558 if ( tr->trackpoints )
559 {
560 GList *iter = tr->trackpoints->next;
561 while (iter)
562 {
563 if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
564 VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
565 (! VIK_TRACKPOINT(iter->data)->newsegment) )
566 {
567 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
568 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
569 time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
570 }
571 iter = iter->next;
572 }
573 }
574 return (time == 0) ? 0 : ABS(len/time);
575}
576
4c21c2fa
RN
577/**
578 * Based on a simple average speed, but with a twist - to give a moving average.
579 * . GPSs often report a moving average in their statistics output
580 * . bicycle speedos often don't factor in time when stopped - hence reporting a moving average for speed
581 *
582 * Often GPS track will record every second but not when stationary
583 * This method doesn't use samples that differ over the specified time limit - effectively skipping that time chunk from the total time
584 *
585 * Suggest to use 60 seconds as the stop length (as the default used in the TrackWaypoint draw stops factor)
586 */
587gdouble vik_track_get_average_speed_moving (const VikTrack *tr, int stop_length_seconds)
588{
589 gdouble len = 0.0;
590 guint32 time = 0;
591 if ( tr->trackpoints )
592 {
593 GList *iter = tr->trackpoints->next;
594 while (iter)
595 {
596 if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
597 VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
598 (! VIK_TRACKPOINT(iter->data)->newsegment) )
599 {
600 if ( ( VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp ) < stop_length_seconds ) {
601 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
602 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
603
604 time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
605 }
606 }
607 iter = iter->next;
608 }
609 }
610 return (time == 0) ? 0 : ABS(len/time);
611}
612
50a14534
EB
613gdouble vik_track_get_max_speed(const VikTrack *tr)
614{
615 gdouble maxspeed = 0.0, speed = 0.0;
616 if ( tr->trackpoints )
617 {
618 GList *iter = tr->trackpoints->next;
619 while (iter)
620 {
621 if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
622 VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
623 (! VIK_TRACKPOINT(iter->data)->newsegment) )
624 {
625 speed = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) )
626 / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
627 if ( speed > maxspeed )
628 maxspeed = speed;
629 }
630 iter = iter->next;
631 }
632 }
633 return maxspeed;
634}
635
636void vik_track_convert ( VikTrack *tr, VikCoordMode dest_mode )
637{
638 GList *iter = tr->trackpoints;
639 while (iter)
640 {
641 vik_coord_convert ( &(VIK_TRACKPOINT(iter->data)->coord), dest_mode );
642 iter = iter->next;
643 }
644}
645
646/* I understood this when I wrote it ... maybe ... Basically it eats up the
647 * proper amounts of length on the track and averages elevation over that. */
648gdouble *vik_track_make_elevation_map ( const VikTrack *tr, guint16 num_chunks )
649{
650 gdouble *pts;
651 gdouble total_length, chunk_length, current_dist, current_area_under_curve, current_seg_length, dist_along_seg = 0.0;
652 gdouble altitude1, altitude2;
653 guint16 current_chunk;
654 gboolean ignore_it = FALSE;
655
656 GList *iter = tr->trackpoints;
657
c3deba01 658 if (!iter || !iter->next) /* zero- or one-point track */
4b31042b
QT
659 return NULL;
660
c79f0206
EB
661 { /* test if there's anything worth calculating */
662 gboolean okay = FALSE;
663 while ( iter )
664 {
5c280098
RN
665 // Sometimes a GPS device (or indeed any random file) can have stupid numbers for elevations
666 // Since when is 9.9999e+24 a valid elevation!!
667 // 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)
668 // Some protection against trying to work with crazily massive numbers (otherwise get SIGFPE, Arithmetic exception)
669 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE &&
670 VIK_TRACKPOINT(iter->data)->altitude < 1E9 ) {
c79f0206
EB
671 okay = TRUE; break;
672 }
673 iter = iter->next;
674 }
675 if ( ! okay )
676 return NULL;
677 }
678
0f941532 679 iter = tr->trackpoints;
c79f0206 680
50a14534
EB
681 g_assert ( num_chunks < 16000 );
682
683 pts = g_malloc ( sizeof(gdouble) * num_chunks );
684
685 total_length = vik_track_get_length_including_gaps ( tr );
686 chunk_length = total_length / num_chunks;
687
6374e157 688 /* Zero chunk_length (eg, track of 2 tp with the same loc) will cause crash */
3b36279c
JJ
689 if (chunk_length <= 0) {
690 g_free(pts);
6374e157 691 return NULL;
3b36279c 692 }
6374e157 693
50a14534
EB
694 current_dist = 0.0;
695 current_area_under_curve = 0;
696 current_chunk = 0;
697 current_seg_length = 0;
698
699 current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
700 &(VIK_TRACKPOINT(iter->next->data)->coord) );
701 altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
702 altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
703 dist_along_seg = 0;
704
705 while ( current_chunk < num_chunks ) {
706
707 /* go along current seg */
708 if ( current_seg_length && (current_seg_length - dist_along_seg) > chunk_length ) {
709 dist_along_seg += chunk_length;
710
711 /* /
712 * pt2 *
713 * /x altitude = alt_at_pt_1 + alt_at_pt_2 / 2 = altitude1 + slope * dist_value_of_pt_inbetween_pt1_and_pt2
714 * /xx avg altitude = area under curve / chunk len
715 *pt1 *xxx avg altitude = altitude1 + (altitude2-altitude1)/(current_seg_length)*(dist_along_seg + (chunk_len/2))
716 * / xxx
717 * / xxx
718 **/
719
720 if ( ignore_it )
aebc49f4
RN
721 // Seemly can't determine average for this section - so use last known good value (much better than just sticking in zero)
722 pts[current_chunk] = altitude1;
50a14534 723 else
9903c388 724 pts[current_chunk] = altitude1 + (altitude2-altitude1)*((dist_along_seg - (chunk_length/2))/current_seg_length);
50a14534
EB
725
726 current_chunk++;
727 } else {
728 /* finish current seg */
729 if ( current_seg_length ) {
730 gdouble altitude_at_dist_along_seg = altitude1 + (altitude2-altitude1)/(current_seg_length)*dist_along_seg;
731 current_dist = current_seg_length - dist_along_seg;
732 current_area_under_curve = current_dist*(altitude_at_dist_along_seg + altitude2)*0.5;
733 } else { current_dist = current_area_under_curve = 0; } /* should only happen if first current_seg_length == 0 */
734
735 /* get intervening segs */
736 iter = iter->next;
737 while ( iter && iter->next ) {
738 current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
739 &(VIK_TRACKPOINT(iter->next->data)->coord) );
740 altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
741 altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
742 ignore_it = VIK_TRACKPOINT(iter->next->data)->newsegment;
743
744 if ( chunk_length - current_dist >= current_seg_length ) {
745 current_dist += current_seg_length;
746 current_area_under_curve += current_seg_length * (altitude1+altitude2) * 0.5;
747 iter = iter->next;
748 } else {
749 break;
750 }
751 }
752
753 /* final seg */
754 dist_along_seg = chunk_length - current_dist;
fa396b8a 755 if ( ignore_it || ( iter && !iter->next ) ) {
50a14534 756 pts[current_chunk] = current_area_under_curve / current_dist;
61950ef8
QT
757 if (!iter->next) {
758 int i;
759 for (i = current_chunk + 1; i < num_chunks; i++)
760 pts[i] = pts[current_chunk];
761 break;
762 }
9903c388 763 }
50a14534
EB
764 else {
765 current_area_under_curve += dist_along_seg * (altitude1 + (altitude2 - altitude1)*dist_along_seg/current_seg_length);
766 pts[current_chunk] = current_area_under_curve / chunk_length;
767 }
768
769 current_dist = 0;
770 current_chunk++;
771 }
772 }
773
774 return pts;
775}
776
777
778void vik_track_get_total_elevation_gain(const VikTrack *tr, gdouble *up, gdouble *down)
779{
780 gdouble diff;
781 *up = *down = 0;
8c4f1350 782 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE )
50a14534
EB
783 {
784 GList *iter = tr->trackpoints->next;
785 while (iter)
786 {
787 diff = VIK_TRACKPOINT(iter->data)->altitude - VIK_TRACKPOINT(iter->prev->data)->altitude;
788 if ( diff > 0 )
789 *up += diff;
790 else
791 *down -= diff;
792 iter = iter->next;
793 }
bf35388d
EB
794 } else
795 *up = *down = VIK_DEFAULT_ALTITUDE;
796}
797
0ba33e1d
FA
798gdouble *vik_track_make_gradient_map ( const VikTrack *tr, guint16 num_chunks )
799{
800 gdouble *pts;
801 gdouble *altitudes;
802 gdouble total_length, chunk_length, current_gradient;
803 gdouble altitude1, altitude2;
804 guint16 current_chunk;
805
806 g_assert ( num_chunks < 16000 );
807
808 total_length = vik_track_get_length_including_gaps ( tr );
809 chunk_length = total_length / num_chunks;
810
811 /* Zero chunk_length (eg, track of 2 tp with the same loc) will cause crash */
812 if (chunk_length <= 0) {
813 return NULL;
814 }
815
816 altitudes = vik_track_make_elevation_map (tr, num_chunks);
817 if (altitudes == NULL) {
818 return NULL;
819 }
820
821 current_gradient = 0.0;
822 pts = g_malloc ( sizeof(gdouble) * num_chunks );
823 for (current_chunk = 0; current_chunk < (num_chunks - 1); current_chunk++) {
824 altitude1 = altitudes[current_chunk];
825 altitude2 = altitudes[current_chunk + 1];
826 current_gradient = 100.0 * (altitude2 - altitude1) / chunk_length;
827
828 pts[current_chunk] = current_gradient;
829 }
830
831 pts[current_chunk] = current_gradient;
832
0215dea4
RN
833 g_free ( altitudes );
834
0ba33e1d
FA
835 return pts;
836}
25e44eac 837
bf35388d 838/* by Alex Foobarian */
25e44eac
AF
839gdouble *vik_track_make_speed_map ( const VikTrack *tr, guint16 num_chunks )
840{
bf35388d 841 gdouble *v, *s, *t;
0654760a 842 gdouble duration, chunk_dur;
25e44eac 843 time_t t1, t2;
d03d80e6 844 int i, pt_count, numpts, index;
bf35388d 845 GList *iter;
25e44eac 846
24d5c7e2
EB
847 if ( ! tr->trackpoints )
848 return NULL;
25e44eac 849
24d5c7e2 850 g_assert ( num_chunks < 16000 );
25e44eac
AF
851
852 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
853 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
854 duration = t2 - t1;
c79f0206
EB
855
856 if ( !t1 || !t2 || !duration )
857 return NULL;
858
25e44eac 859 if (duration < 0) {
4258f4e2 860 g_warning("negative duration: unsorted trackpoint timestamps?");
25e44eac
AF
861 return NULL;
862 }
bf35388d 863 pt_count = vik_track_get_tp_count(tr);
24d5c7e2 864
bf35388d 865 v = g_malloc ( sizeof(gdouble) * num_chunks );
25e44eac 866 chunk_dur = duration / num_chunks;
bf35388d
EB
867
868 s = g_malloc(sizeof(double) * pt_count);
869 t = g_malloc(sizeof(double) * pt_count);
bf35388d
EB
870
871 iter = tr->trackpoints->next;
872 numpts = 0;
873 s[0] = 0;
e979bdab 874 t[0] = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
bf35388d
EB
875 numpts++;
876 while (iter) {
877 s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
878 t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
879 numpts++;
880 iter = iter->next;
25e44eac
AF
881 }
882
d03d80e6
AF
883 /* In the following computation, we iterate through periods of time of duration chunk_dur.
884 * The first period begins at the beginning of the track. The last period ends at the end of the track.
bf35388d 885 */
d03d80e6
AF
886 index = 0; /* index of the current trackpoint. */
887 for (i = 0; i < num_chunks; i++) {
888 /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
889 * find the first trackpoint outside the current interval, averaging the speeds between intermediate trackpoints.
890 */
891 if (t[0] + i*chunk_dur >= t[index]) {
892 gdouble acc_t = 0, acc_s = 0;
d03d80e6
AF
893 while (t[0] + i*chunk_dur >= t[index]) {
894 acc_s += (s[index+1]-s[index]);
895 acc_t += (t[index+1]-t[index]);
896 index++;
d03d80e6
AF
897 }
898 v[i] = acc_s/acc_t;
899 }
900 else if (i) {
901 v[i] = v[i-1];
902 }
903 else {
904 v[i] = 0;
bf35388d 905 }
bf35388d
EB
906 }
907 g_free(s);
908 g_free(t);
bf35388d 909 return v;
25e44eac 910}
24d5c7e2 911
926c8140
RN
912/**
913 * Make a distance/time map, heavily based on the vik_track_make_speed_map method
914 */
915gdouble *vik_track_make_distance_map ( const VikTrack *tr, guint16 num_chunks )
916{
917 gdouble *v, *s, *t;
918 gdouble duration, chunk_dur;
919 time_t t1, t2;
920 int i, pt_count, numpts, index;
921 GList *iter;
922
923 if ( ! tr->trackpoints )
924 return NULL;
925
926 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
927 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
928 duration = t2 - t1;
929
930 if ( !t1 || !t2 || !duration )
931 return NULL;
932
933 if (duration < 0) {
934 g_warning("negative duration: unsorted trackpoint timestamps?");
935 return NULL;
936 }
937 pt_count = vik_track_get_tp_count(tr);
938
939 v = g_malloc ( sizeof(gdouble) * num_chunks );
940 chunk_dur = duration / num_chunks;
941
942 s = g_malloc(sizeof(double) * pt_count);
943 t = g_malloc(sizeof(double) * pt_count);
944
945 iter = tr->trackpoints->next;
946 numpts = 0;
947 s[0] = 0;
e979bdab 948 t[0] = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
926c8140
RN
949 numpts++;
950 while (iter) {
951 s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
952 t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
953 numpts++;
954 iter = iter->next;
955 }
956
957 /* In the following computation, we iterate through periods of time of duration chunk_dur.
958 * The first period begins at the beginning of the track. The last period ends at the end of the track.
959 */
960 index = 0; /* index of the current trackpoint. */
961 for (i = 0; i < num_chunks; i++) {
962 /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
963 * find the first trackpoint outside the current interval, averaging the distance between intermediate trackpoints.
964 */
965 if (t[0] + i*chunk_dur >= t[index]) {
966 gdouble acc_s = 0; // No need for acc_t
967 while (t[0] + i*chunk_dur >= t[index]) {
968 acc_s += (s[index+1]-s[index]);
969 index++;
970 }
971 // The only bit that's really different from the speed map - just keep an accululative record distance
972 v[i] = i ? v[i-1]+acc_s : acc_s;
973 }
974 else if (i) {
975 v[i] = v[i-1];
976 }
977 else {
978 v[i] = 0;
979 }
980 }
981 g_free(s);
982 g_free(t);
983 return v;
984}
985
8de26632
RN
986/**
987 * This uses the 'time' based method to make the graph, (which is a simpler compared to the elevation/distance)
988 * This results in a slightly blocky graph when it does not have many trackpoints: <60
989 * NB Somehow the elevation/distance applies some kind of smoothing algorithm,
990 * but I don't think any one understands it any more (I certainly don't ATM)
991 */
992gdouble *vik_track_make_elevation_time_map ( const VikTrack *tr, guint16 num_chunks )
993{
994 time_t t1, t2;
995 gdouble duration, chunk_dur;
996 GList *iter = tr->trackpoints;
997
998 if (!iter || !iter->next) /* zero- or one-point track */
999 return NULL;
1000
1001 /* test if there's anything worth calculating */
1002 gboolean okay = FALSE;
1003 while ( iter ) {
1004 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1005 okay = TRUE;
1006 break;
1007 }
1008 iter = iter->next;
1009 }
1010 if ( ! okay )
1011 return NULL;
1012
1013 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1014 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1015 duration = t2 - t1;
1016
1017 if ( !t1 || !t2 || !duration )
1018 return NULL;
1019
1020 if (duration < 0) {
1021 g_warning("negative duration: unsorted trackpoint timestamps?");
1022 return NULL;
1023 }
1024 gint pt_count = vik_track_get_tp_count(tr);
1025
1026 // Reset iterator back to the beginning
1027 iter = tr->trackpoints;
1028
1029 gdouble *pts = g_malloc ( sizeof(gdouble) * num_chunks ); // The return altitude values
1030 gdouble *s = g_malloc(sizeof(double) * pt_count); // calculation altitudes
1031 gdouble *t = g_malloc(sizeof(double) * pt_count); // calculation times
1032
1033 chunk_dur = duration / num_chunks;
1034
1035 s[0] = VIK_TRACKPOINT(iter->data)->altitude;
1036 t[0] = VIK_TRACKPOINT(iter->data)->timestamp;
1037 iter = tr->trackpoints->next;
1038 gint numpts = 1;
1039 while (iter) {
1040 s[numpts] = VIK_TRACKPOINT(iter->data)->altitude;
1041 t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
1042 numpts++;
1043 iter = iter->next;
1044 }
1045
1046 /* In the following computation, we iterate through periods of time of duration chunk_dur.
1047 * The first period begins at the beginning of the track. The last period ends at the end of the track.
1048 */
1049 gint index = 0; /* index of the current trackpoint. */
1050 gint i;
1051 for (i = 0; i < num_chunks; i++) {
1052 /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur.
1053 * find the first trackpoint outside the current interval, averaging the heights between intermediate trackpoints.
1054 */
1055 if (t[0] + i*chunk_dur >= t[index]) {
1056 gdouble acc_s = s[index]; // initialise to first point
1057 while (t[0] + i*chunk_dur >= t[index]) {
1058 acc_s += (s[index+1]-s[index]);
1059 index++;
1060 }
1061 pts[i] = acc_s;
1062 }
1063 else if (i) {
1064 pts[i] = pts[i-1];
1065 }
1066 else {
1067 pts[i] = 0;
1068 }
1069 }
1070 g_free(s);
1071 g_free(t);
1072
1073 return pts;
1074}
1075
7b624086
RN
1076/**
1077 * Make a speed/distance map
1078 */
1079gdouble *vik_track_make_speed_dist_map ( const VikTrack *tr, guint16 num_chunks )
1080{
1081 gdouble *v, *s, *t;
1082 time_t t1, t2;
1083 gint i, pt_count, numpts, index;
1084 GList *iter;
1085 gdouble duration, total_length, chunk_length;
1086
1087 if ( ! tr->trackpoints )
1088 return NULL;
1089
1090 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1091 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1092 duration = t2 - t1;
1093
1094 if ( !t1 || !t2 || !duration )
1095 return NULL;
1096
1097 if (duration < 0) {
1098 g_warning("negative duration: unsorted trackpoint timestamps?");
1099 return NULL;
1100 }
1101
1102 total_length = vik_track_get_length_including_gaps ( tr );
1103 chunk_length = total_length / num_chunks;
1104 pt_count = vik_track_get_tp_count(tr);
1105
1106 if (chunk_length <= 0) {
1107 return NULL;
1108 }
1109
1110 v = g_malloc ( sizeof(gdouble) * num_chunks );
1111 s = g_malloc ( sizeof(double) * pt_count );
1112 t = g_malloc ( sizeof(double) * pt_count );
1113
1114 // No special handling of segments ATM...
1115 iter = tr->trackpoints->next;
1116 numpts = 0;
1117 s[0] = 0;
e979bdab 1118 t[0] = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
7b624086
RN
1119 numpts++;
1120 while (iter) {
1121 s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) );
1122 t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp;
1123 numpts++;
1124 iter = iter->next;
1125 }
1126
1127 // Iterate through a portion of the track to get an average speed for that part
1128 // This will essentially interpolate between segments, which I think is right given the usage of 'get_length_including_gaps'
1129 index = 0; /* index of the current trackpoint. */
1130 for (i = 0; i < num_chunks; i++) {
1131 // Similar to the make_speed_map, but instead of using a time chunk, use a distance chunk
1132 if (s[0] + i*chunk_length >= s[index]) {
1133 gdouble acc_t = 0, acc_s = 0;
1134 while (s[0] + i*chunk_length >= s[index]) {
1135 acc_s += (s[index+1]-s[index]);
1136 acc_t += (t[index+1]-t[index]);
1137 index++;
1138 }
1139 v[i] = acc_s/acc_t;
1140 }
1141 else if (i) {
1142 v[i] = v[i-1];
1143 }
1144 else {
1145 v[i] = 0;
1146 }
1147 }
1148 g_free(s);
1149 g_free(t);
1150 return v;
1151}
1152
e58bcab1
RN
1153/**
1154 * vik_track_get_tp_by_dist:
1155 * @trk: The Track on which to find a Trackpoint
1156 * @meters_from_start: The distance along a track that the trackpoint returned is near
1157 * @get_next_point: Since there is a choice of trackpoints, this determines which one to return
1158 * @tp_metres_from_start: For the returned Trackpoint, returns the distance along the track
1159 *
1160 * TODO: Consider changing the boolean get_next_point into an enum with these options PREVIOUS, NEXT, NEAREST
1161 *
1162 * Returns: The #VikTrackpoint fitting the criteria or NULL
1163 */
1164VikTrackpoint *vik_track_get_tp_by_dist ( VikTrack *trk, gdouble meters_from_start, gboolean get_next_point, gdouble *tp_metres_from_start )
1165{
1166 gdouble current_dist = 0.0;
1167 gdouble current_inc = 0.0;
1168 if ( tp_metres_from_start )
1169 *tp_metres_from_start = 0.0;
1170
1171 if ( trk->trackpoints ) {
1172 GList *iter = g_list_next ( g_list_first ( trk->trackpoints ) );
1173 while (iter) {
1174 current_inc = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
1175 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
1176 current_dist += current_inc;
1177 if ( current_dist >= meters_from_start )
1178 break;
1179 iter = g_list_next ( iter );
1180 }
1181 // passed the end of the track
1182 if ( !iter )
1183 return NULL;
1184
1185 if ( tp_metres_from_start )
1186 *tp_metres_from_start = current_dist;
1187
1188 // we've gone past the distance already, is the previous trackpoint wanted?
1189 if ( !get_next_point ) {
1190 if ( iter->prev ) {
1191 if ( tp_metres_from_start )
1192 *tp_metres_from_start = current_dist-current_inc;
1193 return VIK_TRACKPOINT(iter->prev->data);
1194 }
1195 }
1196 return VIK_TRACKPOINT(iter->data);
1197 }
1198
1199 return NULL;
1200}
1201
bf35388d 1202/* by Alex Foobarian */
ddc2372e 1203VikTrackpoint *vik_track_get_closest_tp_by_percentage_dist ( VikTrack *tr, gdouble reldist, gdouble *meters_from_start )
24d5c7e2
EB
1204{
1205 gdouble dist = vik_track_get_length_including_gaps(tr) * reldist;
1206 gdouble current_dist = 0.0;
1207 gdouble current_inc = 0.0;
24d5c7e2
EB
1208 if ( tr->trackpoints )
1209 {
1210 GList *iter = tr->trackpoints->next;
ecb51018 1211 GList *last_iter = NULL;
ddc2372e 1212 gdouble last_dist = 0.0;
24d5c7e2
EB
1213 while (iter)
1214 {
1215 current_inc = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
1216 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
ddc2372e 1217 last_dist = current_dist;
24d5c7e2
EB
1218 current_dist += current_inc;
1219 if ( current_dist >= dist )
1220 break;
ecb51018 1221 last_iter = iter;
24d5c7e2
EB
1222 iter = iter->next;
1223 }
ddc2372e
QT
1224 if (!iter) { /* passing the end the track */
1225 if (last_iter) {
1226 if (meters_from_start)
1227 *meters_from_start = last_dist;
1228 return(VIK_TRACKPOINT(last_iter->data));
1229 }
1230 else
1231 return NULL;
1232 }
24d5c7e2
EB
1233 /* we've gone past the dist already, was prev trackpoint closer? */
1234 /* should do a vik_coord_average_weighted() thingy. */
ddc2372e
QT
1235 if ( iter->prev && abs(current_dist-current_inc-dist) < abs(current_dist-dist) ) {
1236 if (meters_from_start)
1237 *meters_from_start = last_dist;
24d5c7e2 1238 iter = iter->prev;
ddc2372e
QT
1239 }
1240 else
1241 if (meters_from_start)
1242 *meters_from_start = current_dist;
24d5c7e2 1243
e1e2f2c6 1244 return VIK_TRACKPOINT(iter->data);
24d5c7e2
EB
1245
1246 }
1247 return NULL;
1248}
b42a25ba 1249
ddc2372e 1250VikTrackpoint *vik_track_get_closest_tp_by_percentage_time ( VikTrack *tr, gdouble reltime, time_t *seconds_from_start )
32e48121
QT
1251{
1252 time_t t_pos, t_start, t_end, t_total;
1253 t_start = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1254 t_end = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1255 t_total = t_end - t_start;
1256
1257 t_pos = t_start + t_total * reltime;
1258
ddc2372e
QT
1259 if ( !tr->trackpoints )
1260 return NULL;
32e48121 1261
ddc2372e
QT
1262 GList *iter = tr->trackpoints;
1263
1264 while (iter) {
1265 if (VIK_TRACKPOINT(iter->data)->timestamp == t_pos)
1266 break;
1267 if (VIK_TRACKPOINT(iter->data)->timestamp > t_pos) {
1268 if (iter->prev == NULL) /* first trackpoint */
1269 break;
16dcb4dd 1270 time_t t_before = t_pos - VIK_TRACKPOINT(iter->prev->data)->timestamp;
ddc2372e
QT
1271 time_t t_after = VIK_TRACKPOINT(iter->data)->timestamp - t_pos;
1272 if (t_before <= t_after)
1273 iter = iter->prev;
1274 break;
32e48121 1275 }
ddc2372e
QT
1276 else if ((iter->next == NULL) && (t_pos < (VIK_TRACKPOINT(iter->data)->timestamp + 3))) /* last trackpoint: accommodate for round-off */
1277 break;
1278 iter = iter->next;
32e48121 1279 }
ddc2372e
QT
1280
1281 if (!iter)
1282 return NULL;
1283 if (seconds_from_start)
1284 *seconds_from_start = VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1285 return VIK_TRACKPOINT(iter->data);
32e48121
QT
1286}
1287
03e7da75
RN
1288VikTrackpoint* vik_track_get_tp_by_max_speed ( const VikTrack *tr )
1289{
1290 gdouble maxspeed = 0.0, speed = 0.0;
1291
1292 if ( !tr->trackpoints )
1293 return NULL;
1294
1295 GList *iter = tr->trackpoints;
1296 VikTrackpoint *max_speed_tp = NULL;
1297
1298 while (iter) {
7d7acd6d 1299 if (iter->prev) {
03e7da75
RN
1300 if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
1301 VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
1302 (! VIK_TRACKPOINT(iter->data)->newsegment) ) {
1303 speed = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) )
1304 / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
1305 if ( speed > maxspeed ) {
1306 maxspeed = speed;
1307 max_speed_tp = VIK_TRACKPOINT(iter->data);
1308 }
1309 }
1310 }
1311 iter = iter->next;
1312 }
1313
1314 if (!max_speed_tp)
1315 return NULL;
1316
1317 return max_speed_tp;
1318}
1319
c28faca8
RN
1320VikTrackpoint* vik_track_get_tp_by_max_alt ( const VikTrack *tr )
1321{
1322 gdouble maxalt = -5000.0;
1323 if ( !tr->trackpoints )
1324 return NULL;
1325
1326 GList *iter = tr->trackpoints;
1327 VikTrackpoint *max_alt_tp = NULL;
1328
1329 while (iter) {
1330 if ( VIK_TRACKPOINT(iter->data)->altitude > maxalt ) {
1331 maxalt = VIK_TRACKPOINT(iter->data)->altitude;
1332 max_alt_tp = VIK_TRACKPOINT(iter->data);
1333 }
1334 iter = iter->next;
1335 }
1336
1337 if (!max_alt_tp)
1338 return NULL;
1339
1340 return max_alt_tp;
1341}
1342
1343VikTrackpoint* vik_track_get_tp_by_min_alt ( const VikTrack *tr )
1344{
1345 gdouble minalt = 25000.0;
1346 if ( !tr->trackpoints )
1347 return NULL;
1348
1349 GList *iter = tr->trackpoints;
1350 VikTrackpoint *min_alt_tp = NULL;
1351
1352 while (iter) {
1353 if ( VIK_TRACKPOINT(iter->data)->altitude < minalt ) {
1354 minalt = VIK_TRACKPOINT(iter->data)->altitude;
1355 min_alt_tp = VIK_TRACKPOINT(iter->data);
1356 }
1357 iter = iter->next;
1358 }
1359
1360 if (!min_alt_tp)
1361 return NULL;
1362
1363 return min_alt_tp;
1364}
1365
b42a25ba
EB
1366gboolean vik_track_get_minmax_alt ( const VikTrack *tr, gdouble *min_alt, gdouble *max_alt )
1367{
1368 *min_alt = 25000;
1369 *max_alt = -5000;
1370 if ( tr && tr->trackpoints && tr->trackpoints->data && (VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE) ) {
1371 GList *iter = tr->trackpoints->next;
1372 gdouble tmp_alt;
1373 while (iter)
1374 {
1375 tmp_alt = VIK_TRACKPOINT(iter->data)->altitude;
1376 if ( tmp_alt > *max_alt )
1377 *max_alt = tmp_alt;
1378 if ( tmp_alt < *min_alt )
1379 *min_alt = tmp_alt;
1380 iter = iter->next;
1381 }
1382 return TRUE;
1383 }
1384 return FALSE;
1385}
ddc47a46
AF
1386
1387void vik_track_marshall ( VikTrack *tr, guint8 **data, guint *datalen)
1388{
1389 GList *tps;
1390 GByteArray *b = g_byte_array_new();
1391 guint len;
1392 guint intp, ntp;
1393
1394 g_byte_array_append(b, (guint8 *)tr, sizeof(*tr));
1395
1396 /* we'll fill out number of trackpoints later */
1397 intp = b->len;
1398 g_byte_array_append(b, (guint8 *)&len, sizeof(len));
1399
f629b00b
RN
1400 // This allocates space for variant sized strings
1401 // and copies that amount of data from the track to byte array
1402#define vtm_append(s) \
1403 len = (s) ? strlen(s)+1 : 0; \
1404 g_byte_array_append(b, (guint8 *)&len, sizeof(len)); \
1405 if (s) g_byte_array_append(b, (guint8 *)s, len);
1406
ddc47a46
AF
1407 tps = tr->trackpoints;
1408 ntp = 0;
1409 while (tps) {
1410 g_byte_array_append(b, (guint8 *)tps->data, sizeof(VikTrackpoint));
f629b00b 1411 vtm_append(VIK_TRACKPOINT(tps->data)->name);
ddc47a46
AF
1412 tps = tps->next;
1413 ntp++;
1414 }
1415 *(guint *)(b->data + intp) = ntp;
1416
6b2f262e
RN
1417 vtm_append(tr->name);
1418 vtm_append(tr->comment);
1419 vtm_append(tr->description);
ddc47a46
AF
1420
1421 *data = b->data;
1422 *datalen = b->len;
1423 g_byte_array_free(b, FALSE);
1424}
1425
6b2f262e
RN
1426/*
1427 * Take a byte array and convert it into a Track
1428 */
ddc47a46
AF
1429VikTrack *vik_track_unmarshall (guint8 *data, guint datalen)
1430{
1431 guint len;
1432 VikTrack *new_tr = vik_track_new();
1433 VikTrackpoint *new_tp;
1434 guint ntp;
1435 gint i;
1436
0d2b891f 1437 /* basic properties: */
ddc47a46 1438 new_tr->visible = ((VikTrack *)data)->visible;
0d2b891f 1439 new_tr->is_route = ((VikTrack *)data)->is_route;
387ff7ac
RN
1440 new_tr->draw_name_mode = ((VikTrack *)data)->draw_name_mode;
1441 new_tr->max_number_dist_labels = ((VikTrack *)data)->max_number_dist_labels;
b1453c16
RN
1442 new_tr->has_color = ((VikTrack *)data)->has_color;
1443 new_tr->color = ((VikTrack *)data)->color;
20981fd6 1444 new_tr->bbox = ((VikTrack *)data)->bbox;
0d2b891f 1445
ddc47a46
AF
1446 data += sizeof(*new_tr);
1447
1448 ntp = *(guint *)data;
1449 data += sizeof(ntp);
1450
6b2f262e
RN
1451#define vtu_get(s) \
1452 len = *(guint *)data; \
1453 data += sizeof(len); \
1454 if (len) { \
1455 (s) = g_strdup((gchar *)data); \
1456 } else { \
1457 (s) = NULL; \
1458 } \
ce4bd1cf
RN
1459 data += len;
1460
f629b00b
RN
1461 for (i=0; i<ntp; i++) {
1462 new_tp = vik_trackpoint_new();
1463 memcpy(new_tp, data, sizeof(*new_tp));
1464 data += sizeof(*new_tp);
1465 vtu_get(new_tp->name);
1466 new_tr->trackpoints = g_list_prepend(new_tr->trackpoints, new_tp);
1467 }
1468 if ( new_tr->trackpoints )
1469 new_tr->trackpoints = g_list_reverse(new_tr->trackpoints);
1470
6b2f262e
RN
1471 vtu_get(new_tr->name);
1472 vtu_get(new_tr->comment);
1473 vtu_get(new_tr->description);
1474
ddc47a46
AF
1475 return new_tr;
1476}
ad0a8c2d 1477
20981fd6
RN
1478/**
1479 * (Re)Calculate the bounds of the given track,
1480 * updating the track's bounds data.
1481 * This should be called whenever a track's trackpoints are changed
1482 */
1483void vik_track_calculate_bounds ( VikTrack *trk )
1484{
1485 GList *tp_iter;
1486 tp_iter = trk->trackpoints;
1487
1488 struct LatLon topleft, bottomright, ll;
1489
1490 // Set bounds to first point
1491 if ( tp_iter ) {
1492 vik_coord_to_latlon ( &(VIK_TRACKPOINT(tp_iter->data)->coord), &topleft );
1493 vik_coord_to_latlon ( &(VIK_TRACKPOINT(tp_iter->data)->coord), &bottomright );
1494 }
1495 while ( tp_iter ) {
1496
1497 // See if this trackpoint increases the track bounds.
1498
1499 vik_coord_to_latlon ( &(VIK_TRACKPOINT(tp_iter->data)->coord), &ll );
1500
1501 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
1502 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
1503 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
1504 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
1505
1506 tp_iter = tp_iter->next;
1507 }
1508
644eea0e 1509 g_debug ( "Bounds of track: '%s' is: %f,%f to: %f,%f", trk->name, topleft.lat, topleft.lon, bottomright.lat, bottomright.lon );
20981fd6
RN
1510
1511 trk->bbox.north = topleft.lat;
1512 trk->bbox.east = bottomright.lon;
1513 trk->bbox.south = bottomright.lat;
1514 trk->bbox.west = topleft.lon;
1515}
1516
d6218717
RN
1517/**
1518 * vik_track_anonymize_times:
1519 *
1520 * Shift all timestamps to be relatively offset from 1901-01-01
1521 */
1522void vik_track_anonymize_times ( VikTrack *tr )
1523{
1524 GTimeVal gtv;
1525 g_time_val_from_iso8601 ( "1901-01-01T00:00:00Z", &gtv );
1526
1527 time_t anon_timestamp = gtv.tv_sec;
1528 time_t offset = 0;
1529
1530 GList *tp_iter;
1531 tp_iter = tr->trackpoints;
1532 while ( tp_iter ) {
1533 VikTrackpoint *tp = VIK_TRACKPOINT(tp_iter->data);
1534 if ( tp->has_timestamp ) {
1535 // Calculate an offset in time using the first available timestamp
1536 if ( offset == 0 )
1537 offset = tp->timestamp - anon_timestamp;
1538
1539 // Apply this offset to shift all timestamps towards 1901 & hence anonymising the time
1540 // Note that the relative difference between timestamps is kept - thus calculating speeds will still work
1541 tp->timestamp = tp->timestamp - offset;
1542 }
1543 tp_iter = tp_iter->next;
1544 }
1545}
1546
1547
20981fd6 1548/**
4d333042
RN
1549 * vik_track_apply_dem_data:
1550 * @skip_existing: When TRUE, don't change the elevation if the trackpoint already has a value
20981fd6 1551 *
4d333042 1552 * Set elevation data for a track using any available DEM information
20981fd6 1553 */
4d333042 1554gulong vik_track_apply_dem_data ( VikTrack *tr, gboolean skip_existing )
ad0a8c2d 1555{
4d333042 1556 gulong num = 0;
ad0a8c2d
EB
1557 GList *tp_iter;
1558 gint16 elev;
ad0a8c2d
EB
1559 tp_iter = tr->trackpoints;
1560 while ( tp_iter ) {
4d333042
RN
1561 // Don't apply if the point already has a value and the overwrite is off
1562 if ( !(skip_existing && VIK_TRACKPOINT(tp_iter->data)->altitude != VIK_DEFAULT_ALTITUDE) ) {
1563 /* TODO: of the 4 possible choices we have for choosing an elevation
1564 * (trackpoint in between samples), choose the one with the least elevation change
1565 * as the last */
1566 elev = a_dems_get_elev_by_coord ( &(VIK_TRACKPOINT(tp_iter->data)->coord), VIK_DEM_INTERPOL_BEST );
1567
1568 if ( elev != VIK_DEM_INVALID_ELEVATION ) {
1569 VIK_TRACKPOINT(tp_iter->data)->altitude = elev;
1570 num++;
1571 }
1572 }
ad0a8c2d
EB
1573 tp_iter = tp_iter->next;
1574 }
4d333042 1575 return num;
ad0a8c2d 1576}
bddd2056 1577
210ef020
GB
1578/**
1579 * vik_track_apply_dem_data_last_trackpoint:
e85535ea
RN
1580 * Apply DEM data (if available) - to only the last trackpoint
1581 */
1582void vik_track_apply_dem_data_last_trackpoint ( VikTrack *tr )
1583{
1584 gint16 elev;
1585 if ( tr->trackpoints ) {
1586 /* As in vik_track_apply_dem_data above - use 'best' interpolation method */
1587 elev = a_dems_get_elev_by_coord ( &(VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->coord), VIK_DEM_INTERPOL_BEST );
1588 if ( elev != VIK_DEM_INVALID_ELEVATION )
1589 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->altitude = elev;
1590 }
1591}
1592
024f0e93
RN
1593
1594/**
1595 * smoothie:
1596 *
1597 * Apply elevation smoothing over range of trackpoints between the list start and end points
1598 */
1599static void smoothie ( GList *tp1, GList *tp2, gdouble elev1, gdouble elev2, guint points )
1600{
1601 // If was really clever could try and weigh interpolation according to the distance between trackpoints somehow
1602 // Instead a simple average interpolation for the number of points given.
1603 gdouble change = (elev2 - elev1)/(points+1);
1604 gint count = 1;
1605 GList *tp_iter = tp1;
1606 while ( tp_iter != tp2 && tp_iter ) {
1607 VikTrackpoint *tp = VIK_TRACKPOINT(tp_iter->data);
1608
1609 tp->altitude = elev1 + (change*count);
1610
1611 count++;
1612 tp_iter = tp_iter->next;
1613 }
1614}
1615
1616/**
1617 * vik_track_smooth_missing_elevation_data:
81ac2835 1618 * @flat: Specify how the missing elevations will be set.
024f0e93
RN
1619 * When TRUE it uses a simple flat method, using the last known elevation
1620 * When FALSE is uses an interpolation method to the next known elevation
1621 *
1622 * For each point with a missing elevation, set it to use the last known available elevation value.
1623 * Primarily of use for smallish DEM holes where it is missing elevation data.
1624 * Eg see Austria: around N47.3 & E13.8
1625 *
1626 * Returns: The number of points that were adjusted
1627 */
1628gulong vik_track_smooth_missing_elevation_data ( VikTrack *tr, gboolean flat )
1629{
1630 gulong num = 0;
1631
1632 GList *tp_iter;
1633 gdouble elev = VIK_DEFAULT_ALTITUDE;
1634
1635 VikTrackpoint *tp_missing = NULL;
1636 GList *iter_first = NULL;
1637 guint points = 0;
1638
1639 tp_iter = tr->trackpoints;
1640 while ( tp_iter ) {
1641 VikTrackpoint *tp = VIK_TRACKPOINT(tp_iter->data);
1642
1643 if ( VIK_DEFAULT_ALTITUDE == tp->altitude ) {
1644 if ( flat ) {
1645 // Simply assign to last known value
1646 if ( elev != VIK_DEFAULT_ALTITUDE ) {
1647 tp->altitude = elev;
1648 num++;
1649 }
1650 }
1651 else {
1652 if ( !tp_missing ) {
1653 // Remember the first trackpoint (and the list pointer to it) of a section of no altitudes
1654 tp_missing = tp;
1655 iter_first = tp_iter;
1656 points = 1;
1657 }
1658 else {
1659 // More missing altitudes
1660 points++;
1661 }
1662 }
1663 }
1664 else {
1665 // Altitude available (maybe again!)
1666 // If this marks the end of a section of altitude-less points
1667 // then apply smoothing for that section of points
1668 if ( points > 0 && elev != VIK_DEFAULT_ALTITUDE )
1669 if ( !flat ) {
1670 smoothie ( iter_first, tp_iter, elev, tp->altitude, points );
1671 num = num + points;
1672 }
1673
1674 // reset
1675 points = 0;
1676 tp_missing = NULL;
1677
1678 // Store for reuse as the last known good value
1679 elev = tp->altitude;
1680 }
1681
1682 tp_iter = tp_iter->next;
1683 }
1684
1685 return num;
1686}
1687
210ef020
GB
1688/**
1689 * vik_track_steal_and_append_trackpoints:
1690 *
1691 * appends t2 to t1, leaving t2 with no trackpoints
1692 */
bddd2056
EB
1693void vik_track_steal_and_append_trackpoints ( VikTrack *t1, VikTrack *t2 )
1694{
1695 if ( t1->trackpoints ) {
78af4063 1696 t1->trackpoints = g_list_concat ( t1->trackpoints, t2->trackpoints );
bddd2056
EB
1697 } else
1698 t1->trackpoints = t2->trackpoints;
1699 t2->trackpoints = NULL;
20981fd6
RN
1700
1701 // Trackpoints updated - so update the bounds
1702 vik_track_calculate_bounds ( t1 );
bddd2056 1703}
c3deba01 1704
210ef020
GB
1705/**
1706 * vik_track_cut_back_to_double_point:
1707 *
1708 * starting at the end, looks backwards for the last "double point", a duplicate trackpoint.
7ff7d728 1709 * If there is no double point, deletes all the trackpoints.
210ef020
GB
1710 *
1711 * Returns: the new end of the track (or the start if there are no double points)
c3deba01
EB
1712 */
1713VikCoord *vik_track_cut_back_to_double_point ( VikTrack *tr )
1714{
1715 GList *iter = tr->trackpoints;
1716 VikCoord *rv;
1717
1718 if ( !iter )
1719 return NULL;
1720 while ( iter->next )
1721 iter = iter->next;
1722
1723
1724 while ( iter->prev ) {
1725 if ( vik_coord_equals((VikCoord *)iter->data, (VikCoord *)iter->prev->data) ) {
1726 GList *prev = iter->prev;
1727
1728 rv = g_malloc(sizeof(VikCoord));
1729 *rv = *((VikCoord *) iter->data);
1730
1731 /* truncate trackpoint list */
1732 iter->prev = NULL; /* pretend it's the end */
1733 g_list_foreach ( iter, (GFunc) g_free, NULL );
1734 g_list_free( iter );
1735
1736 prev->next = NULL;
1737
1738 return rv;
1739 }
1740 iter = iter->prev;
1741 }
1742
1743 /* no double point found! */
1744 rv = g_malloc(sizeof(VikCoord));
1745 *rv = *((VikCoord *) tr->trackpoints->data);
1746 g_list_foreach ( tr->trackpoints, (GFunc) g_free, NULL );
1747 g_list_free( tr->trackpoints );
1748 tr->trackpoints = NULL;
1749 return rv;
1750}
1751