X-Git-Url: https://git.street.me.uk/andy/viking.git/blobdiff_plain/b1453c161366d6f7a95dfe7e96cf62f5b7d8bb02..aea71c6655e9efb6cb8b777a25c1a206851311ae:/src/gpspoint.c diff --git a/src/gpspoint.c b/src/gpspoint.c index 06c0f015..d16049d4 100644 --- a/src/gpspoint.c +++ b/src/gpspoint.c @@ -2,7 +2,7 @@ * viking -- GPS Data and Topo Analyzer, Explorer, and Manager * * Copyright (C) 2003-2005, Evan Battaglia - * Copyright (C) 2012, Rob Norris + * Copyright (C) 2012-2013, Rob Norris * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -46,7 +46,6 @@ static void a_gpspoint_write_track ( const gpointer id, const VikTrack *t, FILE static void a_gpspoint_write_trackpoint ( VikTrackpoint *tp, TP_write_info_type *write_info ); static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp, FILE *f ); - /* outline for file gpspoint.c reading file: @@ -60,8 +59,8 @@ if trackpoint, make trackpoint, store to current track (error / skip if none) */ /* Thanks to etrex-cache's gpsbabel's gpspoint.c for starting me off! */ - -static char line_buffer[2048]; +#define VIKING_LINE_SIZE 4096 +static char line_buffer[VIKING_LINE_SIZE]; #define GPSPOINT_TYPE_NONE 0 #define GPSPOINT_TYPE_WAYPOINT 1 @@ -77,7 +76,11 @@ static struct LatLon line_latlon; static gchar *line_name; static gchar *line_comment; static gchar *line_description; +static gchar *line_source; +static gchar *line_xtype; static gchar *line_color; +static gint line_name_label = 0; +static gint line_dist_label = 0; static gchar *line_image; static gchar *line_symbol; static gboolean line_newsegment = FALSE; @@ -91,16 +94,19 @@ static gdouble line_speed = NAN; static gdouble line_course = NAN; static gint line_sat = 0; static gint line_fix = 0; +static gdouble line_hdop = VIK_DEFAULT_DOP; +static gdouble line_vdop = VIK_DEFAULT_DOP; +static gdouble line_pdop = VIK_DEFAULT_DOP; /* other possible properties go here */ -static void gpspoint_process_tag ( const gchar *tag, gint len ); -static void gpspoint_process_key_and_value ( const gchar *key, gint key_len, const gchar *value, gint value_len ); +static void gpspoint_process_tag ( const gchar *tag, guint len ); +static void gpspoint_process_key_and_value ( const gchar *key, guint key_len, const gchar *value, guint value_len ); static gchar *slashdup(const gchar *str) { - guint16 len = strlen(str); - guint16 need_bs_count, i, j; + size_t len = strlen(str); + size_t need_bs_count, i, j; gchar *rv; for ( i = 0, need_bs_count = 0; i < len; i++ ) if ( str[i] == '\\' || str[i] == '"' ) @@ -111,6 +117,10 @@ static gchar *slashdup(const gchar *str) if ( str[i] == '\\' || str[i] == '"' ) rv[j++] = '\\'; rv[j] = str[i]; + // Basic normalization of strings - replace Linefeed and Carriage returns as blanks. + // although allowed in GPX Spec - Viking file format can't handle multi-line strings yet... + if ( str[i] == '\n' || str[i] == '\r' ) + rv[j] = ' '; } rv[j] = '\0'; return rv; @@ -155,7 +165,7 @@ static gchar *deslashndup ( const gchar *str, guint16 len ) * No obvious way to test for a 'gpspoint' file, * thus set a flag if any actual tag found during processing of the file */ -gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { +gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f, const gchar *dirpath ) { VikCoordMode coord_mode = vik_trw_layer_get_coord_mode ( trw ); gchar *tag_start, *tag_end; g_assert ( f != NULL && trw != NULL ); @@ -167,7 +177,7 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { current_track = NULL; gboolean have_read_something = FALSE; - while (fgets(line_buffer, 2048, f)) + while (fgets(line_buffer, VIKING_LINE_SIZE, f)) { gboolean inside_quote = 0; gboolean backslash = 0; @@ -188,7 +198,7 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { /* my addition: find first non-whitespace character. if the null, skip line. */ while (*tag_start != '\0' && isspace(*tag_start)) tag_start++; - if (tag_start == '\0') + if (*tag_start == '\0') break; if (*tag_start == '#') @@ -207,7 +217,9 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { inside_quote = !inside_quote; } - gpspoint_process_tag ( tag_start, tag_end - tag_start ); + // Won't have super massively long strings, so potential truncation in cast is acceptable. + guint len = (guint)(tag_end - tag_start); + gpspoint_process_tag ( tag_start, len ); if (*tag_end == '\0' ) break; @@ -220,6 +232,8 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { VikWaypoint *wp = vik_waypoint_new(); wp->visible = line_visible; wp->altitude = line_altitude; + wp->has_timestamp = line_has_timestamp; + wp->timestamp = line_timestamp; vik_coord_load_from_latlon ( &(wp->coord), coord_mode, &line_latlon ); @@ -228,33 +242,40 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { line_name = NULL; if ( line_comment ) - { vik_waypoint_set_comment ( wp, line_comment ); - line_comment = NULL; - } if ( line_description ) - { vik_waypoint_set_description ( wp, line_description ); - line_description = NULL; - } - if ( line_image ) - { - vik_waypoint_set_image ( wp, line_image ); - line_image = NULL; + if ( line_source ) + vik_waypoint_set_source ( wp, line_source ); + + if ( line_xtype ) + vik_waypoint_set_type ( wp, line_xtype ); + + if ( line_image ) { + // Ensure the filename is absolute + if ( g_path_is_absolute ( line_image ) ) + vik_waypoint_set_image ( wp, line_image ); + else { + // Otherwise create the absolute filename from the directory of the .vik file & and the relative filename + gchar *full = g_strconcat(dirpath, G_DIR_SEPARATOR_S, line_image, NULL); + gchar *absolute = file_realpath_dup ( full ); // resolved into the canonical name + vik_waypoint_set_image ( wp, absolute ); + g_free ( absolute ); + g_free ( full ); + } } if ( line_symbol ) - { vik_waypoint_set_symbol ( wp, line_symbol ); - line_symbol = NULL; - } } else if ((line_type == GPSPOINT_TYPE_TRACK || line_type == GPSPOINT_TYPE_ROUTE) && line_name) { have_read_something = TRUE; VikTrack *pl = vik_track_new(); + // NB don't set defaults here as all properties are stored in the GPS_POINT format + //vik_track_set_defaults ( pl ); /* Thanks to Peter Jones for this Fix */ if (!line_name) line_name = g_strdup("UNK"); @@ -263,16 +284,16 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { pl->is_route = (line_type == GPSPOINT_TYPE_ROUTE); if ( line_comment ) - { vik_track_set_comment ( pl, line_comment ); - line_comment = NULL; - } if ( line_description ) - { vik_track_set_description ( pl, line_description ); - line_description = NULL; - } + + if ( line_source ) + vik_track_set_source ( pl, line_source ); + + if ( line_xtype ) + vik_track_set_type ( pl, line_xtype ); if ( line_color ) { @@ -280,6 +301,9 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { pl->has_color = TRUE; } + pl->draw_name_mode = line_name_label; + pl->max_number_dist_labels = line_dist_label; + pl->trackpoints = NULL; vik_trw_layer_filein_add_track ( trw, line_name, pl ); g_free ( line_name ); @@ -296,11 +320,15 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { tp->has_timestamp = line_has_timestamp; tp->timestamp = line_timestamp; tp->altitude = line_altitude; + vik_trackpoint_set_name ( tp, line_name ); if (line_extended) { tp->speed = line_speed; tp->course = line_course; tp->nsats = line_sat; tp->fix_mode = line_fix; + tp->hdop = line_hdop; + tp->vdop = line_vdop; + tp->pdop = line_pdop; } current_track->trackpoints = g_list_append ( current_track->trackpoints, tp ); } @@ -312,6 +340,10 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { g_free ( line_comment ); if (line_description) g_free ( line_description ); + if (line_source) + g_free ( line_source ); + if (line_xtype) + g_free ( line_xtype ); if (line_color) g_free ( line_color ); if (line_image) @@ -320,6 +352,8 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { g_free ( line_symbol ); line_comment = NULL; line_description = NULL; + line_source = NULL; + line_xtype = NULL; line_color = NULL; line_image = NULL; line_symbol = NULL; @@ -336,6 +370,11 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { line_course = NAN; line_sat = 0; line_fix = 0; + line_hdop = VIK_DEFAULT_DOP; + line_vdop = VIK_DEFAULT_DOP; + line_pdop = VIK_DEFAULT_DOP; + line_name_label = 0; + line_dist_label = 0; } return have_read_something; @@ -349,7 +388,7 @@ gboolean a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { So we must determine end of tag name, start of value, end of value. */ -static void gpspoint_process_tag ( const gchar *tag, gint len ) +static void gpspoint_process_tag ( const gchar *tag, guint len ) { const gchar *key_end, *value_start, *value_end; @@ -377,7 +416,7 @@ static void gpspoint_process_tag ( const gchar *tag, gint len ) else { if (*(tag+len-1) == '"') - value_end = tag + len - 1; + value_end = tag + len - 1; else return; /* bogus */ } @@ -385,6 +424,10 @@ static void gpspoint_process_tag ( const gchar *tag, gint len ) else value_end = tag + len; /* value start really IS value start. */ + // Detect broken lines which end without any text or the enclosing ". i.e. like: comment=" + if ( (value_end - value_start) < 0 ) + return; + gpspoint_process_key_and_value(tag, key_end - tag, value_start, value_end - value_start); } } @@ -392,7 +435,7 @@ static void gpspoint_process_tag ( const gchar *tag, gint len ) /* value = NULL for none */ -static void gpspoint_process_key_and_value ( const gchar *key, gint key_len, const gchar *value, gint value_len ) +static void gpspoint_process_key_and_value ( const gchar *key, guint key_len, const gchar *value, guint value_len ) { if (key_len == 4 && strncasecmp( key, "type", key_len ) == 0 ) { @@ -416,7 +459,7 @@ static void gpspoint_process_key_and_value ( const gchar *key, gint key_len, con { if (line_name == NULL) { - line_name = g_strndup ( value, value_len ); + line_name = deslashndup ( value, value_len ); } } else if (key_len == 7 && strncasecmp( key, "comment", key_len ) == 0 && value != NULL) @@ -429,11 +472,30 @@ static void gpspoint_process_key_and_value ( const gchar *key, gint key_len, con if (line_description == NULL) line_description = deslashndup ( value, value_len ); } + else if (key_len == 6 && strncasecmp( key, "source", key_len ) == 0 && value != NULL) + { + if (line_source == NULL) + line_source = deslashndup ( value, value_len ); + } + // NB using 'xtype' to differentiate from our own 'type' key + else if (key_len == 5 && strncasecmp( key, "xtype", key_len ) == 0 && value != NULL) + { + if (line_xtype == NULL) + line_xtype = deslashndup ( value, value_len ); + } else if (key_len == 5 && strncasecmp( key, "color", key_len ) == 0 && value != NULL) { if (line_color == NULL) line_color = deslashndup ( value, value_len ); } + else if (key_len == 14 && strncasecmp( key, "draw_name_mode", key_len ) == 0 && value != NULL) + { + line_name_label = atoi(value); + } + else if (key_len == 18 && strncasecmp( key, "number_dist_labels", key_len ) == 0 && value != NULL) + { + line_dist_label = atoi(value); + } else if (key_len == 5 && strncasecmp( key, "image", key_len ) == 0 && value != NULL) { if (line_image == NULL) @@ -451,7 +513,7 @@ static void gpspoint_process_key_and_value ( const gchar *key, gint key_len, con { line_altitude = g_ascii_strtod(value, NULL); } - else if (key_len == 7 && strncasecmp( key, "visible", key_len ) == 0 && value[0] != 'y' && value[0] != 'Y' && value[0] != 't' && value[0] != 'T') + else if (key_len == 7 && strncasecmp( key, "visible", key_len ) == 0 && value != NULL && value[0] != 'y' && value[0] != 'Y' && value[0] != 't' && value[0] != 'T') { line_visible = FALSE; } @@ -489,28 +551,45 @@ static void gpspoint_process_key_and_value ( const gchar *key, gint key_len, con { line_fix = atoi(value); } + else if (key_len == 4 && strncasecmp( key, "hdop", key_len ) == 0 && value != NULL) + { + line_hdop = g_ascii_strtod(value, NULL); + } + else if (key_len == 4 && strncasecmp( key, "vdop", key_len ) == 0 && value != NULL) + { + line_vdop = g_ascii_strtod(value, NULL); + } + else if (key_len == 4 && strncasecmp( key, "pdop", key_len ) == 0 && value != NULL) + { + line_pdop = g_ascii_strtod(value, NULL); + } } static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp, FILE *f ) { - static struct LatLon ll; - gchar *s_lat, *s_lon; - // Sanity clause - if ( wp && !(wp->name) ) { + struct LatLon ll; + gchar s_lat[COORDS_STR_BUFFER_SIZE]; + gchar s_lon[COORDS_STR_BUFFER_SIZE]; + // Sanity clauses + if ( !wp ) return; - } + if ( !(wp->name) ) + return; + vik_coord_to_latlon ( &(wp->coord), &ll ); - s_lat = a_coords_dtostr(ll.lat); - s_lon = a_coords_dtostr(ll.lon); - fprintf ( f, "type=\"waypoint\" latitude=\"%s\" longitude=\"%s\" name=\"%s\"", s_lat, s_lon, wp->name ); - g_free ( s_lat ); - g_free ( s_lon ); + a_coords_dtostr_buffer ( ll.lat, s_lat ); + a_coords_dtostr_buffer ( ll.lon, s_lon ); + gchar *tmp_name = slashdup(wp->name); + fprintf ( f, "type=\"waypoint\" latitude=\"%s\" longitude=\"%s\" name=\"%s\"", s_lat, s_lon, tmp_name ); + g_free ( tmp_name ); if ( wp->altitude != VIK_DEFAULT_ALTITUDE ) { - gchar *s_alt = a_coords_dtostr(wp->altitude); + gchar s_alt[COORDS_STR_BUFFER_SIZE]; + a_coords_dtostr_buffer ( wp->altitude, s_alt ); fprintf ( f, " altitude=\"%s\"", s_alt ); - g_free(s_alt); } + if ( wp->has_timestamp ) + fprintf ( f, " unixtime=\"%ld\"", wp->timestamp ); if ( wp->comment ) { gchar *tmp_comment = slashdup(wp->comment); @@ -523,15 +602,47 @@ static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp fprintf ( f, " description=\"%s\"", tmp_description ); g_free ( tmp_description ); } + if ( wp->source ) + { + gchar *tmp_source = slashdup(wp->source); + fprintf ( f, " source=\"%s\"", tmp_source ); + g_free ( tmp_source ); + } + if ( wp->type ) + { + gchar *tmp_type = slashdup(wp->type); + fprintf ( f, " xtype=\"%s\"", tmp_type ); + g_free ( tmp_type ); + } if ( wp->image ) { - gchar *tmp_image = slashdup(wp->image); - fprintf ( f, " image=\"%s\"", tmp_image ); + gchar *tmp_image = NULL; + gchar *cwd = NULL; + if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) { + cwd = g_get_current_dir(); + if ( cwd ) + tmp_image = g_strdup ( file_GetRelativeFilename ( cwd, wp->image ) ); + } + + // if cwd not available - use image filename as is + // this should be an absolute path as set in thumbnails + if ( !cwd ) + tmp_image = slashdup(wp->image); + + if ( tmp_image ) + fprintf ( f, " image=\"%s\"", tmp_image ); + + g_free ( cwd ); g_free ( tmp_image ); } if ( wp->symbol ) { - fprintf ( f, " symbol=\"%s\"", wp->symbol ); + // Due to changes in garminsymbols - the symbol name is now in Title Case + // However to keep newly generated .vik files better compatible with older Viking versions + // The symbol names will always be lowercase + gchar *tmp_symbol = g_utf8_strdown(wp->symbol, -1); + fprintf ( f, " symbol=\"%s\"", tmp_symbol ); + g_free ( tmp_symbol ); } if ( ! wp->visible ) fprintf ( f, " visible=\"n\"" ); @@ -540,24 +651,27 @@ static void a_gpspoint_write_waypoint ( const gpointer id, const VikWaypoint *wp static void a_gpspoint_write_trackpoint ( VikTrackpoint *tp, TP_write_info_type *write_info ) { - static struct LatLon ll; - gchar *s_lat, *s_lon; + struct LatLon ll; + gchar s_lat[COORDS_STR_BUFFER_SIZE]; + gchar s_lon[COORDS_STR_BUFFER_SIZE]; + gchar s_alt[COORDS_STR_BUFFER_SIZE]; vik_coord_to_latlon ( &(tp->coord), &ll ); FILE *f = write_info->f; - /* TODO: modify a_coords_dtostr() to accept (optional) buffer - * instead of doing malloc/free everytime */ - s_lat = a_coords_dtostr(ll.lat); - s_lon = a_coords_dtostr(ll.lon); + a_coords_dtostr_buffer ( ll.lat, s_lat ); + a_coords_dtostr_buffer ( ll.lon, s_lon ); fprintf ( f, "type=\"%spoint\" latitude=\"%s\" longitude=\"%s\"", write_info->is_route ? "route" : "track", s_lat, s_lon ); - g_free ( s_lat ); - g_free ( s_lon ); + + if ( tp->name ) { + gchar *name = slashdup(tp->name); + fprintf ( f, " name=\"%s\"", name ); + g_free(name); + } if ( tp->altitude != VIK_DEFAULT_ALTITUDE ) { - gchar *s_alt = a_coords_dtostr(tp->altitude); + a_coords_dtostr_buffer ( tp->altitude, s_alt ); fprintf ( f, " altitude=\"%s\"", s_alt ); - g_free(s_alt); } if ( tp->has_timestamp ) fprintf ( f, " unixtime=\"%ld\"", tp->timestamp ); @@ -567,19 +681,35 @@ static void a_gpspoint_write_trackpoint ( VikTrackpoint *tp, TP_write_info_type if (!isnan(tp->speed) || !isnan(tp->course) || tp->nsats > 0) { fprintf ( f, " extended=\"yes\"" ); if (!isnan(tp->speed)) { - gchar *s_speed = a_coords_dtostr(tp->speed); + gchar s_speed[COORDS_STR_BUFFER_SIZE]; + a_coords_dtostr_buffer ( tp->speed, s_speed ); fprintf ( f, " speed=\"%s\"", s_speed ); - g_free(s_speed); } if (!isnan(tp->course)) { - gchar *s_course = a_coords_dtostr(tp->course); + gchar s_course[COORDS_STR_BUFFER_SIZE]; + a_coords_dtostr_buffer ( tp->course, s_course ); fprintf ( f, " course=\"%s\"", s_course ); - g_free(s_course); } if (tp->nsats > 0) fprintf ( f, " sat=\"%d\"", tp->nsats ); if (tp->fix_mode > 0) fprintf ( f, " fix=\"%d\"", tp->fix_mode ); + + if ( tp->hdop != VIK_DEFAULT_DOP ) { + gchar ss[COORDS_STR_BUFFER_SIZE]; + a_coords_dtostr_buffer ( tp->hdop, ss ); + fprintf ( f, " hdop=\"%s\"", ss ); + } + if ( tp->vdop != VIK_DEFAULT_DOP ) { + gchar ss[COORDS_STR_BUFFER_SIZE]; + a_coords_dtostr_buffer ( tp->vdop, ss ); + fprintf ( f, " vdop=\"%s\"", ss ); + } + if ( tp->pdop != VIK_DEFAULT_DOP ) { + gchar ss[COORDS_STR_BUFFER_SIZE]; + a_coords_dtostr_buffer ( tp->pdop, ss ); + fprintf ( f, " pdop=\"%s\"", ss ); + } } fprintf ( f, "\n" ); } @@ -593,7 +723,9 @@ static void a_gpspoint_write_track ( const gpointer id, const VikTrack *trk, FIL if ( !(trk->name) ) return; - fprintf ( f, "type=\"%s\" name=\"%s\"", trk->is_route ? "route" : "track", trk->name); + gchar *tmp_name = slashdup(trk->name); + fprintf ( f, "type=\"%s\" name=\"%s\"", trk->is_route ? "route" : "track", tmp_name ); + g_free ( tmp_name ); if ( trk->comment ) { gchar *tmp = slashdup(trk->comment); @@ -607,10 +739,28 @@ static void a_gpspoint_write_track ( const gpointer id, const VikTrack *trk, FIL g_free ( tmp ); } + if ( trk->source ) { + gchar *tmp = slashdup(trk->source); + fprintf ( f, " source=\"%s\"", tmp ); + g_free ( tmp ); + } + + if ( trk->type ) { + gchar *tmp = slashdup(trk->type); + fprintf ( f, " xtype=\"%s\"", tmp ); + g_free ( tmp ); + } + if ( trk->has_color ) { fprintf ( f, " color=#%.2x%.2x%.2x", (int)(trk->color.red/256),(int)(trk->color.green/256),(int)(trk->color.blue/256)); } + if ( trk->draw_name_mode > 0 ) + fprintf ( f, " draw_name_mode=\"%d\"", trk->draw_name_mode ); + + if ( trk->max_number_dist_labels > 0 ) + fprintf ( f, " number_dist_labels=\"%d\"", trk->max_number_dist_labels ); + if ( ! trk->visible ) { fprintf ( f, " visible=\"n\"" ); }