]> git.street.me.uk Git - andy/viking.git/commitdiff
Basic support of some GPX Metadata values common to GPX1.0 and GPX1.1
authorRob Norris <rw_norris@hotmail.com>
Mon, 28 Oct 2013 00:05:22 +0000 (00:05 +0000)
committerRob Norris <rw_norris@hotmail.com>
Wed, 30 Oct 2013 00:27:39 +0000 (00:27 +0000)
Includes the fields: name, description, author, time, and keywords.
ATM the time is a read-only value loaded from a GPX file (no GUI way to alter it).
If there is no value or it's a new TrackWaypoint layer then the value auto generated from the earliest track or waypoint
 or else from the current time.

src/file.c
src/gpx.c
src/viktrwlayer.c
src/viktrwlayer.h

index e3f1dc4af16ecbb45c252760b3e2e788f055491a..f3f9b4c0ce146a48199b9859a4c775d382f5511d 100644 (file)
@@ -647,6 +647,7 @@ VikLoadType_t a_file_load ( VikAggregateLayer *top, VikViewport *vp, const gchar
     gboolean success = TRUE; // Detect load failures - mainly to remove the layer created as it's not required
 
     VikLayer *vtl = vik_layer_create ( VIK_LAYER_TRW, vp, NULL, FALSE );
+    vik_layer_rename ( vtl, a_file_basename ( filename ) );
 
     // In fact both kml & gpx files start the same as they are in xml
     if ( check_file_ext ( filename, ".kml" ) && check_magic ( f, GPX_MAGIC ) ) {
@@ -677,7 +678,6 @@ VikLoadType_t a_file_load ( VikAggregateLayer *top, VikViewport *vp, const gchar
     }
     else {
       // Complete the setup from the successful load
-      vik_layer_rename ( vtl, a_file_basename ( filename ) );
       vik_layer_post_read ( vtl, vp, TRUE );
       vik_aggregate_layer_add_layer ( top, vtl, FALSE );
       vik_trw_layer_auto_set_view ( VIK_TRW_LAYER(vtl), vp );
index 6a039b03fa809890d3c80f8b5c8351f482c75b26..f42b8cc2a1f5ea607a6e7a8d3858d6af6c1019a0 100644 (file)
--- a/src/gpx.c
+++ b/src/gpx.c
@@ -48,6 +48,11 @@ typedef enum {
         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,
@@ -70,7 +75,7 @@ typedef enum {
         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,
@@ -96,13 +101,27 @@ typedef struct {
 } 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" },
@@ -127,8 +146,8 @@ tag_mapping tag_path_map[] = {
         { 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" },
@@ -168,6 +187,7 @@ GString *c_cdata = NULL;
 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;
@@ -212,6 +232,10 @@ static void gpx_start(VikTrwLayer *vtl, const char *el, const char **attr)
 
   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 ();
@@ -249,6 +273,11 @@ static void gpx_start(VikTrwLayer *vtl, const char *el, const char **attr)
        }
        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:
@@ -296,6 +325,44 @@ static void gpx_end(VikTrwLayer *vtl, const char *el)
 
   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 )
@@ -445,6 +512,11 @@ static void gpx_end(VikTrwLayer *vtl, const char *el)
 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:
@@ -953,6 +1025,38 @@ void a_gpx_write_file ( VikTrwLayer *vtl, FILE *f, GpxWritingOptions *options )
 
   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 ) );
index ddebc9f10d78da0028dd6d0c5b726f82e0169c36..80ecd7459c9e6ffb8e4bda7530a0032ef89790ff 100644 (file)
@@ -149,6 +149,9 @@ struct _VikTrwLayer {
   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;
@@ -481,8 +484,8 @@ enum {
 
 /****** 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 };
@@ -560,6 +563,13 @@ static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT
 
 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 },
@@ -604,6 +614,11 @@ VikLayerParam trw_layer_params[] = {
   { VIK_LAYER_TRW, "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, &params_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, &params_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, &params_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
@@ -647,6 +662,11 @@ enum {
   PARAM_IS,
   PARAM_IA,
   PARAM_ICS,
+  // Metadata
+  PARAM_MDDESC,
+  PARAM_MDAUTH,
+  PARAM_MDTIME,
+  PARAM_MDKEYS,
   NUM_PARAMS
 };
 
@@ -775,6 +795,28 @@ GType vik_trw_layer_get_type ()
   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;
@@ -1156,6 +1198,12 @@ static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerPara
       }
       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;
 }
@@ -1200,6 +1248,12 @@ static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gbo
     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;
 }
@@ -1267,6 +1321,12 @@ static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values
       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;
@@ -1460,6 +1520,7 @@ static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
   // 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
@@ -10099,6 +10160,64 @@ static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean fro
   //  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 ( &timestamp );
+
+        // 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 ( &timestamp );
+    }
+  }
 }
 
 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
index bf35ba5894040d2d4f37d3807ec67da273a13b89..5a1fc02e263c761ccb01511aadb33e7da0ee8494 100644 (file)
@@ -57,6 +57,19 @@ GType vik_trw_layer_get_type ();
 
 typedef struct _VikTrwLayer VikTrwLayer;
 
+typedef struct {
+  gchar *description;
+  gchar *author;
+  //gboolean has_time;
+  gchar *timestamp; // TODO: Consider storing as proper time_t.
+  gchar *keywords; // TODO: handling/storing a GList of individual tags?
+} VikTRWMetadata;
+
+VikTRWMetadata *vik_trw_metadata_new();
+void vik_trw_metadata_free ( VikTRWMetadata *metadata);
+VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl );
+void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata);
+
 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select );
 
 /* These are meant for use in file loaders (gpspoint.c, gpx.c, etc).