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