]> err.no Git - mapper/blob - src/path.c
Path: misc adjustments
[mapper] / src / path.c
1 /*
2  * This file is part of mapper
3  *
4  * Copyright (C) 2008 Kaj-Michael Lang
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 along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 #include <config.h>
21
22 #include <glib.h>
23 #include <gtk/gtk.h>
24 #include <sqlite3.h>
25 #include <libxml/parser.h>
26 #include <libgnomevfs/gnome-vfs.h>
27
28 #include "path.h"
29 #include "position.h"
30 #include "utils.h"
31 #include "settings.h"
32 #include "latlon.h"
33
34 struct sql_select_stmt {
35         sqlite3_stmt *select_paths;
36         sqlite3_stmt *select_path_nodes;
37
38         sqlite3_stmt *insert_path;
39         sqlite3_stmt *insert_path_node;
40
41         sqlite3_stmt *delete_path;
42         sqlite3_stmt *delete_path_nodes;
43 };
44 static struct sql_select_stmt sql;
45
46 #define PATH_TABLE_PATHS "create table IF NOT EXISTS paths ( \
47         nid             int primary key, \
48         name    text not null, \
49         desc    text, \
50         t               int not null);"
51
52 #define PATH_TABLE_NODES "create table IF NOT EXISTS path_nodes ( \
53         nid             int not null, \
54         lat     real not null, \
55         lon     real not null, \
56         sats    int, \
57         speed   real, \
58         course  real, \
59         pdop    real, \
60         hdop    real, \
61         vdop    real, \
62         name    text default null, \
63         t               int not null);"
64
65 #define PATH_SQL_SELECT_TRACKS "select id,name,slat,slon,elat,elon,len,sdate,edate from tracks order by sdate"
66 #define PATH_SQL_INSERT_TRACK "insert into tracks (id,name,sloc,sdate) values (?,?,?,?);
67 #define PATH_SQL_INSERT_TRACK_POINT "insert into trackpoints (tid,dt,lat,lon,alt,hdop,vdop,pdop,sat,fix) values (?,?,?,?,?,?,?,?,?,?)"
68 #define PATH_SQL_SELECT_TRACK_POINTS "select tid,dt,lat,lon,alt,hdop,vdop,pdop,sat,fix from trackpoints where tid=? order by dt"
69
70 enum {
71         NEW_POINT,                              /* A new point was appended to track track */
72         NEW_BREAK,                              /* A break was appended to the track */
73         NEW_WAYPOINT,                   /* A new waypoint/marker has been added */
74         NEAR_WAYPOINT,                  /* We are near the next route waypoint, announce it */
75         REACHED_WAYPOINT,               /* We have reached the next route waypoint, announce it */
76         REACHED_DESTINATION,    /* We have reached the last route waypoint */
77         CLEARED,                                /* Path was cleared */
78         LAST_SIGNAL
79 };
80 static guint32 signals[LAST_SIGNAL] = {0};
81
82 /** This enum defines the states of the SAX parsing state machine. */
83 typedef enum {
84         START=1,
85         INSIDE_GPX=100,
86         INSIDE_METADATA,
87         INSIDE_PATH,
88         INSIDE_PATH_SEGMENT,
89         INSIDE_PATH_POINT,
90         INSIDE_PATH_POINT_ELE,
91         INSIDE_PATH_POINT_TIME,
92         INSIDE_PATH_POINT_DESC,
93         INSIDE_PATH_POINT_NAME,
94         FINISH=5000,
95         UNKNOWN=6666,
96         ERROR=9999,
97 } SaxState;
98
99 /** Data used during the SAX parsing operation. */
100 typedef struct _SaxData SaxData;
101 struct _SaxData {
102         Path *path;
103         SaxState state;
104         SaxState prev_state;
105         guint unknown_depth;
106         gboolean at_least_one_trkpt;
107         GString *chars;
108 };
109
110 static gchar XML_TZONE[7];
111
112 G_DEFINE_TYPE(Path, path, G_TYPE_OBJECT);
113
114 #define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PATH_TYPE, PathPrivate))
115
116 static void
117 path_dispose(GObject *object)
118 {
119 g_debug("path_dispose");
120
121 G_OBJECT_CLASS(path_parent_class)->dispose(object);
122 }
123
124 static void
125 path_finalize(GObject *object)
126 {
127 Path *path=PATH(object);
128
129 g_debug("path_finalize");
130 MACRO_PATH_FREE(*path);
131
132 if (path->name)
133         g_free(path->name);
134 if (path->desc)
135         g_free(path->desc);
136 if (path->author)
137         g_free(path->author);
138 if (path->keywords)
139         g_free(path->keywords);
140 if (path->copyright)
141         g_free(path->copyright);
142 if (path->src)
143         g_free(path->src);
144 }
145
146 static void
147 path_class_init(PathClass *klass)
148 {
149 GObjectClass *object_class=G_OBJECT_CLASS(klass);
150
151 g_debug("path_class_init");
152
153 object_class->dispose=path_dispose;
154 object_class->finalize=path_finalize;
155 /* g_type_class_add_private (klass, sizeof(PathPrivate)); */
156
157 signals[NEW_POINT]=g_signal_new("new-point", G_OBJECT_CLASS_TYPE(object_class),
158         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(PathClass, new_point),
159         NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
160 signals[NEW_BREAK]=g_signal_new("new-break", G_OBJECT_CLASS_TYPE(object_class),
161         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(PathClass, new_break),
162         NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
163 signals[NEW_WAYPOINT]=g_signal_new("new-waypoint", G_OBJECT_CLASS_TYPE(object_class),
164         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(PathClass, new_waypoint),
165         NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
166 }
167
168 static void
169 path_init(Path *path)
170 {
171 g_debug("path_init");
172 MACRO_PATH_INIT(*path);
173 }
174
175 Path *
176 path_new(PathType type, guint id)
177 {
178 Path *p;
179 static time_t time1=0;
180 struct tm time2;
181
182 p=g_object_new(PATH_TYPE, NULL);
183 p->type=type;
184 p->id=id;
185 p->sensitivity=3;
186
187 if (time1==0) {
188         time1=time(NULL);
189         localtime_r(&time1, &time2);
190         g_snprintf(XML_TZONE, sizeof(XML_TZONE), "%+03ld:%02ld", (time2.tm_gmtoff / 60 / 60), (time2.tm_gmtoff / 60) % 60);
191 }
192
193 return p;
194 }
195
196 void
197 path_free(Path *path)
198 {
199 g_return_if_fail(path);
200 g_object_unref(path);
201 }
202
203 void
204 path_clear(Path *path)
205 {
206 g_return_if_fail(path);
207 MACRO_PATH_FREE(*path);
208 path->length=path->avgspeed=0.0;
209 path->points=0;
210 }
211
212 static gboolean
213 path_resize(Path *path, guint size)
214 {
215 g_return_val_if_fail(path, FALSE);
216
217 if (path->head + size != path->cap) {
218         WayPoint *curr;
219         Point *old_head = path->head;
220
221         path->head = g_renew(Point, old_head, size);
222         g_assert(path->head);
223         path->cap = path->head + size;
224         if (path->head != old_head) {
225                 path->tail = path->head + (path->tail - old_head);
226
227                 /* Adjust all of the waypoints. */
228                 for (curr = path->whead - 1; curr++ != path->wtail;)
229                         curr->point = path->head + (curr->point - old_head);
230         }
231         return TRUE;
232 }
233 return FALSE;
234 }
235
236 static gboolean 
237 path_wresize(Path *path, guint wsize)
238 {
239 g_return_val_if_fail(path, FALSE);
240
241 if (path->whead + wsize != path->wcap) {
242         WayPoint *old_whead = path->whead;
243
244         path->whead = g_renew(WayPoint, old_whead, wsize);
245         path->wtail = path->whead + (path->wtail - old_whead);
246         path->wcap = path->whead + wsize;
247
248         return TRUE;
249 }
250 return FALSE;
251 }
252
253 /**
254  * path_add_waypoint:
255  * @path
256  * @lat
257  * @lon
258  * @desc
259  *
260  * Append a waypoint to path at given lat, lon with description desc.
261  *
262  * Returns: TRUE
263  */
264 gboolean
265 path_add_waypoint(Path *path, gdouble lat, gdouble lon, gchar *desc)
266 {
267 guint unitx, unity;
268
269 latlon2unit(lat, lon, unitx, unity);
270 MACRO_PATH_INCREMENT_TAIL(*path);
271 path->tail->unitx=unitx;
272 path->tail->unity=unity;
273 path->tail->time=0;
274 path->tail->altitude=NAN;
275
276 MACRO_PATH_INCREMENT_WTAIL(*path);
277 path->wtail->point=path->tail;
278 path->wtail->desc=desc;
279
280 g_signal_emit(G_OBJECT(path), signals[NEW_WAYPOINT], 0, NULL);
281
282 return TRUE;
283 }
284
285 /**
286  * path_add_latlon:
287  * @path
288  * @lat
289  * @lon
290  * @ptime
291  * @speed
292  * @altitude
293  *
294  * Append a point with given lat,lon,ptime,speed and altitude to given path.
295  *
296  * Returns: TRUE if the new point was added. FALSE is returned if new point distance was under sensitivity setting.
297  */
298 gboolean 
299 path_add_latlon(Path *path, gdouble lat, gdouble lon, time_t ptime, gfloat speed, gfloat altitude)
300 {
301 guint unitx, unity;
302
303 latlon2unit(lat, lon, unitx, unity);
304
305 if (abs((gint)unitx-path->tail->unitx) > path->sensitivity || abs((gint)unity-path->tail->unity) > path->sensitivity) {
306         if (path->tail->unity && path->tail->unitx) {
307                 gdouble plat, plon;
308
309                 unit2latlon(path->tail->unitx, path->tail->unity, lat, lon);
310                 path->length+=calculate_distance(plat, plon, lat, lon);
311         }
312         MACRO_PATH_INCREMENT_TAIL(*path);
313         path->tail->unitx=unitx;
314         path->tail->unity=unity;
315         path->tail->time=ptime;
316         path->tail->altitude=altitude;
317         if (speed>path->maxspeed)
318                 path->maxspeed=speed;
319         path->tspeed+=speed;
320         path->avgspeed=(path->points>0) ? path->tspeed/path->points : 0.0;
321         path->points++;
322         g_debug("TRACK: %f %f (%d)", path->length, path->avgspeed, path->points);
323
324         g_signal_emit(G_OBJECT(path), signals[NEW_POINT], 0, NULL);
325         return TRUE;
326 }
327
328 return FALSE;
329
330 }
331
332 /**
333  * path_add_break:
334  * @path
335  *
336  * Add a break point to the path.
337  * 
338  * Returns: TRUE if break was added, FALSE if last point was a break already.
339  */
340 gboolean 
341 path_add_break(Path *path)
342 {
343 g_return_val_if_fail(path, FALSE);
344 g_return_val_if_fail(path->tail, FALSE);
345
346 if (path->tail->unity && path->tail->unitx) {
347         /* To mark a "break" in a track, we'll add a (0, 0) point and then 
348            another instance of the most recent track point. */
349
350         MACRO_PATH_INCREMENT_TAIL(*path);
351         *path->tail=_point_null;
352         MACRO_PATH_INCREMENT_TAIL(*path);
353         *path->tail=path->tail[-2];
354
355         g_signal_emit(G_OBJECT(path), signals[NEW_BREAK], 0, NULL);
356
357         return TRUE;
358 }
359 return FALSE;
360 }
361
362 /**
363  * path_has_points:
364  * @path
365  *
366  * Checks if given path has any path points
367  *
368  * Returns: TRUE if there are points, FALSE otherwise
369  */
370 gboolean
371 path_has_points(Path *path)
372 {
373 g_return_val_if_fail(path, FALSE);
374 return path->head==path->tail ? FALSE : TRUE;
375 }
376
377 /**
378  * path_has_waypoints:
379  * 
380  * Checks if given path has any waypoints
381  * 
382  * Returns: TRUE if there are waypoints, FALSE otherwise
383  */
384 gboolean
385 path_has_waypoints(Path *path)
386 {
387 g_return_val_if_fail(path, FALSE);
388 return path->whead==path->wtail ? FALSE : TRUE;
389 }
390
391 /**
392  * path_find_last_point:
393  * @path
394  *
395  * Returns: The last path point or NULL if path is empty
396  */
397 Point *
398 path_find_last_point(Path *path)
399 {
400 Point *p=NULL;
401
402 g_return_val_if_fail(path, NULL);
403
404 if (!path_has_points(path))
405         return p;
406
407 for (p=path->tail; !p->unity; p--) {
408 }
409 return p;
410 }
411
412 /**
413  * Updates near_point, next_way, and next_wpt.  If quick is FALSE (as
414  * it is when this function is called from route_find_nearest_point), then
415  * the entire list (starting from near_point) is searched.  Otherwise, we
416  * stop searching when we find a point that is farther away.
417  */
418 static gboolean 
419 path_update_nears(Path *path, Point *point, gboolean quick)
420 {
421 gboolean ret = FALSE;
422 Point *curr, *near;
423 WayPoint *wcurr, *wnext;
424 guint64 near_dist_squared;
425
426 g_return_val_if_fail(path, FALSE);
427 g_return_val_if_fail(point, FALSE);
428
429 /* If we have waypoints (_next_way != NULL), then determine the "next
430  * waypoint", which is defined as the waypoint after the nearest point,
431  * UNLESS we've passed that waypoint, in which case the waypoint after
432  * that waypoint becomes the "next" waypoint. */
433 if (path->next_way) {
434         /* First, set near_dist_squared with the new distance from near_point. */
435         near = path->near_point;
436         near_dist_squared = DISTANCE_SQUARED(*point, *near);
437
438         /* Now, search path for a closer point.  If quick is TRUE, then we'll
439          * only search forward, only as long as we keep finding closer points.
440          */
441         for (curr = path->near_point; curr++ != path->tail;) {
442                 if (curr->unity) {
443                         guint dist_squared = DISTANCE_SQUARED(*point, *curr);
444                         if (dist_squared <= near_dist_squared) {
445                                 near = curr;
446                                 near_dist_squared = dist_squared;
447                         } else if (quick)
448                                 break;
449                 }
450         }
451
452         /* Update _near_point. */
453         path->near_point = near;
454         path->near_point_dist_squared = near_dist_squared;
455
456         for (wnext = wcurr = path->next_way; wcurr != path->wtail; wcurr++) {
457                 if (wcurr->point < near || (wcurr->point == near && quick 
458                                 && (path->next_wpt && (DISTANCE_SQUARED(*point, *near) > path->next_way_dist_squared
459                                 && DISTANCE_SQUARED(*point, *path->next_wpt) < path->next_wpt_dist_squared))))
460                     /* Okay, this else if expression warrants explanation.  If the
461                      * nearest track point happens to be a waypoint, then we want to
462                      * check if we have "passed" that waypoint.  To check this, we
463                      * test the distance from _gps to the waypoint and from _gps to
464                      * _next_wpt, and if the former is increasing and the latter is
465                      * decreasing, then we have passed the waypoint, and thus we
466                      * should skip it.  Note that if there is no _next_wpt, then
467                      * there is no next waypoint, so we do not skip it in that case. */
468                         wnext = wcurr + 1;
469                 else
470                         break;
471         }
472
473         if (wnext == path->wtail && (wnext->point < near || (wnext->point == near && quick
474                                           && (path->next_wpt && (DISTANCE_SQUARED (*point, *near) > path->next_way_dist_squared
475                                                && DISTANCE_SQUARED(*point, *path->next_wpt) < path->next_wpt_dist_squared)))))
476         {
477                 path->next_way = NULL;
478                 path->next_wpt = NULL;
479                 path->next_way_dist_squared = -1;
480                 path->next_wpt_dist_squared = -1;
481                 ret = TRUE;
482         }
483         /* Only update next_way (and consequently _next_wpt) if _next_way is
484          * different, and record that fact for return. */
485         else {
486                 if (!quick || path->next_way != wnext) {
487                         path->next_way = wnext;
488                         path->next_wpt = wnext->point;
489                         if (path->next_wpt == path->tail)
490                                 path->next_wpt = NULL;
491                         else {
492                                 while (!(++path->next_wpt)->unity) {
493                                         if (path->next_wpt == path->tail) {
494                                                 path->next_wpt = NULL;
495                                                 break;
496                                         }
497                                 }
498                         }
499                         ret = TRUE;
500                 }
501                 path->next_way_dist_squared = DISTANCE_SQUARED(*point, *wnext->point);
502                 if (path->next_wpt)
503                         path->next_wpt_dist_squared = DISTANCE_SQUARED(*point, *path->next_wpt);
504         }
505 }
506 return ret;
507 }
508
509 /**
510  * path_find_nearest_point:
511  *
512  * Reset the near_point data by searching the entire path for the nearest point and waypoint.
513  */
514 void 
515 path_find_nearest_point(Path *path, gdouble lat, gdouble lon)
516 {
517 Point p;
518
519 g_return_if_fail(path);
520
521 /* Initialize near_point to first non-zero point. */
522 path->near_point=path->head;
523 while (!path->near_point->unity && path->near_point != path->tail)
524         path->near_point++;
525
526 /* Initialize next_way */
527 if (path->wtail==path->whead)
528         path->next_way=NULL;
529 else
530         path->next_way=path->whead;
531 path->next_way_dist_squared=-1;
532
533 /* Initialize next_wpt */
534 path->next_wpt=NULL;
535 path->next_wpt_dist_squared=-1;
536
537 latlon2unit(lat, lon, p.unitx, p.unity);
538 path_update_nears(path, &p, FALSE);
539 }
540
541 /**
542  * path_find_nearest_waypoint:
543  *
544  */
545 WayPoint *
546 path_find_nearest_waypoint(Path *path, guint unitx, guint unity)
547 {
548 WayPoint *wcurr;
549 WayPoint *wnear;
550 guint64 nearest_squared;
551 Point pos = { unitx, unity, 0, NAN };
552
553 g_return_val_if_fail(path, NULL);
554
555 wcurr=wnear=path->whead;
556 if (wcurr && wcurr->point && wcurr!=path->wtail) {
557         nearest_squared=DISTANCE_SQUARED(pos, *(wcurr->point));
558
559         while (wcurr++!=path->wtail) {
560                 guint64 test_squared=DISTANCE_SQUARED(pos, *(wcurr->point));
561                 if (test_squared < nearest_squared) {
562                         wnear=wcurr;
563                         nearest_squared=test_squared;
564                 }
565         }
566 }
567
568 if (wnear && wnear->point) {
569         if (abs(unitx - wnear->point->unitx) < path->sensitivity && abs(unity - wnear->point->unity) < path->sensitivity)
570                 return wnear;
571 }
572
573 return NULL;
574 }
575
576 /**
577  * path_check_waypoint_announce:
578  * @path
579  * @gps 
580  *
581  * Check if we should announce upcoming waypoint.
582  *
583  */
584 static void
585 path_check_waypoint_announce(Path *path, GpsData *gps)
586 {
587 guint a_thres_near, a_thres_at;
588
589 g_return_if_fail(path);
590 g_return_if_fail(gps);
591
592 if (!path->next_way)
593         return;
594
595 a_thres_near=(20+(guint)gps->speed)*path->announce_notice_ratio*3;
596 a_thres_at=(20+(guint)gps->speed);
597
598 if (path->next_way_dist_squared<(a_thres_near * a_thres_near) && path->next_way!=path->announced_waypoint) {
599         g_signal_emit(G_OBJECT(path), signals[NEAR_WAYPOINT], 0, NULL);
600         path->announced_waypoint=path->next_way;
601 } else if (path->next_way_dist_squared<(a_thres_at * a_thres_at) && path->next_way!=path->announced_waypoint) {
602         g_signal_emit(G_OBJECT(path), signals[REACHED_WAYPOINT], 0, NULL);
603         path->announced_waypoint=path->next_way;
604 }
605 }
606
607 /******************************************************************************/
608
609 /**
610  *
611  *
612  */
613 gdouble
614 path_calculate_distance_from(Path *path, Point *point)
615 {
616 Point *curr;
617 gdouble lat1, lon1, lat2, lon2;
618 gdouble sum=0.0;
619
620 for (curr = path->tail; curr > point; --curr) {
621         if (curr->unity) {
622                 unit2latlon(curr->unitx, curr->unity, &lat2, &lon2);
623                 sum += calculate_distance(lat1, lon1, lat2, lon2);
624                 lat1 = lat2;
625                 lon1 = lon2;
626         }
627 }
628 return sum;
629 }
630
631 /**
632  * path_get_distance_to:
633  * @path
634  * @point
635  * @lat
636  * @lon
637  *
638  * Calculate distancance from lat,lon to point in given path.
639  *
640  * Returns: The distance, or 0.0 if path or point is invalid.
641  */
642 gdouble
643 path_get_distance_to(Path *path, Point *point, gdouble lat, gdouble lon)
644 {
645 gdouble lat1, lon1, lat2, lon2;
646 gdouble sum=0.0;
647
648 g_return_val_if_fail(path, 0.0);
649
650 /* If point is NULL, use the next waypoint. */
651 if (point == NULL && path->next_way)
652         point = path->next_way->point;
653
654 /* If point is still NULL, return an error. */
655 if (point==NULL)
656         return FALSE;
657
658 lat1=lat;
659 lon1=lon;
660
661 if (point > path->near_point) {
662         Point *curr;
663         /* Skip _near_point in case we have already passed it. */
664         for (curr = path->near_point + 1; curr <= point; ++curr) {
665                 if (curr->unity) {
666                         unit2latlon(curr->unitx, curr->unity, lat2, lon2);
667                         sum += calculate_distance(lat1, lon1, lat2, lon2);
668                         lat1 = lat2;
669                         lon1 = lon2;
670                 }
671         }
672 } else if (point < path->near_point) {
673         Point *curr;
674         /* Skip near_point in case we have already passed it. */
675         for (curr = path->near_point - 1; curr >= point; --curr) {
676                 if (curr->unity) {
677                         unit2latlon(curr->unitx, curr->unity, lat2, lon2);
678                         sum += calculate_distance(lat1, lon1, lat2, lon2);
679                         lat1 = lat2;
680                         lon1 = lon2;
681                 }
682         }
683 } else {
684         /* Waypoint _is_ the nearest point. */
685         unit2latlon(path->near_point->unitx, path->near_point->unity, lat2, lon2);
686         sum += calculate_distance(lat1, lon1, lat2, lon2);
687 }
688 return sum;
689 }
690
691 /******************************************************************************/
692
693 gboolean
694 path_load(Path *path, const gchar *file)
695 {
696 gchar *pfile;
697 gchar *bytes;
698 gint size;
699
700 g_return_val_if_fail(path, FALSE);
701 g_return_val_if_fail(file, FALSE);
702
703 if (gnome_vfs_read_entire_file(file, &size, &bytes)==GNOME_VFS_OK)
704         path_gpx_parse(path, bytes, size, GPX_PATH_NEW);
705 g_free(pfile);
706 return TRUE;
707 }
708
709 gboolean 
710 path_save(Path *path, const gchar *file)
711 {
712 GnomeVFSHandle *handle;
713 gchar *tfile;
714
715 g_return_val_if_fail(path, FALSE);
716 g_return_val_if_fail(file, FALSE);
717
718 if (gnome_vfs_create(&handle, file, GNOME_VFS_OPEN_WRITE, FALSE, 0600)==GNOME_VFS_OK) {
719         path_gpx_write(path, handle, NULL);
720         gnome_vfs_close(handle);
721 }
722 g_free(tfile);
723 return TRUE;
724 }
725
726 /******************************************************************************/
727
728 gboolean
729 path_set_destination_from_last(Path *path, Position *pos)
730 {
731 Point *p;
732 gdouble lat,lon;
733
734 g_return_val_if_fail(path, FALSE);
735 g_return_val_if_fail(pos, FALSE);
736
737 if (path->head==path->tail) {
738         position_set(pos, FALSE, NAN, NAN, NAN);
739         return FALSE;
740 }
741
742 p=path_find_last_point(path);
743 if (p) {
744         unit2latlon(p->unitx, p->unity, &lat, &lon);
745         position_set(pos, TRUE, lat, lon, 0);
746 } else {
747         position_set(pos, FALSE, NAN, NAN, NAN);
748         return FALSE;
749 }
750 return TRUE;
751 }
752
753 /**
754  * path_get_waypoint_latlon:
755  * @way
756  * @lat
757  * @lon
758  *
759  * Get the real latitude and longitude of given waypoint
760  *
761  * Returns: TRUE if waypoint was valid, FALSE otherwise.
762  */
763 gboolean
764 path_get_waypoint_latlon(WayPoint *way, gdouble *lat, gdouble *lon)
765 {
766 gdouble tlat, tlon;
767
768 g_return_val_if_fail(way, FALSE);
769 g_return_val_if_fail(way->point, FALSE);
770 g_return_val_if_fail(lat, FALSE);
771 g_return_val_if_fail(lon, FALSE);
772
773 unit2latlon(way->point->unitx, way->point->unity, tlat, tlon);
774
775 *lat=tlat;
776 *lon=tlon;
777
778 return TRUE;
779 }
780
781 /**
782  * path_insert_mark_text:
783  * @path
784  * @text
785  *
786  * Add a new waypoint to path with given text at the last point.
787  *
788  */
789 void 
790 path_insert_mark_text(Path *path, gchar *text)
791 {
792 g_return_if_fail(path);
793 MACRO_PATH_INCREMENT_WTAIL(*path);
794 path->wtail->point=path->tail;
795 if (text) {
796         path->wtail->desc=text;
797 } else {
798         path->wpcnt++;
799         path->wtail->desc=g_strdup_printf("WPT:#%u", path->wpcnt);
800 }
801 g_signal_emit(G_OBJECT(path), signals[NEW_WAYPOINT], 0, NULL);
802 }
803
804 /**
805  * path_insert_mark_autonumber:
806  * @path
807  *
808  * Add autonumbered waypoint to given path
809  *
810  */
811 void
812 path_insert_mark_autonumber(Path *path)
813 {
814 path_insert_mark_text(path, NULL);
815 }
816
817 /******************************************************************************/
818
819 #define PATH_GPX_WRITE_ERROR path_gpx_write_error_quark()
820
821 static GQuark
822 path_gpx_write_error_quark(void)
823 {
824 return g_quark_from_static_string ("path-gpx-write-error-quark");
825 }
826
827 #define XML_DATE_FORMAT "%FT%T"
828
829 #define WRITE_STRING(string) { \
830         if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write(handle, (string), strlen((string)), &size))) { \
831                 g_set_error(error, \
832                         PATH_GPX_WRITE_ERROR, G_FILE_ERROR_FAILED, \
833                         "%s:\n%s\n%s", \
834                         _("Error while writing to file"), \
835                         _("File is incomplete."), \
836                         gnome_vfs_result_to_string(vfs_result)); \
837                 return FALSE; \
838         } \
839 }
840
841 gboolean 
842 path_gpx_write(Path *path, GnomeVFSHandle *handle, GError **error)
843 {
844 Point *curr = NULL;
845 WayPoint *wcurr = NULL;
846 gboolean trkseg_break = FALSE;
847 gchar buffer[80];
848 time_t gpx_time;
849 GnomeVFSResult vfs_result;
850 GnomeVFSFileSize size;
851
852 g_return_val_if_fail((error == NULL || *error == NULL), FALSE);
853
854 gpx_time = time(NULL);
855
856 /* Find first non-zero point. */
857 for (curr = path->head - 1, wcurr = path->whead; curr++ != path->tail;) {
858         if (curr->unity)
859                 break;
860         else if (wcurr && curr == wcurr->point)
861                 wcurr++;
862 }
863
864 /* Write the header. */
865 WRITE_STRING("<?xml version=\"1.0\"?>\n"
866         "<gpx version=\"1.0\" creator=\"Mapper\" xmlns=\"http://www.topografix.com/GPX/1/0\">\n");
867
868 /* Write any metadata */
869 WRITE_STRING("<metadata>\n");
870
871 WRITE_STRING("<time>");
872 strftime(buffer, sizeof(buffer), XML_DATE_FORMAT, localtime(&gpx_time));
873 WRITE_STRING(buffer);
874 WRITE_STRING(XML_TZONE);
875 WRITE_STRING("</time>\n");
876
877 WRITE_STRING("</metadata>\n");
878
879 /* Write track(s) and waypoint(s) */
880 WRITE_STRING("<trk>\n<trkseg>\n");
881
882 /* Curr points to first non-zero point. */
883 for (curr--; curr++ != path->tail;) {
884         gdouble lat, lon;
885         if (curr->unity) {
886                 gboolean first_sub = TRUE;
887                 if (trkseg_break) {
888                         /* First trkpt of the segment - write trkseg header. */
889                         WRITE_STRING("</trkseg>\n<trkseg>\n");
890                         trkseg_break = FALSE;
891                 }
892                 unit2latlon(curr->unitx, curr->unity, &lat, &lon);
893                 WRITE_STRING("<trkpt lat=\"");
894                 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lat);
895                 WRITE_STRING(buffer);
896                 WRITE_STRING("\" lon=\"");
897                 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lon);
898                 WRITE_STRING(buffer);
899                 WRITE_STRING("\"");
900
901                 /* write the elevation */
902                 if (!isnan(curr->altitude)) {
903                         if (first_sub) {
904                                 WRITE_STRING(">\n");
905                                 first_sub = FALSE;
906                         }
907                         WRITE_STRING("<ele>");
908                         g_ascii_formatd(buffer, sizeof(buffer), "%.2f", curr->altitude);
909                         WRITE_STRING(buffer);
910                         WRITE_STRING("</ele>\n");
911                 }
912
913                 /* write the time */
914                 if (curr->time) {
915                         if (first_sub) {
916                                 WRITE_STRING(">\n");
917                                 first_sub = FALSE;
918                         }
919                         WRITE_STRING("<time>");
920                         strftime(buffer, sizeof(buffer), XML_DATE_FORMAT, localtime(&curr->time));
921                         WRITE_STRING(buffer);
922                         WRITE_STRING(XML_TZONE);
923                         WRITE_STRING("</time>\n");
924                 }
925
926                 if (wcurr && curr == wcurr->point) {
927                         gchar *desc;
928                         if (first_sub) {
929                                 WRITE_STRING(">\n");
930                                 first_sub = FALSE;
931                         }
932                         if (wcurr->desc) {
933                                 desc=g_markup_printf_escaped("<desc>%s</desc>\n", wcurr->desc);
934                                 WRITE_STRING(desc);
935                                 g_free(desc);
936                         }
937                         wcurr++;
938                 }
939                 if (first_sub) {
940                         WRITE_STRING("/>\n");
941                 } else {
942                         WRITE_STRING("</trkpt>\n");
943                 }
944         } else
945                 trkseg_break = TRUE;
946 }
947
948 /* Write the footer. */
949 WRITE_STRING("</trkseg>\n</trk>\n</gpx>\n");
950
951 return TRUE;
952 }
953
954 /**
955  * Handle a start tag in the parsing of a GPX file.
956  */
957 #define MACRO_SET_UNKNOWN(utag) { \
958     data->prev_state = data->state; \
959     data->state = UNKNOWN; \
960     data->unknown_depth = 1; \
961         g_debug("GPX: unknown tag [%s]", (gchar *)utag); }
962
963 static void
964 gpx_start_element(SaxData *data, const xmlChar *name, const xmlChar **attrs)
965 {
966 switch (data->state) {
967 case ERROR:
968 break;
969 case START:
970         if (!strcmp((gchar *) name, "gpx"))
971                 data->state = INSIDE_GPX;
972         else
973                 MACRO_SET_UNKNOWN(name);
974 break;
975 case INSIDE_GPX:
976         if (!strcmp((gchar *) name, "trk"))
977                 data->state = INSIDE_PATH;
978         else if (!strcmp((gchar *) name, "metadata"))
979                 data->state = INSIDE_METADATA;
980         else
981                 MACRO_SET_UNKNOWN(name);
982 break;
983 case INSIDE_METADATA:
984 break;
985 case INSIDE_PATH:
986         if (!strcmp((gchar *) name, "trkseg")) {
987                 data->state = INSIDE_PATH_SEGMENT;
988                 data->at_least_one_trkpt = FALSE;
989         } else
990                 MACRO_SET_UNKNOWN(name);
991 break;
992 case INSIDE_PATH_SEGMENT:
993         if (!strcmp((gchar *) name, "trkpt")) {
994                 const xmlChar **curr_attr;
995                 gchar *error_check;
996                 gdouble lat = 0.f, lon = 0.f;
997                 gboolean has_lat, has_lon;
998                 has_lat = FALSE;
999                 has_lon = FALSE;
1000                 for (curr_attr = attrs; *curr_attr != NULL;) {
1001                         const gchar *attr_name = *curr_attr++;
1002                         const gchar *attr_val = *curr_attr++;
1003                         if (!strcmp(attr_name, "lat")) {
1004                                 lat = g_ascii_strtod(attr_val, &error_check);
1005                                 if (error_check != attr_val)
1006                                         has_lat = TRUE;
1007                         } else if (!strcmp(attr_name, "lon")) {
1008                                 lon = g_ascii_strtod(attr_val, &error_check);
1009                                 if (error_check != attr_val)
1010                                         has_lon = TRUE;
1011                         }
1012                 }
1013                 if (has_lat && has_lon) {
1014                         path_add_latlon(data->path, lat, lon, 0, 0, NAN);
1015                         data->state = INSIDE_PATH_POINT;
1016                 } else
1017                         data->state = ERROR;
1018         } else
1019                 MACRO_SET_UNKNOWN(name);
1020 break;
1021 case INSIDE_PATH_POINT:
1022         if (!strcmp((gchar *) name, "time"))
1023                 data->state = INSIDE_PATH_POINT_TIME;
1024         else if (!strcmp((gchar *) name, "ele"))
1025                 data->state = INSIDE_PATH_POINT_ELE;
1026         else if (!strcmp((gchar *) name, "desc"))
1027                 data->state = INSIDE_PATH_POINT_DESC;
1028         else if (!strcmp((gchar *) name, "name"))
1029                 data->state = INSIDE_PATH_POINT_NAME;
1030         else
1031                 MACRO_SET_UNKNOWN(name);
1032 break;
1033 case UNKNOWN:
1034         data->unknown_depth++;
1035 break;
1036 default: ;
1037 }
1038
1039 }
1040
1041 /**
1042  * Handle an end tag in the parsing of a GPX file.
1043  */
1044 static void 
1045 gpx_end_element(SaxData * data, const xmlChar * name)
1046 {
1047 switch (data->state) {
1048 case ERROR:
1049
1050 break;
1051 case START:
1052         data->state = ERROR;
1053 break;
1054 case INSIDE_GPX:
1055         if (!strcmp((gchar *) name, "gpx"))
1056                 data->state = FINISH;
1057         else
1058                 data->state = ERROR;
1059 break;
1060 case INSIDE_METADATA:
1061         if (!strcmp((gchar *) name, "metadata"))
1062                 data->state = INSIDE_GPX;
1063 break;
1064 case INSIDE_PATH:
1065         if (!strcmp((gchar *) name, "trk"))
1066                 data->state = INSIDE_GPX;
1067         else
1068                 data->state = ERROR;
1069 break;
1070 case INSIDE_PATH_SEGMENT:
1071         if (!strcmp((gchar *) name, "trkseg")) {
1072                 if (data->at_least_one_trkpt) {
1073                         path_add_break(data->path);
1074                 }
1075                 data->state = INSIDE_PATH;
1076         } else
1077                 data->state = ERROR;
1078 break;
1079 case INSIDE_PATH_POINT:
1080         if (!strcmp((gchar *) name, "trkpt")) {
1081                 data->state = INSIDE_PATH_SEGMENT;
1082                 data->at_least_one_trkpt = TRUE;
1083         } else
1084                 data->state = ERROR;
1085 break;
1086 case INSIDE_PATH_POINT_ELE:
1087         if (!strcmp((gchar *) name, "ele")) {
1088                 gchar *error_check;
1089                 data->path->tail->altitude = g_ascii_strtod(data->chars->str, &error_check);
1090                 if (error_check == data->chars->str)
1091                         data->path->tail->altitude = NAN;
1092                 data->state = INSIDE_PATH_POINT;
1093                 g_string_free(data->chars, TRUE);
1094                 data->chars = g_string_new("");
1095         } else
1096                 data->state = ERROR;
1097 break;
1098 case INSIDE_PATH_POINT_TIME:
1099         if (!strcmp((gchar *) name, "time")) {
1100                 struct tm time;
1101                 gchar *ptr;
1102
1103                 if (NULL == (ptr = strptime(data->chars->str, XML_DATE_FORMAT, &time)))
1104                         /* Failed to parse dateTime format. */
1105                         data->state = ERROR;
1106                 else {
1107                         /* Parse was successful. Now we have to parse timezone.
1108                          * From here on, if there is an error, I just assume local
1109                          * timezone.  Yes, this is not proper XML, but I don't
1110                          * care. */
1111                         gchar *error_check;
1112
1113                         /* First, set time in "local" time zone. */
1114                         data->path->tail->time = (mktime(&time));
1115
1116                         /* Now, skip inconsequential characters */
1117                         while (*ptr && *ptr != 'Z' && *ptr != '-' && *ptr != '+')
1118                                 ptr++;
1119
1120                         /* Check if we ran to the end of the string. */
1121                         if (*ptr) {
1122                                 /* Next character is either 'Z', '-', or '+' */
1123                                 if (*ptr == 'Z')
1124                                         /* Zulu (UTC) time. Undo the local time zone's offset. */
1125                                         data->path->tail->time += time.tm_gmtoff;
1126                                 else {
1127                                         /* Not Zulu (UTC). Must parse hours and minutes. */
1128                                         gint offhours = strtol(ptr, &error_check, 10);
1129                                         if (error_check != ptr && *(ptr = error_check) == ':') {
1130                                                 /* Parse of hours worked. Check minutes. */
1131                                                 gint offmins = strtol(ptr + 1, &error_check, 10);
1132                                                 if (error_check != (ptr + 1)) {
1133                                                         /* Parse of minutes worked. Calculate. */
1134                                                         data->path->tail->time += (time.tm_gmtoff - (offhours * 60 * 60 + offmins * 60));
1135                                                 }
1136                                         }
1137                                 }
1138                         }
1139                         /* Successfully parsed dateTime. */
1140                         data->state = INSIDE_PATH_POINT;
1141                 }
1142
1143                 g_string_free(data->chars, TRUE);
1144                 data->chars = g_string_new("");
1145         } else {
1146                 data->state = ERROR;
1147         }
1148 break;
1149 case INSIDE_PATH_POINT_DESC:
1150         /* only parse description for routes */
1151         if (!strcmp((gchar *) name, "desc")) {
1152                 path_insert_mark_text(data->path, g_string_free(data->chars, FALSE));
1153                 data->chars=g_string_new("");
1154                 data->state=INSIDE_PATH_POINT;
1155         } else {
1156                 data->state=ERROR;
1157         }
1158 break;
1159 case INSIDE_PATH_POINT_NAME:
1160         /* Just ignore these for now */
1161         g_string_free(data->chars, FALSE);
1162         data->chars=g_string_new("");
1163         data->state=INSIDE_PATH_POINT;
1164 break;
1165 case UNKNOWN:
1166         if (!--data->unknown_depth)
1167                 data->state=data->prev_state;
1168         else
1169                 data->state=ERROR;
1170 break;
1171 default: ;
1172 }
1173
1174 }
1175
1176 /**
1177  * Handle char data in the parsing of a GPX file.
1178  */
1179 static void 
1180 gpx_chars(SaxData *data, const xmlChar *ch, int len)
1181 {
1182 guint i;
1183
1184 switch (data->state) {
1185 case ERROR:
1186 case UNKNOWN:
1187         break;
1188 case INSIDE_PATH_POINT_ELE:
1189 case INSIDE_PATH_POINT_TIME:
1190 case INSIDE_PATH_POINT_DESC:
1191 case INSIDE_PATH_POINT_NAME:
1192         for (i = 0; i < len; i++)
1193                 data->chars = g_string_append_c(data->chars, ch[i]);
1194         /* g_debug("GPXC: %s", data->chars->str); */
1195         break;
1196 default:
1197 break;
1198 }
1199
1200 }
1201
1202 /**
1203  * Handle an entity in the parsing of a GPX file.  We don't do anything
1204  * special here.
1205  */
1206 static xmlEntityPtr 
1207 gpx_get_entity(SaxData *data, const xmlChar *name)
1208 {
1209 return xmlGetPredefinedEntity(name);
1210 }
1211
1212 /**
1213  * Handle an error in the parsing of a GPX file.
1214  */
1215 static void 
1216 gpx_error(SaxData *data, const gchar *msg, ...)
1217 {
1218 va_list args;
1219
1220 va_start(args, msg);
1221 g_logv("GPX", G_LOG_LEVEL_WARNING, msg, args);
1222 va_end(args);
1223
1224 data->state = ERROR;
1225 }
1226
1227 /**
1228  * gpx_parse:
1229  * @path
1230  * @buffer
1231  * @size
1232  * @policy
1233  *
1234  * Parse a buffer of GPX data.
1235  * XXX: Add support for parsing directly from file if the file is local.
1236  *
1237  */
1238 gboolean
1239 path_gpx_parse(Path *path, gchar *buffer, gint size, gpx_path_policy policy)
1240 {
1241 SaxData data;
1242 xmlSAXHandler sax_handler;
1243
1244 data.path=path;
1245 data.state=START;
1246 data.chars=g_string_new("");
1247
1248 if (policy==GPX_PATH_NEW)
1249         path_clear(path);
1250
1251 memset(&sax_handler, 0, sizeof(sax_handler));
1252 sax_handler.characters=(charactersSAXFunc) gpx_chars;
1253 sax_handler.startElement=(startElementSAXFunc) gpx_start_element;
1254 sax_handler.endElement=(endElementSAXFunc) gpx_end_element;
1255 sax_handler.entityDecl=(entityDeclSAXFunc) gpx_get_entity;
1256 sax_handler.warning=(warningSAXFunc) gpx_error;
1257 sax_handler.error=(errorSAXFunc) gpx_error;
1258 sax_handler.fatalError=(fatalErrorSAXFunc) gpx_error;
1259
1260 xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
1261
1262 g_string_free(data.chars, TRUE);
1263
1264 if (data.state!=FINISH) {
1265         g_warning("GPX: Parser stopped in error state %d", data.state);
1266         return FALSE;
1267 }
1268
1269 switch (policy) {
1270 case GPX_PATH_APPEND:
1271 case GPX_PATH_PREPEND:
1272         {
1273         Point *src_first;
1274         Path *src, *dest;
1275
1276         if (policy==GPX_PATH_APPEND) {
1277                 /* Append to current path. Make sure last path point is zero. */
1278                 path_add_break(path);
1279                 src=data.path;
1280                 dest=path;
1281         } else {
1282                 /* Prepend to current route. */
1283                 src=path;
1284                 dest=data.path;
1285         }
1286
1287         /* Find src_first non-zero point. */
1288         for (src_first = src->head; src_first++ != src->tail;)
1289                 if (src_first->unity && src_first->unitx)
1290                         break;
1291
1292         /* Append route points from src to dest. */
1293         if (src->tail >= src_first) {
1294                 WayPoint *curr;
1295                 guint num_dest_points = dest->tail - dest->head + 1;
1296                 guint num_src_points = src->tail - src_first + 1;
1297
1298                 /* Adjust dest->tail to be able to fit src route data
1299                  * plus room for more route data. */
1300                 path_resize(dest, num_dest_points + num_src_points);
1301
1302                 memcpy(dest->tail + 1, src_first, num_src_points * sizeof(Point));
1303
1304                 dest->tail += num_src_points;
1305
1306                 /* Append waypoints from src to dest->. */
1307                 path_wresize(dest, (dest->wtail - dest->whead) + (src->wtail - src->whead) + 2);
1308                 for (curr = src->whead - 1; curr++ != src->wtail;) {
1309                         (++(dest->wtail))->point = dest->head + num_dest_points + (curr->point - src_first);
1310                         dest->wtail->desc = curr->desc;
1311                 }
1312         }
1313
1314         /* Kill old route - don't use MACRO_PATH_FREE(), because that
1315          * would free the string desc's that we just moved to data.route. */
1316         g_free(src->head);
1317         g_free(src->whead);
1318         if (policy==GPX_PATH_PREPEND)
1319                 (*path) = *dest;
1320         }
1321 break;
1322 case GPX_PATH_NEW:
1323         /* */
1324 break;
1325 default:
1326         g_assert_not_reached();
1327 break;
1328 }
1329
1330 return TRUE;
1331 }
1332
1333 /******************************************************************************/
1334
1335 #if 0
1336 static void
1337 path_store_append_waypoint(GtkListStore *, WayPoint *w)
1338 {
1339 gchar tmp1[16], tmp2[16];
1340 gdouble lat2, lon2;
1341
1342 unit2latlon(wcurr->point->unitx, wcurr->point->unity, lat2, lon2);
1343 lat_format(_degformat, lat2, tmp1);
1344 lon_format(_degformat, lon2, tmp2);
1345
1346 g_snprintf(buffer1, sizeof(buffer1), "%s,%s", tmp1, tmp2);
1347 sum += calculate_distance(lat1, lon1, lat2, lon2);
1348 g_snprintf(buffer2, sizeof(buffer2), "%.02f %s", sum * UNITS_CONVERT[_units], UNITS_TEXT[_units]);
1349
1350 gtk_list_store_append(store, &iter);
1351 gtk_list_store_set(store, &iter,
1352         PATH_LATLON, buffer1,
1353         PATH_DISTANCE, buffer2,
1354         PATH_WAYPOINT, wcurr->desc,
1355         PATH_LAT, lat2,
1356         PATH_LON, lon2,
1357         -1);
1358
1359 lat1=lat2;
1360 lon1=lon2;
1361 }
1362 #endif
1363
1364 /**
1365  * Generate a GtkListStore with information about path waypoints (location, distance)
1366  */
1367 GtkListStore *
1368 path_get_waypoints_store(Path *path)
1369 {
1370 WayPoint *wcurr;
1371 GtkTreeIter iter;
1372 GtkListStore *store;
1373 gchar buffer1[80];
1374 gchar buffer2[32];
1375 gchar tmp1[16], tmp2[16];
1376 gdouble lat1, lon1, lat2, lon2;
1377 gdouble sum=0.0;
1378
1379 g_return_val_if_fail(path!=NULL, NULL);
1380
1381 wcurr=path->whead;
1382
1383 g_return_val_if_fail(wcurr!=NULL, NULL);
1384 g_return_val_if_fail(wcurr->point!=NULL, NULL);
1385 if (!path_has_waypoints(path))
1386         return NULL;
1387
1388 store=gtk_list_store_new(PATH_NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
1389
1390 unit2latlon(wcurr->point->unitx, wcurr->point->unity, lat1, lon1);
1391
1392 while (wcurr!=path->wtail) {
1393         if (!wcurr)
1394                 break;
1395
1396         if (!wcurr->point) {
1397                 g_debug("PSTORE: No point for waypoint (%s) skipping", wcurr->desc);
1398                 wcurr++;
1399                 continue;
1400         }
1401
1402         unit2latlon(wcurr->point->unitx, wcurr->point->unity, lat2, lon2);
1403         lat_format(_degformat, lat2, tmp1);
1404         lon_format(_degformat, lon2, tmp2);
1405
1406         g_snprintf(buffer1, sizeof(buffer1), "%s,%s", tmp1, tmp2);
1407         sum += calculate_distance(lat1, lon1, lat2, lon2);
1408         g_snprintf(buffer2, sizeof(buffer2), "%.02f %s", sum * UNITS_CONVERT[_units], UNITS_TEXT[_units]);
1409
1410         gtk_list_store_append(store, &iter);
1411         gtk_list_store_set(store, &iter,
1412                 PATH_LATLON, buffer1,
1413                 PATH_DISTANCE, buffer2,
1414                 PATH_WAYPOINT, wcurr->desc,
1415                 PATH_LAT, lat2,
1416                 PATH_LON, lon2,
1417                 -1);
1418
1419         lat1=lat2;
1420         lon1=lon2;
1421
1422         wcurr++;
1423 }
1424
1425 return store;
1426 }