2 * This file is part of mapper
4 * Copyright (C) 2008 Kaj-Michael Lang
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.
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.
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.
25 #include <libxml/parser.h>
26 #include <libgnomevfs/gnome-vfs.h>
34 struct sql_select_stmt {
35 sqlite3_stmt *select_paths;
36 sqlite3_stmt *select_path_nodes;
38 sqlite3_stmt *insert_path;
39 sqlite3_stmt *insert_path_node;
41 sqlite3_stmt *delete_path;
42 sqlite3_stmt *delete_path_nodes;
44 static struct sql_select_stmt sql;
46 #define PATH_TABLE_PATHS "create table IF NOT EXISTS paths ( \
47 nid int primary key, \
52 #define PATH_TABLE_NODES "create table IF NOT EXISTS path_nodes ( \
62 name text default null, \
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"
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 */
80 static guint32 signals[LAST_SIGNAL] = {0};
82 /** This enum defines the states of the SAX parsing state machine. */
90 INSIDE_PATH_POINT_ELE,
91 INSIDE_PATH_POINT_TIME,
92 INSIDE_PATH_POINT_DESC,
93 INSIDE_PATH_POINT_NAME,
99 /** Data used during the SAX parsing operation. */
100 typedef struct _SaxData SaxData;
106 gboolean at_least_one_trkpt;
110 static gchar XML_TZONE[7];
112 G_DEFINE_TYPE(Path, path, G_TYPE_OBJECT);
114 #define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PATH_TYPE, PathPrivate))
117 path_dispose(GObject *object)
119 g_debug("path_dispose");
121 G_OBJECT_CLASS(path_parent_class)->dispose(object);
125 path_finalize(GObject *object)
127 Path *path=PATH(object);
129 g_debug("path_finalize");
130 MACRO_PATH_FREE(*path);
137 g_free(path->author);
139 g_free(path->keywords);
141 g_free(path->copyright);
147 path_class_init(PathClass *klass)
149 GObjectClass *object_class=G_OBJECT_CLASS(klass);
151 g_debug("path_class_init");
153 object_class->dispose=path_dispose;
154 object_class->finalize=path_finalize;
155 /* g_type_class_add_private (klass, sizeof(PathPrivate)); */
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);
169 path_init(Path *path)
171 g_debug("path_init");
172 MACRO_PATH_INIT(*path);
176 path_new(PathType type, guint id)
179 static time_t time1=0;
182 p=g_object_new(PATH_TYPE, 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);
197 path_free(Path *path)
199 g_return_if_fail(path);
200 g_object_unref(path);
204 path_clear(Path *path)
206 g_return_if_fail(path);
207 MACRO_PATH_FREE(*path);
208 path->length=path->avgspeed=0.0;
213 path_resize(Path *path, guint size)
215 g_return_val_if_fail(path, FALSE);
217 if (path->head + size != path->cap) {
219 Point *old_head = path->head;
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);
227 /* Adjust all of the waypoints. */
228 for (curr = path->whead - 1; curr++ != path->wtail;)
229 curr->point = path->head + (curr->point - old_head);
237 path_wresize(Path *path, guint wsize)
239 g_return_val_if_fail(path, FALSE);
241 if (path->whead + wsize != path->wcap) {
242 WayPoint *old_whead = path->whead;
244 path->whead = g_renew(WayPoint, old_whead, wsize);
245 path->wtail = path->whead + (path->wtail - old_whead);
246 path->wcap = path->whead + wsize;
260 * Append a waypoint to path at given lat, lon with description desc.
265 path_add_waypoint(Path *path, gdouble lat, gdouble lon, gchar *desc)
269 latlon2unit(lat, lon, unitx, unity);
270 MACRO_PATH_INCREMENT_TAIL(*path);
271 path->tail->unitx=unitx;
272 path->tail->unity=unity;
274 path->tail->altitude=NAN;
276 MACRO_PATH_INCREMENT_WTAIL(*path);
277 path->wtail->point=path->tail;
278 path->wtail->desc=desc;
280 g_signal_emit(G_OBJECT(path), signals[NEW_WAYPOINT], 0, NULL);
294 * Append a point with given lat,lon,ptime,speed and altitude to given path.
296 * Returns: TRUE if the new point was added. FALSE is returned if new point distance was under sensitivity setting.
299 path_add_latlon(Path *path, gdouble lat, gdouble lon, time_t ptime, gfloat speed, gfloat altitude)
303 latlon2unit(lat, lon, unitx, unity);
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) {
309 unit2latlon(path->tail->unitx, path->tail->unity, lat, lon);
310 path->length+=calculate_distance(plat, plon, lat, lon);
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;
320 path->avgspeed=(path->points>0) ? path->tspeed/path->points : 0.0;
322 g_debug("TRACK: %f %f (%d)", path->length, path->avgspeed, path->points);
324 g_signal_emit(G_OBJECT(path), signals[NEW_POINT], 0, NULL);
336 * Add a break point to the path.
338 * Returns: TRUE if break was added, FALSE if last point was a break already.
341 path_add_break(Path *path)
343 g_return_val_if_fail(path, FALSE);
344 g_return_val_if_fail(path->tail, FALSE);
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. */
350 MACRO_PATH_INCREMENT_TAIL(*path);
351 *path->tail=_point_null;
352 MACRO_PATH_INCREMENT_TAIL(*path);
353 *path->tail=path->tail[-2];
355 g_signal_emit(G_OBJECT(path), signals[NEW_BREAK], 0, NULL);
366 * Checks if given path has any path points
368 * Returns: TRUE if there are points, FALSE otherwise
371 path_has_points(Path *path)
373 g_return_val_if_fail(path, FALSE);
374 return path->head==path->tail ? FALSE : TRUE;
378 * path_has_waypoints:
380 * Checks if given path has any waypoints
382 * Returns: TRUE if there are waypoints, FALSE otherwise
385 path_has_waypoints(Path *path)
387 g_return_val_if_fail(path, FALSE);
388 return path->whead==path->wtail ? FALSE : TRUE;
392 * path_find_last_point:
395 * Returns: The last path point or NULL if path is empty
398 path_find_last_point(Path *path)
402 g_return_val_if_fail(path, NULL);
404 if (!path_has_points(path))
407 for (p=path->tail; !p->unity; p--) {
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.
419 path_update_nears(Path *path, Point *point, gboolean quick)
421 gboolean ret = FALSE;
423 WayPoint *wcurr, *wnext;
424 guint64 near_dist_squared;
426 g_return_val_if_fail(path, FALSE);
427 g_return_val_if_fail(point, FALSE);
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);
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.
441 for (curr = path->near_point; curr++ != path->tail;) {
443 guint dist_squared = DISTANCE_SQUARED(*point, *curr);
444 if (dist_squared <= near_dist_squared) {
446 near_dist_squared = dist_squared;
452 /* Update _near_point. */
453 path->near_point = near;
454 path->near_point_dist_squared = near_dist_squared;
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. */
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)))))
477 path->next_way = NULL;
478 path->next_wpt = NULL;
479 path->next_way_dist_squared = -1;
480 path->next_wpt_dist_squared = -1;
483 /* Only update next_way (and consequently _next_wpt) if _next_way is
484 * different, and record that fact for return. */
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;
492 while (!(++path->next_wpt)->unity) {
493 if (path->next_wpt == path->tail) {
494 path->next_wpt = NULL;
501 path->next_way_dist_squared = DISTANCE_SQUARED(*point, *wnext->point);
503 path->next_wpt_dist_squared = DISTANCE_SQUARED(*point, *path->next_wpt);
510 * path_find_nearest_point:
512 * Reset the near_point data by searching the entire path for the nearest point and waypoint.
515 path_find_nearest_point(Path *path, gdouble lat, gdouble lon)
519 g_return_if_fail(path);
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)
526 /* Initialize next_way */
527 if (path->wtail==path->whead)
530 path->next_way=path->whead;
531 path->next_way_dist_squared=-1;
533 /* Initialize next_wpt */
535 path->next_wpt_dist_squared=-1;
537 latlon2unit(lat, lon, p.unitx, p.unity);
538 path_update_nears(path, &p, FALSE);
542 * path_find_nearest_waypoint:
546 path_find_nearest_waypoint(Path *path, guint unitx, guint unity)
550 guint64 nearest_squared;
551 Point pos = { unitx, unity, 0, NAN };
553 g_return_val_if_fail(path, NULL);
555 wcurr=wnear=path->whead;
556 if (wcurr && wcurr->point && wcurr!=path->wtail) {
557 nearest_squared=DISTANCE_SQUARED(pos, *(wcurr->point));
559 while (wcurr++!=path->wtail) {
560 guint64 test_squared=DISTANCE_SQUARED(pos, *(wcurr->point));
561 if (test_squared < nearest_squared) {
563 nearest_squared=test_squared;
568 if (wnear && wnear->point) {
569 if (abs(unitx - wnear->point->unitx) < path->sensitivity && abs(unity - wnear->point->unity) < path->sensitivity)
577 * path_check_waypoint_announce:
581 * Check if we should announce upcoming waypoint.
585 path_check_waypoint_announce(Path *path, GpsData *gps)
587 guint a_thres_near, a_thres_at;
589 g_return_if_fail(path);
590 g_return_if_fail(gps);
595 a_thres_near=(20+(guint)gps->speed)*path->announce_notice_ratio*3;
596 a_thres_at=(20+(guint)gps->speed);
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;
607 /******************************************************************************/
614 path_calculate_distance_from(Path *path, Point *point)
617 gdouble lat1, lon1, lat2, lon2;
620 for (curr = path->tail; curr > point; --curr) {
622 unit2latlon(curr->unitx, curr->unity, &lat2, &lon2);
623 sum += calculate_distance(lat1, lon1, lat2, lon2);
632 * path_get_distance_to:
638 * Calculate distancance from lat,lon to point in given path.
640 * Returns: The distance, or 0.0 if path or point is invalid.
643 path_get_distance_to(Path *path, Point *point, gdouble lat, gdouble lon)
645 gdouble lat1, lon1, lat2, lon2;
648 g_return_val_if_fail(path, 0.0);
650 /* If point is NULL, use the next waypoint. */
651 if (point == NULL && path->next_way)
652 point = path->next_way->point;
654 /* If point is still NULL, return an error. */
661 if (point > path->near_point) {
663 /* Skip _near_point in case we have already passed it. */
664 for (curr = path->near_point + 1; curr <= point; ++curr) {
666 unit2latlon(curr->unitx, curr->unity, lat2, lon2);
667 sum += calculate_distance(lat1, lon1, lat2, lon2);
672 } else if (point < path->near_point) {
674 /* Skip near_point in case we have already passed it. */
675 for (curr = path->near_point - 1; curr >= point; --curr) {
677 unit2latlon(curr->unitx, curr->unity, lat2, lon2);
678 sum += calculate_distance(lat1, lon1, lat2, lon2);
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);
691 /******************************************************************************/
694 path_load(Path *path, const gchar *file)
700 g_return_val_if_fail(path, FALSE);
701 g_return_val_if_fail(file, FALSE);
703 if (gnome_vfs_read_entire_file(file, &size, &bytes)==GNOME_VFS_OK)
704 path_gpx_parse(path, bytes, size, GPX_PATH_NEW);
710 path_save(Path *path, const gchar *file)
712 GnomeVFSHandle *handle;
715 g_return_val_if_fail(path, FALSE);
716 g_return_val_if_fail(file, FALSE);
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);
726 /******************************************************************************/
729 path_set_destination_from_last(Path *path, Position *pos)
734 g_return_val_if_fail(path, FALSE);
735 g_return_val_if_fail(pos, FALSE);
737 if (path->head==path->tail) {
738 position_set(pos, FALSE, NAN, NAN, NAN);
742 p=path_find_last_point(path);
744 unit2latlon(p->unitx, p->unity, &lat, &lon);
745 position_set(pos, TRUE, lat, lon, 0);
747 position_set(pos, FALSE, NAN, NAN, NAN);
754 * path_get_waypoint_latlon:
759 * Get the real latitude and longitude of given waypoint
761 * Returns: TRUE if waypoint was valid, FALSE otherwise.
764 path_get_waypoint_latlon(WayPoint *way, gdouble *lat, gdouble *lon)
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);
773 unit2latlon(way->point->unitx, way->point->unity, tlat, tlon);
782 * path_insert_mark_text:
786 * Add a new waypoint to path with given text at the last point.
790 path_insert_mark_text(Path *path, gchar *text)
792 g_return_if_fail(path);
793 MACRO_PATH_INCREMENT_WTAIL(*path);
794 path->wtail->point=path->tail;
796 path->wtail->desc=text;
799 path->wtail->desc=g_strdup_printf("WPT:#%u", path->wpcnt);
801 g_signal_emit(G_OBJECT(path), signals[NEW_WAYPOINT], 0, NULL);
805 * path_insert_mark_autonumber:
808 * Add autonumbered waypoint to given path
812 path_insert_mark_autonumber(Path *path)
814 path_insert_mark_text(path, NULL);
817 /******************************************************************************/
819 #define PATH_GPX_WRITE_ERROR path_gpx_write_error_quark()
822 path_gpx_write_error_quark(void)
824 return g_quark_from_static_string ("path-gpx-write-error-quark");
827 #define XML_DATE_FORMAT "%FT%T"
829 #define WRITE_STRING(string) { \
830 if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write(handle, (string), strlen((string)), &size))) { \
832 PATH_GPX_WRITE_ERROR, G_FILE_ERROR_FAILED, \
834 _("Error while writing to file"), \
835 _("File is incomplete."), \
836 gnome_vfs_result_to_string(vfs_result)); \
842 path_gpx_write(Path *path, GnomeVFSHandle *handle, GError **error)
845 WayPoint *wcurr = NULL;
846 gboolean trkseg_break = FALSE;
849 GnomeVFSResult vfs_result;
850 GnomeVFSFileSize size;
852 g_return_val_if_fail((error == NULL || *error == NULL), FALSE);
854 gpx_time = time(NULL);
856 /* Find first non-zero point. */
857 for (curr = path->head - 1, wcurr = path->whead; curr++ != path->tail;) {
860 else if (wcurr && curr == wcurr->point)
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");
868 /* Write any metadata */
869 WRITE_STRING("<metadata>\n");
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");
877 WRITE_STRING("</metadata>\n");
879 /* Write track(s) and waypoint(s) */
880 WRITE_STRING("<trk>\n<trkseg>\n");
882 /* Curr points to first non-zero point. */
883 for (curr--; curr++ != path->tail;) {
886 gboolean first_sub = TRUE;
888 /* First trkpt of the segment - write trkseg header. */
889 WRITE_STRING("</trkseg>\n<trkseg>\n");
890 trkseg_break = FALSE;
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);
901 /* write the elevation */
902 if (!isnan(curr->altitude)) {
907 WRITE_STRING("<ele>");
908 g_ascii_formatd(buffer, sizeof(buffer), "%.2f", curr->altitude);
909 WRITE_STRING(buffer);
910 WRITE_STRING("</ele>\n");
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");
926 if (wcurr && curr == wcurr->point) {
933 desc=g_markup_printf_escaped("<desc>%s</desc>\n", wcurr->desc);
940 WRITE_STRING("/>\n");
942 WRITE_STRING("</trkpt>\n");
948 /* Write the footer. */
949 WRITE_STRING("</trkseg>\n</trk>\n</gpx>\n");
955 * Handle a start tag in the parsing of a GPX file.
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); }
964 gpx_start_element(SaxData *data, const xmlChar *name, const xmlChar **attrs)
966 switch (data->state) {
970 if (!strcmp((gchar *) name, "gpx"))
971 data->state = INSIDE_GPX;
973 MACRO_SET_UNKNOWN(name);
976 if (!strcmp((gchar *) name, "trk"))
977 data->state = INSIDE_PATH;
978 else if (!strcmp((gchar *) name, "metadata"))
979 data->state = INSIDE_METADATA;
981 MACRO_SET_UNKNOWN(name);
983 case INSIDE_METADATA:
986 if (!strcmp((gchar *) name, "trkseg")) {
987 data->state = INSIDE_PATH_SEGMENT;
988 data->at_least_one_trkpt = FALSE;
990 MACRO_SET_UNKNOWN(name);
992 case INSIDE_PATH_SEGMENT:
993 if (!strcmp((gchar *) name, "trkpt")) {
994 const xmlChar **curr_attr;
996 gdouble lat = 0.f, lon = 0.f;
997 gboolean has_lat, has_lon;
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)
1007 } else if (!strcmp(attr_name, "lon")) {
1008 lon = g_ascii_strtod(attr_val, &error_check);
1009 if (error_check != attr_val)
1013 if (has_lat && has_lon) {
1014 path_add_latlon(data->path, lat, lon, 0, 0, NAN);
1015 data->state = INSIDE_PATH_POINT;
1017 data->state = ERROR;
1019 MACRO_SET_UNKNOWN(name);
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;
1031 MACRO_SET_UNKNOWN(name);
1034 data->unknown_depth++;
1042 * Handle an end tag in the parsing of a GPX file.
1045 gpx_end_element(SaxData * data, const xmlChar * name)
1047 switch (data->state) {
1052 data->state = ERROR;
1055 if (!strcmp((gchar *) name, "gpx"))
1056 data->state = FINISH;
1058 data->state = ERROR;
1060 case INSIDE_METADATA:
1061 if (!strcmp((gchar *) name, "metadata"))
1062 data->state = INSIDE_GPX;
1065 if (!strcmp((gchar *) name, "trk"))
1066 data->state = INSIDE_GPX;
1068 data->state = ERROR;
1070 case INSIDE_PATH_SEGMENT:
1071 if (!strcmp((gchar *) name, "trkseg")) {
1072 if (data->at_least_one_trkpt) {
1073 path_add_break(data->path);
1075 data->state = INSIDE_PATH;
1077 data->state = ERROR;
1079 case INSIDE_PATH_POINT:
1080 if (!strcmp((gchar *) name, "trkpt")) {
1081 data->state = INSIDE_PATH_SEGMENT;
1082 data->at_least_one_trkpt = TRUE;
1084 data->state = ERROR;
1086 case INSIDE_PATH_POINT_ELE:
1087 if (!strcmp((gchar *) name, "ele")) {
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("");
1096 data->state = ERROR;
1098 case INSIDE_PATH_POINT_TIME:
1099 if (!strcmp((gchar *) name, "time")) {
1103 if (NULL == (ptr = strptime(data->chars->str, XML_DATE_FORMAT, &time)))
1104 /* Failed to parse dateTime format. */
1105 data->state = ERROR;
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
1113 /* First, set time in "local" time zone. */
1114 data->path->tail->time = (mktime(&time));
1116 /* Now, skip inconsequential characters */
1117 while (*ptr && *ptr != 'Z' && *ptr != '-' && *ptr != '+')
1120 /* Check if we ran to the end of the string. */
1122 /* Next character is either 'Z', '-', or '+' */
1124 /* Zulu (UTC) time. Undo the local time zone's offset. */
1125 data->path->tail->time += time.tm_gmtoff;
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));
1139 /* Successfully parsed dateTime. */
1140 data->state = INSIDE_PATH_POINT;
1143 g_string_free(data->chars, TRUE);
1144 data->chars = g_string_new("");
1146 data->state = ERROR;
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;
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;
1166 if (!--data->unknown_depth)
1167 data->state=data->prev_state;
1177 * Handle char data in the parsing of a GPX file.
1180 gpx_chars(SaxData *data, const xmlChar *ch, int len)
1184 switch (data->state) {
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); */
1203 * Handle an entity in the parsing of a GPX file. We don't do anything
1207 gpx_get_entity(SaxData *data, const xmlChar *name)
1209 return xmlGetPredefinedEntity(name);
1213 * Handle an error in the parsing of a GPX file.
1216 gpx_error(SaxData *data, const gchar *msg, ...)
1220 va_start(args, msg);
1221 g_logv("GPX", G_LOG_LEVEL_WARNING, msg, args);
1224 data->state = ERROR;
1234 * Parse a buffer of GPX data.
1235 * XXX: Add support for parsing directly from file if the file is local.
1239 path_gpx_parse(Path *path, gchar *buffer, gint size, gpx_path_policy policy)
1242 xmlSAXHandler sax_handler;
1246 data.chars=g_string_new("");
1248 if (policy==GPX_PATH_NEW)
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;
1260 xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
1262 g_string_free(data.chars, TRUE);
1264 if (data.state!=FINISH) {
1265 g_warning("GPX: Parser stopped in error state %d", data.state);
1270 case GPX_PATH_APPEND:
1271 case GPX_PATH_PREPEND:
1276 if (policy==GPX_PATH_APPEND) {
1277 /* Append to current path. Make sure last path point is zero. */
1278 path_add_break(path);
1282 /* Prepend to current route. */
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)
1292 /* Append route points from src to dest. */
1293 if (src->tail >= src_first) {
1295 guint num_dest_points = dest->tail - dest->head + 1;
1296 guint num_src_points = src->tail - src_first + 1;
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);
1302 memcpy(dest->tail + 1, src_first, num_src_points * sizeof(Point));
1304 dest->tail += num_src_points;
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;
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. */
1318 if (policy==GPX_PATH_PREPEND)
1326 g_assert_not_reached();
1333 /******************************************************************************/
1337 path_store_append_waypoint(GtkListStore *, WayPoint *w)
1339 gchar tmp1[16], tmp2[16];
1342 unit2latlon(wcurr->point->unitx, wcurr->point->unity, lat2, lon2);
1343 lat_format(_degformat, lat2, tmp1);
1344 lon_format(_degformat, lon2, tmp2);
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]);
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,
1365 * Generate a GtkListStore with information about path waypoints (location, distance)
1368 path_get_waypoints_store(Path *path)
1372 GtkListStore *store;
1375 gchar tmp1[16], tmp2[16];
1376 gdouble lat1, lon1, lat2, lon2;
1379 g_return_val_if_fail(path!=NULL, NULL);
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))
1388 store=gtk_list_store_new(PATH_NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
1390 unit2latlon(wcurr->point->unitx, wcurr->point->unity, lat1, lon1);
1392 while (wcurr!=path->wtail) {
1396 if (!wcurr->point) {
1397 g_debug("PSTORE: No point for waypoint (%s) skipping", wcurr->desc);
1402 unit2latlon(wcurr->point->unitx, wcurr->point->unity, lat2, lon2);
1403 lat_format(_degformat, lat2, tmp1);
1404 lon_format(_degformat, lon2, tmp2);
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]);
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,