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