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