]> git.street.me.uk Git - andy/viking.git/blobdiff - src/viktrwlayer.c
Fix drawing of copied MBTiles map layers.
[andy/viking.git] / src / viktrwlayer.c
index cbaba6c02c8f496c278fdd6cea1b81377fb426f4..f69e03e4bb7a7904d6d2e789cf4b9e98cbde801d 100644 (file)
@@ -35,6 +35,7 @@
 #include "vikgpslayer.h"
 #include "viktrwlayer_export.h"
 #include "viktrwlayer_tpwin.h"
+#include "viktrwlayer_wpwin.h"
 #include "viktrwlayer_propwin.h"
 #include "viktrwlayer_analysis.h"
 #include "viktrwlayer_tracklist.h"
@@ -47,6 +48,7 @@
 #include "thumbnails.h"
 #include "background.h"
 #include "gpx.h"
+#include "geojson.h"
 #include "babel.h"
 #include "dem.h"
 #include "dems.h"
@@ -58,7 +60,7 @@
 #include "datasources.h"
 #include "datasource_gps.h"
 #include "vikexttool_datasources.h"
-#include "util.h"
+#include "ui_util.h"
 #include "vikutils.h"
 
 #include "vikrouting.h"
@@ -202,10 +204,8 @@ struct _VikTrwLayer {
 
   /* route finder tool */
   gboolean route_finder_started;
-  VikCoord route_finder_coord;
   gboolean route_finder_check_added_track;
   VikTrack *route_finder_added_track;
-  VikTrack *route_finder_current_track;
   gboolean route_finder_append;
 
   gboolean drawlabels;
@@ -390,8 +390,9 @@ static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, Vi
 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp ); 
 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
-static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
-static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
+static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp);
+static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
+static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
 
 static void cached_pixbuf_free ( CachedPixbuf *cp );
 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
