2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
6 * Some of the code adapted from GPSBabel 1.2.7
7 * http://gpsbabel.sf.net/
8 * Copyright (C) 2002, 2003, 2004, 2005 Robert Lipe, robertlipe@usa.net
10 * This program 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 2 of the License, or
13 * (at your option) any later version.
15 * This program 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 this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #define _XOPEN_SOURCE /* glibc2 needs this */
53 tt_wpt_link
, /* New in GPX 1.1 */
61 tt_trk_trkseg_trkpt_ele
,
62 tt_trk_trkseg_trkpt_time
,
64 tt_trk_trkseg_trkpt_course
,
65 tt_trk_trkseg_trkpt_speed
,
66 tt_trk_trkseg_trkpt_fix
,
67 tt_trk_trkseg_trkpt_sat
,
69 tt_trk_trkseg_trkpt_hdop
,
70 tt_trk_trkseg_trkpt_vdop
,
71 tt_trk_trkseg_trkpt_pdop
,
78 typedef struct tag_mapping
{
79 tag_type tag_type
; /* enum from above for this tag */
80 const char *tag_name
; /* xpath-ish tag name */
84 GpxWritingOptions
*options
;
89 * xpath(ish) mappings between full tag paths and internal identifers.
90 * These appear in the order they appear in the GPX specification.
91 * If it's not a tag we explictly handle, it doesn't go here.
94 tag_mapping tag_path_map
[] = {
96 { tt_wpt
, "/gpx/wpt" },
98 { tt_waypoint
, "/loc/waypoint" },
99 { tt_waypoint_coord
, "/loc/waypoint/coord" },
100 { tt_waypoint_name
, "/loc/waypoint/name" },
102 { tt_wpt_ele
, "/gpx/wpt/ele" },
103 { tt_wpt_name
, "/gpx/wpt/name" },
104 { tt_wpt_desc
, "/gpx/wpt/desc" },
105 { tt_wpt_sym
, "/gpx/wpt/sym" },
106 { tt_wpt_sym
, "/loc/waypoint/type" },
107 { tt_wpt_link
, "/gpx/wpt/link" }, /* GPX 1.1 */
109 { tt_trk
, "/gpx/trk" },
110 { tt_trk
, "/gpx/rte" },
111 { tt_trk_name
, "/gpx/trk/name" },
112 { tt_trk_desc
, "/gpx/trk/desc" },
113 { tt_trk_trkseg
, "/gpx/trk/trkseg" },
114 { tt_trk_trkseg_trkpt
, "/gpx/trk/trkseg/trkpt" },
115 { tt_trk_trkseg_trkpt
, "/gpx/rte/rtept" },
116 { tt_trk_trkseg_trkpt_ele
, "/gpx/trk/trkseg/trkpt/ele" },
117 { tt_trk_trkseg_trkpt_time
, "/gpx/trk/trkseg/trkpt/time" },
119 { tt_trk_trkseg_trkpt_course
, "/gpx/trk/trkseg/trkpt/course" },
120 { tt_trk_trkseg_trkpt_speed
, "/gpx/trk/trkseg/trkpt/speed" },
121 { tt_trk_trkseg_trkpt_fix
, "/gpx/trk/trkseg/trkpt/fix" },
122 { tt_trk_trkseg_trkpt_sat
, "/gpx/trk/trkseg/trkpt/sat" },
124 { tt_trk_trkseg_trkpt_hdop
, "/gpx/trk/trkseg/trkpt/hdop" },
125 { tt_trk_trkseg_trkpt_vdop
, "/gpx/trk/trkseg/trkpt/vdop" },
126 { tt_trk_trkseg_trkpt_pdop
, "/gpx/trk/trkseg/trkpt/pdop" },
130 static tag_type
get_tag(const char *t
)
133 for (tm
= tag_path_map
; tm
->tag_type
!= 0; tm
++)
134 if (0 == strcmp(tm
->tag_name
, t
))
139 /******************************************/
141 tag_type current_tag
= tt_unknown
;
142 GString
*xpath
= NULL
;
143 GString
*c_cdata
= NULL
;
145 /* current ("c_") objects */
146 VikTrackpoint
*c_tp
= NULL
;
147 VikWaypoint
*c_wp
= NULL
;
148 VikTrack
*c_tr
= NULL
;
150 gchar
*c_wp_name
= NULL
;
151 gchar
*c_tr_name
= NULL
;
153 /* temporary things so we don't have to create them lots of times */
154 const gchar
*c_slat
, *c_slon
;
157 /* specialty flags / etc */
158 gboolean f_tr_newseg
;
159 guint unnamed_waypoints
= 0;
160 guint unnamed_tracks
= 0;
163 static const char *get_attr ( const char **attr
, const char *key
)
166 if ( strcmp(*attr
,key
) == 0 )
173 static gboolean
set_c_ll ( const char **attr
)
175 if ( (c_slat
= get_attr ( attr
, "lat" )) && (c_slon
= get_attr ( attr
, "lon" )) ) {
176 c_ll
.lat
= g_ascii_strtod(c_slat
, NULL
);
177 c_ll
.lon
= g_ascii_strtod(c_slon
, NULL
);
183 static void gpx_start(VikTrwLayer
*vtl
, const char *el
, const char **attr
)
185 static const gchar
*tmp
;
187 g_string_append_c ( xpath
, '/' );
188 g_string_append ( xpath
, el
);
189 current_tag
= get_tag ( xpath
->str
);
191 switch ( current_tag
) {
194 if ( set_c_ll( attr
) ) {
195 c_wp
= vik_waypoint_new ();
196 if ( ! get_attr ( attr
, "hidden" ) )
197 c_wp
->visible
= TRUE
;
199 vik_coord_load_from_latlon ( &(c_wp
->coord
), vik_trw_layer_get_coord_mode ( vtl
), &c_ll
);
204 c_tr
= vik_track_new ();
205 if ( ! get_attr ( attr
, "hidden" ) )
206 c_tr
->visible
= TRUE
;
213 case tt_trk_trkseg_trkpt
:
214 if ( set_c_ll( attr
) ) {
215 c_tp
= vik_trackpoint_new ();
216 vik_coord_load_from_latlon ( &(c_tp
->coord
), vik_trw_layer_get_coord_mode ( vtl
), &c_ll
);
218 c_tp
->newsegment
= TRUE
;
221 c_tr
->trackpoints
= g_list_append ( c_tr
->trackpoints
, c_tp
);
225 case tt_trk_trkseg_trkpt_ele
:
226 case tt_trk_trkseg_trkpt_time
:
233 g_string_erase ( c_cdata
, 0, -1 ); /* clear the cdata buffer */
237 c_wp
= vik_waypoint_new ();
238 c_wp
->visible
= TRUE
;
241 case tt_waypoint_coord
:
242 if ( set_c_ll( attr
) )
243 vik_coord_load_from_latlon ( &(c_wp
->coord
), vik_trw_layer_get_coord_mode ( vtl
), &c_ll
);
246 case tt_waypoint_name
:
247 if ( ( tmp
= get_attr(attr
, "id") ) ) {
249 g_free ( c_wp_name
);
250 c_wp_name
= g_strdup ( tmp
);
252 g_string_erase ( c_cdata
, 0, -1 ); /* clear the cdata buffer for description */
259 static void gpx_end(VikTrwLayer
*vtl
, const char *el
)
261 static GTimeVal tp_time
;
263 g_string_truncate ( xpath
, xpath
->len
- strlen(el
) - 1 );
265 switch ( current_tag
) {
270 c_wp_name
= g_strdup_printf("VIKING_WP%d", unnamed_waypoints
++);
271 vik_trw_layer_filein_add_waypoint ( vtl
, c_wp_name
, c_wp
);
272 g_free ( c_wp_name
);
279 c_tr_name
= g_strdup_printf("VIKING_TR%d", unnamed_waypoints
++);
280 vik_trw_layer_filein_add_track ( vtl
, c_tr_name
, c_tr
);
281 g_free ( c_tr_name
);
288 g_free ( c_wp_name
);
289 c_wp_name
= g_strdup ( c_cdata
->str
);
290 g_string_erase ( c_cdata
, 0, -1 );
295 g_free ( c_tr_name
);
296 c_tr_name
= g_strdup ( c_cdata
->str
);
297 g_string_erase ( c_cdata
, 0, -1 );
301 c_wp
->altitude
= g_ascii_strtod ( c_cdata
->str
, NULL
);
302 g_string_erase ( c_cdata
, 0, -1 );
305 case tt_trk_trkseg_trkpt_ele
:
306 c_tp
->altitude
= g_ascii_strtod ( c_cdata
->str
, NULL
);
307 g_string_erase ( c_cdata
, 0, -1 );
310 case tt_waypoint_name
: /* .loc name is really description. */
312 vik_waypoint_set_comment ( c_wp
, c_cdata
->str
);
313 g_string_erase ( c_cdata
, 0, -1 );
317 vik_waypoint_set_image ( c_wp
, c_cdata
->str
);
318 g_string_erase ( c_cdata
, 0, -1 );
322 gchar
*tmp_lower
= g_utf8_strdown(c_cdata
->str
, -1); /* for things like <type>Geocache</type> */
323 vik_waypoint_set_symbol ( c_wp
, tmp_lower
);
324 g_free ( tmp_lower
);
325 g_string_erase ( c_cdata
, 0, -1 );
330 vik_track_set_comment ( c_tr
, c_cdata
->str
);
331 g_string_erase ( c_cdata
, 0, -1 );
334 case tt_trk_trkseg_trkpt_time
:
335 if ( g_time_val_from_iso8601(c_cdata
->str
, &tp_time
) ) {
336 c_tp
->timestamp
= tp_time
.tv_sec
;
337 c_tp
->has_timestamp
= TRUE
;
339 g_string_erase ( c_cdata
, 0, -1 );
342 case tt_trk_trkseg_trkpt_course
:
343 c_tp
->course
= g_ascii_strtod ( c_cdata
->str
, NULL
);
344 g_string_erase ( c_cdata
, 0, -1 );
347 case tt_trk_trkseg_trkpt_speed
:
348 c_tp
->speed
= g_ascii_strtod ( c_cdata
->str
, NULL
);
349 g_string_erase ( c_cdata
, 0, -1 );
352 case tt_trk_trkseg_trkpt_fix
:
353 if (!strcmp("2d", c_cdata
->str
))
354 c_tp
->fix_mode
= VIK_GPS_MODE_2D
;
355 else if (!strcmp("3d", c_cdata
->str
))
356 c_tp
->fix_mode
= VIK_GPS_MODE_3D
;
357 else /* TODO: more fix modes here */
358 c_tp
->fix_mode
= VIK_GPS_MODE_NOT_SEEN
;
359 g_string_erase ( c_cdata
, 0, -1 );
362 case tt_trk_trkseg_trkpt_sat
:
363 c_tp
->nsats
= atoi ( c_cdata
->str
);
364 g_string_erase ( c_cdata
, 0, -1 );
367 case tt_trk_trkseg_trkpt_hdop
:
368 c_tp
->hdop
= g_strtod ( c_cdata
->str
, NULL
);
369 g_string_erase ( c_cdata
, 0, -1 );
372 case tt_trk_trkseg_trkpt_vdop
:
373 c_tp
->vdop
= g_strtod ( c_cdata
->str
, NULL
);
374 g_string_erase ( c_cdata
, 0, -1 );
377 case tt_trk_trkseg_trkpt_pdop
:
378 c_tp
->pdop
= g_strtod ( c_cdata
->str
, NULL
);
379 g_string_erase ( c_cdata
, 0, -1 );
385 current_tag
= get_tag ( xpath
->str
);
388 static void gpx_cdata(void *dta
, const XML_Char
*s
, int len
)
390 switch ( current_tag
) {
394 case tt_trk_trkseg_trkpt_ele
:
399 case tt_trk_trkseg_trkpt_time
:
400 case tt_trk_trkseg_trkpt_course
:
401 case tt_trk_trkseg_trkpt_speed
:
402 case tt_trk_trkseg_trkpt_fix
:
403 case tt_trk_trkseg_trkpt_sat
:
404 case tt_trk_trkseg_trkpt_hdop
:
405 case tt_trk_trkseg_trkpt_vdop
:
406 case tt_trk_trkseg_trkpt_pdop
:
407 case tt_waypoint_name
: /* .loc name is really description. */
408 g_string_append_len ( c_cdata
, s
, len
);
411 default: break; /* ignore cdata from other things */
415 // make like a "stack" of tag names
416 // like gpspoint's separated like /gpx/wpt/whatever
418 void a_gpx_read_file( VikTrwLayer
*vtl
, FILE *f
) {
419 XML_Parser parser
= XML_ParserCreate(NULL
);
422 XML_SetElementHandler(parser
, (XML_StartElementHandler
) gpx_start
, (XML_EndElementHandler
) gpx_end
);
423 XML_SetUserData(parser
, vtl
); /* in the future we could remove all global variables */
424 XML_SetCharacterDataHandler(parser
, (XML_CharacterDataHandler
) gpx_cdata
);
428 g_assert ( f
!= NULL
&& vtl
!= NULL
);
430 xpath
= g_string_new ( "" );
431 c_cdata
= g_string_new ( "" );
433 unnamed_waypoints
= 0;
437 len
= fread(buf
, 1, sizeof(buf
)-7, f
);
438 done
= feof(f
) || !len
;
439 XML_Parse(parser
, buf
, len
, done
);
442 XML_ParserFree (parser
);
443 g_string_free ( xpath
, TRUE
);
444 g_string_free ( c_cdata
, TRUE
);
447 /**** entitize from GPSBabel ****/
455 entity_types stdentities
[] = {
457 { "'", "'", 1 },
460 { "\"", """, 0 },
464 void utf8_to_int( const char *cp
, int *bytes
, int *value
)
466 if ( (*cp
& 0xe0) == 0xc0 ) {
467 if ( (*(cp
+1) & 0xc0) != 0x80 ) goto dodefault
;
469 *value
= ((*cp
& 0x1f) << 6) |
472 else if ( (*cp
& 0xf0) == 0xe0 ) {
473 if ( (*(cp
+1) & 0xc0) != 0x80 ) goto dodefault
;
474 if ( (*(cp
+2) & 0xc0) != 0x80 ) goto dodefault
;
476 *value
= ((*cp
& 0x0f) << 12) |
477 ((*(cp
+1) & 0x3f) << 6) |
480 else if ( (*cp
& 0xf8) == 0xf0 ) {
481 if ( (*(cp
+1) & 0xc0) != 0x80 ) goto dodefault
;
482 if ( (*(cp
+2) & 0xc0) != 0x80 ) goto dodefault
;
483 if ( (*(cp
+3) & 0xc0) != 0x80 ) goto dodefault
;
485 *value
= ((*cp
& 0x07) << 18) |
486 ((*(cp
+1) & 0x3f) << 12) |
487 ((*(cp
+2) & 0x3f) << 6) |
490 else if ( (*cp
& 0xfc) == 0xf8 ) {
491 if ( (*(cp
+1) & 0xc0) != 0x80 ) goto dodefault
;
492 if ( (*(cp
+2) & 0xc0) != 0x80 ) goto dodefault
;
493 if ( (*(cp
+3) & 0xc0) != 0x80 ) goto dodefault
;
494 if ( (*(cp
+4) & 0xc0) != 0x80 ) goto dodefault
;
496 *value
= ((*cp
& 0x03) << 24) |
497 ((*(cp
+1) & 0x3f) << 18) |
498 ((*(cp
+2) & 0x3f) << 12) |
499 ((*(cp
+3) & 0x3f) << 6) |
502 else if ( (*cp
& 0xfe) == 0xfc ) {
503 if ( (*(cp
+1) & 0xc0) != 0x80 ) goto dodefault
;
504 if ( (*(cp
+2) & 0xc0) != 0x80 ) goto dodefault
;
505 if ( (*(cp
+3) & 0xc0) != 0x80 ) goto dodefault
;
506 if ( (*(cp
+4) & 0xc0) != 0x80 ) goto dodefault
;
507 if ( (*(cp
+5) & 0xc0) != 0x80 ) goto dodefault
;
509 *value
= ((*cp
& 0x01) << 30) |
510 ((*(cp
+1) & 0x3f) << 24) |
511 ((*(cp
+2) & 0x3f) << 18) |
512 ((*(cp
+3) & 0x3f) << 12) |
513 ((*(cp
+4) & 0x3f) << 6) |
519 *value
= (unsigned char)*cp
;
525 entitize(const char * str
)
527 int elen
, ecount
, nsecount
;
530 char * p
, * tmp
, * xstr
;
536 elen
= ecount
= nsecount
= 0;
538 /* figure # of entity replacements and additional size. */
541 while ((cp
= strstr(cp
, ep
->text
)) != NULL
) {
542 elen
+= strlen(ep
->entity
) - strlen(ep
->text
);
544 cp
+= strlen(ep
->text
);
549 /* figure the same for other than standard entities (i.e. anything
550 * that isn't in the range U+0000 to U+007F */
551 for ( cp
= str
; *cp
; cp
++ ) {
554 utf8_to_int( cp
, &bytes
, &value
);
556 elen
+= sprintf( tmpsub
, "&#x%x;", value
) - bytes
;
561 /* enough space for the whole string plus entity replacements, if any */
562 tmp
= g_malloc((strlen(str
) + elen
+ 1));
565 /* no entity replacements */
566 if (ecount
== 0 && nsecount
== 0)
570 for (ep
= stdentities
; ep
->text
; ep
++) {
572 while ((p
= strstr(p
, ep
->text
)) != NULL
) {
573 elen
= strlen(ep
->entity
);
575 xstr
= g_strdup(p
+ strlen(ep
->text
));
577 strcpy(p
, ep
->entity
);
578 strcpy(p
+ elen
, xstr
);
587 if ( nsecount
!= 0 ) {
591 utf8_to_int( p
, &bytes
, &value
);
593 xstr
= g_strdup( p
+ bytes
);
598 sprintf( p
, "&#x%x;", value
);
612 /**** end GPSBabel code ****/
616 static void gpx_write_waypoint ( const gchar
*name
, VikWaypoint
*wp
, GpxWritingContext
*context
)
618 FILE *f
= context
->file
;
619 static struct LatLon ll
;
622 vik_coord_to_latlon ( &(wp
->coord
), &ll
);
623 s_lat
= a_coords_dtostr( ll
.lat
);
624 s_lon
= a_coords_dtostr( ll
.lon
);
625 fprintf ( f
, "<wpt lat=\"%s\" lon=\"%s\"%s>\n",
626 s_lat
, s_lon
, wp
->visible
? "" : " hidden=\"hidden\"" );
630 tmp
= entitize ( name
);
631 fprintf ( f
, " <name>%s</name>\n", tmp
);
634 if ( wp
->altitude
!= VIK_DEFAULT_ALTITUDE
)
636 tmp
= a_coords_dtostr ( wp
->altitude
);
637 fprintf ( f
, " <ele>%s</ele>\n", tmp
);
642 tmp
= entitize(wp
->comment
);
643 fprintf ( f
, " <desc>%s</desc>\n", tmp
);
648 tmp
= entitize(wp
->image
);
649 fprintf ( f
, " <link>%s</link>\n", tmp
);
654 tmp
= entitize(wp
->symbol
);
655 fprintf ( f
, " <sym>%s</sym>\n", tmp
);
659 fprintf ( f
, "</wpt>\n" );
662 static void gpx_write_trackpoint ( VikTrackpoint
*tp
, GpxWritingContext
*context
)
664 FILE *f
= context
->file
;
665 static struct LatLon ll
;
666 gchar
*s_lat
,*s_lon
, *s_alt
, *s_dop
;
668 vik_coord_to_latlon ( &(tp
->coord
), &ll
);
670 if ( tp
->newsegment
)
671 fprintf ( f
, " </trkseg>\n <trkseg>\n" );
673 s_lat
= a_coords_dtostr( ll
.lat
);
674 s_lon
= a_coords_dtostr( ll
.lon
);
675 fprintf ( f
, " <trkpt lat=\"%s\" lon=\"%s\">\n", s_lat
, s_lon
);
676 g_free ( s_lat
); s_lat
= NULL
;
677 g_free ( s_lon
); s_lon
= NULL
;
680 if ( tp
->altitude
!= VIK_DEFAULT_ALTITUDE
)
682 s_alt
= a_coords_dtostr ( tp
->altitude
);
684 else if ( context
->options
!= NULL
&& context
->options
->force_ele
)
686 s_alt
= a_coords_dtostr ( 0 );
689 fprintf ( f
, " <ele>%s</ele>\n", s_alt
);
690 g_free ( s_alt
); s_alt
= NULL
;
693 if ( tp
->has_timestamp
) {
695 timestamp
.tv_sec
= tp
->timestamp
;
696 timestamp
.tv_usec
= 0;
698 time_iso8601
= g_time_val_to_iso8601 ( ×tamp
);
700 else if ( context
->options
!= NULL
&& context
->options
->force_time
)
703 g_get_current_time ( ¤t
);
705 time_iso8601
= g_time_val_to_iso8601 ( ¤t
);
707 if ( time_iso8601
!= NULL
)
708 fprintf ( f
, " <time>%s</time>\n", time_iso8601
);
709 g_free(time_iso8601
);
712 if (!isnan(tp
->course
)) {
713 gchar
*s_course
= a_coords_dtostr(tp
->course
);
714 fprintf ( f
, " <course>%s</course>\n", s_course
);
717 if (!isnan(tp
->speed
)) {
718 gchar
*s_speed
= a_coords_dtostr(tp
->speed
);
719 fprintf ( f
, " <speed>%s</speed>\n", s_speed
);
722 if (tp
->fix_mode
== VIK_GPS_MODE_2D
)
723 fprintf ( f
, " <fix>2d</fix>\n");
724 if (tp
->fix_mode
== VIK_GPS_MODE_3D
)
725 fprintf ( f
, " <fix>3d</fix>\n");
727 fprintf ( f
, " <sat>%d</sat>\n", tp
->nsats
);
730 if ( tp
->hdop
!= VIK_DEFAULT_DOP
)
732 s_dop
= a_coords_dtostr ( tp
->hdop
);
735 fprintf ( f
, " <hdop>%s</hdop>\n", s_dop
);
736 g_free ( s_dop
); s_dop
= NULL
;
738 if ( tp
->vdop
!= VIK_DEFAULT_DOP
)
740 s_dop
= a_coords_dtostr ( tp
->vdop
);
743 fprintf ( f
, " <vdop>%s</vdop>\n", s_dop
);
744 g_free ( s_dop
); s_dop
= NULL
;
746 if ( tp
->pdop
!= VIK_DEFAULT_DOP
)
748 s_dop
= a_coords_dtostr ( tp
->pdop
);
751 fprintf ( f
, " <pdop>%s</pdop>\n", s_dop
);
752 g_free ( s_dop
); s_dop
= NULL
;
755 fprintf ( f
, " </trkpt>\n" );
759 static void gpx_write_track ( const gchar
*name
, VikTrack
*t
, GpxWritingContext
*context
)
761 FILE *f
= context
->file
;
763 gboolean first_tp_is_newsegment
= FALSE
; /* must temporarily make it not so, but we want to restore state. not that it matters. */
765 tmp
= entitize ( name
);
766 fprintf ( f
, "<trk%s>\n <name>%s</name>\n", t
->visible
? "" : " hidden=\"hidden\"", tmp
);
771 tmp
= entitize ( t
->comment
);
772 fprintf ( f
, " <desc>%s</desc>\n", tmp
);
776 fprintf ( f
, " <trkseg>\n" );
778 if ( t
->trackpoints
&& t
->trackpoints
->data
) {
779 first_tp_is_newsegment
= VIK_TRACKPOINT(t
->trackpoints
->data
)->newsegment
;
780 VIK_TRACKPOINT(t
->trackpoints
->data
)->newsegment
= FALSE
; /* so we won't write </trkseg><trkseg> already */
781 g_list_foreach ( t
->trackpoints
, (GFunc
) gpx_write_trackpoint
, context
);
782 VIK_TRACKPOINT(t
->trackpoints
->data
)->newsegment
= first_tp_is_newsegment
; /* restore state */
785 fprintf ( f
, "</trkseg>\n</trk>\n" );
788 static void gpx_write_header( FILE *f
)
790 fprintf(f
, "<?xml version=\"1.0\"?>\n"
791 "<gpx version=\"1.0\" creator=\"Viking -- http://viking.sf.net/\"\n"
792 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
793 "xmlns=\"http://www.topografix.com/GPX/1/0\"\n"
794 "xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\n");
797 static void gpx_write_footer( FILE *f
)
799 fprintf(f
, "</gpx>\n");
807 } gpx_waypoint_and_name
;
810 gpx_waypoint_and_name
*wps
;
813 } gpx_gather_waypoints_passalong_t
;
815 /* Type to hold name of track and timestamp of first trackpoint */
817 time_t first_timestamp
;
819 } gpx_track_and_timestamp
;
822 gpx_track_and_timestamp
*trks
;
825 } gpx_gather_tracks_passalong_t
;
827 static void gpx_collect_waypoint ( const gchar
*name
, VikWaypoint
*wp
, gpx_gather_waypoints_passalong_t
*passalong
)
829 if ( passalong
->i
< passalong
->n_wps
) {
830 passalong
->wps
[passalong
->i
].name
= name
;
831 passalong
->wps
[passalong
->i
].wp
= wp
;
836 /* Function to collect a track and the first timestamp in the list */
837 static void gpx_collect_track (const gchar
*name
, VikTrack
*track
, gpx_gather_tracks_passalong_t
*passalong
)
839 if (passalong
->i
< passalong
->n_trks
)
841 passalong
->trks
[passalong
->i
].name
= name
;
842 if (track
&& track
->trackpoints
&& track
->trackpoints
->data
)
844 VikTrackpoint
*first_point
= (VikTrackpoint
*)track
->trackpoints
->data
;
845 passalong
->trks
[passalong
->i
].first_timestamp
= first_point
->timestamp
;
849 passalong
->trks
[passalong
->i
].first_timestamp
= 0;
855 static int gpx_waypoint_and_name_compar(const void *x
, const void *y
)
857 gpx_waypoint_and_name
*a
= (gpx_waypoint_and_name
*)x
;
858 gpx_waypoint_and_name
*b
= (gpx_waypoint_and_name
*)y
;
859 return strcmp(a
->name
,b
->name
);
862 /* Function to compare two tracks by their first timestamp */
863 static int gpx_track_and_timestamp_compar(const void *x
, const void *y
)
865 gpx_track_and_timestamp
*a
= (gpx_track_and_timestamp
*)x
;
866 gpx_track_and_timestamp
*b
= (gpx_track_and_timestamp
*)y
;
867 if (a
->first_timestamp
< b
->first_timestamp
)
871 if (a
->first_timestamp
> b
->first_timestamp
)
878 void a_gpx_write_file( VikTrwLayer
*vtl
, FILE *f
)
880 a_gpx_write_file_options(NULL
, vtl
, f
);
883 void a_gpx_write_file_options ( GpxWritingOptions
*options
, VikTrwLayer
*vtl
, FILE *f
)
885 GpxWritingContext context
= { options
, f
};
888 gpx_write_header ( f
);
891 gpx_gather_waypoints_passalong_t passalong
;
892 passalong
.n_wps
= g_hash_table_size ( vik_trw_layer_get_waypoints ( vtl
) );
894 passalong
.wps
= g_new(gpx_waypoint_and_name
,passalong
.n_wps
);
895 g_hash_table_foreach ( vik_trw_layer_get_waypoints ( vtl
), (GHFunc
) gpx_collect_waypoint
, &passalong
);
896 /* gather waypoints in a list, then sort */
897 qsort(passalong
.wps
, passalong
.n_wps
, sizeof(gpx_waypoint_and_name
), gpx_waypoint_and_name_compar
);
898 for ( i
= 0; i
< passalong
.n_wps
; i
++ )
899 gpx_write_waypoint ( passalong
.wps
[i
].name
, passalong
.wps
[i
].wp
, &context
);
900 g_free ( passalong
.wps
);
902 gpx_gather_tracks_passalong_t passalong_tracks
;
903 passalong_tracks
.n_trks
= g_hash_table_size ( vik_trw_layer_get_tracks (vtl
) );
904 passalong_tracks
.i
= 0;
905 passalong_tracks
.trks
= g_new(gpx_track_and_timestamp
,passalong_tracks
.n_trks
);
906 g_hash_table_foreach (vik_trw_layer_get_tracks(vtl
), (GHFunc
) gpx_collect_track
, &passalong_tracks
);
907 /* Sort by timestamp */
908 qsort(passalong_tracks
.trks
, passalong_tracks
.n_trks
, sizeof(gpx_track_and_timestamp
), gpx_track_and_timestamp_compar
);
909 for (i
=0;i
<passalong_tracks
.n_trks
; i
++)
911 gpx_write_track(passalong_tracks
.trks
[i
].name
, (VikTrack
*)g_hash_table_lookup(vik_trw_layer_get_tracks(vtl
), passalong_tracks
.trks
[i
].name
), &context
);
913 g_free ( passalong_tracks
.trks
);
914 gpx_write_footer ( f
);
917 void a_gpx_write_track_file ( const gchar
*name
, VikTrack
*t
, FILE *f
)
919 a_gpx_write_track_file_options ( NULL
, name
, t
, f
);
922 void a_gpx_write_track_file_options ( GpxWritingOptions
*options
, const gchar
*name
, VikTrack
*t
, FILE *f
)
924 GpxWritingContext context
= {options
, f
};
925 gpx_write_header ( f
);
926 gpx_write_track ( name
, t
, &context
);
927 gpx_write_footer ( f
);