]> git.street.me.uk Git - andy/viking.git/blame - src/viktrack.c
Initial revision
[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>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <glib.h>
23#include <time.h>
24#include "coords.h"
25#include "vikcoord.h"
26#include "viktrack.h"
27#include "globals.h"
28
29VikTrack *vik_track_new()
30{
31 VikTrack *tr = g_malloc ( sizeof ( VikTrack ) );
32 tr->trackpoints = NULL;
33 tr->comment = NULL;
34 tr->ref_count = 1;
35 return tr;
36}
37
38void vik_track_set_comment_no_copy(VikTrack *tr, gchar *comment)
39{
40 if ( tr->comment )
41 g_free ( tr->comment );
42 tr->comment = comment;
43}
44
45
46void vik_track_set_comment(VikTrack *tr, const gchar *comment)
47{
48 if ( tr->comment )
49 g_free ( tr->comment );
50
51 if ( comment && comment[0] != '\0' )
52 tr->comment = g_strdup(comment);
53 else
54 tr->comment = NULL;
55}
56
57void vik_track_ref(VikTrack *tr)
58{
59 tr->ref_count++;
60}
61
62void vik_track_free(VikTrack *tr)
63{
64 if ( tr->ref_count-- > 1 )
65 return;
66
67 if ( tr->comment )
68 g_free ( tr->comment );
69 g_list_foreach ( tr->trackpoints, (GFunc) g_free, NULL );
70 g_list_free( tr->trackpoints );
71 g_free ( tr );
72}
73
74VikTrack *vik_track_copy ( const VikTrack *tr )
75{
76 VikTrack *new_tr = vik_track_new();
77 VikTrackpoint *new_tp;
78 GList *tp_iter = tr->trackpoints;
79 new_tr->visible = tr->visible;
80 new_tr->trackpoints = NULL;
81 while ( tp_iter )
82 {
83 new_tp = g_malloc ( sizeof ( VikTrackpoint ) );
84 *new_tp = *((VikTrackpoint *)(tp_iter->data));
85 new_tr->trackpoints = g_list_append ( new_tr->trackpoints, new_tp );
86 tp_iter = tp_iter->next;
87 }
88 vik_track_set_comment(new_tr,tr->comment);
89 return new_tr;
90}
91
92VikTrackpoint *vik_trackpoint_new()
93{
94 return g_malloc0(sizeof(VikTrackpoint));
95}
96
97void vik_trackpoint_free(VikTrackpoint *tp)
98{
99 g_free(tp);
100}
101
102VikTrackpoint *vik_trackpoint_copy(VikTrackpoint *tp)
103{
104 VikTrackpoint *rv = vik_trackpoint_new();
105 *rv = *tp;
106 return rv;
107}
108
109gdouble vik_track_get_length(const VikTrack *tr)
110{
111 gdouble len = 0.0;
112 if ( tr->trackpoints )
113 {
114 GList *iter = tr->trackpoints->next;
115 while (iter)
116 {
117 if ( ! VIK_TRACKPOINT(iter->data)->newsegment )
118 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
119 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
120 iter = iter->next;
121 }
122 }
123 return len;
124}
125
126gdouble vik_track_get_length_including_gaps(const VikTrack *tr)
127{
128 gdouble len = 0.0;
129 if ( tr->trackpoints )
130 {
131 GList *iter = tr->trackpoints->next;
132 while (iter)
133 {
134 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
135 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
136 iter = iter->next;
137 }
138 }
139 return len;
140}
141
142gulong vik_track_get_tp_count(const VikTrack *tr)
143{
144 gulong num = 0;
145 GList *iter = tr->trackpoints;
146 while ( iter )
147 {
148 num++;
149 iter = iter->next;
150 }
151 return num;
152}
153
154gulong vik_track_get_dup_point_count ( const VikTrack *tr )
155{
156 gulong num = 0;
157 GList *iter = tr->trackpoints;
158 while ( iter )
159 {
160 if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
161 &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
162 num++;
163 iter = iter->next;
164 }
165 return num;
166}
167
168void vik_track_remove_dup_points ( VikTrack *tr )
169{
170 GList *iter = tr->trackpoints;
171 while ( iter )
172 {
173 if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord),
174 &(VIK_TRACKPOINT(iter->next->data)->coord) ) )
175 {
176 g_free ( iter->next->data );
177 tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next );
178 }
179 else
180 iter = iter->next;
181 }
182}
183
184guint vik_track_get_segment_count(const VikTrack *tr)
185{
186 guint num = 1;
187 GList *iter = tr->trackpoints;
188 if ( !iter )
189 return 0;
190 while ( (iter = iter->next) )
191 {
192 if ( VIK_TRACKPOINT(iter->data)->newsegment )
193 num++;
194 }
195 return num;
196}
197
198VikTrack **vik_track_split_into_segments(VikTrack *t, guint *ret_len)
199{
200 VikTrack **rv;
201 VikTrack *tr;
202 guint i;
203 guint segs = vik_track_get_segment_count(t);
204 GList *iter;
205
206 if ( segs < 2 )
207 {
208 *ret_len = 0;
209 return NULL;
210 }
211
212 rv = g_malloc ( segs * sizeof(VikTrack *) );
213 tr = vik_track_copy ( t );
214 rv[0] = tr;
215 iter = tr->trackpoints;
216
217 i = 1;
218 while ( (iter = iter->next) )
219 {
220 if ( VIK_TRACKPOINT(iter->data)->newsegment )
221 {
222 iter->prev->next = NULL;
223 iter->prev = NULL;
224 rv[i] = vik_track_new();
225 if ( tr->comment )
226 vik_track_set_comment ( rv[i], tr->comment );
227 rv[i]->visible = tr->visible;
228 rv[i]->trackpoints = iter;
229 i++;
230 }
231 }
232 *ret_len = segs;
233 return rv;
234}
235
236void vik_track_reverse ( VikTrack *tr )
237{
238 GList *iter;
239 tr->trackpoints = g_list_reverse(tr->trackpoints);
240
241 /* fix 'newsegment' */
242 iter = g_list_last ( tr->trackpoints );
243 while ( iter )
244 {
245 if ( ! iter->next ) /* last segment, was first, cancel newsegment. */
246 VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
247 if ( ! iter->prev ) /* first segment by convention has newsegment flag. */
248 VIK_TRACKPOINT(iter->data)->newsegment = TRUE;
249 else if ( VIK_TRACKPOINT(iter->data)->newsegment && iter->next )
250 {
251 VIK_TRACKPOINT(iter->next->data)->newsegment = TRUE;
252 VIK_TRACKPOINT(iter->data)->newsegment = FALSE;
253 }
254 iter = iter->prev;
255 }
256}
257
258gdouble vik_track_get_average_speed(const VikTrack *tr)
259{
260 gdouble len = 0.0;
261 guint32 time = 0;
262 if ( tr->trackpoints )
263 {
264 GList *iter = tr->trackpoints->next;
265 while (iter)
266 {
267 if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
268 VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
269 (! VIK_TRACKPOINT(iter->data)->newsegment) )
270 {
271 len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
272 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
273 time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
274 }
275 iter = iter->next;
276 }
277 }
278 return (time == 0) ? 0 : ABS(len/time);
279}
280
281gdouble vik_track_get_max_speed(const VikTrack *tr)
282{
283 gdouble maxspeed = 0.0, speed = 0.0;
284 if ( tr->trackpoints )
285 {
286 GList *iter = tr->trackpoints->next;
287 while (iter)
288 {
289 if ( VIK_TRACKPOINT(iter->data)->has_timestamp &&
290 VIK_TRACKPOINT(iter->prev->data)->has_timestamp &&
291 (! VIK_TRACKPOINT(iter->data)->newsegment) )
292 {
293 speed = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) )
294 / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp);
295 if ( speed > maxspeed )
296 maxspeed = speed;
297 }
298 iter = iter->next;
299 }
300 }
301 return maxspeed;
302}
303
304void vik_track_convert ( VikTrack *tr, VikCoordMode dest_mode )
305{
306 GList *iter = tr->trackpoints;
307 while (iter)
308 {
309 vik_coord_convert ( &(VIK_TRACKPOINT(iter->data)->coord), dest_mode );
310 iter = iter->next;
311 }
312}
313
314/* I understood this when I wrote it ... maybe ... Basically it eats up the
315 * proper amounts of length on the track and averages elevation over that. */
316gdouble *vik_track_make_elevation_map ( const VikTrack *tr, guint16 num_chunks )
317{
318 gdouble *pts;
319 gdouble total_length, chunk_length, current_dist, current_area_under_curve, current_seg_length, dist_along_seg = 0.0;
320 gdouble altitude1, altitude2;
321 guint16 current_chunk;
322 gboolean ignore_it = FALSE;
323
324 GList *iter = tr->trackpoints;
325
326 g_assert ( num_chunks < 16000 );
327
328 pts = g_malloc ( sizeof(gdouble) * num_chunks );
329
330 total_length = vik_track_get_length_including_gaps ( tr );
331 chunk_length = total_length / num_chunks;
332
333 current_dist = 0.0;
334 current_area_under_curve = 0;
335 current_chunk = 0;
336 current_seg_length = 0;
337
338 current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
339 &(VIK_TRACKPOINT(iter->next->data)->coord) );
340 altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
341 altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
342 dist_along_seg = 0;
343
344 while ( current_chunk < num_chunks ) {
345
346 /* go along current seg */
347 if ( current_seg_length && (current_seg_length - dist_along_seg) > chunk_length ) {
348 dist_along_seg += chunk_length;
349
350 /* /
351 * pt2 *
352 * /x altitude = alt_at_pt_1 + alt_at_pt_2 / 2 = altitude1 + slope * dist_value_of_pt_inbetween_pt1_and_pt2
353 * /xx avg altitude = area under curve / chunk len
354 *pt1 *xxx avg altitude = altitude1 + (altitude2-altitude1)/(current_seg_length)*(dist_along_seg + (chunk_len/2))
355 * / xxx
356 * / xxx
357 **/
358
359 if ( ignore_it )
360 pts[current_chunk] = VIK_DEFAULT_ALTITUDE;
361 else
362 pts[current_chunk] = altitude1 + (altitude2-altitude1)*((dist_along_seg + (chunk_length/2))/current_seg_length);
363
364 current_chunk++;
365 } else {
366 /* finish current seg */
367 if ( current_seg_length ) {
368 gdouble altitude_at_dist_along_seg = altitude1 + (altitude2-altitude1)/(current_seg_length)*dist_along_seg;
369 current_dist = current_seg_length - dist_along_seg;
370 current_area_under_curve = current_dist*(altitude_at_dist_along_seg + altitude2)*0.5;
371 } else { current_dist = current_area_under_curve = 0; } /* should only happen if first current_seg_length == 0 */
372
373 /* get intervening segs */
374 iter = iter->next;
375 while ( iter && iter->next ) {
376 current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
377 &(VIK_TRACKPOINT(iter->next->data)->coord) );
378 altitude1 = VIK_TRACKPOINT(iter->data)->altitude;
379 altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude;
380 ignore_it = VIK_TRACKPOINT(iter->next->data)->newsegment;
381
382 if ( chunk_length - current_dist >= current_seg_length ) {
383 current_dist += current_seg_length;
384 current_area_under_curve += current_seg_length * (altitude1+altitude2) * 0.5;
385 iter = iter->next;
386 } else {
387 break;
388 }
389 }
390
391 /* final seg */
392 dist_along_seg = chunk_length - current_dist;
393 if ( ignore_it )
394 pts[current_chunk] = current_area_under_curve / current_dist;
395 else {
396 current_area_under_curve += dist_along_seg * (altitude1 + (altitude2 - altitude1)*dist_along_seg/current_seg_length);
397 pts[current_chunk] = current_area_under_curve / chunk_length;
398 }
399
400 current_dist = 0;
401 current_chunk++;
402 }
403 }
404
405 return pts;
406}
407
408
409void vik_track_get_total_elevation_gain(const VikTrack *tr, gdouble *up, gdouble *down)
410{
411 gdouble diff;
412 *up = *down = 0;
413 if ( tr->trackpoints )
414 {
415 GList *iter = tr->trackpoints->next;
416 while (iter)
417 {
418 diff = VIK_TRACKPOINT(iter->data)->altitude - VIK_TRACKPOINT(iter->prev->data)->altitude;
419 if ( diff > 0 )
420 *up += diff;
421 else
422 *down -= diff;
423 iter = iter->next;
424 }
425 }
426}