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