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