@@ -418,7 +419,7 @@ static VikToolInterface trw_layer_tools[] = {
     (VikToolConstructorFunc) tool_new_waypoint_create,    NULL, NULL, NULL,
     (VikToolMouseFunc) tool_new_waypoint_click,    NULL, NULL, (VikToolKeyFunc) NULL,
     FALSE,
-    GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
+    GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf, NULL },
 
   { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
     (VikToolConstructorFunc) tool_new_track_create,       NULL, NULL, NULL,
@@ -427,7 +428,7 @@ static VikToolInterface trw_layer_tools[] = {
     (VikToolMouseFunc) tool_new_track_release,
     (VikToolKeyFunc) tool_new_track_key_press,
     TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
-    GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
+    GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf, NULL },
 
   { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
     (VikToolConstructorFunc) tool_new_route_create,       NULL, NULL, NULL,
@@ -436,7 +437,16 @@ static VikToolInterface trw_layer_tools[] = {
     (VikToolMouseFunc) tool_new_track_release,  //   -> Reuse these track methods on a route
     (VikToolKeyFunc) tool_new_track_key_press,  // -/#
     TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
-    GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
+    GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf, NULL },
+
+  { { "ExtendedRouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
+    (VikToolConstructorFunc) tool_extended_route_finder_create,  NULL, NULL, NULL,
+    (VikToolMouseFunc) tool_extended_route_finder_click,
+    (VikToolMouseMoveFunc) tool_new_track_move, // -\#
+    (VikToolMouseFunc) tool_new_track_release,  //   -> Reuse these track methods on a route
+    (VikToolKeyFunc) tool_extended_route_finder_key_press,
+    TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
+    GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf, NULL },
 
   { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
     (VikToolConstructorFunc) tool_edit_waypoint_create,
@@ -446,7 +456,7 @@ static VikToolInterface trw_layer_tools[] = {
     (VikToolMouseMoveFunc) tool_edit_waypoint_move,
     (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
     FALSE,
-    GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
+    GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf, NULL },
 
   { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
     (VikToolConstructorFunc) tool_edit_trackpoint_create,
@@ -456,29 +466,24 @@ static VikToolInterface trw_layer_tools[] = {
     (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
     (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
     FALSE,
-    GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
+    GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf, NULL },
 
   { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
     (VikToolConstructorFunc) tool_show_picture_create,    NULL, NULL, NULL,
     (VikToolMouseFunc) tool_show_picture_click,    NULL, NULL, (VikToolKeyFunc) NULL,
     FALSE,
-    GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
+    GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf, NULL },
 
-  { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
-    (VikToolConstructorFunc) tool_route_finder_create,  NULL, NULL, NULL,
-    (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
-    FALSE,
-    GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
 };
 
 enum {
   TOOL_CREATE_WAYPOINT=0,
   TOOL_CREATE_TRACK,
   TOOL_CREATE_ROUTE,
+  TOOL_ROUTE_FINDER,
   TOOL_EDIT_WAYPOINT,
   TOOL_EDIT_TRACKPOINT,
   TOOL_SHOW_PICTURE,
-  TOOL_ROUTE_FINDER,
   NUM_TOOLS
 };
 
@@ -705,7 +710,7 @@ static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *i
 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
-static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
+static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t *t );
 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
 /* End Layer Interface function definitions */
@@ -772,26 +777,48 @@ VikLayerInterface vik_trw_layer_interface = {
 };
 
 static gboolean have_diary_program = FALSE;
+static gchar *diary_program = NULL;
+#define VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM "external_diary_program"
+
+static gboolean have_geojson_export = FALSE;
+
+static gboolean have_astro_program = FALSE;
+static gchar *astro_program = NULL;
+#define VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM "external_astro_program"
 
 // NB Only performed once per program run
 static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
 {
-  if ( g_find_program_in_path( "rednotebook" ) ) {
-    gchar *stdout = NULL;
-    gchar *stderr = NULL;
+  if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM, &diary_program ) ) {
+#ifdef WINDOWS
+    //diary_program = g_strdup ( "C:\\Program Files\\Rednotebook\\rednotebook.exe" );
+    diary_program = g_strdup ( "C:/Progra~1/Rednotebook/rednotebook.exe" );
+#else
+    diary_program = g_strdup ( "rednotebook" );
+#endif
+  }
+  else {
+    // User specified so assume it works
+    have_diary_program = TRUE;
+  }
+
+  if ( g_find_program_in_path( diary_program ) ) {
+    gchar *mystdout = NULL;
+    gchar *mystderr = NULL;
     // Needs RedNotebook 1.7.3+ for support of opening on a specified date
-    if ( g_spawn_command_line_sync ( "rednotebook --version", &stdout, &stderr, NULL, NULL ) ) {
+    gchar *cmd = g_strconcat ( diary_program, " --version", NULL ); // "rednotebook --version"
+    if ( g_spawn_command_line_sync ( cmd, &mystdout, &mystderr, NULL, NULL ) ) {
       // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
-      if ( stdout )
-        g_debug ("Diary: %s", stdout ); // Should be something like 'RedNotebook 1.4'
-      if ( stderr )
-        g_warning ("Diary: stderr: %s", stderr );
+      if ( mystdout )
+        g_debug ("Diary: %s", mystdout ); // Should be something like 'RedNotebook 1.4'
+      if ( mystderr )
+        g_warning ("Diary: stderr: %s", mystderr );
 
       gchar **tokens = NULL;
-      if ( stdout && g_strcmp0(stdout, "") )
-        tokens = g_strsplit(stdout, " ", 0);
-      else if ( stderr )
-        tokens = g_strsplit(stderr, " ", 0);
+      if ( mystdout && g_strcmp0(mystdout, "") )
+        tokens = g_strsplit(mystdout, " ", 0);
+      else if ( mystderr )
+        tokens = g_strsplit(mystderr, " ", 0);
 
       gint num = 0;
       gchar *token = tokens[num];
@@ -805,8 +832,30 @@ static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
       }
       g_strfreev ( tokens );
     }
-    g_free ( stdout );
-    g_free ( stderr );
+    g_free ( mystdout );
+    g_free ( mystderr );
+    g_free ( cmd );
+  }
+
+  if ( g_find_program_in_path ( a_geojson_program_export() ) ) {
+    have_geojson_export = TRUE;
+  }
+
+  // Astronomy Domain
+  if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM, &astro_program ) ) {
+#ifdef WINDOWS
+    //astro_program = g_strdup ( "C:\\Program Files\\Stellarium\\stellarium.exe" );
+    astro_program = g_strdup ( "C:/Progra~1/Stellarium/stellarium.exe" );
+#else
+    astro_program = g_strdup ( "stellarium" );
+#endif
+  }
+  else {
+    // User specified so assume it works
+    have_astro_program = TRUE;
+  }
+  if ( g_find_program_in_path( astro_program ) ) {
+    have_astro_program = TRUE;
   }
 }
 
@@ -1729,6 +1778,9 @@ static gdouble distance_in_preferred_units ( gdouble dist )
   case VIK_UNITS_DISTANCE_MILES:
     mydist = VIK_METERS_TO_MILES(dist);
     break;
+  case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
+    mydist = VIK_METERS_TO_NAUTICAL_MILES(dist);
+    break;
   // VIK_UNITS_DISTANCE_KILOMETRES:
   default:
     mydist = dist/1000.0;
@@ -1774,6 +1826,9 @@ static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk
     case VIK_UNITS_DISTANCE_MILES:
       dist_i = VIK_MILES_TO_METERS(dist_i);
       break;
+    case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
+      dist_i = VIK_NAUTICAL_MILES_TO_METERS(dist_i);
+      break;
       // VIK_UNITS_DISTANCE_KILOMETRES:
     default:
       dist_i = dist_i*1000.0;
@@ -2334,6 +2389,7 @@ static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct
         case WP_SYMBOL_CIRCLE: vik_viewport_draw_arc ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - dp->vtl->wp_size, y - dp->vtl->wp_size, dp->vtl->wp_size, dp->vtl->wp_size, 0, 360*64 ); break;
         case WP_SYMBOL_X: vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y - dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y + dp->vtl->wp_size*2 );
                           vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y + dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y - dp->vtl->wp_size*2 );
+        default: break;
       }
     }
     else {
@@ -2343,6 +2399,7 @@ static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct
         case WP_SYMBOL_CIRCLE: vik_viewport_draw_arc ( dp->vp, dp->vtl->waypoint_gc, TRUE, x-dp->vtl->wp_size/2, y-dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size, 0, 360*64 ); break;
         case WP_SYMBOL_X: vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x-dp->vtl->wp_size, y-dp->vtl->wp_size, x+dp->vtl->wp_size, y+dp->vtl->wp_size );
                           vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x-dp->vtl->wp_size, y+dp->vtl->wp_size, x+dp->vtl->wp_size, y-dp->vtl->wp_size ); break;
