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