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