+        default: break;
       }
     }
 
@@ -2722,6 +2779,7 @@ static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype
       else
         return TRUE;
     }
+    default: break;
   }
   return TRUE;
 }
@@ -2750,31 +2808,32 @@ static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_
   tt->length = tt->length + vik_track_get_length (tr);
 
   // Ensure times are available
-  if ( tr->trackpoints &&
-       vik_track_get_tp_first(tr)->has_timestamp &&
-       vik_track_get_tp_last(tr)->has_timestamp ) {
+  if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
+    // Get trkpt only once - as using vik_track_get_tp_last() iterates whole track each time
+    VikTrackpoint *trkpt_last = vik_track_get_tp_last(tr);
+    if ( trkpt_last->has_timestamp ) {
+      time_t t1, t2;
+      t1 = vik_track_get_tp_first(tr)->timestamp;
+      t2 = trkpt_last->timestamp;
 
-    time_t t1, t2;
-    t1 = vik_track_get_tp_first(tr)->timestamp;
-    t2 = vik_track_get_tp_last(tr)->timestamp;
+      // Assume never actually have a track with a time of 0 (1st Jan 1970)
+      // Hence initialize to the first 'proper' value
+      if ( tt->start_time == 0 )
+        tt->start_time = t1;
+      if ( tt->end_time == 0 )
+        tt->end_time = t2;
 
-    // Assume never actually have a track with a time of 0 (1st Jan 1970)
-    // Hence initialize to the first 'proper' value
-    if ( tt->start_time == 0 )
-       tt->start_time = t1;
-    if ( tt->end_time == 0 )
-       tt->end_time = t2;
+      // Update find the earliest / last times
+      if ( t1 < tt->start_time )
+        tt->start_time = t1;
+      if ( t2 > tt->end_time )
+        tt->end_time = t2;
 
-    // Update find the earliest / last times
-    if ( t1 < tt->start_time )
-       tt->start_time = t1;
-    if ( t2 > tt->end_time )
-       tt->end_time = t2;
-
-    // Keep track of total time
-    //  there maybe gaps within a track (eg segments)
-    //  but this should be generally good enough for a simple indicator
-    tt->duration = tt->duration + (int)(t2-t1);
+      // Keep track of total time
+      //  there maybe gaps within a track (eg segments)
+      //  but this should be generally good enough for a simple indicator
+      tt->duration = tt->duration + (int)(t2-t1);
+    }
   }
 }
 
@@ -2802,7 +2861,7 @@ static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
 
   // Safety check - I think these should always be valid
   if ( vtl->tracks && vtl->waypoints ) {
-    tooltip_tracks tt = { 0.0, 0, 0 };
+    tooltip_tracks tt = { 0.0, 0, 0, 0 };
     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
 
     GDate* gdate_start = g_date_new ();
@@ -2828,13 +2887,19 @@ static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
       gdouble len_in_units;
 
       // Setup info dependent on distance units
-      if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
-       g_snprintf (tbuf4, sizeof(tbuf4), "miles");
-       len_in_units = VIK_METERS_TO_MILES(tt.length);
-      }
-      else {
-       g_snprintf (tbuf4, sizeof(tbuf4), "kms");
-       len_in_units = tt.length/1000.0;
+      switch ( a_vik_get_units_distance() ) {
+      case VIK_UNITS_DISTANCE_MILES:
+        g_snprintf (tbuf4, sizeof(tbuf4), "miles");
+        len_in_units = VIK_METERS_TO_MILES(tt.length);
+        break;
+      case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
+        g_snprintf (tbuf4, sizeof(tbuf4), "NM");
+        len_in_units = VIK_METERS_TO_NAUTICAL_MILES(tt.length);
+        break;
+      default:
+        g_snprintf (tbuf4, sizeof(tbuf4), "kms");
+        len_in_units = tt.length/1000.0;
+        break;
       }
 
       // Timing information if available
@@ -2909,11 +2974,9 @@ static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, g
        if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
          // %x     The preferred date representation for the current locale without the time.
          strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
-         if ( vik_track_get_tp_last(tr)->has_timestamp ) {
-           gint dur = ( (vik_track_get_tp_last(tr)->timestamp) - (vik_track_get_tp_first(tr)->timestamp) );
-           if ( dur > 0 )
-             g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
-         }
+         time_t dur = vik_track_get_duration ( tr );
+         if ( dur > 0 )
+           g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
        }
        // Get length and consider the appropriate distance units
        gdouble tr_len = vik_track_get_length(tr);
@@ -2925,6 +2988,9 @@ static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, g
        case VIK_UNITS_DISTANCE_MILES:
          g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
          break;
+       case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
+         g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f NM %s"), time_buf1, VIK_METERS_TO_NAUTICAL_MILES(tr_len), time_buf2);
+         break;
        default:
          break;
        }
@@ -2971,13 +3037,18 @@ static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkp
 {
   gchar *statusbar_format_code = NULL;
   gboolean need2free = FALSE;
+  VikTrackpoint *trkpt_prev = NULL;
   if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
     // Otherwise use default
     statusbar_format_code = g_strdup ( "KEATDN" );
     need2free = TRUE;
   }
