tt_unknown = 0,
tt_gpx,
+ tt_gpx_name,
+ tt_gpx_desc,
+ tt_gpx_author,
+ tt_gpx_time,
+ tt_gpx_keywords,
tt_wpt,
tt_wpt_cmt,
tt_trk_trkseg_trkpt_ele,
tt_trk_trkseg_trkpt_time,
tt_trk_trkseg_trkpt_name,
- /* extended */
+ /* extended */
tt_trk_trkseg_trkpt_course,
tt_trk_trkseg_trkpt_speed,
tt_trk_trkseg_trkpt_fix,
} GpxWritingContext;
/*
- * xpath(ish) mappings between full tag paths and internal identifers.
+ * xpath(ish) mappings between full tag paths and internal identifiers.
* These appear in the order they appear in the GPX specification.
- * If it's not a tag we explictly handle, it doesn't go here.
+ * If it's not a tag we explicitly handle, it doesn't go here.
*/
tag_mapping tag_path_map[] = {
+ { tt_gpx, "/gpx" },
+ { tt_gpx_name, "/gpx/name" },
+ { tt_gpx_desc, "/gpx/desc" },
+ { tt_gpx_time, "/gpx/time" },
+ { tt_gpx_author, "/gpx/author" },
+ { tt_gpx_keywords, "/gpx/keywords" },
+
+ // GPX 1.1 variant - basic properties moved into metadata namespace
+ { tt_gpx_name, "/gpx/metadata/name" },
+ { tt_gpx_desc, "/gpx/metadata/desc" },
+ { tt_gpx_time, "/gpx/metadata/time" },
+ { tt_gpx_author, "/gpx/metadata/author" },
+ { tt_gpx_keywords, "/gpx/metadata/keywords" },
+
{ tt_wpt, "/gpx/wpt" },
{ tt_waypoint, "/loc/waypoint" },
{ tt_trk_trkseg_trkpt_ele, "/gpx/trk/trkseg/trkpt/ele" },
{ tt_trk_trkseg_trkpt_time, "/gpx/trk/trkseg/trkpt/time" },
{ tt_trk_trkseg_trkpt_name, "/gpx/trk/trkseg/trkpt/name" },
- /* extended */
- { tt_trk_trkseg_trkpt_course, "/gpx/trk/trkseg/trkpt/course" },
+ /* extended */
+ { tt_trk_trkseg_trkpt_course, "/gpx/trk/trkseg/trkpt/course" },
{ tt_trk_trkseg_trkpt_speed, "/gpx/trk/trkseg/trkpt/speed" },
{ tt_trk_trkseg_trkpt_fix, "/gpx/trk/trkseg/trkpt/fix" },
{ tt_trk_trkseg_trkpt_sat, "/gpx/trk/trkseg/trkpt/sat" },
VikTrackpoint *c_tp = NULL;
VikWaypoint *c_wp = NULL;
VikTrack *c_tr = NULL;
+VikTRWMetadata *c_md = NULL;
gchar *c_wp_name = NULL;
gchar *c_tr_name = NULL;
switch ( current_tag ) {
+ case tt_gpx:
+ c_md = vik_trw_metadata_new();
+ break;
+
case tt_wpt:
if ( set_c_ll( attr ) ) {
c_wp = vik_waypoint_new ();
}
break;
+ case tt_gpx_name:
+ case tt_gpx_author:
+ case tt_gpx_desc:
+ case tt_gpx_keywords:
+ case tt_gpx_time:
case tt_trk_trkseg_trkpt_name:
case tt_trk_trkseg_trkpt_ele:
case tt_trk_trkseg_trkpt_time:
switch ( current_tag ) {
+ case tt_gpx:
+ vik_trw_layer_set_metadata ( vtl, c_md );
+ c_md = NULL;
+ break;
+
+ case tt_gpx_name:
+ vik_layer_rename ( VIK_LAYER(vtl), c_cdata->str );
+ g_string_erase ( c_cdata, 0, -1 );
+ break;
+
+ case tt_gpx_author:
+ if ( c_md->author )
+ g_free ( c_md->description );
+ c_md->author = g_strdup ( c_cdata->str );
+ g_string_erase ( c_cdata, 0, -1 );
+ break;
+
+ case tt_gpx_desc:
+ if ( c_md->description )
+ g_free ( c_md->description );
+ c_md->description = g_strdup ( c_cdata->str );
+ g_string_erase ( c_cdata, 0, -1 );
+ break;
+
+ case tt_gpx_keywords:
+ if ( c_md->keywords )
+ g_free ( c_md->keywords );
+ c_md->keywords = g_strdup ( c_cdata->str );
+ g_string_erase ( c_cdata, 0, -1 );
+ break;
+
+ case tt_gpx_time:
+ if ( c_md->timestamp )
+ g_free ( c_md->timestamp );
+ c_md->timestamp = g_strdup ( c_cdata->str );
+ g_string_erase ( c_cdata, 0, -1 );
+ break;
+
case tt_waypoint:
case tt_wpt:
if ( ! c_wp_name )
static void gpx_cdata(void *dta, const XML_Char *s, int len)
{
switch ( current_tag ) {
+ case tt_gpx_name:
+ case tt_gpx_author:
+ case tt_gpx_desc:
+ case tt_gpx_keywords:
+ case tt_gpx_time:
case tt_wpt_name:
case tt_trk_name:
case tt_wpt_ele:
gpx_write_header ( f );
+ gchar *tmp;
+ const gchar *name = vik_layer_get_name(VIK_LAYER(vtl));
+ if ( name ) {
+ tmp = entitize ( name );
+ fprintf ( f, " <name>%s</name>\n", tmp );
+ g_free ( tmp );
+ }
+
+ VikTRWMetadata *md = vik_trw_layer_get_metadata (vtl);
+ if ( md ) {
+ if ( md->author ) {
+ tmp = entitize ( md->author );
+ fprintf ( f, " <author>%s</author>\n", tmp );
+ g_free ( tmp );
+ }
+ if ( md->description ) {
+ tmp = entitize ( md->description );
+ fprintf ( f, " <desc>%s</desc>\n", tmp );
+ g_free ( tmp );
+ }
+ if ( md->timestamp ) {
+ tmp = entitize ( md->timestamp );
+ fprintf ( f, " <time>%s</time>\n", tmp );
+ g_free ( tmp );
+ }
+ if ( md->keywords ) {
+ tmp = entitize ( md->keywords );
+ fprintf ( f, " <keywords>%s</keywords>\n", tmp );
+ g_free ( tmp );
+ }
+ }
+
// gather waypoints in a list, then sort
// g_hash_table_get_values: glib 2.14+
GList *gl = g_hash_table_get_values ( vik_trw_layer_get_waypoints ( vtl ) );
guint8 bg_line_thickness;
vik_layer_sort_order_t track_sort_order;
+ // Metadata
+ VikTRWMetadata *metadata;
+
PangoLayout *tracklabellayout;
font_size_t track_font_size;
gchar *track_fsize_str;
/****** PARAMETERS ******/
-static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced") };
-enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV };
+static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
+enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
+static VikLayerParamData string_default ( void )
+{
+ VikLayerParamData data;
+ data.s = "";
+ return data;
+}
+
VikLayerParam trw_layer_params[] = {
{ VIK_LAYER_TRW, "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
{ VIK_LAYER_TRW, "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
{ VIK_LAYER_TRW, "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[3], NULL, NULL, image_size_default, NULL, NULL },
{ VIK_LAYER_TRW, "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[4], NULL, NULL, image_alpha_default, NULL, NULL },
{ VIK_LAYER_TRW, "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[5], NULL, NULL, image_cache_size_default, NULL, NULL },
+
+ { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
+ { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
+ { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
+ { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
};
// ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
PARAM_IS,
PARAM_IA,
PARAM_ICS,
+ // Metadata
+ PARAM_MDDESC,
+ PARAM_MDAUTH,
+ PARAM_MDTIME,
+ PARAM_MDKEYS,
NUM_PARAMS
};
return vtl_type;
}
+VikTRWMetadata *vik_trw_metadata_new()
+{
+ return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
+}
+
+void vik_trw_metadata_free ( VikTRWMetadata *metadata)
+{
+ g_free (metadata);
+}
+
+VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
+{
+ return vtl->metadata;
+}
+
+void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
+{
+ if ( vtl->metadata )
+ vik_trw_metadata_free ( vtl->metadata );
+ vtl->metadata = metadata;
+}
+
typedef struct {
gboolean found;
const gchar *date_str;
}
break;
case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
+ // Metadata
+ case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
+ case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
+ case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
+ case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
+ default: break;
}
return TRUE;
}
case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
+ // Metadata
+ case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
+ case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
+ case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
+ case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
+ default: break;
}
return rv;
}
if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
break;
}
+ case PARAM_MDTIME: {
+ // Force metadata->timestamp to be always read-only for now.
+ GtkWidget **ww = values[UI_CHG_WIDGETS];
+ GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
+ if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
+ }
// NB Since other track settings have been split across tabs,
// I don't think it's useful to set sensitivities on widgets you can't immediately see
default: break;
// Force to on after processing params (which defaults them to off with a zero value)
rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
+ rv->metadata = vik_trw_metadata_new ();
rv->draw_sync_done = TRUE;
rv->draw_sync_do = TRUE;
// Everything else is 0, FALSE or NULL
// since the sorting of a treeview section is now very quick
// NB sorting is also performed after every name change as well to maintain the list order
trw_layer_sort_all ( vtl );
+
+ // Setting metadata time if not otherwise set
+ if ( vtl->metadata ) {
+
+ gboolean need_to_set_time = TRUE;
+ if ( vtl->metadata->timestamp ) {
+ need_to_set_time = FALSE;
+ if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
+ need_to_set_time = TRUE;
+ }
+
+ if ( need_to_set_time ) {
+ // Could rewrite this as a general get first time of a TRW Layer function
+ GTimeVal timestamp;
+ timestamp.tv_usec = 0;
+ gboolean has_timestamp = FALSE;
+
+ GList *gl = NULL;
+ gl = g_hash_table_get_values ( vtl->tracks );
+ gl = g_list_sort ( gl, vik_track_compare_timestamp );
+ gl = g_list_first ( gl );
+
+ // Check times of tracks
+ if ( gl ) {
+ // Only need to check the first track as they have been sorted by time
+ VikTrack *trk = (VikTrack*)gl->data;
+ // Assume trackpoints already sorted by time
+ VikTrackpoint *tpt = vik_track_get_tp_first(trk);
+ if ( tpt && tpt->has_timestamp ) {
+ timestamp.tv_sec = tpt->timestamp;
+ has_timestamp = TRUE;
+ }
+ g_list_free ( gl );
+ }
+
+ if ( !has_timestamp ) {
+ // 'Last' resort - current time
+ // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
+ g_get_current_time ( ×tamp );
+
+ // Check times of waypoints
+ gl = g_hash_table_get_values ( vtl->waypoints );
+ GList *iter;
+ for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
+ VikWaypoint *wpt = (VikWaypoint*)iter->data;
+ if ( wpt->has_timestamp ) {
+ if ( timestamp.tv_sec > wpt->timestamp ) {
+ timestamp.tv_sec = wpt->timestamp;
+ has_timestamp = TRUE;
+ }
+ }
+ }
+ g_list_free ( gl );
+ }
+
+ vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
+ }
+ }
}
VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )