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