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