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