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