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 ) {
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 && MAP_ID_OSM_ON_DISK != vlpd.u && MAP_ID_OSM_ON_DISK != vlpd.u );
717 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
718 GtkWidget **ww2 = values[UI_CHG_LABELS];
719 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
720 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
721 GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
722 GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
723 // Depends on autodownload value
724 gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
725 if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
726 if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
727 if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
728 if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
730 // Cache type not applicable either
731 GtkWidget *w9 = ww1[PARAM_CACHE_LAYOUT];
732 GtkWidget *w10 = ww2[PARAM_CACHE_LAYOUT];
733 if ( w9 ) gtk_widget_set_sensitive ( w9, sensitive );
734 if ( w10 ) gtk_widget_set_sensitive ( w10, sensitive );
736 // File only applicable for MBTiles type
737 // Directory for all other types
738 sensitive = ( MAP_ID_MBTILES == vlpd.u);
739 GtkWidget *w5 = ww1[PARAM_FILE];
740 GtkWidget *w6 = ww2[PARAM_FILE];
741 GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
742 GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
743 if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
744 if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
745 if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
746 if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
751 // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
752 case PARAM_AUTODOWNLOAD: {
754 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
755 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
756 GtkWidget **ww2 = values[UI_CHG_LABELS];
757 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
758 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
759 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
760 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
767 /****************************************/
768 /****** CREATING, COPYING, FREEING ******/
769 /****************************************/
771 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
773 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
774 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
776 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
778 vml->dl_tool_x = vml->dl_tool_y = -1;
779 vml->last_center = NULL;
780 vml->last_xmpp = 0.0;
781 vml->last_ympp = 0.0;
783 vml->dl_right_click_menu = NULL;
784 vml->filename = NULL;
788 static void maps_layer_free ( VikMapsLayer *vml )
790 g_free ( vml->cache_dir );
791 vml->cache_dir = NULL;
792 if ( vml->dl_right_click_menu )
793 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
794 g_free(vml->last_center);
795 vml->last_center = NULL;
796 g_free ( vml->filename );
797 vml->filename = NULL;
799 #ifdef HAVE_SQLITE3_H
800 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
801 if ( vik_map_source_is_mbtiles ( map ) ) {
802 if ( vml->mbtiles ) {
803 int ans = sqlite3_close ( vml->mbtiles );
804 if ( ans != SQLITE_OK ) {
805 // Only to console for information purposes only
806 g_warning ( "SQL Close problem: %d", ans );
813 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
815 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
816 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
820 /* If this method is not called in file reading context
821 * it is called in GUI context.
822 * So, we can check if we have to inform the user about inconsistency */
823 VikViewportDrawMode vp_drawmode;
824 vp_drawmode = vik_viewport_get_drawmode ( vp );
826 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
827 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
828 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
829 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
834 // Performed in post read as we now know the map type
835 #ifdef HAVE_SQLITE3_H
837 if ( vik_map_source_is_mbtiles ( map ) ) {
838 int ans = sqlite3_open_v2 ( vml->filename,
840 SQLITE_OPEN_READONLY,
842 if ( ans != SQLITE_OK ) {
843 // That didn't work, so here's why:
844 g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
846 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
847 _("Failed to open MBTiles file: %s"),
854 // If the on Disk OSM Tile Layout type
855 if ( vik_map_source_get_uniq_id(map) == MAP_ID_OSM_ON_DISK ) {
856 // Copy the directory into filename
857 // thus the mapcache look up will be unique when using more than one of these map types
858 g_free ( vml->filename );
859 vml->filename = g_strdup (vml->cache_dir);
863 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
865 return vik_maps_layer_get_map_label ( vml );
868 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
870 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
873 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
875 VikMapsLayer *rv = maps_layer_new ( vvp );
876 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
877 maps_layer_post_read ( VIK_LAYER(rv), vvp, FALSE );
881 /*********************/
882 /****** DRAWING ******/
883 /*********************/
885 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
888 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
889 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
890 g_object_unref ( G_OBJECT(pixbuf) );
894 #ifdef HAVE_SQLITE3_H
896 static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
898 g_warning ( "Found %d columns", cols );
900 for ( i = 0; i < cols; i++ ) {
901 g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
910 static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
912 GdkPixbuf *pixbuf = NULL;
914 // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
915 gint flip_y = (gint) pow(2, zoom)-1 - yy;
916 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 );
918 gboolean finished = FALSE;
920 sqlite3_stmt *sql_stmt = NULL;
921 int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
922 if ( ans != SQLITE_OK ) {
923 g_warning ( "%s: %s - %d", __FUNCTION__, "prepare failure", ans );
927 while ( !finished ) {
928 ans = sqlite3_step ( sql_stmt );
931 // Get tile_data blob
932 int count = sqlite3_column_count(sql_stmt);
934 g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
938 const void *data = sqlite3_column_blob ( sql_stmt, 0 );
939 int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
941 g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
945 // Convert these blob bytes into a pixbuf via these streaming operations
946 GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
947 GError *error = NULL;
948 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
949 if (error || (!pixbuf)) {
950 g_warning ( "%s: %s", __FUNCTION__, error->message );
951 g_error_free ( error );
953 g_input_stream_close ( stream, NULL, NULL );
959 // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
961 // and give up on any errors
962 if ( ans != SQLITE_DONE )
963 g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
968 ans = sqlite3_finalize ( sql_stmt );
970 g_free ( statement );
976 static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
978 GdkPixbuf *pixbuf = NULL;
980 #ifdef HAVE_SQLITE3_H
981 if ( vml->mbtiles ) {
983 gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
985 int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
986 if ( ans != SQLITE_OK ) {
987 // Only to console for information purposes only
988 g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
989 sqlite3_free( errMsg );
991 g_free ( statement );
994 // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
995 // Hence this specific function
996 pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
1003 static GdkPixbuf *get_pixbuf_from_metatile ( VikMapsLayer *vml, gint xx, gint yy, gint zz )
1005 const int tile_max = METATILE_MAX_SIZE;
1006 char err_msg[PATH_MAX];
1011 buf = g_malloc(tile_max);
1017 len = metatile_read(vml->cache_dir, xx, yy, zz, buf, tile_max, &compressed, err_msg);
1021 // Not handled yet - I don't think this is used often - so implement later if necessary
1022 g_warning ( "Compressed metatiles not implemented:%s\n", __FUNCTION__);
1027 // Convert these buf bytes into a pixbuf via these streaming operations
1028 GdkPixbuf *pixbuf = NULL;
1030 GInputStream *stream = g_memory_input_stream_new_from_data ( buf, len, NULL );
1031 GError *error = NULL;
1032 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
1034 g_warning ( "%s: %s", __FUNCTION__, error->message );
1035 g_error_free ( error );
1037 g_input_stream_close ( stream, NULL, NULL );
1044 g_warning ( "FAILED:%s %s", __FUNCTION__, err_msg);
1050 * Caller has to decrease reference counter of returned
1051 * GdkPixbuf, when buffer is no longer needed.
1053 static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
1055 // Apply alpha setting
1056 if ( pixbuf && vml->alpha < 255 )
1057 pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
1059 if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
1060 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
1063 a_mapcache_add ( pixbuf, (mapcache_extra_t) {0.0}, mapcoord->x, mapcoord->y,
1064 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
1065 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1070 static void get_filename ( const gchar *cache_dir,
1071 VikMapsCacheLayout cl,
1078 gchar *filename_buf,
1080 const gchar* file_extension )
1083 case VIK_MAPS_CACHE_LAYOUT_OSM:
1085 if ( g_strcmp0 ( cache_dir, MAPS_CACHE_DIR ) )
1086 // Cache dir not the default - assume it's been directed somewhere specific
1087 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1089 // Using default cache - so use the map name in the directory path
1090 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS_WITH_NAME, cache_dir, name, (17 - scale), x, y, file_extension );
1093 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1096 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE, cache_dir, id, scale, z, x, y );
1102 * Caller has to decrease reference counter of returned
1103 * GdkPixbuf, when buffer is no longer needed.
1105 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, guint16 id, const gchar* mapname, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
1110 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
1111 id, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1114 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1115 if ( vik_map_source_is_direct_file_access(map) ) {
1116 // ATM MBTiles must be 'a direct access type'
1117 if ( vik_map_source_is_mbtiles(map) ) {
1118 pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1119 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1120 // return now to avoid file tests that aren't appropriate for this map type
1123 else if ( vik_map_source_is_osm_meta_tiles(map) ) {
1124 pixbuf = get_pixbuf_from_metatile ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1125 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1129 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, NULL,
1130 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1131 vik_map_source_get_file_extension(map) );
1134 get_filename ( vml->cache_dir, vml->cache_layout, id, mapname,
1135 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1136 vik_map_source_get_file_extension(map) );
1138 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1141 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
1143 /* free the pixbuf on error */
1146 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE ) {
1148 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1149 gchar* msg = g_strdup_printf ( _("Couldn't open image file: %s"), gx->message );
1150 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1155 g_error_free ( gx );
1157 g_object_unref ( G_OBJECT(pixbuf) );
1160 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1167 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
1169 const VikCoord *center = vik_viewport_get_center ( vvp );
1171 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
1172 /* D'n'D pan in action: do not download */
1175 // Don't attempt to download unsupported zoom levels
1176 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1177 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1178 guint8 zl = map_utils_mpp_to_zoom_level ( xzoom );
1179 if ( zl < vik_map_source_get_zoom_min(map) || zl > vik_map_source_get_zoom_max(map) )
1182 if (vml->last_center == NULL) {
1183 VikCoord *new_center = g_malloc(sizeof(VikCoord));
1184 *new_center = *center;
1185 vml->last_center = new_center;
1186 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1187 vml->last_ympp = vik_viewport_get_ympp(vvp);
1191 /* TODO: perhaps vik_coord_diff() */
1192 if (vik_coord_equals(vml->last_center, center)
1193 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1194 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1197 *(vml->last_center) = *center;
1198 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1199 vml->last_ympp = vik_viewport_get_ympp(vvp);
1206 gboolean try_draw_scale_down (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1207 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1211 for (scale_inc = 1; scale_inc < SCALE_INC_DOWN; scale_inc ++) {
1212 // Try with smaller zooms
1213 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
1214 MapCoord ulm2 = ulm;
1215 ulm2.x = ulm.x / scale_factor;
1216 ulm2.y = ulm.y / scale_factor;
1217 ulm2.scale = ulm.scale + scale_inc;
1218 pixbuf = get_pixbuf ( vml, id, mapname, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1220 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1221 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1222 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1223 g_object_unref(pixbuf);
1233 gboolean try_draw_scale_up (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1234 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1237 // Try with bigger zooms
1239 for (scale_dec = 1; scale_dec < SCALE_INC_UP; scale_dec ++) {
1241 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
1242 MapCoord ulm2 = ulm;
1243 ulm2.x = ulm.x * scale_factor;
1244 ulm2.y = ulm.y * scale_factor;
1245 ulm2.scale = ulm.scale - scale_dec;
1246 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1247 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1248 MapCoord ulm3 = ulm2;
1251 pixbuf = get_pixbuf ( vml, id, mapname, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1255 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1256 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1257 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1258 g_object_unref(pixbuf);
1267 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1270 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1271 gdouble yzoom = vik_viewport_get_ympp ( vvp );
1272 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
1273 gboolean existence_only = FALSE;
1275 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1276 xshrinkfactor = vml->xmapzoom / xzoom;
1277 yshrinkfactor = vml->ymapzoom / yzoom;
1278 xzoom = vml->xmapzoom;
1279 yzoom = vml->xmapzoom;
1280 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1281 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
1282 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1283 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
1284 existence_only = TRUE;
1287 // Report the reason for not drawing
1288 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1289 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));
1290 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1299 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1300 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1301 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
1305 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1306 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
1307 guint16 id = vik_map_source_get_uniq_id(map);
1308 const gchar *mapname = vik_map_source_get_name(map);
1311 gint xx, yy, width, height;
1314 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1315 // which can happen when using a small fixed zoom level and viewing large areas.
1316 // Also prevents very large number of tile download requests
1317 gint tiles = (xmax-xmin) * (ymax-ymin);
1318 if ( tiles > MAX_TILES ) {
1319 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1320 existence_only = TRUE;
1323 guint max_path_len = strlen(vml->cache_dir) + 40;
1324 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1326 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
1327 g_debug("%s: Starting autodownload", __FUNCTION__);
1328 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
1329 // Try to download newer tiles
1330 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1332 // Download only missing tiles
1333 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
1336 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
1337 for ( x = xmin; x <= xmax; x++ ) {
1338 for ( y = ymin; y <= ymax; y++ ) {
1341 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1343 width = gdk_pixbuf_get_width ( pixbuf );
1344 height = gdk_pixbuf_get_height ( pixbuf );
1346 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1347 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1351 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1352 g_object_unref(pixbuf);
1356 } else { /* tilesize is known, don't have to keep converting coords */
1357 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1358 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
1359 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1360 gint tilesize_x_ceil = ceil ( tilesize_x );
1361 gint tilesize_y_ceil = ceil ( tilesize_y );
1362 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1363 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
1364 gint xx_tmp, yy_tmp;
1365 gint base_yy, xend, yend;
1367 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1368 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1370 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1371 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1372 xx = xx_tmp; yy = yy_tmp;
1373 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1374 * eg if tile size 128, shrinkfactor 0.333 */
1375 xx -= (tilesize_x/2);
1376 base_yy = yy - (tilesize_y/2);
1378 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1380 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1384 if ( existence_only ) {
1385 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1386 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, vik_map_source_get_name(map),
1387 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1389 get_filename ( vml->cache_dir, vml->cache_layout, id, vik_map_source_get_name(map),
1390 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1392 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1393 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1394 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1397 // Try correct scale first
1398 int scale_factor = 1;
1399 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1401 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1402 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1403 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1404 g_object_unref(pixbuf);
1407 // Otherwise try different scales
1408 if ( SCALE_SMALLER_ZOOM_FIRST ) {
1409 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) ) {
1410 try_draw_scale_up(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1414 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) ) {
1415 try_draw_scale_down(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1426 // ATM Only show tile grid lines in extreme debug mode
1427 if ( vik_debug && vik_verbose ) {
1428 /* Grid drawing here so it gets drawn on top of the map */
1429 /* Thus loop around x & y again, but this time separately */
1430 /* Only showing grid for the current scale */
1431 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
1432 /* Draw single grid lines across the whole screen */
1433 gint width = vik_viewport_get_width(vvp);
1434 gint height = vik_viewport_get_height(vvp);
1435 xx = xx_tmp; yy = yy_tmp;
1436 gint base_xx = xx - (tilesize_x/2);
1437 base_yy = yy - (tilesize_y/2);
1440 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1441 vik_viewport_draw_line ( vvp, black_gc, xx, base_yy, xx, height );
1446 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1447 vik_viewport_draw_line ( vvp, black_gc, base_xx, yy, width, yy );
1453 g_free ( path_buf );
1457 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1459 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
1464 gdouble level = vik_viewport_get_zoom ( vvp );
1466 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1467 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
1470 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1471 vik_viewport_add_logo ( vvp, logo );
1473 /* get corner coords */
1474 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1475 /* UTM multi-zone stuff by Kit Transue */
1476 gchar leftmost_zone, rightmost_zone, i;
1477 leftmost_zone = vik_viewport_leftmost_zone( vvp );
1478 rightmost_zone = vik_viewport_rightmost_zone( vvp );
1479 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1480 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1481 maps_layer_draw_section ( vml, vvp, &ul, &br );
1485 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1486 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1488 maps_layer_draw_section ( vml, vvp, &ul, &br );
1493 /*************************/
1494 /****** DOWNLOADING ******/
1495 /*************************/
1497 /* pass along data to thread, exists even if layer is deleted. */
1500 gchar *filename_buf;
1501 VikMapsCacheLayout cache_layout;
1502 gint x0, y0, xf, yf;
1508 gboolean refresh_display;
1511 gboolean map_layer_alive;
1515 static void mdi_free ( MapDownloadInfo *mdi )
1517 vik_mutex_free(mdi->mutex);
1518 g_free ( mdi->cache_dir );
1519 mdi->cache_dir = NULL;
1520 g_free ( mdi->filename_buf );
1521 mdi->filename_buf = NULL;
1525 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1527 MapDownloadInfo *mdi = ptr;
1528 g_mutex_lock(mdi->mutex);
1529 mdi->map_layer_alive = FALSE;
1530 g_mutex_unlock(mdi->mutex);
1533 static gboolean is_in_area (VikMapSource *map, MapCoord mc)
1536 vik_map_source_mapcoord_to_center_coord ( map, &mc, &vc );
1539 tl.lat = vik_map_source_get_lat_max(map);
1540 tl.lon = vik_map_source_get_lon_min(map);
1542 br.lat = vik_map_source_get_lat_min(map);
1543 br.lon = vik_map_source_get_lon_max(map);
1545 vik_coord_load_from_latlon (&vctl, VIK_COORD_LATLON, &tl);
1547 vik_coord_load_from_latlon (&vcbr, VIK_COORD_LATLON, &br);
1549 return vik_coord_inside ( &vc, &vctl, &vcbr );
1552 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1554 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1556 MapCoord mcoord = mdi->mapcoord;
1558 for ( x = mdi->x0; x <= mdi->xf; x++ )
1561 for ( y = mdi->y0; y <= mdi->yf; y++ )
1564 // Only attempt to download a tile from supported areas
1565 if ( is_in_area ( MAPS_LAYER_NTH_TYPE(mdi->maptype), mcoord ) )
1567 gboolean remove_mem_cache = FALSE;
1568 gboolean need_download = FALSE;
1570 get_filename ( mdi->cache_dir, mdi->cache_layout,
1571 vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1572 vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1573 mdi->mapcoord.scale, mdi->mapcoord.z, x, y, mdi->filename_buf, mdi->maxlen,
1574 vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1577 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1579 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1583 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1584 need_download = TRUE;
1585 remove_mem_cache = TRUE;
1587 } else { /* in case map file already exists */
1588 switch (mdi->redownload) {
1589 case REDOWNLOAD_NONE:
1592 case REDOWNLOAD_BAD:
1594 /* see if this one is bad or what */
1596 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1597 if (gx || (!pixbuf)) {
1598 if ( g_remove ( mdi->filename_buf ) )
1599 g_warning ( "REDOWNLOAD failed to remove: %s", mdi->filename_buf );
1600 need_download = TRUE;
1601 remove_mem_cache = TRUE;
1602 g_error_free ( gx );
1605 g_object_unref ( pixbuf );
1610 case REDOWNLOAD_NEW:
1611 need_download = TRUE;
1612 remove_mem_cache = TRUE;
1615 case REDOWNLOAD_ALL:
1616 /* FIXME: need a better way than to erase file in case of server/network problem */
1617 if ( g_remove ( mdi->filename_buf ) )
1618 g_warning ( "REDOWNLOAD failed to remove: %s", mdi->filename_buf );
1619 need_download = TRUE;
1620 remove_mem_cache = TRUE;
1623 case DOWNLOAD_OR_REFRESH:
1624 remove_mem_cache = TRUE;
1628 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1632 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1634 if (need_download) {
1635 DownloadResult_t dr = vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle);
1637 case DOWNLOAD_HTTP_ERROR:
1638 case DOWNLOAD_CONTENT_ERROR: {
1639 // TODO: ?? count up the number of download errors somehow...
1640 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Failed to download tile") );
1641 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1645 case DOWNLOAD_FILE_WRITE_ERROR: {
1646 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Unable to save tile") );
1647 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1651 case DOWNLOAD_SUCCESS:
1652 case DOWNLOAD_NOT_REQUIRED:
1658 g_mutex_lock(mdi->mutex);
1659 if (remove_mem_cache)
1660 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 );
1661 if (mdi->refresh_display && mdi->map_layer_alive) {
1662 /* TODO: check if it's on visible area */
1663 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1665 g_mutex_unlock(mdi->mutex);
1666 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1670 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1671 g_mutex_lock(mdi->mutex);
1672 if (mdi->map_layer_alive)
1673 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1674 g_mutex_unlock(mdi->mutex);
1678 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1680 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1682 get_filename ( mdi->cache_dir, mdi->cache_layout,
1683 vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1684 vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1685 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y, mdi->filename_buf, mdi->maxlen,
1686 vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1687 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1689 if ( g_remove ( mdi->filename_buf ) )
1690 g_warning ( "Cleanup failed to remove: %s", mdi->filename_buf );
1695 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1697 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1698 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1700 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1702 // Don't ever attempt download on direct access
1703 if ( vik_map_source_is_direct_file_access ( map ) )
1706 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1707 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1709 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1714 mdi->map_layer_alive = TRUE;
1715 mdi->mutex = vik_mutex_new();
1716 mdi->refresh_display = TRUE;
1718 /* cache_dir and buffer for dest filename */
1719 mdi->cache_dir = g_strdup ( vml->cache_dir );
1720 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1721 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1722 mdi->cache_layout = vml->cache_layout;
1723 mdi->maptype = vml->maptype;
1725 mdi->mapcoord = ulm;
1726 mdi->redownload = redownload;
1728 mdi->x0 = MIN(ulm.x, brm.x);
1729 mdi->xf = MAX(ulm.x, brm.x);
1730 mdi->y0 = MIN(ulm.y, brm.y);
1731 mdi->yf = MAX(ulm.y, brm.y);
1735 MapCoord mcoord = mdi->mapcoord;
1737 if ( mdi->redownload ) {
1738 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1740 /* calculate how many we need */
1741 for ( a = mdi->x0; a <= mdi->xf; a++ )
1744 for ( b = mdi->y0; b <= mdi->yf; b++ )
1747 // Only count tiles from supported areas
1748 if ( is_in_area (map, mcoord) )
1750 get_filename ( mdi->cache_dir, mdi->cache_layout,
1751 vik_map_source_get_uniq_id(map),
1752 vik_map_source_get_name(map),
1753 ulm.scale, ulm.z, a, b, mdi->filename_buf, mdi->maxlen,
1754 vik_map_source_get_file_extension(map) );
1755 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1762 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1764 if ( mdi->mapstoget )
1766 const gchar *tmp_str;
1771 if (redownload == REDOWNLOAD_BAD)
1772 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1774 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1778 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1780 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1782 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1783 /* launch the thread */
1784 a_background_thread ( BACKGROUND_POOL_REMOTE,
1785 VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1786 tmp, /* description string */
1787 (vik_thr_func) map_download_thread, /* function to call within thread */
1788 mdi, /* pass along data */
1789 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1790 (vik_thr_free_func) mdi_cancel_cleanup,
1799 static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1802 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1804 // Don't ever attempt download on direct access
1805 if ( vik_map_source_is_direct_file_access ( map ) )
1808 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1809 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1810 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1814 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1819 mdi->map_layer_alive = TRUE;
1820 mdi->mutex = vik_mutex_new();
1821 mdi->refresh_display = TRUE;
1823 mdi->cache_dir = g_strdup ( vml->cache_dir );
1824 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1825 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1826 mdi->maptype = vml->maptype;
1827 mdi->cache_layout = vml->cache_layout;
1829 mdi->mapcoord = ulm;
1830 mdi->redownload = download_method;
1832 mdi->x0 = MIN(ulm.x, brm.x);
1833 mdi->xf = MAX(ulm.x, brm.x);
1834 mdi->y0 = MIN(ulm.y, brm.y);
1835 mdi->yf = MAX(ulm.y, brm.y);
1839 MapCoord mcoord = mdi->mapcoord;
1841 for (i = mdi->x0; i <= mdi->xf; i++) {
1843 for (j = mdi->y0; j <= mdi->yf; j++) {
1845 // Only count tiles from supported areas
1846 if ( is_in_area (map, mcoord) ) {
1847 get_filename ( mdi->cache_dir, mdi->cache_layout,
1848 vik_map_source_get_uniq_id(map),
1849 vik_map_source_get_name(map),
1850 ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
1851 vik_map_source_get_file_extension(map) );
1852 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1858 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1860 if (mdi->mapstoget) {
1863 fmt = ngettext("Downloading %d %s map...",
1864 "Downloading %d %s maps...",
1866 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1868 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1870 // launch the thread
1871 a_background_thread ( BACKGROUND_POOL_REMOTE,
1872 VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1873 tmp, /* description string */
1874 (vik_thr_func) map_download_thread, /* function to call within thread */
1875 mdi, /* pass along data */
1876 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1877 (vik_thr_free_func) mdi_cancel_cleanup,
1886 * vik_maps_layer_download_section:
1887 * @vml: The Map Layer
1888 * @vvp: The Viewport that the map is on
1889 * @ul: Upper left coordinate of the area to be downloaded
1890 * @br: Bottom right coordinate of the area to be downloaded
1891 * @zoom: The zoom level at which the maps are to be download
1893 * Download a specified map area at a certain zoom level
1895 void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1897 maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1900 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1902 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1905 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1907 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1910 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1912 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1916 * Display a simple dialog with information about this particular map tile
1918 static void maps_layer_tile_info ( VikMapsLayer *vml )
1920 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1922 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1923 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1926 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1929 gchar *filename = NULL;
1930 gchar *source = NULL;
1932 if ( vik_map_source_is_direct_file_access ( map ) ) {
1933 if ( vik_map_source_is_mbtiles ( map ) ) {
1934 filename = g_strdup ( vml->filename );
1935 #ifdef HAVE_SQLITE3_H
1936 // And whether to bother going into the SQL to check it's really there or not...
1937 gchar *exists = NULL;
1938 gint zoom = 17 - ulm.scale;
1939 if ( vml->mbtiles ) {
1940 GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, zoom );
1942 exists = g_strdup ( _("YES") );
1943 g_object_unref ( G_OBJECT(pixbuf) );
1946 exists = g_strdup ( _("NO") );
1950 exists = g_strdup ( _("NO") );
1951 gint flip_y = (gint) pow(2, zoom)-1 - ulm.y;
1952 // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1953 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 );
1956 source = g_strdup ( _("Source: Not available") );
1959 else if ( vik_map_source_is_osm_meta_tiles ( map ) ) {
1960 char path[PATH_MAX];
1961 xyz_to_meta(path, sizeof(path), vml->cache_dir, ulm.x, ulm.y, 17-ulm.scale );
1962 source = g_strdup ( path );
1963 filename = g_strdup ( path );
1966 guint max_path_len = strlen(vml->cache_dir) + 40;
1967 filename = g_malloc ( max_path_len * sizeof(char) );
1968 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM,
1969 vik_map_source_get_uniq_id(map),
1971 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1972 vik_map_source_get_file_extension(map) );
1973 source = g_strconcat ( "Source: file://", filename, NULL );
1977 guint max_path_len = strlen(vml->cache_dir) + 40;
1978 filename = g_malloc ( max_path_len * sizeof(char) );
1979 get_filename ( vml->cache_dir, vml->cache_layout,
1980 vik_map_source_get_uniq_id(map),
1981 vik_map_source_get_name(map),
1982 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1983 vik_map_source_get_file_extension(map) );
1984 source = g_markup_printf_escaped ( "Source: http://%s%s",
1985 vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1986 vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1989 GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
1990 g_array_append_val ( array, source );
1992 gchar *filemsg = NULL;
1993 gchar *timemsg = NULL;
1995 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1996 filemsg = g_strconcat ( "Tile File: ", filename, NULL );
1997 // Get some timestamp information of the tile
1998 struct stat stat_buf;
1999 if ( g_stat ( filename, &stat_buf ) == 0 ) {
2001 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
2002 timemsg = g_strdup_printf ( _("Tile File Timestamp: %s"), time_buf );
2005 timemsg = g_strdup ( _("Tile File Timestamp: Not Available") );
2007 g_array_append_val ( array, filemsg );
2008 g_array_append_val ( array, timemsg );
2011 filemsg = g_strdup_printf ( "Tile File: %s [Not Available]", filename );
2012 g_array_append_val ( array, filemsg );
2015 a_dialog_list ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Tile Information"), array, 5 );
2016 g_array_free ( array, FALSE );
2021 g_free ( filename );
2024 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2026 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2028 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
2030 if ( event->button == 1 )
2033 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 );
2034 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 );
2035 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
2036 vml->dl_tool_x = vml->dl_tool_y = -1;
2041 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) );
2042 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) );
2044 vml->redownload_vvp = vvp;
2046 vml->dl_tool_x = vml->dl_tool_y = -1;
2048 if ( ! vml->dl_right_click_menu ) {
2050 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
2052 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
2053 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
2054 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2056 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
2057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
2058 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2060 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
2061 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
2062 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2064 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
2065 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
2066 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
2067 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
2070 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
2071 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
2077 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
2082 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2085 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2087 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2088 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
2089 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
2090 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
2091 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
2093 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
2099 // A slightly better way of defining the menu callback information
2100 // This should be easier to extend/rework compared to previously
2107 typedef gpointer menu_array_values[MA_LAST];
2109 static void download_onscreen_maps ( menu_array_values values, gint redownload )
2111 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2112 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2113 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
2115 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
2116 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
2121 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
2122 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
2124 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2125 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
2126 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
2127 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
2128 start_download_thread ( vml, vvp, &ul, &br, redownload );
2129 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
2130 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
2131 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
2132 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
2136 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
2140 static void maps_layer_download_missing_onscreen_maps ( menu_array_values values )
2142 download_onscreen_maps( values, REDOWNLOAD_NONE);
2145 static void maps_layer_download_new_onscreen_maps ( menu_array_values values )
2147 download_onscreen_maps( values, REDOWNLOAD_NEW);
2150 static void maps_layer_redownload_all_onscreen_maps ( menu_array_values values )
2152 download_onscreen_maps( values, REDOWNLOAD_ALL);
2155 static void maps_layer_about ( gpointer vml_vvp[2] )
2157 VikMapsLayer *vml = vml_vvp[0];
2158 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2160 if ( vik_map_source_get_license (map) )
2161 maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
2163 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2164 vik_map_source_get_label (map) );
2168 * maps_layer_how_many_maps:
2169 * Copied from maps_layer_download_section but without the actual download and this returns a value
2171 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
2174 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2176 if ( vik_map_source_is_direct_file_access ( map ) )
2179 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
2180 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
2181 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
2185 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
2190 mdi->map_layer_alive = TRUE;
2191 mdi->mutex = vik_mutex_new();
2192 mdi->refresh_display = FALSE;
2194 mdi->cache_dir = g_strdup ( vml->cache_dir );
2195 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
2196 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
2197 mdi->maptype = vml->maptype;
2198 mdi->cache_layout = vml->cache_layout;
2200 mdi->mapcoord = ulm;
2201 mdi->redownload = redownload;
2203 mdi->x0 = MIN(ulm.x, brm.x);
2204 mdi->xf = MAX(ulm.x, brm.x);
2205 mdi->y0 = MIN(ulm.y, brm.y);
2206 mdi->yf = MAX(ulm.y, brm.y);
2210 if ( mdi->redownload == REDOWNLOAD_ALL ) {
2211 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
2214 /* calculate how many we need */
2215 MapCoord mcoord = mdi->mapcoord;
2216 for (i = mdi->x0; i <= mdi->xf; i++) {
2218 for (j = mdi->y0; j <= mdi->yf; j++) {
2220 // Only count tiles from supported areas
2221 if ( is_in_area ( map, mcoord ) ) {
2222 get_filename ( mdi->cache_dir, mdi->cache_layout,
2223 vik_map_source_get_uniq_id(map),
2224 vik_map_source_get_name(map),
2225 ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
2226 vik_map_source_get_file_extension(map) );
2227 if ( mdi->redownload == REDOWNLOAD_NEW ) {
2228 // Assume the worst - always a new file
2229 // Absolute value would require a server lookup - but that is too slow
2233 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
2238 if ( mdi->redownload == REDOWNLOAD_BAD ) {
2239 /* see if this one is bad or what */
2241 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
2242 if (gx || (!pixbuf)) {
2246 // Other download cases already considered or just ignored
2255 gint rv = mdi->mapstoget;
2263 * maps_dialog_zoom_between:
2264 * This dialog is specific to the map layer, so it's here rather than in dialog.c
2266 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
2271 gint *selected_zoom1,
2272 gint *selected_zoom2,
2273 gchar *download_list[],
2274 gint default_download,
2275 gint *selected_download )
2277 GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
2279 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2280 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2281 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2283 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2284 GtkWidget *response_w = NULL;
2285 #if GTK_CHECK_VERSION (2, 20, 0)
2286 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2288 GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
2289 GtkWidget *zoom_combo1 = vik_combo_box_text_new();
2291 for (s = zoom_list; *s; s++)
2292 vik_combo_box_text_append ( zoom_combo1, *s );
2293 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
2295 GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
2296 GtkWidget *zoom_combo2 = vik_combo_box_text_new();
2297 for (s = zoom_list; *s; s++)
2298 vik_combo_box_text_append ( zoom_combo2, *s );
2299 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
2301 GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
2302 GtkWidget *download_combo = vik_combo_box_text_new();
2303 for (s = download_list; *s; s++)
2304 vik_combo_box_text_append ( download_combo, *s );
2305 gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
2307 GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
2308 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
2309 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
2310 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
2311 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
2312 gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
2313 gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
2315 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
2318 gtk_widget_grab_focus ( response_w );
2320 gtk_widget_show_all ( dialog );
2321 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2322 gtk_widget_destroy(dialog);
2326 // Return selected options
2327 *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
2328 *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
2329 *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
2331 gtk_widget_destroy(dialog);
2335 // My best guess of sensible limits
2336 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
2337 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2340 * Get all maps in the region for zoom levels specified by the user
2341 * Sort of similar to trw_layer_download_map_along_track_cb function
2343 static void maps_layer_download_all ( menu_array_values values )
2345 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2346 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2348 // I don't think we should allow users to hammer the servers too much...
2349 // Delibrately not allowing lowest zoom levels
2350 // Still can give massive numbers to download
2351 // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2352 gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2353 gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2355 gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2356 gint selected_download_method;
2358 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2360 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2361 if (cur_zoom == zoom_vals[default_zoom])
2364 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2366 // Default to only 2 zoom levels below the current one
2367 if (default_zoom > 1 )
2368 lower_zoom = default_zoom - 2;
2370 lower_zoom = default_zoom;
2372 // redownload method - needs to align with REDOWNLOAD* macro values
2373 gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2375 gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2377 if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2385 REDOWNLOAD_NONE, // AKA Missing
2386 &selected_download_method ) ) {
2393 // Find out new current positions
2394 gdouble min_lat, max_lat, min_lon, max_lon;
2395 VikCoord vc_ul, vc_br;
2396 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2397 struct LatLon ll_ul = { max_lat, min_lon };
2398 struct LatLon ll_br = { min_lat, max_lon };
2399 vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2400 vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2402 // Get Maps Count - call for each zoom level (in reverse)
2403 // With REDOWNLOAD_NEW this is a possible maximum
2404 // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2407 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2408 map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2411 g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2413 // Absolute protection of hammering a map server
2414 if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2415 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);
2416 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2421 // Confirm really want to do this
2422 if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2423 gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2424 gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2430 // Get Maps - call for each zoom level (in reverse)
2431 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2432 maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2439 static void maps_layer_flush ( menu_array_values values )
2441 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2442 a_mapcache_flush_type ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)) );
2445 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2448 static menu_array_values values;
2449 values[MA_VML] = vml;
2450 values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2452 item = gtk_menu_item_new();
2453 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2454 gtk_widget_show ( item );
2456 /* Now with icons */
2457 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2458 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2459 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), values );
2460 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2461 gtk_widget_show ( item );
2463 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2464 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2465 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2466 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), values );
2467 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2468 gtk_widget_show ( item );
2471 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2472 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2473 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), values );
2474 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2475 gtk_widget_show ( item );
2477 item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2478 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2479 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), values );
2480 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2481 gtk_widget_show ( item );
2483 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2484 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_about), values );
2485 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2486 gtk_widget_show ( item );
2488 // Typical users shouldn't need to use this functionality - so debug only ATM
2490 item = gtk_image_menu_item_new_with_mnemonic ( _("Flush Map Cache") );
2491 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2492 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_flush), values );
2493 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2494 gtk_widget_show ( item );
2499 * Enable downloading maps of the current screen area either 'new' or 'everything'
2501 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2506 static menu_array_values values;
2507 values[MA_VML] = vml;
2508 values[MA_VVP] = vvp;
2511 // Get only new maps
2512 maps_layer_download_new_onscreen_maps ( values );
2514 // Redownload everything
2515 maps_layer_redownload_all_onscreen_maps ( values );