]> git.street.me.uk Git - andy/viking.git/blobdiff - src/vikmapslayer.c
When manually creating a track, automatically give it a default name.
[andy/viking.git] / src / vikmapslayer.c
index f2453be53ecaf1a49f8e2b548bb861f028360393..a6b2c3c137bd9f495d99e5c2c2bbf5e32671074d 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>
  *
@@ -59,6 +60,7 @@
 /* only for dialog.h -- ugh */
 #include "vikwaypoint.h"
 #include "dialog.h"
+#include "preferences.h"
 
 #include "vikstatus.h"
 #include "background.h"
@@ -78,10 +80,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 *********/
 
@@ -108,6 +110,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 +119,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 };
@@ -199,26 +202,53 @@ struct _VikMapsLayer {
   VikViewport *redownload_vvp;
 };
 
-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 +256,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 +267,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 +375,7 @@ 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 );
+    vml->cache_dir = g_strdup ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s );
   else
   {
     len = strlen(dir);
@@ -535,21 +594,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 +618,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 );
-        }
       }
     }
   }
@@ -636,7 +695,12 @@ static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCo
 #ifdef DEBUG
       fputs(stderr, "DEBUG: Starting autodownload\n");
 #endif
-      start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
+      if ( vik_map_source_supports_if_modified_since (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,24 +760,49 @@ 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 );
-            else {
-              /* retry with bigger shrinkfactor */
-              int scale_inc;
-              for (scale_inc = 1; scale_inc < 4; scale_inc ++) {
-                int scale_factor = 1 << scale_inc;  /*  2^scale_inc */
+            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_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;
-                  vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
-                  break;
+                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 );
+                    }
+                  }
                 }
               }
             }
@@ -796,6 +885,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++ )
@@ -810,8 +900,10 @@ 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 );
@@ -837,13 +929,16 @@ static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
           remove_mem_cache = TRUE;
       } else if ( mdi->redownload == DOWNLOAD_OR_REFRESH ) {
         remove_mem_cache = TRUE;
+      } else if ( mdi->redownload == REDOWNLOAD_NEW) {
+        need_download = TRUE;
+        remove_mem_cache = TRUE;
       } else
         continue;
 
       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;
       }
 
@@ -861,6 +956,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);
@@ -1045,11 +1141,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)
@@ -1082,6 +1184,10 @@ static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton
         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 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_label ( _("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 );
@@ -1171,11 +1277,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);
@@ -1192,11 +1303,18 @@ 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 );
+  item = gtk_menu_item_new_with_label ( _("Download missing Onscreen Maps") );
+  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 );
 
+  if ( vik_map_source_supports_if_modified_since (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
+    item = gtk_menu_item_new_with_label ( _("Download new Onscreen Maps from server") );
+    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 );
+  }
+
   /* TODO Add GTK_STOCK_REFRESH icon */
   item = gtk_menu_item_new_with_label ( _("Refresh Onscreen Tiles") );
   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );