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 (VikLayerFuncSetMenuItemsSelection) NULL,
236 (VikLayerFuncGetMenuItemsSelection) NULL,
238 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
239 (VikLayerFuncSublayerAddMenuItems) NULL,
241 (VikLayerFuncSublayerRenameRequest) NULL,
242 (VikLayerFuncSublayerToggleVisible) NULL,
243 (VikLayerFuncSublayerTooltip) NULL,
244 (VikLayerFuncLayerTooltip) maps_layer_tooltip,
245 (VikLayerFuncLayerSelected) NULL,
247 (VikLayerFuncMarshall) maps_layer_marshall,
248 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
250 (VikLayerFuncSetParam) maps_layer_set_param,
251 (VikLayerFuncGetParam) maps_layer_get_param,
252 (VikLayerFuncChangeParam) maps_layer_change_param,
254 (VikLayerFuncReadFileData) NULL,
255 (VikLayerFuncWriteFileData) NULL,
257 (VikLayerFuncDeleteItem) NULL,
258 (VikLayerFuncCutItem) NULL,
259 (VikLayerFuncCopyItem) NULL,
260 (VikLayerFuncPasteItem) NULL,
261 (VikLayerFuncFreeCopiedItem) NULL,
262 (VikLayerFuncDragDropRequest) NULL,
264 (VikLayerFuncSelectClick) NULL,
265 (VikLayerFuncSelectMove) NULL,
266 (VikLayerFuncSelectRelease) NULL,
267 (VikLayerFuncSelectedViewportMenu) NULL,
270 struct _VikMapsLayer {
274 VikMapsCacheLayout cache_layout;
277 gdouble xmapzoom, ymapzoom;
279 gboolean autodownload;
280 gboolean adl_only_missing;
281 VikCoord *last_center;
285 gint dl_tool_x, dl_tool_y;
287 GtkMenu *dl_right_click_menu;
288 VikCoord redownload_ul, redownload_br; /* right click menu only */
289 VikViewport *redownload_vvp;
291 #ifdef HAVE_SQLITE3_H
296 enum { REDOWNLOAD_NONE = 0, /* download only missing maps */
297 REDOWNLOAD_BAD, /* download missing and bad maps */
298 REDOWNLOAD_NEW, /* download missing maps that are newer on server only */
299 REDOWNLOAD_ALL, /* download all maps */
300 DOWNLOAD_OR_REFRESH }; /* download missing maps and refresh cache */
302 static VikLayerParam prefs[] = {
303 { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Default map layer directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, N_("Choose a directory to store cached Map tiles for this layer"), NULL, NULL, NULL },
306 void maps_layer_init ()
308 VikLayerParamData tmp;
309 tmp.s = maps_layer_default_dir();
310 a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
312 gint max_tiles = MAX_TILES;
313 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_MAX_TILES, &max_tiles ) )
314 MAX_TILES = max_tiles;
317 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MIN_SHRINKFACTOR, &gdtmp ) )
318 MIN_SHRINKFACTOR = gdtmp;
320 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MAX_SHRINKFACTOR, &gdtmp ) )
321 MAX_SHRINKFACTOR = gdtmp;
323 if ( a_settings_get_double ( VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR, &gdtmp ) )
324 REAL_MIN_SHRINKFACTOR = gdtmp;
327 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_UP, &gitmp ) )
328 SCALE_INC_UP = gitmp;
330 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_DOWN, &gitmp ) )
331 SCALE_INC_DOWN = gitmp;
333 gboolean gbtmp = TRUE;
334 if ( a_settings_get_boolean ( VIK_SETTINGS_MAP_SCALE_SMALLER_ZOOM_FIRST, &gbtmp ) )
335 SCALE_SMALLER_ZOOM_FIRST = gbtmp;
339 /****************************************/
340 /******** MAPS LAYER TYPES **************/
341 /****************************************/
343 void _add_map_source ( guint16 id, const char *label, VikMapSource *map )
347 len = g_strv_length (params_maptypes);
349 params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
350 params_maptypes[len] = g_strdup (label);
351 params_maptypes[len+1] = NULL;
354 params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
355 params_maptypes_ids[len] = id;
356 params_maptypes_ids[len+1] = 0;
358 /* We have to clone */
359 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
360 /* Register the clone in the list */
361 __map_types = g_list_append(__map_types, clone);
364 We have to ensure the mode LayerParam references the up-to-date
368 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
369 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
371 maps_layer_params[0].widget_data = params_maptypes;
372 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
375 void _update_map_source ( const char *label, VikMapSource *map, int index )
377 GList *item = g_list_nth (__map_types, index);
378 g_object_unref (item->data);
379 item->data = g_object_ref (map);
380 /* Change previous data */
381 g_free (params_maptypes[index]);
382 params_maptypes[index] = g_strdup (label);
386 * maps_layer_register_map_source:
387 * @map: the new VikMapSource
389 * Register a new VikMapSource.
390 * Override existing one (equality of id).
392 void maps_layer_register_map_source ( VikMapSource *map )
394 g_assert(map != NULL);
396 guint16 id = vik_map_source_get_uniq_id(map);
397 const char *label = vik_map_source_get_label(map);
398 g_assert(label != NULL);
400 int previous = map_uniq_id_to_index (id);
401 if (previous != NUM_MAP_TYPES)
403 _update_map_source (label, map, previous);
407 _add_map_source (id, label, map);
411 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
412 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
413 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
416 * vik_maps_layer_get_map_type:
418 * Returns the actual map id (rather than the internal type index value)
420 guint vik_maps_layer_get_map_type(VikMapsLayer *vml)
422 return MAPS_LAYER_NTH_ID(vml->maptype);
426 * vik_maps_layer_set_map_type:
429 void vik_maps_layer_set_map_type(VikMapsLayer *vml, guint map_type)
431 gint maptype = map_uniq_id_to_index(map_type);
432 if ( maptype == NUM_MAP_TYPES )
433 g_warning(_("Unknown map type"));
435 vml->maptype = maptype;
439 * vik_maps_layer_get_default_map_type:
442 guint vik_maps_layer_get_default_map_type ()
444 VikLayerInterface *vli = vik_layer_get_interface ( VIK_LAYER_MAPS );
445 VikLayerParamData vlpd = a_layer_defaults_get ( vli->fixed_layer_name, "mode", VIK_LAYER_PARAM_UINT );
451 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
453 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
456 /****************************************/
457 /******** CACHE DIR STUFF ***************/
458 /****************************************/
460 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
461 #define DIRECTDIRACCESS_WITH_NAME "%s%s" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
462 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
463 #define MAPS_CACHE_DIR maps_layer_default_dir()
467 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
468 #define LOCAL_MAPS_DIR "VIKING-MAPS"
469 #elif defined __APPLE__
471 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
472 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
475 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
476 #define LOCAL_MAPS_DIR ".viking-maps"
479 gchar *maps_layer_default_dir ()
481 static gchar *defaultdir = NULL;
484 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
485 const gchar *mapdir = g_getenv("VIKING_MAPS");
487 defaultdir = g_strdup ( mapdir );
488 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
489 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
491 const gchar *home = g_get_home_dir();
492 if (!home || g_access(home, W_OK))
493 home = g_get_home_dir ();
495 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
497 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
499 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
501 /* Add the separator at the end */
502 gchar *tmp = defaultdir;
503 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
506 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
511 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
513 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
515 g_mkdir ( vml->cache_dir, 0777 );
519 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
521 g_assert ( vml != NULL);
522 g_free ( vml->cache_dir );
523 vml->cache_dir = NULL;
524 const gchar *mydir = dir;
526 if ( dir == NULL || dir[0] == '\0' )
528 if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
529 mydir = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s;
532 gchar *canonical_dir = vu_get_canonical_filename ( VIK_LAYER(vml), mydir );
534 // Ensure cache_dir always ends with a separator
535 guint len = strlen(canonical_dir);
536 if ( canonical_dir[len-1] != G_DIR_SEPARATOR )
538 vml->cache_dir = g_strconcat ( canonical_dir, G_DIR_SEPARATOR_S, NULL );
539 g_free ( canonical_dir );
542 vml->cache_dir = canonical_dir;
545 maps_layer_mkdir_if_default_dir ( vml );
548 static void maps_layer_set_file ( VikMapsLayer *vml, const gchar *name )
551 g_free (vml->filename);
552 vml->filename = g_strdup (name);
555 /****************************************/
556 /******** GOBJECT STUFF *****************/
557 /****************************************/
559 GType vik_maps_layer_get_type ()
561 static GType vml_type = 0;
565 static const GTypeInfo vml_info =
567 sizeof (VikMapsLayerClass),
568 NULL, /* base_init */
569 NULL, /* base_finalize */
570 NULL, /* class init */
571 NULL, /* class_finalize */
572 NULL, /* class_data */
573 sizeof (VikMapsLayer),
575 NULL /* instance init */
577 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
583 /****************************************/
584 /************** PARAMETERS **************/
585 /****************************************/
587 static guint map_index_to_uniq_id (guint16 index)
589 g_assert ( index < NUM_MAP_TYPES );
590 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
593 static guint map_uniq_id_to_index ( guint uniq_id )
596 for ( i = 0; i < NUM_MAP_TYPES; i++ )
597 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
599 return NUM_MAP_TYPES; /* no such thing */
602 #define VIK_SETTINGS_MAP_LICENSE_SHOWN "map_license_shown"
605 * Convenience function to display the license
607 static void maps_show_license ( GtkWindow *parent, VikMapSource *map )
609 a_dialog_license ( parent,
610 vik_map_source_get_label (map),
611 vik_map_source_get_license (map),
612 vik_map_source_get_license_url (map) );
615 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
619 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
620 case PARAM_CACHE_LAYOUT: if ( data.u < VIK_MAPS_CACHE_LAYOUT_NUM ) vml->cache_layout = data.u; break;
621 case PARAM_FILE: maps_layer_set_file ( vml, data.s ); break;
622 case PARAM_MAPTYPE: {
623 gint maptype = map_uniq_id_to_index(data.u);
624 if ( maptype == NUM_MAP_TYPES )
625 g_warning(_("Unknown map type"));
627 vml->maptype = maptype;
629 // When loading from a file don't need the license reminder - ensure it's saved into the 'seen' list
630 if ( is_file_operation ) {
631 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u );
634 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
635 if (vik_map_source_get_license (map) != NULL) {
636 // Check if licence for this map type has been shown before
637 if ( ! a_settings_get_integer_list_contains ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u ) ) {
639 maps_show_license ( VIK_GTK_WINDOW_FROM_WIDGET(vvp), map );
640 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u );
647 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
648 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
649 case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
650 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
651 vml->mapzoom_id = data.u;
652 vml->xmapzoom = __mapzooms_x [data.u];
653 vml->ymapzoom = __mapzooms_y [data.u];
654 }else g_warning (_("Unknown Map Zoom")); break;
660 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
662 VikLayerParamData rv;
665 case PARAM_CACHE_DIR:
667 gboolean set = FALSE;
668 /* Only save a blank when the map cache location equals the default
669 On reading in, when it is blank then the default is reconstructed
670 Since the default changes dependent on the user and OS, it means the resultant file is more portable */
671 if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
675 else if ( is_file_operation ) {
676 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
677 gchar *cwd = g_get_current_dir();
679 rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
680 if ( !rv.s ) rv.s = "";
686 rv.s = vml->cache_dir ? vml->cache_dir : "";
689 case PARAM_CACHE_LAYOUT: rv.u = vml->cache_layout; break;
690 case PARAM_FILE: rv.s = vml->filename; break;
691 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
692 case PARAM_ALPHA: rv.u = vml->alpha; break;
693 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
694 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
695 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
701 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values )
703 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
704 // Alter sensitivity of download option widgets according to the maptype setting.
705 case PARAM_MAPTYPE: {
707 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
708 // Is it *not* the OSM On Disk Tile Layout or the MBTiles type or the OSM Metatiles type
709 gboolean sensitive = ( 21 != vlpd.u && 23 != vlpd.u && 24 != vlpd.u );
710 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
711 GtkWidget **ww2 = values[UI_CHG_LABELS];
712 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
713 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
714 GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
715 GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
716 // Depends on autodownload value
717 gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
718 if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
719 if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
720 if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
721 if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
723 // Cache type not applicable either
724 GtkWidget *w9 = ww1[PARAM_CACHE_LAYOUT];
725 GtkWidget *w10 = ww2[PARAM_CACHE_LAYOUT];
726 if ( w9 ) gtk_widget_set_sensitive ( w9, sensitive );
727 if ( w10 ) gtk_widget_set_sensitive ( w10, sensitive );
729 // File only applicable for MBTiles type
730 // Directory for all other types
731 sensitive = ( 23 == vlpd.u);
732 GtkWidget *w5 = ww1[PARAM_FILE];
733 GtkWidget *w6 = ww2[PARAM_FILE];
734 GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
735 GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
736 if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
737 if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
738 if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
739 if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
744 // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
745 case PARAM_AUTODOWNLOAD: {
747 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
748 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
749 GtkWidget **ww2 = values[UI_CHG_LABELS];
750 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
751 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
752 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
753 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
760 /****************************************/
761 /****** CREATING, COPYING, FREEING ******/
762 /****************************************/
764 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
766 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
767 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
769 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
771 vml->dl_tool_x = vml->dl_tool_y = -1;
772 vml->last_center = NULL;
773 vml->last_xmpp = 0.0;
774 vml->last_ympp = 0.0;
776 vml->dl_right_click_menu = NULL;
777 vml->filename = NULL;
781 static void maps_layer_free ( VikMapsLayer *vml )
783 g_free ( vml->cache_dir );
784 vml->cache_dir = NULL;
785 if ( vml->dl_right_click_menu )
786 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
787 g_free(vml->last_center);
788 vml->last_center = NULL;
789 g_free ( vml->filename );
790 vml->filename = NULL;
792 #ifdef HAVE_SQLITE3_H
793 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
794 if ( vik_map_source_is_mbtiles ( map ) ) {
795 if ( vml->mbtiles ) {
796 int ans = sqlite3_close ( vml->mbtiles );
797 if ( ans != SQLITE_OK ) {
798 // Only to console for information purposes only
799 g_warning ( "SQL Close problem: %d", ans );
806 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
808 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
809 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
813 /* If this method is not called in file reading context
814 * it is called in GUI context.
815 * So, we can check if we have to inform the user about inconsistency */
816 VikViewportDrawMode vp_drawmode;
817 vp_drawmode = vik_viewport_get_drawmode ( vp );
819 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
820 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
821 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
822 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
827 // Performed in post read as we now know the map type
828 #ifdef HAVE_SQLITE3_H
830 if ( vik_map_source_is_mbtiles ( map ) ) {
831 int ans = sqlite3_open_v2 ( vml->filename,
833 SQLITE_OPEN_READONLY,
835 if ( ans != SQLITE_OK ) {
836 // That didn't work, so here's why:
837 g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
839 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
840 _("Failed to open MBTiles file: %s"),
847 // If the on Disk OSM Tile Layout type
848 if ( vml->maptype == 21 )
849 // Copy the directory into filename
850 // thus the mapcache look up will be unique when using more than one of these map types
851 vml->filename = g_strdup (vml->cache_dir);
854 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
856 return vik_maps_layer_get_map_label ( vml );
859 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
861 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
864 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
866 VikMapsLayer *rv = maps_layer_new ( vvp );
867 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
868 maps_layer_post_read ( VIK_LAYER(rv), vvp, FALSE );
872 /*********************/
873 /****** DRAWING ******/
874 /*********************/
876 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
879 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
880 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
881 g_object_unref ( G_OBJECT(pixbuf) );
885 #ifdef HAVE_SQLITE3_H
887 static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
889 g_warning ( "Found %d columns", cols );
891 for ( i = 0; i < cols; i++ ) {
892 g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
901 static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
903 GdkPixbuf *pixbuf = NULL;
905 // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
906 gint flip_y = (gint) pow(2, zoom)-1 - yy;
907 gchar *statement = g_strdup_printf ( "SELECT tile_data FROM tiles WHERE zoom_level=%d AND tile_column=%d AND tile_row=%d;", zoom, xx, flip_y );
909 gboolean finished = FALSE;
911 sqlite3_stmt *sql_stmt = NULL;
912 int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
913 if ( ans != SQLITE_OK ) {
914 g_warning ( "%s: %s - %d", __FUNCTION__, "prepare failure", ans );
918 while ( !finished ) {
919 ans = sqlite3_step ( sql_stmt );
922 // Get tile_data blob
923 int count = sqlite3_column_count(sql_stmt);
925 g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
929 const void *data = sqlite3_column_blob ( sql_stmt, 0 );
930 int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
932 g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
936 // Convert these blob bytes into a pixbuf via these streaming operations
937 GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
938 GError *error = NULL;
939 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
940 if (error || (!pixbuf)) {
941 g_warning ( "%s: %s", __FUNCTION__, error->message );
942 g_error_free ( error );
944 g_input_stream_close ( stream, NULL, NULL );
950 // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
952 // and give up on any errors
953 if ( ans != SQLITE_DONE )
954 g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
959 ans = sqlite3_finalize ( sql_stmt );
961 g_free ( statement );
967 static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
969 GdkPixbuf *pixbuf = NULL;
971 #ifdef HAVE_SQLITE3_H
972 if ( vml->mbtiles ) {
974 gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
976 int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
977 if ( ans != SQLITE_OK ) {
978 // Only to console for information purposes only
979 g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
980 sqlite3_free( errMsg );
982 g_free ( statement );
985 // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
986 // Hence this specific function
987 pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
994 static GdkPixbuf *get_pixbuf_from_metatile ( VikMapsLayer *vml, gint xx, gint yy, gint zz )
996 const int tile_max = METATILE_MAX_SIZE;
997 char err_msg[PATH_MAX];
1002 buf = malloc(tile_max);
1008 len = metatile_read(vml->cache_dir, xx, yy, zz, buf, tile_max, &compressed, err_msg);
1012 // Not handled yet - I don't think this is used often - so implement later if necessary
1013 g_warning ( "Compressed metatiles not implemented:%s\n", __FUNCTION__);
1017 // Convert these buf bytes into a pixbuf via these streaming operations
1018 GdkPixbuf *pixbuf = NULL;
1020 GInputStream *stream = g_memory_input_stream_new_from_data ( buf, len, NULL );
1021 GError *error = NULL;
1022 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
1023 if (error || (!pixbuf)) {
1024 g_warning ( "%s: %s", __FUNCTION__, error->message );
1025 g_error_free ( error );
1027 g_input_stream_close ( stream, NULL, NULL );
1033 g_warning ( "FAILED:%s %s", __FUNCTION__, err_msg);
1039 static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
1041 // Apply alpha setting
1042 if ( pixbuf && vml->alpha < 255 )
1043 pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
1045 if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
1046 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
1049 a_mapcache_add ( pixbuf, (mapcache_extra_t) {0.0}, mapcoord->x, mapcoord->y,
1050 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
1051 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1056 static void get_filename ( const gchar *cache_dir,
1057 VikMapsCacheLayout cl,
1064 gchar *filename_buf,
1066 const gchar* file_extension )
1069 case VIK_MAPS_CACHE_LAYOUT_OSM:
1071 if ( g_strcmp0 ( cache_dir, MAPS_CACHE_DIR ) )
1072 // Cache dir not the default - assume it's been directed somewhere specific
1073 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1075 // Using default cache - so use the map name in the directory path
1076 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS_WITH_NAME, cache_dir, name, (17 - scale), x, y, file_extension );
1079 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1082 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE, cache_dir, id, scale, z, x, y );
1087 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, guint16 id, const gchar* mapname, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
1092 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
1093 id, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1096 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1097 if ( vik_map_source_is_direct_file_access(map) ) {
1098 // ATM MBTiles must be 'a direct access type'
1099 if ( vik_map_source_is_mbtiles(map) ) {
1100 pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1101 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1102 // return now to avoid file tests that aren't appropriate for this map type
1105 else if ( vik_map_source_is_osm_meta_tiles(map) ) {
1106 pixbuf = get_pixbuf_from_metatile ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1107 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1111 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, NULL,
1112 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1113 vik_map_source_get_file_extension(map) );
1116 get_filename ( vml->cache_dir, vml->cache_layout, id, mapname,
1117 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1118 vik_map_source_get_file_extension(map) );
1120 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1123 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
1125 /* free the pixbuf on error */
1128 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE ) {
1130 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1131 gchar* msg = g_strdup_printf ( _("Couldn't open image file: %s"), gx->message );
1132 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1137 g_error_free ( gx );
1139 g_object_unref ( G_OBJECT(pixbuf) );
1142 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1149 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
1151 const VikCoord *center = vik_viewport_get_center ( vvp );
1153 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
1154 /* D'n'D pan in action: do not download */
1157 // Don't attempt to download unsupported zoom levels
1158 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1159 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1160 guint8 zl = map_utils_mpp_to_zoom_level ( xzoom );
1161 if ( zl < vik_map_source_get_zoom_min(map) || zl > vik_map_source_get_zoom_max(map) )
1164 if (vml->last_center == NULL) {
1165 VikCoord *new_center = g_malloc(sizeof(VikCoord));
1166 *new_center = *center;
1167 vml->last_center = new_center;
1168 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1169 vml->last_ympp = vik_viewport_get_ympp(vvp);
1173 /* TODO: perhaps vik_coord_diff() */
1174 if (vik_coord_equals(vml->last_center, center)
1175 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1176 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1179 *(vml->last_center) = *center;
1180 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1181 vml->last_ympp = vik_viewport_get_ympp(vvp);
1188 gboolean try_draw_scale_down (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1189 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1193 for (scale_inc = 1; scale_inc < SCALE_INC_DOWN; scale_inc ++) {
1194 // Try with smaller zooms
1195 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
1196 MapCoord ulm2 = ulm;
1197 ulm2.x = ulm.x / scale_factor;
1198 ulm2.y = ulm.y / scale_factor;
1199 ulm2.scale = ulm.scale + scale_inc;
1200 pixbuf = get_pixbuf ( vml, id, mapname, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1202 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1203 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1204 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1214 gboolean try_draw_scale_up (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1215 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1218 // Try with bigger zooms
1220 for (scale_dec = 1; scale_dec < SCALE_INC_UP; scale_dec ++) {
1222 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
1223 MapCoord ulm2 = ulm;
1224 ulm2.x = ulm.x * scale_factor;
1225 ulm2.y = ulm.y * scale_factor;
1226 ulm2.scale = ulm.scale - scale_dec;
1227 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1228 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1229 MapCoord ulm3 = ulm2;
1232 pixbuf = get_pixbuf ( vml, id, mapname, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1236 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1237 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1238 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1247 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1250 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1251 gdouble yzoom = vik_viewport_get_ympp ( vvp );
1252 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
1253 gboolean existence_only = FALSE;
1255 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1256 xshrinkfactor = vml->xmapzoom / xzoom;
1257 yshrinkfactor = vml->ymapzoom / yzoom;
1258 xzoom = vml->xmapzoom;
1259 yzoom = vml->xmapzoom;
1260 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1261 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
1262 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1263 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
1264 existence_only = TRUE;
1267 // Report the reason for not drawing
1268 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1269 gchar* msg = g_strdup_printf ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
1270 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1279 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1280 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1281 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
1285 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1286 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
1287 guint16 id = vik_map_source_get_uniq_id(map);
1288 const gchar *mapname = vik_map_source_get_name(map);
1291 gint xx, yy, width, height;
1294 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1295 // which can happen when using a small fixed zoom level and viewing large areas.
1296 // Also prevents very large number of tile download requests
1297 gint tiles = (xmax-xmin) * (ymax-ymin);
1298 if ( tiles > MAX_TILES ) {
1299 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1300 existence_only = TRUE;
1303 guint max_path_len = strlen(vml->cache_dir) + 40;
1304 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1306 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
1307 g_debug("%s: Starting autodownload", __FUNCTION__);
1308 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
1309 // Try to download newer tiles
1310 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1312 // Download only missing tiles
1313 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
1316 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
1317 for ( x = xmin; x <= xmax; x++ ) {
1318 for ( y = ymin; y <= ymax; y++ ) {
1321 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1323 width = gdk_pixbuf_get_width ( pixbuf );
1324 height = gdk_pixbuf_get_height ( pixbuf );
1326 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1327 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1331 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1335 } else { /* tilesize is known, don't have to keep converting coords */
1336 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1337 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
1338 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1339 gint tilesize_x_ceil = ceil ( tilesize_x );
1340 gint tilesize_y_ceil = ceil ( tilesize_y );
1341 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1342 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
1343 gint xx_tmp, yy_tmp;
1344 gint base_yy, xend, yend;
1346 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1347 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1349 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1350 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1351 xx = xx_tmp; yy = yy_tmp;
1352 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1353 * eg if tile size 128, shrinkfactor 0.333 */
1354 xx -= (tilesize_x/2);
1355 base_yy = yy - (tilesize_y/2);
1357 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1359 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1363 if ( existence_only ) {
1364 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1365 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, vik_map_source_get_name(map),
1366 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1368 get_filename ( vml->cache_dir, vml->cache_layout, id, vik_map_source_get_name(map),
1369 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1371 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1372 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1373 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1376 // Try correct scale first
1377 int scale_factor = 1;
1378 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1380 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1381 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1382 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1385 // Otherwise try different scales
1386 if ( SCALE_SMALLER_ZOOM_FIRST ) {
1387 if ( !try_draw_scale_down(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len) ) {
1388 try_draw_scale_up(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1392 if ( !try_draw_scale_up(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len) ) {
1393 try_draw_scale_down(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1404 // ATM Only show tile grid lines in extreme debug mode
1405 if ( vik_debug && vik_verbose ) {
1406 /* Grid drawing here so it gets drawn on top of the map */
1407 /* Thus loop around x & y again, but this time separately */
1408 /* Only showing grid for the current scale */
1409 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
1410 /* Draw single grid lines across the whole screen */
1411 gint width = vik_viewport_get_width(vvp);
1412 gint height = vik_viewport_get_height(vvp);
1413 xx = xx_tmp; yy = yy_tmp;
1414 gint base_xx = xx - (tilesize_x/2);
1415 base_yy = yy - (tilesize_y/2);
1418 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1419 vik_viewport_draw_line ( vvp, black_gc, xx, base_yy, xx, height );
1424 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1425 vik_viewport_draw_line ( vvp, black_gc, base_xx, yy, width, yy );
1431 g_free ( path_buf );
1435 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1437 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
1442 gdouble level = vik_viewport_get_zoom ( vvp );
1444 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1445 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
1448 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1449 vik_viewport_add_logo ( vvp, logo );
1451 /* get corner coords */
1452 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1453 /* UTM multi-zone stuff by Kit Transue */
1454 gchar leftmost_zone, rightmost_zone, i;
1455 leftmost_zone = vik_viewport_leftmost_zone( vvp );
1456 rightmost_zone = vik_viewport_rightmost_zone( vvp );
1457 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1458 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1459 maps_layer_draw_section ( vml, vvp, &ul, &br );
1463 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1464 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1466 maps_layer_draw_section ( vml, vvp, &ul, &br );
1471 /*************************/
1472 /****** DOWNLOADING ******/
1473 /*************************/
1475 /* pass along data to thread, exists even if layer is deleted. */
1478 gchar *filename_buf;
1479 VikMapsCacheLayout cache_layout;
1480 gint x0, y0, xf, yf;
1486 gboolean refresh_display;
1489 gboolean map_layer_alive;
1493 static void mdi_free ( MapDownloadInfo *mdi )
1495 vik_mutex_free(mdi->mutex);
1496 g_free ( mdi->cache_dir );
1497 mdi->cache_dir = NULL;
1498 g_free ( mdi->filename_buf );
1499 mdi->filename_buf = NULL;
1503 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1505 MapDownloadInfo *mdi = ptr;
1506 g_mutex_lock(mdi->mutex);
1507 mdi->map_layer_alive = FALSE;
1508 g_mutex_unlock(mdi->mutex);
1511 static gboolean is_in_area (VikMapSource *map, MapCoord mc)
1514 vik_map_source_mapcoord_to_center_coord ( map, &mc, &vc );
1517 tl.lat = vik_map_source_get_lat_max(map);
1518 tl.lon = vik_map_source_get_lon_min(map);
1520 br.lat = vik_map_source_get_lat_min(map);
1521 br.lon = vik_map_source_get_lon_max(map);
1523 vik_coord_load_from_latlon (&vctl, VIK_COORD_LATLON, &tl);
1525 vik_coord_load_from_latlon (&vcbr, VIK_COORD_LATLON, &br);
1527 return vik_coord_inside ( &vc, &vctl, &vcbr );
1530 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1532 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1534 MapCoord mcoord = mdi->mapcoord;
1536 for ( x = mdi->x0; x <= mdi->xf; x++ )
1539 for ( y = mdi->y0; y <= mdi->yf; y++ )
1542 // Only attempt to download a tile from supported areas
1543 if ( is_in_area ( MAPS_LAYER_NTH_TYPE(mdi->maptype), mcoord ) )
1545 gboolean remove_mem_cache = FALSE;
1546 gboolean need_download = FALSE;
1548 get_filename ( mdi->cache_dir, mdi->cache_layout,
1549 vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1550 vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1551 mdi->mapcoord.scale, mdi->mapcoord.z, x, y, mdi->filename_buf, mdi->maxlen,
1552 vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1555 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1557 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1561 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1562 need_download = TRUE;
1563 remove_mem_cache = TRUE;
1565 } else { /* in case map file already exists */
1566 switch (mdi->redownload) {
1567 case REDOWNLOAD_NONE:
1570 case REDOWNLOAD_BAD:
1572 /* see if this one is bad or what */
1574 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1575 if (gx || (!pixbuf)) {
1576 g_remove ( mdi->filename_buf );
1577 need_download = TRUE;
1578 remove_mem_cache = TRUE;
1579 g_error_free ( gx );
1582 g_object_unref ( pixbuf );
1587 case REDOWNLOAD_NEW:
1588 need_download = TRUE;
1589 remove_mem_cache = TRUE;
1592 case REDOWNLOAD_ALL:
1593 /* FIXME: need a better way than to erase file in case of server/network problem */
1594 g_remove ( mdi->filename_buf );
1595 need_download = TRUE;
1596 remove_mem_cache = TRUE;
1599 case DOWNLOAD_OR_REFRESH:
1600 remove_mem_cache = TRUE;
1604 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1608 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1610 if (need_download) {
1611 DownloadResult_t dr = vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle);
1613 case DOWNLOAD_HTTP_ERROR:
1614 case DOWNLOAD_CONTENT_ERROR: {
1615 // TODO: ?? count up the number of download errors somehow...
1616 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Failed to download tile") );
1617 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1621 case DOWNLOAD_FILE_WRITE_ERROR: {
1622 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Unable to save tile") );
1623 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1627 case DOWNLOAD_SUCCESS:
1628 case DOWNLOAD_NOT_REQUIRED:
1634 g_mutex_lock(mdi->mutex);
1635 if (remove_mem_cache)
1636 a_mapcache_remove_all_shrinkfactors ( x, y, mdi->mapcoord.z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)), mdi->mapcoord.scale );
1637 if (mdi->refresh_display && mdi->map_layer_alive) {
1638 /* TODO: check if it's on visible area */
1639 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1641 g_mutex_unlock(mdi->mutex);
1642 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1646 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1647 g_mutex_lock(mdi->mutex);
1648 if (mdi->map_layer_alive)
1649 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1650 g_mutex_unlock(mdi->mutex);
1654 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1656 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1658 get_filename ( mdi->cache_dir, mdi->cache_layout,
1659 vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1660 vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1661 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y, mdi->filename_buf, mdi->maxlen,
1662 vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1663 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1665 g_remove ( mdi->filename_buf );
1670 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1672 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1673 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1675 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1677 // Don't ever attempt download on direct access
1678 if ( vik_map_source_is_direct_file_access ( map ) )
1681 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1682 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1684 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1689 mdi->map_layer_alive = TRUE;
1690 mdi->mutex = vik_mutex_new();
1691 mdi->refresh_display = TRUE;
1693 /* cache_dir and buffer for dest filename */
1694 mdi->cache_dir = g_strdup ( vml->cache_dir );
1695 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1696 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1697 mdi->cache_layout = vml->cache_layout;
1698 mdi->maptype = vml->maptype;
1700 mdi->mapcoord = ulm;
1701 mdi->redownload = redownload;
1703 mdi->x0 = MIN(ulm.x, brm.x);
1704 mdi->xf = MAX(ulm.x, brm.x);
1705 mdi->y0 = MIN(ulm.y, brm.y);
1706 mdi->yf = MAX(ulm.y, brm.y);
1710 MapCoord mcoord = mdi->mapcoord;
1712 if ( mdi->redownload ) {
1713 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1715 /* calculate how many we need */
1716 for ( a = mdi->x0; a <= mdi->xf; a++ )
1719 for ( b = mdi->y0; b <= mdi->yf; b++ )
1722 // Only count tiles from supported areas
1723 if ( is_in_area (map, mcoord) )
1725 get_filename ( mdi->cache_dir, mdi->cache_layout,
1726 vik_map_source_get_uniq_id(map),
1727 vik_map_source_get_name(map),
1728 ulm.scale, ulm.z, a, b, mdi->filename_buf, mdi->maxlen,
1729 vik_map_source_get_file_extension(map) );
1730 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1737 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1739 if ( mdi->mapstoget )
1741 const gchar *tmp_str;
1746 if (redownload == REDOWNLOAD_BAD)
1747 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1749 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1753 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1755 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1757 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1758 /* launch the thread */
1759 a_background_thread ( BACKGROUND_POOL_REMOTE,
1760 VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1761 tmp, /* description string */
1762 (vik_thr_func) map_download_thread, /* function to call within thread */
1763 mdi, /* pass along data */
1764 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1765 (vik_thr_free_func) mdi_cancel_cleanup,
1774 static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1777 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1779 // Don't ever attempt download on direct access
1780 if ( vik_map_source_is_direct_file_access ( map ) )
1783 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1784 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1785 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1789 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1794 mdi->map_layer_alive = TRUE;
1795 mdi->mutex = vik_mutex_new();
1796 mdi->refresh_display = TRUE;
1798 mdi->cache_dir = g_strdup ( vml->cache_dir );
1799 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1800 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1801 mdi->maptype = vml->maptype;
1802 mdi->cache_layout = vml->cache_layout;
1804 mdi->mapcoord = ulm;
1805 mdi->redownload = download_method;
1807 mdi->x0 = MIN(ulm.x, brm.x);
1808 mdi->xf = MAX(ulm.x, brm.x);
1809 mdi->y0 = MIN(ulm.y, brm.y);
1810 mdi->yf = MAX(ulm.y, brm.y);
1814 MapCoord mcoord = mdi->mapcoord;
1816 for (i = mdi->x0; i <= mdi->xf; i++) {
1818 for (j = mdi->y0; j <= mdi->yf; j++) {
1820 // Only count tiles from supported areas
1821 if ( is_in_area (map, mcoord) ) {
1822 get_filename ( mdi->cache_dir, mdi->cache_layout,
1823 vik_map_source_get_uniq_id(map),
1824 vik_map_source_get_name(map),
1825 ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
1826 vik_map_source_get_file_extension(map) );
1827 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1833 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1835 if (mdi->mapstoget) {
1838 fmt = ngettext("Downloading %d %s map...",
1839 "Downloading %d %s maps...",
1841 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1843 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1845 // launch the thread
1846 a_background_thread ( BACKGROUND_POOL_REMOTE,
1847 VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1848 tmp, /* description string */
1849 (vik_thr_func) map_download_thread, /* function to call within thread */
1850 mdi, /* pass along data */
1851 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1852 (vik_thr_free_func) mdi_cancel_cleanup,
1861 * vik_maps_layer_download_section:
1862 * @vml: The Map Layer
1863 * @vvp: The Viewport that the map is on
1864 * @ul: Upper left coordinate of the area to be downloaded
1865 * @br: Bottom right coordinate of the area to be downloaded
1866 * @zoom: The zoom level at which the maps are to be download
1868 * Download a specified map area at a certain zoom level
1870 void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1872 maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1875 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1877 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1880 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1882 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1885 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1887 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1891 * Display a simple dialog with information about this particular map tile
1893 static void maps_layer_tile_info ( VikMapsLayer *vml )
1895 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1897 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1898 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1901 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1904 gchar *filename = NULL;
1905 gchar *source = NULL;
1907 if ( vik_map_source_is_direct_file_access ( map ) ) {
1908 if ( vik_map_source_is_mbtiles ( map ) ) {
1909 filename = g_strdup ( vml->filename );
1910 #ifdef HAVE_SQLITE3_H
1911 // And whether to bother going into the SQL to check it's really there or not...
1912 gchar *exists = NULL;
1913 gint zoom = 17 - ulm.scale;
1914 if ( vml->mbtiles ) {
1915 GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, zoom );
1917 exists = g_strdup ( _("YES") );
1918 g_object_unref ( G_OBJECT(pixbuf) );
1921 exists = g_strdup ( _("NO") );
1925 exists = g_strdup ( _("NO") );
1926 gint flip_y = (gint) pow(2, zoom)-1 - ulm.y;
1927 // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1928 source = g_strdup_printf ( "Source: %s (%d%s%d%s%d.%s %s)", filename, zoom, G_DIR_SEPARATOR_S, ulm.x, G_DIR_SEPARATOR_S, flip_y, "png", exists );
1931 source = g_strdup ( _("Source: Not available") );
1934 else if ( vik_map_source_is_osm_meta_tiles ( map ) ) {
1935 char path[PATH_MAX];
1936 xyz_to_meta(path, sizeof(path), vml->cache_dir, ulm.x, ulm.y, 17-ulm.scale );
1937 source = g_strdup ( path );
1938 filename = g_strdup ( path );
1941 guint max_path_len = strlen(vml->cache_dir) + 40;
1942 filename = g_malloc ( max_path_len * sizeof(char) );
1943 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM,
1944 vik_map_source_get_uniq_id(map),
1946 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1947 vik_map_source_get_file_extension(map) );
1948 source = g_strconcat ( "Source: file://", filename, NULL );
1952 guint max_path_len = strlen(vml->cache_dir) + 40;
1953 filename = g_malloc ( max_path_len * sizeof(char) );
1954 get_filename ( vml->cache_dir, vml->cache_layout,
1955 vik_map_source_get_uniq_id(map),
1956 vik_map_source_get_name(map),
1957 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1958 vik_map_source_get_file_extension(map) );
1959 source = g_strdup_printf ( "Source: http://%s%s",
1960 vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1961 vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1964 GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
1965 g_array_append_val ( array, source );
1967 gchar *filemsg = NULL;
1968 gchar *timemsg = NULL;
1970 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1971 filemsg = g_strconcat ( "Tile File: ", filename, NULL );
1972 // Get some timestamp information of the tile
1973 struct stat stat_buf;
1974 if ( g_stat ( filename, &stat_buf ) == 0 ) {
1976 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1977 timemsg = g_strdup_printf ( _("Tile File Timestamp: %s"), time_buf );
1980 timemsg = g_strdup ( _("Tile File Timestamp: Not Available") );
1982 g_array_append_val ( array, filemsg );
1983 g_array_append_val ( array, timemsg );
1986 filemsg = g_strdup_printf ( "Tile File: %s [Not Available]", filename );
1987 g_array_append_val ( array, filemsg );
1990 a_dialog_list ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Tile Information"), array, 5 );
1991 g_array_free ( array, FALSE );
1996 g_free ( filename );
1999 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2001 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2003 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
2005 if ( event->button == 1 )
2008 vik_viewport_screen_to_coord ( vvp, MAX(0, MIN(event->x, vml->dl_tool_x)), MAX(0, MIN(event->y, vml->dl_tool_y)), &ul );
2009 vik_viewport_screen_to_coord ( vvp, MIN(vik_viewport_get_width(vvp), MAX(event->x, vml->dl_tool_x)), MIN(vik_viewport_get_height(vvp), MAX ( event->y, vml->dl_tool_y ) ), &br );
2010 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
2011 vml->dl_tool_x = vml->dl_tool_y = -1;
2016 vik_viewport_screen_to_coord ( vvp, MAX(0, MIN(event->x, vml->dl_tool_x)), MAX(0, MIN(event->y, vml->dl_tool_y)), &(vml->redownload_ul) );
2017 vik_viewport_screen_to_coord ( vvp, MIN(vik_viewport_get_width(vvp), MAX(event->x, vml->dl_tool_x)), MIN(vik_viewport_get_height(vvp), MAX ( event->y, vml->dl_tool_y ) ), &(vml->redownload_br) );
2019 vml->redownload_vvp = vvp;
2021 vml->dl_tool_x = vml->dl_tool_y = -1;
2023 if ( ! vml->dl_right_click_menu ) {
2025 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
2027 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
2028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
2029 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2031 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
2032 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
2033 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2035 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
2036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
2037 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2039 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
2040 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
2041 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
2042 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
2045 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
2046 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
2052 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
2057 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2060 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2062 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2063 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
2064 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
2065 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
2066 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
2068 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
2074 // A slightly better way of defining the menu callback information
2075 // This should be easier to extend/rework compared to previously
2082 typedef gpointer menu_array_values[MA_LAST];
2084 static void download_onscreen_maps ( menu_array_values values, gint redownload )
2086 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2087 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2088 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
2090 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
2091 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
2096 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
2097 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
2099 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2100 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
2101 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
2102 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
2103 start_download_thread ( vml, vvp, &ul, &br, redownload );
2104 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
2105 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
2106 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
2107 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
2111 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
2115 static void maps_layer_download_missing_onscreen_maps ( menu_array_values values )
2117 download_onscreen_maps( values, REDOWNLOAD_NONE);
2120 static void maps_layer_download_new_onscreen_maps ( menu_array_values values )
2122 download_onscreen_maps( values, REDOWNLOAD_NEW);
2125 static void maps_layer_redownload_all_onscreen_maps ( menu_array_values values )
2127 download_onscreen_maps( values, REDOWNLOAD_ALL);
2130 static void maps_layer_about ( gpointer vml_vvp[2] )
2132 VikMapsLayer *vml = vml_vvp[0];
2133 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2135 if ( vik_map_source_get_license (map) )
2136 maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
2138 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2139 vik_map_source_get_label (map) );
2143 * maps_layer_how_many_maps:
2144 * Copied from maps_layer_download_section but without the actual download and this returns a value
2146 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
2149 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2151 if ( vik_map_source_is_direct_file_access ( map ) )
2154 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
2155 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
2156 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
2160 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
2165 mdi->map_layer_alive = TRUE;
2166 mdi->mutex = vik_mutex_new();
2167 mdi->refresh_display = FALSE;
2169 mdi->cache_dir = g_strdup ( vml->cache_dir );
2170 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
2171 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
2172 mdi->maptype = vml->maptype;
2173 mdi->cache_layout = vml->cache_layout;
2175 mdi->mapcoord = ulm;
2176 mdi->redownload = redownload;
2178 mdi->x0 = MIN(ulm.x, brm.x);
2179 mdi->xf = MAX(ulm.x, brm.x);
2180 mdi->y0 = MIN(ulm.y, brm.y);
2181 mdi->yf = MAX(ulm.y, brm.y);
2185 if ( mdi->redownload == REDOWNLOAD_ALL ) {
2186 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
2189 /* calculate how many we need */
2190 MapCoord mcoord = mdi->mapcoord;
2191 for (i = mdi->x0; i <= mdi->xf; i++) {
2193 for (j = mdi->y0; j <= mdi->yf; j++) {
2195 // Only count tiles from supported areas
2196 if ( is_in_area ( map, mcoord ) ) {
2197 get_filename ( mdi->cache_dir, mdi->cache_layout,
2198 vik_map_source_get_uniq_id(map),
2199 vik_map_source_get_name(map),
2200 ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
2201 vik_map_source_get_file_extension(map) );
2202 if ( mdi->redownload == REDOWNLOAD_NEW ) {
2203 // Assume the worst - always a new file
2204 // Absolute value would require a server lookup - but that is too slow
2208 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
2213 if ( mdi->redownload == REDOWNLOAD_BAD ) {
2214 /* see if this one is bad or what */
2216 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
2217 if (gx || (!pixbuf)) {
2221 // Other download cases already considered or just ignored
2230 gint rv = mdi->mapstoget;
2238 * maps_dialog_zoom_between:
2239 * This dialog is specific to the map layer, so it's here rather than in dialog.c
2241 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
2246 gint *selected_zoom1,
2247 gint *selected_zoom2,
2248 gchar *download_list[],
2249 gint default_download,
2250 gint *selected_download )
2252 GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
2254 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2255 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2256 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2258 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2259 GtkWidget *response_w = NULL;
2260 #if GTK_CHECK_VERSION (2, 20, 0)
2261 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2263 GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
2264 GtkWidget *zoom_combo1 = vik_combo_box_text_new();
2266 for (s = zoom_list; *s; s++)
2267 vik_combo_box_text_append ( zoom_combo1, *s );
2268 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
2270 GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
2271 GtkWidget *zoom_combo2 = vik_combo_box_text_new();
2272 for (s = zoom_list; *s; s++)
2273 vik_combo_box_text_append ( zoom_combo2, *s );
2274 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
2276 GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
2277 GtkWidget *download_combo = vik_combo_box_text_new();
2278 for (s = download_list; *s; s++)
2279 vik_combo_box_text_append ( download_combo, *s );
2280 gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
2282 GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
2283 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
2284 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
2285 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
2286 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
2287 gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
2288 gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
2290 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
2293 gtk_widget_grab_focus ( response_w );
2295 gtk_widget_show_all ( dialog );
2296 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2297 gtk_widget_destroy(dialog);
2301 // Return selected options
2302 *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
2303 *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
2304 *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
2306 gtk_widget_destroy(dialog);
2310 // My best guess of sensible limits
2311 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
2312 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2315 * Get all maps in the region for zoom levels specified by the user
2316 * Sort of similar to trw_layer_download_map_along_track_cb function
2318 static void maps_layer_download_all ( menu_array_values values )
2320 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2321 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2323 // I don't think we should allow users to hammer the servers too much...
2324 // Delibrately not allowing lowest zoom levels
2325 // Still can give massive numbers to download
2326 // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2327 gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2328 gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2330 gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2331 gint selected_download_method;
2333 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2335 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2336 if (cur_zoom == zoom_vals[default_zoom])
2339 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2341 // Default to only 2 zoom levels below the current one
2342 if (default_zoom > 1 )
2343 lower_zoom = default_zoom - 2;
2345 lower_zoom = default_zoom;
2347 // redownload method - needs to align with REDOWNLOAD* macro values
2348 gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2350 gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2352 if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2360 REDOWNLOAD_NONE, // AKA Missing
2361 &selected_download_method ) ) {
2368 // Find out new current positions
2369 gdouble min_lat, max_lat, min_lon, max_lon;
2370 VikCoord vc_ul, vc_br;
2371 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2372 struct LatLon ll_ul = { max_lat, min_lon };
2373 struct LatLon ll_br = { min_lat, max_lon };
2374 vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2375 vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2377 // Get Maps Count - call for each zoom level (in reverse)
2378 // With REDOWNLOAD_NEW this is a possible maximum
2379 // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2382 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2383 map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2386 g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2388 // Absolute protection of hammering a map server
2389 if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2390 gchar *str = g_strdup_printf (_("You are not allowed to download more than %d tiles in one go (requested %d)"), REALLY_LARGE_AMOUNT_OF_TILES, map_count);
2391 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2396 // Confirm really want to do this
2397 if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2398 gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2399 gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2405 // Get Maps - call for each zoom level (in reverse)
2406 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2407 maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2414 static void maps_layer_flush ( menu_array_values values )
2416 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2417 a_mapcache_flush_type ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)) );
2420 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2423 static menu_array_values values;
2424 values[MA_VML] = vml;
2425 values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2427 item = gtk_menu_item_new();
2428 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2429 gtk_widget_show ( item );
2431 /* Now with icons */
2432 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2433 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2434 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), values );
2435 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2436 gtk_widget_show ( item );
2438 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2439 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2440 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2441 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), values );
2442 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2443 gtk_widget_show ( item );
2446 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2447 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2448 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), values );
2449 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2450 gtk_widget_show ( item );
2452 item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2453 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2454 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), values );
2455 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2456 gtk_widget_show ( item );
2458 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2459 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_about), values );
2460 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2461 gtk_widget_show ( item );
2463 // Typical users shouldn't need to use this functionality - so debug only ATM
2465 item = gtk_image_menu_item_new_with_mnemonic ( _("Flush Map Cache") );
2466 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2467 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_flush), values );
2468 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2469 gtk_widget_show ( item );
2474 * Enable downloading maps of the current screen area either 'new' or 'everything'
2476 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2481 static menu_array_values values;
2482 values[MA_VML] = vml;
2483 values[MA_VVP] = vvp;
2486 // Get only new maps
2487 maps_layer_download_new_onscreen_maps ( values );
2489 // Redownload everything
2490 maps_layer_redownload_all_onscreen_maps ( values );