]> git.street.me.uk Git - andy/viking.git/blob - src/vikmapslayer.c
Add map layer option to only download missing tiles.
[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 #define MAX_SHRINKFACTOR 8.0000001 /* zoom 1 viewing 8-tiles */
30 #define MIN_SHRINKFACTOR 0.0312499 /* zoom 32 viewing 1-tiles */
31
32 #define REAL_MIN_SHRINKFACTOR 0.0039062499 /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
33
34 #include <gtk/gtk.h>
35 #include <gdk-pixbuf/gdk-pixdata.h>
36 #include <glib.h>
37 #include <glib/gstdio.h>
38 #include <glib/gi18n.h>
39
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #endif
43 #ifdef HAVE_MATH_H
44 #include <math.h>
45 #endif
46
47 #include "globals.h"
48 #include "util.h"
49 #include "coords.h"
50 #include "vikcoord.h"
51 #include "viktreeview.h"
52 #include "vikviewport.h"
53 #include "viklayer.h"
54 #include "vikmapslayer.h"
55
56 #ifdef HAVE_UNISTD_H
57 #include <unistd.h>
58 #endif
59
60 #include "mapcache.h"
61 /* only for dialog.h -- ugh */
62 #include "vikwaypoint.h"
63 #include "dialog.h"
64 #include "preferences.h"
65
66 #include "vikstatus.h"
67 #include "background.h"
68
69 #include "vikaggregatelayer.h"
70 #include "viklayerspanel.h"
71
72 #include "mapcoord.h"
73
74 #include "icons/icons.h"
75
76 /****** MAP TYPES ******/
77
78 static GList *__map_types = NULL;
79
80 #define NUM_MAP_TYPES g_list_length(__map_types)
81
82 /* List of label for each map type */
83 static gchar **params_maptypes = NULL;
84
85 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
86 static guint *params_maptypes_ids = NULL;
87
88 /******** MAPZOOMS *********/
89
90 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 };
91 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 };
92 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 };
93
94 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
95
96 /**************************/
97
98
99 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
100 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
101 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
102 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
103 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
104 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
105 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
106 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
107 static void maps_layer_free ( VikMapsLayer *vml );
108 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
109 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
110 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
111 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
112 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
113 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
114 static guint map_uniq_id_to_index ( guint uniq_id );
115
116
117 static VikLayerParamScale params_scales[] = {
118   /* min, max, step, digits (decimal places) */
119  { 0, 255, 3, 0 }, /* alpha */
120 };
121
122 VikLayerParam maps_layer_params[] = {
123   { "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL },
124   { "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, NULL },
125   { "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
126     N_("Control the Alpha value for transparency effects") },
127   { "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
128   { "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
129     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.") },
130   { "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
131     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.") },
132 };
133
134 enum {
135   PARAM_MAPTYPE=0,
136   PARAM_CACHE_DIR,
137   PARAM_ALPHA,
138   PARAM_AUTODOWNLOAD,
139   PARAM_ONLYMISSING,
140   PARAM_MAPZOOM,
141   NUM_PARAMS
142 };
143
144 static VikToolInterface maps_tools[] = {
145   { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
146     (VikToolConstructorFunc) maps_layer_download_create,
147     NULL,
148     NULL,
149     NULL,
150     (VikToolMouseFunc) maps_layer_download_click,
151     NULL,
152     (VikToolMouseFunc) maps_layer_download_release,
153     NULL,
154     FALSE,
155     GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf },
156 };
157
158 VikLayerInterface vik_maps_layer_interface = {
159   "Map",
160   N_("Map"),
161   "<control><shift>M",
162   &vikmapslayer_pixbuf,
163
164   maps_tools,
165   sizeof(maps_tools) / sizeof(maps_tools[0]),
166
167   maps_layer_params,
168   NUM_PARAMS,
169   NULL,
170   0,
171
172   VIK_MENU_ITEM_ALL,
173
174   (VikLayerFuncCreate)                  maps_layer_new,
175   (VikLayerFuncRealize)                 NULL,
176   (VikLayerFuncPostRead)                maps_layer_post_read,
177   (VikLayerFuncFree)                    maps_layer_free,
178
179   (VikLayerFuncProperties)              NULL,
180   (VikLayerFuncDraw)                    maps_layer_draw,
181   (VikLayerFuncChangeCoordMode)         NULL,
182
183   (VikLayerFuncSetMenuItemsSelection)   NULL,
184   (VikLayerFuncGetMenuItemsSelection)   NULL,
185
186   (VikLayerFuncAddMenuItems)            maps_layer_add_menu_items,
187   (VikLayerFuncSublayerAddMenuItems)    NULL,
188
189   (VikLayerFuncSublayerRenameRequest)   NULL,
190   (VikLayerFuncSublayerToggleVisible)   NULL,
191   (VikLayerFuncSublayerTooltip)         NULL,
192   (VikLayerFuncLayerTooltip)            maps_layer_tooltip,
193   (VikLayerFuncLayerSelected)           NULL,
194
195   (VikLayerFuncMarshall)                maps_layer_marshall,
196   (VikLayerFuncUnmarshall)              maps_layer_unmarshall,
197
198   (VikLayerFuncSetParam)                maps_layer_set_param,
199   (VikLayerFuncGetParam)                maps_layer_get_param,
200
201   (VikLayerFuncReadFileData)            NULL,
202   (VikLayerFuncWriteFileData)           NULL,
203
204   (VikLayerFuncDeleteItem)              NULL,
205   (VikLayerFuncCutItem)                 NULL,
206   (VikLayerFuncCopyItem)                NULL,
207   (VikLayerFuncPasteItem)               NULL,
208   (VikLayerFuncFreeCopiedItem)          NULL,
209   (VikLayerFuncDragDropRequest)         NULL,
210
211   (VikLayerFuncSelectClick)             NULL,
212   (VikLayerFuncSelectMove)              NULL,
213   (VikLayerFuncSelectRelease)           NULL,
214   (VikLayerFuncSelectedViewportMenu)    NULL,
215 };
216
217 struct _VikMapsLayer {
218   VikLayer vl;
219   guint maptype;
220   gchar *cache_dir;
221   guint8 alpha;
222   guint mapzoom_id;
223   gdouble xmapzoom, ymapzoom;
224
225   gboolean autodownload;
226   gboolean adl_only_missing;
227   VikCoord *last_center;
228   gdouble last_xmpp;
229   gdouble last_ympp;
230
231   gint dl_tool_x, dl_tool_y;
232
233   GtkMenu *dl_right_click_menu;
234   VikCoord redownload_ul, redownload_br; /* right click menu only */
235   VikViewport *redownload_vvp;
236
237   gboolean license_notice_shown; // FALSE for new maps only, otherwise
238                                  // TRUE for saved maps & other layer changes as we don't need to show it again
239 };
240
241 enum { REDOWNLOAD_NONE = 0,    /* download only missing maps */
242        REDOWNLOAD_BAD,         /* download missing and bad maps */
243        REDOWNLOAD_NEW,         /* download missing maps that are newer on server only */
244        REDOWNLOAD_ALL,         /* download all maps */
245        DOWNLOAD_OR_REFRESH };  /* download missing maps and refresh cache */
246
247 static VikLayerParam prefs[] = {
248   { VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Default maplayer directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, N_("Choose a directory to store cached Map tiles for this layer") },
249 };
250
251 void maps_layer_init ()
252 {
253   VikLayerParamData tmp;
254   tmp.s = maps_layer_default_dir();
255   a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
256 }
257
258 /****************************************/
259 /******** MAPS LAYER TYPES **************/
260 /****************************************/
261
262 int _get_index_for_id ( guint id )
263 {
264   int index = 0 ;
265   while (params_maptypes_ids[index] != 0)
266   {
267     if (params_maptypes_ids[index] == id)
268       return index;
269     index++;
270   }
271   return -1;
272 }
273
274 void _add_map_source ( guint id, const char *label, VikMapSource *map )
275 {
276   gsize len = 0;
277   if (params_maptypes)
278     len = g_strv_length (params_maptypes);
279   /* Add the label */
280   params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
281   params_maptypes[len] = g_strdup (label);
282   params_maptypes[len+1] = NULL;
283
284   /* Add the id */
285   params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
286   params_maptypes_ids[len] = id;
287   params_maptypes_ids[len+1] = 0;
288
289   /* We have to clone */
290   VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
291   /* Register the clone in the list */
292   __map_types = g_list_append(__map_types, clone);
293
294   /* Hack
295      We have to ensure the mode LayerParam references the up-to-date
296      GLists.
297   */
298   /*
299   memcpy(&maps_layer_params[0].widget_data, &params_maptypes, sizeof(gpointer));
300   memcpy(&maps_layer_params[0].extra_widget_data, &params_maptypes_ids, sizeof(gpointer));
301   */
302   maps_layer_params[0].widget_data = params_maptypes;
303   maps_layer_params[0].extra_widget_data = params_maptypes_ids;
304 }
305
306 void _update_map_source ( const char *label, VikMapSource *map, int index )
307 {
308   GList *item = g_list_nth (__map_types, index);
309   g_object_unref (item->data);
310   item->data = g_object_ref (map);
311   /* Change previous data */
312   g_free (params_maptypes[index]);
313   params_maptypes[index] = g_strdup (label);
314 }
315
316 /**
317  * maps_layer_register_map_source:
318  * @map: the new VikMapSource
319  *
320  * Register a new VikMapSource.
321  * Override existing one (equality of id).
322  */
323 void maps_layer_register_map_source ( VikMapSource *map )
324 {
325   g_assert(map != NULL);
326   
327   guint id = vik_map_source_get_uniq_id(map);
328   const char *label = vik_map_source_get_label(map);
329   g_assert(label != NULL);
330
331   int previous = map_uniq_id_to_index (id);
332   if (previous != NUM_MAP_TYPES)
333   {
334     _update_map_source (label, map, previous);
335   }
336   else
337   {
338     _add_map_source (id, label, map);
339   }
340 }
341
342 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
343 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
344 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
345
346 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
347 {
348   return(vml->maptype);
349 }
350
351 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
352 {
353   return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
354 }
355
356 /****************************************/
357 /******** CACHE DIR STUFF ***************/
358 /****************************************/
359
360 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
361 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
362 #define MAPS_CACHE_DIR maps_layer_default_dir()
363
364 #ifdef WINDOWS
365 #include <io.h>
366 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
367 #define LOCAL_MAPS_DIR "VIKING-MAPS"
368 #elif defined __APPLE__
369 #include <stdlib.h>
370 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
371 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
372 #else /* POSIX */
373 #include <stdlib.h>
374 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
375 #define LOCAL_MAPS_DIR ".viking-maps"
376 #endif
377
378 gchar *maps_layer_default_dir ()
379 {
380   static gchar *defaultdir = NULL;
381   if ( ! defaultdir )
382   {
383     /* Thanks to Mike Davison for the $VIKING_MAPS usage */
384     const gchar *mapdir = g_getenv("VIKING_MAPS");
385     if ( mapdir ) {
386       defaultdir = g_strdup ( mapdir );
387     } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
388       defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
389     } else {
390       const gchar *home = g_get_home_dir();
391       if (!home || g_access(home, W_OK))
392         home = g_get_home_dir ();
393       if ( home )
394         defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
395       else
396         defaultdir = g_strdup ( LOCAL_MAPS_DIR );
397     }
398     if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
399     {
400       /* Add the separator at the end */
401       gchar *tmp = defaultdir;
402       defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
403       g_free(tmp);
404     }
405     g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
406   }
407   return defaultdir;
408 }
409
410 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
411 {
412   if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
413   {
414     g_mkdir ( vml->cache_dir, 0777 );
415   }
416 }
417
418 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
419 {
420   guint len;
421   g_assert ( vml != NULL);
422   g_free ( vml->cache_dir );
423   vml->cache_dir = NULL;
424
425   if ( dir == NULL || dir[0] == '\0' )
426   {
427     if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
428       vml->cache_dir = g_strdup ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s );
429   }
430   else
431   {
432     len = strlen(dir);
433     if ( dir[len-1] != G_DIR_SEPARATOR )
434     {
435       vml->cache_dir = g_malloc ( len+2 );
436       strncpy ( vml->cache_dir, dir, len );
437       vml->cache_dir[len] = G_DIR_SEPARATOR;
438       vml->cache_dir[len+1] = '\0';
439     }
440     else
441       vml->cache_dir = g_strdup ( dir );
442   }
443   maps_layer_mkdir_if_default_dir ( vml );
444 }
445
446 /****************************************/
447 /******** GOBJECT STUFF *****************/
448 /****************************************/
449
450 GType vik_maps_layer_get_type ()
451 {
452   static GType vml_type = 0;
453
454   if (!vml_type)
455   {
456     static const GTypeInfo vml_info =
457     {
458       sizeof (VikMapsLayerClass),
459       NULL, /* base_init */
460       NULL, /* base_finalize */
461       NULL, /* class init */
462       NULL, /* class_finalize */
463       NULL, /* class_data */
464       sizeof (VikMapsLayer),
465       0,
466       NULL /* instance init */
467     };
468     vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
469   }
470
471   return vml_type;
472 }
473
474 /****************************************/
475 /************** PARAMETERS **************/
476 /****************************************/
477
478 static guint map_index_to_uniq_id (guint8 index)
479 {
480   g_assert ( index < NUM_MAP_TYPES );
481   return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
482 }
483
484 static guint map_uniq_id_to_index ( guint uniq_id )
485 {
486   gint i;
487   for ( i = 0; i < NUM_MAP_TYPES; i++ )
488     if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
489       return i;
490   return NUM_MAP_TYPES; /* no such thing */
491 }
492
493 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
494 {
495   // When loading from a file don't need the license reminder
496   if ( is_file_operation )
497     vml->license_notice_shown = TRUE;
498
499   switch ( id )
500   {
501     case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
502     case PARAM_MAPTYPE: {
503       gint maptype = map_uniq_id_to_index(data.u);
504       if ( maptype == NUM_MAP_TYPES ) g_warning(_("Unknown map type"));
505       else vml->maptype = maptype;
506       break;
507     }
508     case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
509     case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
510     case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
511     case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
512                           vml->mapzoom_id = data.u;
513                           vml->xmapzoom = __mapzooms_x [data.u];
514                           vml->ymapzoom = __mapzooms_y [data.u];
515                         }else g_warning (_("Unknown Map Zoom")); break;
516   }
517   return TRUE;
518 }
519
520 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
521 {
522   VikLayerParamData rv;
523   switch ( id )
524   {
525     case PARAM_CACHE_DIR: rv.s = vml->cache_dir ? vml->cache_dir : ""; break;
526     case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
527     case PARAM_ALPHA: rv.u = vml->alpha; break;
528     case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
529     case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
530     case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
531   }
532   return rv;
533 }
534
535 /****************************************/
536 /****** CREATING, COPYING, FREEING ******/
537 /****************************************/
538
539 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
540 {
541   int idx;
542   VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
543   vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
544   idx = map_uniq_id_to_index(19); /* 19 is id for OSM MapQuest maps */
545     vml->maptype = (idx < NUM_MAP_TYPES) ? idx : 0;
546   vml->maptype = (idx < NUM_MAP_TYPES) ? idx : 0;
547   vml->alpha = 255;
548   vml->mapzoom_id = 0;
549   vml->dl_tool_x = vml->dl_tool_y = -1;
550   maps_layer_set_cache_dir ( vml, NULL );
551   vml->autodownload = FALSE;
552   vml->adl_only_missing = FALSE;
553   vml->last_center = NULL;
554   vml->last_xmpp = 0.0;
555   vml->last_ympp = 0.0;
556
557   vml->dl_right_click_menu = NULL;
558   vml->license_notice_shown = FALSE;
559
560   return vml;
561 }
562
563 static void maps_layer_free ( VikMapsLayer *vml )
564 {
565   g_free ( vml->cache_dir );
566   vml->cache_dir = NULL;
567   if ( vml->dl_right_click_menu )
568     g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
569   g_free(vml->last_center);
570   vml->last_center = NULL;
571 }
572
573 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
574 {
575   if (from_file != TRUE)
576   {
577     /* If this method is not called in file reading context
578      * it is called in GUI context.
579      * So, we can check if we have to inform the user about inconsistency */
580     VikViewportDrawMode vp_drawmode;
581     VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
582     VikMapSource *map = NULL;
583  
584     vp_drawmode = vik_viewport_get_drawmode ( vp );
585     map = MAPS_LAYER_NTH_TYPE(vml->maptype);
586     if (vik_map_source_get_drawmode(map) != vp_drawmode) {
587       const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
588       gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
589       a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
590       g_free(msg);
591     }
592
593     if (vik_map_source_get_license (map) != NULL) {
594       if ( ! vml->license_notice_shown ) {
595         a_dialog_license (VIK_GTK_WINDOW_FROM_WIDGET(vp), vik_map_source_get_label (map),
596                           vik_map_source_get_license (map), vik_map_source_get_license_url (map) );
597         vml->license_notice_shown = TRUE;
598       }
599     }
600   }
601 }
602
603 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
604 {
605   return vik_maps_layer_get_map_label ( vml );
606 }
607
608 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
609 {
610   vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
611 }
612
613 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
614 {
615   VikMapsLayer *rv = maps_layer_new ( vvp );
616   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
617   return rv;
618 }
619
620 /*********************/
621 /****** DRAWING ******/
622 /*********************/
623
624 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
625 {
626   guchar *pixels;
627   gint width, height, iii, jjj;
628
629   if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
630   {
631     GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
632     g_object_unref(G_OBJECT(pixbuf));
633     pixbuf = tmp;
634   }
635
636   pixels = gdk_pixbuf_get_pixels(pixbuf);
637   width = gdk_pixbuf_get_width(pixbuf);
638   height = gdk_pixbuf_get_height(pixbuf);
639
640   /* r,g,b,a,r,g,b,a.... */
641   for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
642   {
643     pixels += 3;
644     *pixels++ = alpha;
645   }
646   return pixbuf;
647 }
648
649 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
650 {
651   GdkPixbuf *tmp;
652   guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
653   tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_NEAREST);
654   g_object_unref ( G_OBJECT(pixbuf) );
655   return tmp;
656 }
657
658 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
659 {
660   GdkPixbuf *pixbuf;
661
662   /* get the thing */
663   pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
664                             mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
665
666   if ( ! pixbuf ) {
667     if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
668       g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS,
669                    vml->cache_dir, (17 - mapcoord->scale), mapcoord->x, mapcoord->y, ".png" );
670     else
671       g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
672                    vml->cache_dir, mode,
673                    mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
674
675     if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
676     {
677       GError *gx = NULL;
678       pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
679
680       /* free the pixbuf on error */
681       if (gx)
682       {
683         if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
684           g_warning ( _("Couldn't open image file: %s"), gx->message );
685
686         g_error_free ( gx );
687         if ( pixbuf )
688           g_object_unref ( G_OBJECT(pixbuf) );
689         pixbuf = NULL;
690       } else {
691           if ( vml->alpha < 255 )
692             pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
693           if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
694             pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
695
696           a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y, 
697               mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
698               mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
699       }
700     }
701   }
702   return pixbuf;
703 }
704
705 gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
706 {
707   const VikCoord *center = vik_viewport_get_center ( vvp );
708
709   if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
710     /* D'n'D pan in action: do not download */
711     return FALSE;
712
713   if (vml->last_center == NULL) {
714     VikCoord *new_center = g_malloc(sizeof(VikCoord));
715     *new_center = *center;
716     vml->last_center = new_center;
717     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
718     vml->last_ympp = vik_viewport_get_ympp(vvp);
719     return TRUE;
720   }
721
722   /* TODO: perhaps vik_coord_diff() */
723   if (vik_coord_equals(vml->last_center, center)
724       && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
725       && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
726     return FALSE;
727
728   *(vml->last_center) = *center;
729     vml->last_xmpp = vik_viewport_get_xmpp(vvp);
730     vml->last_ympp = vik_viewport_get_ympp(vvp);
731   return TRUE;
732 }
733
734 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
735 {
736   MapCoord ulm, brm;
737   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
738   gdouble yzoom = vik_viewport_get_ympp ( vvp );
739   gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
740   gboolean existence_only = FALSE;
741
742   if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
743     xshrinkfactor = vml->xmapzoom / xzoom;
744     yshrinkfactor = vml->ymapzoom / yzoom;
745     xzoom = vml->xmapzoom;
746     yzoom = vml->xmapzoom;
747     if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
748          yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
749       if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR )
750         existence_only = TRUE;
751       else {
752         g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
753         return;
754       }
755     }
756   }
757
758   /* coord -> ID */
759   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
760   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
761        vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
762
763     /* loop & draw */
764     gint x, y;
765     gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
766     gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
767     gint mode = vik_map_source_get_uniq_id(map);
768
769     VikCoord coord;
770     gint xx, yy, width, height;
771     GdkPixbuf *pixbuf;
772
773     guint max_path_len = strlen(vml->cache_dir) + 40;
774     gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
775
776     if ( (!existence_only) && vml->autodownload  && should_start_autodownload(vml, vvp)) {
777       g_debug("%s: Starting autodownload", __FUNCTION__);
778       if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
779         // Try to download newer tiles
780         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
781       else
782         // Download only missing tiles
783         start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
784     }
785
786     if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
787       for ( x = xmin; x <= xmax; x++ ) {
788         for ( y = ymin; y <= ymax; y++ ) {
789           ulm.x = x;
790           ulm.y = y;
791           pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
792           if ( pixbuf ) {
793             width = gdk_pixbuf_get_width ( pixbuf );
794             height = gdk_pixbuf_get_height ( pixbuf );
795
796             vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
797             vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
798             xx -= (width/2);
799             yy -= (height/2);
800
801             vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
802           }
803         }
804       }
805     } else { /* tilesize is known, don't have to keep converting coords */
806       gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
807       gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
808       /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
809       gint tilesize_x_ceil = ceil ( tilesize_x );
810       gint tilesize_y_ceil = ceil ( tilesize_y );
811       gint8 xinc = (ulm.x == xmin) ? 1 : -1;
812       gint8 yinc = (ulm.y == ymin) ? 1 : -1;
813       gdouble xx, yy; gint xx_tmp, yy_tmp;
814       gint base_yy, xend, yend;
815
816       xend = (xinc == 1) ? (xmax+1) : (xmin-1);
817       yend = (yinc == 1) ? (ymax+1) : (ymin-1);
818
819       vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
820       vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
821       xx = xx_tmp; yy = yy_tmp;
822       /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
823        * eg if tile size 128, shrinkfactor 0.333 */
824       xx -= (tilesize_x/2);
825       base_yy = yy - (tilesize_y/2);
826
827       for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
828         yy = base_yy;
829         for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
830           ulm.x = x;
831           ulm.y = y;
832
833           if ( existence_only ) {
834             if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
835               g_snprintf ( path_buf, max_path_len, DIRECTDIRACCESS,
836                            vml->cache_dir, (17 - ulm.scale), ulm.x, ulm.y, ".png" );
837             else
838               g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
839                            vml->cache_dir, mode,
840                            ulm.scale, ulm.z, ulm.x, ulm.y );
841             if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
842               GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
843               vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
844             }
845           } else {
846             int scale_inc;
847             for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
848               /* try with correct then smaller zooms */
849               int scale_factor = 1 << scale_inc;  /*  2^scale_inc */
850               MapCoord ulm2 = ulm;
851               ulm2.x = ulm.x / scale_factor;
852               ulm2.y = ulm.y / scale_factor;
853               ulm2.scale = ulm.scale + scale_inc;
854               pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
855               if ( pixbuf ) {
856                 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
857                 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
858 #ifdef DEBUG
859                 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);
860 #endif
861                 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
862                 break;
863               }
864             }
865             if ( !pixbuf ) {
866               /* retry with bigger zooms */
867               int scale_dec;
868               for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
869                 int pict_x, pict_y;
870                 int scale_factor = 1 << scale_dec;  /*  2^scale_dec */
871                 MapCoord ulm2 = ulm;
872                 ulm2.x = ulm.x * scale_factor;
873                 ulm2.y = ulm.y * scale_factor;
874                 ulm2.scale = ulm.scale - scale_dec;
875                 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
876                   for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
877                     MapCoord ulm3 = ulm2;
878                     ulm3.x += pict_x;
879                     ulm3.y += pict_y;
880                     pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
881                     if ( pixbuf ) {
882                       gint src_x = 0;
883                       gint src_y = 0;
884                       gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
885                       gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
886                       vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
887                     }
888                   }
889                 }
890               }
891             }
892           }
893
894           yy += tilesize_y;
895         }
896         xx += tilesize_x;
897       }
898     }
899
900     g_free ( path_buf );
901   }
902 }
903
904 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
905 {
906   if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
907   {
908     VikCoord ul, br;
909
910     /* Copyright */
911     gdouble level = vik_viewport_get_zoom ( vvp );
912     LatLonBBox bbox;
913     vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
914     vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
915
916     /* Logo */
917     const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
918     vik_viewport_add_logo ( vvp, logo );
919
920     /* get corner coords */
921     if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
922       /* UTM multi-zone stuff by Kit Transue */
923       gchar leftmost_zone, rightmost_zone, i;
924       leftmost_zone = vik_viewport_leftmost_zone( vvp );
925       rightmost_zone = vik_viewport_rightmost_zone( vvp );
926       for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
927         vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
928         maps_layer_draw_section ( vml, vvp, &ul, &br );
929       }
930     }
931     else {
932       vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
933       vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
934
935       maps_layer_draw_section ( vml, vvp, &ul, &br );
936     }
937   }
938 }
939
940 /*************************/
941 /****** DOWNLOADING ******/
942 /*************************/
943
944 /* pass along data to thread, exists even if layer is deleted. */
945 typedef struct {
946   gchar *cache_dir;
947   gchar *filename_buf;
948   gint x0, y0, xf, yf;
949   MapCoord mapcoord;
950   gint maptype;
951   gint maxlen;
952   gint mapstoget;
953   gint redownload;
954   gboolean refresh_display;
955   VikMapsLayer *vml;
956   VikViewport *vvp;
957   gboolean map_layer_alive;
958   GMutex *mutex;
959 } MapDownloadInfo;
960
961 static void mdi_free ( MapDownloadInfo *mdi )
962 {
963   g_mutex_free(mdi->mutex);
964   g_free ( mdi->cache_dir );
965   mdi->cache_dir = NULL;
966   g_free ( mdi->filename_buf );
967   mdi->filename_buf = NULL;
968   g_free ( mdi );
969 }
970
971 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
972 {
973   MapDownloadInfo *mdi = ptr;
974   g_mutex_lock(mdi->mutex);
975   mdi->map_layer_alive = FALSE;
976   g_mutex_unlock(mdi->mutex);
977 }
978
979 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
980 {
981   void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
982   guint donemaps = 0;
983   gint x, y;
984   for ( x = mdi->x0; x <= mdi->xf; x++ )
985   {
986     for ( y = mdi->y0; y <= mdi->yf; y++ )
987     {
988       gboolean remove_mem_cache = FALSE;
989       gboolean need_download = FALSE;
990       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
991                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
992                      mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
993
994       donemaps++;
995       int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
996       if (res != 0) {
997         vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
998         return -1;
999       }
1000
1001       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1002         need_download = TRUE;
1003         remove_mem_cache = TRUE;
1004
1005       } else {  /* in case map file already exists */
1006         switch (mdi->redownload) {
1007           case REDOWNLOAD_NONE:
1008             continue;
1009
1010           case REDOWNLOAD_BAD:
1011           {
1012             /* see if this one is bad or what */
1013             GError *gx = NULL;
1014             GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1015             if (gx || (!pixbuf)) {
1016               g_remove ( mdi->filename_buf );
1017               need_download = TRUE;
1018               remove_mem_cache = TRUE;
1019               g_error_free ( gx );
1020
1021             } else {
1022               g_object_unref ( pixbuf );
1023             }
1024             break;
1025           }
1026
1027           case REDOWNLOAD_NEW:
1028             need_download = TRUE;
1029             remove_mem_cache = TRUE;
1030             break;
1031
1032           case REDOWNLOAD_ALL:
1033             /* FIXME: need a better way than to erase file in case of server/network problem */
1034             g_remove ( mdi->filename_buf );
1035             need_download = TRUE;
1036             remove_mem_cache = TRUE;
1037             break;
1038
1039           case DOWNLOAD_OR_REFRESH:
1040             remove_mem_cache = TRUE;
1041             break;
1042
1043           default:
1044             g_warning ( "redownload state %d unknown\n", mdi->redownload);
1045         }
1046       }
1047
1048       mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1049
1050       if (need_download) {
1051         if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
1052           continue;
1053       }
1054
1055       g_mutex_lock(mdi->mutex);
1056       if (remove_mem_cache)
1057           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 );
1058       if (mdi->refresh_display && mdi->map_layer_alive) {
1059         /* TODO: check if it's on visible area */
1060         vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1061       }
1062       g_mutex_unlock(mdi->mutex);
1063       mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1064
1065     }
1066   }
1067   vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1068   g_mutex_lock(mdi->mutex);
1069   if (mdi->map_layer_alive)
1070     g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1071   g_mutex_unlock(mdi->mutex); 
1072   return 0;
1073 }
1074
1075 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1076 {
1077   if ( mdi->mapcoord.x || mdi->mapcoord.y )
1078   {
1079     g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1080                      mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1081                      mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
1082     if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1083     {
1084       g_remove ( mdi->filename_buf );
1085     }
1086   }
1087 }
1088
1089 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1090 {
1091   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1092   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1093   MapCoord ulm, brm;
1094   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1095
1096   // Don't ever attempt download on direct access
1097   if ( vik_map_source_is_direct_file_access ( map ) )
1098     return;
1099
1100   if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) 
1101     && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1102   {
1103     MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1104     gint a, b;
1105
1106     mdi->vml = vml;
1107     mdi->vvp = vvp;
1108     mdi->map_layer_alive = TRUE;
1109     mdi->mutex = g_mutex_new();
1110     mdi->refresh_display = TRUE;
1111
1112     /* cache_dir and buffer for dest filename */
1113     mdi->cache_dir = g_strdup ( vml->cache_dir );
1114     mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1115     mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1116     mdi->maptype = vml->maptype;
1117
1118     mdi->mapcoord = ulm;
1119
1120     mdi->redownload = redownload;
1121
1122     mdi->x0 = MIN(ulm.x, brm.x);
1123     mdi->xf = MAX(ulm.x, brm.x);
1124     mdi->y0 = MIN(ulm.y, brm.y);
1125     mdi->yf = MAX(ulm.y, brm.y);
1126
1127     mdi->mapstoget = 0;
1128
1129     if ( mdi->redownload ) {
1130       mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1131     } else {
1132       /* calculate how many we need */
1133       for ( a = mdi->x0; a <= mdi->xf; a++ )
1134       {
1135         for ( b = mdi->y0; b <= mdi->yf; b++ )
1136         {
1137           g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1138                        vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1139                        ulm.z, a, b );
1140           if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1141             mdi->mapstoget++;
1142         }
1143       }
1144     }
1145
1146     mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1147
1148     if ( mdi->mapstoget )
1149     {
1150       const gchar *tmp_str;
1151       gchar *tmp;
1152
1153       if (redownload) 
1154       {
1155         if (redownload == REDOWNLOAD_BAD)
1156           tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1157         else
1158           tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1159       } 
1160       else 
1161       {
1162         tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1163       }
1164       tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1165  
1166       g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1167       /* launch the thread */
1168       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1169                             tmp,                                              /* description string */
1170                             (vik_thr_func) map_download_thread,               /* function to call within thread */
1171                             mdi,                                              /* pass along data */
1172                             (vik_thr_free_func) mdi_free,                     /* function to free pass along data */
1173                             (vik_thr_free_func) mdi_cancel_cleanup,
1174                             mdi->mapstoget );
1175       g_free ( tmp );
1176     }
1177     else
1178       mdi_free ( mdi );
1179   }
1180 }
1181
1182 void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
1183 {
1184   MapCoord ulm, brm;
1185   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1186
1187   // Don't ever attempt download on direct access
1188   if ( vik_map_source_is_direct_file_access ( map ) )
1189     return;
1190
1191   if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm) 
1192     || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1193     g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1194     return;
1195   }
1196
1197   MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1198   gint i, j;
1199
1200   mdi->vml = vml;
1201   mdi->vvp = vvp;
1202   mdi->map_layer_alive = TRUE;
1203   mdi->mutex = g_mutex_new();
1204   mdi->refresh_display = TRUE;
1205
1206   mdi->cache_dir = g_strdup ( vml->cache_dir );
1207   mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1208   mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1209   mdi->maptype = vml->maptype;
1210
1211   mdi->mapcoord = ulm;
1212
1213   mdi->redownload = REDOWNLOAD_NONE;
1214
1215   mdi->x0 = MIN(ulm.x, brm.x);
1216   mdi->xf = MAX(ulm.x, brm.x);
1217   mdi->y0 = MIN(ulm.y, brm.y);
1218   mdi->yf = MAX(ulm.y, brm.y);
1219
1220   mdi->mapstoget = 0;
1221
1222   for (i = mdi->x0; i <= mdi->xf; i++) {
1223     for (j = mdi->y0; j <= mdi->yf; j++) {
1224       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1225                    vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1226                    ulm.z, i, j );
1227       if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1228             mdi->mapstoget++;
1229     }
1230   }
1231
1232   mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1233
1234   if (mdi->mapstoget) {
1235     gchar *tmp;
1236     const gchar *fmt;
1237     fmt = ngettext("Downloading %d %s map...",
1238                    "Downloading %d %s maps...",
1239                    mdi->mapstoget);
1240     tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1241
1242     g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1243       /* launch the thread */
1244     a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1245       tmp,                                /* description string */
1246       (vik_thr_func) map_download_thread, /* function to call within thread */
1247       mdi,                                /* pass along data */
1248       (vik_thr_free_func) mdi_free,       /* function to free pass along data */
1249       (vik_thr_free_func) mdi_cancel_cleanup,
1250       mdi->mapstoget );
1251     g_free ( tmp );
1252   }
1253   else
1254     mdi_free ( mdi );
1255 }
1256
1257 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1258 {
1259   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1260 }
1261
1262 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1263 {
1264   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1265 }
1266
1267 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1268 {
1269   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1270 }
1271
1272 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1273 {
1274   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1275     return FALSE;
1276   if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1277   {
1278     if ( event->button == 1 )
1279     {
1280       VikCoord ul, br;
1281       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 );
1282       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 );
1283       start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1284       vml->dl_tool_x = vml->dl_tool_y = -1;
1285       return TRUE;
1286     }
1287     else
1288     {
1289       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) );
1290       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) );
1291
1292       vml->redownload_vvp = vvp;
1293
1294       vml->dl_tool_x = vml->dl_tool_y = -1;
1295
1296       if ( ! vml->dl_right_click_menu ) {
1297         GtkWidget *item;
1298         vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1299
1300         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1301         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1302         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1303
1304         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1305         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1306         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1307
1308         item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1309         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1310         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1311       }
1312
1313       gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1314       gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1315     }
1316   }
1317   return FALSE;
1318 }
1319
1320 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1321 {
1322   return vvp;
1323 }
1324
1325 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1326 {
1327   MapCoord tmp;
1328   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1329     return FALSE;
1330   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1331   if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1332        vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1333            vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1334            vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1335            &tmp ) ) {
1336     vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1337     return TRUE;
1338   }
1339   return FALSE;
1340
1341  
1342 #if 0
1343   if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1344   {
1345     VikCoord coord;
1346     MapCoord mapcoord;
1347     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1348     if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1349                 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1350                 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1351                 &mapcoord ) ) {
1352       gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1353                      vml->cache_dir, __map_types[vml->maptype].uniq_id,
1354                      mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1355
1356       __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1357       g_free ( filename_buf );
1358       vik_layer_emit_update ( VIK_LAYER(vml) );
1359       return TRUE;
1360     }
1361   }
1362   return FALSE;
1363 #endif
1364 }
1365
1366 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1367 {
1368   VikMapsLayer *vml = vml_vvp[0];
1369   VikViewport *vvp = vml_vvp[1];
1370   VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1371
1372   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1373   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1374
1375   VikCoord ul, br;
1376   MapCoord ulm, brm;
1377
1378   vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1379   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1380
1381   VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1382   if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1383        vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1384        vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1385     start_download_thread ( vml, vvp, &ul, &br, redownload );
1386   else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1387     const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1388     gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1389     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1390     g_free(err);
1391   }
1392   else
1393     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1394
1395 }
1396
1397 static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
1398 {
1399   download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1400 }
1401
1402 static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
1403 {
1404   download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
1405 }
1406
1407 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1408 {
1409   download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1410 }
1411
1412 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1413 {
1414   static gpointer pass_along[2];
1415   GtkWidget *item;
1416   pass_along[0] = vml;
1417   pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1418
1419   item = gtk_menu_item_new();
1420   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1421   gtk_widget_show ( item );
1422
1423   /* Now with icons */
1424   item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
1425     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
1426   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
1427   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1428   gtk_widget_show ( item );
1429
1430   if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
1431     item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
1432     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
1433     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
1434     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1435     gtk_widget_show ( item );
1436   }
1437
1438   item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
1439   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
1440   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1441   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1442   gtk_widget_show ( item );
1443 }
1444
1445 /**
1446  * Enable downloading maps of the current screen area either 'new' or 'everything'
1447  */
1448 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
1449 {
1450   if ( !vml ) return;
1451   if ( !vvp ) return;
1452
1453   static gpointer pass_along[2];
1454   pass_along[0] = vml;
1455   pass_along[1] = vvp;
1456
1457   if ( only_new )
1458     // Get only new maps
1459     maps_layer_download_new_onscreen_maps ( pass_along );
1460   else
1461     // Redownload everything
1462     maps_layer_redownload_all_onscreen_maps ( pass_along );
1463 }