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"
52 #include "background.h"
53 #include "preferences.h"
54 #include "vikmapslayer.h"
55 #include "icons/icons.h"
65 #define VIK_SETTINGS_MAP_MAX_TILES "maps_max_tiles"
66 static gint MAX_TILES = 1000;
68 #define VIK_SETTINGS_MAP_MIN_SHRINKFACTOR "maps_min_shrinkfactor"
69 #define VIK_SETTINGS_MAP_MAX_SHRINKFACTOR "maps_max_shrinkfactor"
70 static gdouble MAX_SHRINKFACTOR = 8.0000001; /* zoom 1 viewing 8-tiles */
71 static gdouble MIN_SHRINKFACTOR = 0.0312499; /* zoom 32 viewing 1-tiles */
73 #define VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR "maps_real_min_shrinkfactor"
74 static gdouble REAL_MIN_SHRINKFACTOR = 0.0039062499; /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
76 #define VIK_SETTINGS_MAP_SCALE_INC_UP "maps_scale_inc_up"
77 static guint SCALE_INC_UP = 2;
78 #define VIK_SETTINGS_MAP_SCALE_INC_DOWN "maps_scale_inc_down"
79 static guint SCALE_INC_DOWN = 4;
80 #define VIK_SETTINGS_MAP_SCALE_SMALLER_ZOOM_FIRST "maps_scale_smaller_zoom_first"
81 static gboolean SCALE_SMALLER_ZOOM_FIRST = TRUE;
83 /****** MAP TYPES ******/
85 static GList *__map_types = NULL;
87 #define NUM_MAP_TYPES g_list_length(__map_types)
89 /* List of label for each map type */
90 static gchar **params_maptypes = NULL;
92 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
93 static guint *params_maptypes_ids = NULL;
95 /******** MAPZOOMS *********/
97 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 };
98 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 };
99 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 };
101 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
103 /**************************/
106 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
107 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
108 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
109 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
110 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
111 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
112 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values );
113 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
114 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
115 static void maps_layer_free ( VikMapsLayer *vml );
116 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
117 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
118 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
119 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
120 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
121 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
122 static guint map_uniq_id_to_index ( guint uniq_id );
125 static VikLayerParamScale params_scales[] = {
126 /* min, max, step, digits (decimal places) */
127 { 0, 255, 3, 0 }, /* alpha */
130 static VikLayerParamData id_default ( void ) { return VIK_LPD_UINT ( MAP_ID_MAPQUEST_OSM ); }
131 static VikLayerParamData directory_default ( void )
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 = "";
138 static VikLayerParamData file_default ( void )
140 VikLayerParamData data;
144 static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
145 static VikLayerParamData mapzoom_default ( void ) { return VIK_LPD_UINT ( 0 ); }
147 static gchar *cache_types[] = { "Viking", N_("OSM"), NULL };
148 static VikMapsCacheLayout cache_layout_default_value = VIK_MAPS_CACHE_LAYOUT_VIKING;
149 static VikLayerParamData cache_layout_default ( void ) { return VIK_LPD_UINT ( cache_layout_default_value ); }
151 VikLayerParam 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 },
181 void maps_layer_set_autodownload_default ( gboolean autodownload )
183 // Set appropriate function
185 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_true_default;
187 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_false_default;
190 void maps_layer_set_cache_default ( VikMapsCacheLayout layout )
192 // Override default value returned by the default param function
193 cache_layout_default_value = layout;
196 static VikToolInterface maps_tools[] = {
197 { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
198 (VikToolConstructorFunc) maps_layer_download_create,
202 (VikToolMouseFunc) maps_layer_download_click,
204 (VikToolMouseFunc) maps_layer_download_release,
207 GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf, NULL },
210 VikLayerInterface vik_maps_layer_interface = {
214 &vikmapslayer_pixbuf,
217 sizeof(maps_tools) / sizeof(maps_tools[0]),
226 (VikLayerFuncCreate) maps_layer_new,
227 (VikLayerFuncRealize) NULL,
228 (VikLayerFuncPostRead) maps_layer_post_read,
229 (VikLayerFuncFree) maps_layer_free,
231 (VikLayerFuncProperties) NULL,
232 (VikLayerFuncDraw) maps_layer_draw,
233 (VikLayerFuncChangeCoordMode) NULL,
235 (VikLayerFuncGetTimestamp) NULL,
237 (VikLayerFuncSetMenuItemsSelection) NULL,
238 (VikLayerFuncGetMenuItemsSelection) NULL,
240 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
241 (VikLayerFuncSublayerAddMenuItems) NULL,
243 (VikLayerFuncSublayerRenameRequest) NULL,
244 (VikLayerFuncSublayerToggleVisible) NULL,
245 (VikLayerFuncSublayerTooltip) NULL,
246 (VikLayerFuncLayerTooltip) maps_layer_tooltip,
247 (VikLayerFuncLayerSelected) NULL,
249 (VikLayerFuncMarshall) maps_layer_marshall,
250 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
252 (VikLayerFuncSetParam) maps_layer_set_param,
253 (VikLayerFuncGetParam) maps_layer_get_param,
254 (VikLayerFuncChangeParam) maps_layer_change_param,
256 (VikLayerFuncReadFileData) NULL,
257 (VikLayerFuncWriteFileData) NULL,
259 (VikLayerFuncDeleteItem) NULL,
260 (VikLayerFuncCutItem) NULL,
261 (VikLayerFuncCopyItem) NULL,
262 (VikLayerFuncPasteItem) NULL,
263 (VikLayerFuncFreeCopiedItem) NULL,
264 (VikLayerFuncDragDropRequest) NULL,
266 (VikLayerFuncSelectClick) NULL,
267 (VikLayerFuncSelectMove) NULL,
268 (VikLayerFuncSelectRelease) NULL,
269 (VikLayerFuncSelectedViewportMenu) NULL,
272 struct _VikMapsLayer {
276 VikMapsCacheLayout cache_layout;
279 gdouble xmapzoom, ymapzoom;
281 gboolean autodownload;
282 gboolean adl_only_missing;
283 VikCoord *last_center;
287 gint dl_tool_x, dl_tool_y;
289 GtkMenu *dl_right_click_menu;
290 VikCoord redownload_ul, redownload_br; /* right click menu only */
291 VikViewport *redownload_vvp;
293 #ifdef HAVE_SQLITE3_H
298 enum { REDOWNLOAD_NONE = 0, /* download only missing maps */
299 REDOWNLOAD_BAD, /* download missing and bad maps */
300 REDOWNLOAD_NEW, /* download missing maps that are newer on server only */
301 REDOWNLOAD_ALL, /* download all maps */
302 DOWNLOAD_OR_REFRESH }; /* download missing maps and refresh cache */
304 static VikLayerParam prefs[] = {
305 { 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 },
308 void maps_layer_init ()
310 VikLayerParamData tmp;
311 tmp.s = maps_layer_default_dir();
312 a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
314 gint max_tiles = MAX_TILES;
315 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_MAX_TILES, &max_tiles ) )
316 MAX_TILES = max_tiles;
319 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MIN_SHRINKFACTOR, &gdtmp ) )
320 MIN_SHRINKFACTOR = gdtmp;
322 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MAX_SHRINKFACTOR, &gdtmp ) )
323 MAX_SHRINKFACTOR = gdtmp;
325 if ( a_settings_get_double ( VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR, &gdtmp ) )
326 REAL_MIN_SHRINKFACTOR = gdtmp;
329 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_UP, &gitmp ) )
330 SCALE_INC_UP = gitmp;
332 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_DOWN, &gitmp ) )
333 SCALE_INC_DOWN = gitmp;
335 gboolean gbtmp = TRUE;
336 if ( a_settings_get_boolean ( VIK_SETTINGS_MAP_SCALE_SMALLER_ZOOM_FIRST, &gbtmp ) )
337 SCALE_SMALLER_ZOOM_FIRST = gbtmp;
341 /****************************************/
342 /******** MAPS LAYER TYPES **************/
343 /****************************************/
345 void _add_map_source ( guint16 id, const char *label, VikMapSource *map )
349 len = g_strv_length (params_maptypes);
351 params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
352 params_maptypes[len] = g_strdup (label);
353 params_maptypes[len+1] = NULL;
356 params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
357 params_maptypes_ids[len] = id;
358 params_maptypes_ids[len+1] = 0;
360 /* We have to clone */
361 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
362 /* Register the clone in the list */
363 __map_types = g_list_append(__map_types, clone);
366 We have to ensure the mode LayerParam references the up-to-date
370 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
371 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
373 maps_layer_params[0].widget_data = params_maptypes;
374 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
377 void _update_map_source ( const char *label, VikMapSource *map, int index )
379 GList *item = g_list_nth (__map_types, index);
380 g_object_unref (item->data);
381 item->data = g_object_ref (map);
382 /* Change previous data */
383 g_free (params_maptypes[index]);
384 params_maptypes[index] = g_strdup (label);
388 * maps_layer_register_map_source:
389 * @map: the new VikMapSource
391 * Register a new VikMapSource.
392 * Override existing one (equality of id).
394 void maps_layer_register_map_source ( VikMapSource *map )
396 g_assert(map != NULL);
398 guint16 id = vik_map_source_get_uniq_id(map);
399 const char *label = vik_map_source_get_label(map);
400 g_assert(label != NULL);
402 int previous = map_uniq_id_to_index (id);
403 if (previous != NUM_MAP_TYPES)
405 _update_map_source (label, map, previous);
409 _add_map_source (id, label, map);
413 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
414 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
415 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
418 * vik_maps_layer_get_map_type:
420 * Returns the actual map id (rather than the internal type index value)
422 guint vik_maps_layer_get_map_type(VikMapsLayer *vml)
424 return MAPS_LAYER_NTH_ID(vml->maptype);
428 * vik_maps_layer_set_map_type:
431 void vik_maps_layer_set_map_type(VikMapsLayer *vml, guint map_type)
433 gint maptype = map_uniq_id_to_index(map_type);
434 if ( maptype == NUM_MAP_TYPES )
435 g_warning(_("Unknown map type"));
437 vml->maptype = maptype;
441 * vik_maps_layer_get_default_map_type:
444 guint vik_maps_layer_get_default_map_type ()
446 VikLayerInterface *vli = vik_layer_get_interface ( VIK_LAYER_MAPS );
447 VikLayerParamData vlpd = a_layer_defaults_get ( vli->fixed_layer_name, "mode", VIK_LAYER_PARAM_UINT );
453 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
455 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
458 /****************************************/
459 /******** CACHE DIR STUFF ***************/
460 /****************************************/
462 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
463 #define DIRECTDIRACCESS_WITH_NAME "%s%s" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
464 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
465 #define MAPS_CACHE_DIR maps_layer_default_dir()
469 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
470 #define LOCAL_MAPS_DIR "VIKING-MAPS"
471 #elif defined __APPLE__
473 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
474 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
477 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
478 #define LOCAL_MAPS_DIR ".viking-maps"
481 gchar *maps_layer_default_dir ()
483 static gchar *defaultdir = NULL;
486 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
487 const gchar *mapdir = g_getenv("VIKING_MAPS");
489 defaultdir = g_strdup ( mapdir );
490 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
491 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
493 const gchar *home = g_get_home_dir();
494 if (!home || g_access(home, W_OK))
495 home = g_get_home_dir ();
497 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
499 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
501 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
503 /* Add the separator at the end */
504 gchar *tmp = defaultdir;
505 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
508 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
513 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
515 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
517 if ( g_mkdir ( vml->cache_dir, 0777 ) != 0 )
518 g_warning ( "%s: Failed to create directory %s", __FUNCTION__, vml->cache_dir );
522 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
524 g_assert ( vml != NULL);
525 g_free ( vml->cache_dir );
526 vml->cache_dir = NULL;
527 const gchar *mydir = dir;
529 if ( dir == NULL || dir[0] == '\0' )
531 if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
532 mydir = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s;
535 gchar *canonical_dir = vu_get_canonical_filename ( VIK_LAYER(vml), mydir );
537 // Ensure cache_dir always ends with a separator
538 guint len = strlen(canonical_dir);
539 // Unless the dir is not valid
542 if ( canonical_dir[len-1] != G_DIR_SEPARATOR )
544 vml->cache_dir = g_strconcat ( canonical_dir, G_DIR_SEPARATOR_S, NULL );
545 g_free ( canonical_dir );
548 vml->cache_dir = canonical_dir;
551 maps_layer_mkdir_if_default_dir ( vml );
555 static void maps_layer_set_file ( VikMapsLayer *vml, const gchar *name )
558 g_free (vml->filename);
559 vml->filename = g_strdup (name);
562 /****************************************/
563 /******** GOBJECT STUFF *****************/
564 /****************************************/
566 GType vik_maps_layer_get_type ()
568 static GType vml_type = 0;
572 static const GTypeInfo vml_info =
574 sizeof (VikMapsLayerClass),
575 NULL, /* base_init */
576 NULL, /* base_finalize */
577 NULL, /* class init */
578 NULL, /* class_finalize */
579 NULL, /* class_data */
580 sizeof (VikMapsLayer),
582 NULL /* instance init */
584 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
590 /****************************************/
591 /************** PARAMETERS **************/
592 /****************************************/
594 static guint map_index_to_uniq_id (guint16 index)
596 g_assert ( index < NUM_MAP_TYPES );
597 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
600 static guint map_uniq_id_to_index ( guint uniq_id )
603 for ( i = 0; i < NUM_MAP_TYPES; i++ )
604 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
606 return NUM_MAP_TYPES; /* no such thing */
609 #define VIK_SETTINGS_MAP_LICENSE_SHOWN "map_license_shown"
612 * Convenience function to display the license
614 static void maps_show_license ( GtkWindow *parent, VikMapSource *map )
616 a_dialog_license ( parent,
617 vik_map_source_get_label (map),
618 vik_map_source_get_license (map),
619 vik_map_source_get_license_url (map) );
622 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
626 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
627 case PARAM_CACHE_LAYOUT: if ( data.u < VIK_MAPS_CACHE_LAYOUT_NUM ) vml->cache_layout = data.u; break;
628 case PARAM_FILE: maps_layer_set_file ( vml, data.s ); break;
629 case PARAM_MAPTYPE: {
630 gint maptype = map_uniq_id_to_index(data.u);
631 if ( maptype == NUM_MAP_TYPES )
632 g_warning(_("Unknown map type"));
634 vml->maptype = maptype;
636 // When loading from a file don't need the license reminder - ensure it's saved into the 'seen' list
637 if ( is_file_operation ) {
638 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u );
641 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
642 if (vik_map_source_get_license (map) != NULL) {
643 // Check if licence for this map type has been shown before
644 if ( ! a_settings_get_integer_list_contains ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u ) ) {
646 maps_show_license ( VIK_GTK_WINDOW_FROM_WIDGET(vvp), map );
647 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u );
654 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
655 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
656 case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
657 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
658 vml->mapzoom_id = data.u;
659 vml->xmapzoom = __mapzooms_x [data.u];
660 vml->ymapzoom = __mapzooms_y [data.u];
661 }else g_warning (_("Unknown Map Zoom")); break;
667 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
669 VikLayerParamData rv;
672 case PARAM_CACHE_DIR:
674 gboolean set = FALSE;
675 /* Only save a blank when the map cache location equals the default
676 On reading in, when it is blank then the default is reconstructed
677 Since the default changes dependent on the user and OS, it means the resultant file is more portable */
678 if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
682 else if ( is_file_operation && vml->cache_dir ) {
683 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
684 gchar *cwd = g_get_current_dir();
686 rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
687 if ( !rv.s ) rv.s = "";
693 rv.s = vml->cache_dir ? vml->cache_dir : "";
696 case PARAM_CACHE_LAYOUT: rv.u = vml->cache_layout; break;
697 case PARAM_FILE: rv.s = vml->filename; break;
698 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
699 case PARAM_ALPHA: rv.u = vml->alpha; break;
700 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
701 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
702 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
708 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values )
710 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
711 // Alter sensitivity of download option widgets according to the maptype setting.
712 case PARAM_MAPTYPE: {
714 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
715 // Is it *not* the OSM On Disk Tile Layout or the MBTiles type or the OSM Metatiles type
716 gboolean sensitive = ( MAP_ID_OSM_ON_DISK != vlpd.u &&
717 MAP_ID_MBTILES != vlpd.u &&
718 MAP_ID_OSM_METATILES != vlpd.u );
719 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
720 GtkWidget **ww2 = values[UI_CHG_LABELS];
721 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
722 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
723 GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
724 GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
725 // Depends on autodownload value
726 gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
727 if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
728 if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
729 if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
730 if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
732 // Cache type not applicable either
733 GtkWidget *w9 = ww1[PARAM_CACHE_LAYOUT];
734 GtkWidget *w10 = ww2[PARAM_CACHE_LAYOUT];
735 if ( w9 ) gtk_widget_set_sensitive ( w9, sensitive );
736 if ( w10 ) gtk_widget_set_sensitive ( w10, sensitive );
738 // File only applicable for MBTiles type
739 // Directory for all other types
740 sensitive = ( MAP_ID_MBTILES == vlpd.u);
741 GtkWidget *w5 = ww1[PARAM_FILE];
742 GtkWidget *w6 = ww2[PARAM_FILE];
743 GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
744 GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
745 if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
746 if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
747 if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
748 if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
753 // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
754 case PARAM_AUTODOWNLOAD: {
756 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
757 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
758 GtkWidget **ww2 = values[UI_CHG_LABELS];
759 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
760 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
761 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
762 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
769 /****************************************/
770 /****** CREATING, COPYING, FREEING ******/
771 /****************************************/
773 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
775 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
776 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
778 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
780 vml->dl_tool_x = vml->dl_tool_y = -1;
781 vml->last_center = NULL;
782 vml->last_xmpp = 0.0;
783 vml->last_ympp = 0.0;
785 vml->dl_right_click_menu = NULL;
786 vml->filename = NULL;
790 static void maps_layer_free ( VikMapsLayer *vml )
792 g_free ( vml->cache_dir );
793 vml->cache_dir = NULL;
794 if ( vml->dl_right_click_menu )
795 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
796 g_free(vml->last_center);
797 vml->last_center = NULL;
798 g_free ( vml->filename );
799 vml->filename = NULL;
801 #ifdef HAVE_SQLITE3_H
802 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
803 if ( vik_map_source_is_mbtiles ( map ) ) {
804 if ( vml->mbtiles ) {
805 int ans = sqlite3_close ( vml->mbtiles );
806 if ( ans != SQLITE_OK ) {
807 // Only to console for information purposes only
808 g_warning ( "SQL Close problem: %d", ans );
815 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
817 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
818 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
822 /* If this method is not called in file reading context
823 * it is called in GUI context.
824 * So, we can check if we have to inform the user about inconsistency */
825 VikViewportDrawMode vp_drawmode;
826 vp_drawmode = vik_viewport_get_drawmode ( vp );
828 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
829 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
830 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
831 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
836 // Performed in post read as we now know the map type
837 #ifdef HAVE_SQLITE3_H
839 if ( vik_map_source_is_mbtiles ( map ) ) {
840 int ans = sqlite3_open_v2 ( vml->filename,
842 SQLITE_OPEN_READONLY,
844 if ( ans != SQLITE_OK ) {
845 // That didn't work, so here's why:
846 g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
848 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
849 _("Failed to open MBTiles file: %s"),
856 // If the on Disk OSM Tile Layout type
857 if ( vik_map_source_get_uniq_id(map) == MAP_ID_OSM_ON_DISK ) {
858 // Copy the directory into filename
859 // thus the mapcache look up will be unique when using more than one of these map types
860 g_free ( vml->filename );
861 vml->filename = g_strdup (vml->cache_dir);
865 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
867 return vik_maps_layer_get_map_label ( vml );
870 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
872 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
875 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
877 VikMapsLayer *rv = maps_layer_new ( vvp );
878 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
879 maps_layer_post_read ( VIK_LAYER(rv), vvp, FALSE );
883 /*********************/
884 /****** DRAWING ******/
885 /*********************/
887 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
890 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
891 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
892 g_object_unref ( G_OBJECT(pixbuf) );
896 #ifdef HAVE_SQLITE3_H
898 static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
900 g_warning ( "Found %d columns", cols );
902 for ( i = 0; i < cols; i++ ) {
903 g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
912 static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
914 GdkPixbuf *pixbuf = NULL;
916 // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
917 gint flip_y = (gint) pow(2, zoom)-1 - yy;
918 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 );
920 gboolean finished = FALSE;
922 sqlite3_stmt *sql_stmt = NULL;
923 int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
924 if ( ans != SQLITE_OK ) {
925 g_warning ( "%s: %s - %d", __FUNCTION__, "prepare failure", ans );
929 while ( !finished ) {
930 ans = sqlite3_step ( sql_stmt );
933 // Get tile_data blob
934 int count = sqlite3_column_count(sql_stmt);
936 g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
940 const void *data = sqlite3_column_blob ( sql_stmt, 0 );
941 int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
943 g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
947 // Convert these blob bytes into a pixbuf via these streaming operations
948 GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
949 GError *error = NULL;
950 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
952 g_warning ( "%s: %s", __FUNCTION__, error->message );
953 g_error_free ( error );
955 g_input_stream_close ( stream, NULL, NULL );
961 // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
963 // and give up on any errors
964 if ( ans != SQLITE_DONE )
965 g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
970 (void)sqlite3_finalize ( sql_stmt );
972 g_free ( statement );
978 static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
980 GdkPixbuf *pixbuf = NULL;
982 #ifdef HAVE_SQLITE3_H
983 if ( vml->mbtiles ) {
985 gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
987 int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
988 if ( ans != SQLITE_OK ) {
989 // Only to console for information purposes only
990 g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
991 sqlite3_free( errMsg );
993 g_free ( statement );
996 // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
997 // Hence this specific function
998 pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
1005 static GdkPixbuf *get_pixbuf_from_metatile ( VikMapsLayer *vml, gint xx, gint yy, gint zz )
1007 const int tile_max = METATILE_MAX_SIZE;
1008 char err_msg[PATH_MAX];
1013 buf = g_malloc(tile_max);
1019 len = metatile_read(vml->cache_dir, xx, yy, zz, buf, tile_max, &compressed, err_msg);
1023 // Not handled yet - I don't think this is used often - so implement later if necessary
1024 g_warning ( "Compressed metatiles not implemented:%s\n", __FUNCTION__);
1029 // Convert these buf bytes into a pixbuf via these streaming operations
1030 GdkPixbuf *pixbuf = NULL;
1032 GInputStream *stream = g_memory_input_stream_new_from_data ( buf, len, NULL );
1033 GError *error = NULL;
1034 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
1036 g_warning ( "%s: %s", __FUNCTION__, error->message );
1037 g_error_free ( error );
1039 g_input_stream_close ( stream, NULL, NULL );
1046 g_warning ( "FAILED:%s %s", __FUNCTION__, err_msg);
1052 * Caller has to decrease reference counter of returned
1053 * GdkPixbuf, when buffer is no longer needed.
1055 static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
1057 // Apply alpha setting
1058 if ( pixbuf && vml->alpha < 255 )
1059 pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
1061 if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
1062 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
1065 a_mapcache_add ( pixbuf, (mapcache_extra_t) {0.0}, mapcoord->x, mapcoord->y,
1066 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
1067 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1072 static void get_filename ( const gchar *cache_dir,
1073 VikMapsCacheLayout cl,
1080 gchar *filename_buf,
1082 const gchar* file_extension )
1085 case VIK_MAPS_CACHE_LAYOUT_OSM:
1087 if ( g_strcmp0 ( cache_dir, MAPS_CACHE_DIR ) )
1088 // Cache dir not the default - assume it's been directed somewhere specific
1089 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1091 // Using default cache - so use the map name in the directory path
1092 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS_WITH_NAME, cache_dir, name, (17 - scale), x, y, file_extension );
1095 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1098 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE, cache_dir, id, scale, z, x, y );
1104 * Caller has to decrease reference counter of returned
1105 * GdkPixbuf, when buffer is no longer needed.
1107 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, guint16 id, const gchar* mapname, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
1112 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
1113 id, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1116 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1117 if ( vik_map_source_is_direct_file_access(map) ) {
1118 // ATM MBTiles must be 'a direct access type'
1119 if ( vik_map_source_is_mbtiles(map) ) {
1120 pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1121 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1122 // return now to avoid file tests that aren't appropriate for this map type
1125 else if ( vik_map_source_is_osm_meta_tiles(map) ) {
1126 pixbuf = get_pixbuf_from_metatile ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1127 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1131 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, NULL,
1132 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1133 vik_map_source_get_file_extension(map) );
1136 get_filename ( vml->cache_dir, vml->cache_layout, id, mapname,
1137 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1138 vik_map_source_get_file_extension(map) );
1140 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1143 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
1145 /* free the pixbuf on error */
1148 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE ) {
1150 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1151 gchar* msg = g_strdup_printf ( _("Couldn't open image file: %s"), gx->message );
1152 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1157 g_error_free ( gx );
1159 g_object_unref ( G_OBJECT(pixbuf) );
1162 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1169 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
1171 const VikCoord *center = vik_viewport_get_center ( vvp );
1173 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
1174 /* D'n'D pan in action: do not download */
1177 // Don't attempt to download unsupported zoom levels
1178 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1179 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1180 guint8 zl = map_utils_mpp_to_zoom_level ( xzoom );
1181 if ( zl < vik_map_source_get_zoom_min(map) || zl > vik_map_source_get_zoom_max(map) )
1184 if (vml->last_center == NULL) {
1185 VikCoord *new_center = g_malloc(sizeof(VikCoord));
1186 *new_center = *center;
1187 vml->last_center = new_center;
1188 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1189 vml->last_ympp = vik_viewport_get_ympp(vvp);
1193 /* TODO: perhaps vik_coord_diff() */
1194 if (vik_coord_equals(vml->last_center, center)
1195 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1196 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1199 *(vml->last_center) = *center;
1200 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1201 vml->last_ympp = vik_viewport_get_ympp(vvp);
1208 gboolean try_draw_scale_down (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1209 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1213 for (scale_inc = 1; scale_inc < SCALE_INC_DOWN; scale_inc ++) {
1214 // Try with smaller zooms
1215 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
1216 MapCoord ulm2 = ulm;
1217 ulm2.x = ulm.x / scale_factor;
1218 ulm2.y = ulm.y / scale_factor;
1219 ulm2.scale = ulm.scale + scale_inc;
1220 pixbuf = get_pixbuf ( vml, id, mapname, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1222 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1223 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1224 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1225 g_object_unref(pixbuf);
1235 gboolean try_draw_scale_up (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1236 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1239 // Try with bigger zooms
1241 for (scale_dec = 1; scale_dec < SCALE_INC_UP; scale_dec ++) {
1243 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
1244 MapCoord ulm2 = ulm;
1245 ulm2.x = ulm.x * scale_factor;
1246 ulm2.y = ulm.y * scale_factor;
1247 ulm2.scale = ulm.scale - scale_dec;
1248 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1249 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1250 MapCoord ulm3 = ulm2;
1253 pixbuf = get_pixbuf ( vml, id, mapname, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1257 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1258 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1259 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1260 g_object_unref(pixbuf);
1269 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1272 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1273 gdouble yzoom = vik_viewport_get_ympp ( vvp );
1274 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
1275 gboolean existence_only = FALSE;
1277 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1278 xshrinkfactor = vml->xmapzoom / xzoom;
1279 yshrinkfactor = vml->ymapzoom / yzoom;
1280 xzoom = vml->xmapzoom;
1281 yzoom = vml->xmapzoom;
1282 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1283 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
1284 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1285 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
1286 existence_only = TRUE;
1289 // Report the reason for not drawing
1290 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1291 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));
1292 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1301 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1302 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1303 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
1307 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1308 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
1309 guint16 id = vik_map_source_get_uniq_id(map);
1310 const gchar *mapname = vik_map_source_get_name(map);
1313 gint xx, yy, width, height;
1316 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1317 // which can happen when using a small fixed zoom level and viewing large areas.
1318 // Also prevents very large number of tile download requests
1319 gint tiles = (xmax-xmin) * (ymax-ymin);
1320 if ( tiles > MAX_TILES ) {
1321 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1322 existence_only = TRUE;
1325 guint max_path_len = strlen(vml->cache_dir) + 40;
1326 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1328 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
1329 g_debug("%s: Starting autodownload", __FUNCTION__);
1330 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
1331 // Try to download newer tiles
1332 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1334 // Download only missing tiles
1335 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
1338 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
1339 for ( x = xmin; x <= xmax; x++ ) {
1340 for ( y = ymin; y <= ymax; y++ ) {
1343 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1345 width = gdk_pixbuf_get_width ( pixbuf );
1346 height = gdk_pixbuf_get_height ( pixbuf );
1348 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1349 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1353 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1354 g_object_unref(pixbuf);
1358 } else { /* tilesize is known, don't have to keep converting coords */
1359 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1360 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
1361 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1362 gint tilesize_x_ceil = ceil ( tilesize_x );
1363 gint tilesize_y_ceil = ceil ( tilesize_y );
1364 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1365 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
1366 gint xx_tmp, yy_tmp;
1367 gint base_yy, xend, yend;
1369 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1370 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1372 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1373 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1374 xx = xx_tmp; yy = yy_tmp;
1375 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1376 * eg if tile size 128, shrinkfactor 0.333 */
1377 xx -= (tilesize_x/2);
1378 base_yy = yy - (tilesize_y/2);
1380 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1382 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1386 if ( existence_only ) {
1387 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1388 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, vik_map_source_get_name(map),
1389 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1391 get_filename ( vml->cache_dir, vml->cache_layout, id, vik_map_source_get_name(map),
1392 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1394 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1395 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1396 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1399 // Try correct scale first
1400 int scale_factor = 1;
1401 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1403 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1404 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1405 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1406 g_object_unref(pixbuf);
1409 // Otherwise try different scales
1410 if ( SCALE_SMALLER_ZOOM_FIRST ) {
1411 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) ) {
1412 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 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) ) {
1417 try_draw_scale_down(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1428 // ATM Only show tile grid lines in extreme debug mode
1429 if ( vik_debug && vik_verbose ) {
1430 /* Grid drawing here so it gets drawn on top of the map */
1431 /* Thus loop around x & y again, but this time separately */
1432 /* Only showing grid for the current scale */
1433 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
1434 /* Draw single grid lines across the whole screen */
1435 gint width = vik_viewport_get_width(vvp);
1436 gint height = vik_viewport_get_height(vvp);
1437 xx = xx_tmp; yy = yy_tmp;
1438 gint base_xx = xx - (tilesize_x/2);
1439 base_yy = yy - (tilesize_y/2);
1442 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1443 vik_viewport_draw_line ( vvp, black_gc, xx, base_yy, xx, height );
1448 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1449 vik_viewport_draw_line ( vvp, black_gc, base_xx, yy, width, yy );
1455 g_free ( path_buf );
1459 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1461 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
1466 gdouble level = vik_viewport_get_zoom ( vvp );
1468 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1469 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
1472 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1473 vik_viewport_add_logo ( vvp, logo );
1475 /* get corner coords */
1476 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1477 /* UTM multi-zone stuff by Kit Transue */
1478 gchar leftmost_zone, rightmost_zone, i;
1479 leftmost_zone = vik_viewport_leftmost_zone( vvp );
1480 rightmost_zone = vik_viewport_rightmost_zone( vvp );
1481 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1482 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1483 maps_layer_draw_section ( vml, vvp, &ul, &br );
1487 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1488 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1490 maps_layer_draw_section ( vml, vvp, &ul, &br );
1495 /*************************/
1496 /****** DOWNLOADING ******/
1497 /*************************/
1499 /* pass along data to thread, exists even if layer is deleted. */
1502 gchar *filename_buf;
1503 VikMapsCacheLayout cache_layout;
1504 gint x0, y0, xf, yf;
1510 gboolean refresh_display;
1513 gboolean map_layer_alive;
1517 static void mdi_free ( MapDownloadInfo *mdi )
1519 vik_mutex_free(mdi->mutex);
1520 g_free ( mdi->cache_dir );
1521 mdi->cache_dir = NULL;
1522 g_free ( mdi->filename_buf );
1523 mdi->filename_buf = NULL;
1527 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1529 MapDownloadInfo *mdi = ptr;
1530 g_mutex_lock(mdi->mutex);
1531 mdi->map_layer_alive = FALSE;
1532 g_mutex_unlock(mdi->mutex);
1535 static gboolean is_in_area (VikMapSource *map, MapCoord mc)
1538 vik_map_source_mapcoord_to_center_coord ( map, &mc, &vc );
1541 tl.lat = vik_map_source_get_lat_max(map);
1542 tl.lon = vik_map_source_get_lon_min(map);
1544 br.lat = vik_map_source_get_lat_min(map);
1545 br.lon = vik_map_source_get_lon_max(map);
1547 vik_coord_load_from_latlon (&vctl, VIK_COORD_LATLON, &tl);
1549 vik_coord_load_from_latlon (&vcbr, VIK_COORD_LATLON, &br);
1551 return vik_coord_inside ( &vc, &vctl, &vcbr );
1554 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1556 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1558 MapCoord mcoord = mdi->mapcoord;
1560 for ( x = mdi->x0; x <= mdi->xf; x++ )
1563 for ( y = mdi->y0; y <= mdi->yf; y++ )
1566 // Only attempt to download a tile from supported areas
1567 if ( is_in_area ( MAPS_LAYER_NTH_TYPE(mdi->maptype), mcoord ) )
1569 gboolean remove_mem_cache = FALSE;
1570 gboolean need_download = FALSE;
1572 get_filename ( mdi->cache_dir, mdi->cache_layout,
1573 vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1574 vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1575 mdi->mapcoord.scale, mdi->mapcoord.z, x, y, mdi->filename_buf, mdi->maxlen,
1576 vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1579 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1581 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1585 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1586 need_download = TRUE;
1587 remove_mem_cache = TRUE;
1589 } else { /* in case map file already exists */
1590 switch (mdi->redownload) {
1591 case REDOWNLOAD_NONE:
1594 case REDOWNLOAD_BAD:
1596 /* see if this one is bad or what */
1598 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1599 if (gx || (!pixbuf)) {
1600 if ( g_remove ( mdi->filename_buf ) )
1601 g_warning ( "REDOWNLOAD failed to remove: %s", mdi->filename_buf );
1602 need_download = TRUE;
1603 remove_mem_cache = TRUE;
1604 g_error_free ( gx );
1607 g_object_unref ( pixbuf );
1612 case REDOWNLOAD_NEW:
1613 need_download = TRUE;
1614 remove_mem_cache = TRUE;
1617 case REDOWNLOAD_ALL:
1618 /* FIXME: need a better way than to erase file in case of server/network problem */
1619 if ( g_remove ( mdi->filename_buf ) )
1620 g_warning ( "REDOWNLOAD failed to remove: %s", mdi->filename_buf );
1621 need_download = TRUE;
1622 remove_mem_cache = TRUE;
1625 case DOWNLOAD_OR_REFRESH:
1626 remove_mem_cache = TRUE;
1630 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1634 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1636 if (need_download) {
1637 DownloadResult_t dr = vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle);
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 );
1917 #if !GLIB_CHECK_VERSION(2,26,0)
1918 typedef struct stat GStatBuf;
1922 * Display a simple dialog with information about this particular map tile
1924 static void maps_layer_tile_info ( VikMapsLayer *vml )
1926 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1928 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1929 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1932 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1935 gchar *filename = NULL;
1936 gchar *source = NULL;
1938 if ( vik_map_source_is_direct_file_access ( map ) ) {
1939 if ( vik_map_source_is_mbtiles ( map ) ) {
1940 filename = g_strdup ( vml->filename );
1941 #ifdef HAVE_SQLITE3_H
1942 // And whether to bother going into the SQL to check it's really there or not...
1943 gchar *exists = NULL;
1944 gint zoom = 17 - ulm.scale;
1945 if ( vml->mbtiles ) {
1946 GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, zoom );
1948 exists = g_strdup ( _("YES") );
1949 g_object_unref ( G_OBJECT(pixbuf) );
1952 exists = g_strdup ( _("NO") );
1956 exists = g_strdup ( _("NO") );
1957 gint flip_y = (gint) pow(2, zoom)-1 - ulm.y;
1958 // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1959 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 );
1962 source = g_strdup ( _("Source: Not available") );
1965 else if ( vik_map_source_is_osm_meta_tiles ( map ) ) {
1966 char path[PATH_MAX];
1967 xyz_to_meta(path, sizeof(path), vml->cache_dir, ulm.x, ulm.y, 17-ulm.scale );
1968 source = g_strdup ( path );
1969 filename = g_strdup ( path );
1972 guint max_path_len = strlen(vml->cache_dir) + 40;
1973 filename = g_malloc ( max_path_len * sizeof(char) );
1974 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM,
1975 vik_map_source_get_uniq_id(map),
1977 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1978 vik_map_source_get_file_extension(map) );
1979 source = g_strconcat ( "Source: file://", filename, NULL );
1983 guint max_path_len = strlen(vml->cache_dir) + 40;
1984 filename = g_malloc ( max_path_len * sizeof(char) );
1985 get_filename ( vml->cache_dir, vml->cache_layout,
1986 vik_map_source_get_uniq_id(map),
1987 vik_map_source_get_name(map),
1988 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1989 vik_map_source_get_file_extension(map) );
1990 source = g_markup_printf_escaped ( "Source: http://%s%s",
1991 vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1992 vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1995 GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
1996 g_array_append_val ( array, source );
1998 gchar *filemsg = NULL;
1999 gchar *timemsg = NULL;
2001 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
2002 filemsg = g_strconcat ( "Tile File: ", filename, NULL );
2003 // Get some timestamp information of the tile
2005 if ( g_stat ( filename, &stat_buf ) == 0 ) {
2007 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
2008 timemsg = g_strdup_printf ( _("Tile File Timestamp: %s"), time_buf );
2011 timemsg = g_strdup ( _("Tile File Timestamp: Not Available") );
2013 g_array_append_val ( array, filemsg );
2014 g_array_append_val ( array, timemsg );
2017 filemsg = g_strdup_printf ( "Tile File: %s [Not Available]", filename );
2018 g_array_append_val ( array, filemsg );
2021 a_dialog_list ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Tile Information"), array, 5 );
2022 g_array_free ( array, FALSE );
2027 g_free ( filename );
2030 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2032 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2034 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
2036 if ( event->button == 1 )
2039 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 );
2040 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 );
2041 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
2042 vml->dl_tool_x = vml->dl_tool_y = -1;
2047 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) );
2048 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) );
2050 vml->redownload_vvp = vvp;
2052 vml->dl_tool_x = vml->dl_tool_y = -1;
2054 if ( ! vml->dl_right_click_menu ) {
2056 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
2058 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
2059 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
2060 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2062 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
2063 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
2064 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2066 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
2067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
2068 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2070 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
2071 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
2072 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
2073 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
2076 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
2077 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
2083 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
2088 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2091 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2093 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2094 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
2095 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
2096 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
2097 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
2099 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
2105 // A slightly better way of defining the menu callback information
2106 // This should be easier to extend/rework compared to previously
2113 typedef gpointer menu_array_values[MA_LAST];
2115 static void download_onscreen_maps ( menu_array_values values, gint redownload )
2117 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2118 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2119 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
2121 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
2122 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
2127 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
2128 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
2130 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2131 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
2132 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
2133 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
2134 start_download_thread ( vml, vvp, &ul, &br, redownload );
2135 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
2136 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
2137 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
2138 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
2142 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
2146 static void maps_layer_download_missing_onscreen_maps ( menu_array_values values )
2148 download_onscreen_maps( values, REDOWNLOAD_NONE);
2151 static void maps_layer_download_new_onscreen_maps ( menu_array_values values )
2153 download_onscreen_maps( values, REDOWNLOAD_NEW);
2156 static void maps_layer_redownload_all_onscreen_maps ( menu_array_values values )
2158 download_onscreen_maps( values, REDOWNLOAD_ALL);
2161 static void maps_layer_about ( gpointer vml_vvp[2] )
2163 VikMapsLayer *vml = vml_vvp[0];
2164 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2166 if ( vik_map_source_get_license (map) )
2167 maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
2169 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2170 vik_map_source_get_label (map) );
2174 * maps_layer_how_many_maps:
2175 * Copied from maps_layer_download_section but without the actual download and this returns a value
2177 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
2180 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2182 if ( vik_map_source_is_direct_file_access ( map ) )
2185 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
2186 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
2187 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
2191 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
2196 mdi->map_layer_alive = TRUE;
2197 mdi->mutex = vik_mutex_new();
2198 mdi->refresh_display = FALSE;
2200 mdi->cache_dir = g_strdup ( vml->cache_dir );
2201 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
2202 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
2203 mdi->maptype = vml->maptype;
2204 mdi->cache_layout = vml->cache_layout;
2206 mdi->mapcoord = ulm;
2207 mdi->redownload = redownload;
2209 mdi->x0 = MIN(ulm.x, brm.x);
2210 mdi->xf = MAX(ulm.x, brm.x);
2211 mdi->y0 = MIN(ulm.y, brm.y);
2212 mdi->yf = MAX(ulm.y, brm.y);
2216 if ( mdi->redownload == REDOWNLOAD_ALL ) {
2217 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
2220 /* calculate how many we need */
2221 MapCoord mcoord = mdi->mapcoord;
2222 for (i = mdi->x0; i <= mdi->xf; i++) {
2224 for (j = mdi->y0; j <= mdi->yf; j++) {
2226 // Only count tiles from supported areas
2227 if ( is_in_area ( map, mcoord ) ) {
2228 get_filename ( mdi->cache_dir, mdi->cache_layout,
2229 vik_map_source_get_uniq_id(map),
2230 vik_map_source_get_name(map),
2231 ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
2232 vik_map_source_get_file_extension(map) );
2233 if ( mdi->redownload == REDOWNLOAD_NEW ) {
2234 // Assume the worst - always a new file
2235 // Absolute value would require a server lookup - but that is too slow
2239 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
2244 if ( mdi->redownload == REDOWNLOAD_BAD ) {
2245 /* see if this one is bad or what */
2247 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
2248 if (gx || (!pixbuf)) {
2252 // Other download cases already considered or just ignored
2261 gint rv = mdi->mapstoget;
2269 * maps_dialog_zoom_between:
2270 * This dialog is specific to the map layer, so it's here rather than in dialog.c
2272 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
2277 gint *selected_zoom1,
2278 gint *selected_zoom2,
2279 gchar *download_list[],
2280 gint default_download,
2281 gint *selected_download )
2283 GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
2285 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2286 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2287 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2289 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2290 GtkWidget *response_w = NULL;
2291 #if GTK_CHECK_VERSION (2, 20, 0)
2292 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2294 GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
2295 GtkWidget *zoom_combo1 = vik_combo_box_text_new();
2297 for (s = zoom_list; *s; s++)
2298 vik_combo_box_text_append ( zoom_combo1, *s );
2299 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
2301 GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
2302 GtkWidget *zoom_combo2 = vik_combo_box_text_new();
2303 for (s = zoom_list; *s; s++)
2304 vik_combo_box_text_append ( zoom_combo2, *s );
2305 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
2307 GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
2308 GtkWidget *download_combo = vik_combo_box_text_new();
2309 for (s = download_list; *s; s++)
2310 vik_combo_box_text_append ( download_combo, *s );
2311 gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
2313 GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
2314 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
2315 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
2316 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
2317 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
2318 gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
2319 gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
2321 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
2324 gtk_widget_grab_focus ( response_w );
2326 gtk_widget_show_all ( dialog );
2327 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2328 gtk_widget_destroy(dialog);
2332 // Return selected options
2333 *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
2334 *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
2335 *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
2337 gtk_widget_destroy(dialog);
2341 // My best guess of sensible limits
2342 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
2343 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2346 * Get all maps in the region for zoom levels specified by the user
2347 * Sort of similar to trw_layer_download_map_along_track_cb function
2349 static void maps_layer_download_all ( menu_array_values values )
2351 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2352 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2354 // I don't think we should allow users to hammer the servers too much...
2355 // Delibrately not allowing lowest zoom levels
2356 // Still can give massive numbers to download
2357 // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2358 gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2359 gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2361 gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2362 gint selected_download_method;
2364 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2366 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2367 if (cur_zoom == zoom_vals[default_zoom])
2370 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2372 // Default to only 2 zoom levels below the current one
2373 if (default_zoom > 1 )
2374 lower_zoom = default_zoom - 2;
2376 lower_zoom = default_zoom;
2378 // redownload method - needs to align with REDOWNLOAD* macro values
2379 gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2381 gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2383 if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2391 REDOWNLOAD_NONE, // AKA Missing
2392 &selected_download_method ) ) {
2399 // Find out new current positions
2400 gdouble min_lat, max_lat, min_lon, max_lon;
2401 VikCoord vc_ul, vc_br;
2402 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2403 struct LatLon ll_ul = { max_lat, min_lon };
2404 struct LatLon ll_br = { min_lat, max_lon };
2405 vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2406 vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2408 // Get Maps Count - call for each zoom level (in reverse)
2409 // With REDOWNLOAD_NEW this is a possible maximum
2410 // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2413 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2414 map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2417 g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2419 // Absolute protection of hammering a map server
2420 if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2421 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);
2422 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2427 // Confirm really want to do this
2428 if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2429 gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2430 gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2436 // Get Maps - call for each zoom level (in reverse)
2437 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2438 maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2445 static void maps_layer_flush ( menu_array_values values )
2447 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2448 a_mapcache_flush_type ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)) );
2451 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2454 static menu_array_values values;
2455 values[MA_VML] = vml;
2456 values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2458 item = gtk_menu_item_new();
2459 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2460 gtk_widget_show ( item );
2462 /* Now with icons */
2463 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2464 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2465 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), values );
2466 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2467 gtk_widget_show ( item );
2469 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2470 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2471 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2472 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), values );
2473 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2474 gtk_widget_show ( item );
2477 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2478 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2479 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), values );
2480 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2481 gtk_widget_show ( item );
2483 item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2484 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2485 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), values );
2486 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2487 gtk_widget_show ( item );
2489 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2490 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_about), values );
2491 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2492 gtk_widget_show ( item );
2494 // Typical users shouldn't need to use this functionality - so debug only ATM
2496 item = gtk_image_menu_item_new_with_mnemonic ( _("Flush Map Cache") );
2497 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2498 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_flush), values );
2499 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2500 gtk_widget_show ( item );
2505 * Enable downloading maps of the current screen area either 'new' or 'everything'
2507 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2512 static menu_array_values values;
2513 values[MA_VML] = vml;
2514 values[MA_VVP] = vvp;
2517 // Get only new maps
2518 maps_layer_download_new_onscreen_maps ( values );
2520 // Redownload everything
2521 maps_layer_redownload_all_onscreen_maps ( values );