+ VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
+
+ vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
+ vtl->route_finder_coord = last_coord;
+ vtl->route_finder_current_track = track;
+ vtl->route_finder_started = TRUE;
+
+ if ( track->trackpoints )
+ goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
+
+}
+#endif
+
+static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
+{
+ /* TODO: check & warn if no DEM data, or no applicable DEM data. */
+ /* Also warn if overwrite old elevation data */
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *track;
+ if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
+ track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
+ else
+ track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+
+ if ( track )
+ vik_track_apply_dem_data ( track );
+}
+
+static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *track;
+ if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
+ track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
+ else
+ track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+
+ if ( !track )
+ return;
+
+ GList *trps = track->trackpoints;
+ if ( !trps )
+ return;
+ trps = g_list_last(trps);
+ goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
+}
+
+static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *track;
+ if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
+ track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
+ else
+ track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+
+ if ( !track )
+ return;
+
+ VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
+ if ( !vtp )
+ return;
+ goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
+}
+
+static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *track;
+ if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
+ track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
+ else
+ track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+
+ if ( !track )
+ return;
+
+ VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
+ if ( !vtp )
+ return;
+ goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
+}
+
+static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *track;
+ if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
+ track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
+ else
+ track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+
+ if ( !track )
+ return;
+
+ VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
+ if ( !vtp )
+ return;
+ goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
+}
+
+/*
+ * Automatically change the viewport to center on the track and zoom to see the extent of the track
+ */
+static void trw_layer_auto_track_view ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *trk;
+ if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
+ trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
+ else
+ trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+
+ if ( trk && trk->trackpoints )
+ {
+ struct LatLon maxmin[2] = { {0,0}, {0,0} };
+ trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
+ trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
+ if ( pass_along[1] )
+ vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
+ else
+ vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
+ }
+}
+
+static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
+ trw_layer_tpwin_init ( vtl );
+}
+
+/*************************************
+ * merge/split by time routines
+ *************************************/
+
+/* called for each key in track hash table.
+ * If the current track has the same time stamp type, add it to the result,
+ * except the one pointed by "exclude".
+ * set exclude to NULL if there is no exclude to check.
+ * Note that the result is in reverse (for performance reasons).
+ */
+typedef struct {
+ GList **result;
+ GList *exclude;
+ gboolean with_timestamps;
+} twt_udata;
+static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
+{
+ twt_udata *user_data = udata;
+ VikTrackpoint *p1, *p2;
+
+ if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
+ return;
+ }
+
+ if (VIK_TRACK(value)->trackpoints) {
+ p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
+ p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
+
+ if ( user_data->with_timestamps ) {
+ if (!p1->has_timestamp || !p2->has_timestamp) {
+ return;
+ }
+ }
+ else {
+ // Don't add tracks with timestamps when getting non timestamp tracks
+ if (p1->has_timestamp || p2->has_timestamp) {
+ return;
+ }
+ }
+ }
+
+ *(user_data->result) = g_list_prepend(*(user_data->result), key);
+}
+
+/* called for each key in track hash table. if original track user_data[1] is close enough
+ * to the passed one, add it to list in user_data[0]
+ */
+static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
+{
+ time_t t1, t2;
+ VikTrackpoint *p1, *p2;
+ VikTrack *trk = VIK_TRACK(value);
+
+ GList **nearby_tracks = ((gpointer *)user_data)[0];
+ GList *tpoints = ((gpointer *)user_data)[1];
+
+ /* outline:
+ * detect reasons for not merging, and return
+ * if no reason is found not to merge, then do it.
+ */
+
+ // Exclude the original track from the compiled list
+ if (trk->trackpoints == tpoints) {
+ return;
+ }
+
+ t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
+ t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
+
+ if (trk->trackpoints) {
+ p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
+ p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
+
+ if (!p1->has_timestamp || !p2->has_timestamp) {
+ //g_print("no timestamp\n");
+ return;
+ }
+
+ guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
+ //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
+ if (! (abs(t1 - p2->timestamp) < threshold ||
+ /* p1 p2 t1 t2 */
+ abs(p1->timestamp - t2) < threshold)
+ /* t1 t2 p1 p2 */
+ ) {
+ return;
+ }
+ }
+
+ *nearby_tracks = g_list_prepend(*nearby_tracks, value);
+}
+
+/* comparison function used to sort tracks; a and b are hash table keys */
+/* Not actively used - can be restored if needed
+static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ GHashTable *tracks = user_data;
+ time_t t1, t2;
+
+ t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
+ t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
+
+ if (t1 < t2) return -1;
+ if (t1 > t2) return 1;
+ return 0;
+}
+*/
+
+/* comparison function used to sort trackpoints */
+static gint trackpoint_compare(gconstpointer a, gconstpointer b)
+{
+ time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
+
+ if (t1 < t2) return -1;
+ if (t1 > t2) return 1;
+ return 0;
+}
+
+/**
+ * comparison function which can be used to sort tracks or waypoints by name
+ */
+static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ const gchar* namea = (const gchar*) a;
+ const gchar* nameb = (const gchar*) b;
+ if ( namea == NULL || nameb == NULL)
+ return 0;
+ else
+ // Same sort method as used in the vik_treeview_*_alphabetize functions
+ return strcmp ( namea, nameb );
+}
+
+/**
+ * Attempt to merge selected track with other tracks specified by the user
+ * Tracks to merge with must be of the same 'type' as the selected track -
+ * either all with timestamps, or all without timestamps
+ */
+static void trw_layer_merge_with_other ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ GList *other_tracks = NULL;
+ GHashTable *ght_tracks;
+ if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
+ ght_tracks = vtl->routes;
+ else
+ ght_tracks = vtl->tracks;
+
+ VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
+
+ if ( !track )
+ return;
+
+ if ( !track->trackpoints )
+ return;
+
+ twt_udata udata;
+ udata.result = &other_tracks;
+ udata.exclude = track->trackpoints;
+ // Allow merging with 'similar' time type time tracks
+ // i.e. either those times, or those without
+ udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
+
+ g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
+ other_tracks = g_list_reverse(other_tracks);
+
+ if ( !other_tracks ) {
+ if ( udata.with_timestamps )
+ a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
+ else
+ a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
+ return;
+ }
+
+ // Sort alphabetically for user presentation
+ // Convert into list of names for usage with dialog function
+ // TODO: Need to consider how to work best when we can have multiple tracks the same name...
+ GList *other_tracks_names = NULL;
+ GList *iter = g_list_first ( other_tracks );
+ while ( iter ) {
+ other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
+ iter = g_list_next ( iter );
+ }
+
+ other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
+
+ GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
+ other_tracks_names,
+ TRUE,
+ _("Merge with..."),
+ track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
+ g_list_free(other_tracks);
+ g_list_free(other_tracks_names);
+
+ if (merge_list)
+ {
+ GList *l;
+ for (l = merge_list; l != NULL; l = g_list_next(l)) {
+ VikTrack *merge_track;
+ if ( track->is_route )
+ merge_track = vik_trw_layer_get_route ( vtl, l->data );
+ else
+ merge_track = vik_trw_layer_get_track ( vtl, l->data );
+
+ if (merge_track) {
+ track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
+ merge_track->trackpoints = NULL;
+ if ( track->is_route )
+ vik_trw_layer_delete_route (vtl, merge_track);
+ else
+ vik_trw_layer_delete_track (vtl, merge_track);
+ track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
+ }
+ }
+ /* TODO: free data before free merge_list */
+ for (l = merge_list; l != NULL; l = g_list_next(l))
+ g_free(l->data);
+ g_list_free(merge_list);
+ vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
+ }
+}
+
+// c.f. trw_layer_sorted_track_id_by_name_list
+// but don't add the specified track to the list (normally current track)
+static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
+{
+ twt_udata *user_data = udata;
+
+ // Skip self
+ if (trk->trackpoints == user_data->exclude) {
+ return;
+ }
+
+ // Sort named list alphabetically
+ *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
+}
+
+/**
+ * Join - this allows combining 'tracks' and 'track routes'
+ * i.e. doesn't care about whether tracks have consistent timestamps
+ * ATM can only append one track at a time to the currently selected track
+ */
+static void trw_layer_append_track ( gpointer pass_along[6] )
+{
+
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *trk;
+ GHashTable *ght_tracks;
+ if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
+ ght_tracks = vtl->routes;
+ else
+ ght_tracks = vtl->tracks;
+
+ trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
+
+ if ( !trk )
+ return;
+
+ GList *other_tracks_names = NULL;
+
+ // Sort alphabetically for user presentation
+ // Convert into list of names for usage with dialog function
+ // TODO: Need to consider how to work best when we can have multiple tracks the same name...
+ twt_udata udata;
+ udata.result = &other_tracks_names;
+ udata.exclude = trk->trackpoints;
+
+ g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
+
+ // Note the limit to selecting one track only
+ // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
+ // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
+ GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
+ other_tracks_names,
+ FALSE,
+ trk->is_route ? _("Append Route"): _("Append Track"),
+ trk->is_route ? _("Select the route to append after the current route") :
+ _("Select the track to append after the current track") );
+
+ g_list_free(other_tracks_names);
+
+ // It's a list, but shouldn't contain more than one other track!
+ if ( append_list ) {
+ GList *l;
+ for (l = append_list; l != NULL; l = g_list_next(l)) {
+ // TODO: at present this uses the first track found by name,
+ // which with potential multiple same named tracks may not be the one selected...
+ VikTrack *append_track;
+ if ( trk->is_route )
+ append_track = vik_trw_layer_get_route ( vtl, l->data );
+ else
+ append_track = vik_trw_layer_get_track ( vtl, l->data );
+
+ if ( append_track ) {
+ trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
+ append_track->trackpoints = NULL;
+ if ( trk->is_route )
+ vik_trw_layer_delete_route (vtl, append_track);
+ else
+ vik_trw_layer_delete_track (vtl, append_track);
+ }
+ }
+ for (l = append_list; l != NULL; l = g_list_next(l))
+ g_free(l->data);
+ g_list_free(append_list);
+ vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
+ }
+}
+
+/**
+ * Very similar to trw_layer_append_track for joining
+ * but this allows selection from the 'other' list
+ * If a track is selected, then is shows routes and joins the selected one
+ * If a route is selected, then is shows tracks and joins the selected one
+ */
+static void trw_layer_append_other ( gpointer pass_along[6] )
+{
+
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *trk;
+ GHashTable *ght_mykind, *ght_others;
+ if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
+ ght_mykind = vtl->routes;
+ ght_others = vtl->tracks;
+ }
+ else {
+ ght_mykind = vtl->tracks;
+ ght_others = vtl->routes;
+ }
+
+ trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
+
+ if ( !trk )
+ return;
+
+ GList *other_tracks_names = NULL;
+
+ // Sort alphabetically for user presentation
+ // Convert into list of names for usage with dialog function
+ // TODO: Need to consider how to work best when we can have multiple tracks the same name...
+ twt_udata udata;
+ udata.result = &other_tracks_names;
+ udata.exclude = trk->trackpoints;
+
+ g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
+
+ // Note the limit to selecting one track only
+ // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
+ // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
+ GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
+ other_tracks_names,
+ FALSE,
+ trk->is_route ? _("Append Track"): _("Append Route"),
+ trk->is_route ? _("Select the track to append after the current route") :
+ _("Select the route to append after the current track") );
+
+ g_list_free(other_tracks_names);
+
+ // It's a list, but shouldn't contain more than one other track!
+ if ( append_list ) {
+ GList *l;
+ for (l = append_list; l != NULL; l = g_list_next(l)) {
+ // TODO: at present this uses the first track found by name,
+ // which with potential multiple same named tracks may not be the one selected...
+
+ // Get FROM THE OTHER TYPE list
+ VikTrack *append_track;
+ if ( trk->is_route )
+ append_track = vik_trw_layer_get_track ( vtl, l->data );
+ else
+ append_track = vik_trw_layer_get_route ( vtl, l->data );
+
+ if ( append_track ) {
+
+ if ( !append_track->is_route &&
+ ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
+ ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
+
+ if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
+ _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
+ vik_track_merge_segments ( append_track );
+ vik_track_to_routepoints ( append_track );
+ }
+ else {
+ break;
+ }
+ }
+
+ trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
+ append_track->trackpoints = NULL;
+
+ // Delete copied which is FROM THE OTHER TYPE list
+ if ( trk->is_route )
+ vik_trw_layer_delete_track (vtl, append_track);
+ else
+ vik_trw_layer_delete_route (vtl, append_track);
+ }
+ }
+ for (l = append_list; l != NULL; l = g_list_next(l))
+ g_free(l->data);
+ g_list_free(append_list);
+ vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
+ }
+}
+
+/* merge by segments */
+static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+ guint segments = vik_track_merge_segments ( trk );
+ // NB currently no need to redraw as segments not actually shown on the display
+ // However inform the user of what happened:
+ gchar str[64];
+ const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
+ g_snprintf(str, 64, tmp_str, segments);
+ a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
+}
+
+/* merge by time routine */
+static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+
+ //time_t t1, t2;
+
+ GList *tracks_with_timestamp = NULL;
+ VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+ if (orig_trk->trackpoints &&
+ !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
+ a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
+ return;
+ }
+
+ twt_udata udata;
+ udata.result = &tracks_with_timestamp;
+ udata.exclude = orig_trk->trackpoints;
+ udata.with_timestamps = TRUE;
+ g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
+ tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
+
+ if (!tracks_with_timestamp) {
+ a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
+ return;
+ }
+ g_list_free(tracks_with_timestamp);
+
+ static guint threshold_in_minutes = 1;
+ if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
+ _("Merge Threshold..."),
+ _("Merge when time between tracks less than:"),
+ &threshold_in_minutes)) {
+ return;
+ }
+
+ // keep attempting to merge all tracks until no merges within the time specified is possible
+ gboolean attempt_merge = TRUE;
+ GList *nearby_tracks = NULL;
+ GList *trps;
+ static gpointer params[3];
+
+ while ( attempt_merge ) {
+
+ // Don't try again unless tracks have changed
+ attempt_merge = FALSE;
+
+ trps = orig_trk->trackpoints;
+ if ( !trps )
+ return;
+
+ if (nearby_tracks) {
+ g_list_free(nearby_tracks);
+ nearby_tracks = NULL;
+ }
+
+ //t1 = ((VikTrackpoint *)trps->data)->timestamp;
+ //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
+
+ /* g_print("Original track times: %d and %d\n", t1, t2); */
+ params[0] = &nearby_tracks;
+ params[1] = (gpointer)trps;
+ params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
+
+ /* get a list of adjacent-in-time tracks */
+ g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
+
+ /* merge them */
+ GList *l = nearby_tracks;
+ while ( l ) {
+ /*
+#define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
+#define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
+ time_t t1, t2;
+ t1 = get_first_trackpoint(l)->timestamp;
+ t2 = get_last_trackpoint(l)->timestamp;
+#undef get_first_trackpoint
+#undef get_last_trackpoint
+ g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
+ */
+
+ /* remove trackpoints from merged track, delete track */
+ orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
+ VIK_TRACK(l->data)->trackpoints = NULL;
+ vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
+
+ // Tracks have changed, therefore retry again against all the remaining tracks
+ attempt_merge = TRUE;
+
+ l = g_list_next(l);
+ }
+
+ orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
+ }
+
+ g_list_free(nearby_tracks);
+ vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
+}
+
+/**
+ * Split a track at the currently selected trackpoint
+ */
+static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
+{
+ if ( !vtl->current_tpl )
+ return;
+
+ if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
+ gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
+ if ( name ) {
+ VikTrack *tr = vik_track_new ();
+ GList *newglist = g_list_alloc ();
+ newglist->prev = NULL;
+ newglist->next = vtl->current_tpl->next;
+ newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
+ tr->trackpoints = newglist;
+ tr->is_route = vtl->current_tp_track->is_route;
+ tr->visible = TRUE;
+
+ vtl->current_tpl->next->prev = newglist; /* end old track here */
+ vtl->current_tpl->next = NULL;
+
+ vtl->current_tpl = newglist; /* change tp to first of new track. */
+ vtl->current_tp_track = tr;
+
+ if ( tr->is_route )
+ vik_trw_layer_add_route ( vtl, name, tr );
+ else
+ vik_trw_layer_add_track ( vtl, name, tr );
+
+ trku_udata udata;
+ udata.trk = tr;
+ udata.uuid = NULL;
+
+ // Also need id of newly created track
+ gpointer *trkf;
+ if ( tr->is_route )
+ trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
+ else
+ trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
+
+ if ( trkf && udata.uuid )
+ vtl->current_tp_id = udata.uuid;
+ else
+ vtl->current_tp_id = NULL;
+
+ vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
+ }
+ }
+}
+
+/* split by time routine */
+static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+ GList *trps = track->trackpoints;
+ GList *iter;
+ GList *newlists = NULL;
+ GList *newtps = NULL;
+ static guint thr = 1;
+
+ time_t ts, prev_ts;
+
+ if ( !trps )
+ return;
+
+ if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
+ _("Split Threshold..."),
+ _("Split when time between trackpoints exceeds:"),
+ &thr)) {
+ return;
+ }
+
+ /* iterate through trackpoints, and copy them into new lists without touching original list */
+ prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
+ iter = trps;
+
+ while (iter) {
+ ts = VIK_TRACKPOINT(iter->data)->timestamp;
+ if (ts < prev_ts) {
+ g_print("panic: ts < prev_ts: this should never happen!\n");
+ return;
+ }
+ if (ts - prev_ts > thr*60) {
+ /* flush accumulated trackpoints into new list */
+ newlists = g_list_append(newlists, g_list_reverse(newtps));
+ newtps = NULL;
+ }
+
+ /* accumulate trackpoint copies in newtps, in reverse order */
+ newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
+ prev_ts = ts;
+ iter = g_list_next(iter);
+ }
+ if (newtps) {
+ newlists = g_list_append(newlists, g_list_reverse(newtps));
+ }
+
+ /* put lists of trackpoints into tracks */
+ iter = newlists;
+ // Only bother updating if the split results in new tracks
+ if (g_list_length (newlists) > 1) {
+ while (iter) {
+ gchar *new_tr_name;
+ VikTrack *tr;
+
+ tr = vik_track_new();
+ tr->visible = track->visible;
+ tr->trackpoints = (GList *)(iter->data);
+
+ new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
+ vik_trw_layer_add_track(vtl, new_tr_name, tr);
+ /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
+ VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
+
+ iter = g_list_next(iter);
+ }
+ // Remove original track and then update the display
+ vik_trw_layer_delete_track (vtl, track);
+ vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
+ }
+ g_list_free(newlists);
+}
+
+/**
+ * Split a track by the number of points as specified by the user
+ */
+static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *track;
+ if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
+ track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
+ else
+ track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+
+ if ( !track )
+ return;
+
+ // Check valid track
+ GList *trps = track->trackpoints;
+ if ( !trps )
+ return;
+
+ gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
+ _("Split Every Nth Point"),
+ _("Split on every Nth point:"),
+ 250, // Default value as per typical limited track capacity of various GPS devices
+ 2, // Min
+ 65536, // Max
+ 5); // Step
+ // Was a valid number returned?
+ if (!points)
+ return;
+
+ // Now split...
+ GList *iter;
+ GList *newlists = NULL;
+ GList *newtps = NULL;
+ gint count = 0;
+ iter = trps;
+
+ while (iter) {
+ /* accumulate trackpoint copies in newtps, in reverse order */
+ newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
+ count++;
+ if (count >= points) {
+ /* flush accumulated trackpoints into new list */
+ newlists = g_list_append(newlists, g_list_reverse(newtps));
+ newtps = NULL;
+ count = 0;
+ }
+ iter = g_list_next(iter);
+ }
+
+ // If there is a remaining chunk put that into the new split list
+ // This may well be the whole track if no split points were encountered
+ if (newtps) {
+ newlists = g_list_append(newlists, g_list_reverse(newtps));
+ }
+
+ /* put lists of trackpoints into tracks */
+ iter = newlists;
+ // Only bother updating if the split results in new tracks
+ if (g_list_length (newlists) > 1) {
+ while (iter) {
+ gchar *new_tr_name;
+ VikTrack *tr;
+
+ tr = vik_track_new();
+ tr->visible = track->visible;
+ tr->is_route = track->is_route;
+ tr->trackpoints = (GList *)(iter->data);
+
+ if ( track->is_route ) {
+ new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
+ vik_trw_layer_add_route(vtl, new_tr_name, tr);
+ }
+ else {
+ new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
+ vik_trw_layer_add_track(vtl, new_tr_name, tr);
+ }
+ iter = g_list_next(iter);
+ }
+ // Remove original track and then update the display
+ if ( track->is_route )
+ vik_trw_layer_delete_route (vtl, track);
+ else
+ vik_trw_layer_delete_track (vtl, track);
+ vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
+ }
+ g_list_free(newlists);
+}
+
+/**
+ * Split a track at the currently selected trackpoint
+ */
+static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ gint subtype = GPOINTER_TO_INT (pass_along[2]);
+ trw_layer_split_at_selected_trackpoint ( vtl, subtype );
+}
+
+/**
+ * Split a track by its segments
+ * Routes do not have segments so don't call this for routes
+ */
+static void trw_layer_split_segments ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+
+ if ( !trk )
+ return;
+
+ guint ntracks;
+
+ VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
+ gchar *new_tr_name;
+ guint i;
+ for ( i = 0; i < ntracks; i++ ) {
+ if ( tracks[i] ) {
+ new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
+ vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
+ }
+ }
+ if ( tracks ) {
+ g_free ( tracks );
+ // Remove original track
+ vik_trw_layer_delete_track ( vtl, trk );
+ vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
+ }
+ else {
+ a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
+ }
+}
+/* end of split/merge routines */
+
+/**
+ * Delete adjacent track points at the same position
+ * AKA Delete Dulplicates on the Properties Window
+ */
+static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *trk;
+ if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
+ trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
+ else
+ trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+
+ if ( !trk )
+ return;
+
+ gulong removed = vik_track_remove_dup_points ( trk );
+
+ // Track has been updated so update tps:
+ trw_layer_cancel_tps_of_track ( vtl, trk );
+
+ // Inform user how much was deleted as it's not obvious from the normal view
+ gchar str[64];
+ const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
+ g_snprintf(str, 64, tmp_str, removed);
+ a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
+
+ vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
+}
+
+/**
+ * Delete adjacent track points with the same timestamp
+ * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
+ */
+static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
+{
+ VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
+ VikTrack *trk;
+ if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
+ trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
+ else
+ trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
+
+ if ( !trk )
+ return;
+
+ gulong removed = vik_track_remove_same_time_points ( trk );
+
+ // Track has been updated so update tps:
+ trw_layer_cancel_tps_of_track ( vtl, trk );