]> git.street.me.uk Git - andy/viking.git/blob - src/vikmapslayer.c
[QA] Update year values.
[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  * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
7  * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <gtk/gtk.h>
30 #include <gdk-pixbuf/gdk-pixdata.h>
31 #include <glib.h>
32 #include <glib/gstdio.h>
33 #include <glib/gi18n.h>
34
35 #ifdef HAVE_STRING_H
36 #include <string.h>
37 #endif
38 #ifdef HAVE_MATH_H
39 #include <math.h>
40 #endif
41
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45
46 #include "viking.h"
47 #include "vikmapsourcedefault.h"
48 #include "maputils.h"
49 #include "mapcache.h"
50 #include "background.h"
51 #include "preferences.h"
52 #include "vikmapslayer.h"
53 #include "icons/icons.h"
54
55 #define VIK_SETTINGS_MAP_MAX_TILES "maps_max_tiles"
56 static gint MAX_TILES = 1000;
57
58 #define VIK_SETTINGS_MAP_MIN_SHRINKFACTOR "maps_min_shrinkfactor"
59 #define VIK_SETTINGS_MAP_MAX_SHRINKFACTOR "maps_max_shrinkfactor"
60 static gdouble MAX_SHRINKFACTOR = 8.0000001; /* zoom 1 viewing 8-tiles */
61 static gdouble MIN_SHRINKFACTOR = 0.0312499; /* zoom 32 viewing 1-tiles */
62
63 #define VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR "maps_real_min_shrinkfactor"
64 static gdouble REAL_MIN_SHRINKFACTOR = 0.0039062499; /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
65
66 /****** MAP TYPES ******/
67
68 static GList *__map_types = NULL;
69
70 #define NUM_MAP_TYPES g_list_length(__map_types)
71
72 /* List of label for each map type */
73 static gchar **params_maptypes = NULL;
74
75 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
76 static guint *params_maptypes_ids = NULL;
77
78 /******** MAPZOOMS *********/
79
80 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 };
81 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 };
82 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 };
83
84 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
85
86 /**************************/
87
88
89 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
90 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
91 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
92 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
93 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
94 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
95 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
96 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
97 static void maps_layer_free ( VikMapsLayer *vml );
98 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
99 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
100 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
101 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
102 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
103 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
104 static guint map_uniq_id_to_index ( guint uniq_id );
105
106
107 static VikLayerParamScale params_scales[] = {
108   /* min, max, step, digits (decimal places) */
109  { 0, 255, 3, 0 }, /* alpha */
110 };
111
112 static VikLayerParamData mode_default ( void ) { return VIK_LPD_UINT ( 19 ); } // OSM MapQuest maps
113 static VikLayerParamData directory_default ( void )
114 {
115   VikLayerParamData data;
116   VikLayerParamData *pref = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir");
117   if (pref) data.s = g_strdup ( pref->s ); else data.s = "";
118   return data;
119 }
120 static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
121 static VikLayerParamData mapzoom_default ( void ) { return VIK_LPD_UINT ( 0 ); }
122
123 VikLayerParam maps_layer_params[] = {
124   { VIK_LAYER_MAPS, "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL, mode_default, NULL, NULL },
125   { 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 },
126   { VIK_LAYER_MAPS, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
127     N_("Control the Alpha value for transparency effects"), alpha_default, NULL, NULL },
128   { 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 },
129   { VIK_LAYER_MAPS, "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON,
130     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 },
131   { VIK_LAYER_MAPS, "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
132     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."),
133     mapzoom_default, NULL, NULL },
134 };
135
136 enum {
137   PARAM_MAPTYPE=0,
138   PARAM_CACHE_DIR,
139   PARAM_ALPHA,
140   PARAM_AUTODOWNLOAD,
141   PARAM_ONLYMISSING,
142   PARAM_MAPZOOM,
143   NUM_PARAMS
144 };
145
146 void maps_layer_set_autodownload_default ( gboolean autodownload )
147 {
148   // Set appropriate function
149   if ( autodownload )
150     maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_true_default;
151   else
152     maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_false_default;
153 }
154
155 static VikToolInterface maps_tools[] = {
156   { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
157     (VikToolConstructorFunc) maps_layer_download_create,
158     NULL,
159     NULL,
160     NULL,
161     (VikToolMouseFunc) maps_layer_download_click,
162     NULL,
163     (VikToolMouseFunc) maps_layer_download_release,
164     NULL,
165     FALSE,
166     GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf },
167 };
168
169 VikLayerInterface vik_maps_layer_interface = {
170   "Map",
171   N_("Map"),
172   "<control><shift>M",
173   &vikmapslayer_pixbuf,
174
175   maps_tools,
176   sizeof(maps_tools) / sizeof(maps_tools[0]),
177
178   maps_layer_params,
179   NUM_PARAMS,
180   NULL,
181   0,
182
183   VIK_MENU_ITEM_ALL,
184
185   (VikLayerFuncCreate)                  maps_layer_new,
186   (VikLayerFuncRealize)                 NULL,
187   (VikLayerFuncPostRead)                maps_layer_post_read,
188   (VikLayerFuncFree)                    maps_layer_free,
189
190   (VikLayerFuncProperties)              NULL,
191   (VikLayerFuncDraw)                    maps_layer_draw,
192   (VikLayerFuncChangeCoordMode)         NULL,
193
194   (VikLayerFuncSetMenuItemsSelection)   NULL,
195   (VikLayerFuncGetMenuItemsSelection)   NULL,
196
197   (VikLayerFuncAddMenuItems)            maps_layer_add_menu_items,
198   (VikLayerFuncSublayerAddMenuItems)    NULL,
199
200   (VikLayerFuncSublayerRenameRequest)   NULL,
201   (VikLayerFuncSublayerToggleVisible)   NULL,
202   (VikLayerFuncSublayerTooltip)         NULL,
203   (VikLayerFuncLayerTooltip)            maps_layer_tooltip,
204   (VikLayerFuncLayerSelected)           NULL,
205
206   (VikLayerFuncMarshall)                maps_layer_marshall,
207   (VikLayerFuncUnmarshall)              maps_layer_unmarshall,
208
209   (VikLayerFuncSetParam)                maps_layer_set_param,
210   (VikLayerFuncGetParam)                maps_layer_get_param,
211
212   (VikLayerFuncReadFileData)            NULL,
213   (VikLayerFuncWriteFileData)           NULL,
214
215   (VikLayerFuncDeleteItem)              NULL,
216   (VikLayerFuncCutItem)                 NULL,
217   (VikLayerFuncCopyItem)                NULL,
218   (VikLayerFuncPasteItem)               NULL,
219   (VikLayerFuncFreeCopiedItem)          NULL,
220   (VikLayerFuncDragDropRequest)         NULL,
221
222   (VikLayerFuncSelectClick)             NULL,
223   (VikLayerFuncSelectMove)              NULL,
224   (VikLayerFuncSelectRelease)           NULL,
225   (VikLayerFuncSelectedViewportMenu)    NULL,
226 };
227
228 struct _VikMapsLayer {
229   VikLayer vl;
230   guint maptype;
231   gchar *cache_dir;
232   guint8 alpha;
233   guint mapzoom_id;
234   gdouble xmapzoom, ymapzoom;
235
236   gboolean autodownload;
237   gboolean adl_only_missing;
238   VikCoord *last_center;
239   gdouble last_xmpp;
240   gdouble last_ympp;
241
242   gint dl_tool_x, dl_tool_y;
243
244   GtkMenu *dl_right_click_menu;
245   VikCoord redownload_ul, redownload_br; /* right click menu only */
246   VikViewport *redownload_vvp;
247
248   gboolean license_notice_shown; // FALSE for new maps only, otherwise
249                                  // TRUE for saved maps & other layer changes as we don't need to show it again
250 };
251
252 enum { REDOWNLOAD_NONE = 0,    /* download only missing maps */
253        REDOWNLOAD_BAD,         /* download missing and bad maps */
254        REDOWNLOAD_NEW,         /* download missing maps that are newer on server only */
255        REDOWNLOAD_ALL,         /* download all maps */
256        DOWNLOAD_OR_REFRESH };  /* download missing maps and refresh cache */
257
258 static VikLayerParam prefs[] = {
259   { 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") },
260 };
261
262 void maps_layer_init ()
263 {
264   VikLayerParamData tmp;
265   tmp.s = maps_layer_default_dir();
266   a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
267
268   gint max_tiles = MAX_TILES;
269   if ( a_settings_get_integer ( VIK_SETTINGS_MAP_MAX_TILES, &max_tiles ) )
270     MAX_TILES = max_tiles;
271
272   gdouble gdtmp;
273   if ( a_settings_get_double ( VIK_SETTINGS_MAP_MIN_SHRINKFACTOR, &gdtmp ) )
274     MIN_SHRINKFACTOR = gdtmp;
275
276   if ( a_settings_get_double ( VIK_SETTINGS_MAP_MAX_SHRINKFACTOR, &gdtmp ) )
277     MAX_SHRINKFACTOR = gdtmp;
278
279   if ( a_settings_get_double ( VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR, &gdtmp ) )
280     REAL_MIN_SHRINKFACTOR = gdtmp;
281 }
282
283 /****************************************/
284 /******** MAPS LAYER TYPES **************/
285 /****************************************/
286
287 void _add_map_source ( guint id, const char *label, VikMapSource *map )
288 {
289   gsize len = 0;
290   if (params_maptypes)
291     len = g_strv_length (params_maptypes);
292   /* Add the label */
293   params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
294   params_maptypes[len] = g_strdup (label);
295   params_maptypes[len+1] = NULL;
296
297   /* Add the id */
298   params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
299   params_maptypes_ids[len] = id;
300   params_maptypes_ids[len+1] = 0;
301
302   /* We have to clone */
303   VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
304   /* Register the clone in the list */
305   __map_types = g_list_append(__map_types, clone);
306
307   /* Hack
308      We have to ensure the mode LayerParam references the up-to-date
309      GLists.
310   */
311   /*
312   memcpy(&maps_layer_params[0].widget_data, &params_maptypes, sizeof(gpointer));
313   memcpy(&maps_layer_params[0].extra_widget_data, &params_maptypes_ids, sizeof(gpointer));
314   */
315   maps_layer_params[0].widget_data = params_maptypes;
316   maps_layer_params[0].extra_widget_data = params_maptypes_ids;
317 }
318
319 void _update_map_source ( const char *label, VikMapSource *map, int index )
320 {
321   GList *item = g_list_nth (__map_types, index);
322   g_object_unref (item->data);
323   item->data = g_object_ref (map);
324   /* Change previous data */
325   g_free (params_maptypes[index]);
326   params_maptypes[index] = g_strdup (label);
327 }
328
329 /**
330  * maps_layer_register_map_source:
331  * @map: the new VikMapSource
332  *
333  * Register a new VikMapSource.
334  * Override existing one (equality of id).
335  */
336 void maps_layer_register_map_source ( VikMapSource *map )
337 {
338   g_assert(map != NULL);
339   
340   guint id = vik_map_source_get_uniq_id(map);
341   const char *label = vik_map_source_get_label(map);
342   g_assert(label != NULL);
343
344   int previous = map_uniq_id_to_index (id);
345   if (previous != NUM_MAP_TYPES)
346   {
347     _update_map_source (label, map, previous);
348   }
349   else
350   {
351     _add_map_source (id, label, map);
352   }
353 }
354
355 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
356 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
357 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
358
359 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
360 {
361   return(vml->maptype);
362 }
363
364 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
365 {
366   return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
367 }
368
369 /****************************************/
370 /******** CACHE DIR STUFF ***************/
371 /****************************************/
372
373 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
374 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
375 #define MAPS_CACHE_DIR maps_layer_default_dir()
376
377 #ifdef WINDOWS
378 #include <io.h>
379 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
380 #define LOCAL_MAPS_DIR "VIKING-MAPS"
381 #elif defined __APPLE__
382 #include <stdlib.h>
383 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
384 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
385 #else /* POSIX */
386 #include <stdlib.h>
387 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
388 #define LOCAL_MAPS_DIR ".viking-maps"
389 #endif
390
391 gchar *maps_layer_default_dir ()
392 {
393   static gchar *defaultdir = NULL;
394   if ( ! defaultdir )
395   {
396     /* Thanks to Mike Davison for the $VIKING_MAPS usage */
397     const gchar *mapdir = g_getenv("VIKING_MAPS");
398     if ( mapdir ) {
399       defaultdir = g_strdup ( mapdir );
400     } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
401       defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
402     } else {
403       const gchar *home = g_get_home_dir();
404       if (!home || g_access(home, W_OK))
405         home = g_get_home_dir ();
406       if ( home )
407         defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
408       else
409         defaultdir = g_strdup ( LOCAL_MAPS_DIR );
410     }
411     if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
412     {
413       /* Add the separator at the end */
414       gchar *tmp = defaultdir;
415       defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
416       g_free(tmp);
417     }
418     g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
419   }
420   return defaultdir;
421 }
422
423 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
424 {
425   if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
426   {
427     g_mkdir ( vml->cache_dir, 0777 );
428   }
429 }
430
431 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
432 {
433   guint len;
434   g_assert ( vml != NULL);
435   g_free ( vml->cache_dir );
436   vml->cache_dir = NULL;
437   const gchar *mydir = dir;
438
439   if ( dir == NULL || dir[0] == '\0' )
440   {
441     if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
442       mydir = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s;
443   }
444
445   // Ensure cache_dir always ends with a separator
446   len = strlen(mydir);
447   if ( mydir[len-1] != G_DIR_SEPARATOR )
448   {
449     vml->cache_dir = g_malloc ( len+2 );
450     strncpy ( vml->cache_dir, mydir, len );
451     vml->cache_dir[len] = G_DIR_SEPARATOR;
452     vml->cache_dir[len+1] = '\0';
453   }
454   else
455     vml->cache_dir = g_strdup ( mydir );
456
457   maps_layer_mkdir_if_default_dir ( vml );
458 }
459
460 /****************************************/
461 /******** GOBJECT STUFF *****************/
462 /****************************************/
463
464 GType vik_maps_layer_get_type ()
465 {
466   static GType vml_type = 0;
467
468   if (!vml_type)
469   {
470     static const GTypeInfo vml_info =
471     {
472       sizeof (VikMapsLayerClass),
473       NULL, /* base_init */
474       NULL, /* base_finalize */
475       NULL, /* class init */
476       NULL, /* class_finalize */
477       NULL, /* class_data */
478       sizeof (VikMapsLayer),
479       0,
480       NULL /* instance init */
481     };
482     vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
483   }
484
485   return vml_type;
486 }
487
488 /****************************************/
489 /************** PARAMETERS **************/
490 /****************************************/
491
492 static guint map_index_to_uniq_id (guint16 index)
493 {
494   g_assert ( index < NUM_MAP_TYPES );
495   return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
496 }
497
498 static guint map_uniq_id_to_index ( guint uniq_id )
499 {
500   gint i;
501   for ( i = 0; i < NUM_MAP_TYPES; i++ )
502     if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
503       return i;
504   return NUM_MAP_TYPES; /* no such thing */
505 }
506
507 void vik_maps_layer_pretend_licence_shown ( VikMapsLayer *vml )
508 {
509   vml->license_notice_shown = TRUE;
510 }
511
512 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
513 {
514   // When loading from a file don't need the license reminder
515   if ( is_file_operation )
516     vml->license_notice_shown = TRUE;
517
518   switch ( id )
519   {
520     case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
521     case PARAM_MAPTYPE: {
522       gint maptype = map_uniq_id_to_index(data.u);
523       if ( maptype == NUM_MAP_TYPES ) g_warning(_("Unknown map type"));
524       else vml->maptype = maptype;
525       break;
526     }
527     case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
528     case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
529     case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
530     case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
531                           vml->mapzoom_id = data.u;
532                           vml->xmapzoom = __mapzooms_x [data.u];
533                           vml->ymapzoom = __mapzooms_y [data.u];
534                         }else g_warning (_("Unknown Map Zoom")); break;
535   }
536   return TRUE;
537 }
538
539 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
540 {
541   VikLayerParamData rv;
542   switch ( id )
543   {
544     case PARAM_CACHE_DIR:
545     {
546       gboolean set = FALSE;
547       /* Only save a blank when the map cache location equals the default
548           On reading in, when it is blank then the default is reconstructed
549           Since the default changes dependent on the user and OS, it means the resultant file is more portable */
550       if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
551         rv.s = "";
552         set = TRUE;
553       }
554       else if ( is_file_operation ) {
555         if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
556           gchar *cwd = g_get_current_dir();
557           if ( cwd ) {
558             rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
559             if ( !rv.s ) rv.s = "";
560             set = TRUE;
561           }
562         }
563       }
564       if ( !set )
565         rv.s = vml->cache_dir ? vml->cache_dir : "";
566       break;
567     }
568     case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
569     case PARAM_ALPHA: rv.u = vml->alpha; break;
570     case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
571     case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
572     case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
573   }
574   return rv;
575 }
576
577 /****************************************/
578 /****** CREATING, COPYING, FREEING ******/
579 /****************************************/
580
581 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
582 {
583   VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
584   vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
585
586   vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
587
588   vml->dl_tool_x = vml->dl_tool_y = -1;
589   vml->last_center = NULL;
590   vml->last_xmpp = 0.0;
591   vml->last_ympp = 0.0;
592
593   vml->dl_right_click_menu = NULL;
594   vml->license_notice_shown = FALSE;
595
596   return vml;
597 }
598
599 static void maps_layer_free ( VikMapsLayer *vml )
600 {
601   g_free ( vml->cache_dir );
602   vml->cache_dir = NULL;
603   if ( vml->dl_right_click_menu )
604     g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
605   g_free(vml->last_center);
606   vml->last_center = NULL;
607 }
608
609 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
610 {
611   if (from_file != TRUE)
612   {
613     /* If this method is not called in file reading context
614      * it is called in GUI context.
615      * So, we can check if we have to inform the user about inconsistency */
616     VikViewportDrawMode vp_drawmode;
617     VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
618     VikMapSource *map = NULL;
619  
620     vp_drawmode = vik_viewport_get_drawmode ( vp );
621     map = MAPS_LAYER_NTH_TYPE(vml->maptype);
622     if (vik_map_source_get_drawmode(map) != vp_drawmode) {
623       const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
624       gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
625       a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
626       g_free(msg);
627     }
628
629     if (vik_map_source_get_license (map) != NULL) {
630       if ( ! vml->license_notice_shown ) {
631         a_dialog_license (VIK_GTK_WINDOW_FROM_WIDGET(vp), vik_map_source_get_label (map),
632                           vik_map_source_get_license (map), vik_map_source_get_license_url (map) );
633         vml->license_notice_shown = TRUE;
634       }
635     }
636   }
637 }
638
639 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
640 {
641   return vik_maps_layer_get_map_label ( vml );
642 }
643
644 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
645 {
646   vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
647 }
648
649 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
650 {
651   VikMapsLayer *rv = maps_layer_new ( vvp );
652   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
653   return rv;
654 }
655
656 /*********************/
657 /****** DRAWING ******/
658 /*********************/
659
660 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
661 {
662   guchar *pixels;
663   gint width, height, iii, jjj;
664
665   if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
666   {
667     GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
668     g_object_unref(G_OBJECT(pixbuf));
669     pixbuf = tmp;
670     if ( !pixbuf )
671       return NULL;
672   }
673
674   pixels = gdk_pixbuf_get_pixels(pixbuf);
675   width = gdk_pixbuf_get_width(pixbuf);
676   height = gdk_pixbuf_get_height(pixbuf);
677
678   /* r,g,b,a,r,g,b,a.... */
679   for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
680   {
681     pixels += 3;
682     *pixels++ = alpha;
683   }
684   return pixbuf;
685 }
686
687 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
688 {
689   GdkPixbuf *tmp;
690   guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
691   tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
692   g_object_unref ( G_OBJECT(pixbuf) );
693   return tmp;
694 }
695
696 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
697 {
698   GdkPixbuf *pixbuf;
699
700   /* get the thing */
701   pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
702                             mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
703
704   if ( ! pixbuf ) {
705     if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
706       g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS,
707                    vml->cache_dir, (17 - mapcoord->scale), mapcoord->x, mapcoord->y, ".png" );
708     else
709       g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
710                    vml->cache_dir, mode,
711                    mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
712
713     if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
714     {
715       GError *gx = NULL;
716       pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
717
718       /* free the pixbuf on error */
719       if (gx)
720       {
721         if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
722           g_warning ( _("Couldn't open image file: %s"), gx->message );
723
724         g_error_free ( gx );
725         if ( pixbuf )
726           g_object_unref ( G_OBJECT(pixbuf) );
727         pixbuf = NULL;
728       } else {
729           if ( vml->alpha < 255 )
730             pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
731           if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
732             pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
733
734           if ( pixbuf )
735             a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
736                              mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
737                              mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
738       }
739     }
740   }
741   return pixbuf;
742 }
743
744 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
745 {
746   const VikCoord *center = vik_viewport_get_center ( vvp );
747
748   if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
749     /* D'n'D pan in action: do not download */
750     return FALSE;
751
752   // TEMPORARY HACK
753   // Prevent requests for downloading tiles at Zoom Level 19 and above for most map types
754   // Allow MapQuest Zoom Level up to 19
755   // TODO: This should be made a property of the map source and then use that value
756   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
757   if ( (vml->maptype != 19 && map_utils_mpp_to_scale (xzoom) < -1) || (vml->maptype == 19 && map_utils_mpp_to_scale (xzoom) < -2) )
758     return FALSE;
759
760   if (vml->last_center == NULL) {
761     VikCoord *new_center = g_malloc(sizeof(VikCoord));
762     *new_center = *center;
763     vml->last_center = new_center;
764     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
765     vml->last_ympp = vik_viewport_get_ympp(vvp);
766     return TRUE;
767   }
768
769   /* TODO: perhaps vik_coord_diff() */
770   if (vik_coord_equals(vml->last_center, center)
771       && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
772       && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
773     return FALSE;
774
775   *(vml->last_center) = *center;
776     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
777     vml->last_ympp = vik_viewport_get_ympp(vvp);
778   return TRUE;
779 }
780
781 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
782 {
783   MapCoord ulm, brm;
784   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
785   gdouble yzoom = vik_viewport_get_ympp ( vvp );
786   gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
787   gboolean existence_only = FALSE;
788
789   if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
790     xshrinkfactor = vml->xmapzoom / xzoom;
791     yshrinkfactor = vml->ymapzoom / yzoom;
792     xzoom = vml->xmapzoom;
793     yzoom = vml->xmapzoom;
794     if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
795          yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
796       if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
797         g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
798         existence_only = TRUE;
799       }
800       else {
801         g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
802         return;
803       }
804     }
805   }
806
807   /* coord -> ID */
808   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
809   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
810        vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
811
812     /* loop & draw */
813     gint x, y;
814     gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
815     gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
816     gint mode = vik_map_source_get_uniq_id(map);
817
818     VikCoord coord;
819     gint xx, yy, width, height;
820     GdkPixbuf *pixbuf;
821
822     // Prevent the program grinding to a halt if trying to deal with thousands of tiles
823     //  which can happen when using a small fixed zoom level and viewing large areas.
824     // Also prevents very large number of tile download requests
825     gint tiles = (xmax-xmin) * (ymax-ymin);
826     if ( tiles > MAX_TILES ) {
827       g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
828       existence_only = TRUE;
829     }
830
831     guint max_path_len = strlen(vml->cache_dir) + 40;
832     gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
833
834     if ( (!existence_only) && vml->autodownload  && should_start_autodownload(vml, vvp)) {
835       g_debug("%s: Starting autodownload", __FUNCTION__);
836       if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
837         // Try to download newer tiles
838         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
839       else
840         // Download only missing tiles
841         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
842     }
843
844     if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
845       for ( x = xmin; x <= xmax; x++ ) {
846         for ( y = ymin; y <= ymax; y++ ) {
847           ulm.x = x;
848           ulm.y = y;
849           pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
850           if ( pixbuf ) {
851             width = gdk_pixbuf_get_width ( pixbuf );
852             height = gdk_pixbuf_get_height ( pixbuf );
853
854             vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
855             vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
856             xx -= (width/2);
857             yy -= (height/2);
858
859             vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
860           }
861         }
862       }
863     } else { /* tilesize is known, don't have to keep converting coords */
864       gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
865       gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
866       /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
867       gint tilesize_x_ceil = ceil ( tilesize_x );
868       gint tilesize_y_ceil = ceil ( tilesize_y );
869       gint8 xinc = (ulm.x == xmin) ? 1 : -1;
870       gint8 yinc = (ulm.y == ymin) ? 1 : -1;
871       gdouble xx, yy; gint xx_tmp, yy_tmp;
872       gint base_yy, xend, yend;
873
874       xend = (xinc == 1) ? (xmax+1) : (xmin-1);
875       yend = (yinc == 1) ? (ymax+1) : (ymin-1);
876
877       vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
878       vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
879       xx = xx_tmp; yy = yy_tmp;
880       /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
881        * eg if tile size 128, shrinkfactor 0.333 */
882       xx -= (tilesize_x/2);
883       base_yy = yy - (tilesize_y/2);
884
885       for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
886         yy = base_yy;
887         for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
888           ulm.x = x;
889           ulm.y = y;
890
891           if ( existence_only ) {
892             if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
893               g_snprintf ( path_buf, max_path_len, DIRECTDIRACCESS,
894                            vml->cache_dir, (17 - ulm.scale), ulm.x, ulm.y, ".png" );
895             else
896               g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
897                            vml->cache_dir, mode,
898                            ulm.scale, ulm.z, ulm.x, ulm.y );
899             if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
900               GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
901               vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
902             }
903           } else {
904             int scale_inc;
905             for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
906               /* try with correct then smaller zooms */
907               int scale_factor = 1 << scale_inc;  /*  2^scale_inc */
908               MapCoord ulm2 = ulm;
909               ulm2.x = ulm.x / scale_factor;
910               ulm2.y = ulm.y / scale_factor;
911               ulm2.scale = ulm.scale + scale_inc;
912               pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
913               if ( pixbuf ) {
914                 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
915                 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
916 #ifdef DEBUG
917                 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);
918 #endif
919                 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
920                 break;
921               }
922             }
923             if ( !pixbuf ) {
924               /* retry with bigger zooms */
925               int scale_dec;
926               for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
927                 int pict_x, pict_y;
928                 int scale_factor = 1 << scale_dec;  /*  2^scale_dec */
929                 MapCoord ulm2 = ulm;
930                 ulm2.x = ulm.x * scale_factor;
931                 ulm2.y = ulm.y * scale_factor;
932                 ulm2.scale = ulm.scale - scale_dec;
933                 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
934                   for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
935                     MapCoord ulm3 = ulm2;
936                     ulm3.x += pict_x;
937                     ulm3.y += pict_y;
938                     pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
939                     if ( pixbuf ) {
940                       gint src_x = 0;
941                       gint src_y = 0;
942                       gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
943                       gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
944                       vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
945                     }
946                   }
947                 }
948               }
949             }
950           }
951
952           yy += tilesize_y;
953         }
954         xx += tilesize_x;
955       }
956     }
957
958     g_free ( path_buf );
959   }
960 }
961
962 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
963 {
964   if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
965   {
966     VikCoord ul, br;
967
968     /* Copyright */
969     gdouble level = vik_viewport_get_zoom ( vvp );
970     LatLonBBox bbox;
971     vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
972     vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
973
974     /* Logo */
975     const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
976     vik_viewport_add_logo ( vvp, logo );
977
978     /* get corner coords */
979     if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
980       /* UTM multi-zone stuff by Kit Transue */
981       gchar leftmost_zone, rightmost_zone, i;
982       leftmost_zone = vik_viewport_leftmost_zone( vvp );
983       rightmost_zone = vik_viewport_rightmost_zone( vvp );
984       for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
985         vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
986         maps_layer_draw_section ( vml, vvp, &ul, &br );
987       }
988     }
989     else {
990       vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
991       vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
992
993       maps_layer_draw_section ( vml, vvp, &ul, &br );
994     }
995   }
996 }
997
998 /*************************/
999 /****** DOWNLOADING ******/
1000 /*************************/
1001
1002 /* pass along data to thread, exists even if layer is deleted. */
1003 typedef struct {
1004   gchar *cache_dir;
1005   gchar *filename_buf;
1006   gint x0, y0, xf, yf;
1007   MapCoord mapcoord;
1008   gint maptype;
1009   gint maxlen;
1010   gint mapstoget;
1011   gint redownload;
1012   gboolean refresh_display;
1013   VikMapsLayer *vml;
1014   VikViewport *vvp;
1015   gboolean map_layer_alive;
1016   GMutex *mutex;
1017 } MapDownloadInfo;
1018
1019 static void mdi_free ( MapDownloadInfo *mdi )
1020 {
1021   g_mutex_free(mdi->mutex);
1022   g_free ( mdi->cache_dir );
1023   mdi->cache_dir = NULL;
1024   g_free ( mdi->filename_buf );
1025   mdi->filename_buf = NULL;
1026   g_free ( mdi );
1027 }
1028
1029 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1030 {
1031   MapDownloadInfo *mdi = ptr;
1032   g_mutex_lock(mdi->mutex);
1033   mdi->map_layer_alive = FALSE;
1034   g_mutex_unlock(mdi->mutex);
1035 }
1036
1037 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1038 {
1039   void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1040   guint donemaps = 0;
1041   gint x, y;
1042   for ( x = mdi->x0; x <= mdi->xf; x++ )
1043   {
1044     for ( y = mdi->y0; y <= mdi->yf; y++ )
1045     {
1046       gboolean remove_mem_cache = FALSE;
1047       gboolean need_download = FALSE;
1048       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1049                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1050                      mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
1051
1052       donemaps++;
1053       int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1054       if (res != 0) {
1055         vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1056         return -1;
1057       }
1058
1059       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1060         need_download = TRUE;
1061         remove_mem_cache = TRUE;
1062
1063       } else {  /* in case map file already exists */
1064         switch (mdi->redownload) {
1065           case REDOWNLOAD_NONE:
1066             continue;
1067
1068           case REDOWNLOAD_BAD:
1069           {
1070             /* see if this one is bad or what */
1071             GError *gx = NULL;
1072             GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1073             if (gx || (!pixbuf)) {
1074               g_remove ( mdi->filename_buf );
1075               need_download = TRUE;
1076               remove_mem_cache = TRUE;
1077               g_error_free ( gx );
1078
1079             } else {
1080               g_object_unref ( pixbuf );
1081             }
1082             break;
1083           }
1084
1085           case REDOWNLOAD_NEW:
1086             need_download = TRUE;
1087             remove_mem_cache = TRUE;
1088             break;
1089
1090           case REDOWNLOAD_ALL:
1091             /* FIXME: need a better way than to erase file in case of server/network problem */
1092             g_remove ( mdi->filename_buf );
1093             need_download = TRUE;
1094             remove_mem_cache = TRUE;
1095             break;
1096
1097           case DOWNLOAD_OR_REFRESH:
1098             remove_mem_cache = TRUE;
1099             break;
1100
1101           default:
1102             g_warning ( "redownload state %d unknown\n", mdi->redownload);
1103         }
1104       }
1105
1106       mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1107
1108       if (need_download) {
1109         if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
1110           continue;
1111       }
1112
1113       g_mutex_lock(mdi->mutex);
1114       if (remove_mem_cache)
1115           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 );
1116       if (mdi->refresh_display && mdi->map_layer_alive) {
1117         /* TODO: check if it's on visible area */
1118         vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1119       }
1120       g_mutex_unlock(mdi->mutex);
1121       mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1122
1123     }
1124   }
1125   vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1126   g_mutex_lock(mdi->mutex);
1127   if (mdi->map_layer_alive)
1128     g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1129   g_mutex_unlock(mdi->mutex); 
1130   return 0;
1131 }
1132
1133 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1134 {
1135   if ( mdi->mapcoord.x || mdi->mapcoord.y )
1136   {
1137     g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1138                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1139                      mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
1140     if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1141     {
1142       g_remove ( mdi->filename_buf );
1143     }
1144   }
1145 }
1146
1147 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1148 {
1149   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1150   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1151   MapCoord ulm, brm;
1152   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1153
1154   // Don't ever attempt download on direct access
1155   if ( vik_map_source_is_direct_file_access ( map ) )
1156     return;
1157
1158   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) 
1159     && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1160   {
1161     MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1162     gint a, b;
1163
1164     mdi->vml = vml;
1165     mdi->vvp = vvp;
1166     mdi->map_layer_alive = TRUE;
1167     mdi->mutex = g_mutex_new();
1168     mdi->refresh_display = TRUE;
1169
1170     /* cache_dir and buffer for dest filename */
1171     mdi->cache_dir = g_strdup ( vml->cache_dir );
1172     mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1173     mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1174     mdi->maptype = vml->maptype;
1175
1176     mdi->mapcoord = ulm;
1177     mdi->redownload = redownload;
1178
1179     mdi->x0 = MIN(ulm.x, brm.x);
1180     mdi->xf = MAX(ulm.x, brm.x);
1181     mdi->y0 = MIN(ulm.y, brm.y);
1182     mdi->yf = MAX(ulm.y, brm.y);
1183
1184     mdi->mapstoget = 0;
1185
1186     if ( mdi->redownload ) {
1187       mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1188     } else {
1189       /* calculate how many we need */
1190       for ( a = mdi->x0; a <= mdi->xf; a++ )
1191       {
1192         for ( b = mdi->y0; b <= mdi->yf; b++ )
1193         {
1194           g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1195                        vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1196                        ulm.z, a, b );
1197           if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1198             mdi->mapstoget++;
1199         }
1200       }
1201     }
1202
1203     mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1204
1205     if ( mdi->mapstoget )
1206     {
1207       const gchar *tmp_str;
1208       gchar *tmp;
1209
1210       if (redownload) 
1211       {
1212         if (redownload == REDOWNLOAD_BAD)
1213           tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1214         else
1215           tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1216       } 
1217       else 
1218       {
1219         tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1220       }
1221       tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1222  
1223       g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1224       /* launch the thread */
1225       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1226                             tmp,                                              /* description string */
1227                             (vik_thr_func) map_download_thread,               /* function to call within thread */
1228                             mdi,                                              /* pass along data */
1229                             (vik_thr_free_func) mdi_free,                     /* function to free pass along data */
1230                             (vik_thr_free_func) mdi_cancel_cleanup,
1231                             mdi->mapstoget );
1232       g_free ( tmp );
1233     }
1234     else
1235       mdi_free ( mdi );
1236   }
1237 }
1238
1239 static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1240 {
1241   MapCoord ulm, brm;
1242   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1243
1244   // Don't ever attempt download on direct access
1245   if ( vik_map_source_is_direct_file_access ( map ) )
1246     return;
1247
1248   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm) 
1249     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1250     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1251     return;
1252   }
1253
1254   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1255   gint i, j;
1256
1257   mdi->vml = vml;
1258   mdi->vvp = vvp;
1259   mdi->map_layer_alive = TRUE;
1260   mdi->mutex = g_mutex_new();
1261   mdi->refresh_display = TRUE;
1262
1263   mdi->cache_dir = g_strdup ( vml->cache_dir );
1264   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1265   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1266   mdi->maptype = vml->maptype;
1267
1268   mdi->mapcoord = ulm;
1269   mdi->redownload = download_method;
1270
1271   mdi->x0 = MIN(ulm.x, brm.x);
1272   mdi->xf = MAX(ulm.x, brm.x);
1273   mdi->y0 = MIN(ulm.y, brm.y);
1274   mdi->yf = MAX(ulm.y, brm.y);
1275
1276   mdi->mapstoget = 0;
1277
1278   for (i = mdi->x0; i <= mdi->xf; i++) {
1279     for (j = mdi->y0; j <= mdi->yf; j++) {
1280       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1281                    vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1282                    ulm.z, i, j );
1283       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1284             mdi->mapstoget++;
1285     }
1286   }
1287
1288   mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1289
1290   if (mdi->mapstoget) {
1291     gchar *tmp;
1292     const gchar *fmt;
1293     fmt = ngettext("Downloading %d %s map...",
1294                    "Downloading %d %s maps...",
1295                    mdi->mapstoget);
1296     tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1297
1298     g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1299       /* launch the thread */
1300     a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1301       tmp,                                /* description string */
1302       (vik_thr_func) map_download_thread, /* function to call within thread */
1303       mdi,                                /* pass along data */
1304       (vik_thr_free_func) mdi_free,       /* function to free pass along data */
1305       (vik_thr_free_func) mdi_cancel_cleanup,
1306       mdi->mapstoget );
1307     g_free ( tmp );
1308   }
1309   else
1310     mdi_free ( mdi );
1311 }
1312
1313 /**
1314  * vik_maps_layer_download_section:
1315  * @vml:  The Map Layer
1316  * @vvp:  The Viewport that the map is on
1317  * @ul:   Upper left coordinate of the area to be downloaded
1318  * @br:   Bottom right coordinate of the area to be downloaded
1319  * @zoom: The zoom level at which the maps are to be download
1320  *
1321  * Download a specified map area at a certain zoom level
1322  */
1323 void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1324 {
1325   maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1326 }
1327
1328 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1329 {
1330   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1331 }
1332
1333 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1334 {
1335   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1336 }
1337
1338 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1339 {
1340   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1341 }
1342
1343 /**
1344  * Display a simple dialog with information about this particular map tile
1345  */
1346 static void maps_layer_tile_info ( VikMapsLayer *vml )
1347 {
1348   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1349
1350   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1351   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1352   MapCoord ulm;
1353
1354   if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1355     return;
1356
1357   gchar *filename = NULL;
1358   gchar *message = NULL;
1359   gchar *source = NULL;
1360
1361   if ( vik_map_source_is_direct_file_access ( map ) ) {
1362     filename = g_strdup_printf ( DIRECTDIRACCESS, vml->cache_dir, ulm.scale, ulm.x, ulm.y, ".png" );
1363     source = g_strconcat ( "file://", filename, NULL );
1364   }
1365   else {
1366         filename = g_strdup_printf ( DIRSTRUCTURE, vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale, ulm.z, ulm.x, ulm.y );
1367     source = g_strdup_printf ( "http://%s%s",
1368                                vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1369                                vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1370   }
1371
1372   if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1373
1374     // Get some timestamp information of the tile
1375     struct stat stat_buf;
1376     if ( g_stat ( filename, &stat_buf ) == 0 ) {
1377       gchar time_buf[64];
1378       strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1379       message = g_strdup_printf ( _("\nSource: %s\n\nTile File: %s\nTile File Timestamp: %s"), source, filename, time_buf );
1380     }
1381   }
1382   else
1383     message = g_strdup_printf ( _("Source: %s\n\nNo Tile File!"), source );
1384
1385   // Show the info
1386   a_dialog_info_msg (  VIK_GTK_WINDOW_FROM_LAYER(vml), message );
1387
1388   g_free ( message );
1389   g_free ( source );
1390   g_free ( filename );
1391 }
1392
1393 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1394 {
1395   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1396     return FALSE;
1397   if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1398   {
1399     if ( event->button == 1 )
1400     {
1401       VikCoord ul, br;
1402       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 );
1403       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 );
1404       start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1405       vml->dl_tool_x = vml->dl_tool_y = -1;
1406       return TRUE;
1407     }
1408     else
1409     {
1410       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) );
1411       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) );
1412
1413       vml->redownload_vvp = vvp;
1414
1415       vml->dl_tool_x = vml->dl_tool_y = -1;
1416
1417       if ( ! vml->dl_right_click_menu ) {
1418         GtkWidget *item;
1419         vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1420
1421         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1422         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1423         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1424
1425         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1426         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1427         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1428
1429         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1430         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1431         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1432
1433         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
1434         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1435         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
1436         gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
1437       }
1438
1439       gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1440       gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1441     }
1442   }
1443   return FALSE;
1444 }
1445
1446 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1447 {
1448   return vvp;
1449 }
1450
1451 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1452 {
1453   MapCoord tmp;
1454   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1455     return FALSE;
1456   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1457   if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1458        vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1459            vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1460            vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1461            &tmp ) ) {
1462     vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1463     return TRUE;
1464   }
1465   return FALSE;
1466
1467  
1468 #if 0
1469   if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1470   {
1471     VikCoord coord;
1472     MapCoord mapcoord;
1473     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1474     if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1475                 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1476                 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1477                 &mapcoord ) ) {
1478       gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1479                      vml->cache_dir, __map_types[vml->maptype].uniq_id,
1480                      mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1481
1482       __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1483       g_free ( filename_buf );
1484       vik_layer_emit_update ( VIK_LAYER(vml) );
1485       return TRUE;
1486     }
1487   }
1488   return FALSE;
1489 #endif
1490 }
1491
1492 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1493 {
1494   VikMapsLayer *vml = vml_vvp[0];
1495   VikViewport *vvp = vml_vvp[1];
1496   VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1497
1498   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1499   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1500
1501   VikCoord ul, br;
1502   MapCoord ulm, brm;
1503
1504   vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1505   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1506
1507   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1508   if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1509        vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1510        vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1511     start_download_thread ( vml, vvp, &ul, &br, redownload );
1512   else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1513     const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1514     gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1515     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1516     g_free(err);
1517   }
1518   else
1519     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1520
1521 }
1522
1523 static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
1524 {
1525   download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1526 }
1527
1528 static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
1529 {
1530   download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
1531 }
1532
1533 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1534 {
1535   download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1536 }
1537
1538 /**
1539  * maps_layer_how_many_maps:
1540  * Copied from maps_layer_download_section but without the actual download and this returns a value
1541  */
1542 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
1543 {
1544   MapCoord ulm, brm;
1545   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1546
1547   if ( vik_map_source_is_direct_file_access ( map ) )
1548     return 0;
1549
1550   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1551     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1552     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1553     return 0;
1554   }
1555
1556   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1557   gint i, j;
1558
1559   mdi->vml = vml;
1560   mdi->vvp = vvp;
1561   mdi->map_layer_alive = TRUE;
1562   mdi->mutex = g_mutex_new();
1563   mdi->refresh_display = FALSE;
1564
1565   mdi->cache_dir = g_strdup ( vml->cache_dir );
1566   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1567   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1568   mdi->maptype = vml->maptype;
1569
1570   mdi->mapcoord = ulm;
1571   mdi->redownload = redownload;
1572
1573   mdi->x0 = MIN(ulm.x, brm.x);
1574   mdi->xf = MAX(ulm.x, brm.x);
1575   mdi->y0 = MIN(ulm.y, brm.y);
1576   mdi->yf = MAX(ulm.y, brm.y);
1577
1578   mdi->mapstoget = 0;
1579
1580   if ( mdi->redownload == REDOWNLOAD_ALL ) {
1581     mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1582   }
1583   else {
1584     /* calculate how many we need */
1585     for (i = mdi->x0; i <= mdi->xf; i++) {
1586       for (j = mdi->y0; j <= mdi->yf; j++) {
1587         g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1588                      vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1589                      ulm.z, i, j );
1590         if ( mdi->redownload == REDOWNLOAD_NEW ) {
1591           // Assume the worst - always a new file
1592           // Absolute value would requires server lookup - but that is too slow
1593           mdi->mapstoget++;
1594         }
1595         else {
1596           if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1597             // Missing
1598             mdi->mapstoget++;
1599           }
1600           else {
1601             if ( mdi->redownload == REDOWNLOAD_BAD ) {
1602               /* see if this one is bad or what */
1603               GError *gx = NULL;
1604               GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1605               if (gx || (!pixbuf)) {
1606                 mdi->mapstoget++;
1607               }
1608               break;
1609               // Other download cases already considered or just ignored
1610             }
1611           }
1612         }
1613       }
1614     }
1615   }
1616
1617   gint rv = mdi->mapstoget;
1618
1619   mdi_free ( mdi );
1620
1621   return rv;
1622 }
1623
1624 /**
1625  * maps_dialog_zoom_between:
1626  * This dialog is specific to the map layer, so it's here rather than in dialog.c
1627  */
1628 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
1629                                     gchar *title,
1630                                     gchar *zoom_list[],
1631                                     gint default_zoom1,
1632                                     gint default_zoom2,
1633                                     gint *selected_zoom1,
1634                                     gint *selected_zoom2,
1635                                     gchar *download_list[],
1636                                     gint default_download,
1637                                     gint *selected_download )
1638 {
1639   GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
1640                                                     parent,
1641                                                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1642                                                     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1643                                                     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1644                                                     NULL );
1645   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1646   GtkWidget *response_w = NULL;
1647 #if GTK_CHECK_VERSION (2, 20, 0)
1648   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1649 #endif
1650   GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
1651   GtkWidget *zoom_combo1 = vik_combo_box_text_new();
1652   gchar **s;
1653   for (s = zoom_list; *s; s++)
1654     vik_combo_box_text_append ( zoom_combo1, *s );
1655   gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
1656
1657   GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
1658   GtkWidget *zoom_combo2 = vik_combo_box_text_new();
1659   for (s = zoom_list; *s; s++)
1660     vik_combo_box_text_append ( zoom_combo2, *s );
1661   gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
1662
1663   GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
1664   GtkWidget *download_combo = vik_combo_box_text_new();
1665   for (s = download_list; *s; s++)
1666     vik_combo_box_text_append ( download_combo, *s );
1667   gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
1668
1669   GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
1670   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
1671   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
1672   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
1673   gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
1674   gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
1675   gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
1676
1677   gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
1678
1679   if ( response_w )
1680     gtk_widget_grab_focus ( response_w );
1681
1682   gtk_widget_show_all ( dialog );
1683   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
1684     gtk_widget_destroy(dialog);
1685     return FALSE;
1686   }
1687
1688   // Return selected options
1689   *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
1690   *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
1691   *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
1692
1693   gtk_widget_destroy(dialog);
1694   return TRUE;
1695 }
1696
1697 // My best guess of sensible limits
1698 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
1699 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
1700
1701 /**
1702  * Get all maps in the region for zoom levels specified by the user
1703  * Sort of similar to trw_layer_download_map_along_track_cb function
1704  */
1705 static void maps_layer_download_all ( gpointer vml_vvp[2] )
1706 {
1707   VikMapsLayer *vml = vml_vvp[0];
1708   VikViewport *vvp = vml_vvp[1];
1709
1710   // I don't think we should allow users to hammer the servers too much...
1711   // Delibrately not allowing lowest zoom levels
1712   // Still can give massive numbers to download
1713   // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
1714   gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
1715   gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
1716
1717   gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
1718   gint selected_download_method;
1719   
1720   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
1721
1722   for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
1723     if (cur_zoom == zoom_vals[default_zoom])
1724       break;
1725   }
1726   default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
1727
1728   // Default to only 2 zoom levels below the current one
1729   if (default_zoom > 1 )
1730     lower_zoom = default_zoom - 2;
1731   else
1732     lower_zoom = default_zoom;
1733
1734   // redownload method - needs to align with REDOWNLOAD* macro values
1735   gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
1736
1737   gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
1738
1739   if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
1740                                     title,
1741                                     zoom_list,
1742                                     lower_zoom,
1743                                     default_zoom,
1744                                     &selected_zoom1,
1745                                     &selected_zoom2,
1746                                     download_list,
1747                                     REDOWNLOAD_NONE, // AKA Missing
1748                                     &selected_download_method ) ) {
1749     // Cancelled
1750     g_free ( title );
1751     return;
1752   }
1753   g_free ( title );
1754
1755   // Find out new current positions
1756   gdouble min_lat, max_lat, min_lon, max_lon;
1757   VikCoord vc_ul, vc_br;
1758   vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1759   struct LatLon ll_ul = { max_lat, min_lon };
1760   struct LatLon ll_br = { min_lat, max_lon };
1761   vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
1762   vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
1763
1764   // Get Maps Count - call for each zoom level (in reverse)
1765   // With REDOWNLOAD_NEW this is a possible maximum
1766   // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
1767   gint map_count = 0;
1768   gint zz;
1769   for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
1770     map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
1771   }
1772
1773   g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
1774
1775   // Absolute protection of hammering a map server
1776   if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
1777     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);
1778     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
1779     g_free (str);
1780     return;
1781   }
1782
1783   // Confirm really want to do this
1784   if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
1785     gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
1786     gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
1787     g_free (str);
1788     if ( ! ans )
1789       return;
1790   }
1791
1792   // Get Maps - call for each zoom level (in reverse)
1793   for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
1794     maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
1795   }
1796 }
1797
1798 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1799 {
1800   static gpointer pass_along[2];
1801   GtkWidget *item;
1802   pass_along[0] = vml;
1803   pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1804
1805   item = gtk_menu_item_new();
1806   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1807   gtk_widget_show ( item );
1808
1809   /* Now with icons */
1810   item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
1811     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
1812   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
1813   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1814   gtk_widget_show ( item );
1815
1816   if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
1817     item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
1818     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
1819     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
1820     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1821     gtk_widget_show ( item );
1822   }
1823
1824   item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
1825   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
1826   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1827   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1828   gtk_widget_show ( item );
1829
1830   item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
1831   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
1832   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), pass_along );
1833   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1834   gtk_widget_show ( item );
1835 }
1836
1837 /**
1838  * Enable downloading maps of the current screen area either 'new' or 'everything'
1839  */
1840 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
1841 {
1842   if ( !vml ) return;
1843   if ( !vvp ) return;
1844
1845   static gpointer pass_along[2];
1846   pass_along[0] = vml;
1847   pass_along[1] = vvp;
1848
1849   if ( only_new )
1850     // Get only new maps
1851     maps_layer_download_new_onscreen_maps ( pass_along );
1852   else
1853     // Redownload everything
1854     maps_layer_redownload_all_onscreen_maps ( pass_along );
1855 }