+  else {
+    // Format code may want to show speed - so may need previous trkpt to work it out
+    trkpt_prev = vik_track_get_tp_prev ( vtl->current_tp_track, trkpt );
+  }
 
-  gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
+  gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, trkpt_prev, vtl->current_tp_track, NAN );
   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
   g_free ( msg );
 
@@ -3374,6 +3445,15 @@ static void trw_layer_export_kml ( menu_array_layer values )
   g_free ( auto_save_name );
 }
 
+static void trw_layer_export_geojson ( menu_array_layer values )
+{
+  gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON );
+
+  vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON );
+
+  g_free ( auto_save_name );
+}
+
 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
 {
   const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
@@ -3602,7 +3682,10 @@ static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface
   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
   VikViewport *vvp =  vik_window_viewport(vw);
 
-  a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
+  vik_datasource_mode_t mode = datasource->mode;
+  if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
+    mode = VIK_DATASOURCE_ADDTOLAYER;
+  a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL );
 }
 
 /*
@@ -3610,7 +3693,6 @@ static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface
  */
 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
 {
-  vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
   trw_layer_acquire ( values, &vik_datasource_gps_interface );
 }
 
@@ -3627,7 +3709,6 @@ static void trw_layer_acquire_routing_cb ( menu_array_layer values )
  */
 static void trw_layer_acquire_url_cb ( menu_array_layer values )
 {
-  vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
   trw_layer_acquire ( values, &vik_datasource_url_interface );
 }
 
@@ -3667,7 +3748,6 @@ static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
 {
   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
 
-  vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
   trw_layer_acquire ( values, &vik_datasource_geotag_interface );
 
   // Reverify thumbnails as they may have changed
@@ -3676,6 +3756,14 @@ static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
 }
 #endif
 
+/*
+ * Acquire into this TRW Layer from any GPS Babel supported file
+ */
+static void trw_layer_acquire_file_cb ( menu_array_layer values )
+{
+  trw_layer_acquire ( values, &vik_datasource_file_interface );
+}
+
 static void trw_layer_gps_upload ( menu_array_layer values )
 {
   menu_array_sublayer data;
@@ -3783,19 +3871,6 @@ static void trw_layer_gps_upload_any ( menu_array_sublayer values )
                  turn_off );
 }
 
-/*
- * Acquire into this TRW Layer from any GPS Babel supported file
- */
-static void trw_layer_acquire_file_cb ( menu_array_layer values )
-{
-  VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
-  VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
-  VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
-  VikViewport *vvp =  vik_window_viewport(vw);
-
-  a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
-}
-
 static void trw_layer_new_wp ( menu_array_layer values )
 {
   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
@@ -3877,6 +3952,7 @@ static void trw_layer_finish_track ( menu_array_layer values )
 {
   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
   vtl->current_track = NULL;
+  vtl->route_finder_started = FALSE;
   vik_layer_emit_update ( VIK_LAYER(vtl) );
 }
 
@@ -4030,6 +4106,13 @@ static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer
   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
   gtk_widget_show ( item );
 
+  if ( have_geojson_export ) {
+    item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") );
+    g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_geojson), pass_along );
+    gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
+    gtk_widget_show ( item );
+  }
+
   item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
@@ -4431,9 +4514,19 @@ void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypo
 
 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
 {
-  if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
+  if ( vtl->route_finder_append && vtl->current_track ) {
     vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
-    vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
+
+    // enforce end of current track equal to start of tr
+    VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
+    VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
+    if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
+        vik_track_add_trackpoint ( vtl->current_track,
+                                   vik_trackpoint_copy ( cur_end ),
+                                   FALSE );
+    }
+
+    vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
     vik_track_free ( tr );
     vtl->route_finder_append = FALSE; /* this means we have added it */
   } else {
@@ -4574,13 +4667,11 @@ gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
       vtl->current_tp_track = NULL;
       vtl->current_tp_id = NULL;
       vtl->moving_tp = FALSE;
+      vtl->route_finder_started = FALSE;
     }
 
     was_visible = trk->visible;
 
-    if ( trk == vtl->route_finder_current_track )
-      vtl->route_finder_current_track = NULL;
-
     if ( trk == vtl->route_finder_added_track )
       vtl->route_finder_added_track = NULL;
 
@@ -4629,9 +4720,6 @@ gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
 
     was_visible = trk->visible;
 
-    if ( trk == vtl->route_finder_current_track )
-      vtl->route_finder_current_track = NULL;
-
     if ( trk == vtl->route_finder_added_track )
       vtl->route_finder_added_track = NULL;
 
@@ -4802,7 +4890,6 @@ void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
 {
 
   vtl->current_track = NULL;
-  vtl->route_finder_current_track = NULL;
   vtl->route_finder_added_track = NULL;
   if (vtl->current_tp_track)
     trw_layer_cancel_current_tp(vtl, FALSE);
@@ -4820,7 +4907,6 @@ void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
 {
 
   vtl->current_track = NULL;
-  vtl->route_finder_current_track = NULL;
   vtl->route_finder_added_track = NULL;
   if (vtl->current_tp_track)
     trw_layer_cancel_current_tp(vtl, FALSE);
@@ -4897,6 +4983,7 @@ static void trw_layer_delete_item ( menu_array_sublayer values )
             wp->name ) )
           return;
       was_visible = trw_layer_delete_waypoint ( vtl, wp );
