2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
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>
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.
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.
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
31 #include <gdk-pixbuf/gdk-pixdata.h>
33 #include <glib/gstdio.h>
34 #include <glib/gi18n.h>
48 #include "vikmapsourcedefault.h"
51 #include "background.h"
52 #include "preferences.h"
53 #include "vikmapslayer.h"
54 #include "icons/icons.h"
64 #define VIK_SETTINGS_MAP_MAX_TILES "maps_max_tiles"
65 static gint MAX_TILES = 1000;
67 #define VIK_SETTINGS_MAP_MIN_SHRINKFACTOR "maps_min_shrinkfactor"
68 #define VIK_SETTINGS_MAP_MAX_SHRINKFACTOR "maps_max_shrinkfactor"
69 static gdouble MAX_SHRINKFACTOR = 8.0000001; /* zoom 1 viewing 8-tiles */
70 static gdouble MIN_SHRINKFACTOR = 0.0312499; /* zoom 32 viewing 1-tiles */
72 #define VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR "maps_real_min_shrinkfactor"
73 static gdouble REAL_MIN_SHRINKFACTOR = 0.0039062499; /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
75 #define VIK_SETTINGS_MAP_SCALE_INC_UP "maps_scale_inc_up"
76 static guint SCALE_INC_UP = 2;
77 #define VIK_SETTINGS_MAP_SCALE_INC_DOWN "maps_scale_inc_down"
78 static guint SCALE_INC_DOWN = 4;
79 #define VIK_SETTINGS_MAP_SCALE_SMALLER_ZOOM_FIRST "maps_scale_smaller_zoom_first"
80 static gboolean SCALE_SMALLER_ZOOM_FIRST = TRUE;
82 /****** MAP TYPES ******/
84 static GList *__map_types = NULL;
86 #define NUM_MAP_TYPES g_list_length(__map_types)
88 /* List of label for each map type */
89 static gchar **params_maptypes = NULL;
91 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
92 static guint *params_maptypes_ids = NULL;
94 /******** MAPZOOMS *********/
96 static gchar *params_mapzooms[] = { N_("Use Viking Zoom Level"), "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "USGS 10k", "USGS 24k", "USGS 25k", "USGS 50k", "USGS 100k", "USGS 200k", "USGS 250k", NULL };
97 static gdouble __mapzooms_x[] = { 0.0, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 1.016, 2.4384, 2.54, 5.08, 10.16, 20.32, 25.4 };
98 static gdouble __mapzooms_y[] = { 0.0, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 1.016, 2.4384, 2.54, 5.08, 10.16, 20.32, 25.4 };
100 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
102 /**************************/
105 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
106 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
107 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
108 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
109 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
110 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
111 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values );
112 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
113 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
114 static void maps_layer_free ( VikMapsLayer *vml );
115 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
116 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
117 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
118 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
119 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
120 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
121 static guint map_uniq_id_to_index ( guint uniq_id );
124 static VikLayerParamScale params_scales[] = {
125 /* min, max, step, digits (decimal places) */
126 { 0, 255, 3, 0 }, /* alpha */
129 static VikLayerParamData id_default ( void ) { return VIK_LPD_UINT ( MAP_ID_MAPBOX_OUTDOORS ); }
130 static VikLayerParamData directory_default ( void )
132 VikLayerParamData data;
133 VikLayerParamData *pref = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir");
134 if (pref) data.s = g_strdup ( pref->s ); else data.s = "";
137 static VikLayerParamData file_default ( void )
139 VikLayerParamData data;
143 static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
144 static VikLayerParamData mapzoom_default ( void ) { return VIK_LPD_UINT ( 0 ); }
146 static gchar *cache_types[] = { "Viking", N_("OSM"), NULL };
147 static VikMapsCacheLayout cache_layout_default_value = VIK_MAPS_CACHE_LAYOUT_VIKING;
148 static VikLayerParamData cache_layout_default ( void ) { return VIK_LPD_UINT ( cache_layout_default_value ); }
150 VikLayerParam maps_layer_params[] = {
151 // NB mode => id - But can't break file format just to rename something better
152 { 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 },
153 { 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 },
154 { VIK_LAYER_MAPS, "cache_type", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Cache Layout:"), VIK_LAYER_WIDGET_COMBOBOX, cache_types, NULL,
155 N_("This determines the tile storage layout on disk"), cache_layout_default, NULL, NULL },
156 { 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,
157 N_("An MBTiles file. Only applies when the map type method is 'MBTiles'"), file_default, NULL, NULL },
158 { VIK_LAYER_MAPS, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
159 N_("Control the Alpha value for transparency effects"), alpha_default, NULL, NULL },
160 { 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 },
161 { VIK_LAYER_MAPS, "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
162 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 },
163 { VIK_LAYER_MAPS, "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
164 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."),
165 mapzoom_default, NULL, NULL },
180 void maps_layer_set_autodownload_default ( gboolean autodownload )
182 // Set appropriate function
184 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_true_default;
186 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_false_default;
189 void maps_layer_set_cache_default ( VikMapsCacheLayout layout )
191 // Override default value returned by the default param function
192 cache_layout_default_value = layout;
195 static VikToolInterface maps_tools[] = {
196 { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
197 (VikToolConstructorFunc) maps_layer_download_create,
201 (VikToolMouseFunc) maps_layer_download_click,
203 (VikToolMouseFunc) maps_layer_download_release,
206 GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf, NULL },
209 VikLayerInterface vik_maps_layer_interface = {
213 &vikmapslayer_pixbuf,
216 sizeof(maps_tools) / sizeof(maps_tools[0]),
225 (VikLayerFuncCreate) maps_layer_new,
226 (VikLayerFuncRealize) NULL,
227 (VikLayerFuncPostRead) maps_layer_post_read,
228 (VikLayerFuncFree) maps_layer_free,
230 (VikLayerFuncProperties) NULL,
231 (VikLayerFuncDraw) maps_layer_draw,
232 (VikLayerFuncChangeCoordMode) NULL,
234 (VikLayerFuncGetTimestamp) NULL,
236 (VikLayerFuncSetMenuItemsSelection) NULL,
237 (VikLayerFuncGetMenuItemsSelection) NULL,
239 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
240 (VikLayerFuncSublayerAddMenuItems) NULL,
242 (VikLayerFuncSublayerRenameRequest) NULL,
243 (VikLayerFuncSublayerToggleVisible) NULL,
244 (VikLayerFuncSublayerTooltip) NULL,
245 (VikLayerFuncLayerTooltip) maps_layer_tooltip,
246 (VikLayerFuncLayerSelected) NULL,
248 (VikLayerFuncMarshall) maps_layer_marshall,
249 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
251 (VikLayerFuncSetParam) maps_layer_set_param,
252 (VikLayerFuncGetParam) maps_layer_get_param,
253 (VikLayerFuncChangeParam) maps_layer_change_param,
255 (VikLayerFuncReadFileData) NULL,
256 (VikLayerFuncWriteFileData) NULL,
258 (VikLayerFuncDeleteItem) NULL,
259 (VikLayerFuncCutItem) NULL,
260 (VikLayerFuncCopyItem) NULL,
261 (VikLayerFuncPasteItem) NULL,
262 (VikLayerFuncFreeCopiedItem) NULL,
263 (VikLayerFuncDragDropRequest) NULL,
265 (VikLayerFuncSelectClick) NULL,
266 (VikLayerFuncSelectMove) NULL,
267 (VikLayerFuncSelectRelease) NULL,
268 (VikLayerFuncSelectedViewportMenu) NULL,
271 struct _VikMapsLayer {
275 VikMapsCacheLayout cache_layout;
278 gdouble xmapzoom, ymapzoom;
280 gboolean autodownload;
281 gboolean adl_only_missing;
282 VikCoord *last_center;
286 gint dl_tool_x, dl_tool_y;
288 GtkMenu *dl_right_click_menu;
289 VikCoord redownload_ul, redownload_br; /* right click menu only */
290 VikViewport *redownload_vvp;
292 #ifdef HAVE_SQLITE3_H
297 enum { REDOWNLOAD_NONE = 0, /* download only missing maps */
298 REDOWNLOAD_BAD, /* download missing and bad maps */
299 REDOWNLOAD_NEW, /* download missing maps that are newer on server only */
300 REDOWNLOAD_ALL, /* download all maps */
301 DOWNLOAD_OR_REFRESH }; /* download missing maps and refresh cache */
303 static VikLayerParam prefs[] = {
304 { 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 },
307 void maps_layer_init ()
309 VikLayerParamData tmp;
310 tmp.s = maps_layer_default_dir();
311 a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
313 gint max_tiles = MAX_TILES;
314 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_MAX_TILES, &max_tiles ) )
315 MAX_TILES = max_tiles;
318 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MIN_SHRINKFACTOR, &gdtmp ) )
319 MIN_SHRINKFACTOR = gdtmp;
321 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MAX_SHRINKFACTOR, &gdtmp ) )
322 MAX_SHRINKFACTOR = gdtmp;
324 if ( a_settings_get_double ( VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR, &gdtmp ) )
325 REAL_MIN_SHRINKFACTOR = gdtmp;
328 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_UP, &gitmp ) )
329 SCALE_INC_UP = gitmp;
331 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_DOWN, &gitmp ) )
332 SCALE_INC_DOWN = gitmp;
334 gboolean gbtmp = TRUE;
335 if ( a_settings_get_boolean ( VIK_SETTINGS_MAP_SCALE_SMALLER_ZOOM_FIRST, &gbtmp ) )
336 SCALE_SMALLER_ZOOM_FIRST = gbtmp;
340 /****************************************/
341 /******** MAPS LAYER TYPES **************/
342 /****************************************/
344 void _add_map_source ( guint16 id, const char *label, VikMapSource *map )
348 len = g_strv_length (params_maptypes);
350 params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
351 params_maptypes[len] = g_strdup (label);
352 params_maptypes[len+1] = NULL;
355 params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
356 params_maptypes_ids[len] = id;
357 params_maptypes_ids[len+1] = 0;
359 /* We have to clone */
360 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
361 /* Register the clone in the list */
362 __map_types = g_list_append(__map_types, clone);
365 We have to ensure the mode LayerParam references the up-to-date
369 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
370 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
372 maps_layer_params[0].widget_data = params_maptypes;
373 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
376 void _update_map_source ( const char *label, VikMapSource *map, int index )
378 GList *item = g_list_nth (__map_types, index);
379 g_object_unref (item->data);
380 item->data = g_object_ref (map);
381 /* Change previous data */
382 g_free (params_maptypes[index]);
383 params_maptypes[index] = g_strdup (label);
387 * maps_layer_register_map_source:
388 * @map: the new VikMapSource
390 * Register a new VikMapSource.
391 * Override existing one (equality of id).
393 void maps_layer_register_map_source ( VikMapSource *map )
395 g_assert(map != NULL);
397 guint16 id = vik_map_source_get_uniq_id(map);
398 const char *label = vik_map_source_get_label(map);
399 g_assert(label != NULL);
401 int previous = map_uniq_id_to_index (id);
402 if (previous != NUM_MAP_TYPES)
404 _update_map_source (label, map, previous);
408 _add_map_source (id, label, map);
412 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
413 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
414 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
417 * vik_maps_layer_get_map_type:
419 * Returns the actual map id (rather than the internal type index value)
421 guint vik_maps_layer_get_map_type(VikMapsLayer *vml)
423 return MAPS_LAYER_NTH_ID(vml->maptype);
427 * vik_maps_layer_set_map_type:
430 void vik_maps_layer_set_map_type(VikMapsLayer *vml, guint map_type)
432 gint maptype = map_uniq_id_to_index(map_type);
433 if ( maptype == NUM_MAP_TYPES )
434 g_warning(_("Unknown map type"));
436 vml->maptype = maptype;
440 * vik_maps_layer_get_default_map_type:
443 guint vik_maps_layer_get_default_map_type ()
445 VikLayerInterface *vli = vik_layer_get_interface ( VIK_LAYER_MAPS );
446 VikLayerParamData vlpd = a_layer_defaults_get ( vli->fixed_layer_name, "mode", VIK_LAYER_PARAM_UINT );
452 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
454 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
457 /****************************************/
458 /******** CACHE DIR STUFF ***************/
459 /****************************************/
461 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
462 #define DIRECTDIRACCESS_WITH_NAME "%s%s" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
463 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
464 #define MAPS_CACHE_DIR maps_layer_default_dir()
468 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
469 #define LOCAL_MAPS_DIR "VIKING-MAPS"
470 #elif defined __APPLE__
472 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
473 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
476 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
477 #define LOCAL_MAPS_DIR ".viking-maps"
480 gchar *maps_layer_default_dir ()
482 static gchar *defaultdir = NULL;
485 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
486 const gchar *mapdir = g_getenv("VIKING_MAPS");
488 defaultdir = g_strdup ( mapdir );
489 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
490 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
492 const gchar *home = g_get_home_dir();
493 if (!home || g_access(home, W_OK))
494 home = g_get_home_dir ();
496 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
498 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
500 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
502 /* Add the separator at the end */
503 gchar *tmp = defaultdir;
504 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
507 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
512 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
514 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
516 if ( g_mkdir ( vml->cache_dir, 0777 ) != 0 )
517 g_warning ( "%s: Failed to create directory %s", __FUNCTION__, vml->cache_dir );
521 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
523 g_assert ( vml != NULL);
524 g_free ( vml->cache_dir );
525 vml->cache_dir = NULL;
526 const gchar *mydir = dir;
528 if ( dir == NULL || dir[0] == '\0' )
530 if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
531 mydir = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s;
534 gchar *canonical_dir = vu_get_canonical_filename ( VIK_LAYER(vml), mydir );
536 // Ensure cache_dir always ends with a separator
537 guint len = strlen(canonical_dir);
538 // Unless the dir is not valid
541 if ( canonical_dir[len-1] != G_DIR_SEPARATOR )
543 vml->cache_dir = g_strconcat ( canonical_dir, G_DIR_SEPARATOR_S, NULL );
544 g_free ( canonical_dir );
547 vml->cache_dir = canonical_dir;
550 maps_layer_mkdir_if_default_dir ( vml );
554 static void maps_layer_set_file ( VikMapsLayer *vml, const gchar *name )
557 g_free (vml->filename);
558 vml->filename = g_strdup (name);
561 /****************************************/
562 /******** GOBJECT STUFF *****************/
563 /****************************************/
565 GType vik_maps_layer_get_type ()
567 static GType vml_type = 0;
571 static const GTypeInfo vml_info =
573 sizeof (VikMapsLayerClass),
574 NULL, /* base_init */
575 NULL, /* base_finalize */
576 NULL, /* class init */
577 NULL, /* class_finalize */
578 NULL, /* class_data */
579 sizeof (VikMapsLayer),
581 NULL /* instance init */
583 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
589 /****************************************/
590 /************** PARAMETERS **************/
591 /****************************************/
593 static guint map_index_to_uniq_id (guint16 index)
595 g_assert ( index < NUM_MAP_TYPES );
596 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
599 static guint map_uniq_id_to_index ( guint uniq_id )
602 for ( i = 0; i < NUM_MAP_TYPES; i++ )
603 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
605 return NUM_MAP_TYPES; /* no such thing */
608 #define VIK_SETTINGS_MAP_LICENSE_SHOWN "map_license_shown"
611 * Convenience function to display the license
613 static void maps_show_license ( GtkWindow *parent, VikMapSource *map )
615 a_dialog_license ( parent,
616 vik_map_source_get_label (map),
617 vik_map_source_get_license (map),
618 vik_map_source_get_license_url (map) );
621 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
625 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
626 case PARAM_CACHE_LAYOUT: if ( data.u < VIK_MAPS_CACHE_LAYOUT_NUM ) vml->cache_layout = data.u; break;
627 case PARAM_FILE: maps_layer_set_file ( vml, data.s ); break;
628 case PARAM_MAPTYPE: {
629 gint maptype = map_uniq_id_to_index(data.u);
630 if ( maptype == NUM_MAP_TYPES )
631 g_warning(_("Unknown map type"));
633 vml->maptype = maptype;
635 // When loading from a file don't need the license reminder - ensure it's saved into the 'seen' list
636 if ( is_file_operation ) {
637 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u );
640 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
641 if (vik_map_source_get_license (map) != NULL) {
642 // Check if licence for this map type has been shown before
643 if ( ! a_settings_get_integer_list_contains ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u ) ) {
645 maps_show_license ( VIK_GTK_WINDOW_FROM_WIDGET(vvp), map );
646 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u );
653 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
654 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
655 case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
656 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
657 vml->mapzoom_id = data.u;
658 vml->xmapzoom = __mapzooms_x [data.u];
659 vml->ymapzoom = __mapzooms_y [data.u];
660 }else g_warning (_("Unknown Map Zoom")); break;
666 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
668 VikLayerParamData rv;
671 case PARAM_CACHE_DIR:
673 gboolean set = FALSE;
674 /* Only save a blank when the map cache location equals the default
675 On reading in, when it is blank then the default is reconstructed
676 Since the default changes dependent on the user and OS, it means the resultant file is more portable */
677 if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
681 else if ( is_file_operation && vml->cache_dir ) {
682 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
683 gchar *cwd = g_get_current_dir();
685 rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
686 if ( !rv.s ) rv.s = "";
692 rv.s = vml->cache_dir ? vml->cache_dir : "";
695 case PARAM_CACHE_LAYOUT: rv.u = vml->cache_layout; break;
696 case PARAM_FILE: rv.s = vml->filename; break;
697 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
698 case PARAM_ALPHA: rv.u = vml->alpha; break;
699 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
700 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
701 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
707 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values )
709 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
710 // Alter sensitivity of download option widgets according to the maptype setting.
711 case PARAM_MAPTYPE: {
713 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
714 // Is it *not* the OSM On Disk Tile Layout or the MBTiles type or the OSM Metatiles type
715 gboolean sensitive = ( MAP_ID_OSM_ON_DISK != vlpd.u &&
716 MAP_ID_MBTILES != vlpd.u &&
717 MAP_ID_OSM_METATILES != vlpd.u );
718 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
719 GtkWidget **ww2 = values[UI_CHG_LABELS];
720 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
721 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
722 GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
723 GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
724 // Depends on autodownload value
725 gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
726 if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
727 if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
728 if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
729 if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
731 // Cache type not applicable either
732 GtkWidget *w9 = ww1[PARAM_CACHE_LAYOUT];
733 GtkWidget *w10 = ww2[PARAM_CACHE_LAYOUT];
734 if ( w9 ) gtk_widget_set_sensitive ( w9, sensitive );
735 if ( w10 ) gtk_widget_set_sensitive ( w10, sensitive );
737 // File only applicable for MBTiles type
738 // Directory for all other types
739 sensitive = ( MAP_ID_MBTILES == vlpd.u);
740 GtkWidget *w5 = ww1[PARAM_FILE];
741 GtkWidget *w6 = ww2[PARAM_FILE];
742 GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
743 GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
744 if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
745 if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
746 if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
747 if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
752 // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
753 case PARAM_AUTODOWNLOAD: {
755 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
756 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
757 GtkWidget **ww2 = values[UI_CHG_LABELS];
758 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
759 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
760 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
761 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
768 /****************************************/
769 /****** CREATING, COPYING, FREEING ******/
770 /****************************************/
772 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
774 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
775 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
777 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
779 vml->dl_tool_x = vml->dl_tool_y = -1;
780 vml->last_center = NULL;
781 vml->last_xmpp = 0.0;
782 vml->last_ympp = 0.0;
784 vml->dl_right_click_menu = NULL;
785 vml->filename = NULL;
789 static void maps_layer_free ( VikMapsLayer *vml )
791 g_free ( vml->cache_dir );
792 vml->cache_dir = NULL;
793 if ( vml->dl_right_click_menu )
794 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
795 g_free(vml->last_center);
796 vml->last_center = NULL;
797 g_free ( vml->filename );
798 vml->filename = NULL;
800 #ifdef HAVE_SQLITE3_H
801 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
802 if ( vik_map_source_is_mbtiles ( map ) ) {
803 if ( vml->mbtiles ) {
804 int ans = sqlite3_close ( vml->mbtiles );
805 if ( ans != SQLITE_OK ) {
806 // Only to console for information purposes only
807 g_warning ( "SQL Close problem: %d", ans );
814 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
816 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
817 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
821 /* If this method is not called in file reading context
822 * it is called in GUI context.
823 * So, we can check if we have to inform the user about inconsistency */
824 VikViewportDrawMode vp_drawmode;
825 vp_drawmode = vik_viewport_get_drawmode ( vp );
827 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
828 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
829 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
830 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
835 // Performed in post read as we now know the map type
836 #ifdef HAVE_SQLITE3_H
838 if ( vik_map_source_is_mbtiles ( map ) ) {
839 int ans = sqlite3_open_v2 ( vml->filename,
841 SQLITE_OPEN_READONLY,
843 if ( ans != SQLITE_OK ) {
844 // That didn't work, so here's why:
845 g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
847 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
848 _("Failed to open MBTiles file: %s"),
855 // If the on Disk OSM Tile Layout type
856 if ( vik_map_source_get_uniq_id(map) == MAP_ID_OSM_ON_DISK ) {
857 // Copy the directory into filename
858 // thus the mapcache look up will be unique when using more than one of these map types
859 g_free ( vml->filename );
860 vml->filename = g_strdup (vml->cache_dir);
864 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
866 return vik_maps_layer_get_map_label ( vml );
869 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
871 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
874 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
876 VikMapsLayer *rv = maps_layer_new ( vvp );
877 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
878 maps_layer_post_read ( VIK_LAYER(rv), vvp, FALSE );
882 /*********************/
883 /****** DRAWING ******/
884 /*********************/
886 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
889 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
890 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
891 g_object_unref ( G_OBJECT(pixbuf) );
895 #ifdef HAVE_SQLITE3_H
897 static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
899 g_warning ( "Found %d columns", cols );
901 for ( i = 0; i < cols; i++ ) {
902 g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
911 static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
913 GdkPixbuf *pixbuf = NULL;
915 // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
916 gint flip_y = (gint) pow(2, zoom)-1 - yy;
917 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 );
919 gboolean finished = FALSE;
921 sqlite3_stmt *sql_stmt = NULL;
922 int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
923 if ( ans != SQLITE_OK ) {
924 g_warning ( "%s: %s - %d: %s", __FUNCTION__, "prepare failure", ans, statement );
928 while ( !finished ) {
929 ans = sqlite3_step ( sql_stmt );
932 // Get tile_data blob
933 int count = sqlite3_column_count(sql_stmt);
935 g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
939 const void *data = sqlite3_column_blob ( sql_stmt, 0 );
940 int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
942 g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
946 // Convert these blob bytes into a pixbuf via these streaming operations
947 GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
948 GError *error = NULL;
949 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
951 g_warning ( "%s: %s", __FUNCTION__, error->message );
952 g_error_free ( error );
954 g_input_stream_close ( stream, NULL, NULL );
960 // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
962 // and give up on any errors
963 if ( ans != SQLITE_DONE )
964 g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
969 (void)sqlite3_finalize ( sql_stmt );
971 g_free ( statement );
977 static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
979 GdkPixbuf *pixbuf = NULL;
981 #ifdef HAVE_SQLITE3_H
982 if ( vml->mbtiles ) {
984 gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
986 int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
987 if ( ans != SQLITE_OK ) {
988 // Only to console for information purposes only
989 g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
990 sqlite3_free( errMsg );
992 g_free ( statement );
995 // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
996 // Hence this specific function
997 pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
1004 static GdkPixbuf *get_pixbuf_from_metatile ( VikMapsLayer *vml, gint xx, gint yy, gint zz )
1006 const int tile_max = METATILE_MAX_SIZE;
1007 char err_msg[PATH_MAX];
1012 buf = g_malloc(tile_max);
1018 len = metatile_read(vml->cache_dir, xx, yy, zz, buf, tile_max, &compressed, err_msg);
1022 // Not handled yet - I don't think this is used often - so implement later if necessary
1023 g_warning ( "Compressed metatiles not implemented:%s\n", __FUNCTION__);
1028 // Convert these buf bytes into a pixbuf via these streaming operations
1029 GdkPixbuf *pixbuf = NULL;
1031 GInputStream *stream = g_memory_input_stream_new_from_data ( buf, len, NULL );
1032 GError *error = NULL;
1033 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
1035 g_warning ( "%s: %s", __FUNCTION__, error->message );
1036 g_error_free ( error );
1038 g_input_stream_close ( stream, NULL, NULL );
1045 g_warning ( "FAILED:%s %s", __FUNCTION__, err_msg);
1051 * Caller has to decrease reference counter of returned
1052 * GdkPixbuf, when buffer is no longer needed.
1054 static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
1056 // Apply alpha setting
1057 if ( pixbuf && vml->alpha < 255 )
1058 pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
1060 if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
1061 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
1064 a_mapcache_add ( pixbuf, (mapcache_extra_t) {0.0}, mapcoord->x, mapcoord->y,
1065 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
1066 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1071 static void get_filename ( const gchar *cache_dir,
1072 VikMapsCacheLayout cl,
1079 gchar *filename_buf,
1081 const gchar* file_extension )
1084 case VIK_MAPS_CACHE_LAYOUT_OSM:
1086 if ( g_strcmp0 ( cache_dir, MAPS_CACHE_DIR ) )
1087 // Cache dir not the default - assume it's been directed somewhere specific
1088 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1090 // Using default cache - so use the map name in the directory path
1091 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS_WITH_NAME, cache_dir, name, (17 - scale), x, y, file_extension );
1094 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1097 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE, cache_dir, id, scale, z, x, y );
1103 * Caller has to decrease reference counter of returned
1104 * GdkPixbuf, when buffer is no longer needed.
1106 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, guint16 id, const gchar* mapname, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
1111 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
1112 id, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1115 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1116 if ( vik_map_source_is_direct_file_access(map) ) {
1117 // ATM MBTiles must be 'a direct access type'
1118 if ( vik_map_source_is_mbtiles(map) ) {
1119 pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1120 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1121 // return now to avoid file tests that aren't appropriate for this map type
1124 else if ( vik_map_source_is_osm_meta_tiles(map) ) {
1125 pixbuf = get_pixbuf_from_metatile ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1126 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1130 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, NULL,
1131 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1132 vik_map_source_get_file_extension(map) );
1135 get_filename ( vml->cache_dir, vml->cache_layout, id, mapname,
1136 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1137 vik_map_source_get_file_extension(map) );
1139 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1142 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
1144 /* free the pixbuf on error */
1147 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE ) {
1149 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1150 gchar* msg = g_strdup_printf ( _("Couldn't open image file: %s"), gx->message );
1151 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1156 g_error_free ( gx );
1158 g_object_unref ( G_OBJECT(pixbuf) );
1161 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1168 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
1170 const VikCoord *center = vik_viewport_get_center ( vvp );
1172 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
1173 /* D'n'D pan in action: do not download */
1176 // Don't attempt to download unsupported zoom levels
1177 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1178 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1179 guint8 zl = map_utils_mpp_to_zoom_level ( xzoom );
1180 if ( zl < vik_map_source_get_zoom_min(map) || zl > vik_map_source_get_zoom_max(map) )
1183 if (vml->last_center == NULL) {
1184 VikCoord *new_center = g_malloc(sizeof(VikCoord));
1185 *new_center = *center;
1186 vml->last_center = new_center;
1187 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1188 vml->last_ympp = vik_viewport_get_ympp(vvp);
1192 /* TODO: perhaps vik_coord_diff() */
1193 if (vik_coord_equals(vml->last_center, center)
1194 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1195 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1198 *(vml->last_center) = *center;
1199 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1200 vml->last_ympp = vik_viewport_get_ympp(vvp);
1207 gboolean try_draw_scale_down (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1208 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1212 for (scale_inc = 1; scale_inc <= SCALE_INC_DOWN; scale_inc++) {
1213 // Try with smaller zooms
1214 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
1215 MapCoord ulm2 = ulm;
1216 ulm2.x = ulm.x / scale_factor;
1217 ulm2.y = ulm.y / scale_factor;
1218 ulm2.scale = ulm.scale + scale_inc;
1219 pixbuf = get_pixbuf ( vml, id, mapname, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1221 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1222 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1223 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1224 g_object_unref(pixbuf);
1234 gboolean try_draw_scale_up (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1235 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1238 // Try with bigger zooms
1240 for (scale_dec = 1; scale_dec <= SCALE_INC_UP; scale_dec++) {
1242 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
1243 MapCoord ulm2 = ulm;
1244 ulm2.x = ulm.x * scale_factor;
1245 ulm2.y = ulm.y * scale_factor;
1246 ulm2.scale = ulm.scale - scale_dec;
1247 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1248 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1249 MapCoord ulm3 = ulm2;
1252 pixbuf = get_pixbuf ( vml, id, mapname, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1256 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1257 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1258 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1259 g_object_unref(pixbuf);
1268 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1271 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1272 gdouble yzoom = vik_viewport_get_ympp ( vvp );
1273 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
1274 gboolean existence_only = FALSE;
1276 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1277 xshrinkfactor = vml->xmapzoom / xzoom;
1278 yshrinkfactor = vml->ymapzoom / yzoom;
1279 xzoom = vml->xmapzoom;
1280 yzoom = vml->xmapzoom;
1281 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1282 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
1283 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1284 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
1285 existence_only = TRUE;
1288 // Report the reason for not drawing
1289 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1290 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));
1291 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1300 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1301 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1302 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
1306 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1307 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
1308 guint16 id = vik_map_source_get_uniq_id(map);
1309 const gchar *mapname = vik_map_source_get_name(map);
1312 gint xx, yy, width, height;
1315 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1316 // which can happen when using a small fixed zoom level and viewing large areas.
1317 // Also prevents very large number of tile download requests
1318 gint tiles = (xmax-xmin) * (ymax-ymin);
1319 if ( tiles > MAX_TILES ) {
1320 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1321 existence_only = TRUE;
1324 guint max_path_len = strlen(vml->cache_dir) + 40;
1325 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1327 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
1328 g_debug("%s: Starting autodownload", __FUNCTION__);
1329 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
1330 // Try to download newer tiles
1331 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1333 // Download only missing tiles
1334 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
1337 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
1338 for ( x = xmin; x <= xmax; x++ ) {
1339 for ( y = ymin; y <= ymax; y++ ) {
1342 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1344 width = gdk_pixbuf_get_width ( pixbuf );
1345 height = gdk_pixbuf_get_height ( pixbuf );
1347 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1348 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1352 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1353 g_object_unref(pixbuf);
1357 } else { /* tilesize is known, don't have to keep converting coords */
1358 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1359 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
1360 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1361 gint tilesize_x_ceil = ceil ( tilesize_x );
1362 gint tilesize_y_ceil = ceil ( tilesize_y );
1363 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1364 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
1365 gint xx_tmp, yy_tmp;
1366 gint base_yy, xend, yend;
1368 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1369 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1371 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1372 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1373 xx = xx_tmp; yy = yy_tmp;
1374 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1375 * eg if tile size 128, shrinkfactor 0.333 */
1376 xx -= (tilesize_x/2);
1377 base_yy = yy - (tilesize_y/2);
1379 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1381 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1385 if ( existence_only ) {
1386 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1387 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, vik_map_source_get_name(map),
1388 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1390 get_filename ( vml->cache_dir, vml->cache_layout, id, vik_map_source_get_name(map),
1391 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1393 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1394 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1395 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1398 // Try correct scale first
1399 int scale_factor = 1;
1400 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1402 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1403 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1404 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1405 g_object_unref(pixbuf);
1408 // Otherwise try different scales
1409 if ( SCALE_SMALLER_ZOOM_FIRST ) {
1410 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) ) {
1411 try_draw_scale_up(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1415 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) ) {
1416 try_draw_scale_down(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1427 // ATM Only show tile grid lines in extreme debug mode
1428 if ( vik_debug && vik_verbose ) {
1429 /* Grid drawing here so it gets drawn on top of the map */
1430 /* Thus loop around x & y again, but this time separately */
1431 /* Only showing grid for the current scale */
1432 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
1433 /* Draw single grid lines across the whole screen */
1434 gint width = vik_viewport_get_width(vvp);
1435 gint height = vik_viewport_get_height(vvp);
1436 xx = xx_tmp; yy = yy_tmp;
1437 gint base_xx = xx - (tilesize_x/2);
1438 base_yy = yy - (tilesize_y/2);
1441 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1442 vik_viewport_draw_line ( vvp, black_gc, xx, base_yy, xx, height );
1447 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1448 vik_viewport_draw_line ( vvp, black_gc, base_xx, yy, width, yy );
1454 g_free ( path_buf );
1458 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1460 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
1465 gdouble level = vik_viewport_get_zoom ( vvp );
1467 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1468 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
1471 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1472 vik_viewport_add_logo ( vvp, logo );
1474 /* get corner coords */
1475 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1476 /* UTM multi-zone stuff by Kit Transue */
1477 gchar leftmost_zone, rightmost_zone, i;
1478 leftmost_zone = vik_viewport_leftmost_zone( vvp );
1479 rightmost_zone = vik_viewport_rightmost_zone( vvp );
1480 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1481 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1482 maps_layer_draw_section ( vml, vvp, &ul, &br );
1486 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1487 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1489 maps_layer_draw_section ( vml, vvp, &ul, &br );
1494 /*************************/
1495 /****** DOWNLOADING ******/
1496 /*************************/
1498 /* pass along data to thread, exists even if layer is deleted. */
1501 gchar *filename_buf;
1502 VikMapsCacheLayout cache_layout;
1503 gint x0, y0, xf, yf;
1509 gboolean refresh_display;
1512 gboolean map_layer_alive;
1516 static void mdi_free ( MapDownloadInfo *mdi )
1518 vik_mutex_free(mdi->mutex);
1519 g_free ( mdi->cache_dir );
1520 mdi->cache_dir = NULL;
1521 g_free ( mdi->filename_buf );
1522 mdi->filename_buf = NULL;
1526 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1528 MapDownloadInfo *mdi = ptr;
1529 g_mutex_lock(mdi->mutex);
1530 mdi->map_layer_alive = FALSE;
1531 g_mutex_unlock(mdi->mutex);
1534 static gboolean is_in_area (VikMapSource *map, MapCoord mc)
1537 vik_map_source_mapcoord_to_center_coord ( map, &mc, &vc );
1540 tl.lat = vik_map_source_get_lat_max(map);
1541 tl.lon = vik_map_source_get_lon_min(map);
1543 br.lat = vik_map_source_get_lat_min(map);
1544 br.lon = vik_map_source_get_lon_max(map);
1546 vik_coord_load_from_latlon (&vctl, VIK_COORD_LATLON, &tl);
1548 vik_coord_load_from_latlon (&vcbr, VIK_COORD_LATLON, &br);
1550 return vik_coord_inside ( &vc, &vctl, &vcbr );
1553 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1555 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1557 MapCoord mcoord = mdi->mapcoord;
1559 for ( x = mdi->x0; x <= mdi->xf; x++ )
1562 for ( y = mdi->y0; y <= mdi->yf; y++ )
1565 // Only attempt to download a tile from supported areas
1566 if ( is_in_area ( MAPS_LAYER_NTH_TYPE(mdi->maptype), mcoord ) )
1568 gboolean remove_mem_cache = FALSE;
1569 gboolean need_download = FALSE;
1571 get_filename ( mdi->cache_dir, mdi->cache_layout,
1572 vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1573 vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1574 mdi->mapcoord.scale, mdi->mapcoord.z, x, y, mdi->filename_buf, mdi->maxlen,
1575 vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1578 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1580 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1584 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1585 need_download = TRUE;
1586 remove_mem_cache = TRUE;
1588 } else { /* in case map file already exists */
1589 switch (mdi->redownload) {
1590 case REDOWNLOAD_NONE:
1593 case REDOWNLOAD_BAD:
1595 /* see if this one is bad or what */
1597 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1598 if (gx || (!pixbuf)) {
1599 if ( g_remove ( mdi->filename_buf ) )
1600 g_warning ( "REDOWNLOAD failed to remove: %s", mdi->filename_buf );
1601 need_download = TRUE;
1602 remove_mem_cache = TRUE;
1603 g_error_free ( gx );
1606 g_object_unref ( pixbuf );
1611 case REDOWNLOAD_NEW:
1612 need_download = TRUE;
1613 remove_mem_cache = TRUE;
1616 case REDOWNLOAD_ALL:
1617 /* FIXME: need a better way than to erase file in case of server/network problem */
1618 if ( g_remove ( mdi->filename_buf ) )
1619 g_warning ( "REDOWNLOAD failed to remove: %s", mdi->filename_buf );
1620 need_download = TRUE;
1621 remove_mem_cache = TRUE;
1624 case DOWNLOAD_OR_REFRESH:
1625 remove_mem_cache = TRUE;
1629 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1633 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1635 if (need_download) {
1636 DownloadResult_t dr = vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle);
1638 case DOWNLOAD_PARAMETERS_ERROR:
1639 case DOWNLOAD_HTTP_ERROR:
1640 case DOWNLOAD_CONTENT_ERROR: {
1641 // TODO: ?? count up the number of download errors somehow...
1642 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Failed to download tile") );
1643 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1647 case DOWNLOAD_FILE_WRITE_ERROR: {
1648 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Unable to save tile") );
1649 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1653 case DOWNLOAD_SUCCESS:
1654 case DOWNLOAD_NOT_REQUIRED:
1660 g_mutex_lock(mdi->mutex);
1661 if (remove_mem_cache)
1662 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 );
1663 if (mdi->refresh_display && mdi->map_layer_alive) {
1664 /* TODO: check if it's on visible area */
1665 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1667 g_mutex_unlock(mdi->mutex);
1668 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1672 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1673 g_mutex_lock(mdi->mutex);
1674 if (mdi->map_layer_alive)
1675 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1676 g_mutex_unlock(mdi->mutex);
1680 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1682 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1684 get_filename ( mdi->cache_dir, mdi->cache_layout,
1685 vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1686 vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1687 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y, mdi->filename_buf, mdi->maxlen,
1688 vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1689 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1691 if ( g_remove ( mdi->filename_buf ) )
1692 g_warning ( "Cleanup failed to remove: %s", mdi->filename_buf );
1697 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1699 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1700 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1702 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1704 // Don't ever attempt download on direct access
1705 if ( vik_map_source_is_direct_file_access ( map ) )
1708 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1709 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1711 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1716 mdi->map_layer_alive = TRUE;
1717 mdi->mutex = vik_mutex_new();
1718 mdi->refresh_display = TRUE;
1720 /* cache_dir and buffer for dest filename */
1721 mdi->cache_dir = g_strdup ( vml->cache_dir );
1722 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1723 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1724 mdi->cache_layout = vml->cache_layout;
1725 mdi->maptype = vml->maptype;
1727 mdi->mapcoord = ulm;
1728 mdi->redownload = redownload;
1730 mdi->x0 = MIN(ulm.x, brm.x);
1731 mdi->xf = MAX(ulm.x, brm.x);
1732 mdi->y0 = MIN(ulm.y, brm.y);
1733 mdi->yf = MAX(ulm.y, brm.y);
1737 MapCoord mcoord = mdi->mapcoord;
1739 if ( mdi->redownload ) {
1740 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1742 /* calculate how many we need */
1743 for ( a = mdi->x0; a <= mdi->xf; a++ )
1746 for ( b = mdi->y0; b <= mdi->yf; b++ )
1749 // Only count tiles from supported areas
1750 if ( is_in_area (map, mcoord) )
1752 get_filename ( mdi->cache_dir, mdi->cache_layout,
1753 vik_map_source_get_uniq_id(map),
1754 vik_map_source_get_name(map),
1755 ulm.scale, ulm.z, a, b, mdi->filename_buf, mdi->maxlen,
1756 vik_map_source_get_file_extension(map) );
1757 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1764 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1766 if ( mdi->mapstoget )
1768 const gchar *tmp_str;
1773 if (redownload == REDOWNLOAD_BAD)
1774 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1776 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1780 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1782 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1784 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1785 /* launch the thread */
1786 a_background_thread ( BACKGROUND_POOL_REMOTE,
1787 VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1788 tmp, /* description string */
1789 (vik_thr_func) map_download_thread, /* function to call within thread */
1790 mdi, /* pass along data */
1791 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1792 (vik_thr_free_func) mdi_cancel_cleanup,
1801 static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1804 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1806 // Don't ever attempt download on direct access
1807 if ( vik_map_source_is_direct_file_access ( map ) )
1810 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1811 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1812 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1816 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1821 mdi->map_layer_alive = TRUE;
1822 mdi->mutex = vik_mutex_new();
1823 mdi->refresh_display = TRUE;
1825 mdi->cache_dir = g_strdup ( vml->cache_dir );
1826 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1827 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1828 mdi->maptype = vml->maptype;
1829 mdi->cache_layout = vml->cache_layout;
1831 mdi->mapcoord = ulm;
1832 mdi->redownload = download_method;
1834 mdi->x0 = MIN(ulm.x, brm.x);
1835 mdi->xf = MAX(ulm.x, brm.x);
1836 mdi->y0 = MIN(ulm.y, brm.y);
1837 mdi->yf = MAX(ulm.y, brm.y);
1841 MapCoord mcoord = mdi->mapcoord;
1843 for (i = mdi->x0; i <= mdi->xf; i++) {
1845 for (j = mdi->y0; j <= mdi->yf; j++) {
1847 // Only count tiles from supported areas
1848 if ( is_in_area (map, mcoord) ) {
1849 get_filename ( mdi->cache_dir, mdi->cache_layout,
1850 vik_map_source_get_uniq_id(map),
1851 vik_map_source_get_name(map),
1852 ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
1853 vik_map_source_get_file_extension(map) );
1854 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1860 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1862 if (mdi->mapstoget) {
1865 fmt = ngettext("Downloading %d %s map...",
1866 "Downloading %d %s maps...",
1868 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1870 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1872 // launch the thread
1873 a_background_thread ( BACKGROUND_POOL_REMOTE,
1874 VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1875 tmp, /* description string */
1876 (vik_thr_func) map_download_thread, /* function to call within thread */
1877 mdi, /* pass along data */
1878 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1879 (vik_thr_free_func) mdi_cancel_cleanup,
1888 * vik_maps_layer_download_section:
1889 * @vml: The Map Layer
1890 * @vvp: The Viewport that the map is on
1891 * @ul: Upper left coordinate of the area to be downloaded
1892 * @br: Bottom right coordinate of the area to be downloaded
1893 * @zoom: The zoom level at which the maps are to be download
1895 * Download a specified map area at a certain zoom level
1897 void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1899 maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1902 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1904 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1907 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1909 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1912 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1914 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1918 * Display a simple dialog with information about this particular map tile
1920 static void maps_layer_tile_info ( VikMapsLayer *vml )
1922 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1924 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1925 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1928 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1931 gchar *filename = NULL;
1932 gchar *source = NULL;
1934 if ( vik_map_source_is_direct_file_access ( map ) ) {
1935 if ( vik_map_source_is_mbtiles ( map ) ) {
1936 filename = g_strdup ( vml->filename );
1937 #ifdef HAVE_SQLITE3_H
1938 // And whether to bother going into the SQL to check it's really there or not...
1939 gchar *exists = NULL;
1940 gint zoom = 17 - ulm.scale;
1941 if ( vml->mbtiles ) {
1942 GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, zoom );
1944 exists = g_strdup ( _("YES") );
1945 g_object_unref ( G_OBJECT(pixbuf) );
1948 exists = g_strdup ( _("NO") );
1952 exists = g_strdup ( _("NO") );
1953 gint flip_y = (gint) pow(2, zoom)-1 - ulm.y;
1954 // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1955 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 );
1958 source = g_strdup ( _("Source: Not available") );
1961 else if ( vik_map_source_is_osm_meta_tiles ( map ) ) {
1962 char path[PATH_MAX];
1963 xyz_to_meta(path, sizeof(path), vml->cache_dir, ulm.x, ulm.y, 17-ulm.scale );
1964 source = g_strdup ( path );
1965 filename = g_strdup ( path );
1968 guint max_path_len = strlen(vml->cache_dir) + 40;
1969 filename = g_malloc ( max_path_len * sizeof(char) );
1970 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM,
1971 vik_map_source_get_uniq_id(map),
1973 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1974 vik_map_source_get_file_extension(map) );
1975 source = g_strconcat ( _("Source: file://"), filename, NULL );
1979 guint max_path_len = strlen(vml->cache_dir) + 40;
1980 filename = g_malloc ( max_path_len * sizeof(char) );
1981 get_filename ( vml->cache_dir, vml->cache_layout,
1982 vik_map_source_get_uniq_id(map),
1983 vik_map_source_get_name(map),
1984 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1985 vik_map_source_get_file_extension(map) );
1986 gchar *url = vik_map_source_default_get_url_display ( VIK_MAP_SOURCE_DEFAULT(map), &ulm );
1987 source = g_markup_printf_escaped ( _("Source: %s"), url );
1991 GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
1992 g_array_append_val ( array, source );
1994 gchar *filemsg = NULL;
1995 gchar *timemsg = NULL;
1997 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1998 filemsg = g_strconcat ( "Tile File: ", filename, NULL );
1999 // Get some timestamp information of the tile
2001 if ( g_stat ( filename, &stat_buf ) == 0 ) {
2003 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
2004 timemsg = g_strdup_printf ( _("Tile File Timestamp: %s"), time_buf );
2007 timemsg = g_strdup ( _("Tile File Timestamp: Not Available") );
2009 g_array_append_val ( array, filemsg );
2010 g_array_append_val ( array, timemsg );
2013 filemsg = g_strdup_printf ( _("Tile File: %s [Not Available]"), filename );
2014 g_array_append_val ( array, filemsg );
2017 a_dialog_list ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Tile Information"), array, 5 );
2018 g_array_free ( array, FALSE );
2023 g_free ( filename );
2026 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2028 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2030 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
2032 if ( event->button == 1 )
2035 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 );
2036 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 );
2037 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
2038 vml->dl_tool_x = vml->dl_tool_y = -1;
2043 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) );
2044 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) );
2046 vml->redownload_vvp = vvp;
2048 vml->dl_tool_x = vml->dl_tool_y = -1;
2050 if ( ! vml->dl_right_click_menu ) {
2052 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2054 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
2056 // Download options aren't for on disk only maps
2057 if ( ! (vik_map_source_is_mbtiles(map) ||
2058 vik_map_source_is_direct_file_access(map) ||
2059 vik_map_source_is_osm_meta_tiles(map)) ) {
2061 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
2062 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
2063 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2065 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
2066 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
2067 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2069 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
2070 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
2071 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2074 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
2075 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
2076 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
2077 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
2080 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
2081 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
2087 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
2092 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2095 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2097 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2098 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
2099 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
2100 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
2101 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
2103 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
2109 // A slightly better way of defining the menu callback information
2110 // This should be easier to extend/rework compared to previously
2117 typedef gpointer menu_array_values[MA_LAST];
2119 static void download_onscreen_maps ( menu_array_values values, gint redownload )
2121 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2122 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2123 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
2125 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
2126 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
2131 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
2132 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
2134 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2135 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
2136 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
2137 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
2138 start_download_thread ( vml, vvp, &ul, &br, redownload );
2139 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
2140 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
2141 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
2142 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
2146 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
2150 static void maps_layer_download_missing_onscreen_maps ( menu_array_values values )
2152 download_onscreen_maps( values, REDOWNLOAD_NONE);
2155 static void maps_layer_download_new_onscreen_maps ( menu_array_values values )
2157 download_onscreen_maps( values, REDOWNLOAD_NEW);
2160 static void maps_layer_redownload_all_onscreen_maps ( menu_array_values values )
2162 download_onscreen_maps( values, REDOWNLOAD_ALL);
2165 static void maps_layer_about ( menu_array_values values )
2167 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2168 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2170 if ( vik_map_source_get_license (map) )
2171 maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
2173 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2174 vik_map_source_get_label (map) );
2178 * maps_layer_how_many_maps:
2179 * Copied from maps_layer_download_section but without the actual download and this returns a value
2181 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
2184 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2186 if ( vik_map_source_is_direct_file_access ( map ) )
2189 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
2190 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
2191 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
2195 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
2200 mdi->map_layer_alive = TRUE;
2201 mdi->mutex = vik_mutex_new();
2202 mdi->refresh_display = FALSE;
2204 mdi->cache_dir = g_strdup ( vml->cache_dir );
2205 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
2206 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
2207 mdi->maptype = vml->maptype;
2208 mdi->cache_layout = vml->cache_layout;
2210 mdi->mapcoord = ulm;
2211 mdi->redownload = redownload;
2213 mdi->x0 = MIN(ulm.x, brm.x);
2214 mdi->xf = MAX(ulm.x, brm.x);
2215 mdi->y0 = MIN(ulm.y, brm.y);
2216 mdi->yf = MAX(ulm.y, brm.y);
2220 if ( mdi->redownload == REDOWNLOAD_ALL ) {
2221 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
2224 /* calculate how many we need */
2225 MapCoord mcoord = mdi->mapcoord;
2226 for (i = mdi->x0; i <= mdi->xf; i++) {
2228 for (j = mdi->y0; j <= mdi->yf; j++) {
2230 // Only count tiles from supported areas
2231 if ( is_in_area ( map, mcoord ) ) {
2232 get_filename ( mdi->cache_dir, mdi->cache_layout,
2233 vik_map_source_get_uniq_id(map),
2234 vik_map_source_get_name(map),
2235 ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
2236 vik_map_source_get_file_extension(map) );
2237 if ( mdi->redownload == REDOWNLOAD_NEW ) {
2238 // Assume the worst - always a new file
2239 // Absolute value would require a server lookup - but that is too slow
2243 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
2248 if ( mdi->redownload == REDOWNLOAD_BAD ) {
2249 /* see if this one is bad or what */
2251 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
2252 if (gx || (!pixbuf)) {
2256 // Other download cases already considered or just ignored
2265 gint rv = mdi->mapstoget;
2273 * maps_dialog_zoom_between:
2274 * This dialog is specific to the map layer, so it's here rather than in dialog.c
2276 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
2281 gint *selected_zoom1,
2282 gint *selected_zoom2,
2283 gchar *download_list[],
2284 gint default_download,
2285 gint *selected_download )
2287 GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
2289 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2290 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2291 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2293 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2294 GtkWidget *response_w = NULL;
2295 #if GTK_CHECK_VERSION (2, 20, 0)
2296 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2298 GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
2299 GtkWidget *zoom_combo1 = vik_combo_box_text_new();
2301 for (s = zoom_list; *s; s++)
2302 vik_combo_box_text_append ( zoom_combo1, *s );
2303 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
2305 GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
2306 GtkWidget *zoom_combo2 = vik_combo_box_text_new();
2307 for (s = zoom_list; *s; s++)
2308 vik_combo_box_text_append ( zoom_combo2, *s );
2309 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
2311 GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
2312 GtkWidget *download_combo = vik_combo_box_text_new();
2313 for (s = download_list; *s; s++)
2314 vik_combo_box_text_append ( download_combo, *s );
2315 gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
2317 GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
2318 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
2319 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
2320 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
2321 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
2322 gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
2323 gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
2325 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
2328 gtk_widget_grab_focus ( response_w );
2330 gtk_widget_show_all ( dialog );
2331 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2332 gtk_widget_destroy(dialog);
2336 // Return selected options
2337 *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
2338 *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
2339 *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
2341 gtk_widget_destroy(dialog);
2345 // My best guess of sensible limits
2346 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
2347 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2350 * Get all maps in the region for zoom levels specified by the user
2351 * Sort of similar to trw_layer_download_map_along_track_cb function
2353 static void maps_layer_download_all ( menu_array_values values )
2355 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2356 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2358 // I don't think we should allow users to hammer the servers too much...
2359 // Delibrately not allowing lowest zoom levels
2360 // Still can give massive numbers to download
2361 // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2362 gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2363 gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2365 gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2366 gint selected_download_method;
2368 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2370 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2371 if (cur_zoom == zoom_vals[default_zoom])
2374 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2376 // Default to only 2 zoom levels below the current one
2377 if (default_zoom > 1 )
2378 lower_zoom = default_zoom - 2;
2380 lower_zoom = default_zoom;
2382 // redownload method - needs to align with REDOWNLOAD* macro values
2383 gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2385 gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2387 if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2395 REDOWNLOAD_NONE, // AKA Missing
2396 &selected_download_method ) ) {
2403 // Find out new current positions
2404 gdouble min_lat, max_lat, min_lon, max_lon;
2405 VikCoord vc_ul, vc_br;
2406 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2407 struct LatLon ll_ul = { max_lat, min_lon };
2408 struct LatLon ll_br = { min_lat, max_lon };
2409 vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2410 vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2412 // Get Maps Count - call for each zoom level (in reverse)
2413 // With REDOWNLOAD_NEW this is a possible maximum
2414 // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2417 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2418 map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2421 g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2423 // Absolute protection of hammering a map server
2424 if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2425 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);
2426 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2431 // Confirm really want to do this
2432 if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2433 gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2434 gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2440 // Get Maps - call for each zoom level (in reverse)
2441 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2442 maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2449 static void maps_layer_flush ( menu_array_values values )
2451 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2452 a_mapcache_flush_type ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)) );
2455 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2458 static menu_array_values values;
2459 values[MA_VML] = vml;
2460 values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2462 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2464 item = gtk_menu_item_new();
2465 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2466 gtk_widget_show ( item );
2468 // Download options aren't for on disk only maps
2469 if ( ! (vik_map_source_is_mbtiles(map) ||
2470 vik_map_source_is_direct_file_access(map) ||
2471 vik_map_source_is_osm_meta_tiles(map)) ) {
2472 /* Now with icons */
2473 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2474 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2475 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), values );
2476 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2477 gtk_widget_show ( item );
2479 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2480 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2481 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2482 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), values );
2483 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2484 gtk_widget_show ( item );
2487 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2488 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2489 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), values );
2490 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2491 gtk_widget_show ( item );
2493 item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2494 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2495 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), values );
2496 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2497 gtk_widget_show ( item );
2500 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2501 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_about), values );
2502 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2503 gtk_widget_show ( item );
2505 // Typical users shouldn't need to use this functionality - so debug only ATM
2507 item = gtk_image_menu_item_new_with_mnemonic ( _("Flush Map Cache") );
2508 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2509 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_flush), values );
2510 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2511 gtk_widget_show ( item );
2516 * Enable downloading maps of the current screen area either 'new' or 'everything'
2518 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2523 static menu_array_values values;
2524 values[MA_VML] = vml;
2525 values[MA_VVP] = vvp;
2528 // Get only new maps
2529 maps_layer_download_new_onscreen_maps ( values );
2531 // Redownload everything
2532 maps_layer_redownload_all_onscreen_maps ( values );