]> git.itanic.dy.fi Git - maemo-mapper/blob - src/gpx.c
Fixed bug in POI export to GPX (closes #2216)
[maemo-mapper] / src / gpx.c
1 /*
2  * Copyright (C) 2006, 2007 John Costigan.
3  *
4  * POI and GPS-Info code originally written by Cezary Jackiewicz.
5  *
6  * Default map data provided by http://www.openstreetmap.org/
7  *
8  * This file is part of Maemo Mapper.
9  *
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.
14  *
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.
19  *
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/>.
22  */
23
24 #define _GNU_SOURCE
25
26 #include <string.h>
27 #include <math.h>
28 #include <libxml/parser.h>
29
30 #include "types.h"
31 #include "data.h"
32 #include "defines.h"
33
34 #include "gpx.h"
35 #include "path.h"
36 #include "util.h"
37
38 /**
39  * Handle a start tag in the parsing of a GPX file.
40  */
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; \
45 }
46
47 static gchar XML_TZONE[7];
48
49 /**
50  * Handle char data in the parsing of a GPX file.
51  */
52 static void
53 gpx_chars(SaxData *data, const xmlChar *ch, int len)
54 {
55     gint i;
56     vprintf("%s()\n", __PRETTY_FUNCTION__);
57
58     switch(data->state)
59     {
60         case ERROR:
61         case UNKNOWN:
62             break;
63         case INSIDE_WPT_NAME:
64         case INSIDE_WPT_DESC:
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);
71             break;
72         default:
73             break;
74     }
75
76     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
77 }
78
79 /**
80  * Handle an entity in the parsing of a GPX file.  We don't do anything
81  * special here.
82  */
83 static xmlEntityPtr
84 gpx_get_entity(SaxData *data, const xmlChar *name)
85 {
86     vprintf("%s()\n", __PRETTY_FUNCTION__);
87     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
88     return xmlGetPredefinedEntity(name);
89 }
90
91 /**
92  * Handle an error in the parsing of a GPX file.
93  */
94 static void
95 gpx_error(SaxData *data, const gchar *msg, ...)
96 {
97     vprintf("%s()\n", __PRETTY_FUNCTION__);
98     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
99     data->state = ERROR;
100 }
101
102 /****************************************************************************
103  * BELOW: OPEN PATH *********************************************************
104  ****************************************************************************/
105
106 static void
107 gpx_path_start_element(PathSaxData *data,
108         const xmlChar *name, const xmlChar **attrs)
109 {
110     vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
111
112     switch(data->sax_data.state)
113     {
114         case ERROR:
115             printf("ERROR!\n");
116             break;
117         case START:
118             if(!strcmp((gchar*)name, "gpx"))
119                 data->sax_data.state = INSIDE_GPX;
120             else
121                 MACRO_SET_UNKNOWN();
122             break;
123         case INSIDE_GPX:
124             if(!strcmp((gchar*)name, "trk"))
125                 data->sax_data.state = INSIDE_PATH;
126             else
127                 MACRO_SET_UNKNOWN();
128             break;
129         case INSIDE_PATH:
130             if(!strcmp((gchar*)name, "trkseg"))
131             {
132                 data->sax_data.state = INSIDE_PATH_SEGMENT;
133                 data->sax_data.at_least_one_trkpt = FALSE;
134             }
135             else
136                 MACRO_SET_UNKNOWN();
137             break;
138         case INSIDE_PATH_SEGMENT:
139             if(!strcmp((gchar*)name, "trkpt"))
140             {
141                 const xmlChar **curr_attr;
142                 gchar *error_check;
143                 gdouble lat = 0.0, lon = 0.0;
144                 gboolean has_lat, has_lon;
145                 has_lat = FALSE;
146                 has_lon = FALSE;
147                 for(curr_attr = attrs; *curr_attr != NULL; )
148                 {
149                     const gchar *attr_name = *curr_attr++;
150                     const gchar *attr_val = *curr_attr++;
151                     if(!strcmp(attr_name, "lat"))
152                     {
153                         lat = g_ascii_strtod(attr_val, &error_check);
154                         if(error_check != attr_val)
155                             has_lat = TRUE;
156                     }
157                     else if(!strcmp(attr_name, "lon"))
158                     {
159                         lon = g_ascii_strtod(attr_val, &error_check);
160                         if(error_check != attr_val)
161                             has_lon = TRUE;
162                     }
163                 }
164                 if(has_lat && has_lon)
165                 {
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;
173                 }
174                 else
175                     data->sax_data.state = ERROR;
176             }
177             else
178                 MACRO_SET_UNKNOWN();
179             break;
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;
187
188             else
189                 MACRO_SET_UNKNOWN();
190             break;
191         case UNKNOWN:
192             data->sax_data.unknown_depth++;
193             break;
194         default:
195             ;
196     }
197
198     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
199 }
200
201 /**
202  * Handle an end tag in the parsing of a GPX file.
203  */
204 static void
205 gpx_path_end_element(PathSaxData *data, const xmlChar *name)
206 {
207     vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
208
209     switch(data->sax_data.state)
210     {
211         case ERROR:
212             printf("ERROR!\n");
213             break;
214         case START:
215             data->sax_data.state = ERROR;
216             break;
217         case INSIDE_GPX:
218             if(!strcmp((gchar*)name, "gpx"))
219                 data->sax_data.state = FINISH;
220             else
221                 data->sax_data.state = ERROR;
222             break;
223         case INSIDE_PATH:
224             if(!strcmp((gchar*)name, "trk"))
225                 data->sax_data.state = INSIDE_GPX;
226             else
227                 data->sax_data.state = ERROR;
228             break;
229         case INSIDE_PATH_SEGMENT:
230             if(!strcmp((gchar*)name, "trkseg"))
231             {
232                 if(data->sax_data.at_least_one_trkpt)
233                 {
234                     MACRO_PATH_INCREMENT_TAIL(data->path);
235                     *data->path.tail = _point_null;
236                 }
237                 data->sax_data.state = INSIDE_PATH;
238             }
239             else
240                 data->sax_data.state = ERROR;
241             break;
242         case INSIDE_PATH_POINT:
243             if(!strcmp((gchar*)name, "trkpt"))
244             {
245                 data->sax_data.state = INSIDE_PATH_SEGMENT;
246                 data->sax_data.at_least_one_trkpt = TRUE;
247             }
248             else
249                 data->sax_data.state = ERROR;
250             break;
251         case INSIDE_PATH_POINT_ELE:
252             if(!strcmp((gchar*)name, "ele"))
253             {
254                 gchar *error_check;
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("");
262             }
263             else
264                 data->sax_data.state = ERROR;
265             break;
266         case INSIDE_PATH_POINT_TIME:
267             if(!strcmp((gchar*)name, "time"))
268             {
269                 struct tm time;
270                 gchar *ptr;
271
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;
276                 else
277                 {
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
281                      * care. */
282                     gchar *error_check;
283
284                     /* First, set time in "local" time zone. */
285                     data->path.tail->time = (mktime(&time));
286
287                     /* Now, skip inconsequential characters */
288                     while(*ptr && *ptr != 'Z' && *ptr != '-' && *ptr != '+')
289                         ptr++;
290
291                     /* Check if we ran to the end of the string. */
292                     if(*ptr)
293                     {
294                         /* Next character is either 'Z', '-', or '+' */
295                         if(*ptr == 'Z')
296                             /* Zulu (UTC) time. Undo the local time zone's
297                              * offset. */
298                             data->path.tail->time += time.tm_gmtoff;
299                         else
300                         {
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) == ':')
305                             {
306                                 /* Parse of hours worked. Check minutes. */
307                                 gint offmins = strtol(ptr + 1,
308                                         &error_check, 10);
309                                 if(error_check != (ptr + 1))
310                                 {
311                                     /* Parse of minutes worked. Calculate. */
312                                     data->path.tail->time
313                                         += (time.tm_gmtoff
314                                                 - (offhours * 60 * 60
315                                                     + offmins * 60));
316                                 }
317                             }
318                         }
319                     }
320                     /* Successfully parsed dateTime. */
321                     data->sax_data.state = INSIDE_PATH_POINT;
322                 }
323
324                 g_string_free(data->sax_data.chars, TRUE);
325                 data->sax_data.chars = g_string_new("");
326             }
327             else
328                 data->sax_data.state = ERROR;
329             break;
330         case INSIDE_PATH_POINT_DESC:
331             /* only parse description for routes */
332             if(!strcmp((gchar*)name, "desc"))
333             {
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;
340             }
341             else
342                 data->sax_data.state = ERROR;
343             break;
344         case UNKNOWN:
345             if(!--data->sax_data.unknown_depth)
346                 data->sax_data.state = data->sax_data.prev_state;
347             else
348                 data->sax_data.state = ERROR;
349             break;
350         default:
351             ;
352     }
353
354     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
355 }
356
357 gboolean
358 gpx_path_parse(Path *to_replace, gchar *buffer, gint size, gint policy_old)
359 {
360     PathSaxData data;
361     xmlSAXHandler sax_handler;
362     printf("%s()\n", __PRETTY_FUNCTION__);
363
364     MACRO_PATH_INIT(data.path);
365     data.sax_data.state = START;
366     data.sax_data.chars = g_string_new("");
367
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;
376
377     xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
378     g_string_free(data.sax_data.chars, TRUE);
379
380     if(data.sax_data.state != FINISH)
381     {
382         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
383         return FALSE;
384     }
385
386     if(policy_old && to_replace->head != to_replace->tail)
387     {
388         Point *src_first;
389         Path *src, *dest;
390
391         if(policy_old > 0)
392         {
393             /* Append to current path. Make sure last path point is zero. */
394             if(to_replace->tail->unity != 0)
395             {
396                 MACRO_PATH_INCREMENT_TAIL((*to_replace));
397                 *to_replace->tail = _point_null;
398             }
399             src = &data.path;
400             dest = to_replace;
401         }
402         else
403         {
404             /* Prepend to current route. */
405             src = to_replace;
406             dest = &data.path;
407         }
408
409         /* Find src_first non-zero point. */
410         for(src_first = src->head - 1; src_first++ != src->tail; )
411             if(src_first->unity)
412                 break;
413
414         /* Append route points from src to dest. */
415         if(src->tail >= src_first)
416         {
417             WayPoint *curr;
418             gint num_dest_points = dest->tail - dest->head + 1;
419             gint num_src_points = src->tail - src_first + 1;
420
421             /* Adjust dest->tail to be able to fit src route data
422              * plus room for more route data. */
423             path_resize(dest,
424                     num_dest_points + num_src_points + ARRAY_CHUNK_SIZE);
425
426             memcpy(dest->tail + 1, src_first,
427                     num_src_points * sizeof(Point));
428
429             dest->tail += num_src_points;
430
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; )
435             {
436                 (++(dest->wtail))->point = dest->head + num_dest_points
437                     + (curr->point - src_first);
438                 dest->wtail->desc = curr->desc;
439             }
440
441         }
442
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. */
445         g_free(src->head);
446         g_free(src->whead);
447         if(policy_old < 0)
448             (*to_replace) = *dest;
449     }
450     else
451     {
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);
459     }
460
461     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
462     return TRUE;
463 }
464
465 /****************************************************************************
466  * ABOVE: OPEN PATH *********************************************************
467  ****************************************************************************/
468
469 /****************************************************************************
470  * BELOW: SAVE PATH *********************************************************
471  ****************************************************************************/
472
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))) \
478     { \
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); \
485         return FALSE; \
486     } \
487 }
488
489 gboolean
490 gpx_path_write(Path *path, GnomeVFSHandle *handle)
491 {
492     Point *curr = NULL;
493     WayPoint *wcurr = NULL;
494     gboolean trkseg_break = FALSE;
495     printf("%s()\n", __PRETTY_FUNCTION__);
496
497     /* Find first non-zero point. */
498     for(curr = path->head - 1, wcurr = path->whead; curr++ != path->tail; )
499     {
500         if(curr->unity)
501             break;
502         else if(wcurr <= path->wtail && curr == wcurr->point)
503             wcurr++;
504     }
505
506     /* Write the header. */
507     WRITE_STRING(
508             "<?xml version=\"1.0\"?>\n"
509             "<gpx version=\"1.0\" creator=\"maemo-mapper\" "
510             "xmlns=\"http://www.topografix.com/GPX/1/0\">\n"
511             "  <trk>\n"
512             "    <trkseg>\n");
513
514     /* Curr points to first non-zero point. */
515     for(curr--; curr++ != path->tail; )
516     {
517         gdouble lat, lon;
518         if(curr->unity)
519         {
520             gchar buffer[80];
521             gboolean first_sub = TRUE;
522             if(trkseg_break)
523             {
524                 /* First trkpt of the segment - write trkseg header. */
525                 WRITE_STRING("    </trkseg>\n"
526                              "    <trkseg>\n");
527                 trkseg_break = FALSE;
528             }
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);
536             WRITE_STRING("\"");
537
538             /* write the elevation */
539             if(curr->altitude != 0)
540             {
541                 if(first_sub)
542                 {
543                     WRITE_STRING(">\n");
544                     first_sub = FALSE;
545                 }
546                 WRITE_STRING("        <ele>");
547                 {
548                     g_ascii_formatd(buffer, 80, "%.2f", curr->altitude);
549                     WRITE_STRING(buffer);
550                 }
551                 WRITE_STRING("</ele>\n");
552             }
553
554             /* write the time */
555             if(curr->time)
556             {
557                 if(first_sub)
558                 {
559                     WRITE_STRING(">\n");
560                     first_sub = FALSE;
561                 }
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");
567             }
568
569             if(wcurr && curr == wcurr->point)
570             {
571                 if(first_sub)
572                 {
573                     WRITE_STRING(">\n");
574                     first_sub = FALSE;
575                 }
576                 WRITE_STRING("        <desc>");
577                 WRITE_STRING(wcurr->desc);
578                 WRITE_STRING("</desc>\n");
579                 wcurr++;
580             }
581             if(first_sub)
582             {
583                 WRITE_STRING("/>\n");
584             }
585             else
586             {
587                 WRITE_STRING("      </trkpt>\n");
588             }
589         }
590         else
591             trkseg_break = TRUE;
592     }
593
594     /* Write the footer. */
595     WRITE_STRING(
596             "    </trkseg>\n"
597             "  </trk>\n"
598             "</gpx>\n");
599
600
601     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
602     return TRUE;
603 }
604
605 /****************************************************************************
606  * ABOVE: SAVE PATH *********************************************************
607  ****************************************************************************/
608
609 /****************************************************************************
610  * BELOW: OPEN POI **********************************************************
611  ****************************************************************************/
612
613 static void
614 gpx_poi_start_element(PoiSaxData *data,
615         const xmlChar *name, const xmlChar **attrs)
616 {
617     vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
618
619     switch(data->sax_data.state)
620     {
621         case ERROR:
622             printf("ERROR!\n");
623             break;
624         case START:
625             if(!strcmp((gchar*)name, "gpx"))
626                 data->sax_data.state = INSIDE_GPX;
627             else
628                 MACRO_SET_UNKNOWN();
629             break;
630         case INSIDE_GPX:
631             if(!strcmp((gchar*)name, "wpt"))
632             {
633                 const xmlChar **curr_attr;
634                 gchar *error_check;
635                 gdouble lat = 0.0, lon = 0.0;
636                 gboolean has_lat, has_lon;
637                 has_lat = FALSE;
638                 has_lon = FALSE;
639
640                 /* Parse the attributes - there should be lat and lon. */
641                 for(curr_attr = attrs; *curr_attr != NULL; )
642                 {
643                     const gchar *attr_name = *curr_attr++;
644                     const gchar *attr_val = *curr_attr++;
645                     if(!strcmp(attr_name, "lat"))
646                     {
647                         lat = g_ascii_strtod(attr_val, &error_check);
648                         if(error_check != attr_val)
649                             has_lat = TRUE;
650                     }
651                     else if(!strcmp(attr_name, "lon"))
652                     {
653                         lon = g_ascii_strtod(attr_val, &error_check);
654                         if(error_check != attr_val)
655                             has_lon = TRUE;
656                     }
657                 }
658                 if(has_lat && has_lon)
659                 {
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);
666                 }
667                 else
668                     data->sax_data.state = ERROR;
669             }
670             else
671                 MACRO_SET_UNKNOWN();
672             break;
673         case INSIDE_WPT:
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;
678             else
679                 MACRO_SET_UNKNOWN();
680             break;
681         case UNKNOWN:
682             printf("UNKNOWN!\n");
683             data->sax_data.unknown_depth++;
684             break;
685         default:
686             ;
687     }
688
689     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
690 }
691
692 /**
693  * Handle an end tag in the parsing of a GPX file.
694  */
695 static void
696 gpx_poi_end_element(PoiSaxData *data, const xmlChar *name)
697 {
698     vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name);
699
700     switch(data->sax_data.state)
701     {
702         case ERROR:
703             printf("ERROR!\n");
704             break;
705         case START:
706             data->sax_data.state = ERROR;
707             break;
708         case INSIDE_GPX:
709             if(!strcmp((gchar*)name, "gpx"))
710             {
711                 data->sax_data.state = FINISH;
712             }
713             else
714                 data->sax_data.state = ERROR;
715             break;
716         case INSIDE_WPT:
717             if(!strcmp((gchar*)name, "wpt"))
718                 data->sax_data.state = INSIDE_GPX;
719             else
720                 data->sax_data.state = ERROR;
721             break;
722         case INSIDE_WPT_NAME:
723             if(!strcmp((gchar*)name, "name"))
724             {
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;
729             }
730             else
731                 data->sax_data.state = ERROR;
732             break;
733         case INSIDE_WPT_DESC:
734             if(!strcmp((gchar*)name, "desc"))
735             {
736                 data->curr_poi->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;
740             }
741             else
742                 data->sax_data.state = ERROR;
743             break;
744         case UNKNOWN:
745             printf("UNKNOWN!\n");
746             if(!--data->sax_data.unknown_depth)
747                 data->sax_data.state = data->sax_data.prev_state;
748             else
749                 data->sax_data.state = ERROR;
750             break;
751         default:
752             ;
753     }
754
755     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
756 }
757
758 gboolean
759 gpx_poi_parse(gchar *buffer, gint size, GList **poi_list)
760 {
761     PoiSaxData data;
762     xmlSAXHandler sax_handler;
763     printf("%s()\n", __PRETTY_FUNCTION__);
764
765     data.poi_list = *poi_list;
766     data.sax_data.state = START;
767     data.sax_data.chars = g_string_new("");
768
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;
777
778     xmlSAXUserParseMemory(&sax_handler, &data, buffer, size);
779     g_string_free(data.sax_data.chars, TRUE);
780     *poi_list = data.poi_list;
781
782     if(data.sax_data.state != FINISH)
783     {
784         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
785         return FALSE;
786     }
787
788     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
789     return TRUE;
790 }
791
792 /****************************************************************************
793  * ABOVE: OPEN POI **********************************************************
794  ****************************************************************************/
795
796 /****************************************************************************
797  * BELOW: SAVE POI **********************************************************
798  ****************************************************************************/
799
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))) \
805     { \
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); \
812         return FALSE; \
813     } \
814 }
815
816 gint
817 gpx_poi_write(GtkTreeModel *model, GnomeVFSHandle *handle)
818 {
819     gint num_written = 0;
820     GtkTreeIter iter;
821     printf("%s()\n", __PRETTY_FUNCTION__);
822
823     /* Write the header. */
824     WRITE_STRING(
825             "<?xml version=\"1.0\"?>\n"
826             "<gpx version=\"1.0\" creator=\"maemo-mapper\" "
827             "xmlns=\"http://www.topografix.com/GPX/1/0\">\n");
828
829     /* Iterate through the data model and import as desired. */
830     if(gtk_tree_model_get_iter_first(model, &iter)) do
831     {   
832         PoiInfo poi;
833         gboolean selected;
834         memset(&poi, 0, sizeof(poi));
835
836         gtk_tree_model_get(model, &iter,
837                 POI_SELECTED, &selected,
838                 POI_POIID, &(poi.poi_id),
839                 POI_CATID, &(poi.cat_id),
840                 POI_LAT, &(poi.lat),
841                 POI_LON, &(poi.lon),
842                 POI_LABEL, &(poi.label),
843                 POI_DESC, &(poi.desc),
844                 POI_CLABEL, &(poi.clabel),
845                 -1);
846
847         if(selected)
848         {
849             gchar buffer[80];
850
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");
858
859             if(poi.label && *poi.label)
860             {
861                 WRITE_STRING("    <name>");
862                 WRITE_STRING(poi.label);
863                 WRITE_STRING("</name>\n");
864             }
865
866             if(poi.desc && *poi.desc)
867             {
868                 WRITE_STRING("    <desc>");
869                 WRITE_STRING(poi.desc);
870                 WRITE_STRING("</desc>\n");
871             }
872             WRITE_STRING("  </wpt>\n");
873             ++ num_written;
874         }
875     } while(gtk_tree_model_iter_next(model, &iter));
876
877     /* Write the footer. */
878     WRITE_STRING(
879             "</gpx>\n");
880
881     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, num_written);
882     return num_written;
883 }
884
885 /****************************************************************************
886  * ABOVE: SAVE POI **********************************************************
887  ****************************************************************************/
888
889 void
890 gpx_init()
891 {
892     printf("%s()\n", __PRETTY_FUNCTION__);
893
894     /* set XML_TZONE */
895     {   
896         time_t time1;
897         struct tm time2;
898         time1 = time(NULL);
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);
902     }
903
904     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
905 }