]> git.street.me.uk Git - andy/viking.git/blobdiff - src/vikmapslayer.c
Add map's logo support
[andy/viking.git] / src / vikmapslayer.c
index 597c823c548a482d63ffd7340c708b9f9be39026..e43aa6efe1c138816d7f761f9e85b39e815d6af3 100644 (file)
@@ -2,6 +2,7 @@
  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
  *
  * Copyright (C) 2005, Evan Battaglia <viking@greentorch.org>
+ * Copyright (C) 2010, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
  * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
  * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
  *
@@ -44,6 +45,7 @@
 #endif
 
 #include "globals.h"
+#include "util.h"
 #include "coords.h"
 #include "vikcoord.h"
 #include "viktreeview.h"
@@ -59,6 +61,7 @@
 /* only for dialog.h -- ugh */
 #include "vikwaypoint.h"
 #include "dialog.h"
+#include "preferences.h"
 
 #include "vikstatus.h"
 #include "background.h"
@@ -78,10 +81,10 @@ static GList *__map_types = NULL;
 #define NUM_MAP_TYPES g_list_length(__map_types)
 
 /* List of label for each map type */
-static GList *params_maptypes = NULL;
+static gchar **params_maptypes = NULL;
 
 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
-static GList *params_maptypes_ids = NULL;
+static guint *params_maptypes_ids = NULL;
 
 /******** MAPZOOMS *********/
 
@@ -95,10 +98,11 @@ static gdouble __mapzooms_y[] = { 0.0, 0.25, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.
 
 
 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
+static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
-static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp );
-static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id );
+static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
+static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
 static void maps_layer_free ( VikMapsLayer *vml );
