2 * This file is part of maemo-mapper
4 * Copyright (C) 2006-2007 John Costigan.
6 * POI and GPS-Info code originally written by Cezary Jackiewicz.
8 * Default map data provided by http://www.openstreetmap.org/
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #define _(String) gettext(String)
39 #include <glib/gstdio.h>
42 #include <libgnomevfs/gnome-vfs.h>
43 #include <curl/multi.h>
44 #include <gconf/gconf-client.h>
45 #include <libxml/parser.h>
52 #include "ui-common.h"
55 #include "mapper-types.h"
58 #define XML_DATE_FORMAT "%FT%T"
68 localtime_r(&time1, &time2);
69 snprintf(XML_TZONE, sizeof(XML_TZONE), "%+03ld:%02ld",
70 (time2.tm_gmtoff / 60 / 60), (time2.tm_gmtoff / 60) % 60);
74 write_gpx(Path * path, GnomeVFSHandle * handle)
77 WayPoint *wcurr = NULL;
78 gboolean trkseg_break = FALSE;
79 printf("%s()\n", __PRETTY_FUNCTION__);
81 /* Find first non-zero point. */
82 for (curr = path->head - 1, wcurr = path->whead; curr++ != path->tail;) {
85 else if (wcurr && curr == wcurr->point)
89 /* Write the header. */
90 WRITE_STRING("<?xml version=\"1.0\"?>\n"
91 "<gpx version=\"1.0\" creator=\"mapper\" "
92 "xmlns=\"http://www.topografix.com/GPX/1/0\">\n"
93 " <trk>\n" " <trkseg>\n");
95 /* Curr points to first non-zero point. */
96 for (curr--; curr++ != path->tail;) {
100 gboolean first_sub = TRUE;
102 /* First trkpt of the segment - write trkseg header. */
103 WRITE_STRING(" </trkseg>\n"
105 trkseg_break = FALSE;
107 unit2latlon(curr->unitx, curr->unity, lat, lon);
108 WRITE_STRING(" <trkpt lat=\"");
109 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lat);
110 WRITE_STRING(buffer);
111 WRITE_STRING("\" lon=\"");
112 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lon);
113 WRITE_STRING(buffer);
116 /* write the elevation */
117 if (!isnan(curr->altitude)) {
122 WRITE_STRING(" <ele>");
124 g_ascii_formatd(buffer, 80, "%.2f",
126 WRITE_STRING(buffer);
128 WRITE_STRING("</ele>\n");
137 WRITE_STRING(" <time>");
138 strftime(buffer, 80, XML_DATE_FORMAT,
139 localtime(&curr->time));
140 WRITE_STRING(buffer);
141 WRITE_STRING(XML_TZONE);
142 WRITE_STRING("</time>\n");
145 if (wcurr && curr == wcurr->point) {
150 WRITE_STRING(" <desc>");
151 WRITE_STRING(wcurr->desc);
152 WRITE_STRING("</desc>\n");
156 WRITE_STRING("/>\n");
158 WRITE_STRING(" </trkpt>\n");
164 /* Write the footer. */
165 WRITE_STRING(" </trkseg>\n" " </trk>\n" "</gpx>\n");
167 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
172 * Handle a start tag in the parsing of a GPX file.
174 #define MACRO_SET_UNKNOWN() { \
175 data->prev_state = data->state; \
176 data->state = UNKNOWN; \
177 data->unknown_depth = 1; \
181 gpx_start_element(SaxData * data, const xmlChar * name, const xmlChar ** attrs)
183 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
185 switch (data->state) {
189 if (!strcmp((gchar *) name, "gpx"))
190 data->state = INSIDE_GPX;
195 if (!strcmp((gchar *) name, "trk"))
196 data->state = INSIDE_PATH;
201 if (!strcmp((gchar *) name, "trkseg")) {
202 data->state = INSIDE_PATH_SEGMENT;
203 data->at_least_one_trkpt = FALSE;
207 case INSIDE_PATH_SEGMENT:
208 if (!strcmp((gchar *) name, "trkpt")) {
209 const xmlChar **curr_attr;
211 gfloat lat = 0.f, lon = 0.f;
212 gboolean has_lat, has_lon;
215 for (curr_attr = attrs; *curr_attr != NULL;) {
216 const gchar *attr_name = *curr_attr++;
217 const gchar *attr_val = *curr_attr++;
218 if (!strcmp(attr_name, "lat")) {
219 lat = g_ascii_strtod(attr_val, &error_check);
220 if (error_check != attr_val)
222 } else if (!strcmp(attr_name, "lon")) {
223 lon = g_ascii_strtod(attr_val, &error_check);
224 if (error_check != attr_val)
228 if (has_lat && has_lon) {
229 MACRO_PATH_INCREMENT_TAIL(data->path);
230 latlon2unit(lat, lon, data->path.tail->unitx, data->path.tail->unity);
231 data->path.tail->time = 0;
232 data->path.tail->altitude = NAN;
233 data->state = INSIDE_PATH_POINT;
239 case INSIDE_PATH_POINT:
240 if (!strcmp((gchar *) name, "time"))
241 data->state = INSIDE_PATH_POINT_TIME;
242 else if (!strcmp((gchar *) name, "ele"))
243 data->state = INSIDE_PATH_POINT_ELE;
244 else if (!strcmp((gchar *) name, "desc"))
245 data->state = INSIDE_PATH_POINT_DESC;
251 data->unknown_depth++;
257 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
261 * Handle an end tag in the parsing of a GPX file.
264 gpx_end_element(SaxData * data, const xmlChar * name)
266 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
268 switch (data->state) {
275 if (!strcmp((gchar *) name, "gpx"))
276 data->state = FINISH;
281 if (!strcmp((gchar *) name, "trk"))
282 data->state = INSIDE_GPX;
286 case INSIDE_PATH_SEGMENT:
287 if (!strcmp((gchar *) name, "trkseg")) {
288 if (data->at_least_one_trkpt) {
289 MACRO_PATH_INCREMENT_TAIL(data->path);
290 *data->path.tail = _point_null;
292 data->state = INSIDE_PATH;
296 case INSIDE_PATH_POINT:
297 if (!strcmp((gchar *) name, "trkpt")) {
298 data->state = INSIDE_PATH_SEGMENT;
299 data->at_least_one_trkpt = TRUE;
303 case INSIDE_PATH_POINT_ELE:
304 if (!strcmp((gchar *) name, "ele")) {
306 data->path.tail->altitude = g_ascii_strtod(data->chars->str, &error_check);
307 if (error_check == data->chars->str)
308 data->path.tail->altitude = NAN;
309 data->state = INSIDE_PATH_POINT;
310 g_string_free(data->chars, TRUE);
311 data->chars = g_string_new("");
315 case INSIDE_PATH_POINT_TIME:
316 if (!strcmp((gchar *) name, "time")) {
320 if (NULL == (ptr = strptime(data->chars->str,
321 XML_DATE_FORMAT, &time)))
322 /* Failed to parse dateTime format. */
325 /* Parse was successful. Now we have to parse timezone.
326 * From here on, if there is an error, I just assume local
327 * timezone. Yes, this is not proper XML, but I don't
331 /* First, set time in "local" time zone. */
332 data->path.tail->time = (mktime(&time));
334 /* Now, skip inconsequential characters */
335 while (*ptr && *ptr != 'Z' && *ptr != '-'
339 /* Check if we ran to the end of the string. */
341 /* Next character is either 'Z', '-', or '+' */
343 /* Zulu (UTC) time. Undo the local time zone's
345 data->path.tail->time += time.tm_gmtoff;
347 /* Not Zulu (UTC). Must parse hours and minutes. */
349 strtol(ptr, &error_check,
351 if (error_check != ptr
353 error_check) == ':') {
354 /* Parse of hours worked. Check minutes. */
361 /* Parse of minutes worked. Calculate. */
375 /* Successfully parsed dateTime. */
376 data->state = INSIDE_PATH_POINT;
379 g_string_free(data->chars, TRUE);
380 data->chars = g_string_new("");
384 case INSIDE_PATH_POINT_DESC:
385 /* only parse description for routes */
386 if (!strcmp((gchar *) name, "desc")) {
387 MACRO_PATH_INCREMENT_WTAIL(data->path);
388 data->path.wtail->point = data->path.tail;
389 data->path.wtail->desc
390 = g_string_free(data->chars, FALSE);
391 data->chars = g_string_new("");
392 data->state = INSIDE_PATH_POINT;
397 if (!--data->unknown_depth)
398 data->state = data->prev_state;
406 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
410 * Handle char data in the parsing of a GPX file.
413 gpx_chars(SaxData * data, const xmlChar * ch, int len)
416 vprintf("%s()\n", __PRETTY_FUNCTION__);
418 switch (data->state) {
422 case INSIDE_PATH_POINT_ELE:
423 case INSIDE_PATH_POINT_TIME:
424 case INSIDE_PATH_POINT_DESC:
425 for (i = 0; i < len; i++)
426 data->chars = g_string_append_c(data->chars, ch[i]);
427 vprintf("%s\n", data->chars->str);
433 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
437 * Handle an entity in the parsing of a GPX file. We don't do anything
441 gpx_get_entity(SaxData * data, const xmlChar * name)
443 vprintf("%s()\n", __PRETTY_FUNCTION__);
444 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
445 return xmlGetPredefinedEntity(name);
449 * Handle an error in the parsing of a GPX file.
452 gpx_error(SaxData * data, const gchar * msg, ...)
454 vprintf("%s()\n", __PRETTY_FUNCTION__);
455 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
460 parse_gpx(Path * to_replace, gchar * buffer, gint size, gint policy_old)
463 xmlSAXHandler sax_handler;
464 printf("%s()\n", __PRETTY_FUNCTION__);
466 MACRO_PATH_INIT(data.path);
468 data.chars = g_string_new("");
470 memset(&sax_handler, 0, sizeof(sax_handler));
471 sax_handler.characters = (charactersSAXFunc) gpx_chars;
472 sax_handler.startElement = (startElementSAXFunc) gpx_start_element;
473 sax_handler.endElement = (endElementSAXFunc) gpx_end_element;
474 sax_handler.entityDecl = (entityDeclSAXFunc) gpx_get_entity;
475 sax_handler.warning = (warningSAXFunc) gpx_error;
476 sax_handler.error = (errorSAXFunc) gpx_error;
477 sax_handler.fatalError = (fatalErrorSAXFunc) gpx_error;
479 xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
480 g_string_free(data.chars, TRUE);
482 if (data.state != FINISH) {
483 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
487 if (policy_old && to_replace->head != to_replace->tail) {
491 if (policy_old > 0) {
492 /* Append to current path. Make sure last path point is zero. */
493 if (to_replace->tail->unity != 0) {
494 MACRO_PATH_INCREMENT_TAIL((*to_replace));
495 *to_replace->tail = _point_null;
500 /* Prepend to current route. */
505 /* Find src_first non-zero point. */
506 for (src_first = src->head - 1; src_first++ != src->tail;)
507 if (src_first->unity)
510 /* Append route points from src to dest. */
511 if (src->tail >= src_first) {
513 guint num_dest_points = dest->tail - dest->head + 1;
514 guint num_src_points = src->tail - src_first + 1;
516 /* Adjust dest->tail to be able to fit src route data
517 * plus room for more route data. */
518 path_resize(dest, num_dest_points + num_src_points);
520 memcpy(dest->tail + 1, src_first,
521 num_src_points * sizeof(Point));
523 dest->tail += num_src_points;
525 /* Append waypoints from src to dest->. */
526 path_wresize(dest, (dest->wtail - dest->whead)
527 + (src->wtail - src->whead) + 2);
528 for (curr = src->whead - 1; curr++ != src->wtail;) {
529 (++(dest->wtail))->point =
530 dest->head + num_dest_points +
531 (curr->point - src_first);
532 dest->wtail->desc = curr->desc;
537 /* Kill old route - don't use MACRO_PATH_FREE(), because that
538 * would free the string desc's that we just moved to data.route. */
542 (*to_replace) = *dest;
544 MACRO_PATH_FREE((*to_replace));
545 /* Overwrite with data.route. */
546 (*to_replace) = data.path;
547 path_resize(to_replace,
548 to_replace->tail - to_replace->head + 1);
549 path_wresize(to_replace,
550 to_replace->wtail - to_replace->whead + 1);
553 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);