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