X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fpath.c;h=ec2de141585a29fa8bf27516eadcae970f5719ce;hb=a434e8412bc14d3f7fd5a013a9e8a8b5f215df90;hp=55cdf9b80f45234fb83cbe3d0d5688077be872d3;hpb=954596a73cc844a6d5388e1cb956225d89fac140;p=mapper diff --git a/src/path.c b/src/path.c index 55cdf9b..ec2de14 100644 --- a/src/path.c +++ b/src/path.c @@ -19,17 +19,17 @@ */ #include +#include #include +#include +#include +#include -#include "mapper-types.h" #include "path.h" +#include "position.h" #include "utils.h" -#include "track.h" -#include "route.h" -#include "gps.h" #include "settings.h" #include "latlon.h" -#include "gpx.h" struct sql_select_stmt { sqlite3_stmt *select_paths; @@ -62,20 +62,56 @@ static struct sql_select_stmt sql; name text default null, \ t int not null);" +#define PATH_SQL_SELECT_TRACKS "select id,name,slat,slon,elat,elon,len,sdate,edate from tracks order by sdate" +#define PATH_SQL_INSERT_TRACK "insert into tracks (id,name,sloc,sdate) values (?,?,?,?); +#define PATH_SQL_INSERT_TRACK_POINT "insert into trackpoints (tid,dt,lat,lon,alt,hdop,vdop,pdop,sat,fix) values (?,?,?,?,?,?,?,?,?,?)" +#define PATH_SQL_SELECT_TRACK_POINTS "select tid,dt,lat,lon,alt,hdop,vdop,pdop,sat,fix from trackpoints where tid=? order by dt" + enum { NEW_POINT, /* A new point was appended to track track */ NEW_BREAK, /* A break was appended to the track */ NEW_WAYPOINT, /* A new waypoint/marker has been added */ - NEAR_WAYPOINT, /* We are near the next route waypoint */ - REACHED_WAYPOINT, /* We have reached the next route waypoint */ + NEAR_WAYPOINT, /* We are near the next route waypoint, announce it */ + REACHED_WAYPOINT, /* We have reached the next route waypoint, announce it */ REACHED_DESTINATION, /* We have reached the last route waypoint */ + CLEARED, /* Path was cleared */ LAST_SIGNAL }; static guint32 signals[LAST_SIGNAL] = {0}; +/** This enum defines the states of the SAX parsing state machine. */ +typedef enum { + START=1, + INSIDE_GPX=100, + INSIDE_METADATA, + INSIDE_PATH, + INSIDE_PATH_SEGMENT, + INSIDE_PATH_POINT, + INSIDE_PATH_POINT_ELE, + INSIDE_PATH_POINT_TIME, + INSIDE_PATH_POINT_DESC, + INSIDE_PATH_POINT_NAME, + FINISH=5000, + UNKNOWN=6666, + ERROR=9999, +} SaxState; + +/** Data used during the SAX parsing operation. */ +typedef struct _SaxData SaxData; +struct _SaxData { + Path *path; + SaxState state; + SaxState prev_state; + guint unknown_depth; + gboolean at_least_one_trkpt; + GString *chars; +}; + +static gchar XML_TZONE[7]; + G_DEFINE_TYPE(Path, path, G_TYPE_OBJECT); -/* #define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PATH_TYPE, PathPrivate)) */ +#define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PATH_TYPE, PathPrivate)) static void path_dispose(GObject *object) @@ -140,11 +176,20 @@ Path * path_new(PathType type, guint id) { Path *p; +static time_t time1=0; +struct tm time2; p=g_object_new(PATH_TYPE, NULL); p->type=type; p->id=id; p->sensitivity=3; + +if (time1==0) { + time1=time(NULL); + localtime_r(&time1, &time2); + g_snprintf(XML_TZONE, sizeof(XML_TZONE), "%+03ld:%02ld", (time2.tm_gmtoff / 60 / 60), (time2.tm_gmtoff / 60) % 60); +} + return p; } @@ -152,7 +197,6 @@ void path_free(Path *path) { g_return_if_fail(path); -g_debug("path_free"); g_object_unref(path); } @@ -165,7 +209,7 @@ path->length=path->avgspeed=0.0; path->points=0; } -gboolean +static gboolean path_resize(Path *path, guint size) { g_return_val_if_fail(path, FALSE); @@ -189,7 +233,7 @@ if (path->head + size != path->cap) { return FALSE; } -gboolean +static gboolean path_wresize(Path *path, guint wsize) { g_return_val_if_fail(path, FALSE); @@ -206,6 +250,17 @@ if (path->whead + wsize != path->wcap) { return FALSE; } +/** + * path_add_waypoint: + * @path + * @lat + * @lon + * @desc + * + * Append a waypoint to path at given lat, lon with description desc. + * + * Returns: TRUE + */ gboolean path_add_waypoint(Path *path, gdouble lat, gdouble lon, gchar *desc) { @@ -213,68 +268,82 @@ guint unitx, unity; latlon2unit(lat, lon, unitx, unity); MACRO_PATH_INCREMENT_TAIL(*path); -path->tail->unitx = unitx; -path->tail->unity = unity; -path->tail->time = 0; -path->tail->altitude = NAN; +path->tail->unitx=unitx; +path->tail->unity=unity; +path->tail->time=0; +path->tail->altitude=NAN; MACRO_PATH_INCREMENT_WTAIL(*path); -path->wtail->point = path->tail; -path->wtail->desc = desc; +path->wtail->point=path->tail; +path->wtail->desc=desc; -path_find_nearest_point(path); +g_signal_emit(G_OBJECT(path), signals[NEW_WAYPOINT], 0, NULL); return TRUE; } -gboolean -path_add_point(Path *path, GpsData *gps) +/** + * path_add_latlon: + * @path + * @lat + * @lon + * @ptime + * @speed + * @altitude + * + * Append a point with given lat,lon,ptime,speed and altitude to given path. + * + * Returns: TRUE if the new point was added. FALSE is returned if new point distance was under sensitivity setting. + */ +gboolean +path_add_latlon(Path *path, gdouble lat, gdouble lon, time_t ptime, gfloat speed, gfloat altitude) { guint unitx, unity; -g_return_val_if_fail(path, FALSE); - -if (!gps) { - MACRO_PATH_INCREMENT_TAIL(*path); - *path->tail=_point_null; - return FALSE; -} - -latlon2unit(gps->lat, gps->lon, unitx, unity); +latlon2unit(lat, lon, unitx, unity); if (abs((gint)unitx-path->tail->unitx) > path->sensitivity || abs((gint)unity-path->tail->unity) > path->sensitivity) { if (path->tail->unity && path->tail->unitx) { - gdouble lat, lon; + gdouble plat, plon; unit2latlon(path->tail->unitx, path->tail->unity, lat, lon); - path->length+=calculate_distance(lat, lon, gps->lat, gps->lon); + path->length+=calculate_distance(plat, plon, lat, lon); } MACRO_PATH_INCREMENT_TAIL(*path); path->tail->unitx=unitx; path->tail->unity=unity; - path->tail->time=gps->time; - path->tail->altitude=gps->altitude; - path->maxspeed=gps->maxspeed; - path->tspeed+=gps->speed; + path->tail->time=ptime; + path->tail->altitude=altitude; + if (speed>path->maxspeed) + path->maxspeed=speed; + path->tspeed+=speed; path->avgspeed=(path->points>0) ? path->tspeed/path->points : 0.0; path->points++; g_debug("TRACK: %f %f (%d)", path->length, path->avgspeed, path->points); g_signal_emit(G_OBJECT(path), signals[NEW_POINT], 0, NULL); + return TRUE; } -return TRUE; +return FALSE; + } +/** + * path_add_break: + * @path + * + * Add a break point to the path. + * + * Returns: TRUE if break was added, FALSE if last point was a break already. + */ gboolean -path_insert_break(Path *path) +path_add_break(Path *path) { g_return_val_if_fail(path, FALSE); g_return_val_if_fail(path->tail, FALSE); if (path->tail->unity && path->tail->unitx) { - guint x1, y1; - /* To mark a "break" in a track, we'll add a (0, 0) point and then another instance of the most recent track point. */ @@ -290,6 +359,14 @@ if (path->tail->unity && path->tail->unitx) { return FALSE; } +/** + * path_has_points: + * @path + * + * Checks if given path has any path points + * + * Returns: TRUE if there are points, FALSE otherwise + */ gboolean path_has_points(Path *path) { @@ -297,6 +374,13 @@ g_return_val_if_fail(path, FALSE); return path->head==path->tail ? FALSE : TRUE; } +/** + * path_has_waypoints: + * + * Checks if given path has any waypoints + * + * Returns: TRUE if there are waypoints, FALSE otherwise + */ gboolean path_has_waypoints(Path *path) { @@ -304,6 +388,12 @@ g_return_val_if_fail(path, FALSE); return path->whead==path->wtail ? FALSE : TRUE; } +/** + * path_find_last_point: + * @path + * + * Returns: The last path point or NULL if path is empty + */ Point * path_find_last_point(Path *path) { @@ -319,6 +409,236 @@ for (p=path->tail; !p->unity; p--) { return p; } +/** + * Updates near_point, next_way, and next_wpt. If quick is FALSE (as + * it is when this function is called from route_find_nearest_point), then + * the entire list (starting from near_point) is searched. Otherwise, we + * stop searching when we find a point that is farther away. + */ +static gboolean +path_update_nears(Path *path, Point *point, gboolean quick) +{ +gboolean ret = FALSE; +Point *curr, *near; +WayPoint *wcurr, *wnext; +guint64 near_dist_squared; + +g_return_val_if_fail(path, FALSE); +g_return_val_if_fail(point, FALSE); + +/* If we have waypoints (_next_way != NULL), then determine the "next + * waypoint", which is defined as the waypoint after the nearest point, + * UNLESS we've passed that waypoint, in which case the waypoint after + * that waypoint becomes the "next" waypoint. */ +if (path->next_way) { + /* First, set near_dist_squared with the new distance from near_point. */ + near = path->near_point; + near_dist_squared = DISTANCE_SQUARED(*point, *near); + + /* Now, search path for a closer point. If quick is TRUE, then we'll + * only search forward, only as long as we keep finding closer points. + */ + for (curr = path->near_point; curr++ != path->tail;) { + if (curr->unity) { + guint dist_squared = DISTANCE_SQUARED(*point, *curr); + if (dist_squared <= near_dist_squared) { + near = curr; + near_dist_squared = dist_squared; + } else if (quick) + break; + } + } + + /* Update _near_point. */ + path->near_point = near; + path->near_point_dist_squared = near_dist_squared; + + for (wnext = wcurr = path->next_way; wcurr != path->wtail; wcurr++) { + if (wcurr->point < near || (wcurr->point == near && quick + && (path->next_wpt && (DISTANCE_SQUARED(*point, *near) > path->next_way_dist_squared + && DISTANCE_SQUARED(*point, *path->next_wpt) < path->next_wpt_dist_squared)))) + /* Okay, this else if expression warrants explanation. If the + * nearest track point happens to be a waypoint, then we want to + * check if we have "passed" that waypoint. To check this, we + * test the distance from _gps to the waypoint and from _gps to + * _next_wpt, and if the former is increasing and the latter is + * decreasing, then we have passed the waypoint, and thus we + * should skip it. Note that if there is no _next_wpt, then + * there is no next waypoint, so we do not skip it in that case. */ + wnext = wcurr + 1; + else + break; + } + + if (wnext == path->wtail && (wnext->point < near || (wnext->point == near && quick + && (path->next_wpt && (DISTANCE_SQUARED (*point, *near) > path->next_way_dist_squared + && DISTANCE_SQUARED(*point, *path->next_wpt) < path->next_wpt_dist_squared))))) + { + path->next_way = NULL; + path->next_wpt = NULL; + path->next_way_dist_squared = -1; + path->next_wpt_dist_squared = -1; + ret = TRUE; + } + /* Only update next_way (and consequently _next_wpt) if _next_way is + * different, and record that fact for return. */ + else { + if (!quick || path->next_way != wnext) { + path->next_way = wnext; + path->next_wpt = wnext->point; + if (path->next_wpt == path->tail) + path->next_wpt = NULL; + else { + while (!(++path->next_wpt)->unity) { + if (path->next_wpt == path->tail) { + path->next_wpt = NULL; + break; + } + } + } + ret = TRUE; + } + path->next_way_dist_squared = DISTANCE_SQUARED(*point, *wnext->point); + if (path->next_wpt) + path->next_wpt_dist_squared = DISTANCE_SQUARED(*point, *path->next_wpt); + } +} +return ret; +} + +/** + * path_find_nearest_point: + * + * Reset the near_point data by searching the entire path for the nearest point and waypoint. + */ +void +path_find_nearest_point(Path *path, gdouble lat, gdouble lon) +{ +Point p; + +g_return_if_fail(path); + +/* Initialize near_point to first non-zero point. */ +path->near_point=path->head; +while (!path->near_point->unity && path->near_point != path->tail) + path->near_point++; + +/* Initialize next_way */ +if (path->wtail==path->whead) + path->next_way=NULL; +else + path->next_way=path->whead; +path->next_way_dist_squared=-1; + +/* Initialize next_wpt */ +path->next_wpt=NULL; +path->next_wpt_dist_squared=-1; + +latlon2unit(lat, lon, p.unitx, p.unity); +path_update_nears(path, &p, FALSE); +} + +/** + * path_find_nearest_waypoint: + * + */ +WayPoint * +path_find_nearest_waypoint(Path *path, guint unitx, guint unity) +{ +WayPoint *wcurr; +WayPoint *wnear; +guint64 nearest_squared; +Point pos = { unitx, unity, 0, NAN }; + +g_return_val_if_fail(path, NULL); + +wcurr=wnear=path->whead; +if (wcurr && wcurr->point && wcurr!=path->wtail) { + nearest_squared=DISTANCE_SQUARED(pos, *(wcurr->point)); + + while (wcurr++!=path->wtail) { + guint64 test_squared=DISTANCE_SQUARED(pos, *(wcurr->point)); + if (test_squared < nearest_squared) { + wnear=wcurr; + nearest_squared=test_squared; + } + } +} + +if (wnear && wnear->point) { + if (abs(unitx - wnear->point->unitx) < path->sensitivity && abs(unity - wnear->point->unity) < path->sensitivity) + return wnear; +} + +return NULL; +} + +/** + * path_check_waypoint_announce: + * @path + * @gps + * + * Check if we should announce upcoming waypoint. + * + */ +static void +path_check_waypoint_announce(Path *path, GpsData *gps) +{ +guint a_thres_near, a_thres_at; + +g_return_if_fail(path); +g_return_if_fail(gps); + +if (!path->next_way) + return; + +a_thres_near=(20+(guint)gps->speed)*path->announce_notice_ratio*3; +a_thres_at=(20+(guint)gps->speed); + +if (path->next_way_dist_squared<(a_thres_near * a_thres_near) && path->next_way!=path->announced_waypoint) { + g_signal_emit(G_OBJECT(path), signals[NEAR_WAYPOINT], 0, NULL); + path->announced_waypoint=path->next_way; +} else if (path->next_way_dist_squared<(a_thres_at * a_thres_at) && path->next_way!=path->announced_waypoint) { + g_signal_emit(G_OBJECT(path), signals[REACHED_WAYPOINT], 0, NULL); + path->announced_waypoint=path->next_way; +} +} + +/******************************************************************************/ + +/** + * + * + */ +gdouble +path_calculate_distance_from(Path *path, Point *point) +{ +Point *curr; +gdouble lat1, lon1, lat2, lon2; +gdouble sum=0.0; + +for (curr = path->tail; curr > point; --curr) { + if (curr->unity) { + unit2latlon(curr->unitx, curr->unity, &lat2, &lon2); + sum += calculate_distance(lat1, lon1, lat2, lon2); + lat1 = lat2; + lon1 = lon2; + } +} +return sum; +} + +/** + * path_get_distance_to: + * @path + * @point + * @lat + * @lon + * + * Calculate distancance from lat,lon to point in given path. + * + * Returns: The distance, or 0.0 if path or point is invalid. + */ gdouble path_get_distance_to(Path *path, Point *point, gdouble lat, gdouble lon) { @@ -368,43 +688,78 @@ if (point > path->near_point) { return sum; } +/******************************************************************************/ + gboolean -path_load(Path *path, const gchar *config_dir, const gchar *file) +path_load(Path *path, const gchar *file) { gchar *pfile; gchar *bytes; gint size; g_return_val_if_fail(path, FALSE); -g_return_val_if_fail(config_dir, FALSE); g_return_val_if_fail(file, FALSE); -pfile = gnome_vfs_uri_make_full_from_relative(config_dir, file); -if (gnome_vfs_read_entire_file(pfile, &size, &bytes)==GNOME_VFS_OK) - gpx_parse(path, bytes, size, GPX_PATH_NEW); +if (gnome_vfs_read_entire_file(file, &size, &bytes)==GNOME_VFS_OK) + path_gpx_parse(path, bytes, size, GPX_PATH_NEW); g_free(pfile); return TRUE; } gboolean -path_save(Path *path, const gchar *config_dir, const gchar *file) +path_save(Path *path, const gchar *file) { GnomeVFSHandle *handle; gchar *tfile; g_return_val_if_fail(path, FALSE); -g_return_val_if_fail(config_dir, FALSE); g_return_val_if_fail(file, FALSE); -tfile=gnome_vfs_uri_make_full_from_relative(config_dir, file); -if (gnome_vfs_create(&handle, tfile, GNOME_VFS_OPEN_WRITE, FALSE, 0600)==GNOME_VFS_OK) { - gpx_write(path, handle); +if (gnome_vfs_create(&handle, file, GNOME_VFS_OPEN_WRITE, FALSE, 0600)==GNOME_VFS_OK) { + path_gpx_write(path, handle, NULL); gnome_vfs_close(handle); } g_free(tfile); return TRUE; } +/******************************************************************************/ + +gboolean +path_set_destination_from_last(Path *path, Position *pos) +{ +Point *p; +gdouble lat,lon; + +g_return_val_if_fail(path, FALSE); +g_return_val_if_fail(pos, FALSE); + +if (path->head==path->tail) { + position_set(pos, FALSE, NAN, NAN, NAN); + return FALSE; +} + +p=path_find_last_point(path); +if (p) { + unit2latlon(p->unitx, p->unity, &lat, &lon); + position_set(pos, TRUE, lat, lon, 0); +} else { + position_set(pos, FALSE, NAN, NAN, NAN); + return FALSE; +} +return TRUE; +} + +/** + * path_get_waypoint_latlon: + * @way + * @lat + * @lon + * + * Get the real latitude and longitude of given waypoint + * + * Returns: TRUE if waypoint was valid, FALSE otherwise. + */ gboolean path_get_waypoint_latlon(WayPoint *way, gdouble *lat, gdouble *lon) { @@ -424,8 +779,12 @@ return TRUE; } /** - * Add a text description at current point - * + * path_insert_mark_text: + * @path + * @text + * + * Add a new waypoint to path with given text at the last point. + * */ void path_insert_mark_text(Path *path, gchar *text) @@ -437,11 +796,542 @@ if (text) { path->wtail->desc=text; } else { path->wpcnt++; - path->wtail->desc=g_strdup_printf("WP: %u", path->wpcnt); + path->wtail->desc=g_strdup_printf("WPT:#%u", path->wpcnt); } g_signal_emit(G_OBJECT(path), signals[NEW_WAYPOINT], 0, NULL); } +/** + * path_insert_mark_autonumber: + * @path + * + * Add autonumbered waypoint to given path + * + */ +void +path_insert_mark_autonumber(Path *path) +{ +path_insert_mark_text(path, NULL); +} + +/******************************************************************************/ + +#define PATH_GPX_WRITE_ERROR path_gpx_write_error_quark() + +static GQuark +path_gpx_write_error_quark(void) +{ +return g_quark_from_static_string ("path-gpx-write-error-quark"); +} + +#define XML_DATE_FORMAT "%FT%T" + +#define WRITE_STRING(string) { \ + if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write(handle, (string), strlen((string)), &size))) { \ + g_set_error(error, \ + PATH_GPX_WRITE_ERROR, G_FILE_ERROR_FAILED, \ + "%s:\n%s\n%s", \ + _("Error while writing to file"), \ + _("File is incomplete."), \ + gnome_vfs_result_to_string(vfs_result)); \ + return FALSE; \ + } \ +} + +gboolean +path_gpx_write(Path *path, GnomeVFSHandle *handle, GError **error) +{ +Point *curr = NULL; +WayPoint *wcurr = NULL; +gboolean trkseg_break = FALSE; +gchar buffer[80]; +time_t gpx_time; +GnomeVFSResult vfs_result; +GnomeVFSFileSize size; + +g_return_val_if_fail((error == NULL || *error == NULL), FALSE); + +gpx_time = time(NULL); + +/* Find first non-zero point. */ +for (curr = path->head - 1, wcurr = path->whead; curr++ != path->tail;) { + if (curr->unity) + break; + else if (wcurr && curr == wcurr->point) + wcurr++; +} + +/* Write the header. */ +WRITE_STRING("\n" + "\n"); + +/* Write any metadata */ +WRITE_STRING("\n"); + +WRITE_STRING("\n"); + +WRITE_STRING("\n"); + +/* Write track(s) and waypoint(s) */ +WRITE_STRING("\n\n"); + +/* Curr points to first non-zero point. */ +for (curr--; curr++ != path->tail;) { + gdouble lat, lon; + if (curr->unity) { + gboolean first_sub = TRUE; + if (trkseg_break) { + /* First trkpt of the segment - write trkseg header. */ + WRITE_STRING("\n\n"); + trkseg_break = FALSE; + } + unit2latlon(curr->unitx, curr->unity, &lat, &lon); + WRITE_STRING("altitude)) { + if (first_sub) { + WRITE_STRING(">\n"); + first_sub = FALSE; + } + WRITE_STRING(""); + g_ascii_formatd(buffer, sizeof(buffer), "%.2f", curr->altitude); + WRITE_STRING(buffer); + WRITE_STRING("\n"); + } + + /* write the time */ + if (curr->time) { + if (first_sub) { + WRITE_STRING(">\n"); + first_sub = FALSE; + } + WRITE_STRING("\n"); + } + + if (wcurr && curr == wcurr->point) { + gchar *desc; + if (first_sub) { + WRITE_STRING(">\n"); + first_sub = FALSE; + } + if (wcurr->desc) { + desc=g_markup_printf_escaped("%s\n", wcurr->desc); + WRITE_STRING(desc); + g_free(desc); + } + wcurr++; + } + if (first_sub) { + WRITE_STRING("/>\n"); + } else { + WRITE_STRING("\n"); + } + } else + trkseg_break = TRUE; +} + +/* Write the footer. */ +WRITE_STRING("\n\n\n"); + +return TRUE; +} + +/** + * Handle a start tag in the parsing of a GPX file. + */ +#define MACRO_SET_UNKNOWN(utag) { \ + data->prev_state = data->state; \ + data->state = UNKNOWN; \ + data->unknown_depth = 1; \ + g_debug("GPX: unknown tag [%s]", (gchar *)utag); } + +static void +gpx_start_element(SaxData *data, const xmlChar *name, const xmlChar **attrs) +{ +switch (data->state) { +case ERROR: +break; +case START: + if (!strcmp((gchar *) name, "gpx")) + data->state = INSIDE_GPX; + else + MACRO_SET_UNKNOWN(name); +break; +case INSIDE_GPX: + if (!strcmp((gchar *) name, "trk")) + data->state = INSIDE_PATH; + else if (!strcmp((gchar *) name, "metadata")) + data->state = INSIDE_METADATA; + else + MACRO_SET_UNKNOWN(name); +break; +case INSIDE_METADATA: +break; +case INSIDE_PATH: + if (!strcmp((gchar *) name, "trkseg")) { + data->state = INSIDE_PATH_SEGMENT; + data->at_least_one_trkpt = FALSE; + } else + MACRO_SET_UNKNOWN(name); +break; +case INSIDE_PATH_SEGMENT: + if (!strcmp((gchar *) name, "trkpt")) { + const xmlChar **curr_attr; + gchar *error_check; + gdouble lat = 0.f, lon = 0.f; + gboolean has_lat, has_lon; + has_lat = FALSE; + has_lon = FALSE; + for (curr_attr = attrs; *curr_attr != NULL;) { + const gchar *attr_name = *curr_attr++; + const gchar *attr_val = *curr_attr++; + if (!strcmp(attr_name, "lat")) { + lat = g_ascii_strtod(attr_val, &error_check); + if (error_check != attr_val) + has_lat = TRUE; + } else if (!strcmp(attr_name, "lon")) { + lon = g_ascii_strtod(attr_val, &error_check); + if (error_check != attr_val) + has_lon = TRUE; + } + } + if (has_lat && has_lon) { + path_add_latlon(data->path, lat, lon, 0, 0, NAN); + data->state = INSIDE_PATH_POINT; + } else + data->state = ERROR; + } else + MACRO_SET_UNKNOWN(name); +break; +case INSIDE_PATH_POINT: + if (!strcmp((gchar *) name, "time")) + data->state = INSIDE_PATH_POINT_TIME; + else if (!strcmp((gchar *) name, "ele")) + data->state = INSIDE_PATH_POINT_ELE; + else if (!strcmp((gchar *) name, "desc")) + data->state = INSIDE_PATH_POINT_DESC; + else if (!strcmp((gchar *) name, "name")) + data->state = INSIDE_PATH_POINT_NAME; + else + MACRO_SET_UNKNOWN(name); +break; +case UNKNOWN: + data->unknown_depth++; +break; +default: ; +} + +} + +/** + * Handle an end tag in the parsing of a GPX file. + */ +static void +gpx_end_element(SaxData * data, const xmlChar * name) +{ +switch (data->state) { +case ERROR: + +break; +case START: + data->state = ERROR; +break; +case INSIDE_GPX: + if (!strcmp((gchar *) name, "gpx")) + data->state = FINISH; + else + data->state = ERROR; +break; +case INSIDE_METADATA: + if (!strcmp((gchar *) name, "metadata")) + data->state = INSIDE_GPX; +break; +case INSIDE_PATH: + if (!strcmp((gchar *) name, "trk")) + data->state = INSIDE_GPX; + else + data->state = ERROR; +break; +case INSIDE_PATH_SEGMENT: + if (!strcmp((gchar *) name, "trkseg")) { + if (data->at_least_one_trkpt) { + path_add_break(data->path); + } + data->state = INSIDE_PATH; + } else + data->state = ERROR; +break; +case INSIDE_PATH_POINT: + if (!strcmp((gchar *) name, "trkpt")) { + data->state = INSIDE_PATH_SEGMENT; + data->at_least_one_trkpt = TRUE; + } else + data->state = ERROR; +break; +case INSIDE_PATH_POINT_ELE: + if (!strcmp((gchar *) name, "ele")) { + gchar *error_check; + data->path->tail->altitude = g_ascii_strtod(data->chars->str, &error_check); + if (error_check == data->chars->str) + data->path->tail->altitude = NAN; + data->state = INSIDE_PATH_POINT; + g_string_free(data->chars, TRUE); + data->chars = g_string_new(""); + } else + data->state = ERROR; +break; +case INSIDE_PATH_POINT_TIME: + if (!strcmp((gchar *) name, "time")) { + struct tm time; + gchar *ptr; + + if (NULL == (ptr = strptime(data->chars->str, XML_DATE_FORMAT, &time))) + /* Failed to parse dateTime format. */ + data->state = ERROR; + else { + /* Parse was successful. Now we have to parse timezone. + * From here on, if there is an error, I just assume local + * timezone. Yes, this is not proper XML, but I don't + * care. */ + gchar *error_check; + + /* First, set time in "local" time zone. */ + data->path->tail->time = (mktime(&time)); + + /* Now, skip inconsequential characters */ + while (*ptr && *ptr != 'Z' && *ptr != '-' && *ptr != '+') + ptr++; + + /* Check if we ran to the end of the string. */ + if (*ptr) { + /* Next character is either 'Z', '-', or '+' */ + if (*ptr == 'Z') + /* Zulu (UTC) time. Undo the local time zone's offset. */ + data->path->tail->time += time.tm_gmtoff; + else { + /* Not Zulu (UTC). Must parse hours and minutes. */ + gint offhours = strtol(ptr, &error_check, 10); + if (error_check != ptr && *(ptr = error_check) == ':') { + /* Parse of hours worked. Check minutes. */ + gint offmins = strtol(ptr + 1, &error_check, 10); + if (error_check != (ptr + 1)) { + /* Parse of minutes worked. Calculate. */ + data->path->tail->time += (time.tm_gmtoff - (offhours * 60 * 60 + offmins * 60)); + } + } + } + } + /* Successfully parsed dateTime. */ + data->state = INSIDE_PATH_POINT; + } + + g_string_free(data->chars, TRUE); + data->chars = g_string_new(""); + } else { + data->state = ERROR; + } +break; +case INSIDE_PATH_POINT_DESC: + /* only parse description for routes */ + if (!strcmp((gchar *) name, "desc")) { + path_insert_mark_text(data->path, g_string_free(data->chars, FALSE)); + data->chars=g_string_new(""); + data->state=INSIDE_PATH_POINT; + } else { + data->state=ERROR; + } +break; +case INSIDE_PATH_POINT_NAME: + /* Just ignore these for now */ + g_string_free(data->chars, FALSE); + data->chars=g_string_new(""); + data->state=INSIDE_PATH_POINT; +break; +case UNKNOWN: + if (!--data->unknown_depth) + data->state=data->prev_state; + else + data->state=ERROR; +break; +default: ; +} + +} + +/** + * Handle char data in the parsing of a GPX file. + */ +static void +gpx_chars(SaxData *data, const xmlChar *ch, int len) +{ +guint i; + +switch (data->state) { +case ERROR: +case UNKNOWN: + break; +case INSIDE_PATH_POINT_ELE: +case INSIDE_PATH_POINT_TIME: +case INSIDE_PATH_POINT_DESC: +case INSIDE_PATH_POINT_NAME: + for (i = 0; i < len; i++) + data->chars = g_string_append_c(data->chars, ch[i]); + /* g_debug("GPXC: %s", data->chars->str); */ + break; +default: +break; +} + +} + +/** + * Handle an entity in the parsing of a GPX file. We don't do anything + * special here. + */ +static xmlEntityPtr +gpx_get_entity(SaxData *data, const xmlChar *name) +{ +return xmlGetPredefinedEntity(name); +} + +/** + * Handle an error in the parsing of a GPX file. + */ +static void +gpx_error(SaxData *data, const gchar *msg, ...) +{ +va_list args; + +va_start(args, msg); +g_logv("GPX", G_LOG_LEVEL_WARNING, msg, args); +va_end(args); + +data->state = ERROR; +} + +/** + * gpx_parse: + * @path + * @buffer + * @size + * @policy + * + * Parse a buffer of GPX data. + * XXX: Add support for parsing directly from file if the file is local. + * + */ +gboolean +path_gpx_parse(Path *path, gchar *buffer, gint size, gpx_path_policy policy) +{ +SaxData data; +xmlSAXHandler sax_handler; + +data.path=path; +data.state=START; +data.chars=g_string_new(""); + +if (policy==GPX_PATH_NEW) + path_clear(path); + +memset(&sax_handler, 0, sizeof(sax_handler)); +sax_handler.characters=(charactersSAXFunc) gpx_chars; +sax_handler.startElement=(startElementSAXFunc) gpx_start_element; +sax_handler.endElement=(endElementSAXFunc) gpx_end_element; +sax_handler.entityDecl=(entityDeclSAXFunc) gpx_get_entity; +sax_handler.warning=(warningSAXFunc) gpx_error; +sax_handler.error=(errorSAXFunc) gpx_error; +sax_handler.fatalError=(fatalErrorSAXFunc) gpx_error; + +xmlSAXUserParseMemory(&sax_handler, &data, buffer, size); + +g_string_free(data.chars, TRUE); + +if (data.state!=FINISH) { + g_warning("GPX: Parser stopped in error state %d", data.state); + return FALSE; +} + +switch (policy) { +case GPX_PATH_APPEND: +case GPX_PATH_PREPEND: + { + Point *src_first; + Path *src, *dest; + + if (policy==GPX_PATH_APPEND) { + /* Append to current path. Make sure last path point is zero. */ + path_add_break(path); + src=data.path; + dest=path; + } else { + /* Prepend to current route. */ + src=path; + dest=data.path; + } + + /* Find src_first non-zero point. */ + for (src_first = src->head; src_first++ != src->tail;) + if (src_first->unity && src_first->unitx) + break; + + /* Append route points from src to dest. */ + if (src->tail >= src_first) { + WayPoint *curr; + guint num_dest_points = dest->tail - dest->head + 1; + guint num_src_points = src->tail - src_first + 1; + + /* Adjust dest->tail to be able to fit src route data + * plus room for more route data. */ + path_resize(dest, num_dest_points + num_src_points); + + memcpy(dest->tail + 1, src_first, num_src_points * sizeof(Point)); + + dest->tail += num_src_points; + + /* Append waypoints from src to dest->. */ + path_wresize(dest, (dest->wtail - dest->whead) + (src->wtail - src->whead) + 2); + for (curr = src->whead - 1; curr++ != src->wtail;) { + (++(dest->wtail))->point = dest->head + num_dest_points + (curr->point - src_first); + dest->wtail->desc = curr->desc; + } + } + + /* Kill old route - don't use MACRO_PATH_FREE(), because that + * would free the string desc's that we just moved to data.route. */ + g_free(src->head); + g_free(src->whead); + if (policy==GPX_PATH_PREPEND) + (*path) = *dest; + } +break; +case GPX_PATH_NEW: + /* */ +break; +default: + g_assert_not_reached(); +break; +} + +return TRUE; +} + +/******************************************************************************/ + #if 0 static void path_store_append_waypoint(GtkListStore *, WayPoint *w) @@ -459,11 +1349,11 @@ g_snprintf(buffer2, sizeof(buffer2), "%.02f %s", sum * UNITS_CONVERT[_units], UN gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, - ROUTE_LATLON, buffer1, - ROUTE_DISTANCE, buffer2, - ROUTE_WAYPOINT, wcurr->desc, - ROUTE_LAT, lat2, - ROUTE_LON, lon2, + PATH_LATLON, buffer1, + PATH_DISTANCE, buffer2, + PATH_WAYPOINT, wcurr->desc, + PATH_LAT, lat2, + PATH_LON, lon2, -1); lat1=lat2; @@ -495,7 +1385,7 @@ g_return_val_if_fail(wcurr->point!=NULL, NULL); if (!path_has_waypoints(path)) return NULL; -store=gtk_list_store_new(ROUTE_NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE); +store=gtk_list_store_new(PATH_NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE); unit2latlon(wcurr->point->unitx, wcurr->point->unity, lat1, lon1); @@ -519,11 +1409,11 @@ while (wcurr!=path->wtail) { gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, - ROUTE_LATLON, buffer1, - ROUTE_DISTANCE, buffer2, - ROUTE_WAYPOINT, wcurr->desc, - ROUTE_LAT, lat2, - ROUTE_LON, lon2, + PATH_LATLON, buffer1, + PATH_DISTANCE, buffer2, + PATH_WAYPOINT, wcurr->desc, + PATH_LAT, lat2, + PATH_LON, lon2, -1); lat1=lat2;