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
,
74 typedef struct tag_mapping
{
75 tag_type tag_type
; /* enum from above for this tag */
76 const char *tag_name
; /* xpath-ish tag name */
80 GpxWritingOptions
*options
;
85 * xpath(ish) mappings between full tag paths and internal identifers.
86 * These appear in the order they appear in the GPX specification.
87 * If it's not a tag we explictly handle, it doesn't go here.
90 tag_mapping tag_path_map
[] = {
92 { tt_wpt
, "/gpx/wpt" },
94 { tt_waypoint
, "/loc/waypoint" },
95 { tt_waypoint_coord
, "/loc/waypoint/coord" },
96 { tt_waypoint_name
, "/loc/waypoint/name" },
98 { tt_wpt_ele
, "/gpx/wpt/ele" },
99 { tt_wpt_name
, "/gpx/wpt/name" },
100 { tt_wpt_desc
, "/gpx/wpt/desc" },
101 { tt_wpt_sym
, "/gpx/wpt/sym" },
102 { tt_wpt_sym
, "/loc/waypoint/type" },
103 { tt_wpt_link
, "/gpx/wpt/link" }, /* GPX 1.1 */
105 { tt_trk
, "/gpx/trk" },
106 { tt_trk
, "/gpx/rte" },
107 { tt_trk_name
, "/gpx/trk/name" },
108 { tt_trk_desc
, "/gpx/trk/desc" },
109 { tt_trk_trkseg
, "/gpx/trk/trkseg" },
110 { tt_trk_trkseg_trkpt
, "/gpx/trk/trkseg/trkpt" },
111 { tt_trk_trkseg_trkpt
, "/gpx/rte/rtept" },
112 { tt_trk_trkseg_trkpt_ele
, "/gpx/trk/trkseg/trkpt/ele" },
113 { tt_trk_trkseg_trkpt_time
, "/gpx/trk/trkseg/trkpt/time" },
115 { tt_trk_trkseg_trkpt_course
, "/gpx/trk/trkseg/trkpt/course" },
116 { tt_trk_trkseg_trkpt_speed
, "/gpx/trk/trkseg/trkpt/speed" },
117 { tt_trk_trkseg_trkpt_fix
, "/gpx/trk/trkseg/trkpt/fix" },
118 { tt_trk_trkseg_trkpt_sat
, "/gpx/trk/trkseg/trkpt/sat" },
123 static tag_type
get_tag(const char *t
)
126 for (tm
= tag_path_map
; tm
->tag_type
!= 0; tm
++)
127 if (0 == strcmp(tm
->tag_name
, t
))
132 /******************************************/
134 tag_type current_tag
= tt_unknown
;
135 GString
*xpath
= NULL
;
136 GString
*c_cdata
= NULL
;
138 /* current ("c_") objects */
139 VikTrackpoint
*c_tp
= NULL
;
140 VikWaypoint
*c_wp
= NULL
;
141 VikTrack
*c_tr
= NULL
;
143 gchar
*c_wp_name
= NULL
;
144 gchar
*c_tr_name
= NULL
;
146 /* temporary things so we don't have to create them lots of times */
147 const gchar
*c_slat
, *c_slon
;
150 /* specialty flags / etc */
151 gboolean f_tr_newseg
;
152 guint unnamed_waypoints
= 0;
153 guint unnamed_tracks
= 0;
156 static const char *get_attr ( const char **attr
, const char *key
)
159 if ( strcmp(*attr
,key
) == 0 )
166 static gboolean
set_c_ll ( const char **attr
)
168 if ( (c_slat
= get_attr ( attr
, "lat" )) && (c_slon
= get_attr ( attr
, "lon" )) ) {
169 c_ll
.lat
= g_strtod(c_slat
, NULL
);
170 c_ll
.lon
= g_strtod(c_slon
, NULL
);
176 static void gpx_start(VikTrwLayer
*vtl
, const char *el
, const char **attr
)
178 static const gchar
*tmp
;
180 g_string_append_c ( xpath
, '/' );
181 g_string_append ( xpath
, el
);
182 current_tag
= get_tag ( xpath
->str
);
184 switch ( current_tag
) {
187 if ( set_c_ll( attr
) ) {
188 c_wp
= vik_waypoint_new ();
189 c_wp
->altitude
= VIK_DEFAULT_ALTITUDE
;
190 if ( ! get_attr ( attr
, "hidden" ) )
191 c_wp
->visible
= TRUE
;
193 vik_coord_load_from_latlon ( &(c_wp
->coord
), vik_trw_layer_get_coord_mode ( vtl
), &c_ll
);
198 c_tr
= vik_track_new ();
199 if ( ! get_attr ( attr
, "hidden" ) )
200 c_tr
->visible
= TRUE
;
207 case tt_trk_trkseg_trkpt
:
208 if ( set_c_ll( attr
) ) {
209 c_tp
= vik_trackpoint_new ();
210 c_tp
->altitude
= VIK_DEFAULT_ALTITUDE
;
211 vik_coord_load_from_latlon ( &(c_tp
->coord
), vik_trw_layer_get_coord_mode ( vtl
), &c_ll
);
213 c_tp
->newsegment
= TRUE
;
216 c_tr
->trackpoints
= g_list_append ( c_tr
->trackpoints
, c_tp
);
220 case tt_trk_trkseg_trkpt_ele
:
221 case tt_trk_trkseg_trkpt_time
:
228 g_string_erase ( c_cdata
, 0, -1 ); /* clear the cdata buffer */
232 c_wp
= vik_waypoint_new ();
233 c_wp
->altitude
= VIK_DEFAULT_ALTITUDE
;
234 c_wp
->visible
= TRUE
;
237 case tt_waypoint_coord
:
238 if ( set_c_ll( attr
) )
239 vik_coord_load_from_latlon ( &(c_wp
->coord
), vik_trw_layer_get_coord_mode ( vtl
), &c_ll
);
242 case tt_waypoint_name
:
243 if ( ( tmp
= get_attr(attr
, "id") ) ) {
245 g_free ( c_wp_name
);
246 c_wp_name
= g_strdup ( tmp
);
248 g_string_erase ( c_cdata
, 0, -1 ); /* clear the cdata buffer for description */
255 static void gpx_end(VikTrwLayer
*vtl
, const char *el
)
257 static GTimeVal tp_time
;
259 g_string_truncate ( xpath
, xpath
->len
- strlen(el
) - 1 );
261 switch ( current_tag
) {
266 c_wp_name
= g_strdup_printf("VIKING_WP%d", unnamed_waypoints
++);
267 vik_trw_layer_filein_add_waypoint ( vtl
, c_wp_name
, c_wp
);
268 g_free ( c_wp_name
);
275 c_tr_name
= g_strdup_printf("VIKING_TR%d", unnamed_waypoints
++);
276 vik_trw_layer_filein_add_track ( vtl
, c_tr_name
, c_tr
);
277 g_free ( c_tr_name
);
284 g_free ( c_wp_name
);
285 c_wp_name
= g_strdup ( c_cdata
->str
);
286 g_string_erase ( c_cdata
, 0, -1 );
291 g_free ( c_tr_name
);
292 c_tr_name
= g_strdup ( c_cdata
->str
);
293 g_string_erase ( c_cdata
, 0, -1 );
297 c_wp
->altitude
= g_strtod ( c_cdata
->str
, NULL
);
298 g_string_erase ( c_cdata
, 0, -1 );
301 case tt_trk_trkseg_trkpt_ele
:
302 c_tp
->altitude
= g_strtod ( c_cdata
->str
, NULL
);
303 g_string_erase ( c_cdata
, 0, -1 );
306 case tt_waypoint_name
: /* .loc name is really description. */
308 vik_waypoint_set_comment ( c_wp
, c_cdata
->str
);
309 g_string_erase ( c_cdata
, 0, -1 );
313 vik_waypoint_set_image ( c_wp
, c_cdata
->str
);
314 g_string_erase ( c_cdata
, 0, -1 );
318 gchar
*tmp_lower
= g_utf8_strdown(c_cdata
->str
, -1); /* for things like <type>Geocache</type> */
319 vik_waypoint_set_symbol ( c_wp
, tmp_lower
);
320 g_free ( tmp_lower
);
321 g_string_erase ( c_cdata
, 0, -1 );
326 vik_track_set_comment ( c_tr
, c_cdata
->str
);
327 g_string_erase ( c_cdata
, 0, -1 );
330 case tt_trk_trkseg_trkpt_time
:
331 if ( g_time_val_from_iso8601(c_cdata
->str
, &tp_time
) ) {
332 c_tp
->timestamp
= tp_time
.tv_sec
;
333 c_tp
->has_timestamp
= TRUE
;
335 g_string_erase ( c_cdata
, 0, -1 );
338 case tt_trk_trkseg_trkpt_course
:
339 c_tp
->extended
= TRUE
;
340 c_tp
->course
= g_strtod ( c_cdata
->str
, NULL
);
341 g_string_erase ( c_cdata
, 0, -1 );
344 case tt_trk_trkseg_trkpt_speed
:
345 c_tp
->extended
= TRUE
;
346 c_tp
->speed
= g_strtod ( c_cdata
->str
, NULL
);
347 g_string_erase ( c_cdata
, 0, -1 );
350 case tt_trk_trkseg_trkpt_fix
:
351 c_tp
->extended
= TRUE
;
352 if (!strcmp("2d", c_cdata
->str
))
353 c_tp
->fix_mode
= VIK_GPS_MODE_2D
;
354 else if (!strcmp("3d", c_cdata
->str
))
355 c_tp
->fix_mode
= VIK_GPS_MODE_3D
;
356 else /* TODO: more fix modes here */
357 c_tp
->fix_mode
= VIK_GPS_MODE_NOT_SEEN
;
358 g_string_erase ( c_cdata
, 0, -1 );
361 case tt_trk_trkseg_trkpt_sat
:
362 c_tp
->extended
= TRUE
;
363 c_tp
->nsats
= atoi ( c_cdata
->str
);
364 g_string_erase ( c_cdata
, 0, -1 );
370 current_tag
= get_tag ( xpath
->str
);
373 static void gpx_cdata(void *dta
, const XML_Char
*s
, int len
)
375 switch ( current_tag
) {
379 case tt_trk_trkseg_trkpt_ele
:
384 case tt_trk_trkseg_trkpt_time
:
385 case tt_trk_trkseg_trkpt_course
:
386 case tt_trk_trkseg_trkpt_speed
:
387 case tt_trk_trkseg_trkpt_fix
:
388 case tt_trk_trkseg_trkpt_sat
:
389 case tt_waypoint_name
: /* .loc name is really description. */
390 g_string_append_len ( c_cdata
, s
, len
);
393 default: break; /* ignore cdata from other things */
397 // make like a "stack" of tag names
398 // like gpspoint's separated like /gpx/wpt/whatever
400 void a_gpx_read_file( VikTrwLayer
*vtl
, FILE *f
) {
401 XML_Parser parser
= XML_ParserCreate(NULL
);
404 XML_SetElementHandler(parser
, (XML_StartElementHandler
) gpx_start
, (XML_EndElementHandler
) gpx_end
);
405 XML_SetUserData(parser
, vtl
); /* in the future we could remove all global variables */
406 XML_SetCharacterDataHandler(parser
, (XML_CharacterDataHandler
) gpx_cdata
);
410 g_assert ( f
!= NULL
&& vtl
!= NULL
);
412 xpath
= g_string_new ( "" );
413 c_cdata
= g_string_new ( "" );
415 unnamed_waypoints
= 0;
419 len
= fread(buf
, 1, sizeof(buf
)-7, f
);
420 done
= feof(f
) || !len
;
421 XML_Parse(parser
, buf
, len
, done
);
424 XML_ParserFree (parser
);
425 g_string_free ( xpath
, TRUE
);
426 g_string_free ( c_cdata
, TRUE
);
429 /**** entitize from GPSBabel ****/
437 entity_types stdentities
[] = {
439 { "'", "'", 1 },
442 { "\"", """, 0 },
446 void utf8_to_int( const char *cp
, int *bytes
, int *value
)
448 if ( (*cp
& 0xe0) == 0xc0 ) {
449 if ( (*(cp
+1) & 0xc0) != 0x80 ) goto dodefault
;
451 *value
= ((*cp
& 0x1f) << 6) |
454 else if ( (*cp
& 0xf0) == 0xe0 ) {
455 if ( (*(cp
+1) & 0xc0) != 0x80 ) goto dodefault
;
456 if ( (*(cp
+2) & 0xc0) != 0x80 ) goto dodefault
;
458 *value
= ((*cp
& 0x0f) << 12) |
459 ((*(cp
+1) & 0x3f) << 6) |
462 else if ( (*cp
& 0xf8) == 0xf0 ) {
463 if ( (*(cp
+1) & 0xc0) != 0x80 ) goto dodefault
;
464 if ( (*(cp
+2) & 0xc0) != 0x80 ) goto dodefault
;
465 if ( (*(cp
+3) & 0xc0) != 0x80 ) goto dodefault
;
467 *value
= ((*cp
& 0x07) << 18) |
468 ((*(cp
+1) & 0x3f) << 12) |
469 ((*(cp
+2) & 0x3f) << 6) |
472 else if ( (*cp
& 0xfc) == 0xf8 ) {
473 if ( (*(cp
+1) & 0xc0) != 0x80 ) goto dodefault
;
474 if ( (*(cp
+2) & 0xc0) != 0x80 ) goto dodefault
;
475 if ( (*(cp
+3) & 0xc0) != 0x80 ) goto dodefault
;
476 if ( (*(cp
+4) & 0xc0) != 0x80 ) goto dodefault
;
478 *value
= ((*cp
& 0x03) << 24) |
479 ((*(cp
+1) & 0x3f) << 18) |
480 ((*(cp
+2) & 0x3f) << 12) |
481 ((*(cp
+3) & 0x3f) << 6) |
484 else if ( (*cp
& 0xfe) == 0xfc ) {
485 if ( (*(cp
+1) & 0xc0) != 0x80 ) goto dodefault
;
486 if ( (*(cp
+2) & 0xc0) != 0x80 ) goto dodefault
;
487 if ( (*(cp
+3) & 0xc0) != 0x80 ) goto dodefault
;
488 if ( (*(cp
+4) & 0xc0) != 0x80 ) goto dodefault
;
489 if ( (*(cp
+5) & 0xc0) != 0x80 ) goto dodefault
;
491 *value
= ((*cp
& 0x01) << 30) |
492 ((*(cp
+1) & 0x3f) << 24) |
493 ((*(cp
+2) & 0x3f) << 18) |
494 ((*(cp
+3) & 0x3f) << 12) |
495 ((*(cp
+4) & 0x3f) << 6) |
501 *value
= (unsigned char)*cp
;
507 entitize(const char * str
)
509 int elen
, ecount
, nsecount
;
512 char * p
, * tmp
, * xstr
;
518 elen
= ecount
= nsecount
= 0;
520 /* figure # of entity replacements and additional size. */
523 while ((cp
= strstr(cp
, ep
->text
)) != NULL
) {
524 elen
+= strlen(ep
->entity
) - strlen(ep
->text
);
526 cp
+= strlen(ep
->text
);
531 /* figure the same for other than standard entities (i.e. anything
532 * that isn't in the range U+0000 to U+007F */
533 for ( cp
= str
; *cp
; cp
++ ) {
536 utf8_to_int( cp
, &bytes
, &value
);
538 elen
+= sprintf( tmpsub
, "&#x%x;", value
) - bytes
;
543 /* enough space for the whole string plus entity replacements, if any */
544 tmp
= g_malloc((strlen(str
) + elen
+ 1));
547 /* no entity replacements */
548 if (ecount
== 0 && nsecount
== 0)
552 for (ep
= stdentities
; ep
->text
; ep
++) {
554 while ((p
= strstr(p
, ep
->text
)) != NULL
) {
555 elen
= strlen(ep
->entity
);
557 xstr
= g_strdup(p
+ strlen(ep
->text
));
559 strcpy(p
, ep
->entity
);
560 strcpy(p
+ elen
, xstr
);
569 if ( nsecount
!= 0 ) {
573 utf8_to_int( p
, &bytes
, &value
);
575 xstr
= g_strdup( p
+ bytes
);
580 sprintf( p
, "&#x%x;", value
);
594 /**** end GPSBabel code ****/
598 static void gpx_write_waypoint ( const gchar
*name
, VikWaypoint
*wp
, GpxWritingContext
*context
)
600 FILE *f
= context
->file
;
601 static struct LatLon ll
;
604 vik_coord_to_latlon ( &(wp
->coord
), &ll
);
605 s_lat
= a_coords_dtostr( ll
.lat
);
606 s_lon
= a_coords_dtostr( ll
.lon
);
607 fprintf ( f
, "<wpt lat=\"%s\" lon=\"%s\"%s>\n",
608 s_lat
, s_lon
, wp
->visible
? "" : " hidden=\"hidden\"" );
612 tmp
= entitize ( name
);
613 fprintf ( f
, " <name>%s</name>\n", tmp
);
616 if ( wp
->altitude
!= VIK_DEFAULT_ALTITUDE
)
618 tmp
= a_coords_dtostr ( wp
->altitude
);
619 fprintf ( f
, " <ele>%s</ele>\n", tmp
);
624 tmp
= entitize(wp
->comment
);
625 fprintf ( f
, " <desc>%s</desc>\n", tmp
);
630 tmp
= entitize(wp
->image
);
631 fprintf ( f
, " <link>%s</link>\n", tmp
);
636 tmp
= entitize(wp
->symbol
);
637 fprintf ( f
, " <sym>%s</sym>\n", tmp
);
641 fprintf ( f
, "</wpt>\n" );
644 static void gpx_write_trackpoint ( VikTrackpoint
*tp
, GpxWritingContext
*context
)
646 FILE *f
= context
->file
;
647 static struct LatLon ll
;
648 gchar
*s_lat
,*s_lon
, *s_alt
;
650 vik_coord_to_latlon ( &(tp
->coord
), &ll
);
652 if ( tp
->newsegment
)
653 fprintf ( f
, " </trkseg>\n <trkseg>\n" );
655 s_lat
= a_coords_dtostr( ll
.lat
);
656 s_lon
= a_coords_dtostr( ll
.lon
);
657 fprintf ( f
, " <trkpt lat=\"%s\" lon=\"%s\">\n", s_lat
, s_lon
);
658 g_free ( s_lat
); s_lat
= NULL
;
659 g_free ( s_lon
); s_lon
= NULL
;
662 if ( tp
->altitude
!= VIK_DEFAULT_ALTITUDE
)
664 s_alt
= a_coords_dtostr ( tp
->altitude
);
666 else if ( context
->options
!= NULL
&& context
->options
->force_ele
)
668 s_alt
= a_coords_dtostr ( 0 );
671 fprintf ( f
, " <ele>%s</ele>\n", s_alt
);
672 g_free ( s_alt
); s_alt
= NULL
;
675 if ( tp
->has_timestamp
) {
677 timestamp
.tv_sec
= tp
->timestamp
;
678 timestamp
.tv_usec
= 0;
680 time_iso8601
= g_time_val_to_iso8601 ( ×tamp
);
682 else if ( context
->options
!= NULL
&& context
->options
->force_time
)
685 g_get_current_time ( ¤t
);
687 time_iso8601
= g_time_val_to_iso8601 ( ¤t
);
689 if ( time_iso8601
!= NULL
)
690 fprintf ( f
, " <time>%s</time>\n", time_iso8601
);
691 g_free(time_iso8601
);
694 if (tp
->extended
&& (tp
->fix_mode
>= VIK_GPS_MODE_2D
)) {
695 if (!isnan(tp
->course
)) {
696 gchar
*s_course
= a_coords_dtostr(tp
->course
);
697 fprintf ( f
, " <course>%s</course>\n", s_course
);
700 if (!isnan(tp
->speed
)) {
701 gchar
*s_speed
= a_coords_dtostr(tp
->speed
);
702 fprintf ( f
, " <speed>%s</speed>\n", s_speed
);
705 if (tp
->fix_mode
== VIK_GPS_MODE_2D
)
706 fprintf ( f
, " <fix>2d</fix>\n");
707 if (tp
->fix_mode
== VIK_GPS_MODE_3D
)
708 fprintf ( f
, " <fix>3d</fix>\n");
710 fprintf ( f
, " <sat>%d</sat>\n", tp
->nsats
);
713 fprintf ( f
, " </trkpt>\n" );
717 static void gpx_write_track ( const gchar
*name
, VikTrack
*t
, GpxWritingContext
*context
)
719 FILE *f
= context
->file
;
721 gboolean first_tp_is_newsegment
= FALSE
; /* must temporarily make it not so, but we want to restore state. not that it matters. */
723 tmp
= entitize ( name
);
724 fprintf ( f
, "<trk%s>\n <name>%s</name>\n", t
->visible
? "" : " hidden=\"hidden\"", tmp
);
729 tmp
= entitize ( t
->comment
);
730 fprintf ( f
, " <desc>%s</desc>\n", tmp
);
734 fprintf ( f
, " <trkseg>\n" );
736 if ( t
->trackpoints
&& t
->trackpoints
->data
) {
737 first_tp_is_newsegment
= VIK_TRACKPOINT(t
->trackpoints
->data
)->newsegment
;
738 VIK_TRACKPOINT(t
->trackpoints
->data
)->newsegment
= FALSE
; /* so we won't write </trkseg><trkseg> already */
739 g_list_foreach ( t
->trackpoints
, (GFunc
) gpx_write_trackpoint
, context
);
740 VIK_TRACKPOINT(t
->trackpoints
->data
)->newsegment
= first_tp_is_newsegment
; /* restore state */
743 fprintf ( f
, "</trkseg>\n</trk>\n" );
746 static void gpx_write_header( FILE *f
)
748 fprintf(f
, "<?xml version=\"1.0\"?>\n"
749 "<gpx version=\"1.0\" creator=\"Viking -- http://viking.sf.net/\"\n"
750 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
751 "xmlns=\"http://www.topografix.com/GPX/1/0\"\n"
752 "xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\n");
755 static void gpx_write_footer( FILE *f
)
757 fprintf(f
, "</gpx>\n");
765 } gpx_waypoint_and_name
;
768 gpx_waypoint_and_name
*wps
;
771 } gpx_gather_waypoints_passalong_t
;
773 /* Type to hold name of track and timestamp of first trackpoint */
775 time_t first_timestamp
;
777 } gpx_track_and_timestamp
;
780 gpx_track_and_timestamp
*trks
;
783 } gpx_gather_tracks_passalong_t
;
785 static void gpx_collect_waypoint ( const gchar
*name
, VikWaypoint
*wp
, gpx_gather_waypoints_passalong_t
*passalong
)
787 if ( passalong
->i
< passalong
->n_wps
) {
788 passalong
->wps
[passalong
->i
].name
= name
;
789 passalong
->wps
[passalong
->i
].wp
= wp
;
794 /* Function to collect a track and the first timestamp in the list */
795 static void gpx_collect_track (const gchar
*name
, VikTrack
*track
, gpx_gather_tracks_passalong_t
*passalong
)
797 if (passalong
->i
< passalong
->n_trks
)
799 VikTrackpoint
*first_point
= (VikTrackpoint
*)track
->trackpoints
->data
;
800 passalong
->trks
[passalong
->i
].name
= name
;
801 passalong
->trks
[passalong
->i
].first_timestamp
= first_point
->timestamp
;
806 static int gpx_waypoint_and_name_compar(const void *x
, const void *y
)
808 gpx_waypoint_and_name
*a
= (gpx_waypoint_and_name
*)x
;
809 gpx_waypoint_and_name
*b
= (gpx_waypoint_and_name
*)y
;
810 return strcmp(a
->name
,b
->name
);
813 /* Function to compare two tracks by their first timestamp */
814 static int gpx_track_and_timestamp_compar(const void *x
, const void *y
)
816 gpx_track_and_timestamp
*a
= (gpx_track_and_timestamp
*)x
;
817 gpx_track_and_timestamp
*b
= (gpx_track_and_timestamp
*)y
;
818 if (a
->first_timestamp
< b
->first_timestamp
)
822 if (a
->first_timestamp
> b
->first_timestamp
)
829 void a_gpx_write_file( VikTrwLayer
*vtl
, FILE *f
)
831 a_gpx_write_file_options(NULL
, vtl
, f
);
834 void a_gpx_write_file_options ( GpxWritingOptions
*options
, VikTrwLayer
*vtl
, FILE *f
)
836 GpxWritingContext context
= { options
, f
};
839 gpx_write_header ( f
);
842 gpx_gather_waypoints_passalong_t passalong
;
843 passalong
.n_wps
= g_hash_table_size ( vik_trw_layer_get_waypoints ( vtl
) );
845 passalong
.wps
= g_new(gpx_waypoint_and_name
,passalong
.n_wps
);
846 g_hash_table_foreach ( vik_trw_layer_get_waypoints ( vtl
), (GHFunc
) gpx_collect_waypoint
, &passalong
);
847 /* gather waypoints in a list, then sort */
848 qsort(passalong
.wps
, passalong
.n_wps
, sizeof(gpx_waypoint_and_name
), gpx_waypoint_and_name_compar
);
849 for ( i
= 0; i
< passalong
.n_wps
; i
++ )
850 gpx_write_waypoint ( passalong
.wps
[i
].name
, passalong
.wps
[i
].wp
, &context
);
851 g_free ( passalong
.wps
);
853 gpx_gather_tracks_passalong_t passalong_tracks
;
854 passalong_tracks
.n_trks
= g_hash_table_size ( vik_trw_layer_get_tracks (vtl
) );
855 passalong_tracks
.i
= 0;
856 passalong_tracks
.trks
= g_new(gpx_track_and_timestamp
,passalong_tracks
.n_trks
);
857 g_hash_table_foreach (vik_trw_layer_get_tracks(vtl
), (GHFunc
) gpx_collect_track
, &passalong_tracks
);
858 /* Sort by timestamp */
859 qsort(passalong_tracks
.trks
, passalong_tracks
.n_trks
, sizeof(gpx_track_and_timestamp
), gpx_track_and_timestamp_compar
);
860 for (i
=0;i
<passalong_tracks
.n_trks
; i
++)
862 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
);
864 g_free ( passalong_tracks
.trks
);
865 gpx_write_footer ( f
);
868 void a_gpx_write_track_file ( const gchar
*name
, VikTrack
*t
, FILE *f
)
870 a_gpx_write_track_file_options ( NULL
, name
, t
, f
);
873 void a_gpx_write_track_file_options ( GpxWritingOptions
*options
, const gchar
*name
, VikTrack
*t
, FILE *f
)
875 GpxWritingContext context
= {options
, f
};
876 gpx_write_header ( f
);
877 gpx_write_track ( name
, t
, &context
);
878 gpx_write_footer ( f
);