]> git.street.me.uk Git - andy/viking.git/blob - src/gpspoint.c
[MAPS] Add OpenSeaMap source
[andy/viking.git] / src / gpspoint.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (C) 2012, Rob Norris <rw_norris@hotmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #ifdef HAVE_MATH_H
27 #include <math.h>
28 #endif
29
30 #include "viking.h"
31
32 #include <ctype.h>
33 #ifdef HAVE_STRING_H
34 #include <string.h>
35 #endif
36
37 #include <stdlib.h>
38 /* strtod */
39
40
41 static void a_gpspoint_write_track ( const gpointer id, const VikTrack *t, FILE *f );
42 static void a_gpspoint_write_trackpoint ( VikTrackpoint *tp, FILE *f );
43 static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp, FILE *f );
44
45
46 /* outline for file gpspoint.c
47
48 reading file:
49
50 take a line.
51 get first tag, if not type, skip it.
52 if type, record type.  if waypoint list, etc move on. if track, make a new track, make it current track, add it, etc.
53 if waypoint, read on and store to the waypoint.
54 if trackpoint, make trackpoint, store to current track (error / skip if none)
55
56 */
57
58 /* Thanks to etrex-cache's gpsbabel's gpspoint.c for starting me off! */
59
60 static char line_buffer[2048];
61
62 #define GPSPOINT_TYPE_NONE 0
63 #define GPSPOINT_TYPE_WAYPOINT 1
64 #define GPSPOINT_TYPE_TRACKPOINT 2
65 /* #define GPSPOINT_TYPE_ROUTEPOINT 3 */
66 #define GPSPOINT_TYPE_TRACK 4
67
68 /* #define GPSPOINT_TYPE_ROUTE 5 */
69
70 static VikTrack *current_track; /* pointer to pointer to first GList */
71
72 static gint line_type = GPSPOINT_TYPE_NONE;
73 static struct LatLon line_latlon;
74 static gchar *line_name;
75 static gchar *line_comment;
76 static gchar *line_image;
77 static gchar *line_symbol;
78 static gboolean line_newsegment = FALSE;
79 static gboolean line_has_timestamp = FALSE;
80 static time_t line_timestamp = 0;
81 static gdouble line_altitude = VIK_DEFAULT_ALTITUDE;
82 static gboolean line_visible = TRUE;
83
84 static gboolean line_extended = FALSE;
85 static gdouble line_speed = NAN;
86 static gdouble line_course = NAN;
87 static gint line_sat = 0;
88 static gint line_fix = 0;
89 /* other possible properties go here */
90
91
92 static void gpspoint_process_tag ( const gchar *tag, gint len );
93 static void gpspoint_process_key_and_value ( const gchar *key, gint key_len, const gchar *value, gint value_len );
94
95 static gchar *slashdup(const gchar *str)
96 {
97   guint16 len = strlen(str);
98   guint16 need_bs_count, i, j;
99   gchar *rv;
100   for ( i = 0, need_bs_count = 0; i < len; i++ )
101     if ( str[i] == '\\' || str[i] == '"' )
102       need_bs_count++;
103   rv = g_malloc ( (len+need_bs_count+1) * sizeof(gchar) );
104   for ( i = 0, j = 0; i < len; i++, j++ )
105   {
106     if ( str[i] == '\\' || str[i] == '"' )
107       rv[j++] = '\\';
108     rv[j] = str[i];
109   }
110   rv[j] = '\0';
111   return rv;
112 }
113
114 static gchar *deslashndup ( const gchar *str, guint16 len )
115 {
116   guint16 i,j, bs_count, new_len;
117   gboolean backslash = FALSE;
118   gchar *rv;
119
120   if ( len < 1 )
121     return NULL;
122
123   for ( i = 0, bs_count = 0; i < len; i++ )
124    if ( str[i] == '\\' )
125    {
126      bs_count++;
127      i++;
128    }
129
130   if ( str[i-1] == '\\' && (len == 1 || str[i-2] != '\\') )
131     bs_count--;
132
133   new_len = len - bs_count;
134   rv = g_malloc ( (new_len+1) * sizeof(gchar) );
135   for ( i = 0, j = 0; i < len && j < new_len; i++ )
136     if ( str[i] == '\\' && !backslash )
137       backslash = TRUE;
138     else
139     {
140       rv[j++] = str[i];
141       backslash = FALSE;
142     }
143
144   rv[new_len] = '\0';
145   return rv;
146 }
147
148 /*
149  * Returns whether file read was a success
150  * No obvious way to test for a 'gpspoint' file,
151  *  thus set a flag if any actual tag found during processing of the file
152  */
153 gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) {
154   VikCoordMode coord_mode = vik_trw_layer_get_coord_mode ( trw );
155   gchar *tag_start, *tag_end;
156   g_assert ( f != NULL && trw != NULL );
157   line_type = 0;
158   line_timestamp = 0;
159   line_newsegment = FALSE;
160   line_image = NULL;
161   line_symbol = NULL;
162   current_track = NULL;
163   gboolean have_read_something = FALSE;
164
165   while (fgets(line_buffer, 2048, f))
166   {
167     gboolean inside_quote = 0;
168     gboolean backslash = 0;
169
170     line_buffer[strlen(line_buffer)-1] = '\0'; /* chop off newline */
171
172     /* for gpspoint files wrapped inside */
173     if ( strlen(line_buffer) >= 13 && strncmp ( line_buffer, "~EndLayerData", 13 ) == 0 )
174       break;
175
176     /* each line: nullify stuff, make thing if nes, free name if ness */
177     tag_start = line_buffer;
178     for (;;)
179     {
180       /* my addition: find first non-whitespace character. if the null, skip line. */
181       while (*tag_start != '\0' && isspace(*tag_start))
182         tag_start++;
183       if (tag_start == '\0')
184         break;
185
186       if (*tag_start == '#')
187         break;
188
189       tag_end = tag_start;
190         if (*tag_end == '"')
191           inside_quote = !inside_quote;
192       while (*tag_end != '\0' && (!isspace(*tag_end) || inside_quote)) {
193         tag_end++;
194         if (*tag_end == '\\' && !backslash)
195           backslash = TRUE;
196         else if (backslash)
197           backslash = FALSE;
198         else if (*tag_end == '"')
199           inside_quote = !inside_quote;
200       }
201
202       gpspoint_process_tag ( tag_start, tag_end - tag_start );
203
204       if (*tag_end == '\0' )
205         break;
206       else
207         tag_start = tag_end+1;
208     }
209     if (line_type == GPSPOINT_TYPE_WAYPOINT && line_name)
210     {
211       have_read_something = TRUE;
212       VikWaypoint *wp = vik_waypoint_new();
213       wp->visible = line_visible;
214       wp->altitude = line_altitude;
215
216       vik_coord_load_from_latlon ( &(wp->coord), coord_mode, &line_latlon );
217
218       vik_trw_layer_filein_add_waypoint ( trw, line_name, wp );
219       g_free ( line_name );
220       line_name = NULL;
221
222       if ( line_comment )
223       {
224         vik_waypoint_set_comment ( wp, line_comment );
225         line_comment = NULL;
226       }
227
228       if ( line_image )
229       {
230         vik_waypoint_set_image ( wp, line_image );
231         line_image = NULL;
232       }
233
234       if ( line_symbol )
235       {
236         vik_waypoint_set_symbol ( wp, line_symbol );
237         line_symbol = NULL;
238       }
239     }
240     else if (line_type == GPSPOINT_TYPE_TRACK && line_name)
241     {
242       have_read_something = TRUE;
243       VikTrack *pl = vik_track_new();
244
245       /* Thanks to Peter Jones for this Fix */
246       if (!line_name) line_name = g_strdup("UNK");
247
248       pl->visible = line_visible;
249
250       if ( line_comment )
251       {
252         vik_track_set_comment ( pl, line_comment );
253         line_comment = NULL;
254       }
255
256       pl->trackpoints = NULL;
257       vik_trw_layer_filein_add_track ( trw, line_name, pl );
258       g_free ( line_name );
259       line_name = NULL;
260
261       current_track = pl;
262     }
263     else if (line_type == GPSPOINT_TYPE_TRACKPOINT && current_track)
264     {
265       have_read_something = TRUE;
266       VikTrackpoint *tp = vik_trackpoint_new();
267       vik_coord_load_from_latlon ( &(tp->coord), coord_mode, &line_latlon );
268       tp->newsegment = line_newsegment;
269       tp->has_timestamp = line_has_timestamp;
270       tp->timestamp = line_timestamp;
271       tp->altitude = line_altitude;
272       if (line_extended) {
273         tp->speed = line_speed;
274         tp->course = line_course;
275         tp->nsats = line_sat;
276         tp->fix_mode = line_fix;
277       }
278       current_track->trackpoints = g_list_append ( current_track->trackpoints, tp );
279     }
280
281     if (line_name) 
282       g_free ( line_name );
283     line_name = NULL;
284     if (line_comment) 
285       g_free ( line_comment );
286     if (line_image)
287       g_free ( line_image );
288     if (line_symbol)
289       g_free ( line_symbol );
290     line_comment = NULL;
291     line_image = NULL;
292     line_symbol = NULL;
293     line_type = GPSPOINT_TYPE_NONE;
294     line_newsegment = FALSE;
295     line_has_timestamp = FALSE;
296     line_timestamp = 0;
297     line_altitude = VIK_DEFAULT_ALTITUDE;
298     line_visible = TRUE;
299     line_symbol = NULL;
300
301     line_extended = FALSE;
302     line_speed = NAN;
303     line_course = NAN;
304     line_sat = 0;
305     line_fix = 0;
306   }
307
308   return have_read_something;
309 }
310
311 /* Tag will be of a few defined forms:
312    ^[:alpha:]*=".*"$
313    ^[:alpha:]*=.*$
314
315    <invalid tag>
316
317 So we must determine end of tag name, start of value, end of value.
318 */
319 static void gpspoint_process_tag ( const gchar *tag, gint len )
320 {
321   const gchar *key_end, *value_start, *value_end;
322
323   /* Searching for key end */
324   key_end = tag;
325
326   while (++key_end - tag < len)
327     if (*key_end == '=')
328       break;
329
330   if (key_end - tag == len)
331     return; /* no good */
332
333   if (key_end - tag == len + 1)
334     value_start = value_end = 0; /* size = 0 */
335   else
336   {
337     value_start = key_end + 1; /* equal_sign plus one */
338
339     if (*value_start == '"')
340     {
341       value_start++;
342       if (*value_start == '"')
343         value_start = value_end = 0; /* size = 0 */
344       else
345       {
346         if (*(tag+len-1) == '"')
347         value_end = tag + len - 1;
348         else
349           return; /* bogus */
350       }
351     }
352     else
353       value_end = tag + len; /* value start really IS value start. */
354
355     gpspoint_process_key_and_value(tag, key_end - tag, value_start, value_end - value_start);
356   }
357 }
358
359 /*
360 value = NULL for none
361 */
362 static void gpspoint_process_key_and_value ( const gchar *key, gint key_len, const gchar *value, gint value_len )
363 {
364   if (key_len == 4 && strncasecmp( key, "type", key_len ) == 0 )
365   {
366     if (value == NULL)
367       line_type = GPSPOINT_TYPE_NONE;
368     else if (value_len == 5 && strncasecmp( value, "track", value_len ) == 0 )
369       line_type = GPSPOINT_TYPE_TRACK;
370     else if (value_len == 10 && strncasecmp( value, "trackpoint", value_len ) == 0 )
371       line_type = GPSPOINT_TYPE_TRACKPOINT;
372     else if (value_len == 8 && strncasecmp( value, "waypoint", value_len ) == 0 )
373       line_type = GPSPOINT_TYPE_WAYPOINT;
374     else
375       /* all others are ignored */
376       line_type = GPSPOINT_TYPE_NONE;
377   }
378   else if (key_len == 4 && strncasecmp( key, "name", key_len ) == 0 && value != NULL)
379   {
380     if (line_name == NULL)
381     {
382       line_name = g_strndup ( value, value_len );
383     }
384   }
385   else if (key_len == 7 && strncasecmp( key, "comment", key_len ) == 0 && value != NULL)
386   {
387     if (line_comment == NULL)
388       line_comment = deslashndup ( value, value_len );
389   }
390   else if (key_len == 5 && strncasecmp( key, "image", key_len ) == 0 && value != NULL)
391   {
392     if (line_image == NULL)
393       line_image = deslashndup ( value, value_len );
394   }
395   else if (key_len == 8 && strncasecmp( key, "latitude", key_len ) == 0 && value != NULL)
396   {
397     line_latlon.lat = g_ascii_strtod(value, NULL);
398   }
399   else if (key_len == 9 && strncasecmp( key, "longitude", key_len ) == 0 && value != NULL)
400   {
401     line_latlon.lon = g_ascii_strtod(value, NULL);
402   }
403   else if (key_len == 8 && strncasecmp( key, "altitude", key_len ) == 0 && value != NULL)
404   {
405     line_altitude = g_ascii_strtod(value, NULL);
406   }
407   else if (key_len == 7 && strncasecmp( key, "visible", key_len ) == 0 && value[0] != 'y' && value[0] != 'Y' && value[0] != 't' && value[0] != 'T')
408   {
409     line_visible = FALSE;
410   }
411   else if (key_len == 6 && strncasecmp( key, "symbol", key_len ) == 0 && value != NULL)
412   {
413     line_symbol = g_strndup ( value, value_len );
414   }
415   else if (key_len == 8 && strncasecmp( key, "unixtime", key_len ) == 0 && value != NULL)
416   {
417     line_timestamp = g_ascii_strtod(value, NULL);
418     if ( line_timestamp != 0x80000000 )
419       line_has_timestamp = TRUE;
420   }
421   else if (key_len == 10 && strncasecmp( key, "newsegment", key_len ) == 0 && value != NULL)
422   {
423     line_newsegment = TRUE;
424   }
425   else if (key_len == 8 && strncasecmp( key, "extended", key_len ) == 0 && value != NULL)
426   {
427     line_extended = TRUE;
428   }
429   else if (key_len == 5 && strncasecmp( key, "speed", key_len ) == 0 && value != NULL)
430   {
431     line_speed = g_ascii_strtod(value, NULL);
432   }
433   else if (key_len == 6 && strncasecmp( key, "course", key_len ) == 0 && value != NULL)
434   {
435     line_course = g_ascii_strtod(value, NULL);
436   }
437   else if (key_len == 3 && strncasecmp( key, "sat", key_len ) == 0 && value != NULL)
438   {
439     line_sat = atoi(value);
440   }
441   else if (key_len == 3 && strncasecmp( key, "fix", key_len ) == 0 && value != NULL)
442   {
443     line_fix = atoi(value);
444   }
445 }
446
447 static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp, FILE *f )
448 {
449   static struct LatLon ll;
450   gchar *s_lat, *s_lon;
451   // Sanity clause
452   if ( wp && !(wp->name) ) {
453     return;
454   }
455   vik_coord_to_latlon ( &(wp->coord), &ll );
456   s_lat = a_coords_dtostr(ll.lat);
457   s_lon = a_coords_dtostr(ll.lon);
458   fprintf ( f, "type=\"waypoint\" latitude=\"%s\" longitude=\"%s\" name=\"%s\"", s_lat, s_lon, wp->name );
459   g_free ( s_lat ); 
460   g_free ( s_lon );
461
462   if ( wp->altitude != VIK_DEFAULT_ALTITUDE ) {
463     gchar *s_alt = a_coords_dtostr(wp->altitude);
464     fprintf ( f, " altitude=\"%s\"", s_alt );
465     g_free(s_alt);
466   }
467   if ( wp->comment )
468   {
469     gchar *tmp_comment = slashdup(wp->comment);
470     fprintf ( f, " comment=\"%s\"", tmp_comment );
471     g_free ( tmp_comment );
472   }
473   if ( wp->image )
474   {
475     gchar *tmp_image = slashdup(wp->image);
476     fprintf ( f, " image=\"%s\"", tmp_image );
477     g_free ( tmp_image );
478   }
479   if ( wp->symbol )
480   {
481     fprintf ( f, " symbol=\"%s\"", wp->symbol );
482   }
483   if ( ! wp->visible )
484     fprintf ( f, " visible=\"n\"" );
485   fprintf ( f, "\n" );
486 }
487
488 static void a_gpspoint_write_trackpoint ( VikTrackpoint *tp, FILE *f )
489 {
490   static struct LatLon ll;
491   gchar *s_lat, *s_lon;
492   vik_coord_to_latlon ( &(tp->coord), &ll );
493
494   /* TODO: modify a_coords_dtostr() to accept (optional) buffer
495    * instead of doing malloc/free everytime */
496   s_lat = a_coords_dtostr(ll.lat);
497   s_lon = a_coords_dtostr(ll.lon);
498   fprintf ( f, "type=\"trackpoint\" latitude=\"%s\" longitude=\"%s\"", s_lat, s_lon );
499   g_free ( s_lat ); 
500   g_free ( s_lon );
501
502   if ( tp->altitude != VIK_DEFAULT_ALTITUDE ) {
503     gchar *s_alt = a_coords_dtostr(tp->altitude);
504     fprintf ( f, " altitude=\"%s\"", s_alt );
505     g_free(s_alt);
506   }
507   if ( tp->has_timestamp )
508     fprintf ( f, " unixtime=\"%ld\"", tp->timestamp );
509   if ( tp->newsegment )
510     fprintf ( f, " newsegment=\"yes\"" );
511
512   if (!isnan(tp->speed) || !isnan(tp->course) || tp->nsats > 0) {
513     fprintf ( f, " extended=\"yes\"" );
514     if (!isnan(tp->speed)) {
515       gchar *s_speed = a_coords_dtostr(tp->speed);
516       fprintf ( f, " speed=\"%s\"", s_speed );
517       g_free(s_speed);
518     }
519     if (!isnan(tp->course)) {
520       gchar *s_course = a_coords_dtostr(tp->course);
521       fprintf ( f, " course=\"%s\"", s_course );
522       g_free(s_course);
523     }
524     if (tp->nsats > 0)
525       fprintf ( f, " sat=\"%d\"", tp->nsats );
526     if (tp->fix_mode > 0)
527       fprintf ( f, " fix=\"%d\"", tp->fix_mode );
528   }
529   fprintf ( f, "\n" );
530 }
531
532
533 static void a_gpspoint_write_track ( const gpointer id, const VikTrack *t, FILE *f )
534 {
535   // Sanity clause
536   if ( t && !(t->name) ) {
537     return;
538   }
539   if ( t->comment )
540   {
541     gchar *tmp_comment = slashdup(t->comment);
542     fprintf ( f, "type=\"track\" name=\"%s\" comment=\"%s\"%s\n", t->name, tmp_comment, t->visible ? "" : " visible=\"n\"" );
543     g_free ( tmp_comment );
544   }
545   else
546     fprintf ( f, "type=\"track\" name=\"%s\"%s\n", t->name, t->visible ? "" : " visible=\"n\"" );
547   g_list_foreach ( t->trackpoints, (GFunc) a_gpspoint_write_trackpoint, f );
548   fprintf ( f, "type=\"trackend\"\n" );
549 }
550
551 void a_gpspoint_write_file ( VikTrwLayer *trw, FILE *f )
552 {
553   GHashTable *tracks = vik_trw_layer_get_tracks ( trw );
554   GHashTable *waypoints = vik_trw_layer_get_waypoints ( trw );
555
556   fprintf ( f, "type=\"waypointlist\"\n" );
557   g_hash_table_foreach ( waypoints, (GHFunc) a_gpspoint_write_waypoint, f );
558   fprintf ( f, "type=\"waypointlistend\"\n" );
559   g_hash_table_foreach ( tracks, (GHFunc) a_gpspoint_write_track, f );
560 }