+      trw_layer_calculate_bounds_waypoints ( vtl );
     }
   }
   else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
@@ -5226,15 +5313,13 @@ static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values
   VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
   if ( !track )
     return;
-  if ( !track->trackpoints )
-    return;
 
   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
-  vtl->route_finder_coord = vik_track_get_tp_last(track)->coord;
-  vtl->route_finder_current_track = track;
+  vtl->current_track = track;
   vtl->route_finder_started = TRUE;
 
-  goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vtl->route_finder_coord );
+  if ( track->trackpoints )
+      goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
 }
 
 /**
@@ -5644,16 +5729,22 @@ static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpoint
   *(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] 
+/**
+ * find_nearby_tracks_by_time:
+ *
+ * Called for each track in track hash table.
+ *  If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
+ *  to the current track, then the current track is added to the 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];
+  VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
+
+  if ( !orig_trk || !orig_trk->trackpoints )
+    return;
 
   /* outline: 
    * detect reasons for not merging, and return
@@ -5666,12 +5757,13 @@ static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer u
     return;
   }
 
-  t1 = vik_track_get_tp_first(trk)->timestamp;
-  t2 = vik_track_get_tp_last(trk)->timestamp;
+  time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
+  time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
 
   if (trk->trackpoints) {
-    p1 = vik_track_get_tp_first(trk);
-    p2 = vik_track_get_tp_last(trk);
+
+    VikTrackpoint *p1 = vik_track_get_tp_first(trk);
+    VikTrackpoint *p2 = vik_track_get_tp_last(trk);
 
     if (!p1->has_timestamp || !p2->has_timestamp) {
       //g_print("no timestamp\n");
@@ -6073,7 +6165,7 @@ static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
     }
 
     params[0] = &nearby_tracks;
-    params[1] = (gpointer)trps;
+    params[1] = orig_trk;
     params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
 
     /* get a list of adjacent-in-time tracks */
@@ -6547,14 +6639,17 @@ static void trw_layer_reverse ( menu_array_sublayer values )
 }
 
 /**
- * Open a diary at the specified date
+ * Open a program at the specified date
+ * Mainly for RedNotebook - http://rednotebook.sourceforge.net/
+ * But could work with any program that accepts a command line of --date=<date>
+ * FUTURE: Allow configuring of command line options + date format
  */
 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
 {
   GError *err = NULL;
-  gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
+  gchar *cmd = g_strdup_printf ( "%s %s%s", diary_program, "--date=", date_str );
   if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
-    a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
+    a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), diary_program );
     g_error_free ( err );
   }
   g_free ( cmd );
@@ -6597,6 +6692,131 @@ static void trw_layer_diary ( menu_array_sublayer values )
   }
 }
 
+/**
+ * Open a program at the specified date
+ * Mainly for Stellarium - http://stellarium.org/
+ * But could work with any program that accepts the same command line options...
+ * FUTURE: Allow configuring of command line options + format or parameters
+ */
+static void trw_layer_astro_open ( VikTrwLayer *vtl, const gchar *date_str, const gchar *time_str, const gchar *lat_str, const gchar *lon_str, const gchar *alt_str )
+{
+  GError *err = NULL;
+  gchar *tmp;
+  g_file_open_tmp ( "vik-astro-XXXXXX.ini", &tmp, NULL );
+  gchar *cmd = g_strdup_printf ( "%s %s %s %s %s %s %s %s %s %s %s %s %s %s",
+                                  astro_program, "-c", tmp, "--full-screen no", "--sky-date", date_str, "--sky-time", time_str, "--latitude", lat_str, "--longitude", lon_str, "--altitude", alt_str );
+  g_warning ( "%s", cmd );
+  if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
+    a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s"), astro_program );
+    g_warning ( "%s", err->message );
+    g_error_free ( err );
+  }
+  util_add_to_deletion_list ( tmp );
+  g_free ( tmp );
+  g_free ( cmd );
+}
+
+// Format of stellarium lat & lon seems designed to be particularly awkward
+//  who uses ' & " in the parameters for the command line?!
+// -1d4'27.48"
+// +53d58'16.65"
+static gchar *convert_to_dms ( gdouble dec )
+{
+  gdouble tmp;
+  gchar sign_c = ' ';
+  gint val_d, val_m;
+  gdouble val_s;
+  gchar *result = NULL;
+
+  if ( dec > 0 )
+    sign_c = '+';
+  else if ( dec < 0 )
+    sign_c = '-';
+  else // Nul value
+    sign_c = ' ';
+
+  // Degrees
+  tmp = fabs(dec);
+  val_d = (gint)tmp;
+
+  // Minutes
+  tmp = (tmp - val_d) * 60;
+  val_m = (gint)tmp;
+
+  // Seconds
+  val_s = (tmp - val_m) * 60;
+
+  // Format
+  result = g_strdup_printf ( "%c%dd%d\\\'%.4f\\\"", sign_c, val_d, val_m, val_s );
+  return result;
+}
+
+/**
+ * Open an astronomy program at the date & position of the track center, trackpoint or waypoint
+ */
+static void trw_layer_astro ( menu_array_sublayer values )
+{
+  VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
+
+  if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
+    VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
+    if ( ! trk )
+      return;
+
+    VikTrackpoint *tp = NULL;
+    if ( vtl->current_tpl )
+      // Current Trackpoint
+      tp = VIK_TRACKPOINT(vtl->current_tpl->data);
+    else if ( trk->trackpoints )
+      // Otherwise first trackpoint
+      tp = VIK_TRACKPOINT(trk->trackpoints->data);
+    else
+      // Give up
+      return;
+
+    if ( tp->has_timestamp ) {
+      gchar date_buf[20];
+      strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(tp->timestamp)));
+      gchar time_buf[20];
+      strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(tp->timestamp)));
+      struct LatLon ll;
+      vik_coord_to_latlon ( &tp->coord, &ll );
+      gchar *lat_str = convert_to_dms ( ll.lat );
+      gchar *lon_str = convert_to_dms ( ll.lon );
+      gchar alt_buf[20];
+      snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(tp->altitude) );
+      trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf);
+      g_free ( lat_str );
+      g_free ( lon_str );
+    }
+    else
+      a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
+  }
+  else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
+    VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
+    if ( ! wpt )
+      return;
+
+    if ( wpt->has_timestamp ) {
+      gchar date_buf[20];
+      strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(wpt->timestamp)));
+      gchar time_buf[20];
+      strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(wpt->timestamp)));
+      struct LatLon ll;
+      vik_coord_to_latlon ( &wpt->coord, &ll );
+      gchar *lat_str = convert_to_dms ( ll.lat );
+      gchar *lon_str = convert_to_dms ( ll.lon );
+      gchar alt_buf[20];
+      snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(wpt->altitude) );
+      trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf );
+      g_free ( lat_str );
+      g_free ( lon_str );
+    }
+    else
+      a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
+  }
+}
+
 /**
  * Similar to trw_layer_enum_item, but this uses a sorted method
  */