@@ -108,6 +112,7 @@ static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
+static guint map_uniq_id_to_index ( guint uniq_id );
 
 
 static VikLayerParamScale params_scales[] = {
@@ -116,11 +121,11 @@ static VikLayerParamScale params_scales[] = {
 };
 
 VikLayerParam maps_layer_params[] = {
-  { "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL, NULL },
+  { "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL },
   { "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY },
   { "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales },
   { "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload maps:"), VIK_LAYER_WIDGET_CHECKBUTTON },
-  { "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms },
+  { "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL },
 };
 
 enum { PARAM_MAPTYPE=0, PARAM_CACHE_DIR, PARAM_ALPHA, PARAM_AUTODOWNLOAD, PARAM_MAPZOOM, NUM_PARAMS };
@@ -162,6 +167,8 @@ VikLayerInterface vik_maps_layer_interface = {
 
   (VikLayerFuncSublayerRenameRequest)   NULL,
   (VikLayerFuncSublayerToggleVisible)   NULL,
+  (VikLayerFuncSublayerTooltip)         NULL,
+  (VikLayerFuncLayerTooltip)            maps_layer_tooltip,
 
   (VikLayerFuncMarshall)               maps_layer_marshall,
   (VikLayerFuncUnmarshall)             maps_layer_unmarshall,
@@ -173,6 +180,7 @@ VikLayerInterface vik_maps_layer_interface = {
   (VikLayerFuncWriteFileData)           NULL,
 
   (VikLayerFuncDeleteItem)              NULL,
+  (VikLayerFuncCutItem)                 NULL,
   (VikLayerFuncCopyItem)                NULL,
   (VikLayerFuncPasteItem)               NULL,
   (VikLayerFuncFreeCopiedItem)          NULL,
@@ -197,28 +205,58 @@ struct _VikMapsLayer {
   GtkMenu *dl_right_click_menu;
   VikCoord redownload_ul, redownload_br; /* right click menu only */
   VikViewport *redownload_vvp;
+
+  gboolean license_notice_shown; // FALSE for new maps only, otherwise
+                                 // TRUE for saved maps & other layer changes as we don't need to show it again
 };
 
-enum { REDOWNLOAD_NONE = 0, REDOWNLOAD_BAD, REDOWNLOAD_ALL, DOWNLOAD_OR_REFRESH };
+enum { REDOWNLOAD_NONE = 0,    /* download only missing maps */
+       REDOWNLOAD_BAD,         /* download missing and bad maps */
+       REDOWNLOAD_NEW,         /* download missing maps that are newer on server only */
+       REDOWNLOAD_ALL,         /* download all maps */
+       DOWNLOAD_OR_REFRESH };  /* download missing maps and refresh cache */
+
+static VikLayerParam prefs[] = {
+  { VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Default maplayer directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL },
+};
 
+void maps_layer_init ()
+{
+  VikLayerParamData tmp;
+  tmp.s = maps_layer_default_dir();
+  a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
+}
 
 /****************************************/
 /******** MAPS LAYER TYPES **************/
 /****************************************/
 
-void maps_layer_register_map_source ( VikMapSource *map )
+int _get_index_for_id ( guint id )
 {
-  g_assert(map != NULL);
-  
-  guint id = vik_map_source_get_uniq_id(map);
-  const char *label = vik_map_source_get_label(map);
-  g_assert(label != NULL);
+  int index = 0 ;
+  while (params_maptypes_ids[index] != 0)
+  {
+    if (params_maptypes_ids[index] == id)
+      return index;
+    index++;
+  }
+  return -1;
+}
 
+void _add_map_source ( guint id, const char *label, VikMapSource *map )
+{
+  gsize len = 0;
+  if (params_maptypes)
+    len = g_strv_length (params_maptypes);
   /* Add the label */
-  params_maptypes = g_list_append(params_maptypes, g_strdup(label));
+  params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
+  params_maptypes[len] = g_strdup (label);
+  params_maptypes[len+1] = NULL;
 
   /* Add the id */
-  params_maptypes_ids = g_list_append(params_maptypes_ids, GUINT_TO_POINTER (id));
+  params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
+  params_maptypes_ids[len] = id;
+  params_maptypes_ids[len+1] = 0;
 
   /* We have to clone */
   VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
@@ -226,7 +264,7 @@ void maps_layer_register_map_source ( VikMapSource *map )
   __map_types = g_list_append(__map_types, clone);
 
   /* Hack
-     We have to ensure the mode LayerParam reference the up-to-date
+     We have to ensure the mode LayerParam references the up-to-date
      GLists.
   */
   /*
@@ -237,8 +275,37 @@ void maps_layer_register_map_source ( VikMapSource *map )
   maps_layer_params[0].extra_widget_data = params_maptypes_ids;
 }
 
-#define MAPS_LAYER_NTH_LABEL(n) ((gchar*)g_list_nth_data(params_maptypes, (n)))
-#define MAPS_LAYER_NTH_ID(n) ((guint)g_list_nth_data(params_maptypes_ids, (n)))
+void _update_map_source ( const char *label, VikMapSource *map, int index )
+{
+  GList *item = g_list_nth (__map_types, index);
+  g_object_unref (item->data);
+  item->data = g_object_ref (map);
+  /* Change previous data */
+  g_free (params_maptypes[index]);
+  params_maptypes[index] = g_strdup (label);
+}
+
+void maps_layer_register_map_source ( VikMapSource *map )
+{
+  g_assert(map != NULL);
+  
+  guint id = vik_map_source_get_uniq_id(map);
+  const char *label = vik_map_source_get_label(map);
+  g_assert(label != NULL);
+
+  int previous = map_uniq_id_to_index (id);
+  if (previous != NUM_MAP_TYPES)
+  {
+    _update_map_source (label, map, previous);
+  }
+  else
+  {
+    _add_map_source (id, label, map);
+  }
+}
+
+#define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
+#define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
 
 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
@@ -316,7 +383,10 @@ static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
   vml->cache_dir = NULL;
 
   if ( dir == NULL || dir[0] == '\0' )
-    vml->cache_dir = g_strdup ( MAPS_CACHE_DIR );
+  {
+    if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
+      vml->cache_dir = g_strdup ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s );
+  }
   else
   {
     len = strlen(dir);
@@ -380,8 +450,12 @@ static guint map_uniq_id_to_index ( guint uniq_id )
   return NUM_MAP_TYPES; /* no such thing */
 }
 
-static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp )
+static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
 {
+  // When loading from a file don't need the license reminder
+  if ( is_file_operation )
+    vml->license_notice_shown = TRUE;
+
   switch ( id )
   {
     case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
@@ -402,7 +476,7 @@ static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerPa
   return TRUE;
 }
 
-static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id )
+static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
 {
   VikLayerParamData rv;
   switch ( id )
@@ -425,7 +499,7 @@ static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
   int idx;
   VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
   vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS );
-  idx = map_uniq_id_to_index(7); /* 7 is id for google maps */
+  idx = map_uniq_id_to_index(13); /* 13 is id for OSM Mapnik maps */
     vml->maptype = (idx < NUM_MAP_TYPES) ? idx : 0;
   vml->alpha = 255;
   vml->mapzoom_id = 0;
@@ -437,6 +511,7 @@ static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
   vml->last_ympp = 0.0;
 
   vml->dl_right_click_menu = NULL;
+  vml->license_notice_shown = FALSE;
 
   return vml;
 }
@@ -462,17 +537,30 @@ static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_f
     VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
     VikMapSource *map = NULL;
  
-    vp_drawmode = vik_viewport_get_drawmode ( VIK_VIEWPORT(vp) );
+    vp_drawmode = vik_viewport_get_drawmode ( vp );
     map = MAPS_LAYER_NTH_TYPE(vml->maptype);
     if (vik_map_source_get_drawmode(map) != vp_drawmode) {
-      const gchar *drawmode_name = vik_viewport_get_drawmode_name (VIK_VIEWPORT(vp), vik_map_source_get_drawmode(map));
+      const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
       gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
-      a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), msg );
+      a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
       g_free(msg);
     }
+
+    if (vik_map_source_get_license (map) != NULL) {
+      if ( ! vml->license_notice_shown ) {
+       a_dialog_license (VIK_GTK_WINDOW_FROM_WIDGET(vp), vik_map_source_get_label (map),
+                         vik_map_source_get_license (map), vik_map_source_get_license_url (map) );
+       vml->license_notice_shown = TRUE;
+      }
+    }
   }
 }
 
+static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
+{
+  return vik_maps_layer_get_map_label ( vml );
+}
+
 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
 {
   vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
@@ -518,7 +606,7 @@ static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdou
 {
   GdkPixbuf *tmp;
   guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
-  tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
+  tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_NEAREST);
   g_object_unref ( G_OBJECT(pixbuf) );
   return tmp;
 }
@@ -535,21 +623,22 @@ static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord,
     g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
                      vml->cache_dir, mode,
                      mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
-    if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE) {
+    if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
     {
       GError *gx = NULL;
       pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
 
+      /* free the pixbuf on error */
       if (gx)
       {
         if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
           g_warning ( _("Couldn't open image file: %s"), gx->message );
 
-          g_error_free ( gx );
-          if ( pixbuf )
-            g_object_unref ( G_OBJECT(pixbuf) );
-          pixbuf = NULL;
-        } else {
+        g_error_free ( gx );
+        if ( pixbuf )
+          g_object_unref ( G_OBJECT(pixbuf) );
+        pixbuf = NULL;
+      } else {
           if ( vml->alpha < 255 )
             pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
           if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
@@ -558,7 +647,6 @@ static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord,
           a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y, 
               mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
               mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
-        }
       }
     }
   }
