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