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