@@ -7380,7 +7600,9 @@ static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
   VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
   if ( !wp )
     return;
-  if ( !strncmp(wp->comment, "http", 4) ) {
+  if ( wp->url ) {
+    open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
+  } else if ( !strncmp(wp->comment, "http", 4) ) {
     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
   } else if ( !strncmp(wp->description, "http", 4) ) {
     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
@@ -7646,7 +7868,8 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men
 
       if ( wp )
       {
-        if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
+        if ( wp->url ||
+             ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
              ( wp->description && !strncmp(wp->description, "http", 4) )) {
           item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
           gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
@@ -8260,12 +8483,28 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men
   }
 
   // Only made available if a suitable program is installed
-  if ( have_diary_program ) {
-    if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
-      item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
+  if ( (have_astro_program || have_diary_program) &&
+       (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) ) {
+    GtkWidget *external_submenu;
+    external_submenu = gtk_menu_new ();
+    item = gtk_image_menu_item_new_with_mnemonic ( _("Externa_l") );
+    gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU) );
+    gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+    gtk_widget_show ( item );
+    gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), external_submenu );
+
+    if ( have_diary_program ) {
+      item = gtk_image_menu_item_new_with_mnemonic ( _("_Diary") );
       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
-      gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
+      gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
+      gtk_widget_show ( item );
+    }
+
+    if ( have_astro_program ) {
+      item = gtk_image_menu_item_new_with_mnemonic ( _("_Astronomy") );
+      g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_astro), pass_along );
+      gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
       gtk_widget_show ( item );
     }
   }
@@ -8460,6 +8699,16 @@ static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
   }
 }
 
+static void my_tpwin_set_tp ( VikTrwLayer *vtl )
+{
+  VikTrack *trk = vtl->current_tp_track;
+  VikCoord vc;
+  // Notional center of a track is simply an average of the bounding box extremities
+  struct LatLon center = { (trk->bbox.north+trk->bbox.south)/2, (trk->bbox.east+trk->bbox.west)/2 };
+  vik_coord_load_from_latlon ( &vc, vtl->coord_mode, &center );
+  vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, trk->name, vtl->current_tp_track->is_route );
+}
+
 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
 {
   g_assert ( vtl->tpwin != NULL );
@@ -8472,7 +8721,7 @@ static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
   if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
   {
     trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
-    vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
+    my_tpwin_set_tp ( vtl );
   }
   else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
   {
@@ -8486,20 +8735,24 @@ static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
 
     if ( vtl->current_tpl )
       // Reset dialog with the available adjacent trackpoint
-      vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
+      my_tpwin_set_tp ( vtl );
 
     vik_layer_emit_update(VIK_LAYER(vtl));
   }
   else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
   {
-    if ( vtl->current_tp_track )
-      vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
+    if ( vtl->current_tp_track ) {
+      vtl->current_tpl = vtl->current_tpl->next;
+      my_tpwin_set_tp ( vtl );
+    }
     vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
   }
   else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
   {
-    if ( vtl->current_tp_track )
-      vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
+    if ( vtl->current_tp_track ) {
+      vtl->current_tpl = vtl->current_tpl->prev;
+      my_tpwin_set_tp ( vtl );
+    }
     vik_layer_emit_update(VIK_LAYER(vtl));
   }
   else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
@@ -8626,7 +8879,7 @@ static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
 
   if ( vtl->current_tpl )
     if ( vtl->current_tp_track )
-      vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
+      my_tpwin_set_tp ( vtl );
   /* set layer name and TP data */
 }
 
