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__);
102 /****************************************************************************
103 * BELOW: OPEN PATH *********************************************************
104 ****************************************************************************/
107 gpx_path_start_element(PathSaxData *data,
108 const xmlChar *name, const xmlChar **attrs)
110 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
112 switch(data->sax_data.state)
118 if(!strcmp((gchar*)name, "gpx"))
119 data->sax_data.state = INSIDE_GPX;
124 if(!strcmp((gchar*)name, "trk"))
125 data->sax_data.state = INSIDE_PATH;
130 if(!strcmp((gchar*)name, "trkseg"))
132 data->sax_data.state = INSIDE_PATH_SEGMENT;
133 data->sax_data.at_least_one_trkpt = FALSE;
138 case INSIDE_PATH_SEGMENT:
139 if(!strcmp((gchar*)name, "trkpt"))
141 const xmlChar **curr_attr;
143 gdouble lat = 0.0, lon = 0.0;
144 gboolean has_lat, has_lon;
147 for(curr_attr = attrs; *curr_attr != NULL; )
149 const gchar *attr_name = *curr_attr++;
150 const gchar *attr_val = *curr_attr++;
151 if(!strcmp(attr_name, "lat"))
153 lat = g_ascii_strtod(attr_val, &error_check);
154 if(error_check != attr_val)
157 else if(!strcmp(attr_name, "lon"))
159 lon = g_ascii_strtod(attr_val, &error_check);
160 if(error_check != attr_val)
164 if(has_lat && has_lon)
166 MACRO_PATH_INCREMENT_TAIL(data->path);
167 latlon2unit(lat, lon,
168 data->path.tail->unitx,
169 data->path.tail->unity);
170 data->path.tail->time = 0;
171 data->path.tail->altitude = 0;
172 data->sax_data.state = INSIDE_PATH_POINT;
175 data->sax_data.state = ERROR;
180 case INSIDE_PATH_POINT:
181 if(!strcmp((gchar*)name, "time"))
182 data->sax_data.state = INSIDE_PATH_POINT_TIME;
183 else if(!strcmp((gchar*)name, "ele"))
184 data->sax_data.state = INSIDE_PATH_POINT_ELE;
185 else if(!strcmp((gchar*)name, "desc"))
186 data->sax_data.state = INSIDE_PATH_POINT_DESC;
192 data->sax_data.unknown_depth++;
198 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
202 * Handle an end tag in the parsing of a GPX file.
205 gpx_path_end_element(PathSaxData *data, const xmlChar *name)
207 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
209 switch(data->sax_data.state)
215 data->sax_data.state = ERROR;
218 if(!strcmp((gchar*)name, "gpx"))
219 data->sax_data.state = FINISH;
221 data->sax_data.state = ERROR;
224 if(!strcmp((gchar*)name, "trk"))
225 data->sax_data.state = INSIDE_GPX;
227 data->sax_data.state = ERROR;
229 case INSIDE_PATH_SEGMENT:
230 if(!strcmp((gchar*)name, "trkseg"))
232 if(data->sax_data.at_least_one_trkpt)
234 MACRO_PATH_INCREMENT_TAIL(data->path);
235 *data->path.tail = _point_null;
237 data->sax_data.state = INSIDE_PATH;
240 data->sax_data.state = ERROR;
242 case INSIDE_PATH_POINT:
243 if(!strcmp((gchar*)name, "trkpt"))
245 data->sax_data.state = INSIDE_PATH_SEGMENT;
246 data->sax_data.at_least_one_trkpt = TRUE;
249 data->sax_data.state = ERROR;
251 case INSIDE_PATH_POINT_ELE:
252 if(!strcmp((gchar*)name, "ele"))
255 data->path.tail->altitude
256 = g_ascii_strtod(data->sax_data.chars->str, &error_check);
257 if(error_check == data->sax_data.chars->str)
258 data->path.tail->altitude = 0;
259 data->sax_data.state = INSIDE_PATH_POINT;
260 g_string_free(data->sax_data.chars, TRUE);
261 data->sax_data.chars = g_string_new("");
264 data->sax_data.state = ERROR;
266 case INSIDE_PATH_POINT_TIME:
267 if(!strcmp((gchar*)name, "time"))
272 if(NULL == (ptr = strptime(data->sax_data.chars->str,
273 XML_DATE_FORMAT, &time)))
274 /* Failed to parse dateTime format. */
275 data->sax_data.state = ERROR;
278 /* Parse was successful. Now we have to parse timezone.
279 * From here on, if there is an error, I just assume local
280 * timezone. Yes, this is not proper XML, but I don't
284 /* First, set time in "local" time zone. */
285 data->path.tail->time = (mktime(&time));
287 /* Now, skip inconsequential characters */
288 while(*ptr && *ptr != 'Z' && *ptr != '-' && *ptr != '+')
291 /* Check if we ran to the end of the string. */
294 /* Next character is either 'Z', '-', or '+' */
296 /* Zulu (UTC) time. Undo the local time zone's
298 data->path.tail->time += time.tm_gmtoff;
301 /* Not Zulu (UTC). Must parse hours and minutes. */
302 gint offhours = strtol(ptr, &error_check, 10);
303 if(error_check != ptr
304 && *(ptr = error_check) == ':')
306 /* Parse of hours worked. Check minutes. */
307 gint offmins = strtol(ptr + 1,
309 if(error_check != (ptr + 1))
311 /* Parse of minutes worked. Calculate. */
312 data->path.tail->time
314 - (offhours * 60 * 60
320 /* Successfully parsed dateTime. */
321 data->sax_data.state = INSIDE_PATH_POINT;
324 g_string_free(data->sax_data.chars, TRUE);
325 data->sax_data.chars = g_string_new("");
328 data->sax_data.state = ERROR;
330 case INSIDE_PATH_POINT_DESC:
331 /* only parse description for routes */
332 if(!strcmp((gchar*)name, "desc"))
334 MACRO_PATH_INCREMENT_WTAIL(data->path);
335 data->path.wtail->point = data->path.tail;
336 data->path.wtail->desc
337 = g_string_free(data->sax_data.chars, FALSE);
338 data->sax_data.chars = g_string_new("");
339 data->sax_data.state = INSIDE_PATH_POINT;
342 data->sax_data.state = ERROR;
345 if(!--data->sax_data.unknown_depth)
346 data->sax_data.state = data->sax_data.prev_state;
348 data->sax_data.state = ERROR;
354 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
358 gpx_path_parse(Path *to_replace, gchar *buffer, gint size, gint policy_old)
361 xmlSAXHandler sax_handler;
362 printf("%s()\n", __PRETTY_FUNCTION__);
364 MACRO_PATH_INIT(data.path);
365 data.sax_data.state = START;
366 data.sax_data.chars = g_string_new("");
368 memset(&sax_handler, 0, sizeof(sax_handler));
369 sax_handler.characters = (charactersSAXFunc)gpx_chars;
370 sax_handler.startElement = (startElementSAXFunc)gpx_path_start_element;
371 sax_handler.endElement = (endElementSAXFunc)gpx_path_end_element;
372 sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity;
373 sax_handler.warning = (warningSAXFunc)gpx_error;
374 sax_handler.error = (errorSAXFunc)gpx_error;
375 sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error;
377 xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
378 g_string_free(data.sax_data.chars, TRUE);
380 if(data.sax_data.state != FINISH)
382 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
386 if(policy_old && to_replace->head != to_replace->tail)
393 /* Append to current path. Make sure last path point is zero. */
394 if(to_replace->tail->unity != 0)
396 MACRO_PATH_INCREMENT_TAIL((*to_replace));
397 *to_replace->tail = _point_null;
404 /* Prepend to current route. */
409 /* Find src_first non-zero point. */
410 for(src_first = src->head - 1; src_first++ != src->tail; )
414 /* Append route points from src to dest. */
415 if(src->tail >= src_first)
418 gint num_dest_points = dest->tail - dest->head + 1;
419 gint num_src_points = src->tail - src_first + 1;
421 /* Adjust dest->tail to be able to fit src route data
422 * plus room for more route data. */
424 num_dest_points + num_src_points + ARRAY_CHUNK_SIZE);
426 memcpy(dest->tail + 1, src_first,
427 num_src_points * sizeof(Point));
429 dest->tail += num_src_points;
431 /* Append waypoints from src to dest->. */
432 path_wresize(dest, (dest->wtail - dest->whead)
433 + (src->wtail - src->whead) + 2 + ARRAY_CHUNK_SIZE);
434 for(curr = src->whead - 1; curr++ != src->wtail; )
436 (++(dest->wtail))->point = dest->head + num_dest_points
437 + (curr->point - src_first);
438 dest->wtail->desc = curr->desc;
443 /* Kill old route - don't use MACRO_PATH_FREE(), because that
444 * would free the string desc's that we just moved to data.route. */
448 (*to_replace) = *dest;
452 MACRO_PATH_FREE((*to_replace));
453 /* Overwrite with data.route. */
454 (*to_replace) = data.path;
455 path_resize(to_replace,
456 to_replace->tail - to_replace->head + 1 + ARRAY_CHUNK_SIZE);
457 path_wresize(to_replace,
458 to_replace->wtail - to_replace->whead + 1 + ARRAY_CHUNK_SIZE);
461 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
465 /****************************************************************************
466 * ABOVE: OPEN PATH *********************************************************
467 ****************************************************************************/
469 /****************************************************************************
470 * BELOW: SAVE PATH *********************************************************
471 ****************************************************************************/
473 #define WRITE_STRING(string) { \
474 GnomeVFSResult vfs_result; \
475 GnomeVFSFileSize size; \
476 if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write( \
477 handle, (string), strlen((string)), &size))) \
479 gchar buffer[BUFFER_SIZE]; \
480 snprintf(buffer, sizeof(buffer), \
481 "%s:\n%s\n%s", _("Error while writing to file"), \
482 _("File is incomplete."), \
483 gnome_vfs_result_to_string(vfs_result)); \
484 popup_error(_window, buffer); \
490 gpx_path_write(Path *path, GnomeVFSHandle *handle)
493 WayPoint *wcurr = NULL;
494 gboolean trkseg_break = FALSE;
495 printf("%s()\n", __PRETTY_FUNCTION__);
497 /* Find first non-zero point. */
498 for(curr = path->head - 1, wcurr = path->whead; curr++ != path->tail; )
502 else if(wcurr <= path->wtail && curr == wcurr->point)
506 /* Write the header. */
508 "<?xml version=\"1.0\"?>\n"
509 "<gpx version=\"1.0\" creator=\"maemo-mapper\" "
510 "xmlns=\"http://www.topografix.com/GPX/1/0\">\n"
514 /* Curr points to first non-zero point. */
515 for(curr--; curr++ != path->tail; )
521 gboolean first_sub = TRUE;
524 /* First trkpt of the segment - write trkseg header. */
525 WRITE_STRING(" </trkseg>\n"
527 trkseg_break = FALSE;
529 unit2latlon(curr->unitx, curr->unity, lat, lon);
530 WRITE_STRING(" <trkpt lat=\"");
531 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lat);
532 WRITE_STRING(buffer);
533 WRITE_STRING("\" lon=\"");
534 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lon);
535 WRITE_STRING(buffer);
538 /* write the elevation */
539 if(curr->altitude != 0)
546 WRITE_STRING(" <ele>");
548 g_ascii_formatd(buffer, 80, "%.2f", curr->altitude);
549 WRITE_STRING(buffer);
551 WRITE_STRING("</ele>\n");
562 WRITE_STRING(" <time>");
563 strftime(buffer, 80, XML_DATE_FORMAT, localtime(&curr->time));
564 WRITE_STRING(buffer);
565 WRITE_STRING(XML_TZONE);
566 WRITE_STRING("</time>\n");
569 if(wcurr && curr == wcurr->point)
576 WRITE_STRING(" <desc>");
577 WRITE_STRING(wcurr->desc);
578 WRITE_STRING("</desc>\n");
583 WRITE_STRING("/>\n");
587 WRITE_STRING(" </trkpt>\n");
594 /* Write the footer. */
601 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
605 /****************************************************************************
606 * ABOVE: SAVE PATH *********************************************************
607 ****************************************************************************/
609 /****************************************************************************
610 * BELOW: OPEN POI **********************************************************
611 ****************************************************************************/
614 gpx_poi_start_element(PoiSaxData *data,
615 const xmlChar *name, const xmlChar **attrs)
617 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
619 switch(data->sax_data.state)
625 if(!strcmp((gchar*)name, "gpx"))
626 data->sax_data.state = INSIDE_GPX;
631 if(!strcmp((gchar*)name, "wpt"))
633 const xmlChar **curr_attr;
635 gdouble lat = 0.0, lon = 0.0;
636 gboolean has_lat, has_lon;
640 /* Parse the attributes - there should be lat and lon. */
641 for(curr_attr = attrs; *curr_attr != NULL; )
643 const gchar *attr_name = *curr_attr++;
644 const gchar *attr_val = *curr_attr++;
645 if(!strcmp(attr_name, "lat"))
647 lat = g_ascii_strtod(attr_val, &error_check);
648 if(error_check != attr_val)
651 else if(!strcmp(attr_name, "lon"))
653 lon = g_ascii_strtod(attr_val, &error_check);
654 if(error_check != attr_val)
658 if(has_lat && has_lon)
660 data->sax_data.state = INSIDE_WPT;
661 data->curr_poi = g_slice_new0(PoiInfo);
662 data->curr_poi->lat = lat;
663 data->curr_poi->lon = lon;
664 data->poi_list = g_list_append(
665 data->poi_list, data->curr_poi);
668 data->sax_data.state = ERROR;
674 if(!strcmp((gchar*)name, "name"))
675 data->sax_data.state = INSIDE_WPT_NAME;
676 else if(!strcmp((gchar*)name, "desc"))
677 data->sax_data.state = INSIDE_WPT_DESC;
682 printf("UNKNOWN!\n");
683 data->sax_data.unknown_depth++;
689 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
693 * Handle an end tag in the parsing of a GPX file.
696 gpx_poi_end_element(PoiSaxData *data, const xmlChar *name)
698 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
700 switch(data->sax_data.state)
706 data->sax_data.state = ERROR;
709 if(!strcmp((gchar*)name, "gpx"))
711 data->sax_data.state = FINISH;
714 data->sax_data.state = ERROR;
717 if(!strcmp((gchar*)name, "wpt"))
718 data->sax_data.state = INSIDE_GPX;
720 data->sax_data.state = ERROR;
722 case INSIDE_WPT_NAME:
723 if(!strcmp((gchar*)name, "name"))
725 data->curr_poi->label
726 = g_string_free(data->sax_data.chars, FALSE);
727 data->sax_data.chars = g_string_new("");
728 data->sax_data.state = INSIDE_WPT;
731 data->sax_data.state = ERROR;
733 case INSIDE_WPT_DESC:
734 if(!strcmp((gchar*)name, "desc"))
737 = g_string_free(data->sax_data.chars, FALSE);
738 data->sax_data.chars = g_string_new("");
739 data->sax_data.state = INSIDE_WPT;
742 data->sax_data.state = ERROR;
745 printf("UNKNOWN!\n");
746 if(!--data->sax_data.unknown_depth)
747 data->sax_data.state = data->sax_data.prev_state;
749 data->sax_data.state = ERROR;
755 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
759 gpx_poi_parse(gchar *buffer, gint size, GList **poi_list)
762 xmlSAXHandler sax_handler;
763 printf("%s()\n", __PRETTY_FUNCTION__);
765 data.poi_list = *poi_list;
766 data.sax_data.state = START;
767 data.sax_data.chars = g_string_new("");
769 memset(&sax_handler, 0, sizeof(sax_handler));
770 sax_handler.characters = (charactersSAXFunc)gpx_chars;
771 sax_handler.startElement = (startElementSAXFunc)gpx_poi_start_element;
772 sax_handler.endElement = (endElementSAXFunc)gpx_poi_end_element;
773 sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity;
774 sax_handler.warning = (warningSAXFunc)gpx_error;
775 sax_handler.error = (errorSAXFunc)gpx_error;
776 sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error;
778 xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
779 g_string_free(data.sax_data.chars, TRUE);
780 *poi_list = data.poi_list;
782 if(data.sax_data.state != FINISH)
784 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
788 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
792 /****************************************************************************
793 * ABOVE: OPEN POI **********************************************************
794 ****************************************************************************/
796 /****************************************************************************
797 * BELOW: SAVE POI **********************************************************
798 ****************************************************************************/
800 #define WRITE_STRING(string) { \
801 GnomeVFSResult vfs_result; \
802 GnomeVFSFileSize size; \
803 if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write( \
804 handle, (string), strlen((string)), &size))) \
806 gchar buffer[BUFFER_SIZE]; \
807 snprintf(buffer, sizeof(buffer), \
808 "%s:\n%s\n%s", _("Error while writing to file"), \
809 _("File is incomplete."), \
810 gnome_vfs_result_to_string(vfs_result)); \
811 popup_error(_window, buffer); \
817 gpx_poi_write(GtkTreeModel *model, GnomeVFSHandle *handle)
819 gint num_written = 0;
821 printf("%s()\n", __PRETTY_FUNCTION__);
823 /* Write the header. */
825 "<?xml version=\"1.0\"?>\n"
826 "<gpx version=\"1.0\" creator=\"maemo-mapper\" "
827 "xmlns=\"http://www.topografix.com/GPX/1/0\">\n");
829 /* Iterate through the data model and import as desired. */
830 if(gtk_tree_model_get_iter_first(model, &iter)) do
834 memset(&poi, 0, sizeof(poi));
836 gtk_tree_model_get(model, &iter,
837 POI_SELECTED, &selected,
838 POI_POIID, &(poi.poi_id),
839 POI_CATID, &(poi.cat_id),
842 POI_LABEL, &(poi.label),
843 POI_DESC, &(poi.desc),
844 POI_CLABEL, &(poi.clabel),
851 WRITE_STRING(" <wpt lat=\"");
852 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", poi.lat);
853 WRITE_STRING(buffer);
854 WRITE_STRING("\" lon=\"");
855 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", poi.lon);
856 WRITE_STRING(buffer);
857 WRITE_STRING("\"/>\n");
859 if(poi.label && *poi.label)
861 WRITE_STRING(" <name>");
862 WRITE_STRING(poi.label);
863 WRITE_STRING("</name>\n");
866 if(poi.desc && *poi.desc)
868 WRITE_STRING(" <desc>");
869 WRITE_STRING(poi.desc);
870 WRITE_STRING("</desc>\n");
872 WRITE_STRING(" </wpt>\n");
875 } while(gtk_tree_model_iter_next(model, &iter));
877 /* Write the footer. */
881 vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, num_written);
885 /****************************************************************************
886 * ABOVE: SAVE POI **********************************************************
887 ****************************************************************************/
892 printf("%s()\n", __PRETTY_FUNCTION__);
899 localtime_r(&time1, &time2);
900 snprintf(XML_TZONE, sizeof(XML_TZONE), "%+03ld:%02ld",
901 (time2.tm_gmtoff / 60 / 60), (time2.tm_gmtoff / 60) % 60);
904 vprintf("%s(): return\n", __PRETTY_FUNCTION__);