+++ /dev/null
-/*
- * This file is part of mapper
- *
- * Copyright (C) 2006-2007 John Costigan.
- *
- * POI and GPS-Info code originally written by Cezary Jackiewicz.
- *
- * Default map data provided by http://www.openstreetmap.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <config.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <stddef.h>
-#include <locale.h>
-#include <math.h>
-#include <errno.h>
-#include <sys/wait.h>
-#include <glib/gstdio.h>
-#include <glib/gi18n.h>
-#include <gtk/gtk.h>
-#include <fcntl.h>
-#include <libgnomevfs/gnome-vfs.h>
-#include <curl/multi.h>
-#include <gconf/gconf-client.h>
-#include <libxml/parser.h>
-
-#include <libintl.h>
-#include <locale.h>
-
-#include <sqlite3.h>
-
-#include "ui-common.h"
-#include "path.h"
-#include "utils.h"
-#include "mapper-types.h"
-#include "latlon.h"
-#include "gpx.h"
-#include "dialogs.h"
-#include "gtkmap.h"
-
-#define XML_DATE_FORMAT "%FT%T"
-
-#define WRITE_STRING(string) { \
- GnomeVFSResult vfs_result; \
- GnomeVFSFileSize size; \
- if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write(handle, (string), strlen((string)), &size))) \
- { \
- gchar buffer[BUFFER_SIZE]; \
- g_snprintf(buffer, sizeof(buffer), \
- "%s:\n%s\n%s", _("Error while writing to file"), _("File is incomplete."), \
- gnome_vfs_result_to_string(vfs_result)); \
- popup_error(_window, buffer); \
- return FALSE; \
- } \
-}
-
-/** 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];
-
-void
-gpx_init(void)
-{
-time_t time1;
-struct tm time2;
-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);
-}
-
-gboolean
-gpx_write(Path *path, GnomeVFSHandle *handle)
-{
-Point *curr = NULL;
-WayPoint *wcurr = NULL;
-gboolean trkseg_break = FALSE;
-gchar buffer[80];
-time_t gpx_time;
-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("<?xml version=\"1.0\"?>\n"
- "<gpx version=\"1.0\" creator=\"mapper\" xmlns=\"http://www.topografix.com/GPX/1/0\">\n");
-
-/* Write any metadata */
-WRITE_STRING("<metadata>\n");
-
-WRITE_STRING("<time>");
-strftime(buffer, sizeof(buffer), XML_DATE_FORMAT, localtime(&gpx_time));
-WRITE_STRING(buffer);
-WRITE_STRING(XML_TZONE);
-WRITE_STRING("</time>\n");
-
-WRITE_STRING("</metadata>\n");
-
-/* Write track(s) and waypoint(s) */
-WRITE_STRING("<trk>\n<trkseg>\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("</trkseg>\n<trkseg>\n");
- trkseg_break = FALSE;
- }
- unit2latlon(curr->unitx, curr->unity, &lat, &lon);
- WRITE_STRING("<trkpt lat=\"");
- g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lat);
- WRITE_STRING(buffer);
- WRITE_STRING("\" lon=\"");
- g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lon);
- WRITE_STRING(buffer);
- WRITE_STRING("\"");
-
- /* write the elevation */
- if (!isnan(curr->altitude)) {
- if (first_sub) {
- WRITE_STRING(">\n");
- first_sub = FALSE;
- }
- WRITE_STRING("<ele>");
- g_ascii_formatd(buffer, sizeof(buffer), "%.2f", curr->altitude);
- WRITE_STRING(buffer);
- WRITE_STRING("</ele>\n");
- }
-
- /* write the time */
- if (curr->time) {
- if (first_sub) {
- WRITE_STRING(">\n");
- first_sub = FALSE;
- }
- WRITE_STRING("<time>");
- strftime(buffer, sizeof(buffer), XML_DATE_FORMAT, localtime(&curr->time));
- WRITE_STRING(buffer);
- WRITE_STRING(XML_TZONE);
- WRITE_STRING("</time>\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("<desc>%s</desc>\n", wcurr->desc);
- WRITE_STRING(desc);
- g_free(desc);
- }
- wcurr++;
- }
- if (first_sub) {
- WRITE_STRING("/>\n");
- } else {
- WRITE_STRING("</trkpt>\n");
- }
- } else
- trkseg_break = TRUE;
-}
-
-/* Write the footer. */
-WRITE_STRING("</trkseg>\n</trk>\n</gpx>\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) {
- MACRO_PATH_INCREMENT_TAIL(data->path);
- latlon2unit(lat, lon, &data->path.tail->unitx, &data->path.tail->unity);
- data->path.tail->time = 0;
- data->path.tail->altitude = 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) {
- MACRO_PATH_INCREMENT_TAIL(data->path);
- *data->path.tail = _point_null;
- }
- 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")) {
- MACRO_PATH_INCREMENT_WTAIL(data->path);
- data->path.wtail->point = data->path.tail;
- data->path.wtail->desc = 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;
-}
-
-/**
- * Parse a buffer of GPX data.
- *
- * XXX: Add support for parsing directly from file if the file is local.
- *
- */
-gboolean
-gpx_parse(Path *path, gchar *buffer, gint size, gpx_path_policy policy)
-{
-SaxData data;
-xmlSAXHandler sax_handler;
-
-MACRO_PATH_INIT(data.path);
-data.state = START;
-data.chars = g_string_new("");
-
-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. */
- if (path->tail->unity != 0) {
- MACRO_PATH_INCREMENT_TAIL((*path));
- *path->tail = _point_null;
- }
- 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 - 1; src_first++ != src->tail;)
- if (src_first->unity)
- 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:
- /* Overwrite with data.route. */
- MACRO_PATH_FREE((*path));
- (*path) = data.path;
- path_resize(path, path->tail - path->head + 1);
- path_wresize(path, path->wtail - path->whead + 1);
-break;
-default:
- g_assert_not_reached();
-break;
-}
-
-return TRUE;
-}