]> git.street.me.uk Git - andy/viking.git/blame_incremental - src/vikmapslayer.c
Mapnik library needs full filename for the configuration file.
[andy/viking.git] / src / vikmapslayer.c
... / ...
CommitLineData
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 * Copyright (c) 2013, Rob Norris <rw_norris@hotmail.com>
7 * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
8 * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif
29
30#include <gtk/gtk.h>
31#include <gdk-pixbuf/gdk-pixdata.h>
32#include <glib.h>
33#include <glib/gstdio.h>
34#include <glib/gi18n.h>
35
36#ifdef HAVE_STRING_H
37#include <string.h>
38#endif
39#ifdef HAVE_MATH_H
40#include <math.h>
41#endif
42
43#ifdef HAVE_UNISTD_H
44#include <unistd.h>
45#endif
46
47#include "viking.h"
48#include "vikmapsourcedefault.h"
49#include "vikutils.h"
50#include "maputils.h"
51#include "mapcache.h"
52#include "background.h"
53#include "preferences.h"
54#include "vikmapslayer.h"
55#include "icons/icons.h"
56#include "metatile.h"
57#include "ui_util.h"
58#include "map_ids.h"
59
60#ifdef HAVE_SQLITE3_H
61#include "sqlite3.h"
62#include <gio/gio.h>
63#endif
64
65#define VIK_SETTINGS_MAP_MAX_TILES "maps_max_tiles"
66static gint MAX_TILES = 1000;
67
68#define VIK_SETTINGS_MAP_MIN_SHRINKFACTOR "maps_min_shrinkfactor"
69#define VIK_SETTINGS_MAP_MAX_SHRINKFACTOR "maps_max_shrinkfactor"
70static gdouble MAX_SHRINKFACTOR = 8.0000001; /* zoom 1 viewing 8-tiles */
71static gdouble MIN_SHRINKFACTOR = 0.0312499; /* zoom 32 viewing 1-tiles */
72
73#define VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR "maps_real_min_shrinkfactor"
74static gdouble REAL_MIN_SHRINKFACTOR = 0.0039062499; /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
75
76#define VIK_SETTINGS_MAP_SCALE_INC_UP "maps_scale_inc_up"
77static guint SCALE_INC_UP = 2;
78#define VIK_SETTINGS_MAP_SCALE_INC_DOWN "maps_scale_inc_down"
79static guint SCALE_INC_DOWN = 4;
80#define VIK_SETTINGS_MAP_SCALE_SMALLER_ZOOM_FIRST "maps_scale_smaller_zoom_first"
81static gboolean SCALE_SMALLER_ZOOM_FIRST = TRUE;
82
83/****** MAP TYPES ******/
84
85static GList *__map_types = NULL;
86
87#define NUM_MAP_TYPES g_list_length(__map_types)
88
89/* List of label for each map type */
90static gchar **params_maptypes = NULL;
91
92/* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
93static guint *params_maptypes_ids = NULL;
94
95/******** MAPZOOMS *********/
96
97static 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 };
98static 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 };
99static 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 };
100
101#define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
102
103/**************************/
104
105
106static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
107static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
108static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
109static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
110static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
111static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
112static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values );
113static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
114static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
115static void maps_layer_free ( VikMapsLayer *vml );
116static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
117static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
118static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
119static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
120static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
121static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
122static guint map_uniq_id_to_index ( guint uniq_id );
123
124
125static VikLayerParamScale params_scales[] = {
126 /* min, max, step, digits (decimal places) */
127 { 0, 255, 3, 0 }, /* alpha */
128};
129
130static VikLayerParamData id_default ( void ) { return VIK_LPD_UINT ( MAP_ID_MAPQUEST_OSM ); }
131static VikLayerParamData directory_default ( void )
132{
133 VikLayerParamData data;
134 VikLayerParamData *pref = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir");
135 if (pref) data.s = g_strdup ( pref->s ); else data.s = "";
136 return data;
137}
138static VikLayerParamData file_default ( void )
139{
140 VikLayerParamData data;
141 data.s = "";
142 return data;
143}
144static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
145static VikLayerParamData mapzoom_default ( void ) { return VIK_LPD_UINT ( 0 ); }
146
147static gchar *cache_types[] = { "Viking", N_("OSM"), NULL };
148static VikMapsCacheLayout cache_layout_default_value = VIK_MAPS_CACHE_LAYOUT_VIKING;
149static VikLayerParamData cache_layout_default ( void ) { return VIK_LPD_UINT ( cache_layout_default_value ); }
150
151VikLayerParam maps_layer_params[] = {
152 // NB mode => id - But can't break file format just to rename something better
153 { VIK_LAYER_MAPS, "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL, id_default, NULL, NULL },
154 { VIK_LAYER_MAPS, "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, NULL, directory_default, NULL, NULL },
155 { VIK_LAYER_MAPS, "cache_type", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Cache Layout:"), VIK_LAYER_WIDGET_COMBOBOX, cache_types, NULL,
156 N_("This determines the tile storage layout on disk"), cache_layout_default, NULL, NULL },
157 { VIK_LAYER_MAPS, "mapfile", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Map File:"), VIK_LAYER_WIDGET_FILEENTRY, GINT_TO_POINTER(VF_FILTER_MBTILES), NULL,
158 N_("An MBTiles file. Only applies when the map type method is 'MBTiles'"), file_default, NULL, NULL },
159 { VIK_LAYER_MAPS, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
160 N_("Control the Alpha value for transparency effects"), alpha_default, NULL, NULL },
161 { VIK_LAYER_MAPS, "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
162 { VIK_LAYER_MAPS, "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
163 N_("Using this option avoids attempting to update already acquired tiles. This can be useful if you want to restrict the network usage, without having to resort to manual control. Only applies when 'Autodownload Maps' is on."), vik_lpd_false_default, NULL, NULL },
164 { VIK_LAYER_MAPS, "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
165 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."),
166 mapzoom_default, NULL, NULL },
167};
168
169enum {
170 PARAM_MAPTYPE=0,
171 PARAM_CACHE_DIR,
172 PARAM_CACHE_LAYOUT,
173 PARAM_FILE,
174 PARAM_ALPHA,
175 PARAM_AUTODOWNLOAD,
176 PARAM_ONLYMISSING,
177 PARAM_MAPZOOM,
178 NUM_PARAMS
179};
180
181void maps_layer_set_autodownload_default ( gboolean autodownload )
182{
183 // Set appropriate function
184 if ( autodownload )
185 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_true_default;
186 else
187 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_false_default;
188}
189
190void maps_layer_set_cache_default ( VikMapsCacheLayout layout )
191{
192 // Override default value returned by the default param function
193 cache_layout_default_value = layout;
194}
195
196static VikToolInterface maps_tools[] = {
197 { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
198 (VikToolConstructorFunc) maps_layer_download_create,
199 NULL,
200 NULL,
201 NULL,
202 (VikToolMouseFunc) maps_layer_download_click,
203 NULL,
204 (VikToolMouseFunc) maps_layer_download_release,
205 NULL,
206 FALSE,
207 GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf, NULL },
208};
209
210VikLayerInterface vik_maps_layer_interface = {
211 "Map",
212 N_("Map"),
213 "<control><shift>M",
214 &vikmapslayer_pixbuf,
215
216 maps_tools,
217 sizeof(maps_tools) / sizeof(maps_tools[0]),
218
219 maps_layer_params,
220 NUM_PARAMS,
221 NULL,
222 0,
223
224 VIK_MENU_ITEM_ALL,
225
226 (VikLayerFuncCreate) maps_layer_new,
227 (VikLayerFuncRealize) NULL,
228 (VikLayerFuncPostRead) maps_layer_post_read,
229 (VikLayerFuncFree) maps_layer_free,
230
231 (VikLayerFuncProperties) NULL,
232 (VikLayerFuncDraw) maps_layer_draw,
233 (VikLayerFuncChangeCoordMode) NULL,
234
235 (VikLayerFuncSetMenuItemsSelection) NULL,
236 (VikLayerFuncGetMenuItemsSelection) NULL,
237
238 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
239 (VikLayerFuncSublayerAddMenuItems) NULL,
240
241 (VikLayerFuncSublayerRenameRequest) NULL,
242 (VikLayerFuncSublayerToggleVisible) NULL,
243 (VikLayerFuncSublayerTooltip) NULL,
244 (VikLayerFuncLayerTooltip) maps_layer_tooltip,
245 (VikLayerFuncLayerSelected) NULL,
246
247 (VikLayerFuncMarshall) maps_layer_marshall,
248 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
249
250 (VikLayerFuncSetParam) maps_layer_set_param,
251 (VikLayerFuncGetParam) maps_layer_get_param,
252 (VikLayerFuncChangeParam) maps_layer_change_param,
253
254 (VikLayerFuncReadFileData) NULL,
255 (VikLayerFuncWriteFileData) NULL,
256
257 (VikLayerFuncDeleteItem) NULL,
258 (VikLayerFuncCutItem) NULL,
259 (VikLayerFuncCopyItem) NULL,
260 (VikLayerFuncPasteItem) NULL,
261 (VikLayerFuncFreeCopiedItem) NULL,
262 (VikLayerFuncDragDropRequest) NULL,
263
264 (VikLayerFuncSelectClick) NULL,
265 (VikLayerFuncSelectMove) NULL,
266 (VikLayerFuncSelectRelease) NULL,
267 (VikLayerFuncSelectedViewportMenu) NULL,
268};
269
270struct _VikMapsLayer {
271 VikLayer vl;
272 guint maptype;
273 gchar *cache_dir;
274 VikMapsCacheLayout cache_layout;
275 guint8 alpha;
276 guint mapzoom_id;
277 gdouble xmapzoom, ymapzoom;
278
279 gboolean autodownload;
280 gboolean adl_only_missing;
281 VikCoord *last_center;
282 gdouble last_xmpp;
283 gdouble last_ympp;
284
285 gint dl_tool_x, dl_tool_y;
286
287 GtkMenu *dl_right_click_menu;
288 VikCoord redownload_ul, redownload_br; /* right click menu only */
289 VikViewport *redownload_vvp;
290 gchar *filename;
291#ifdef HAVE_SQLITE3_H
292 sqlite3 *mbtiles;
293#endif
294};
295
296enum { REDOWNLOAD_NONE = 0, /* download only missing maps */
297 REDOWNLOAD_BAD, /* download missing and bad maps */
298 REDOWNLOAD_NEW, /* download missing maps that are newer on server only */
299 REDOWNLOAD_ALL, /* download all maps */
300 DOWNLOAD_OR_REFRESH }; /* download missing maps and refresh cache */
301
302static VikLayerParam prefs[] = {
303 { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Default map layer directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, N_("Choose a directory to store cached Map tiles for this layer"), NULL, NULL, NULL },
304};
305
306void maps_layer_init ()
307{
308 VikLayerParamData tmp;
309 tmp.s = maps_layer_default_dir();
310 a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
311
312 gint max_tiles = MAX_TILES;
313 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_MAX_TILES, &max_tiles ) )
314 MAX_TILES = max_tiles;
315
316 gdouble gdtmp;
317 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MIN_SHRINKFACTOR, &gdtmp ) )
318 MIN_SHRINKFACTOR = gdtmp;
319
320 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MAX_SHRINKFACTOR, &gdtmp ) )
321 MAX_SHRINKFACTOR = gdtmp;
322
323 if ( a_settings_get_double ( VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR, &gdtmp ) )
324 REAL_MIN_SHRINKFACTOR = gdtmp;
325
326 gint gitmp = 0;
327 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_UP, &gitmp ) )
328 SCALE_INC_UP = gitmp;
329
330 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_DOWN, &gitmp ) )
331 SCALE_INC_DOWN = gitmp;
332
333 gboolean gbtmp = TRUE;
334 if ( a_settings_get_boolean ( VIK_SETTINGS_MAP_SCALE_SMALLER_ZOOM_FIRST, &gbtmp ) )
335 SCALE_SMALLER_ZOOM_FIRST = gbtmp;
336
337}
338
339/****************************************/
340/******** MAPS LAYER TYPES **************/
341/****************************************/
342
343void _add_map_source ( guint16 id, const char *label, VikMapSource *map )
344{
345 gsize len = 0;
346 if (params_maptypes)
347 len = g_strv_length (params_maptypes);
348 /* Add the label */
349 params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
350 params_maptypes[len] = g_strdup (label);
351 params_maptypes[len+1] = NULL;
352
353 /* Add the id */
354 params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
355 params_maptypes_ids[len] = id;
356 params_maptypes_ids[len+1] = 0;
357
358 /* We have to clone */
359 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
360 /* Register the clone in the list */
361 __map_types = g_list_append(__map_types, clone);
362
363 /* Hack
364 We have to ensure the mode LayerParam references the up-to-date
365 GLists.
366 */
367 /*
368 memcpy(&maps_layer_params[0].widget_data, &params_maptypes, sizeof(gpointer));
369 memcpy(&maps_layer_params[0].extra_widget_data, &params_maptypes_ids, sizeof(gpointer));
370 */
371 maps_layer_params[0].widget_data = params_maptypes;
372 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
373}
374
375void _update_map_source ( const char *label, VikMapSource *map, int index )
376{
377 GList *item = g_list_nth (__map_types, index);
378 g_object_unref (item->data);
379 item->data = g_object_ref (map);
380 /* Change previous data */
381 g_free (params_maptypes[index]);
382 params_maptypes[index] = g_strdup (label);
383}
384
385/**
386 * maps_layer_register_map_source:
387 * @map: the new VikMapSource
388 *
389 * Register a new VikMapSource.
390 * Override existing one (equality of id).
391 */
392void maps_layer_register_map_source ( VikMapSource *map )
393{
394 g_assert(map != NULL);
395
396 guint16 id = vik_map_source_get_uniq_id(map);
397 const char *label = vik_map_source_get_label(map);
398 g_assert(label != NULL);
399
400 int previous = map_uniq_id_to_index (id);
401 if (previous != NUM_MAP_TYPES)
402 {
403 _update_map_source (label, map, previous);
404 }
405 else
406 {
407 _add_map_source (id, label, map);
408 }
409}
410
411#define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
412#define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
413#define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
414
415/**
416 * vik_maps_layer_get_map_type:
417 *
418 * Returns the actual map id (rather than the internal type index value)
419 */
420guint vik_maps_layer_get_map_type(VikMapsLayer *vml)
421{
422 return MAPS_LAYER_NTH_ID(vml->maptype);
423}
424
425/**
426 * vik_maps_layer_set_map_type:
427 *
428 */
429void vik_maps_layer_set_map_type(VikMapsLayer *vml, guint map_type)
430{
431 gint maptype = map_uniq_id_to_index(map_type);
432 if ( maptype == NUM_MAP_TYPES )
433 g_warning(_("Unknown map type"));
434 else
435 vml->maptype = maptype;
436}
437
438/**
439 * vik_maps_layer_get_default_map_type:
440 *
441 */
442guint vik_maps_layer_get_default_map_type ()
443{
444 VikLayerInterface *vli = vik_layer_get_interface ( VIK_LAYER_MAPS );
445 VikLayerParamData vlpd = a_layer_defaults_get ( vli->fixed_layer_name, "mode", VIK_LAYER_PARAM_UINT );
446 if ( vlpd.u == 0 )
447 vlpd = id_default();
448 return vlpd.u;
449}
450
451gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
452{
453 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
454}
455
456/****************************************/
457/******** CACHE DIR STUFF ***************/
458/****************************************/
459
460#define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
461#define DIRECTDIRACCESS_WITH_NAME "%s%s" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
462#define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
463#define MAPS_CACHE_DIR maps_layer_default_dir()
464
465#ifdef WINDOWS
466#include <io.h>
467#define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
468#define LOCAL_MAPS_DIR "VIKING-MAPS"
469#elif defined __APPLE__
470#include <stdlib.h>
471#define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
472#define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
473#else /* POSIX */
474#include <stdlib.h>
475#define GLOBAL_MAPS_DIR "/var/cache/maps/"
476#define LOCAL_MAPS_DIR ".viking-maps"
477#endif
478
479gchar *maps_layer_default_dir ()
480{
481 static gchar *defaultdir = NULL;
482 if ( ! defaultdir )
483 {
484 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
485 const gchar *mapdir = g_getenv("VIKING_MAPS");
486 if ( mapdir ) {
487 defaultdir = g_strdup ( mapdir );
488 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
489 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
490 } else {
491 const gchar *home = g_get_home_dir();
492 if (!home || g_access(home, W_OK))
493 home = g_get_home_dir ();
494 if ( home )
495 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
496 else
497 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
498 }
499 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
500 {
501 /* Add the separator at the end */
502 gchar *tmp = defaultdir;
503 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
504 g_free(tmp);
505 }
506 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
507 }
508 return defaultdir;
509}
510
511static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
512{
513 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
514 {
515 g_mkdir ( vml->cache_dir, 0777 );
516 }
517}
518
519static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
520{
521 g_assert ( vml != NULL);
522 g_free ( vml->cache_dir );
523 vml->cache_dir = NULL;
524 const gchar *mydir = dir;
525
526 if ( dir == NULL || dir[0] == '\0' )
527 {
528 if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
529 mydir = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s;
530 }
531
532 gchar *canonical_dir = vu_get_canonical_filename ( VIK_LAYER(vml), mydir );
533
534 // Ensure cache_dir always ends with a separator
535 guint len = strlen(canonical_dir);
536 if ( canonical_dir[len-1] != G_DIR_SEPARATOR )
537 {
538 vml->cache_dir = g_strconcat ( canonical_dir, G_DIR_SEPARATOR_S, NULL );
539 g_free ( canonical_dir );
540 }
541 else {
542 vml->cache_dir = canonical_dir;
543 }
544
545 maps_layer_mkdir_if_default_dir ( vml );
546}
547
548static void maps_layer_set_file ( VikMapsLayer *vml, const gchar *name )
549{
550 if ( vml->filename )
551 g_free (vml->filename);
552 vml->filename = g_strdup (name);
553}
554
555/****************************************/
556/******** GOBJECT STUFF *****************/
557/****************************************/
558
559GType vik_maps_layer_get_type ()
560{
561 static GType vml_type = 0;
562
563 if (!vml_type)
564 {
565 static const GTypeInfo vml_info =
566 {
567 sizeof (VikMapsLayerClass),
568 NULL, /* base_init */
569 NULL, /* base_finalize */
570 NULL, /* class init */
571 NULL, /* class_finalize */
572 NULL, /* class_data */
573 sizeof (VikMapsLayer),
574 0,
575 NULL /* instance init */
576 };
577 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
578 }
579
580 return vml_type;
581}
582
583/****************************************/
584/************** PARAMETERS **************/
585/****************************************/
586
587static guint map_index_to_uniq_id (guint16 index)
588{
589 g_assert ( index < NUM_MAP_TYPES );
590 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
591}
592
593static guint map_uniq_id_to_index ( guint uniq_id )
594{
595 gint i;
596 for ( i = 0; i < NUM_MAP_TYPES; i++ )
597 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
598 return i;
599 return NUM_MAP_TYPES; /* no such thing */
600}
601
602#define VIK_SETTINGS_MAP_LICENSE_SHOWN "map_license_shown"
603
604/**
605 * Convenience function to display the license
606 */
607static void maps_show_license ( GtkWindow *parent, VikMapSource *map )
608{
609 a_dialog_license ( parent,
610 vik_map_source_get_label (map),
611 vik_map_source_get_license (map),
612 vik_map_source_get_license_url (map) );
613}
614
615static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
616{
617 switch ( id )
618 {
619 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
620 case PARAM_CACHE_LAYOUT: if ( data.u < VIK_MAPS_CACHE_LAYOUT_NUM ) vml->cache_layout = data.u; break;
621 case PARAM_FILE: maps_layer_set_file ( vml, data.s ); break;
622 case PARAM_MAPTYPE: {
623 gint maptype = map_uniq_id_to_index(data.u);
624 if ( maptype == NUM_MAP_TYPES )
625 g_warning(_("Unknown map type"));
626 else {
627 vml->maptype = maptype;
628
629 // When loading from a file don't need the license reminder - ensure it's saved into the 'seen' list
630 if ( is_file_operation ) {
631 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u );
632 }
633 else {
634 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
635 if (vik_map_source_get_license (map) != NULL) {
636 // Check if licence for this map type has been shown before
637 if ( ! a_settings_get_integer_list_contains ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u ) ) {
638 if ( vvp )
639 maps_show_license ( VIK_GTK_WINDOW_FROM_WIDGET(vvp), map );
640 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u );
641 }
642 }
643 }
644 }
645 break;
646 }
647 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
648 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
649 case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
650 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
651 vml->mapzoom_id = data.u;
652 vml->xmapzoom = __mapzooms_x [data.u];
653 vml->ymapzoom = __mapzooms_y [data.u];
654 }else g_warning (_("Unknown Map Zoom")); break;
655 default: break;
656 }
657 return TRUE;
658}
659
660static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
661{
662 VikLayerParamData rv;
663 switch ( id )
664 {
665 case PARAM_CACHE_DIR:
666 {
667 gboolean set = FALSE;
668 /* Only save a blank when the map cache location equals the default
669 On reading in, when it is blank then the default is reconstructed
670 Since the default changes dependent on the user and OS, it means the resultant file is more portable */
671 if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
672 rv.s = "";
673 set = TRUE;
674 }
675 else if ( is_file_operation ) {
676 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
677 gchar *cwd = g_get_current_dir();
678 if ( cwd ) {
679 rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
680 if ( !rv.s ) rv.s = "";
681 set = TRUE;
682 }
683 }
684 }
685 if ( !set )
686 rv.s = vml->cache_dir ? vml->cache_dir : "";
687 break;
688 }
689 case PARAM_CACHE_LAYOUT: rv.u = vml->cache_layout; break;
690 case PARAM_FILE: rv.s = vml->filename; break;
691 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
692 case PARAM_ALPHA: rv.u = vml->alpha; break;
693 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
694 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
695 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
696 default: break;
697 }
698 return rv;
699}
700
701static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values )
702{
703 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
704 // Alter sensitivity of download option widgets according to the maptype setting.
705 case PARAM_MAPTYPE: {
706 // Get new value
707 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
708 // Is it *not* the OSM On Disk Tile Layout or the MBTiles type or the OSM Metatiles type
709 gboolean sensitive = ( 21 != vlpd.u && 23 != vlpd.u && 24 != vlpd.u );
710 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
711 GtkWidget **ww2 = values[UI_CHG_LABELS];
712 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
713 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
714 GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
715 GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
716 // Depends on autodownload value
717 gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
718 if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
719 if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
720 if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
721 if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
722
723 // Cache type not applicable either
724 GtkWidget *w9 = ww1[PARAM_CACHE_LAYOUT];
725 GtkWidget *w10 = ww2[PARAM_CACHE_LAYOUT];
726 if ( w9 ) gtk_widget_set_sensitive ( w9, sensitive );
727 if ( w10 ) gtk_widget_set_sensitive ( w10, sensitive );
728
729 // File only applicable for MBTiles type
730 // Directory for all other types
731 sensitive = ( 23 == vlpd.u);
732 GtkWidget *w5 = ww1[PARAM_FILE];
733 GtkWidget *w6 = ww2[PARAM_FILE];
734 GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
735 GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
736 if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
737 if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
738 if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
739 if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
740
741 break;
742 }
743
744 // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
745 case PARAM_AUTODOWNLOAD: {
746 // Get new value
747 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
748 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
749 GtkWidget **ww2 = values[UI_CHG_LABELS];
750 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
751 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
752 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
753 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
754 break;
755 }
756 default: break;
757 }
758}
759
760/****************************************/
761/****** CREATING, COPYING, FREEING ******/
762/****************************************/
763
764static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
765{
766 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
767 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
768
769 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
770
771 vml->dl_tool_x = vml->dl_tool_y = -1;
772 vml->last_center = NULL;
773 vml->last_xmpp = 0.0;
774 vml->last_ympp = 0.0;
775
776 vml->dl_right_click_menu = NULL;
777 vml->filename = NULL;
778 return vml;
779}
780
781static void maps_layer_free ( VikMapsLayer *vml )
782{
783 g_free ( vml->cache_dir );
784 vml->cache_dir = NULL;
785 if ( vml->dl_right_click_menu )
786 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
787 g_free(vml->last_center);
788 vml->last_center = NULL;
789 g_free ( vml->filename );
790 vml->filename = NULL;
791
792#ifdef HAVE_SQLITE3_H
793 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
794 if ( vik_map_source_is_mbtiles ( map ) ) {
795 if ( vml->mbtiles ) {
796 int ans = sqlite3_close ( vml->mbtiles );
797 if ( ans != SQLITE_OK ) {
798 // Only to console for information purposes only
799 g_warning ( "SQL Close problem: %d", ans );
800 }
801 }
802 }
803#endif
804}
805
806static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
807{
808 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
809 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
810
811 if (!from_file)
812 {
813 /* If this method is not called in file reading context
814 * it is called in GUI context.
815 * So, we can check if we have to inform the user about inconsistency */
816 VikViewportDrawMode vp_drawmode;
817 vp_drawmode = vik_viewport_get_drawmode ( vp );
818
819 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
820 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
821 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
822 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
823 g_free(msg);
824 }
825 }
826
827 // Performed in post read as we now know the map type
828#ifdef HAVE_SQLITE3_H
829 // Do some SQL stuff
830 if ( vik_map_source_is_mbtiles ( map ) ) {
831 int ans = sqlite3_open_v2 ( vml->filename,
832 &(vml->mbtiles),
833 SQLITE_OPEN_READONLY,
834 NULL );
835 if ( ans != SQLITE_OK ) {
836 // That didn't work, so here's why:
837 g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
838
839 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
840 _("Failed to open MBTiles file: %s"),
841 vml->filename );
842 vml->mbtiles = NULL;
843 }
844 }
845#endif
846
847 // If the on Disk OSM Tile Layout type
848 if ( vml->maptype == 21 )
849 // Copy the directory into filename
850 // thus the mapcache look up will be unique when using more than one of these map types
851 vml->filename = g_strdup (vml->cache_dir);
852}
853
854static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
855{
856 return vik_maps_layer_get_map_label ( vml );
857}
858
859static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
860{
861 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
862}
863
864static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
865{
866 VikMapsLayer *rv = maps_layer_new ( vvp );
867 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
868 maps_layer_post_read ( VIK_LAYER(rv), vvp, FALSE );
869 return rv;
870}
871
872/*********************/
873/****** DRAWING ******/
874/*********************/
875
876static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
877{
878 GdkPixbuf *tmp;
879 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
880 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
881 g_object_unref ( G_OBJECT(pixbuf) );
882 return tmp;
883}
884
885#ifdef HAVE_SQLITE3_H
886/*
887static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
888{
889 g_warning ( "Found %d columns", cols );
890 int i;
891 for ( i = 0; i < cols; i++ ) {
892 g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
893 }
894 return 0;
895}
896*/
897
898/**
899 *
900 */
901static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
902{
903 GdkPixbuf *pixbuf = NULL;
904
905 // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
906 gint flip_y = (gint) pow(2, zoom)-1 - yy;
907 gchar *statement = g_strdup_printf ( "SELECT tile_data FROM tiles WHERE zoom_level=%d AND tile_column=%d AND tile_row=%d;", zoom, xx, flip_y );
908
909 gboolean finished = FALSE;
910
911 sqlite3_stmt *sql_stmt = NULL;
912 int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
913 if ( ans != SQLITE_OK ) {
914 g_warning ( "%s: %s - %d", __FUNCTION__, "prepare failure", ans );
915 finished = TRUE;
916 }
917
918 while ( !finished ) {
919 ans = sqlite3_step ( sql_stmt );
920 switch (ans) {
921 case SQLITE_ROW: {
922 // Get tile_data blob
923 int count = sqlite3_column_count(sql_stmt);
924 if ( count != 1 ) {
925 g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
926 finished = TRUE;
927 }
928 else {
929 const void *data = sqlite3_column_blob ( sql_stmt, 0 );
930 int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
931 if ( bytes < 1 ) {
932 g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
933 finished = TRUE;
934 }
935 else {
936 // Convert these blob bytes into a pixbuf via these streaming operations
937 GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
938 GError *error = NULL;
939 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
940 if (error || (!pixbuf)) {
941 g_warning ( "%s: %s", __FUNCTION__, error->message );
942 g_error_free ( error );
943 }
944 g_input_stream_close ( stream, NULL, NULL );
945 }
946 }
947 break;
948 }
949 default:
950 // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
951 // Finished normally
952 // and give up on any errors
953 if ( ans != SQLITE_DONE )
954 g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
955 finished = TRUE;
956 break;
957 }
958 }
959 ans = sqlite3_finalize ( sql_stmt );
960
961 g_free ( statement );
962
963 return pixbuf;
964}
965#endif
966
967static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
968{
969 GdkPixbuf *pixbuf = NULL;
970
971#ifdef HAVE_SQLITE3_H
972 if ( vml->mbtiles ) {
973 /*
974 gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
975 char *errMsg = NULL;
976 int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
977 if ( ans != SQLITE_OK ) {
978 // Only to console for information purposes only
979 g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
980 sqlite3_free( errMsg );
981 }
982 g_free ( statement );
983 */
984
985 // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
986 // Hence this specific function
987 pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
988 }
989#endif
990
991 return pixbuf;
992}
993
994static GdkPixbuf *get_pixbuf_from_metatile ( VikMapsLayer *vml, gint xx, gint yy, gint zz )
995{
996 const int tile_max = METATILE_MAX_SIZE;
997 char err_msg[PATH_MAX];
998 char *buf;
999 int len;
1000 int compressed;
1001
1002 buf = malloc(tile_max);
1003 if (!buf) {
1004 return NULL;
1005 }
1006
1007 err_msg[0] = 0;
1008 len = metatile_read(vml->cache_dir, xx, yy, zz, buf, tile_max, &compressed, err_msg);
1009
1010 if (len > 0) {
1011 if (compressed) {
1012 // Not handled yet - I don't think this is used often - so implement later if necessary
1013 g_warning ( "Compressed metatiles not implemented:%s\n", __FUNCTION__);
1014 return NULL;
1015 }
1016
1017 // Convert these buf bytes into a pixbuf via these streaming operations
1018 GdkPixbuf *pixbuf = NULL;
1019
1020 GInputStream *stream = g_memory_input_stream_new_from_data ( buf, len, NULL );
1021 GError *error = NULL;
1022 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
1023 if (error || (!pixbuf)) {
1024 g_warning ( "%s: %s", __FUNCTION__, error->message );
1025 g_error_free ( error );
1026 }
1027 g_input_stream_close ( stream, NULL, NULL );
1028
1029 free(buf);
1030 return pixbuf;
1031 }
1032 else {
1033 g_warning ( "FAILED:%s %s", __FUNCTION__, err_msg);
1034 return NULL;
1035 }
1036}
1037
1038
1039static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
1040{
1041 // Apply alpha setting
1042 if ( pixbuf && vml->alpha < 255 )
1043 pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
1044
1045 if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
1046 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
1047
1048 if ( pixbuf )
1049 a_mapcache_add ( pixbuf, (mapcache_extra_t) {0.0}, mapcoord->x, mapcoord->y,
1050 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
1051 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1052
1053 return pixbuf;
1054}
1055
1056static void get_filename ( const gchar *cache_dir,
1057 VikMapsCacheLayout cl,
1058 guint16 id,
1059 const gchar *name,
1060 gint scale,
1061 gint z,
1062 gint x,
1063 gint y,
1064 gchar *filename_buf,
1065 gint buf_len,
1066 const gchar* file_extension )
1067{
1068 switch ( cl ) {
1069 case VIK_MAPS_CACHE_LAYOUT_OSM:
1070 if ( name ) {
1071 if ( g_strcmp0 ( cache_dir, MAPS_CACHE_DIR ) )
1072 // Cache dir not the default - assume it's been directed somewhere specific
1073 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1074 else
1075 // Using default cache - so use the map name in the directory path
1076 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS_WITH_NAME, cache_dir, name, (17 - scale), x, y, file_extension );
1077 }
1078 else
1079 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1080 break;
1081 default:
1082 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE, cache_dir, id, scale, z, x, y );
1083 break;
1084 }
1085}
1086
1087static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, guint16 id, const gchar* mapname, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
1088{
1089 GdkPixbuf *pixbuf;
1090
1091 /* get the thing */
1092 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
1093 id, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1094
1095 if ( ! pixbuf ) {
1096 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1097 if ( vik_map_source_is_direct_file_access(map) ) {
1098 // ATM MBTiles must be 'a direct access type'
1099 if ( vik_map_source_is_mbtiles(map) ) {
1100 pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1101 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1102 // return now to avoid file tests that aren't appropriate for this map type
1103 return pixbuf;
1104 }
1105 else if ( vik_map_source_is_osm_meta_tiles(map) ) {
1106 pixbuf = get_pixbuf_from_metatile ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1107 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1108 return pixbuf;
1109 }
1110 else
1111 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, NULL,
1112 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1113 vik_map_source_get_file_extension(map) );
1114 }
1115 else
1116 get_filename ( vml->cache_dir, vml->cache_layout, id, mapname,
1117 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1118 vik_map_source_get_file_extension(map) );
1119
1120 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1121 {
1122 GError *gx = NULL;
1123 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
1124
1125 /* free the pixbuf on error */
1126 if (gx)
1127 {
1128 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE ) {
1129 // Report a warning
1130 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1131 gchar* msg = g_strdup_printf ( _("Couldn't open image file: %s"), gx->message );
1132 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1133 g_free (msg);
1134 }
1135 }
1136
1137 g_error_free ( gx );
1138 if ( pixbuf )
1139 g_object_unref ( G_OBJECT(pixbuf) );
1140 pixbuf = NULL;
1141 } else {
1142 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1143 }
1144 }
1145 }
1146 return pixbuf;
1147}
1148
1149static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
1150{
1151 const VikCoord *center = vik_viewport_get_center ( vvp );
1152
1153 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
1154 /* D'n'D pan in action: do not download */
1155 return FALSE;
1156
1157 // Don't attempt to download unsupported zoom levels
1158 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1159 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1160 guint8 zl = map_utils_mpp_to_zoom_level ( xzoom );
1161 if ( zl < vik_map_source_get_zoom_min(map) || zl > vik_map_source_get_zoom_max(map) )
1162 return FALSE;
1163
1164 if (vml->last_center == NULL) {
1165 VikCoord *new_center = g_malloc(sizeof(VikCoord));
1166 *new_center = *center;
1167 vml->last_center = new_center;
1168 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1169 vml->last_ympp = vik_viewport_get_ympp(vvp);
1170 return TRUE;
1171 }
1172
1173 /* TODO: perhaps vik_coord_diff() */
1174 if (vik_coord_equals(vml->last_center, center)
1175 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1176 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1177 return FALSE;
1178
1179 *(vml->last_center) = *center;
1180 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1181 vml->last_ympp = vik_viewport_get_ympp(vvp);
1182 return TRUE;
1183}
1184
1185/**
1186 *
1187 */
1188gboolean try_draw_scale_down (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1189 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1190{
1191 GdkPixbuf *pixbuf;
1192 int scale_inc;
1193 for (scale_inc = 1; scale_inc < SCALE_INC_DOWN; scale_inc ++) {
1194 // Try with smaller zooms
1195 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
1196 MapCoord ulm2 = ulm;
1197 ulm2.x = ulm.x / scale_factor;
1198 ulm2.y = ulm.y / scale_factor;
1199 ulm2.scale = ulm.scale + scale_inc;
1200 pixbuf = get_pixbuf ( vml, id, mapname, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1201 if ( pixbuf ) {
1202 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1203 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1204 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1205 return TRUE;
1206 }
1207 }
1208 return FALSE;
1209}
1210
1211/**
1212 *
1213 */
1214gboolean try_draw_scale_up (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1215 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1216{
1217 GdkPixbuf *pixbuf;
1218 // Try with bigger zooms
1219 int scale_dec;
1220 for (scale_dec = 1; scale_dec < SCALE_INC_UP; scale_dec ++) {
1221 int pict_x, pict_y;
1222 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
1223 MapCoord ulm2 = ulm;
1224 ulm2.x = ulm.x * scale_factor;
1225 ulm2.y = ulm.y * scale_factor;
1226 ulm2.scale = ulm.scale - scale_dec;
1227 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1228 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1229 MapCoord ulm3 = ulm2;
1230 ulm3.x += pict_x;
1231 ulm3.y += pict_y;
1232 pixbuf = get_pixbuf ( vml, id, mapname, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1233 if ( pixbuf ) {
1234 gint src_x = 0;
1235 gint src_y = 0;
1236 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1237 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1238 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1239 return TRUE;
1240 }
1241 }
1242 }
1243 }
1244 return FALSE;
1245}
1246
1247static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1248{
1249 MapCoord ulm, brm;
1250 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1251 gdouble yzoom = vik_viewport_get_ympp ( vvp );
1252 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
1253 gboolean existence_only = FALSE;
1254
1255 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1256 xshrinkfactor = vml->xmapzoom / xzoom;
1257 yshrinkfactor = vml->ymapzoom / yzoom;
1258 xzoom = vml->xmapzoom;
1259 yzoom = vml->xmapzoom;
1260 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1261 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
1262 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1263 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
1264 existence_only = TRUE;
1265 }
1266 else {
1267 // Report the reason for not drawing
1268 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1269 gchar* msg = g_strdup_printf ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
1270 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1271 g_free (msg);
1272 }
1273 return;
1274 }
1275 }
1276 }
1277
1278 /* coord -> ID */
1279 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1280 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1281 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
1282
1283 /* loop & draw */
1284 gint x, y;
1285 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1286 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
1287 guint16 id = vik_map_source_get_uniq_id(map);
1288 const gchar *mapname = vik_map_source_get_name(map);
1289
1290 VikCoord coord;
1291 gint xx, yy, width, height;
1292 GdkPixbuf *pixbuf;
1293
1294 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1295 // which can happen when using a small fixed zoom level and viewing large areas.
1296 // Also prevents very large number of tile download requests
1297 gint tiles = (xmax-xmin) * (ymax-ymin);
1298 if ( tiles > MAX_TILES ) {
1299 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1300 existence_only = TRUE;
1301 }
1302
1303 guint max_path_len = strlen(vml->cache_dir) + 40;
1304 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1305
1306 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
1307 g_debug("%s: Starting autodownload", __FUNCTION__);
1308 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
1309 // Try to download newer tiles
1310 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1311 else
1312 // Download only missing tiles
1313 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
1314 }
1315
1316 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
1317 for ( x = xmin; x <= xmax; x++ ) {
1318 for ( y = ymin; y <= ymax; y++ ) {
1319 ulm.x = x;
1320 ulm.y = y;
1321 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1322 if ( pixbuf ) {
1323 width = gdk_pixbuf_get_width ( pixbuf );
1324 height = gdk_pixbuf_get_height ( pixbuf );
1325
1326 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1327 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1328 xx -= (width/2);
1329 yy -= (height/2);
1330
1331 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1332 }
1333 }
1334 }
1335 } else { /* tilesize is known, don't have to keep converting coords */
1336 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1337 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
1338 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1339 gint tilesize_x_ceil = ceil ( tilesize_x );
1340 gint tilesize_y_ceil = ceil ( tilesize_y );
1341 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1342 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
1343 gint xx_tmp, yy_tmp;
1344 gint base_yy, xend, yend;
1345
1346 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1347 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1348
1349 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1350 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1351 xx = xx_tmp; yy = yy_tmp;
1352 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1353 * eg if tile size 128, shrinkfactor 0.333 */
1354 xx -= (tilesize_x/2);
1355 base_yy = yy - (tilesize_y/2);
1356
1357 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1358 yy = base_yy;
1359 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1360 ulm.x = x;
1361 ulm.y = y;
1362
1363 if ( existence_only ) {
1364 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1365 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, vik_map_source_get_name(map),
1366 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1367 else
1368 get_filename ( vml->cache_dir, vml->cache_layout, id, vik_map_source_get_name(map),
1369 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1370
1371 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1372 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1373 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1374 }
1375 } else {
1376 // Try correct scale first
1377 int scale_factor = 1;
1378 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1379 if ( pixbuf ) {
1380 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1381 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1382 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1383 }
1384 else {
1385 // Otherwise try different scales
1386 if ( SCALE_SMALLER_ZOOM_FIRST ) {
1387 if ( !try_draw_scale_down(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len) ) {
1388 try_draw_scale_up(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1389 }
1390 }
1391 else {
1392 if ( !try_draw_scale_up(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len) ) {
1393 try_draw_scale_down(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1394 }
1395 }
1396 }
1397 }
1398
1399 yy += tilesize_y;
1400 }
1401 xx += tilesize_x;
1402 }
1403
1404 // ATM Only show tile grid lines in extreme debug mode
1405 if ( vik_debug && vik_verbose ) {
1406 /* Grid drawing here so it gets drawn on top of the map */
1407 /* Thus loop around x & y again, but this time separately */
1408 /* Only showing grid for the current scale */
1409 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
1410 /* Draw single grid lines across the whole screen */
1411 gint width = vik_viewport_get_width(vvp);
1412 gint height = vik_viewport_get_height(vvp);
1413 xx = xx_tmp; yy = yy_tmp;
1414 gint base_xx = xx - (tilesize_x/2);
1415 base_yy = yy - (tilesize_y/2);
1416
1417 xx = base_xx;
1418 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1419 vik_viewport_draw_line ( vvp, black_gc, xx, base_yy, xx, height );
1420 xx += tilesize_x;
1421 }
1422
1423 yy = base_yy;
1424 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1425 vik_viewport_draw_line ( vvp, black_gc, base_xx, yy, width, yy );
1426 yy += tilesize_y;
1427 }
1428 }
1429
1430 }
1431 g_free ( path_buf );
1432 }
1433}
1434
1435static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1436{
1437 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
1438 {
1439 VikCoord ul, br;
1440
1441 /* Copyright */
1442 gdouble level = vik_viewport_get_zoom ( vvp );
1443 LatLonBBox bbox;
1444 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1445 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
1446
1447 /* Logo */
1448 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1449 vik_viewport_add_logo ( vvp, logo );
1450
1451 /* get corner coords */
1452 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1453 /* UTM multi-zone stuff by Kit Transue */
1454 gchar leftmost_zone, rightmost_zone, i;
1455 leftmost_zone = vik_viewport_leftmost_zone( vvp );
1456 rightmost_zone = vik_viewport_rightmost_zone( vvp );
1457 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1458 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1459 maps_layer_draw_section ( vml, vvp, &ul, &br );
1460 }
1461 }
1462 else {
1463 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1464 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1465
1466 maps_layer_draw_section ( vml, vvp, &ul, &br );
1467 }
1468 }
1469}
1470
1471/*************************/
1472/****** DOWNLOADING ******/
1473/*************************/
1474
1475/* pass along data to thread, exists even if layer is deleted. */
1476typedef struct {
1477 gchar *cache_dir;
1478 gchar *filename_buf;
1479 VikMapsCacheLayout cache_layout;
1480 gint x0, y0, xf, yf;
1481 MapCoord mapcoord;
1482 gint maptype;
1483 gint maxlen;
1484 gint mapstoget;
1485 gint redownload;
1486 gboolean refresh_display;
1487 VikMapsLayer *vml;
1488 VikViewport *vvp;
1489 gboolean map_layer_alive;
1490 GMutex *mutex;
1491} MapDownloadInfo;
1492
1493static void mdi_free ( MapDownloadInfo *mdi )
1494{
1495 vik_mutex_free(mdi->mutex);
1496 g_free ( mdi->cache_dir );
1497 mdi->cache_dir = NULL;
1498 g_free ( mdi->filename_buf );
1499 mdi->filename_buf = NULL;
1500 g_free ( mdi );
1501}
1502
1503static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1504{
1505 MapDownloadInfo *mdi = ptr;
1506 g_mutex_lock(mdi->mutex);
1507 mdi->map_layer_alive = FALSE;
1508 g_mutex_unlock(mdi->mutex);
1509}
1510
1511static gboolean is_in_area (VikMapSource *map, MapCoord mc)
1512{
1513 VikCoord vc;
1514 vik_map_source_mapcoord_to_center_coord ( map, &mc, &vc );
1515
1516 struct LatLon tl;
1517 tl.lat = vik_map_source_get_lat_max(map);
1518 tl.lon = vik_map_source_get_lon_min(map);
1519 struct LatLon br;
1520 br.lat = vik_map_source_get_lat_min(map);
1521 br.lon = vik_map_source_get_lon_max(map);
1522 VikCoord vctl;
1523 vik_coord_load_from_latlon (&vctl, VIK_COORD_LATLON, &tl);
1524 VikCoord vcbr;
1525 vik_coord_load_from_latlon (&vcbr, VIK_COORD_LATLON, &br);
1526
1527 return vik_coord_inside ( &vc, &vctl, &vcbr );
1528}
1529
1530static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1531{
1532 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1533 guint donemaps = 0;
1534 MapCoord mcoord = mdi->mapcoord;
1535 gint x, y;
1536 for ( x = mdi->x0; x <= mdi->xf; x++ )
1537 {
1538 mcoord.x = x;
1539 for ( y = mdi->y0; y <= mdi->yf; y++ )
1540 {
1541 mcoord.y = y;
1542 // Only attempt to download a tile from supported areas
1543 if ( is_in_area ( MAPS_LAYER_NTH_TYPE(mdi->maptype), mcoord ) )
1544 {
1545 gboolean remove_mem_cache = FALSE;
1546 gboolean need_download = FALSE;
1547
1548 get_filename ( mdi->cache_dir, mdi->cache_layout,
1549 vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1550 vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1551 mdi->mapcoord.scale, mdi->mapcoord.z, x, y, mdi->filename_buf, mdi->maxlen,
1552 vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1553
1554 donemaps++;
1555 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1556 if (res != 0) {
1557 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1558 return -1;
1559 }
1560
1561 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1562 need_download = TRUE;
1563 remove_mem_cache = TRUE;
1564
1565 } else { /* in case map file already exists */
1566 switch (mdi->redownload) {
1567 case REDOWNLOAD_NONE:
1568 continue;
1569
1570 case REDOWNLOAD_BAD:
1571 {
1572 /* see if this one is bad or what */
1573 GError *gx = NULL;
1574 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1575 if (gx || (!pixbuf)) {
1576 g_remove ( mdi->filename_buf );
1577 need_download = TRUE;
1578 remove_mem_cache = TRUE;
1579 g_error_free ( gx );
1580
1581 } else {
1582 g_object_unref ( pixbuf );
1583 }
1584 break;
1585 }
1586
1587 case REDOWNLOAD_NEW:
1588 need_download = TRUE;
1589 remove_mem_cache = TRUE;
1590 break;
1591
1592 case REDOWNLOAD_ALL:
1593 /* FIXME: need a better way than to erase file in case of server/network problem */
1594 g_remove ( mdi->filename_buf );
1595 need_download = TRUE;
1596 remove_mem_cache = TRUE;
1597 break;
1598
1599 case DOWNLOAD_OR_REFRESH:
1600 remove_mem_cache = TRUE;
1601 break;
1602
1603 default:
1604 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1605 }
1606 }
1607
1608 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1609
1610 if (need_download) {
1611 DownloadResult_t dr = vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle);
1612 switch ( dr ) {
1613 case DOWNLOAD_HTTP_ERROR:
1614 case DOWNLOAD_CONTENT_ERROR: {
1615 // TODO: ?? count up the number of download errors somehow...
1616 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Failed to download tile") );
1617 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1618 g_free (msg);
1619 break;
1620 }
1621 case DOWNLOAD_FILE_WRITE_ERROR: {
1622 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Unable to save tile") );
1623 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1624 g_free (msg);
1625 break;
1626 }
1627 case DOWNLOAD_SUCCESS:
1628 case DOWNLOAD_NOT_REQUIRED:
1629 default:
1630 break;
1631 }
1632 }
1633
1634 g_mutex_lock(mdi->mutex);
1635 if (remove_mem_cache)
1636 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, mdi->vml->filename );
1637 if (mdi->refresh_display && mdi->map_layer_alive) {
1638 /* TODO: check if it's on visible area */
1639 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1640 }
1641 g_mutex_unlock(mdi->mutex);
1642 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1643 }
1644 }
1645 }
1646 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1647 g_mutex_lock(mdi->mutex);
1648 if (mdi->map_layer_alive)
1649 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1650 g_mutex_unlock(mdi->mutex);
1651 return 0;
1652}
1653
1654static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1655{
1656 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1657 {
1658 get_filename ( mdi->cache_dir, mdi->cache_layout,
1659 vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1660 vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1661 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y, mdi->filename_buf, mdi->maxlen,
1662 vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1663 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1664 {
1665 g_remove ( mdi->filename_buf );
1666 }
1667 }
1668}
1669
1670static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1671{
1672 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1673 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1674 MapCoord ulm, brm;
1675 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1676
1677 // Don't ever attempt download on direct access
1678 if ( vik_map_source_is_direct_file_access ( map ) )
1679 return;
1680
1681 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1682 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1683 {
1684 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1685 gint a, b;
1686
1687 mdi->vml = vml;
1688 mdi->vvp = vvp;
1689 mdi->map_layer_alive = TRUE;
1690 mdi->mutex = vik_mutex_new();
1691 mdi->refresh_display = TRUE;
1692
1693 /* cache_dir and buffer for dest filename */
1694 mdi->cache_dir = g_strdup ( vml->cache_dir );
1695 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1696 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1697 mdi->cache_layout = vml->cache_layout;
1698 mdi->maptype = vml->maptype;
1699
1700 mdi->mapcoord = ulm;
1701 mdi->redownload = redownload;
1702
1703 mdi->x0 = MIN(ulm.x, brm.x);
1704 mdi->xf = MAX(ulm.x, brm.x);
1705 mdi->y0 = MIN(ulm.y, brm.y);
1706 mdi->yf = MAX(ulm.y, brm.y);
1707
1708 mdi->mapstoget = 0;
1709
1710 MapCoord mcoord = mdi->mapcoord;
1711
1712 if ( mdi->redownload ) {
1713 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1714 } else {
1715 /* calculate how many we need */
1716 for ( a = mdi->x0; a <= mdi->xf; a++ )
1717 {
1718 mcoord.x = a;
1719 for ( b = mdi->y0; b <= mdi->yf; b++ )
1720 {
1721 mcoord.y = b;
1722 // Only count tiles from supported areas
1723 if ( is_in_area (map, mcoord) )
1724 {
1725 get_filename ( mdi->cache_dir, mdi->cache_layout,
1726 vik_map_source_get_uniq_id(map),
1727 vik_map_source_get_name(map),
1728 ulm.scale, ulm.z, a, b, mdi->filename_buf, mdi->maxlen,
1729 vik_map_source_get_file_extension(map) );
1730 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1731 mdi->mapstoget++;
1732 }
1733 }
1734 }
1735 }
1736
1737 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1738
1739 if ( mdi->mapstoget )
1740 {
1741 const gchar *tmp_str;
1742 gchar *tmp;
1743
1744 if (redownload)
1745 {
1746 if (redownload == REDOWNLOAD_BAD)
1747 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1748 else
1749 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1750 }
1751 else
1752 {
1753 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1754 }
1755 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1756
1757 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1758 /* launch the thread */
1759 a_background_thread ( BACKGROUND_POOL_REMOTE,
1760 VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1761 tmp, /* description string */
1762 (vik_thr_func) map_download_thread, /* function to call within thread */
1763 mdi, /* pass along data */
1764 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1765 (vik_thr_free_func) mdi_cancel_cleanup,
1766 mdi->mapstoget );
1767 g_free ( tmp );
1768 }
1769 else
1770 mdi_free ( mdi );
1771 }
1772}
1773
1774static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1775{
1776 MapCoord ulm, brm;
1777 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1778
1779 // Don't ever attempt download on direct access
1780 if ( vik_map_source_is_direct_file_access ( map ) )
1781 return;
1782
1783 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1784 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1785 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1786 return;
1787 }
1788
1789 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1790 gint i, j;
1791
1792 mdi->vml = vml;
1793 mdi->vvp = vvp;
1794 mdi->map_layer_alive = TRUE;
1795 mdi->mutex = vik_mutex_new();
1796 mdi->refresh_display = TRUE;
1797
1798 mdi->cache_dir = g_strdup ( vml->cache_dir );
1799 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1800 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1801 mdi->maptype = vml->maptype;
1802 mdi->cache_layout = vml->cache_layout;
1803
1804 mdi->mapcoord = ulm;
1805 mdi->redownload = download_method;
1806
1807 mdi->x0 = MIN(ulm.x, brm.x);
1808 mdi->xf = MAX(ulm.x, brm.x);
1809 mdi->y0 = MIN(ulm.y, brm.y);
1810 mdi->yf = MAX(ulm.y, brm.y);
1811
1812 mdi->mapstoget = 0;
1813
1814 MapCoord mcoord = mdi->mapcoord;
1815
1816 for (i = mdi->x0; i <= mdi->xf; i++) {
1817 mcoord.x = i;
1818 for (j = mdi->y0; j <= mdi->yf; j++) {
1819 mcoord.y = j;
1820 // Only count tiles from supported areas
1821 if ( is_in_area (map, mcoord) ) {
1822 get_filename ( mdi->cache_dir, mdi->cache_layout,
1823 vik_map_source_get_uniq_id(map),
1824 vik_map_source_get_name(map),
1825 ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
1826 vik_map_source_get_file_extension(map) );
1827 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1828 mdi->mapstoget++;
1829 }
1830 }
1831 }
1832
1833 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1834
1835 if (mdi->mapstoget) {
1836 gchar *tmp;
1837 const gchar *fmt;
1838 fmt = ngettext("Downloading %d %s map...",
1839 "Downloading %d %s maps...",
1840 mdi->mapstoget);
1841 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1842
1843 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1844
1845 // launch the thread
1846 a_background_thread ( BACKGROUND_POOL_REMOTE,
1847 VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1848 tmp, /* description string */
1849 (vik_thr_func) map_download_thread, /* function to call within thread */
1850 mdi, /* pass along data */
1851 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1852 (vik_thr_free_func) mdi_cancel_cleanup,
1853 mdi->mapstoget );
1854 g_free ( tmp );
1855 }
1856 else
1857 mdi_free ( mdi );
1858}
1859
1860/**
1861 * vik_maps_layer_download_section:
1862 * @vml: The Map Layer
1863 * @vvp: The Viewport that the map is on
1864 * @ul: Upper left coordinate of the area to be downloaded
1865 * @br: Bottom right coordinate of the area to be downloaded
1866 * @zoom: The zoom level at which the maps are to be download
1867 *
1868 * Download a specified map area at a certain zoom level
1869 */
1870void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1871{
1872 maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1873}
1874
1875static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1876{
1877 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1878}
1879
1880static void maps_layer_redownload_all ( VikMapsLayer *vml )
1881{
1882 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1883}
1884
1885static void maps_layer_redownload_new ( VikMapsLayer *vml )
1886{
1887 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1888}
1889
1890/**
1891 * Display a simple dialog with information about this particular map tile
1892 */
1893static void maps_layer_tile_info ( VikMapsLayer *vml )
1894{
1895 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1896
1897 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1898 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1899 MapCoord ulm;
1900
1901 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1902 return;
1903
1904 gchar *filename = NULL;
1905 gchar *source = NULL;
1906
1907 if ( vik_map_source_is_direct_file_access ( map ) ) {
1908 if ( vik_map_source_is_mbtiles ( map ) ) {
1909 filename = g_strdup ( vml->filename );
1910#ifdef HAVE_SQLITE3_H
1911 // And whether to bother going into the SQL to check it's really there or not...
1912 gchar *exists = NULL;
1913 gint zoom = 17 - ulm.scale;
1914 if ( vml->mbtiles ) {
1915 GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, zoom );
1916 if ( pixbuf ) {
1917 exists = g_strdup ( _("YES") );
1918 g_object_unref ( G_OBJECT(pixbuf) );
1919 }
1920 else {
1921 exists = g_strdup ( _("NO") );
1922 }
1923 }
1924 else
1925 exists = g_strdup ( _("NO") );
1926 gint flip_y = (gint) pow(2, zoom)-1 - ulm.y;
1927 // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1928 source = g_strdup_printf ( "Source: %s (%d%s%d%s%d.%s %s)", filename, zoom, G_DIR_SEPARATOR_S, ulm.x, G_DIR_SEPARATOR_S, flip_y, "png", exists );
1929 g_free ( exists );
1930#else
1931 source = g_strdup ( _("Source: Not available") );
1932#endif
1933 }
1934 else if ( vik_map_source_is_osm_meta_tiles ( map ) ) {
1935 char path[PATH_MAX];
1936 xyz_to_meta(path, sizeof(path), vml->cache_dir, ulm.x, ulm.y, 17-ulm.scale );
1937 source = g_strdup ( path );
1938 filename = g_strdup ( path );
1939 }
1940 else {
1941 guint max_path_len = strlen(vml->cache_dir) + 40;
1942 filename = g_malloc ( max_path_len * sizeof(char) );
1943 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM,
1944 vik_map_source_get_uniq_id(map),
1945 NULL,
1946 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1947 vik_map_source_get_file_extension(map) );
1948 source = g_strconcat ( "Source: file://", filename, NULL );
1949 }
1950 }
1951 else {
1952 guint max_path_len = strlen(vml->cache_dir) + 40;
1953 filename = g_malloc ( max_path_len * sizeof(char) );
1954 get_filename ( vml->cache_dir, vml->cache_layout,
1955 vik_map_source_get_uniq_id(map),
1956 vik_map_source_get_name(map),
1957 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1958 vik_map_source_get_file_extension(map) );
1959 source = g_strdup_printf ( "Source: http://%s%s",
1960 vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1961 vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1962 }
1963
1964 GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
1965 g_array_append_val ( array, source );
1966
1967 gchar *filemsg = NULL;
1968 gchar *timemsg = NULL;
1969
1970 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1971 filemsg = g_strconcat ( "Tile File: ", filename, NULL );
1972 // Get some timestamp information of the tile
1973 struct stat stat_buf;
1974 if ( g_stat ( filename, &stat_buf ) == 0 ) {
1975 gchar time_buf[64];
1976 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1977 timemsg = g_strdup_printf ( _("Tile File Timestamp: %s"), time_buf );
1978 }
1979 else {
1980 timemsg = g_strdup ( _("Tile File Timestamp: Not Available") );
1981 }
1982 g_array_append_val ( array, filemsg );
1983 g_array_append_val ( array, timemsg );
1984 }
1985 else {
1986 filemsg = g_strdup_printf ( "Tile File: %s [Not Available]", filename );
1987 g_array_append_val ( array, filemsg );
1988 }
1989
1990 a_dialog_list ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Tile Information"), array, 5 );
1991 g_array_free ( array, FALSE );
1992
1993 g_free ( timemsg );
1994 g_free ( filemsg );
1995 g_free ( source );
1996 g_free ( filename );
1997}
1998
1999static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2000{
2001 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2002 return FALSE;
2003 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
2004 {
2005 if ( event->button == 1 )
2006 {
2007 VikCoord ul, br;
2008 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 );
2009 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 );
2010 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
2011 vml->dl_tool_x = vml->dl_tool_y = -1;
2012 return TRUE;
2013 }
2014 else
2015 {
2016 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) );
2017 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) );
2018
2019 vml->redownload_vvp = vvp;
2020
2021 vml->dl_tool_x = vml->dl_tool_y = -1;
2022
2023 if ( ! vml->dl_right_click_menu ) {
2024 GtkWidget *item;
2025 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
2026
2027 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
2028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
2029 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2030
2031 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
2032 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
2033 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2034
2035 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
2036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
2037 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2038
2039 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
2040 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
2041 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
2042 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
2043 }
2044
2045 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
2046 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
2047 }
2048 }
2049 return FALSE;
2050}
2051
2052static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
2053{
2054 return vvp;
2055}
2056
2057static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2058{
2059 MapCoord tmp;
2060 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2061 return FALSE;
2062 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2063 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
2064 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
2065 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
2066 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
2067 &tmp ) ) {
2068 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
2069 return TRUE;
2070 }
2071 return FALSE;
2072}
2073
2074// A slightly better way of defining the menu callback information
2075// This should be easier to extend/rework compared to previously
2076typedef enum {
2077 MA_VML = 0,
2078 MA_VVP,
2079 MA_LAST
2080} menu_array_index;
2081
2082typedef gpointer menu_array_values[MA_LAST];
2083
2084static void download_onscreen_maps ( menu_array_values values, gint redownload )
2085{
2086 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2087 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2088 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
2089
2090 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
2091 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
2092
2093 VikCoord ul, br;
2094 MapCoord ulm, brm;
2095
2096 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
2097 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
2098
2099 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2100 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
2101 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
2102 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
2103 start_download_thread ( vml, vvp, &ul, &br, redownload );
2104 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
2105 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
2106 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
2107 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
2108 g_free(err);
2109 }
2110 else
2111 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
2112
2113}
2114
2115static void maps_layer_download_missing_onscreen_maps ( menu_array_values values )
2116{
2117 download_onscreen_maps( values, REDOWNLOAD_NONE);
2118}
2119
2120static void maps_layer_download_new_onscreen_maps ( menu_array_values values )
2121{
2122 download_onscreen_maps( values, REDOWNLOAD_NEW);
2123}
2124
2125static void maps_layer_redownload_all_onscreen_maps ( menu_array_values values )
2126{
2127 download_onscreen_maps( values, REDOWNLOAD_ALL);
2128}
2129
2130static void maps_layer_about ( gpointer vml_vvp[2] )
2131{
2132 VikMapsLayer *vml = vml_vvp[0];
2133 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2134
2135 if ( vik_map_source_get_license (map) )
2136 maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
2137 else
2138 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2139 vik_map_source_get_label (map) );
2140}
2141
2142/**
2143 * maps_layer_how_many_maps:
2144 * Copied from maps_layer_download_section but without the actual download and this returns a value
2145 */
2146static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
2147{
2148 MapCoord ulm, brm;
2149 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2150
2151 if ( vik_map_source_is_direct_file_access ( map ) )
2152 return 0;
2153
2154 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
2155 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
2156 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
2157 return 0;
2158 }
2159
2160 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
2161 gint i, j;
2162
2163 mdi->vml = vml;
2164 mdi->vvp = vvp;
2165 mdi->map_layer_alive = TRUE;
2166 mdi->mutex = vik_mutex_new();
2167 mdi->refresh_display = FALSE;
2168
2169 mdi->cache_dir = g_strdup ( vml->cache_dir );
2170 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
2171 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
2172 mdi->maptype = vml->maptype;
2173 mdi->cache_layout = vml->cache_layout;
2174
2175 mdi->mapcoord = ulm;
2176 mdi->redownload = redownload;
2177
2178 mdi->x0 = MIN(ulm.x, brm.x);
2179 mdi->xf = MAX(ulm.x, brm.x);
2180 mdi->y0 = MIN(ulm.y, brm.y);
2181 mdi->yf = MAX(ulm.y, brm.y);
2182
2183 mdi->mapstoget = 0;
2184
2185 if ( mdi->redownload == REDOWNLOAD_ALL ) {
2186 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
2187 }
2188 else {
2189 /* calculate how many we need */
2190 MapCoord mcoord = mdi->mapcoord;
2191 for (i = mdi->x0; i <= mdi->xf; i++) {
2192 mcoord.x = i;
2193 for (j = mdi->y0; j <= mdi->yf; j++) {
2194 mcoord.y = j;
2195 // Only count tiles from supported areas
2196 if ( is_in_area ( map, mcoord ) ) {
2197 get_filename ( mdi->cache_dir, mdi->cache_layout,
2198 vik_map_source_get_uniq_id(map),
2199 vik_map_source_get_name(map),
2200 ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
2201 vik_map_source_get_file_extension(map) );
2202 if ( mdi->redownload == REDOWNLOAD_NEW ) {
2203 // Assume the worst - always a new file
2204 // Absolute value would require a server lookup - but that is too slow
2205 mdi->mapstoget++;
2206 }
2207 else {
2208 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
2209 // Missing
2210 mdi->mapstoget++;
2211 }
2212 else {
2213 if ( mdi->redownload == REDOWNLOAD_BAD ) {
2214 /* see if this one is bad or what */
2215 GError *gx = NULL;
2216 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
2217 if (gx || (!pixbuf)) {
2218 mdi->mapstoget++;
2219 }
2220 break;
2221 // Other download cases already considered or just ignored
2222 }
2223 }
2224 }
2225 }
2226 }
2227 }
2228 }
2229
2230 gint rv = mdi->mapstoget;
2231
2232 mdi_free ( mdi );
2233
2234 return rv;
2235}
2236
2237/**
2238 * maps_dialog_zoom_between:
2239 * This dialog is specific to the map layer, so it's here rather than in dialog.c
2240 */
2241gboolean maps_dialog_zoom_between ( GtkWindow *parent,
2242 gchar *title,
2243 gchar *zoom_list[],
2244 gint default_zoom1,
2245 gint default_zoom2,
2246 gint *selected_zoom1,
2247 gint *selected_zoom2,
2248 gchar *download_list[],
2249 gint default_download,
2250 gint *selected_download )
2251{
2252 GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
2253 parent,
2254 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2255 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2256 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2257 NULL );
2258 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2259 GtkWidget *response_w = NULL;
2260#if GTK_CHECK_VERSION (2, 20, 0)
2261 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2262#endif
2263 GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
2264 GtkWidget *zoom_combo1 = vik_combo_box_text_new();
2265 gchar **s;
2266 for (s = zoom_list; *s; s++)
2267 vik_combo_box_text_append ( zoom_combo1, *s );
2268 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
2269
2270 GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
2271 GtkWidget *zoom_combo2 = vik_combo_box_text_new();
2272 for (s = zoom_list; *s; s++)
2273 vik_combo_box_text_append ( zoom_combo2, *s );
2274 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
2275
2276 GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
2277 GtkWidget *download_combo = vik_combo_box_text_new();
2278 for (s = download_list; *s; s++)
2279 vik_combo_box_text_append ( download_combo, *s );
2280 gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
2281
2282 GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
2283 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
2284 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
2285 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
2286 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
2287 gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
2288 gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
2289
2290 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
2291
2292 if ( response_w )
2293 gtk_widget_grab_focus ( response_w );
2294
2295 gtk_widget_show_all ( dialog );
2296 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2297 gtk_widget_destroy(dialog);
2298 return FALSE;
2299 }
2300
2301 // Return selected options
2302 *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
2303 *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
2304 *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
2305
2306 gtk_widget_destroy(dialog);
2307 return TRUE;
2308}
2309
2310// My best guess of sensible limits
2311#define REALLY_LARGE_AMOUNT_OF_TILES 5000
2312#define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2313
2314/**
2315 * Get all maps in the region for zoom levels specified by the user
2316 * Sort of similar to trw_layer_download_map_along_track_cb function
2317 */
2318static void maps_layer_download_all ( menu_array_values values )
2319{
2320 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2321 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2322
2323 // I don't think we should allow users to hammer the servers too much...
2324 // Delibrately not allowing lowest zoom levels
2325 // Still can give massive numbers to download
2326 // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2327 gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2328 gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2329
2330 gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2331 gint selected_download_method;
2332
2333 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2334
2335 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2336 if (cur_zoom == zoom_vals[default_zoom])
2337 break;
2338 }
2339 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2340
2341 // Default to only 2 zoom levels below the current one
2342 if (default_zoom > 1 )
2343 lower_zoom = default_zoom - 2;
2344 else
2345 lower_zoom = default_zoom;
2346
2347 // redownload method - needs to align with REDOWNLOAD* macro values
2348 gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2349
2350 gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2351
2352 if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2353 title,
2354 zoom_list,
2355 lower_zoom,
2356 default_zoom,
2357 &selected_zoom1,
2358 &selected_zoom2,
2359 download_list,
2360 REDOWNLOAD_NONE, // AKA Missing
2361 &selected_download_method ) ) {
2362 // Cancelled
2363 g_free ( title );
2364 return;
2365 }
2366 g_free ( title );
2367
2368 // Find out new current positions
2369 gdouble min_lat, max_lat, min_lon, max_lon;
2370 VikCoord vc_ul, vc_br;
2371 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2372 struct LatLon ll_ul = { max_lat, min_lon };
2373 struct LatLon ll_br = { min_lat, max_lon };
2374 vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2375 vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2376
2377 // Get Maps Count - call for each zoom level (in reverse)
2378 // With REDOWNLOAD_NEW this is a possible maximum
2379 // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2380 gint map_count = 0;
2381 gint zz;
2382 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2383 map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2384 }
2385
2386 g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2387
2388 // Absolute protection of hammering a map server
2389 if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2390 gchar *str = g_strdup_printf (_("You are not allowed to download more than %d tiles in one go (requested %d)"), REALLY_LARGE_AMOUNT_OF_TILES, map_count);
2391 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2392 g_free (str);
2393 return;
2394 }
2395
2396 // Confirm really want to do this
2397 if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2398 gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2399 gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2400 g_free (str);
2401 if ( ! ans )
2402 return;
2403 }
2404
2405 // Get Maps - call for each zoom level (in reverse)
2406 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2407 maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2408 }
2409}
2410
2411/**
2412 *
2413 */
2414static void maps_layer_flush ( menu_array_values values )
2415{
2416 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2417 a_mapcache_flush_type ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)) );
2418}
2419
2420static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2421{
2422 GtkWidget *item;
2423 static menu_array_values values;
2424 values[MA_VML] = vml;
2425 values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2426
2427 item = gtk_menu_item_new();
2428 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2429 gtk_widget_show ( item );
2430
2431 /* Now with icons */
2432 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2433 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2434 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), values );
2435 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2436 gtk_widget_show ( item );
2437
2438 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2439 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2440 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2441 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), values );
2442 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2443 gtk_widget_show ( item );
2444 }
2445
2446 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2447 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2448 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), values );
2449 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2450 gtk_widget_show ( item );
2451
2452 item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2453 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2454 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), values );
2455 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2456 gtk_widget_show ( item );
2457
2458 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2459 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_about), values );
2460 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2461 gtk_widget_show ( item );
2462
2463 // Typical users shouldn't need to use this functionality - so debug only ATM
2464 if ( vik_debug ) {
2465 item = gtk_image_menu_item_new_with_mnemonic ( _("Flush Map Cache") );
2466 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2467 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_flush), values );
2468 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2469 gtk_widget_show ( item );
2470 }
2471}
2472
2473/**
2474 * Enable downloading maps of the current screen area either 'new' or 'everything'
2475 */
2476void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2477{
2478 if ( !vml ) return;
2479 if ( !vvp ) return;
2480
2481 static menu_array_values values;
2482 values[MA_VML] = vml;
2483 values[MA_VVP] = vvp;
2484
2485 if ( only_new )
2486 // Get only new maps
2487 maps_layer_download_new_onscreen_maps ( values );
2488 else
2489 // Redownload everything
2490 maps_layer_redownload_all_onscreen_maps ( values );
2491}