@@ -569,6 +657,10 @@ gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
 {
   const VikCoord *center = vik_viewport_get_center ( vvp );
 
+  if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
+    /* D'n'D pan in action: do not download */
+    return FALSE;
+
   if (vml->last_center == NULL) {
     VikCoord *new_center = g_malloc(sizeof(VikCoord));
     *new_center = *center;
@@ -633,10 +725,13 @@ static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCo
     gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
 
     if ( (!existence_only) && vml->autodownload  && should_start_autodownload(vml, vvp)) {
-#ifdef DEBUG
-      fputs(stderr, "DEBUG: Starting autodownload\n");
-#endif
-      start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
+      g_debug("%s: Starting autodownload", __FUNCTION__);
+      if ( vik_map_source_supports_download_only_new (map) )
+        // Try to download newer tiles
+        start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
+      else
+        // Download only missing tiles
+        start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
     }
 
     if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
@@ -696,9 +791,52 @@ static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCo
               vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
             }
           } else {
-            pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
-            if ( pixbuf )
-              vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
+            int scale_inc;
+            for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
+              /* try with correct then smaller zooms */
+              int scale_factor = 1 << scale_inc;  /*  2^scale_inc */
+              MapCoord ulm2 = ulm;
+              ulm2.x = ulm.x / scale_factor;
+              ulm2.y = ulm.y / scale_factor;
+              ulm2.scale = ulm.scale + scale_inc;
+              pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
+              if ( pixbuf ) {
+                gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
+                gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
+#ifdef DEBUG
+                printf("maps_layer_draw_section - x=%d, y=%d, z=%d, src_x=%d, src_y=%d, xx=%d, yy=%d - %x\n", ulm.x, ulm.y, ulm.scale, src_x, src_y, (int)xx, (int)yy, vvp);
+#endif
+                vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
+                break;
+              }
+            }
+            if ( !pixbuf ) {
+              /* retry with bigger zooms */
+              int scale_dec;
+              for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
+                int pict_x, pict_y;
+                int scale_factor = 1 << scale_dec;  /*  2^scale_dec */
+                MapCoord ulm2 = ulm;
+                ulm2.x = ulm.x * scale_factor;
+                ulm2.y = ulm.y * scale_factor;
+                ulm2.scale = ulm.scale - scale_dec;
+                for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
+                  for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
+                    MapCoord ulm3 = ulm2;
+                    ulm3.x += pict_x;
+                    ulm3.y += pict_y;
+                    pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
+                    if ( pixbuf ) {
+                      gint src_x = 0;
+                      gint src_y = 0;
+                      gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
+                      gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
+                      vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
+                    }
+                  }
+                }
+              }
+            }
           }
 
           yy += tilesize_y;
