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