]> git.street.me.uk Git - andy/viking.git/blob - src/vikmapslayer.c
Allow creating a File Chooser dialog with optional file filters.
[andy/viking.git] / src / vikmapslayer.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2005, Evan Battaglia <viking@greentorch.org>
5  * Copyright (C) 2010, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
6  * Copyright (c) 2013, Rob Norris <rw_norris@hotmail.com>
7  * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
8  * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <gtk/gtk.h>
31 #include <gdk-pixbuf/gdk-pixdata.h>
32 #include <glib.h>
33 #include <glib/gstdio.h>
34 #include <glib/gi18n.h>
35
36 #ifdef HAVE_STRING_H
37 #include <string.h>
38 #endif
39 #ifdef HAVE_MATH_H
40 #include <math.h>
41 #endif
42
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46
47 #include "viking.h"
48 #include "vikmapsourcedefault.h"
49 #include "maputils.h"
50 #include "mapcache.h"
51 #include "background.h"
52 #include "preferences.h"
53 #include "vikmapslayer.h"
54 #include "icons/icons.h"
55
56 #ifdef HAVE_SQLITE3_H
57 #include "sqlite3.h"
58 #include <gio/gio.h>
59 #endif
60
61 #define VIK_SETTINGS_MAP_MAX_TILES "maps_max_tiles"
62 static gint MAX_TILES = 1000;
63
64 #define VIK_SETTINGS_MAP_MIN_SHRINKFACTOR "maps_min_shrinkfactor"
65 #define VIK_SETTINGS_MAP_MAX_SHRINKFACTOR "maps_max_shrinkfactor"
66 static gdouble MAX_SHRINKFACTOR = 8.0000001; /* zoom 1 viewing 8-tiles */
67 static gdouble MIN_SHRINKFACTOR = 0.0312499; /* zoom 32 viewing 1-tiles */
68
69 #define VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR "maps_real_min_shrinkfactor"
70 static gdouble REAL_MIN_SHRINKFACTOR = 0.0039062499; /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
71
72 /****** MAP TYPES ******/
73
74 static GList *__map_types = NULL;
75
76 #define NUM_MAP_TYPES g_list_length(__map_types)
77
78 /* List of label for each map type */
79 static gchar **params_maptypes = NULL;
80
81 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
82 static guint *params_maptypes_ids = NULL;
83
84 /******** MAPZOOMS *********/
85
86 static gchar *params_mapzooms[] = { N_("Use Viking Zoom Level"), "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "USGS 10k", "USGS 24k", "USGS 25k", "USGS 50k", "USGS 100k", "USGS 200k", "USGS 250k", NULL };
87 static gdouble __mapzooms_x[] = { 0.0, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 1.016, 2.4384, 2.54, 5.08, 10.16, 20.32, 25.4 };
88 static gdouble __mapzooms_y[] = { 0.0, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 1.016, 2.4384, 2.54, 5.08, 10.16, 20.32, 25.4 };
89
90 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
91
92 /**************************/
93
94
95 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
96 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
97 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
98 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
99 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
100 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
101 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values );
102 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
103 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
104 static void maps_layer_free ( VikMapsLayer *vml );
105 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
106 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
107 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
108 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
109 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
110 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
111 static guint map_uniq_id_to_index ( guint uniq_id );
112
113
114 static VikLayerParamScale params_scales[] = {
115   /* min, max, step, digits (decimal places) */
116  { 0, 255, 3, 0 }, /* alpha */
117 };
118
119 static VikLayerParamData mode_default ( void ) { return VIK_LPD_UINT ( 19 ); } // OSM MapQuest maps
120 static VikLayerParamData directory_default ( void )
121 {
122   VikLayerParamData data;
123   VikLayerParamData *pref = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir");
124   if (pref) data.s = g_strdup ( pref->s ); else data.s = "";
125   return data;
126 }
127 static VikLayerParamData file_default ( void )
128 {
129   VikLayerParamData data;
130   data.s = "";
131   return data;
132 }
133 static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
134 static VikLayerParamData mapzoom_default ( void ) { return VIK_LPD_UINT ( 0 ); }
135
136 VikLayerParam maps_layer_params[] = {
137   { VIK_LAYER_MAPS, "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL, mode_default, NULL, NULL },
138   { VIK_LAYER_MAPS, "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, NULL, directory_default, NULL, NULL },
139   { VIK_LAYER_MAPS, "mapfile", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Map File:"), VIK_LAYER_WIDGET_FILEENTRY, GINT_TO_POINTER(VF_FILTER_MBTILES), NULL,
140     N_("An MBTiles file. Only applies when the map type method is 'MBTiles'"), file_default, NULL, NULL },
141   { VIK_LAYER_MAPS, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
142     N_("Control the Alpha value for transparency effects"), alpha_default, NULL, NULL },
143   { VIK_LAYER_MAPS, "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
144   { VIK_LAYER_MAPS, "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
145     N_("Using this option avoids attempting to update already acquired tiles. This can be useful if you want to restrict the network usage, without having to resort to manual control. Only applies when 'Autodownload Maps' is on."), vik_lpd_false_default, NULL, NULL },
146   { VIK_LAYER_MAPS, "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
147     N_("Determines the method of displaying map tiles for the current zoom level. 'Viking Zoom Level' uses the best matching level, otherwise setting a fixed value will always use map tiles of the specified value regardless of the actual zoom level."),
148     mapzoom_default, NULL, NULL },
149 };
150
151 enum {
152   PARAM_MAPTYPE=0,
153   PARAM_CACHE_DIR,
154   PARAM_FILE,
155   PARAM_ALPHA,
156   PARAM_AUTODOWNLOAD,
157   PARAM_ONLYMISSING,
158   PARAM_MAPZOOM,
159   NUM_PARAMS
160 };
161
162 void maps_layer_set_autodownload_default ( gboolean autodownload )
163 {
164   // Set appropriate function
165   if ( autodownload )
166     maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_true_default;
167   else
168     maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_false_default;
169 }
170
171 static VikToolInterface maps_tools[] = {
172   { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
173     (VikToolConstructorFunc) maps_layer_download_create,
174     NULL,
175     NULL,
176     NULL,
177     (VikToolMouseFunc) maps_layer_download_click,
178     NULL,
179     (VikToolMouseFunc) maps_layer_download_release,
180     NULL,
181     FALSE,
182     GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf },
183 };
184
185 VikLayerInterface vik_maps_layer_interface = {
186   "Map",
187   N_("Map"),
188   "<control><shift>M",
189   &vikmapslayer_pixbuf,
190
191   maps_tools,
192   sizeof(maps_tools) / sizeof(maps_tools[0]),
193
194   maps_layer_params,
195   NUM_PARAMS,
196   NULL,
197   0,
198
199   VIK_MENU_ITEM_ALL,
200
201   (VikLayerFuncCreate)                  maps_layer_new,
202   (VikLayerFuncRealize)                 NULL,
203   (VikLayerFuncPostRead)                maps_layer_post_read,
204   (VikLayerFuncFree)                    maps_layer_free,
205
206   (VikLayerFuncProperties)              NULL,
207   (VikLayerFuncDraw)                    maps_layer_draw,
208   (VikLayerFuncChangeCoordMode)         NULL,
209
210   (VikLayerFuncSetMenuItemsSelection)   NULL,
211   (VikLayerFuncGetMenuItemsSelection)   NULL,
212
213   (VikLayerFuncAddMenuItems)            maps_layer_add_menu_items,
214   (VikLayerFuncSublayerAddMenuItems)    NULL,
215
216   (VikLayerFuncSublayerRenameRequest)   NULL,
217   (VikLayerFuncSublayerToggleVisible)   NULL,
218   (VikLayerFuncSublayerTooltip)         NULL,
219   (VikLayerFuncLayerTooltip)            maps_layer_tooltip,
220   (VikLayerFuncLayerSelected)           NULL,
221
222   (VikLayerFuncMarshall)                maps_layer_marshall,
223   (VikLayerFuncUnmarshall)              maps_layer_unmarshall,
224
225   (VikLayerFuncSetParam)                maps_layer_set_param,
226   (VikLayerFuncGetParam)                maps_layer_get_param,
227   (VikLayerFuncChangeParam)             maps_layer_change_param,
228
229   (VikLayerFuncReadFileData)            NULL,
230   (VikLayerFuncWriteFileData)           NULL,
231
232   (VikLayerFuncDeleteItem)              NULL,
233   (VikLayerFuncCutItem)                 NULL,
234   (VikLayerFuncCopyItem)                NULL,
235   (VikLayerFuncPasteItem)               NULL,
236   (VikLayerFuncFreeCopiedItem)          NULL,
237   (VikLayerFuncDragDropRequest)         NULL,
238
239   (VikLayerFuncSelectClick)             NULL,
240   (VikLayerFuncSelectMove)              NULL,
241   (VikLayerFuncSelectRelease)           NULL,
242   (VikLayerFuncSelectedViewportMenu)    NULL,
243 };
244
245 struct _VikMapsLayer {
246   VikLayer vl;
247   guint maptype;
248   gchar *cache_dir;
249   guint8 alpha;
250   guint mapzoom_id;
251   gdouble xmapzoom, ymapzoom;
252
253   gboolean autodownload;
254   gboolean adl_only_missing;
255   VikCoord *last_center;
256   gdouble last_xmpp;
257   gdouble last_ympp;
258
259   gint dl_tool_x, dl_tool_y;
260
261   GtkMenu *dl_right_click_menu;
262   VikCoord redownload_ul, redownload_br; /* right click menu only */
263   VikViewport *redownload_vvp;
264   gchar *filename;
265 #ifdef HAVE_SQLITE3_H
266   sqlite3 *mbtiles;
267 #endif
268 };
269
270 enum { REDOWNLOAD_NONE = 0,    /* download only missing maps */
271        REDOWNLOAD_BAD,         /* download missing and bad maps */
272        REDOWNLOAD_NEW,         /* download missing maps that are newer on server only */
273        REDOWNLOAD_ALL,         /* download all maps */
274        DOWNLOAD_OR_REFRESH };  /* download missing maps and refresh cache */
275
276 static VikLayerParam prefs[] = {
277   { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Default map layer directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, N_("Choose a directory to store cached Map tiles for this layer") },
278 };
279
280 void maps_layer_init ()
281 {
282   VikLayerParamData tmp;
283   tmp.s = maps_layer_default_dir();
284   a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
285
286   gint max_tiles = MAX_TILES;
287   if ( a_settings_get_integer ( VIK_SETTINGS_MAP_MAX_TILES, &max_tiles ) )
288     MAX_TILES = max_tiles;
289
290   gdouble gdtmp;
291   if ( a_settings_get_double ( VIK_SETTINGS_MAP_MIN_SHRINKFACTOR, &gdtmp ) )
292     MIN_SHRINKFACTOR = gdtmp;
293
294   if ( a_settings_get_double ( VIK_SETTINGS_MAP_MAX_SHRINKFACTOR, &gdtmp ) )
295     MAX_SHRINKFACTOR = gdtmp;
296
297   if ( a_settings_get_double ( VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR, &gdtmp ) )
298     REAL_MIN_SHRINKFACTOR = gdtmp;
299 }
300
301 /****************************************/
302 /******** MAPS LAYER TYPES **************/
303 /****************************************/
304
305 void _add_map_source ( guint id, const char *label, VikMapSource *map )
306 {
307   gsize len = 0;
308   if (params_maptypes)
309     len = g_strv_length (params_maptypes);
310   /* Add the label */
311   params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
312   params_maptypes[len] = g_strdup (label);
313   params_maptypes[len+1] = NULL;
314
315   /* Add the id */
316   params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
317   params_maptypes_ids[len] = id;
318   params_maptypes_ids[len+1] = 0;
319
320   /* We have to clone */
321   VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
322   /* Register the clone in the list */
323   __map_types = g_list_append(__map_types, clone);
324
325   /* Hack
326      We have to ensure the mode LayerParam references the up-to-date
327      GLists.
328   */
329   /*
330   memcpy(&maps_layer_params[0].widget_data, &params_maptypes, sizeof(gpointer));
331   memcpy(&maps_layer_params[0].extra_widget_data, &params_maptypes_ids, sizeof(gpointer));
332   */
333   maps_layer_params[0].widget_data = params_maptypes;
334   maps_layer_params[0].extra_widget_data = params_maptypes_ids;
335 }
336
337 void _update_map_source ( const char *label, VikMapSource *map, int index )
338 {
339   GList *item = g_list_nth (__map_types, index);
340   g_object_unref (item->data);
341   item->data = g_object_ref (map);
342   /* Change previous data */
343   g_free (params_maptypes[index]);
344   params_maptypes[index] = g_strdup (label);
345 }
346
347 /**
348  * maps_layer_register_map_source:
349  * @map: the new VikMapSource
350  *
351  * Register a new VikMapSource.
352  * Override existing one (equality of id).
353  */
354 void maps_layer_register_map_source ( VikMapSource *map )
355 {
356   g_assert(map != NULL);
357   
358   guint id = vik_map_source_get_uniq_id(map);
359   const char *label = vik_map_source_get_label(map);
360   g_assert(label != NULL);
361
362   int previous = map_uniq_id_to_index (id);
363   if (previous != NUM_MAP_TYPES)
364   {
365     _update_map_source (label, map, previous);
366   }
367   else
368   {
369     _add_map_source (id, label, map);
370   }
371 }
372
373 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
374 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
375 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
376
377 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
378 {
379   return(vml->maptype);
380 }
381
382 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
383 {
384   return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
385 }
386
387 /****************************************/
388 /******** CACHE DIR STUFF ***************/
389 /****************************************/
390
391 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
392 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
393 #define MAPS_CACHE_DIR maps_layer_default_dir()
394
395 #ifdef WINDOWS
396 #include <io.h>
397 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
398 #define LOCAL_MAPS_DIR "VIKING-MAPS"
399 #elif defined __APPLE__
400 #include <stdlib.h>
401 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
402 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
403 #else /* POSIX */
404 #include <stdlib.h>
405 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
406 #define LOCAL_MAPS_DIR ".viking-maps"
407 #endif
408
409 gchar *maps_layer_default_dir ()
410 {
411   static gchar *defaultdir = NULL;
412   if ( ! defaultdir )
413   {
414     /* Thanks to Mike Davison for the $VIKING_MAPS usage */
415     const gchar *mapdir = g_getenv("VIKING_MAPS");
416     if ( mapdir ) {
417       defaultdir = g_strdup ( mapdir );
418     } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
419       defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
420     } else {
421       const gchar *home = g_get_home_dir();
422       if (!home || g_access(home, W_OK))
423         home = g_get_home_dir ();
424       if ( home )
425         defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
426       else
427         defaultdir = g_strdup ( LOCAL_MAPS_DIR );
428     }
429     if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
430     {
431       /* Add the separator at the end */
432       gchar *tmp = defaultdir;
433       defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
434       g_free(tmp);
435     }
436     g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
437   }
438   return defaultdir;
439 }
440
441 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
442 {
443   if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
444   {
445     g_mkdir ( vml->cache_dir, 0777 );
446   }
447 }
448
449 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
450 {
451   guint len;
452   g_assert ( vml != NULL);
453   g_free ( vml->cache_dir );
454   vml->cache_dir = NULL;
455   const gchar *mydir = dir;
456
457   if ( dir == NULL || dir[0] == '\0' )
458   {
459     if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
460       mydir = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s;
461   }
462
463   // Ensure cache_dir always ends with a separator
464   len = strlen(mydir);
465   if ( mydir[len-1] != G_DIR_SEPARATOR )
466   {
467     vml->cache_dir = g_malloc ( len+2 );
468     strncpy ( vml->cache_dir, mydir, len );
469     vml->cache_dir[len] = G_DIR_SEPARATOR;
470     vml->cache_dir[len+1] = '\0';
471   }
472   else
473     vml->cache_dir = g_strdup ( mydir );
474
475   maps_layer_mkdir_if_default_dir ( vml );
476 }
477
478 static void maps_layer_set_file ( VikMapsLayer *vml, const gchar *name )
479 {
480   if ( vml->filename )
481     g_free (vml->filename);
482   vml->filename = g_strdup (name);
483 }
484
485 /****************************************/
486 /******** GOBJECT STUFF *****************/
487 /****************************************/
488
489 GType vik_maps_layer_get_type ()
490 {
491   static GType vml_type = 0;
492
493   if (!vml_type)
494   {
495     static const GTypeInfo vml_info =
496     {
497       sizeof (VikMapsLayerClass),
498       NULL, /* base_init */
499       NULL, /* base_finalize */
500       NULL, /* class init */
501       NULL, /* class_finalize */
502       NULL, /* class_data */
503       sizeof (VikMapsLayer),
504       0,
505       NULL /* instance init */
506     };
507     vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
508   }
509
510   return vml_type;
511 }
512
513 /****************************************/
514 /************** PARAMETERS **************/
515 /****************************************/
516
517 static guint map_index_to_uniq_id (guint16 index)
518 {
519   g_assert ( index < NUM_MAP_TYPES );
520   return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
521 }
522
523 static guint map_uniq_id_to_index ( guint uniq_id )
524 {
525   gint i;
526   for ( i = 0; i < NUM_MAP_TYPES; i++ )
527     if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
528       return i;
529   return NUM_MAP_TYPES; /* no such thing */
530 }
531
532 #define VIK_SETTINGS_MAP_LICENSE_SHOWN "map_license_shown"
533
534 /**
535  * Convenience function to display the license
536  */
537 static void maps_show_license ( GtkWindow *parent, VikMapSource *map )
538 {
539   a_dialog_license ( parent,
540                      vik_map_source_get_label (map),
541                      vik_map_source_get_license (map),
542                      vik_map_source_get_license_url (map) );
543 }
544
545 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
546 {
547   switch ( id )
548   {
549     case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
550     case PARAM_FILE: maps_layer_set_file ( vml, data.s ); break;
551     case PARAM_MAPTYPE: {
552       gint maptype = map_uniq_id_to_index(data.u);
553       if ( maptype == NUM_MAP_TYPES )
554         g_warning(_("Unknown map type"));
555       else {
556         vml->maptype = maptype;
557
558         // When loading from a file don't need the license reminder - ensure it's saved into the 'seen' list
559         if ( is_file_operation ) {
560           a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, maptype );
561         }
562         else {
563           VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
564           if (vik_map_source_get_license (map) != NULL) {
565             // Check if licence for this map type has been shown before
566             if ( ! a_settings_get_integer_list_contains ( VIK_SETTINGS_MAP_LICENSE_SHOWN, maptype ) ) {
567               if ( vvp )
568                 maps_show_license ( VIK_GTK_WINDOW_FROM_WIDGET(vvp), map );
569               a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, maptype );
570             }
571           }
572         }
573       }
574       break;
575     }
576     case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
577     case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
578     case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
579     case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
580                           vml->mapzoom_id = data.u;
581                           vml->xmapzoom = __mapzooms_x [data.u];
582                           vml->ymapzoom = __mapzooms_y [data.u];
583                         }else g_warning (_("Unknown Map Zoom")); break;
584   }
585   return TRUE;
586 }
587
588 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
589 {
590   VikLayerParamData rv;
591   switch ( id )
592   {
593     case PARAM_CACHE_DIR:
594     {
595       gboolean set = FALSE;
596       /* Only save a blank when the map cache location equals the default
597           On reading in, when it is blank then the default is reconstructed
598           Since the default changes dependent on the user and OS, it means the resultant file is more portable */
599       if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
600         rv.s = "";
601         set = TRUE;
602       }
603       else if ( is_file_operation ) {
604         if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
605           gchar *cwd = g_get_current_dir();
606           if ( cwd ) {
607             rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
608             if ( !rv.s ) rv.s = "";
609             set = TRUE;
610           }
611         }
612       }
613       if ( !set )
614         rv.s = vml->cache_dir ? vml->cache_dir : "";
615       break;
616     }
617     case PARAM_FILE: rv.s = vml->filename; break;
618     case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
619     case PARAM_ALPHA: rv.u = vml->alpha; break;
620     case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
621     case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
622     case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
623   }
624   return rv;
625 }
626
627 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values )
628 {
629   switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
630     // Alter sensitivity of download option widgets according to the maptype setting.
631     case PARAM_MAPTYPE: {
632       // Get new value
633       VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
634       // Is it *not* the OSM On Disk Tile Layout or the MBTiles type
635       gboolean sensitive = ( 21 != vlpd.u && 23 != vlpd.u);
636       GtkWidget **ww1 = values[UI_CHG_WIDGETS];
637       GtkWidget **ww2 = values[UI_CHG_LABELS];
638       GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
639       GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
640       GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
641       GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
642       // Depends on autodownload value
643       gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
644       if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
645       if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
646       if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
647       if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
648
649       // File only applicable for MBTiles type
650       // Directory for all other types
651       sensitive = ( 23 == vlpd.u);
652       GtkWidget *w5 = ww1[PARAM_FILE];
653       GtkWidget *w6 = ww2[PARAM_FILE];
654       GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
655       GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
656       if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
657       if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
658       if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
659       if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
660
661       break;
662     }
663
664     // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
665     case PARAM_AUTODOWNLOAD: {
666       // Get new value
667       VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
668       GtkWidget **ww1 = values[UI_CHG_WIDGETS];
669       GtkWidget **ww2 = values[UI_CHG_LABELS];
670       GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
671       GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
672       if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
673       if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
674       break;
675     }
676     default: break;
677   }
678 }
679
680 /****************************************/
681 /****** CREATING, COPYING, FREEING ******/
682 /****************************************/
683
684 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
685 {
686   VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
687   vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
688
689   vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
690
691   vml->dl_tool_x = vml->dl_tool_y = -1;
692   vml->last_center = NULL;
693   vml->last_xmpp = 0.0;
694   vml->last_ympp = 0.0;
695
696   vml->dl_right_click_menu = NULL;
697   vml->filename = NULL;
698   return vml;
699 }
700
701 static void maps_layer_free ( VikMapsLayer *vml )
702 {
703   g_free ( vml->cache_dir );
704   vml->cache_dir = NULL;
705   if ( vml->dl_right_click_menu )
706     g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
707   g_free(vml->last_center);
708   vml->last_center = NULL;
709   g_free ( vml->filename );
710   vml->filename = NULL;
711
712 #ifdef HAVE_SQLITE3_H
713   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
714   if ( vik_map_source_is_mbtiles ( map ) ) {
715     if ( vml->mbtiles ) {
716       int ans = sqlite3_close ( vml->mbtiles );
717       if ( ans != SQLITE_OK ) {
718         // Only to console for information purposes only
719         g_warning ( "SQL Close problem: %d", ans );
720       }
721     }
722   }
723 #endif
724 }
725
726 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
727 {
728   VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
729   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
730
731   if (from_file != TRUE)
732   {
733     /* If this method is not called in file reading context
734      * it is called in GUI context.
735      * So, we can check if we have to inform the user about inconsistency */
736     VikViewportDrawMode vp_drawmode;
737     vp_drawmode = vik_viewport_get_drawmode ( vp );
738
739     if (vik_map_source_get_drawmode(map) != vp_drawmode) {
740       const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
741       gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
742       a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
743       g_free(msg);
744     }
745   }
746   
747   // Performed in post read as we now know the map type
748 #ifdef HAVE_SQLITE3_H
749   // Do some SQL stuff
750   if ( vik_map_source_is_mbtiles ( map ) ) {
751     int ans = sqlite3_open_v2 ( vml->filename,
752                                 &(vml->mbtiles),
753                                 SQLITE_OPEN_READONLY,
754                                 NULL );
755     if ( ans != SQLITE_OK ) {
756       // That didn't work, so here's why:
757       g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
758
759       a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
760                                  _("Failed to open MBTiles file: %s"),
761                                  vml->filename );
762       vml->mbtiles = NULL;
763     }
764   }
765 #endif
766
767   // If the on Disk OSM Tile Layout type
768   if ( vml->maptype == 21 )
769     // Copy the directory into filename
770     //  thus the mapcache look up will be unique when using more than one of these map types
771     vml->filename = g_strdup (vml->cache_dir);
772 }
773
774 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
775 {
776   return vik_maps_layer_get_map_label ( vml );
777 }
778
779 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
780 {
781   vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
782 }
783
784 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
785 {
786   VikMapsLayer *rv = maps_layer_new ( vvp );
787   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
788   return rv;
789 }
790
791 /*********************/
792 /****** DRAWING ******/
793 /*********************/
794
795 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
796 {
797   guchar *pixels;
798   gint width, height, iii, jjj;
799
800   if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
801   {
802     GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
803     g_object_unref(G_OBJECT(pixbuf));
804     pixbuf = tmp;
805     if ( !pixbuf )
806       return NULL;
807   }
808
809   pixels = gdk_pixbuf_get_pixels(pixbuf);
810   width = gdk_pixbuf_get_width(pixbuf);
811   height = gdk_pixbuf_get_height(pixbuf);
812
813   /* r,g,b,a,r,g,b,a.... */
814   for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
815   {
816     pixels += 3;
817     *pixels++ = alpha;
818   }
819   return pixbuf;
820 }
821
822 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
823 {
824   GdkPixbuf *tmp;
825   guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
826   tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
827   g_object_unref ( G_OBJECT(pixbuf) );
828   return tmp;
829 }
830
831 #ifdef HAVE_SQLITE3_H
832 /*
833 static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
834 {
835   g_warning ( "Found %d columns", cols );
836   int i;
837   for ( i = 0; i < cols; i++ ) {
838     g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
839   }
840   return 0;
841 }
842 */
843
844 /**
845  *
846  */
847 static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
848 {
849   GdkPixbuf *pixbuf = NULL;
850
851   // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
852   gint flip_y = (gint) pow(2, zoom)-1 - yy;
853   gchar *statement = g_strdup_printf ( "SELECT tile_data FROM tiles WHERE zoom_level=%d AND tile_column=%d AND tile_row=%d;", zoom, xx, flip_y );
854
855   gboolean finished = FALSE;
856
857   sqlite3_stmt *sql_stmt = NULL;
858   int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
859   if ( ans != SQLITE_OK ) {
860     g_warning ( "%s: %s - %d", __FUNCTION__, "prepare failure", ans );
861     finished = TRUE;
862   }
863
864   while ( !finished ) {
865     ans = sqlite3_step ( sql_stmt );
866     switch (ans) {
867       case SQLITE_ROW: {
868         // Get tile_data blob
869         int count = sqlite3_column_count(sql_stmt);
870         if ( count != 1 )  {
871           g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
872           finished = TRUE;
873         }
874         else {
875           const void *data = sqlite3_column_blob ( sql_stmt, 0 );
876           int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
877           if ( bytes < 1 )  {
878             g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
879             finished = TRUE;
880           }
881           else {
882             // Convert these blob bytes into a pixbuf via these streaming operations
883             GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
884             GError *error = NULL;
885             pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
886             if (error || (!pixbuf)) {
887               g_warning ( "%s: %s", __FUNCTION__, error->message );
888               g_error_free ( error );
889             }
890             g_input_stream_close ( stream, NULL, NULL );
891           }
892         }
893         break;
894       }
895       default:
896        // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
897        // Finished normally
898        //  and give up on any errors
899        if ( ans != SQLITE_DONE )
900          g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
901        finished = TRUE;
902        break;
903     }
904   }
905   ans = sqlite3_finalize ( sql_stmt );
906   
907   g_free ( statement );
908
909   return pixbuf;
910 }
911 #endif
912
913 static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
914 {
915   GdkPixbuf *pixbuf = NULL;
916
917 #ifdef HAVE_SQLITE3_H
918   if ( vml->mbtiles ) {
919     /*
920     gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
921     char *errMsg = NULL;
922     int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
923     if ( ans != SQLITE_OK ) {
924       // Only to console for information purposes only
925       g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
926       sqlite3_free( errMsg );
927     }
928     g_free ( statement );
929     */
930
931     // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
932     // Hence this specific function
933     pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
934   }
935 #endif
936
937   return pixbuf;
938 }
939
940 static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
941 {
942   // Apply alpha setting
943   if ( pixbuf && vml->alpha < 255 )
944     pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
945
946   if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
947     pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
948
949   if ( pixbuf )
950     a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
951                      mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
952                      mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
953
954   return pixbuf;
955 }
956
957 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
958 {
959   GdkPixbuf *pixbuf;
960
961   /* get the thing */
962   pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
963                             mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
964
965   if ( ! pixbuf ) {
966     if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
967       // ATM MBTiles must be 'a direct access type'
968       if ( vik_map_source_is_mbtiles (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
969         pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
970         pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
971         // return now to avoid file tests that aren't appropriate for this map type
972         return pixbuf;
973       }
974       else
975         g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS,
976                      vml->cache_dir, (17 - mapcoord->scale), mapcoord->x, mapcoord->y, ".png" );
977     }
978     else
979       g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
980                    vml->cache_dir, mode,
981                    mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
982
983     if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
984     {
985       GError *gx = NULL;
986       pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
987
988       /* free the pixbuf on error */
989       if (gx)
990       {
991         if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
992           g_warning ( _("Couldn't open image file: %s"), gx->message );
993
994         g_error_free ( gx );
995         if ( pixbuf )
996           g_object_unref ( G_OBJECT(pixbuf) );
997         pixbuf = NULL;
998       } else {
999         pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1000       }
1001     }
1002   }
1003   return pixbuf;
1004 }
1005
1006 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
1007 {
1008   const VikCoord *center = vik_viewport_get_center ( vvp );
1009
1010   if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
1011     /* D'n'D pan in action: do not download */
1012     return FALSE;
1013
1014   // TEMPORARY HACK
1015   // Prevent requests for downloading tiles at Zoom Level 19 and above for most map types
1016   // Allow MapQuest Zoom Level up to 19
1017   // TODO: This should be made a property of the map source and then use that value
1018   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1019   if ( (vml->maptype != 19 && map_utils_mpp_to_scale (xzoom) < -1) || (vml->maptype == 19 && map_utils_mpp_to_scale (xzoom) < -2) )
1020     return FALSE;
1021
1022   if (vml->last_center == NULL) {
1023     VikCoord *new_center = g_malloc(sizeof(VikCoord));
1024     *new_center = *center;
1025     vml->last_center = new_center;
1026     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1027     vml->last_ympp = vik_viewport_get_ympp(vvp);
1028     return TRUE;
1029   }
1030
1031   /* TODO: perhaps vik_coord_diff() */
1032   if (vik_coord_equals(vml->last_center, center)
1033       && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1034       && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1035     return FALSE;
1036
1037   *(vml->last_center) = *center;
1038     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1039     vml->last_ympp = vik_viewport_get_ympp(vvp);
1040   return TRUE;
1041 }
1042
1043 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1044 {
1045   MapCoord ulm, brm;
1046   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1047   gdouble yzoom = vik_viewport_get_ympp ( vvp );
1048   gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
1049   gboolean existence_only = FALSE;
1050
1051   if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1052     xshrinkfactor = vml->xmapzoom / xzoom;
1053     yshrinkfactor = vml->ymapzoom / yzoom;
1054     xzoom = vml->xmapzoom;
1055     yzoom = vml->xmapzoom;
1056     if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1057          yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
1058       if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1059         g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
1060         existence_only = TRUE;
1061       }
1062       else {
1063         g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
1064         return;
1065       }
1066     }
1067   }
1068
1069   /* coord -> ID */
1070   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1071   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1072        vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
1073
1074     /* loop & draw */
1075     gint x, y;
1076     gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1077     gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
1078     gint mode = vik_map_source_get_uniq_id(map);
1079
1080     VikCoord coord;
1081     gint xx, yy, width, height;
1082     GdkPixbuf *pixbuf;
1083
1084     // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1085     //  which can happen when using a small fixed zoom level and viewing large areas.
1086     // Also prevents very large number of tile download requests
1087     gint tiles = (xmax-xmin) * (ymax-ymin);
1088     if ( tiles > MAX_TILES ) {
1089       g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1090       existence_only = TRUE;
1091     }
1092
1093     guint max_path_len = strlen(vml->cache_dir) + 40;
1094     gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1095
1096     if ( (!existence_only) && vml->autodownload  && should_start_autodownload(vml, vvp)) {
1097       g_debug("%s: Starting autodownload", __FUNCTION__);
1098       if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
1099         // Try to download newer tiles
1100         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1101       else
1102         // Download only missing tiles
1103         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
1104     }
1105
1106     if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
1107       for ( x = xmin; x <= xmax; x++ ) {
1108         for ( y = ymin; y <= ymax; y++ ) {
1109           ulm.x = x;
1110           ulm.y = y;
1111           pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1112           if ( pixbuf ) {
1113             width = gdk_pixbuf_get_width ( pixbuf );
1114             height = gdk_pixbuf_get_height ( pixbuf );
1115
1116             vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1117             vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1118             xx -= (width/2);
1119             yy -= (height/2);
1120
1121             vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1122           }
1123         }
1124       }
1125     } else { /* tilesize is known, don't have to keep converting coords */
1126       gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1127       gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
1128       /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1129       gint tilesize_x_ceil = ceil ( tilesize_x );
1130       gint tilesize_y_ceil = ceil ( tilesize_y );
1131       gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1132       gint8 yinc = (ulm.y == ymin) ? 1 : -1;
1133       gdouble xx, yy; gint xx_tmp, yy_tmp;
1134       gint base_yy, xend, yend;
1135
1136       xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1137       yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1138
1139       vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1140       vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1141       xx = xx_tmp; yy = yy_tmp;
1142       /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1143        * eg if tile size 128, shrinkfactor 0.333 */
1144       xx -= (tilesize_x/2);
1145       base_yy = yy - (tilesize_y/2);
1146
1147       for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1148         yy = base_yy;
1149         for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1150           ulm.x = x;
1151           ulm.y = y;
1152
1153           if ( existence_only ) {
1154             if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1155               g_snprintf ( path_buf, max_path_len, DIRECTDIRACCESS,
1156                            vml->cache_dir, (17 - ulm.scale), ulm.x, ulm.y, ".png" );
1157             else
1158               g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
1159                            vml->cache_dir, mode,
1160                            ulm.scale, ulm.z, ulm.x, ulm.y );
1161             if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1162               GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1163               vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1164             }
1165           } else {
1166             int scale_inc;
1167             for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
1168               /* try with correct then smaller zooms */
1169               int scale_factor = 1 << scale_inc;  /*  2^scale_inc */
1170               MapCoord ulm2 = ulm;
1171               ulm2.x = ulm.x / scale_factor;
1172               ulm2.y = ulm.y / scale_factor;
1173               ulm2.scale = ulm.scale + scale_inc;
1174               pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1175               if ( pixbuf ) {
1176                 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1177                 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1178 #ifdef DEBUG
1179                 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);
1180 #endif
1181                 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1182                 break;
1183               }
1184             }
1185             if ( !pixbuf ) {
1186               /* retry with bigger zooms */
1187               int scale_dec;
1188               for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
1189                 int pict_x, pict_y;
1190                 int scale_factor = 1 << scale_dec;  /*  2^scale_dec */
1191                 MapCoord ulm2 = ulm;
1192                 ulm2.x = ulm.x * scale_factor;
1193                 ulm2.y = ulm.y * scale_factor;
1194                 ulm2.scale = ulm.scale - scale_dec;
1195                 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1196                   for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1197                     MapCoord ulm3 = ulm2;
1198                     ulm3.x += pict_x;
1199                     ulm3.y += pict_y;
1200                     pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1201                     if ( pixbuf ) {
1202                       gint src_x = 0;
1203                       gint src_y = 0;
1204                       gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1205                       gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1206                       vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1207                     }
1208                   }
1209                 }
1210               }
1211             }
1212           }
1213
1214           yy += tilesize_y;
1215         }
1216         xx += tilesize_x;
1217       }
1218     }
1219
1220     g_free ( path_buf );
1221   }
1222 }
1223
1224 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1225 {
1226   if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
1227   {
1228     VikCoord ul, br;
1229
1230     /* Copyright */
1231     gdouble level = vik_viewport_get_zoom ( vvp );
1232     LatLonBBox bbox;
1233     vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1234     vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
1235
1236     /* Logo */
1237     const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1238     vik_viewport_add_logo ( vvp, logo );
1239
1240     /* get corner coords */
1241     if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1242       /* UTM multi-zone stuff by Kit Transue */
1243       gchar leftmost_zone, rightmost_zone, i;
1244       leftmost_zone = vik_viewport_leftmost_zone( vvp );
1245       rightmost_zone = vik_viewport_rightmost_zone( vvp );
1246       for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1247         vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1248         maps_layer_draw_section ( vml, vvp, &ul, &br );
1249       }
1250     }
1251     else {
1252       vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1253       vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1254
1255       maps_layer_draw_section ( vml, vvp, &ul, &br );
1256     }
1257   }
1258 }
1259
1260 /*************************/
1261 /****** DOWNLOADING ******/
1262 /*************************/
1263
1264 /* pass along data to thread, exists even if layer is deleted. */
1265 typedef struct {
1266   gchar *cache_dir;
1267   gchar *filename_buf;
1268   gint x0, y0, xf, yf;
1269   MapCoord mapcoord;
1270   gint maptype;
1271   gint maxlen;
1272   gint mapstoget;
1273   gint redownload;
1274   gboolean refresh_display;
1275   VikMapsLayer *vml;
1276   VikViewport *vvp;
1277   gboolean map_layer_alive;
1278   GMutex *mutex;
1279 } MapDownloadInfo;
1280
1281 static void mdi_free ( MapDownloadInfo *mdi )
1282 {
1283   g_mutex_free(mdi->mutex);
1284   g_free ( mdi->cache_dir );
1285   mdi->cache_dir = NULL;
1286   g_free ( mdi->filename_buf );
1287   mdi->filename_buf = NULL;
1288   g_free ( mdi );
1289 }
1290
1291 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1292 {
1293   MapDownloadInfo *mdi = ptr;
1294   g_mutex_lock(mdi->mutex);
1295   mdi->map_layer_alive = FALSE;
1296   g_mutex_unlock(mdi->mutex);
1297 }
1298
1299 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1300 {
1301   void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1302   guint donemaps = 0;
1303   gint x, y;
1304   for ( x = mdi->x0; x <= mdi->xf; x++ )
1305   {
1306     for ( y = mdi->y0; y <= mdi->yf; y++ )
1307     {
1308       gboolean remove_mem_cache = FALSE;
1309       gboolean need_download = FALSE;
1310       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1311                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1312                      mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
1313
1314       donemaps++;
1315       int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1316       if (res != 0) {
1317         vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1318         return -1;
1319       }
1320
1321       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1322         need_download = TRUE;
1323         remove_mem_cache = TRUE;
1324
1325       } else {  /* in case map file already exists */
1326         switch (mdi->redownload) {
1327           case REDOWNLOAD_NONE:
1328             continue;
1329
1330           case REDOWNLOAD_BAD:
1331           {
1332             /* see if this one is bad or what */
1333             GError *gx = NULL;
1334             GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1335             if (gx || (!pixbuf)) {
1336               g_remove ( mdi->filename_buf );
1337               need_download = TRUE;
1338               remove_mem_cache = TRUE;
1339               g_error_free ( gx );
1340
1341             } else {
1342               g_object_unref ( pixbuf );
1343             }
1344             break;
1345           }
1346
1347           case REDOWNLOAD_NEW:
1348             need_download = TRUE;
1349             remove_mem_cache = TRUE;
1350             break;
1351
1352           case REDOWNLOAD_ALL:
1353             /* FIXME: need a better way than to erase file in case of server/network problem */
1354             g_remove ( mdi->filename_buf );
1355             need_download = TRUE;
1356             remove_mem_cache = TRUE;
1357             break;
1358
1359           case DOWNLOAD_OR_REFRESH:
1360             remove_mem_cache = TRUE;
1361             break;
1362
1363           default:
1364             g_warning ( "redownload state %d unknown\n", mdi->redownload);
1365         }
1366       }
1367
1368       mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1369
1370       if (need_download) {
1371         if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
1372           continue;
1373       }
1374
1375       g_mutex_lock(mdi->mutex);
1376       if (remove_mem_cache)
1377           a_mapcache_remove_all_shrinkfactors ( x, y, mdi->mapcoord.z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)), mdi->mapcoord.scale );
1378       if (mdi->refresh_display && mdi->map_layer_alive) {
1379         /* TODO: check if it's on visible area */
1380         vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1381       }
1382       g_mutex_unlock(mdi->mutex);
1383       mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1384
1385     }
1386   }
1387   vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1388   g_mutex_lock(mdi->mutex);
1389   if (mdi->map_layer_alive)
1390     g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1391   g_mutex_unlock(mdi->mutex); 
1392   return 0;
1393 }
1394
1395 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1396 {
1397   if ( mdi->mapcoord.x || mdi->mapcoord.y )
1398   {
1399     g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1400                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1401                      mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
1402     if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1403     {
1404       g_remove ( mdi->filename_buf );
1405     }
1406   }
1407 }
1408
1409 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1410 {
1411   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1412   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1413   MapCoord ulm, brm;
1414   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1415
1416   // Don't ever attempt download on direct access
1417   if ( vik_map_source_is_direct_file_access ( map ) )
1418     return;
1419
1420   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) 
1421     && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1422   {
1423     MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1424     gint a, b;
1425
1426     mdi->vml = vml;
1427     mdi->vvp = vvp;
1428     mdi->map_layer_alive = TRUE;
1429     mdi->mutex = g_mutex_new();
1430     mdi->refresh_display = TRUE;
1431
1432     /* cache_dir and buffer for dest filename */
1433     mdi->cache_dir = g_strdup ( vml->cache_dir );
1434     mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1435     mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1436     mdi->maptype = vml->maptype;
1437
1438     mdi->mapcoord = ulm;
1439     mdi->redownload = redownload;
1440
1441     mdi->x0 = MIN(ulm.x, brm.x);
1442     mdi->xf = MAX(ulm.x, brm.x);
1443     mdi->y0 = MIN(ulm.y, brm.y);
1444     mdi->yf = MAX(ulm.y, brm.y);
1445
1446     mdi->mapstoget = 0;
1447
1448     if ( mdi->redownload ) {
1449       mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1450     } else {
1451       /* calculate how many we need */
1452       for ( a = mdi->x0; a <= mdi->xf; a++ )
1453       {
1454         for ( b = mdi->y0; b <= mdi->yf; b++ )
1455         {
1456           g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1457                        vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1458                        ulm.z, a, b );
1459           if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1460             mdi->mapstoget++;
1461         }
1462       }
1463     }
1464
1465     mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1466
1467     if ( mdi->mapstoget )
1468     {
1469       const gchar *tmp_str;
1470       gchar *tmp;
1471
1472       if (redownload) 
1473       {
1474         if (redownload == REDOWNLOAD_BAD)
1475           tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1476         else
1477           tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1478       } 
1479       else 
1480       {
1481         tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1482       }
1483       tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1484  
1485       g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1486       /* launch the thread */
1487       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1488                             tmp,                                              /* description string */
1489                             (vik_thr_func) map_download_thread,               /* function to call within thread */
1490                             mdi,                                              /* pass along data */
1491                             (vik_thr_free_func) mdi_free,                     /* function to free pass along data */
1492                             (vik_thr_free_func) mdi_cancel_cleanup,
1493                             mdi->mapstoget );
1494       g_free ( tmp );
1495     }
1496     else
1497       mdi_free ( mdi );
1498   }
1499 }
1500
1501 static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1502 {
1503   MapCoord ulm, brm;
1504   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1505
1506   // Don't ever attempt download on direct access
1507   if ( vik_map_source_is_direct_file_access ( map ) )
1508     return;
1509
1510   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm) 
1511     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1512     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1513     return;
1514   }
1515
1516   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1517   gint i, j;
1518
1519   mdi->vml = vml;
1520   mdi->vvp = vvp;
1521   mdi->map_layer_alive = TRUE;
1522   mdi->mutex = g_mutex_new();
1523   mdi->refresh_display = TRUE;
1524
1525   mdi->cache_dir = g_strdup ( vml->cache_dir );
1526   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1527   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1528   mdi->maptype = vml->maptype;
1529
1530   mdi->mapcoord = ulm;
1531   mdi->redownload = download_method;
1532
1533   mdi->x0 = MIN(ulm.x, brm.x);
1534   mdi->xf = MAX(ulm.x, brm.x);
1535   mdi->y0 = MIN(ulm.y, brm.y);
1536   mdi->yf = MAX(ulm.y, brm.y);
1537
1538   mdi->mapstoget = 0;
1539
1540   for (i = mdi->x0; i <= mdi->xf; i++) {
1541     for (j = mdi->y0; j <= mdi->yf; j++) {
1542       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1543                    vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1544                    ulm.z, i, j );
1545       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1546             mdi->mapstoget++;
1547     }
1548   }
1549
1550   mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1551
1552   if (mdi->mapstoget) {
1553     gchar *tmp;
1554     const gchar *fmt;
1555     fmt = ngettext("Downloading %d %s map...",
1556                    "Downloading %d %s maps...",
1557                    mdi->mapstoget);
1558     tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1559
1560     g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1561       /* launch the thread */
1562     a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1563       tmp,                                /* description string */
1564       (vik_thr_func) map_download_thread, /* function to call within thread */
1565       mdi,                                /* pass along data */
1566       (vik_thr_free_func) mdi_free,       /* function to free pass along data */
1567       (vik_thr_free_func) mdi_cancel_cleanup,
1568       mdi->mapstoget );
1569     g_free ( tmp );
1570   }
1571   else
1572     mdi_free ( mdi );
1573 }
1574
1575 /**
1576  * vik_maps_layer_download_section:
1577  * @vml:  The Map Layer
1578  * @vvp:  The Viewport that the map is on
1579  * @ul:   Upper left coordinate of the area to be downloaded
1580  * @br:   Bottom right coordinate of the area to be downloaded
1581  * @zoom: The zoom level at which the maps are to be download
1582  *
1583  * Download a specified map area at a certain zoom level
1584  */
1585 void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1586 {
1587   maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1588 }
1589
1590 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1591 {
1592   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1593 }
1594
1595 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1596 {
1597   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1598 }
1599
1600 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1601 {
1602   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1603 }
1604
1605 /**
1606  * Display a simple dialog with information about this particular map tile
1607  */
1608 static void maps_layer_tile_info ( VikMapsLayer *vml )
1609 {
1610   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1611
1612   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1613   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1614   MapCoord ulm;
1615
1616   if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1617     return;
1618
1619   gchar *filename = NULL;
1620   gchar *message = NULL;
1621   gchar *source = NULL;
1622
1623   if ( vik_map_source_is_direct_file_access ( map ) ) {
1624     if ( vik_map_source_is_mbtiles ( map ) ) {
1625       filename = g_strdup ( vml->filename );
1626 #ifdef HAVE_SQLITE3_H
1627       // And whether to bother going into the SQL to check it's really there or not...
1628       gchar *exists = NULL;
1629       if ( vml->mbtiles ) {
1630         GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, (17 - ulm.scale) );
1631         if ( pixbuf ) {
1632           exists = g_strdup ( _("YES") );
1633           g_object_unref ( G_OBJECT(pixbuf) );
1634         }
1635         else {
1636           exists = g_strdup ( _("NO") );
1637         }
1638       }
1639       else
1640         exists = g_strdup ( _("NO") );
1641       gint flip_y = (gint) pow(2, (17 - ulm.scale))-1 - ulm.y;
1642       // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1643       source = g_strdup_printf ( "%s (%d%s%d%s%d.%s %s)", filename, ulm.scale, G_DIR_SEPARATOR_S, ulm.x, G_DIR_SEPARATOR_S, flip_y, "png", exists );
1644       g_free ( exists );
1645 #else
1646       source = g_strdup ( _("Not available") );
1647 #endif
1648     }
1649     else {
1650       filename = g_strdup_printf ( DIRECTDIRACCESS, vml->cache_dir, ulm.scale, ulm.x, ulm.y, ".png" );
1651       source = g_strconcat ( "file://", filename, NULL );
1652     }
1653   }
1654   else {
1655         filename = g_strdup_printf ( DIRSTRUCTURE, vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale, ulm.z, ulm.x, ulm.y );
1656     source = g_strdup_printf ( "http://%s%s",
1657                                vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1658                                vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1659   }
1660
1661   if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1662
1663     // Get some timestamp information of the tile
1664     struct stat stat_buf;
1665     if ( g_stat ( filename, &stat_buf ) == 0 ) {
1666       gchar time_buf[64];
1667       strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1668       message = g_strdup_printf ( _("\nSource: %s\n\nTile File: %s\nTile File Timestamp: %s"), source, filename, time_buf );
1669     }
1670   }
1671   else
1672     message = g_strdup_printf ( _("Source: %s\n\nNo Tile File!"), source );
1673
1674   // Show the info
1675   a_dialog_info_msg (  VIK_GTK_WINDOW_FROM_LAYER(vml), message );
1676
1677   g_free ( message );
1678   g_free ( source );
1679   g_free ( filename );
1680 }
1681
1682 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1683 {
1684   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1685     return FALSE;
1686   if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1687   {
1688     if ( event->button == 1 )
1689     {
1690       VikCoord ul, br;
1691       vik_viewport_screen_to_coord ( vvp, MAX(0, MIN(event->x, vml->dl_tool_x)), MAX(0, MIN(event->y, vml->dl_tool_y)), &ul );
1692       vik_viewport_screen_to_coord ( vvp, MIN(vik_viewport_get_width(vvp), MAX(event->x, vml->dl_tool_x)), MIN(vik_viewport_get_height(vvp), MAX ( event->y, vml->dl_tool_y ) ), &br );
1693       start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1694       vml->dl_tool_x = vml->dl_tool_y = -1;
1695       return TRUE;
1696     }
1697     else
1698     {
1699       vik_viewport_screen_to_coord ( vvp, MAX(0, MIN(event->x, vml->dl_tool_x)), MAX(0, MIN(event->y, vml->dl_tool_y)), &(vml->redownload_ul) );
1700       vik_viewport_screen_to_coord ( vvp, MIN(vik_viewport_get_width(vvp), MAX(event->x, vml->dl_tool_x)), MIN(vik_viewport_get_height(vvp), MAX ( event->y, vml->dl_tool_y ) ), &(vml->redownload_br) );
1701
1702       vml->redownload_vvp = vvp;
1703
1704       vml->dl_tool_x = vml->dl_tool_y = -1;
1705
1706       if ( ! vml->dl_right_click_menu ) {
1707         GtkWidget *item;
1708         vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1709
1710         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1711         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1712         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1713
1714         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1715         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1716         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1717
1718         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1719         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1720         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1721
1722         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
1723         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1724         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
1725         gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
1726       }
1727
1728       gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1729       gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1730     }
1731   }
1732   return FALSE;
1733 }
1734
1735 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1736 {
1737   return vvp;
1738 }
1739
1740 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1741 {
1742   MapCoord tmp;
1743   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1744     return FALSE;
1745   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1746   if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1747        vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1748            vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1749            vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1750            &tmp ) ) {
1751     vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1752     return TRUE;
1753   }
1754   return FALSE;
1755
1756  
1757 #if 0
1758   if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1759   {
1760     VikCoord coord;
1761     MapCoord mapcoord;
1762     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1763     if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1764                 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1765                 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1766                 &mapcoord ) ) {
1767       gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1768                      vml->cache_dir, __map_types[vml->maptype].uniq_id,
1769                      mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1770
1771       __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1772       g_free ( filename_buf );
1773       vik_layer_emit_update ( VIK_LAYER(vml) );
1774       return TRUE;
1775     }
1776   }
1777   return FALSE;
1778 #endif
1779 }
1780
1781 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1782 {
1783   VikMapsLayer *vml = vml_vvp[0];
1784   VikViewport *vvp = vml_vvp[1];
1785   VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1786
1787   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1788   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1789
1790   VikCoord ul, br;
1791   MapCoord ulm, brm;
1792
1793   vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1794   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1795
1796   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1797   if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1798        vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1799        vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1800     start_download_thread ( vml, vvp, &ul, &br, redownload );
1801   else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1802     const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1803     gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1804     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1805     g_free(err);
1806   }
1807   else
1808     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1809
1810 }
1811
1812 static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
1813 {
1814   download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1815 }
1816
1817 static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
1818 {
1819   download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
1820 }
1821
1822 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1823 {
1824   download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1825 }
1826
1827 static void maps_layers_about ( gpointer vml_vvp[2] )
1828 {
1829   VikMapsLayer *vml = vml_vvp[0];
1830   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1831
1832   if ( vik_map_source_get_license (map) )
1833     maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
1834   else
1835     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
1836                         vik_map_source_get_label (map) );
1837 }
1838
1839 /**
1840  * maps_layer_how_many_maps:
1841  * Copied from maps_layer_download_section but without the actual download and this returns a value
1842  */
1843 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
1844 {
1845   MapCoord ulm, brm;
1846   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1847
1848   if ( vik_map_source_is_direct_file_access ( map ) )
1849     return 0;
1850
1851   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1852     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1853     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1854     return 0;
1855   }
1856
1857   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1858   gint i, j;
1859
1860   mdi->vml = vml;
1861   mdi->vvp = vvp;
1862   mdi->map_layer_alive = TRUE;
1863   mdi->mutex = g_mutex_new();
1864   mdi->refresh_display = FALSE;
1865
1866   mdi->cache_dir = g_strdup ( vml->cache_dir );
1867   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1868   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1869   mdi->maptype = vml->maptype;
1870
1871   mdi->mapcoord = ulm;
1872   mdi->redownload = redownload;
1873
1874   mdi->x0 = MIN(ulm.x, brm.x);
1875   mdi->xf = MAX(ulm.x, brm.x);
1876   mdi->y0 = MIN(ulm.y, brm.y);
1877   mdi->yf = MAX(ulm.y, brm.y);
1878
1879   mdi->mapstoget = 0;
1880
1881   if ( mdi->redownload == REDOWNLOAD_ALL ) {
1882     mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1883   }
1884   else {
1885     /* calculate how many we need */
1886     for (i = mdi->x0; i <= mdi->xf; i++) {
1887       for (j = mdi->y0; j <= mdi->yf; j++) {
1888         g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1889                      vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1890                      ulm.z, i, j );
1891         if ( mdi->redownload == REDOWNLOAD_NEW ) {
1892           // Assume the worst - always a new file
1893           // Absolute value would requires server lookup - but that is too slow
1894           mdi->mapstoget++;
1895         }
1896         else {
1897           if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1898             // Missing
1899             mdi->mapstoget++;
1900           }
1901           else {
1902             if ( mdi->redownload == REDOWNLOAD_BAD ) {
1903               /* see if this one is bad or what */
1904               GError *gx = NULL;
1905               GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1906               if (gx || (!pixbuf)) {
1907                 mdi->mapstoget++;
1908               }
1909               break;
1910               // Other download cases already considered or just ignored
1911             }
1912           }
1913         }
1914       }
1915     }
1916   }
1917
1918   gint rv = mdi->mapstoget;
1919
1920   mdi_free ( mdi );
1921
1922   return rv;
1923 }
1924
1925 /**
1926  * maps_dialog_zoom_between:
1927  * This dialog is specific to the map layer, so it's here rather than in dialog.c
1928  */
1929 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
1930                                     gchar *title,
1931                                     gchar *zoom_list[],
1932                                     gint default_zoom1,
1933                                     gint default_zoom2,
1934                                     gint *selected_zoom1,
1935                                     gint *selected_zoom2,
1936                                     gchar *download_list[],
1937                                     gint default_download,
1938                                     gint *selected_download )
1939 {
1940   GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
1941                                                     parent,
1942                                                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1943                                                     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1944                                                     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1945                                                     NULL );
1946   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1947   GtkWidget *response_w = NULL;
1948 #if GTK_CHECK_VERSION (2, 20, 0)
1949   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1950 #endif
1951   GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
1952   GtkWidget *zoom_combo1 = vik_combo_box_text_new();
1953   gchar **s;
1954   for (s = zoom_list; *s; s++)
1955     vik_combo_box_text_append ( zoom_combo1, *s );
1956   gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
1957
1958   GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
1959   GtkWidget *zoom_combo2 = vik_combo_box_text_new();
1960   for (s = zoom_list; *s; s++)
1961     vik_combo_box_text_append ( zoom_combo2, *s );
1962   gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
1963
1964   GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
1965   GtkWidget *download_combo = vik_combo_box_text_new();
1966   for (s = download_list; *s; s++)
1967     vik_combo_box_text_append ( download_combo, *s );
1968   gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
1969
1970   GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
1971   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
1972   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
1973   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
1974   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
1975   gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
1976   gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
1977
1978   gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
1979
1980   if ( response_w )
1981     gtk_widget_grab_focus ( response_w );
1982
1983   gtk_widget_show_all ( dialog );
1984   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
1985     gtk_widget_destroy(dialog);
1986     return FALSE;
1987   }
1988
1989   // Return selected options
1990   *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
1991   *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
1992   *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
1993
1994   gtk_widget_destroy(dialog);
1995   return TRUE;
1996 }
1997
1998 // My best guess of sensible limits
1999 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
2000 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2001
2002 /**
2003  * Get all maps in the region for zoom levels specified by the user
2004  * Sort of similar to trw_layer_download_map_along_track_cb function
2005  */
2006 static void maps_layer_download_all ( gpointer vml_vvp[2] )
2007 {
2008   VikMapsLayer *vml = vml_vvp[0];
2009   VikViewport *vvp = vml_vvp[1];
2010
2011   // I don't think we should allow users to hammer the servers too much...
2012   // Delibrately not allowing lowest zoom levels
2013   // Still can give massive numbers to download
2014   // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2015   gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2016   gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2017
2018   gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2019   gint selected_download_method;
2020   
2021   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2022
2023   for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2024     if (cur_zoom == zoom_vals[default_zoom])
2025       break;
2026   }
2027   default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2028
2029   // Default to only 2 zoom levels below the current one
2030   if (default_zoom > 1 )
2031     lower_zoom = default_zoom - 2;
2032   else
2033     lower_zoom = default_zoom;
2034
2035   // redownload method - needs to align with REDOWNLOAD* macro values
2036   gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2037
2038   gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2039
2040   if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2041                                     title,
2042                                     zoom_list,
2043                                     lower_zoom,
2044                                     default_zoom,
2045                                     &selected_zoom1,
2046                                     &selected_zoom2,
2047                                     download_list,
2048                                     REDOWNLOAD_NONE, // AKA Missing
2049                                     &selected_download_method ) ) {
2050     // Cancelled
2051     g_free ( title );
2052     return;
2053   }
2054   g_free ( title );
2055
2056   // Find out new current positions
2057   gdouble min_lat, max_lat, min_lon, max_lon;
2058   VikCoord vc_ul, vc_br;
2059   vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2060   struct LatLon ll_ul = { max_lat, min_lon };
2061   struct LatLon ll_br = { min_lat, max_lon };
2062   vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2063   vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2064
2065   // Get Maps Count - call for each zoom level (in reverse)
2066   // With REDOWNLOAD_NEW this is a possible maximum
2067   // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2068   gint map_count = 0;
2069   gint zz;
2070   for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2071     map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2072   }
2073
2074   g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2075
2076   // Absolute protection of hammering a map server
2077   if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2078     gchar *str = g_strdup_printf (_("You are not allowed to download more than %d tiles in one go (requested %d)"), REALLY_LARGE_AMOUNT_OF_TILES, map_count);
2079     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2080     g_free (str);
2081     return;
2082   }
2083
2084   // Confirm really want to do this
2085   if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2086     gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2087     gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2088     g_free (str);
2089     if ( ! ans )
2090       return;
2091   }
2092
2093   // Get Maps - call for each zoom level (in reverse)
2094   for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2095     maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2096   }
2097 }
2098
2099 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2100 {
2101   static gpointer pass_along[2];
2102   GtkWidget *item;
2103   pass_along[0] = vml;
2104   pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2105
2106   item = gtk_menu_item_new();
2107   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2108   gtk_widget_show ( item );
2109
2110   /* Now with icons */
2111   item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2112     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2113   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
2114   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2115   gtk_widget_show ( item );
2116
2117   if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2118     item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2119     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2120     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
2121     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2122     gtk_widget_show ( item );
2123   }
2124
2125   item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2126   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2127   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
2128   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2129   gtk_widget_show ( item );
2130
2131   item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2132   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2133   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), pass_along );
2134   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2135   gtk_widget_show ( item );
2136
2137   item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2138   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layers_about), pass_along );
2139   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2140   gtk_widget_show ( item );
2141 }
2142
2143 /**
2144  * Enable downloading maps of the current screen area either 'new' or 'everything'
2145  */
2146 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2147 {
2148   if ( !vml ) return;
2149   if ( !vvp ) return;
2150
2151   static gpointer pass_along[2];
2152   pass_along[0] = vml;
2153   pass_along[1] = vvp;
2154
2155   if ( only_new )
2156     // Get only new maps
2157     maps_layer_download_new_onscreen_maps ( pass_along );
2158   else
2159     // Redownload everything
2160     maps_layer_redownload_all_onscreen_maps ( pass_along );
2161 }