@@ -8755,7 +9008,7 @@ static void marker_moveto ( tool_ed_t *t, gint x, gint y );
 static void marker_end_move ( tool_ed_t *t );
 //
 
-static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
+static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t* t )
 {
   if ( t->holding ) {
     VikCoord new_coord;
@@ -8795,6 +9048,11 @@ static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *eve
 {
   if ( t->holding && event->button == 1 )
   {
+    // Prevent accidental (small) shifts when specific movement has not been requested
+    //  (as the click release has occurred within the click object detection area)
+    if ( !t->moving )
+      return FALSE;
+
     VikCoord new_coord;
     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
 
@@ -8834,7 +9092,7 @@ static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *eve
 
         if ( vtl->tpwin )
           if ( vtl->current_tp_track )
-            vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
+            my_tpwin_set_tp ( vtl );
         // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
       }
     }
@@ -8949,7 +9207,7 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event
       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
 
       if ( vtl->tpwin )
-       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
+        my_tpwin_set_tp ( vtl );
 
       vik_layer_emit_update ( VIK_LAYER(vtl) );
       return TRUE;
@@ -8984,7 +9242,7 @@ static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event
       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
 
       if ( vtl->tpwin )
-       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
+        my_tpwin_set_tp ( vtl );
 
       vik_layer_emit_update ( VIK_LAYER(vtl) );
       return TRUE;
@@ -9117,6 +9375,7 @@ static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
   vik_viewport_sync(t->vvp);
   t->oldx = x;
   t->oldy = y;
+  t->moving = FALSE;
 }
 
 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
@@ -9126,6 +9385,7 @@ static void marker_moveto ( tool_ed_t *t, gint x, gint y )
   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
   t->oldx = x;
   t->oldy = y;
+  t->moving = TRUE;
 
   if (tool_sync_done) {
     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
@@ -9138,6 +9398,7 @@ static void marker_end_move ( tool_ed_t *t )
   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
   g_object_unref ( t->gc );
   t->holding = FALSE;
+  t->moving = FALSE;
 }
 
 /*** Edit waypoint ****/
@@ -9371,6 +9632,15 @@ static gchar* distance_string (gdouble distance)
       g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
     }
     break;
+  case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
+    if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
+      g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
+    } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
+      g_sprintf(str, "%d yards", (int)(distance*1.0936133));
+    } else {
+      g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
+    }
+    break;
   default:
     // VIK_UNITS_DISTANCE_KILOMETRES
     if (distance >= 1000 && distance < 100000) {
@@ -9571,6 +9841,13 @@ static void undo_trackpoint_add ( VikTrwLayer *vtl )
 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
 {
   if ( vtl->current_track && event->keyval == GDK_Escape ) {
+    // Bin track if only one point as it's not very useful
+    if ( vik_track_get_tp_count(vtl->current_track) == 1 ) {
+      if ( vtl->current_track->is_route )
+        vik_trw_layer_delete_route ( vtl, vtl->current_track );
+      else
+        vik_trw_layer_delete_track ( vtl, vtl->current_track );
+    }
     vtl->current_track = NULL;
     vik_layer_emit_update ( VIK_LAYER(vtl) );
     return TRUE;
@@ -9658,17 +9935,20 @@ static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton
 
 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
 {
+  // if we were running the route finder, cancel it
+  vtl->route_finder_started = FALSE;
+
   // ----------------------------------------------------- if current is a route - switch to new track
   if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
   {
     gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
-    if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
-    {
-      new_track_create_common ( vtl, name );
-      g_free ( name );
+    if ( a_vik_get_ask_for_create_track_name() ) {
+      name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE );
+      if ( !name )
+        return FALSE;
     }
-    else
-      return TRUE;
+    new_track_create_common ( vtl, name );
+    g_free ( name );
   }
   return tool_new_track_or_route_click ( vtl, event, vvp );
 }
@@ -9691,16 +9971,21 @@ static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
 
 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
 {
-  // -------------------------- if current is a track - switch to new route
-  if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
+  // if we were running the route finder, cancel it
+  vtl->route_finder_started = FALSE;
+
+  // -------------------------- if current is a track - switch to new route,
+  if ( event->button == 1 && ( ! vtl->current_track ||
+                               (vtl->current_track && !vtl->current_track->is_route ) ) )
   {
     gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
-    if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
-      new_route_create_common ( vtl, name );
-      g_free ( name );
+    if ( a_vik_get_ask_for_create_track_name() ) {
+      name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE );
+      if ( !name )
+        return FALSE;
     }
-    else
-      return TRUE;
+    new_route_create_common ( vtl, name );
+    g_free ( name );
   }
   return tool_new_track_or_route_click ( vtl, event, vvp );
 }
@@ -9886,7 +10171,7 @@ static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton
 
     /* diff dist is diff from orig */
     if ( vtl->tpwin )
-      vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
+      my_tpwin_set_tp ( vtl );
 
     vik_layer_emit_update ( VIK_LAYER(vtl) );
     return TRUE;
@@ -9895,77 +10180,121 @@ static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton
 }
 
 
-/*** Route Finder ***/
-static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
+/*** Extended Route Finder ***/
+
+static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
 {
   return vvp;
 }
 
-static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
+static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
+{
+  VikCoord *new_end;
+  new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
+  if ( new_end ) {
+    g_free ( new_end );
+    vik_layer_emit_update ( VIK_LAYER(vtl) );
+
+    /* remove last ' to:...' */
+    if ( vtl->current_track->comment ) {
+      gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
+      if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
+        gchar *new_comment = g_strndup ( vtl->current_track->comment,
+                                         last_to - vtl->current_track->comment - 1);
+        vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
+      }
+    }
+  }
+}
+
+
+static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
 {
   VikCoord tmp;
   if ( !vtl ) return FALSE;
   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
-  if ( event->button == 3 && vtl->route_finder_current_track ) {
-    VikCoord *new_end;
-    new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
-    if ( new_end ) {
-      vtl->route_finder_coord = *new_end;
-      g_free ( new_end );
-      vik_layer_emit_update ( VIK_LAYER(vtl) );
-      /* remove last ' to:...' */
-      if ( vtl->route_finder_current_track->comment ) {
-        gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
-        if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
-          gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
-                                           last_to - vtl->route_finder_current_track->comment - 1);
-          vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
-        }
-      }
-    }
+  if ( event->button == 3 && vtl->current_track ) {
+    tool_extended_route_finder_undo ( vtl );
   }
