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>
38 /** This enum defines the states of the SAX parsing state machine. */
50 INSIDE_PATH_POINT_ELE,
51 INSIDE_PATH_POINT_TIME,
52 INSIDE_PATH_POINT_DESC,
53 INSIDE_PATH_POINT_CMT,
59 /** Data used during the SAX parsing operation. */
60 typedef struct _SaxData SaxData;
65 gboolean at_least_one_trkpt;
69 typedef struct _PathSaxData PathSaxData;
75 typedef struct _PoiSaxData PoiSaxData;
83 * Handle a start tag in the parsing of a GPX file.
85 #define MACRO_SET_UNKNOWN() { \
86 data->sax_data.prev_state = data->sax_data.state; \
87 data->sax_data.state = UNKNOWN; \
88 data->sax_data.unknown_depth = 1; \
91 static gchar XML_TZONE[7];
94 * Handle char data in the parsing of a GPX file.
97 gpx_chars(SaxData *data, const xmlChar *ch, int len)
100 vprintf("%s()\n", __PRETTY_FUNCTION__);
107 case INSIDE_WPT_NAME:
108 case INSIDE_WPT_DESC:
109 case INSIDE_PATH_POINT_ELE:
110 case INSIDE_PATH_POINT_TIME:
111 case INSIDE_PATH_POINT_DESC:
112 case INSIDE_PATH_POINT_CMT:
113 for(i = 0; i < len; i++)
114 data->chars = g_string_append_c(data->chars, ch[i]);
115 vprintf("%s\n", data->chars->str);
121 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
125 * Handle an entity in the parsing of a GPX file. We don't do anything
129 gpx_get_entity(SaxData *data, const xmlChar *name)
131 vprintf("%s()\n", __PRETTY_FUNCTION__);
132 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
133 return xmlGetPredefinedEntity(name);
137 * Handle an error in the parsing of a GPX file.
140 gpx_error(SaxData *data, const gchar *msg, ...)
142 vprintf("%s()\n", __PRETTY_FUNCTION__);
143 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
148 gpx_write_string(GnomeVFSHandle *handle, const gchar *str)
150 GnomeVFSResult vfs_result;
151 GnomeVFSFileSize size;
152 if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write(
153 handle, str, strlen(str), &size)))
155 gchar buffer[BUFFER_SIZE];
156 snprintf(buffer, sizeof(buffer),
157 "%s:\n%s\n%s", _("Error while writing to file"),
158 _("File is incomplete."),
159 gnome_vfs_result_to_string(vfs_result));
160 popup_error(_window, buffer);
167 gpx_write_escaped(GnomeVFSHandle *handle, const gchar *str)
169 const gchar *ptr = str;
170 const gchar *nullchr = ptr + strlen(ptr);
173 gchar *newptr = strpbrk(ptr, "&<>");
176 /* First, write out what we have so far. */
177 const gchar *to_write;
178 GnomeVFSResult vfs_result;
179 GnomeVFSFileSize size;
180 if(GNOME_VFS_OK != (vfs_result = gnome_vfs_write(
181 handle, ptr, newptr - ptr, &size)))
183 gchar buffer[BUFFER_SIZE];
184 snprintf(buffer, sizeof(buffer),
185 "%s:\n%s\n%s", _("Error while writing to file"),
186 _("File is incomplete."),
187 gnome_vfs_result_to_string(vfs_result));
188 popup_error(_window, buffer);
192 /* Now, write the XML entity. */
207 gpx_write_string(handle, to_write);
209 /* Advance the pointer to continue searching for entities. */
214 /* No characters need escaping - write the whole thing. */
215 gpx_write_string(handle, ptr);
222 /****************************************************************************
223 * BELOW: OPEN PATH *********************************************************
224 ****************************************************************************/
227 gpx_path_start_element(PathSaxData *data,
228 const xmlChar *name, const xmlChar **attrs)
230 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
232 switch(data->sax_data.state)
238 if(!strcmp((gchar*)name, "gpx"))
239 data->sax_data.state = INSIDE_GPX;
246 if(!strcmp((gchar*)name, "trk"))
247 data->sax_data.state = INSIDE_PATH;
254 if(!strcmp((gchar*)name, "trkseg"))
256 data->sax_data.state = INSIDE_PATH_SEGMENT;
257 data->sax_data.at_least_one_trkpt = FALSE;
264 case INSIDE_PATH_SEGMENT:
265 if(!strcmp((gchar*)name, "trkpt"))
267 const xmlChar **curr_attr;
269 gdouble lat = 0.0, lon = 0.0;
270 gboolean has_lat, has_lon;
273 for(curr_attr = attrs; *curr_attr != NULL; )
275 const gchar *attr_name = *curr_attr++;
276 const gchar *attr_val = *curr_attr++;
277 if(!strcmp(attr_name, "lat"))
279 lat = g_ascii_strtod(attr_val, &error_check);
280 if(error_check != attr_val)
283 else if(!strcmp(attr_name, "lon"))
285 lon = g_ascii_strtod(attr_val, &error_check);
286 if(error_check != attr_val)
290 if(has_lat && has_lon)
292 MACRO_PATH_INCREMENT_TAIL(data->path);
293 latlon2unit(lat, lon,
294 data->path.tail->unitx,
295 data->path.tail->unity);
296 data->path.tail->time = 0;
297 data->path.tail->altitude = 0;
298 data->sax_data.state = INSIDE_PATH_POINT;
301 data->sax_data.state = ERROR;
308 case INSIDE_PATH_POINT:
309 if(!strcmp((gchar*)name, "time"))
310 data->sax_data.state = INSIDE_PATH_POINT_TIME;
311 else if(!strcmp((gchar*)name, "ele"))
312 data->sax_data.state = INSIDE_PATH_POINT_ELE;
313 else if(!strcmp((gchar*)name, "desc"))
314 data->sax_data.state = INSIDE_PATH_POINT_DESC;
315 else if(!strcmp((gchar*)name, "cmt"))
316 data->sax_data.state = INSIDE_PATH_POINT_CMT;
324 data->sax_data.unknown_depth++;
330 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
334 * Handle an end tag in the parsing of a GPX file.
337 gpx_path_end_element(PathSaxData *data, const xmlChar *name)
339 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
341 switch(data->sax_data.state)
347 data->sax_data.state = ERROR;
350 if(!strcmp((gchar*)name, "gpx"))
351 data->sax_data.state = FINISH;
353 data->sax_data.state = ERROR;
356 if(!strcmp((gchar*)name, "trk"))
357 data->sax_data.state = INSIDE_GPX;
359 data->sax_data.state = ERROR;
361 case INSIDE_PATH_SEGMENT:
362 if(!strcmp((gchar*)name, "trkseg"))
364 if(data->sax_data.at_least_one_trkpt)
366 MACRO_PATH_INCREMENT_TAIL(data->path);
367 *data->path.tail = _point_null;
369 data->sax_data.state = INSIDE_PATH;
372 data->sax_data.state = ERROR;
374 case INSIDE_PATH_POINT:
375 if(!strcmp((gchar*)name, "trkpt"))
377 data->sax_data.state = INSIDE_PATH_SEGMENT;
378 data->sax_data.at_least_one_trkpt = TRUE;
381 data->sax_data.state = ERROR;
383 case INSIDE_PATH_POINT_ELE:
384 if(!strcmp((gchar*)name, "ele"))
387 data->path.tail->altitude
388 = g_ascii_strtod(data->sax_data.chars->str, &error_check);
389 if(error_check == data->sax_data.chars->str)
390 data->path.tail->altitude = 0;
391 data->sax_data.state = INSIDE_PATH_POINT;
392 g_string_free(data->sax_data.chars, TRUE);
393 data->sax_data.chars = g_string_new("");
396 data->sax_data.state = ERROR;
398 case INSIDE_PATH_POINT_TIME:
399 if(!strcmp((gchar*)name, "time"))
404 if(NULL == (ptr = strptime(data->sax_data.chars->str,
405 XML_DATE_FORMAT, &time)))
406 /* Failed to parse dateTime format. */
407 data->sax_data.state = ERROR;
410 /* Parse was successful. Now we have to parse timezone.
411 * From here on, if there is an error, I just assume local
412 * timezone. Yes, this is not proper XML, but I don't
416 /* First, set time in "local" time zone. */
417 data->path.tail->time = (mktime(&time));
419 /* Now, skip inconsequential characters */
420 while(*ptr && *ptr != 'Z' && *ptr != '-' && *ptr != '+')
423 /* Check if we ran to the end of the string. */
426 /* Next character is either 'Z', '-', or '+' */
428 /* Zulu (UTC) time. Undo the local time zone's
430 data->path.tail->time += time.tm_gmtoff;
433 /* Not Zulu (UTC). Must parse hours and minutes. */
434 gint offhours = strtol(ptr, &error_check, 10);
435 if(error_check != ptr
436 && *(ptr = error_check) == ':')
438 /* Parse of hours worked. Check minutes. */
439 gint offmins = strtol(ptr + 1,
441 if(error_check != (ptr + 1))
443 /* Parse of minutes worked. Calculate. */
444 data->path.tail->time
446 - (offhours * 60 * 60
452 /* Successfully parsed dateTime. */
453 data->sax_data.state = INSIDE_PATH_POINT;
456 g_string_free(data->sax_data.chars, TRUE);
457 data->sax_data.chars = g_string_new("");
460 data->sax_data.state = ERROR;
462 case INSIDE_PATH_POINT_CMT:
463 /* only parse description for routes */
464 if(!strcmp((gchar*)name, "cmt"))
466 if(data->path.wtail < data->path.whead
467 || data->path.wtail->point != data->path.tail)
469 MACRO_PATH_INCREMENT_WTAIL(data->path);
470 data->path.wtail->point = data->path.tail;
471 data->path.wtail->desc
472 = g_string_free(data->sax_data.chars, FALSE);
475 g_string_free(data->sax_data.chars, TRUE);
477 data->sax_data.chars = g_string_new("");
478 data->sax_data.state = INSIDE_PATH_POINT;
481 data->sax_data.state = ERROR;
483 case INSIDE_PATH_POINT_DESC:
484 /* only parse description for routes */
485 if(!strcmp((gchar*)name, "desc"))
487 /* If we already have a desc (e.g. from cmt), then overwrite */
488 if(data->path.wtail >= data->path.whead
489 && data->path.wtail->point == data->path.tail)
490 g_free(data->path.wtail->desc);
493 MACRO_PATH_INCREMENT_WTAIL(data->path);
495 data->path.wtail->point = data->path.tail;
496 data->path.wtail->desc
497 = g_string_free(data->sax_data.chars, FALSE);
498 data->sax_data.chars = g_string_new("");
499 data->sax_data.state = INSIDE_PATH_POINT;
502 data->sax_data.state = ERROR;
505 if(!--data->sax_data.unknown_depth)
506 data->sax_data.state = data->sax_data.prev_state;
512 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
516 gpx_path_parse(Path *to_replace, gchar *buffer, gint size, gint policy_old)
519 xmlSAXHandler sax_handler;
520 printf("%s()\n", __PRETTY_FUNCTION__);
522 MACRO_PATH_INIT(data.path);
523 data.sax_data.state = START;
524 data.sax_data.chars = g_string_new("");
526 memset(&sax_handler, 0, sizeof(sax_handler));
527 sax_handler.characters = (charactersSAXFunc)gpx_chars;
528 sax_handler.startElement = (startElementSAXFunc)gpx_path_start_element;
529 sax_handler.endElement = (endElementSAXFunc)gpx_path_end_element;
530 sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity;
531 sax_handler.warning = (warningSAXFunc)gpx_error;
532 sax_handler.error = (errorSAXFunc)gpx_error;
533 sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error;
535 xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
536 g_string_free(data.sax_data.chars, TRUE);
538 if(data.sax_data.state != FINISH)
540 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
544 if(policy_old && to_replace->head != to_replace->tail)
551 /* Append to current path. Make sure last path point is zero. */
552 if(to_replace->tail->unity != 0)
554 MACRO_PATH_INCREMENT_TAIL((*to_replace));
555 *to_replace->tail = _point_null;
562 /* Prepend to current route. */
567 /* Find src_first non-zero point. */
568 for(src_first = src->head - 1; src_first++ != src->tail; )
572 /* Append route points from src to dest. */
573 if(src->tail >= src_first)
576 gint num_dest_points = dest->tail - dest->head + 1;
577 gint num_src_points = src->tail - src_first + 1;
579 /* Adjust dest->tail to be able to fit src route data
580 * plus room for more route data. */
582 num_dest_points + num_src_points + ARRAY_CHUNK_SIZE);
584 memcpy(dest->tail + 1, src_first,
585 num_src_points * sizeof(Point));
587 dest->tail += num_src_points;
589 /* Append waypoints from src to dest->. */
590 path_wresize(dest, (dest->wtail - dest->whead)
591 + (src->wtail - src->whead) + 2 + ARRAY_CHUNK_SIZE);
592 for(curr = src->whead - 1; curr++ != src->wtail; )
594 (++(dest->wtail))->point = dest->head + num_dest_points
595 + (curr->point - src_first);
596 dest->wtail->desc = curr->desc;
601 /* Kill old route - don't use MACRO_PATH_FREE(), because that
602 * would free the string desc's that we just moved to data.route. */
606 (*to_replace) = *dest;
610 MACRO_PATH_FREE((*to_replace));
611 /* Overwrite with data.route. */
612 (*to_replace) = data.path;
613 path_resize(to_replace,
614 to_replace->tail - to_replace->head + 1 + ARRAY_CHUNK_SIZE);
615 path_wresize(to_replace,
616 to_replace->wtail - to_replace->whead + 1 + ARRAY_CHUNK_SIZE);
619 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
623 /****************************************************************************
624 * ABOVE: OPEN PATH *********************************************************
625 ****************************************************************************/
627 /****************************************************************************
628 * BELOW: SAVE PATH *********************************************************
629 ****************************************************************************/
632 gpx_path_write(Path *path, GnomeVFSHandle *handle)
635 WayPoint *wcurr = NULL;
636 gboolean trkseg_break = FALSE;
637 printf("%s()\n", __PRETTY_FUNCTION__);
639 /* Find first non-zero point. */
640 for(curr = path->head - 1, wcurr = path->whead; curr++ != path->tail; )
644 else if(wcurr <= path->wtail && curr == wcurr->point)
648 /* Write the header. */
649 gpx_write_string(handle,
650 "<?xml version=\"1.0\"?>\n"
651 "<gpx version=\"1.0\" creator=\"maemo-mapper\" "
652 "xmlns=\"http://www.topografix.com/GPX/1/0\">\n"
656 /* Curr points to first non-zero point. */
657 for(curr--; curr++ != path->tail; )
663 gboolean first_sub = TRUE;
666 /* First trkpt of the segment - write trkseg header. */
667 gpx_write_string(handle, " </trkseg>\n"
669 trkseg_break = FALSE;
671 unit2latlon(curr->unitx, curr->unity, lat, lon);
672 gpx_write_string(handle, " <trkpt lat=\"");
673 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lat);
674 gpx_write_string(handle, buffer);
675 gpx_write_string(handle, "\" lon=\"");
676 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", lon);
677 gpx_write_string(handle, buffer);
678 gpx_write_string(handle, "\"");
680 /* write the elevation */
681 if(curr->altitude != 0)
685 gpx_write_string(handle, ">\n");
688 gpx_write_string(handle, " <ele>");
690 g_ascii_formatd(buffer, 80, "%.2f", curr->altitude);
691 gpx_write_string(handle, buffer);
693 gpx_write_string(handle, "</ele>\n");
701 gpx_write_string(handle, ">\n");
704 gpx_write_string(handle, " <time>");
705 strftime(buffer, 80, XML_DATE_FORMAT, localtime(&curr->time));
706 gpx_write_string(handle, buffer);
707 gpx_write_string(handle, XML_TZONE);
708 gpx_write_string(handle, "</time>\n");
711 if(wcurr && curr == wcurr->point)
715 gpx_write_string(handle, ">\n");
718 gpx_write_string(handle, " <desc>");
719 gpx_write_escaped(handle, wcurr->desc);
720 gpx_write_string(handle, "</desc>\n");
725 gpx_write_string(handle, "/>\n");
729 gpx_write_string(handle, " </trkpt>\n");
736 /* Write the footer. */
737 gpx_write_string(handle,
743 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
747 /****************************************************************************
748 * ABOVE: SAVE PATH *********************************************************
749 ****************************************************************************/
751 /****************************************************************************
752 * BELOW: OPEN POI **********************************************************
753 ****************************************************************************/
756 gpx_poi_start_element(PoiSaxData *data,
757 const xmlChar *name, const xmlChar **attrs)
759 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
761 switch(data->sax_data.state)
767 if(!strcmp((gchar*)name, "gpx"))
768 data->sax_data.state = INSIDE_GPX;
775 if(!strcmp((gchar*)name, "wpt"))
777 const xmlChar **curr_attr;
779 gdouble lat = 0.0, lon = 0.0;
780 gboolean has_lat, has_lon;
784 /* Parse the attributes - there should be lat and lon. */
785 for(curr_attr = attrs; *curr_attr != NULL; )
787 const gchar *attr_name = *curr_attr++;
788 const gchar *attr_val = *curr_attr++;
789 if(!strcmp(attr_name, "lat"))
791 lat = g_ascii_strtod(attr_val, &error_check);
792 if(error_check != attr_val)
795 else if(!strcmp(attr_name, "lon"))
797 lon = g_ascii_strtod(attr_val, &error_check);
798 if(error_check != attr_val)
802 if(has_lat && has_lon)
804 data->sax_data.state = INSIDE_WPT;
805 data->curr_poi = g_slice_new0(PoiInfo);
806 data->curr_poi->lat = lat;
807 data->curr_poi->lon = lon;
808 data->poi_list = g_list_append(
809 data->poi_list, data->curr_poi);
812 data->sax_data.state = ERROR;
820 if(!strcmp((gchar*)name, "name"))
821 data->sax_data.state = INSIDE_WPT_NAME;
822 else if(!strcmp((gchar*)name, "desc"))
823 data->sax_data.state = INSIDE_WPT_DESC;
824 else if(!strcmp((gchar*)name, "cmt"))
825 data->sax_data.state = INSIDE_WPT_CMT;
832 data->sax_data.unknown_depth++;
838 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
842 * Handle an end tag in the parsing of a GPX file.
845 gpx_poi_end_element(PoiSaxData *data, const xmlChar *name)
847 vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
849 switch(data->sax_data.state)
855 data->sax_data.state = ERROR;
858 if(!strcmp((gchar*)name, "gpx"))
860 data->sax_data.state = FINISH;
863 data->sax_data.state = ERROR;
866 if(!strcmp((gchar*)name, "wpt"))
867 data->sax_data.state = INSIDE_GPX;
869 data->sax_data.state = ERROR;
871 case INSIDE_WPT_NAME:
872 if(!strcmp((gchar*)name, "name"))
874 data->curr_poi->label
875 = g_string_free(data->sax_data.chars, FALSE);
876 data->sax_data.chars = g_string_new("");
877 data->sax_data.state = INSIDE_WPT;
880 data->sax_data.state = ERROR;
883 if(!strcmp((gchar*)name, "cmt"))
885 /* Only use if we don't already have a desc */
886 if(!data->curr_poi->desc)
889 = g_string_free(data->sax_data.chars, FALSE);
890 data->sax_data.chars = g_string_new("");
891 data->sax_data.state = INSIDE_WPT;
895 data->sax_data.state = ERROR;
897 case INSIDE_WPT_DESC:
898 if(!strcmp((gchar*)name, "desc"))
900 /* If we already have a desc (e.g. from cmt), then overwrite */
901 if(data->curr_poi->desc)
902 g_free(data->curr_poi->desc);
904 = g_string_free(data->sax_data.chars, FALSE);
905 data->sax_data.chars = g_string_new("");
906 data->sax_data.state = INSIDE_WPT;
909 data->sax_data.state = ERROR;
912 if(!--data->sax_data.unknown_depth)
913 data->sax_data.state = data->sax_data.prev_state;
919 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
923 gpx_poi_parse(gchar *buffer, gint size, GList **poi_list)
926 xmlSAXHandler sax_handler;
927 printf("%s()\n", __PRETTY_FUNCTION__);
929 data.poi_list = *poi_list;
930 data.sax_data.state = START;
931 data.sax_data.chars = g_string_new("");
933 memset(&sax_handler, 0, sizeof(sax_handler));
934 sax_handler.characters = (charactersSAXFunc)gpx_chars;
935 sax_handler.startElement = (startElementSAXFunc)gpx_poi_start_element;
936 sax_handler.endElement = (endElementSAXFunc)gpx_poi_end_element;
937 sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity;
938 sax_handler.warning = (warningSAXFunc)gpx_error;
939 sax_handler.error = (errorSAXFunc)gpx_error;
940 sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error;
942 xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
943 g_string_free(data.sax_data.chars, TRUE);
944 *poi_list = data.poi_list;
946 if(data.sax_data.state != FINISH)
948 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
952 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
956 /****************************************************************************
957 * ABOVE: OPEN POI **********************************************************
958 ****************************************************************************/
960 /****************************************************************************
961 * BELOW: SAVE POI **********************************************************
962 ****************************************************************************/
965 gpx_poi_write(GtkTreeModel *model, GnomeVFSHandle *handle)
967 gint num_written = 0;
969 printf("%s()\n", __PRETTY_FUNCTION__);
971 /* Write the header. */
972 gpx_write_string(handle,
973 "<?xml version=\"1.0\"?>\n"
974 "<gpx version=\"1.0\" creator=\"maemo-mapper\" "
975 "xmlns=\"http://www.topografix.com/GPX/1/0\">\n");
977 /* Iterate through the data model and import as desired. */
978 if(gtk_tree_model_get_iter_first(model, &iter)) do
982 memset(&poi, 0, sizeof(poi));
984 gtk_tree_model_get(model, &iter,
985 POI_SELECTED, &selected,
986 POI_POIID, &(poi.poi_id),
987 POI_CATID, &(poi.cat_id),
990 POI_LABEL, &(poi.label),
991 POI_DESC, &(poi.desc),
992 POI_CLABEL, &(poi.clabel),
999 gpx_write_string(handle, " <wpt lat=\"");
1000 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", poi.lat);
1001 gpx_write_string(handle, buffer);
1002 gpx_write_string(handle, "\" lon=\"");
1003 g_ascii_formatd(buffer, sizeof(buffer), "%.06f", poi.lon);
1004 gpx_write_string(handle, buffer);
1005 gpx_write_string(handle, "\">\n");
1007 if(poi.label && *poi.label)
1009 gpx_write_string(handle, " <name>");
1010 gpx_write_escaped(handle, poi.label);
1011 gpx_write_string(handle, "</name>\n");
1014 if(poi.desc && *poi.desc)
1016 gpx_write_string(handle, " <desc>");
1017 gpx_write_escaped(handle, poi.desc);
1018 gpx_write_string(handle, "</desc>\n");
1020 gpx_write_string(handle, " </wpt>\n");
1023 } while(gtk_tree_model_iter_next(model, &iter));
1025 /* Write the footer. */
1026 gpx_write_string(handle, "</gpx>\n");
1028 vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, num_written);
1032 /****************************************************************************
1033 * ABOVE: SAVE POI **********************************************************
1034 ****************************************************************************/
1039 printf("%s()\n", __PRETTY_FUNCTION__);
1046 localtime_r(&time1, &time2);
1047 snprintf(XML_TZONE, sizeof(XML_TZONE), "%+03ld:%02ld",
1048 (time2.tm_gmtoff / 60 / 60), (time2.tm_gmtoff / 60) % 60);
1051 vprintf("%s(): return\n", __PRETTY_FUNCTION__);