2 * Copyright (C) 2006, 2007 John Costigan.
4 * POI and GPS-Info code originally written by Cezary Jackiewicz.
6 * Default map data provided by http://www.openstreetmap.org/
8 * This file is part of Maemo Mapper.
10 * Maemo Mapper 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 3 of the License, or
13 * (at your option) any later version.
15 * Maemo Mapper 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
21 * along with Maemo Mapper. If not, see <http://www.gnu.org/licenses/>.
28 #include <libxml/parser.h>
39 * Handle a start tag in the parsing of a GPX file.
41 #define MACRO_SET_UNKNOWN() { \
42 data->sax_data.prev_state = data->sax_data.state; \
43 data->sax_data.state = UNKNOWN; \
44 data->sax_data.unknown_depth = 1; \
47 static gchar XML_TZONE[7];
50 * Handle char data in the parsing of a GPX file.
53 gpx_chars(SaxData *data, const xmlChar *ch, int len)
56 vprintf("%s()\n", __PRETTY_FUNCTION__);
65 case INSIDE_PATH_POINT_ELE:
66 case INSIDE_PATH_POINT_TIME:
67 case INSIDE_PATH_POINT_DESC:
68 for(i = 0; i < len; i++)
69 data->chars = g_string_append_c(data->chars, ch[i]);
70 vprintf("%s\n", data->chars->str);
76 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
80 * Handle an entity in the parsing of a GPX file. We don't do anything
84 gpx_get_entity(SaxData *data, const xmlChar *name)
86 vprintf("%s()\n", __PRETTY_FUNCTION__);
87 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
88 return xmlGetPredefinedEntity(name);
92 * Handle an error in the parsing of a GPX file.
95 gpx_error(SaxData *data, const gchar *msg, ...)
97 vprintf("%s()\n", __PRETTY_FUNCTION__);
98 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
103 gpx_write_string(GnomeVFSHandle *handle, const gchar *str)
105 GnomeVFSResult vfs_result;
106 GnomeVFSFileSize size;
107 if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write(
108 handle, str, strlen(str), &size)))
110 gchar buffer[BUFFER_SIZE];
111 snprintf(buffer, sizeof(buffer),
112 "%s:\n%s\n%s", _("Error while writing to file"),
113 _("File is incomplete."),
114 gnome_vfs_result_to_string(vfs_result));
115 popup_error(_window, buffer);
122 gpx_write_escaped(GnomeVFSHandle *handle, const gchar *str)
124 const gchar *ptr = str;
125 const gchar *nullchr = ptr + strlen(ptr);
128 gchar *newptr = strpbrk(ptr, "&<>");
131 /* First, write out what we have so far. */
132 const gchar *to_write;
133 GnomeVFSResult vfs_result;
134 GnomeVFSFileSize size;
135 if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write(
136 handle, ptr, newptr - ptr, &size)))
138 gchar buffer[BUFFER_SIZE];
139 snprintf(buffer, sizeof(buffer),
140 "%s:\n%s\n%s", _("Error while writing to file"),
141 _("File is incomplete."),
142 gnome_vfs_result_to_string(vfs_result));
143 popup_error(_window, buffer);
147 /* Now, write the XML entity. */
162 gpx_write_string(handle, to_write);
164 /* Advance the pointer to continue searching for entities. */
169 /* No characters need escaping - write the whole thing. */
170 gpx_write_string(handle, ptr);
177 /****************************************************************************
178 * BELOW: OPEN PATH *********************************************************
179 ****************************************************************************/
182 gpx_path_start_element(PathSaxData *data,
183 const xmlChar *name, const xmlChar **attrs)
185 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
187 switch(data->sax_data.state)
193 if(!strcmp((gchar*)name, "gpx"))
194 data->sax_data.state = INSIDE_GPX;
199 if(!strcmp((gchar*)name, "trk"))
200 data->sax_data.state = INSIDE_PATH;
205 if(!strcmp((gchar*)name, "trkseg"))
207 data->sax_data.state = INSIDE_PATH_SEGMENT;
208 data->sax_data.at_least_one_trkpt = FALSE;
213 case INSIDE_PATH_SEGMENT:
214 if(!strcmp((gchar*)name, "trkpt"))
216 const xmlChar **curr_attr;
218 gdouble lat = 0.0, lon = 0.0;
219 gboolean has_lat, has_lon;
222 for(curr_attr = attrs; *curr_attr != NULL; )
224 const gchar *attr_name = *curr_attr++;
225 const gchar *attr_val = *curr_attr++;
226 if(!strcmp(attr_name, "lat"))
228 lat = g_ascii_strtod(attr_val, &error_check);
229 if(error_check != attr_val)
232 else if(!strcmp(attr_name, "lon"))
234 lon = g_ascii_strtod(attr_val, &error_check);
235 if(error_check != attr_val)
239 if(has_lat && has_lon)
241 MACRO_PATH_INCREMENT_TAIL(data->path);
242 latlon2unit(lat, lon,
243 data->path.tail->unitx,
244 data->path.tail->unity);
245 data->path.tail->time = 0;
246 data->path.tail->altitude = 0;
247 data->sax_data.state = INSIDE_PATH_POINT;
250 data->sax_data.state = ERROR;
255 case INSIDE_PATH_POINT:
256 if(!strcmp((gchar*)name, "time"))
257 data->sax_data.state = INSIDE_PATH_POINT_TIME;
258 else if(!strcmp((gchar*)name, "ele"))
259 data->sax_data.state = INSIDE_PATH_POINT_ELE;
260 else if(!strcmp((gchar*)name, "desc"))
261 data->sax_data.state = INSIDE_PATH_POINT_DESC;
267 data->sax_data.unknown_depth++;
273 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
277 * Handle an end tag in the parsing of a GPX file.
280 gpx_path_end_element(PathSaxData *data, const xmlChar *name)
282 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
284 switch(data->sax_data.state)
290 data->sax_data.state = ERROR;
293 if(!strcmp((gchar*)name, "gpx"))
294 data->sax_data.state = FINISH;
296 data->sax_data.state = ERROR;
299 if(!strcmp((gchar*)name, "trk"))
300 data->sax_data.state = INSIDE_GPX;
302 data->sax_data.state = ERROR;
304 case INSIDE_PATH_SEGMENT:
305 if(!strcmp((gchar*)name, "trkseg"))
307 if(data->sax_data.at_least_one_trkpt)
309 MACRO_PATH_INCREMENT_TAIL(data->path);
310 *data->path.tail = _point_null;
312 data->sax_data.state = INSIDE_PATH;
315 data->sax_data.state = ERROR;
317 case INSIDE_PATH_POINT:
318 if(!strcmp((gchar*)name, "trkpt"))
320 data->sax_data.state = INSIDE_PATH_SEGMENT;
321 data->sax_data.at_least_one_trkpt = TRUE;
324 data->sax_data.state = ERROR;
326 case INSIDE_PATH_POINT_ELE:
327 if(!strcmp((gchar*)name, "ele"))
330 data->path.tail->altitude
331 = g_ascii_strtod(data->sax_data.chars->str, &error_check);
332 if(error_check == data->sax_data.chars->str)
333 data->path.tail->altitude = 0;
334 data->sax_data.state = INSIDE_PATH_POINT;
335 g_string_free(data->sax_data.chars, TRUE);
336 data->sax_data.chars = g_string_new("");
339 data->sax_data.state = ERROR;
341 case INSIDE_PATH_POINT_TIME:
342 if(!strcmp((gchar*)name, "time"))
347 if(NULL == (ptr = strptime(data->sax_data.chars->str,
348 XML_DATE_FORMAT, &time)))
349 /* Failed to parse dateTime format. */
350 data->sax_data.state = ERROR;
353 /* Parse was successful. Now we have to parse timezone.
354 * From here on, if there is an error, I just assume local
355 * timezone. Yes, this is not proper XML, but I don't
359 /* First, set time in "local" time zone. */
360 data->path.tail->time = (mktime(&time));
362 /* Now, skip inconsequential characters */
363 while(*ptr && *ptr != 'Z' && *ptr != '-' && *ptr != '+')
366 /* Check if we ran to the end of the string. */
369 /* Next character is either 'Z', '-', or '+' */
371 /* Zulu (UTC) time. Undo the local time zone's
373 data->path.tail->time += time.tm_gmtoff;
376 /* Not Zulu (UTC). Must parse hours and minutes. */
377 gint offhours = strtol(ptr, &error_check, 10);
378 if(error_check != ptr
379 && *(ptr = error_check) == ':')
381 /* Parse of hours worked. Check minutes. */
382 gint offmins = strtol(ptr + 1,
384 if(error_check != (ptr + 1))
386 /* Parse of minutes worked. Calculate. */
387 data->path.tail->time
389 - (offhours * 60 * 60
395 /* Successfully parsed dateTime. */
396 data->sax_data.state = INSIDE_PATH_POINT;
399 g_string_free(data->sax_data.chars, TRUE);
400 data->sax_data.chars = g_string_new("");
403 data->sax_data.state = ERROR;
405 case INSIDE_PATH_POINT_DESC:
406 /* only parse description for routes */
407 if(!strcmp((gchar*)name, "desc"))
409 MACRO_PATH_INCREMENT_WTAIL(data->path);
410 data->path.wtail->point = data->path.tail;
411 data->path.wtail->desc
412 = g_string_free(data->sax_data.chars, FALSE);
413 data->sax_data.chars = g_string_new("");
414 data->sax_data.state = INSIDE_PATH_POINT;
417 data->sax_data.state = ERROR;
420 if(!--data->sax_data.unknown_depth)
421 data->sax_data.state = data->sax_data.prev_state;
423 data->sax_data.state = ERROR;
429 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
433 gpx_path_parse(Path *to_replace, gchar *buffer, gint size, gint policy_old)
436 xmlSAXHandler sax_handler;
437 printf("%s()\n", __PRETTY_FUNCTION__);
439 MACRO_PATH_INIT(data.path);
440 data.sax_data.state = START;
441 data.sax_data.chars = g_string_new("");
443 memset(&sax_handler, 0, sizeof(sax_handler));
444 sax_handler.characters = (charactersSAXFunc)gpx_chars;
445 sax_handler.startElement = (startElementSAXFunc)gpx_path_start_element;
446 sax_handler.endElement = (endElementSAXFunc)gpx_path_end_element;
447 sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity;
448 sax_handler.warning = (warningSAXFunc)gpx_error;
449 sax_handler.error = (errorSAXFunc)gpx_error;
450 sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error;
452 xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
453 g_string_free(data.sax_data.chars, TRUE);
455 if(data.sax_data.state != FINISH)
457 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
461 if(policy_old && to_replace->head != to_replace->tail)
468 /* Append to current path. Make sure last path point is zero. */
469 if(to_replace->tail->unity != 0)
471 MACRO_PATH_INCREMENT_TAIL((*to_replace));
472 *to_replace->tail = _point_null;
479 /* Prepend to current route. */
484 /* Find src_first non-zero point. */
485 for(src_first = src->head - 1; src_first++ != src->tail; )
489 /* Append route points from src to dest. */
490 if(src->tail >= src_first)
493 gint num_dest_points = dest->tail - dest->head + 1;
494 gint num_src_points = src->tail - src_first + 1;
496 /* Adjust dest->tail to be able to fit src route data
497 * plus room for more route data. */
499 num_dest_points + num_src_points + ARRAY_CHUNK_SIZE);
501 memcpy(dest->tail + 1, src_first,
502 num_src_points * sizeof(Point));
504 dest->tail += num_src_points;
506 /* Append waypoints from src to dest->. */
507 path_wresize(dest, (dest->wtail - dest->whead)
508 + (src->wtail - src->whead) + 2 + ARRAY_CHUNK_SIZE);
509 for(curr = src->whead - 1; curr++ != src->wtail; )
511 (++(dest->wtail))->point = dest->head + num_dest_points
512 + (curr->point - src_first);
513 dest->wtail->desc = curr->desc;
518 /* Kill old route - don't use MACRO_PATH_FREE(), because that
519 * would free the string desc's that we just moved to data.route. */
523 (*to_replace) = *dest;
527 MACRO_PATH_FREE((*to_replace));
528 /* Overwrite with data.route. */
529 (*to_replace) = data.path;
530 path_resize(to_replace,
531 to_replace->tail - to_replace->head + 1 + ARRAY_CHUNK_SIZE);
532 path_wresize(to_replace,
533 to_replace->wtail - to_replace->whead + 1 + ARRAY_CHUNK_SIZE);
536 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
540 /****************************************************************************
541 * ABOVE: OPEN PATH *********************************************************
542 ****************************************************************************/
544 /****************************************************************************
545 * BELOW: SAVE PATH *********************************************************
546 ****************************************************************************/
549 gpx_path_write(Path *path, GnomeVFSHandle *handle)
552 WayPoint *wcurr = NULL;
553 gboolean trkseg_break = FALSE;
554 printf("%s()\n", __PRETTY_FUNCTION__);
556 /* Find first non-zero point. */
557 for(curr = path->head - 1, wcurr = path->whead; curr++ != path->tail; )
561 else if(wcurr <= path->wtail && curr == wcurr->point)
565 /* Write the header. */
566 gpx_write_string(handle,
567 "<?xml version=\"1.0\"?>\n"
568 "<gpx version=\"1.0\" creator=\"maemo-mapper\" "
569 "xmlns=\"http://www.topografix.com/GPX/1/0\">\n"
573 /* Curr points to first non-zero point. */
574 for(curr--; curr++ != path->tail; )
580 gboolean first_sub = TRUE;
583 /* First trkpt of the segment - write trkseg header. */
584 gpx_write_string(handle, " </trkseg>\n"
586 trkseg_break = FALSE;
588 unit2latlon(curr->unitx, curr->unity, lat, lon);
589 gpx_write_string(handle, " <trkpt lat=\"");
590 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lat);
591 gpx_write_string(handle, buffer);
592 gpx_write_string(handle, "\" lon=\"");
593 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lon);
594 gpx_write_string(handle, buffer);
595 gpx_write_string(handle, "\"");
597 /* write the elevation */
598 if(curr->altitude != 0)
602 gpx_write_string(handle, ">\n");
605 gpx_write_string(handle, " <ele>");
607 g_ascii_formatd(buffer, 80, "%.2f", curr->altitude);
608 gpx_write_string(handle, buffer);
610 gpx_write_string(handle, "</ele>\n");
618 gpx_write_string(handle, ">\n");
621 gpx_write_string(handle, " <time>");
622 strftime(buffer, 80, XML_DATE_FORMAT, localtime(&curr->time));
623 gpx_write_string(handle, buffer);
624 gpx_write_string(handle, XML_TZONE);
625 gpx_write_string(handle, "</time>\n");
628 if(wcurr && curr == wcurr->point)
632 gpx_write_string(handle, ">\n");
635 gpx_write_string(handle, " <desc>");
636 gpx_write_escaped(handle, wcurr->desc);
637 gpx_write_string(handle, "</desc>\n");
642 gpx_write_string(handle, "/>\n");
646 gpx_write_string(handle, " </trkpt>\n");
653 /* Write the footer. */
654 gpx_write_string(handle,
660 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
664 /****************************************************************************
665 * ABOVE: SAVE PATH *********************************************************
666 ****************************************************************************/
668 /****************************************************************************
669 * BELOW: OPEN POI **********************************************************
670 ****************************************************************************/
673 gpx_poi_start_element(PoiSaxData *data,
674 const xmlChar *name, const xmlChar **attrs)
676 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
678 switch(data->sax_data.state)
684 if(!strcmp((gchar*)name, "gpx"))
685 data->sax_data.state = INSIDE_GPX;
690 if(!strcmp((gchar*)name, "wpt"))
692 const xmlChar **curr_attr;
694 gdouble lat = 0.0, lon = 0.0;
695 gboolean has_lat, has_lon;
699 /* Parse the attributes - there should be lat and lon. */
700 for(curr_attr = attrs; *curr_attr != NULL; )
702 const gchar *attr_name = *curr_attr++;
703 const gchar *attr_val = *curr_attr++;
704 if(!strcmp(attr_name, "lat"))
706 lat = g_ascii_strtod(attr_val, &error_check);
707 if(error_check != attr_val)
710 else if(!strcmp(attr_name, "lon"))
712 lon = g_ascii_strtod(attr_val, &error_check);
713 if(error_check != attr_val)
717 if(has_lat && has_lon)
719 data->sax_data.state = INSIDE_WPT;
720 data->curr_poi = g_slice_new0(PoiInfo);
721 data->curr_poi->lat = lat;
722 data->curr_poi->lon = lon;
723 data->poi_list = g_list_append(
724 data->poi_list, data->curr_poi);
727 data->sax_data.state = ERROR;
733 if(!strcmp((gchar*)name, "name"))
734 data->sax_data.state = INSIDE_WPT_NAME;
735 else if(!strcmp((gchar*)name, "desc"))
736 data->sax_data.state = INSIDE_WPT_DESC;
741 printf("UNKNOWN!\n");
742 data->sax_data.unknown_depth++;
748 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
752 * Handle an end tag in the parsing of a GPX file.
755 gpx_poi_end_element(PoiSaxData *data, const xmlChar *name)
757 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
759 switch(data->sax_data.state)
765 data->sax_data.state = ERROR;
768 if(!strcmp((gchar*)name, "gpx"))
770 data->sax_data.state = FINISH;
773 data->sax_data.state = ERROR;
776 if(!strcmp((gchar*)name, "wpt"))
777 data->sax_data.state = INSIDE_GPX;
779 data->sax_data.state = ERROR;
781 case INSIDE_WPT_NAME:
782 if(!strcmp((gchar*)name, "name"))
784 data->curr_poi->label
785 = g_string_free(data->sax_data.chars, FALSE);
786 data->sax_data.chars = g_string_new("");
787 data->sax_data.state = INSIDE_WPT;
790 data->sax_data.state = ERROR;
792 case INSIDE_WPT_DESC:
793 if(!strcmp((gchar*)name, "desc"))
796 = g_string_free(data->sax_data.chars, FALSE);
797 data->sax_data.chars = g_string_new("");
798 data->sax_data.state = INSIDE_WPT;
801 data->sax_data.state = ERROR;
804 printf("UNKNOWN!\n");
805 if(!--data->sax_data.unknown_depth)
806 data->sax_data.state = data->sax_data.prev_state;
808 data->sax_data.state = ERROR;
814 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
818 gpx_poi_parse(gchar *buffer, gint size, GList **poi_list)
821 xmlSAXHandler sax_handler;
822 printf("%s()\n", __PRETTY_FUNCTION__);
824 data.poi_list = *poi_list;
825 data.sax_data.state = START;
826 data.sax_data.chars = g_string_new("");
828 memset(&sax_handler, 0, sizeof(sax_handler));
829 sax_handler.characters = (charactersSAXFunc)gpx_chars;
830 sax_handler.startElement = (startElementSAXFunc)gpx_poi_start_element;
831 sax_handler.endElement = (endElementSAXFunc)gpx_poi_end_element;
832 sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity;
833 sax_handler.warning = (warningSAXFunc)gpx_error;
834 sax_handler.error = (errorSAXFunc)gpx_error;
835 sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error;
837 xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
838 g_string_free(data.sax_data.chars, TRUE);
839 *poi_list = data.poi_list;
841 if(data.sax_data.state != FINISH)
843 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
847 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
851 /****************************************************************************
852 * ABOVE: OPEN POI **********************************************************
853 ****************************************************************************/
855 /****************************************************************************
856 * BELOW: SAVE POI **********************************************************
857 ****************************************************************************/
860 gpx_poi_write(GtkTreeModel *model, GnomeVFSHandle *handle)
862 gint num_written = 0;
864 printf("%s()\n", __PRETTY_FUNCTION__);
866 /* Write the header. */
867 gpx_write_string(handle,
868 "<?xml version=\"1.0\"?>\n"
869 "<gpx version=\"1.0\" creator=\"maemo-mapper\" "
870 "xmlns=\"http://www.topografix.com/GPX/1/0\">\n");
872 /* Iterate through the data model and import as desired. */
873 if(gtk_tree_model_get_iter_first(model, &iter)) do
877 memset(&poi, 0, sizeof(poi));
879 gtk_tree_model_get(model, &iter,
880 POI_SELECTED, &selected,
881 POI_POIID, &(poi.poi_id),
882 POI_CATID, &(poi.cat_id),
885 POI_LABEL, &(poi.label),
886 POI_DESC, &(poi.desc),
887 POI_CLABEL, &(poi.clabel),
894 gpx_write_string(handle, " <wpt lat=\"");
895 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", poi.lat);
896 gpx_write_string(handle, buffer);
897 gpx_write_string(handle, "\" lon=\"");
898 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", poi.lon);
899 gpx_write_string(handle, buffer);
900 gpx_write_string(handle, "\">\n");
902 if(poi.label && *poi.label)
904 gpx_write_string(handle, " <name>");
905 gpx_write_escaped(handle, poi.label);
906 gpx_write_string(handle, "</name>\n");
909 if(poi.desc && *poi.desc)
911 gpx_write_string(handle, " <desc>");
912 gpx_write_escaped(handle, poi.desc);
913 gpx_write_string(handle, "</desc>\n");
915 gpx_write_string(handle, " </wpt>\n");
918 } while(gtk_tree_model_iter_next(model, &iter));
920 /* Write the footer. */
921 gpx_write_string(handle, "</gpx>\n");
923 vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, num_written);
927 /****************************************************************************
928 * ABOVE: SAVE POI **********************************************************
929 ****************************************************************************/
934 printf("%s()\n", __PRETTY_FUNCTION__);
941 localtime_r(&time1, &time2);
942 snprintf(XML_TZONE, sizeof(XML_TZONE), "%+03ld:%02ld",
943 (time2.tm_gmtoff / 60 / 60), (time2.tm_gmtoff / 60) % 60);
946 vprintf("%s(): return\n", __PRETTY_FUNCTION__);