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. */
160 gpx_write_string(handle, to_write);
162 /* Advance the pointer to continue searching for entities. */
167 /* No characters need escaping - write the whole thing. */
168 gpx_write_string(handle, ptr);
175 /****************************************************************************
176 * BELOW: OPEN PATH *********************************************************
177 ****************************************************************************/
180 gpx_path_start_element(PathSaxData *data,
181 const xmlChar *name, const xmlChar **attrs)
183 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
185 switch(data->sax_data.state)
191 if(!strcmp((gchar*)name, "gpx"))
192 data->sax_data.state = INSIDE_GPX;
197 if(!strcmp((gchar*)name, "trk"))
198 data->sax_data.state = INSIDE_PATH;
203 if(!strcmp((gchar*)name, "trkseg"))
205 data->sax_data.state = INSIDE_PATH_SEGMENT;
206 data->sax_data.at_least_one_trkpt = FALSE;
211 case INSIDE_PATH_SEGMENT:
212 if(!strcmp((gchar*)name, "trkpt"))
214 const xmlChar **curr_attr;
216 gdouble lat = 0.0, lon = 0.0;
217 gboolean has_lat, has_lon;
220 for(curr_attr = attrs; *curr_attr != NULL; )
222 const gchar *attr_name = *curr_attr++;
223 const gchar *attr_val = *curr_attr++;
224 if(!strcmp(attr_name, "lat"))
226 lat = g_ascii_strtod(attr_val, &error_check);
227 if(error_check != attr_val)
230 else if(!strcmp(attr_name, "lon"))
232 lon = g_ascii_strtod(attr_val, &error_check);
233 if(error_check != attr_val)
237 if(has_lat && has_lon)
239 MACRO_PATH_INCREMENT_TAIL(data->path);
240 latlon2unit(lat, lon,
241 data->path.tail->unitx,
242 data->path.tail->unity);
243 data->path.tail->time = 0;
244 data->path.tail->altitude = 0;
245 data->sax_data.state = INSIDE_PATH_POINT;
248 data->sax_data.state = ERROR;
253 case INSIDE_PATH_POINT:
254 if(!strcmp((gchar*)name, "time"))
255 data->sax_data.state = INSIDE_PATH_POINT_TIME;
256 else if(!strcmp((gchar*)name, "ele"))
257 data->sax_data.state = INSIDE_PATH_POINT_ELE;
258 else if(!strcmp((gchar*)name, "desc"))
259 data->sax_data.state = INSIDE_PATH_POINT_DESC;
265 data->sax_data.unknown_depth++;
271 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
275 * Handle an end tag in the parsing of a GPX file.
278 gpx_path_end_element(PathSaxData *data, const xmlChar *name)
280 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
282 switch(data->sax_data.state)
288 data->sax_data.state = ERROR;
291 if(!strcmp((gchar*)name, "gpx"))
292 data->sax_data.state = FINISH;
294 data->sax_data.state = ERROR;
297 if(!strcmp((gchar*)name, "trk"))
298 data->sax_data.state = INSIDE_GPX;
300 data->sax_data.state = ERROR;
302 case INSIDE_PATH_SEGMENT:
303 if(!strcmp((gchar*)name, "trkseg"))
305 if(data->sax_data.at_least_one_trkpt)
307 MACRO_PATH_INCREMENT_TAIL(data->path);
308 *data->path.tail = _point_null;
310 data->sax_data.state = INSIDE_PATH;
313 data->sax_data.state = ERROR;
315 case INSIDE_PATH_POINT:
316 if(!strcmp((gchar*)name, "trkpt"))
318 data->sax_data.state = INSIDE_PATH_SEGMENT;
319 data->sax_data.at_least_one_trkpt = TRUE;
322 data->sax_data.state = ERROR;
324 case INSIDE_PATH_POINT_ELE:
325 if(!strcmp((gchar*)name, "ele"))
328 data->path.tail->altitude
329 = g_ascii_strtod(data->sax_data.chars->str, &error_check);
330 if(error_check == data->sax_data.chars->str)
331 data->path.tail->altitude = 0;
332 data->sax_data.state = INSIDE_PATH_POINT;
333 g_string_free(data->sax_data.chars, TRUE);
334 data->sax_data.chars = g_string_new("");
337 data->sax_data.state = ERROR;
339 case INSIDE_PATH_POINT_TIME:
340 if(!strcmp((gchar*)name, "time"))
345 if(NULL == (ptr = strptime(data->sax_data.chars->str,
346 XML_DATE_FORMAT, &time)))
347 /* Failed to parse dateTime format. */
348 data->sax_data.state = ERROR;
351 /* Parse was successful. Now we have to parse timezone.
352 * From here on, if there is an error, I just assume local
353 * timezone. Yes, this is not proper XML, but I don't
357 /* First, set time in "local" time zone. */
358 data->path.tail->time = (mktime(&time));
360 /* Now, skip inconsequential characters */
361 while(*ptr && *ptr != 'Z' && *ptr != '-' && *ptr != '+')
364 /* Check if we ran to the end of the string. */
367 /* Next character is either 'Z', '-', or '+' */
369 /* Zulu (UTC) time. Undo the local time zone's
371 data->path.tail->time += time.tm_gmtoff;
374 /* Not Zulu (UTC). Must parse hours and minutes. */
375 gint offhours = strtol(ptr, &error_check, 10);
376 if(error_check != ptr
377 && *(ptr = error_check) == ':')
379 /* Parse of hours worked. Check minutes. */
380 gint offmins = strtol(ptr + 1,
382 if(error_check != (ptr + 1))
384 /* Parse of minutes worked. Calculate. */
385 data->path.tail->time
387 - (offhours * 60 * 60
393 /* Successfully parsed dateTime. */
394 data->sax_data.state = INSIDE_PATH_POINT;
397 g_string_free(data->sax_data.chars, TRUE);
398 data->sax_data.chars = g_string_new("");
401 data->sax_data.state = ERROR;
403 case INSIDE_PATH_POINT_DESC:
404 /* only parse description for routes */
405 if(!strcmp((gchar*)name, "desc"))
407 MACRO_PATH_INCREMENT_WTAIL(data->path);
408 data->path.wtail->point = data->path.tail;
409 data->path.wtail->desc
410 = g_string_free(data->sax_data.chars, FALSE);
411 data->sax_data.chars = g_string_new("");
412 data->sax_data.state = INSIDE_PATH_POINT;
415 data->sax_data.state = ERROR;
418 if(!--data->sax_data.unknown_depth)
419 data->sax_data.state = data->sax_data.prev_state;
421 data->sax_data.state = ERROR;
427 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
431 gpx_path_parse(Path *to_replace, gchar *buffer, gint size, gint policy_old)
434 xmlSAXHandler sax_handler;
435 printf("%s()\n", __PRETTY_FUNCTION__);
437 MACRO_PATH_INIT(data.path);
438 data.sax_data.state = START;
439 data.sax_data.chars = g_string_new("");
441 memset(&sax_handler, 0, sizeof(sax_handler));
442 sax_handler.characters = (charactersSAXFunc)gpx_chars;
443 sax_handler.startElement = (startElementSAXFunc)gpx_path_start_element;
444 sax_handler.endElement = (endElementSAXFunc)gpx_path_end_element;
445 sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity;
446 sax_handler.warning = (warningSAXFunc)gpx_error;
447 sax_handler.error = (errorSAXFunc)gpx_error;
448 sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error;
450 xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
451 g_string_free(data.sax_data.chars, TRUE);
453 if(data.sax_data.state != FINISH)
455 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
459 if(policy_old && to_replace->head != to_replace->tail)
466 /* Append to current path. Make sure last path point is zero. */
467 if(to_replace->tail->unity != 0)
469 MACRO_PATH_INCREMENT_TAIL((*to_replace));
470 *to_replace->tail = _point_null;
477 /* Prepend to current route. */
482 /* Find src_first non-zero point. */
483 for(src_first = src->head - 1; src_first++ != src->tail; )
487 /* Append route points from src to dest. */
488 if(src->tail >= src_first)
491 gint num_dest_points = dest->tail - dest->head + 1;
492 gint num_src_points = src->tail - src_first + 1;
494 /* Adjust dest->tail to be able to fit src route data
495 * plus room for more route data. */
497 num_dest_points + num_src_points + ARRAY_CHUNK_SIZE);
499 memcpy(dest->tail + 1, src_first,
500 num_src_points * sizeof(Point));
502 dest->tail += num_src_points;
504 /* Append waypoints from src to dest->. */
505 path_wresize(dest, (dest->wtail - dest->whead)
506 + (src->wtail - src->whead) + 2 + ARRAY_CHUNK_SIZE);
507 for(curr = src->whead - 1; curr++ != src->wtail; )
509 (++(dest->wtail))->point = dest->head + num_dest_points
510 + (curr->point - src_first);
511 dest->wtail->desc = curr->desc;
516 /* Kill old route - don't use MACRO_PATH_FREE(), because that
517 * would free the string desc's that we just moved to data.route. */
521 (*to_replace) = *dest;
525 MACRO_PATH_FREE((*to_replace));
526 /* Overwrite with data.route. */
527 (*to_replace) = data.path;
528 path_resize(to_replace,
529 to_replace->tail - to_replace->head + 1 + ARRAY_CHUNK_SIZE);
530 path_wresize(to_replace,
531 to_replace->wtail - to_replace->whead + 1 + ARRAY_CHUNK_SIZE);
534 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
538 /****************************************************************************
539 * ABOVE: OPEN PATH *********************************************************
540 ****************************************************************************/
542 /****************************************************************************
543 * BELOW: SAVE PATH *********************************************************
544 ****************************************************************************/
547 gpx_path_write(Path *path, GnomeVFSHandle *handle)
550 WayPoint *wcurr = NULL;
551 gboolean trkseg_break = FALSE;
552 printf("%s()\n", __PRETTY_FUNCTION__);
554 /* Find first non-zero point. */
555 for(curr = path->head - 1, wcurr = path->whead; curr++ != path->tail; )
559 else if(wcurr <= path->wtail && curr == wcurr->point)
563 /* Write the header. */
564 gpx_write_string(handle,
565 "<?xml version=\"1.0\"?>\n"
566 "<gpx version=\"1.0\" creator=\"maemo-mapper\" "
567 "xmlns=\"http://www.topografix.com/GPX/1/0\">\n"
571 /* Curr points to first non-zero point. */
572 for(curr--; curr++ != path->tail; )
578 gboolean first_sub = TRUE;
581 /* First trkpt of the segment - write trkseg header. */
582 gpx_write_string(handle, " </trkseg>\n"
584 trkseg_break = FALSE;
586 unit2latlon(curr->unitx, curr->unity, lat, lon);
587 gpx_write_string(handle, " <trkpt lat=\"");
588 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lat);
589 gpx_write_string(handle, buffer);
590 gpx_write_string(handle, "\" lon=\"");
591 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lon);
592 gpx_write_string(handle, buffer);
593 gpx_write_string(handle, "\"");
595 /* write the elevation */
596 if(curr->altitude != 0)
600 gpx_write_string(handle, ">\n");
603 gpx_write_string(handle, " <ele>");
605 g_ascii_formatd(buffer, 80, "%.2f", curr->altitude);
606 gpx_write_string(handle, buffer);
608 gpx_write_string(handle, "</ele>\n");
616 gpx_write_string(handle, ">\n");
619 gpx_write_string(handle, " <time>");
620 strftime(buffer, 80, XML_DATE_FORMAT, localtime(&curr->time));
621 gpx_write_string(handle, buffer);
622 gpx_write_string(handle, XML_TZONE);
623 gpx_write_string(handle, "</time>\n");
626 if(wcurr && curr == wcurr->point)
630 gpx_write_string(handle, ">\n");
633 gpx_write_string(handle, " <desc>");
634 gpx_write_escaped(handle, wcurr->desc);
635 gpx_write_string(handle, "</desc>\n");
640 gpx_write_string(handle, "/>\n");
644 gpx_write_string(handle, " </trkpt>\n");
651 /* Write the footer. */
652 gpx_write_string(handle,
658 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
662 /****************************************************************************
663 * ABOVE: SAVE PATH *********************************************************
664 ****************************************************************************/
666 /****************************************************************************
667 * BELOW: OPEN POI **********************************************************
668 ****************************************************************************/
671 gpx_poi_start_element(PoiSaxData *data,
672 const xmlChar *name, const xmlChar **attrs)
674 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
676 switch(data->sax_data.state)
682 if(!strcmp((gchar*)name, "gpx"))
683 data->sax_data.state = INSIDE_GPX;
688 if(!strcmp((gchar*)name, "wpt"))
690 const xmlChar **curr_attr;
692 gdouble lat = 0.0, lon = 0.0;
693 gboolean has_lat, has_lon;
697 /* Parse the attributes - there should be lat and lon. */
698 for(curr_attr = attrs; *curr_attr != NULL; )
700 const gchar *attr_name = *curr_attr++;
701 const gchar *attr_val = *curr_attr++;
702 if(!strcmp(attr_name, "lat"))
704 lat = g_ascii_strtod(attr_val, &error_check);
705 if(error_check != attr_val)
708 else if(!strcmp(attr_name, "lon"))
710 lon = g_ascii_strtod(attr_val, &error_check);
711 if(error_check != attr_val)
715 if(has_lat && has_lon)
717 data->sax_data.state = INSIDE_WPT;
718 data->curr_poi = g_slice_new0(PoiInfo);
719 data->curr_poi->lat = lat;
720 data->curr_poi->lon = lon;
721 data->poi_list = g_list_append(
722 data->poi_list, data->curr_poi);
725 data->sax_data.state = ERROR;
731 if(!strcmp((gchar*)name, "name"))
732 data->sax_data.state = INSIDE_WPT_NAME;
733 else if(!strcmp((gchar*)name, "desc"))
734 data->sax_data.state = INSIDE_WPT_DESC;
739 printf("UNKNOWN!\n");
740 data->sax_data.unknown_depth++;
746 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
750 * Handle an end tag in the parsing of a GPX file.
753 gpx_poi_end_element(PoiSaxData *data, const xmlChar *name)
755 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
757 switch(data->sax_data.state)
763 data->sax_data.state = ERROR;
766 if(!strcmp((gchar*)name, "gpx"))
768 data->sax_data.state = FINISH;
771 data->sax_data.state = ERROR;
774 if(!strcmp((gchar*)name, "wpt"))
775 data->sax_data.state = INSIDE_GPX;
777 data->sax_data.state = ERROR;
779 case INSIDE_WPT_NAME:
780 if(!strcmp((gchar*)name, "name"))
782 data->curr_poi->label
783 = g_string_free(data->sax_data.chars, FALSE);
784 data->sax_data.chars = g_string_new("");
785 data->sax_data.state = INSIDE_WPT;
788 data->sax_data.state = ERROR;
790 case INSIDE_WPT_DESC:
791 if(!strcmp((gchar*)name, "desc"))
794 = g_string_free(data->sax_data.chars, FALSE);
795 data->sax_data.chars = g_string_new("");
796 data->sax_data.state = INSIDE_WPT;
799 data->sax_data.state = ERROR;
802 printf("UNKNOWN!\n");
803 if(!--data->sax_data.unknown_depth)
804 data->sax_data.state = data->sax_data.prev_state;
806 data->sax_data.state = ERROR;
812 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
816 gpx_poi_parse(gchar *buffer, gint size, GList **poi_list)
819 xmlSAXHandler sax_handler;
820 printf("%s()\n", __PRETTY_FUNCTION__);
822 data.poi_list = *poi_list;
823 data.sax_data.state = START;
824 data.sax_data.chars = g_string_new("");
826 memset(&sax_handler, 0, sizeof(sax_handler));
827 sax_handler.characters = (charactersSAXFunc)gpx_chars;
828 sax_handler.startElement = (startElementSAXFunc)gpx_poi_start_element;
829 sax_handler.endElement = (endElementSAXFunc)gpx_poi_end_element;
830 sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity;
831 sax_handler.warning = (warningSAXFunc)gpx_error;
832 sax_handler.error = (errorSAXFunc)gpx_error;
833 sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error;
835 xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
836 g_string_free(data.sax_data.chars, TRUE);
837 *poi_list = data.poi_list;
839 if(data.sax_data.state != FINISH)
841 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
845 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
849 /****************************************************************************
850 * ABOVE: OPEN POI **********************************************************
851 ****************************************************************************/
853 /****************************************************************************
854 * BELOW: SAVE POI **********************************************************
855 ****************************************************************************/
858 gpx_poi_write(GtkTreeModel *model, GnomeVFSHandle *handle)
860 gint num_written = 0;
862 printf("%s()\n", __PRETTY_FUNCTION__);
864 /* Write the header. */
865 gpx_write_string(handle,
866 "<?xml version=\"1.0\"?>\n"
867 "<gpx version=\"1.0\" creator=\"maemo-mapper\" "
868 "xmlns=\"http://www.topografix.com/GPX/1/0\">\n");
870 /* Iterate through the data model and import as desired. */
871 if(gtk_tree_model_get_iter_first(model, &iter)) do
875 memset(&poi, 0, sizeof(poi));
877 gtk_tree_model_get(model, &iter,
878 POI_SELECTED, &selected,
879 POI_POIID, &(poi.poi_id),
880 POI_CATID, &(poi.cat_id),
883 POI_LABEL, &(poi.label),
884 POI_DESC, &(poi.desc),
885 POI_CLABEL, &(poi.clabel),
892 gpx_write_string(handle, " <wpt lat=\"");
893 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", poi.lat);
894 gpx_write_string(handle, buffer);
895 gpx_write_string(handle, "\" lon=\"");
896 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", poi.lon);
897 gpx_write_string(handle, buffer);
898 gpx_write_string(handle, "\">\n");
900 if(poi.label && *poi.label)
902 gpx_write_string(handle, " <name>");
903 gpx_write_escaped(handle, poi.label);
904 gpx_write_string(handle, "</name>\n");
907 if(poi.desc && *poi.desc)
909 gpx_write_string(handle, " <desc>");
910 gpx_write_escaped(handle, poi.desc);
911 gpx_write_string(handle, "</desc>\n");
913 gpx_write_string(handle, " </wpt>\n");
916 } while(gtk_tree_model_iter_next(model, &iter));
918 /* Write the footer. */
919 gpx_write_string(handle, "</gpx>\n");
921 vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, num_written);
925 /****************************************************************************
926 * ABOVE: SAVE POI **********************************************************
927 ****************************************************************************/
932 printf("%s()\n", __PRETTY_FUNCTION__);
939 localtime_r(&time1, &time2);
940 snprintf(XML_TZONE, sizeof(XML_TZONE), "%+03ld:%02ld",
941 (time2.tm_gmtoff / 60 / 60), (time2.tm_gmtoff / 60) % 60);
944 vprintf("%s(): return\n", __PRETTY_FUNCTION__);