-  else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
+  else if ( event->button == 2 ) {
+     vtl->draw_sync_do = FALSE;
+     return FALSE;
+  }
+  // if we started the track but via undo deleted all the track points, begin again
+  else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
+    return tool_new_track_or_route_click ( vtl, event, vvp );
+  }
+  else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
+            ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
     struct LatLon start, end;
 
-    vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
+    VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
+    vik_coord_to_latlon ( &(tp_start->coord), &start );
     vik_coord_to_latlon ( &(tmp), &end );
-    vtl->route_finder_coord = tmp; /* for continuations */
 
-    /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
-    if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
-      vtl->route_finder_append = TRUE;  // merge tracks. keep started true.
-    } else {
-      vtl->route_finder_check_added_track = TRUE;
-      vtl->route_finder_started = FALSE;
+    vtl->route_finder_started = TRUE;
+    vtl->route_finder_append = TRUE;  // merge tracks. keep started true.
+
+    // update UI to let user know what's going on
+    VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
+    VikRoutingEngine *engine = vik_routing_default_engine ( );
+    if ( ! engine ) {
+        vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
+        return TRUE;
     }
+    gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
+                                   vik_routing_engine_get_label ( engine ),
+                                   start.lat, start.lon, end.lat, end.lon );
+    vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
+    g_free ( msg );
+    vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
 
-    vik_routing_default_find ( vtl, start, end);
 
-    /* see if anything was done -- a track was added or appended to */
-    if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
-      vik_track_set_comment_no_copy ( vtl->route_finder_added_track, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
-    } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
-      /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
-      gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
-      vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
-    }
+    /* Give GTK a change to display the new status bar before querying the web */
+    while ( gtk_events_pending ( ) )
+        gtk_main_iteration ( );
 
-    if ( vtl->route_finder_added_track )
-      vik_track_calculate_bounds ( vtl->route_finder_added_track );
+    gboolean find_status = vik_routing_default_find ( vtl, start, end );
 
-    vtl->route_finder_added_track = NULL;
-    vtl->route_finder_check_added_track = FALSE;
-    vtl->route_finder_append = FALSE;
+    /* Update UI to say we're done */
+    vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
+    msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
+                            vik_routing_engine_get_label ( engine ),
+                            start.lat, start.lon, end.lat, end.lon )
+                          : g_strdup_printf ( _("Error getting route from %s."),
+                                              vik_routing_engine_get_label ( engine ) );
+    vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
+    g_free ( msg );
 
     vik_layer_emit_update ( VIK_LAYER(vtl) );
   } else {
+    vtl->current_track = NULL;
+
+    // create a new route where we will add the planned route to
+    gboolean ret = tool_new_route_click( vtl, event, vvp );
+
     vtl->route_finder_started = TRUE;
-    vtl->route_finder_coord = tmp;
-    vtl->route_finder_current_track = NULL;
+
+    return ret;
   }
   return TRUE;
 }
 
+static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
+{
+  if ( vtl->current_track && event->keyval == GDK_Escape ) {
+    vtl->route_finder_started = FALSE;
+    vtl->current_track = NULL;
+    vik_layer_emit_update ( VIK_LAYER(vtl) );
+    return TRUE;
+  } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
+    tool_extended_route_finder_undo ( vtl );
+  }
+  return FALSE;
+}
+
+
+
 /*** Show picture ****/
 
 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
@@ -10489,16 +10818,16 @@ void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, g
     g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
 
   if (fillins) {
-    GList *iter = fillins;
-    while (iter) {
-      cur_coord = (VikCoord *)(iter->data);
+    GList *fiter = fillins;
+    while (fiter) {
+      cur_coord = (VikCoord *)(fiter->data);
       vik_coord_set_area(cur_coord, &wh, &tl, &br);
       rect = g_malloc(sizeof(Rect));
       rect->tl = tl;
       rect->br = br;
       rect->center = *cur_coord;
       rects_to_download = g_list_prepend(rects_to_download, rect);
-      iter = iter->next;
+      fiter = fiter->next;
     }
   }