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