@@ -717,6 +855,14 @@ static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
   {
     VikCoord ul, br;
 
+    /* Copyright */
+    const gchar *copyright = vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
+    vik_viewport_add_copyright ( vvp, copyright );
+
+    /* Logo */
+    const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
+    vik_viewport_add_logo ( vvp, logo );
+
     /* get corner coords */
     if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
       /* UTM multi-zone stuff by Kit Transue */
@@ -778,6 +924,7 @@ static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
 
 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
 {
+  void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
   guint donemaps = 0;
   gint x, y;
   for ( x = mdi->x0; x <= mdi->xf; x++ )
@@ -792,40 +939,62 @@ static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
 
       donemaps++;
       int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
-      if (res != 0)
+      if (res != 0) {
+        vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
         return -1;
-
-      if ( mdi->redownload == REDOWNLOAD_ALL)
-        g_remove ( mdi->filename_buf );
-
-      else if ( (mdi->redownload == REDOWNLOAD_BAD) && (g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE) )
-      {
-        /* see if this one is bad or what */
-        GError *gx = NULL;
-        GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
-        if (gx || (!pixbuf))
-          g_remove ( mdi->filename_buf );
-        if ( pixbuf )
-          g_object_unref ( pixbuf );
-        if ( gx )
-          g_error_free ( gx );
       }
 
-      if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
-      {
+      if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
         need_download = TRUE;
-        if (( mdi->redownload != REDOWNLOAD_NONE ) &&
-            ( mdi->redownload != DOWNLOAD_OR_REFRESH ))
-          remove_mem_cache = TRUE;
-      } else if ( mdi->redownload == DOWNLOAD_OR_REFRESH ) {
         remove_mem_cache = TRUE;
-      } else
-        continue;
+
+      } else {  /* in case map file already exists */
+        switch (mdi->redownload) {
+          case REDOWNLOAD_NONE:
+            continue;
+
+          case REDOWNLOAD_BAD:
+          {
+            /* see if this one is bad or what */
+            GError *gx = NULL;
+            GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
+            if (gx || (!pixbuf)) {
+              g_remove ( mdi->filename_buf );
+              need_download = TRUE;
+              remove_mem_cache = TRUE;
+              g_error_free ( gx );
+
+            } else {
+              g_object_unref ( pixbuf );
+            }
+            break;
+          }
+
+          case REDOWNLOAD_NEW:
+            need_download = TRUE;
+            remove_mem_cache = TRUE;
+            break;
+
+          case REDOWNLOAD_ALL:
+            /* FIXME: need a better way than to erase file in case of server/network problem */
+            g_remove ( mdi->filename_buf );
+            need_download = TRUE;
+            remove_mem_cache = TRUE;
+            break;
+
+          case DOWNLOAD_OR_REFRESH:
+            remove_mem_cache = TRUE;
+            break;
+
+          default:
+            g_warning ( "redownload state %d unknown\n", mdi->redownload);
+        }
+      }
 
       mdi->mapcoord.x = x; mdi->mapcoord.y = y;
 
       if (need_download) {
-        if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf ))
+        if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
           continue;
       }
 
@@ -843,6 +1012,7 @@ static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
 
     }
   }
+  vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
   g_mutex_lock(mdi->mutex);
   if (mdi->map_layer_alive)
     g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
@@ -1027,11 +1197,17 @@ static void maps_layer_redownload_bad ( VikMapsLayer *vml )
 {
   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
 }
+
 static void maps_layer_redownload_all ( VikMapsLayer *vml )
 {
   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
 }
 
+static void maps_layer_redownload_new ( VikMapsLayer *vml )
+{
+  start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
+}
+
 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
 {
   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
@@ -1060,11 +1236,15 @@ static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton
         GtkWidget *item;
         vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
 
-        item = gtk_menu_item_new_with_label ( _("Redownload bad map(s)") );
+        item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
 
-        item = gtk_menu_item_new_with_label ( _("Redownload all map(s)") );
+        item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
+        g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
+        gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
+
+        item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
       }
@@ -1153,11 +1333,16 @@ static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
 
 }
 
-static void maps_layer_download_onscreen_maps ( gpointer vml_vvp[2] )
+static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
 {
   download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
 }
 
+static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
+{
+  download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
+}
+
 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
 {
   download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
@@ -1174,13 +1359,23 @@ static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLay
   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
   gtk_widget_show ( item );
 
-  item = gtk_menu_item_new_with_label ( _("Download Onscreen Maps") );
-  g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_onscreen_maps), pass_along );
+  /* Now with icons */
+  item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
+    gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
+  g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
   gtk_widget_show ( item );
 
-  /* TODO Add GTK_STOCK_REFRESH icon */
-  item = gtk_menu_item_new_with_label ( _("Refresh Onscreen Tiles") );
+  if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
+    item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
+    gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
+    g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
+    gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+    gtk_widget_show ( item );
+  }
+
+  item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
+  gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
   gtk_widget_show ( item );