2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2005, Evan Battaglia <viking@greentorch.org>
5 * Copyright (C) 2010, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
6 * Copyright (c) 2013, Rob Norris <rw_norris@hotmail.com>
7 * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
8 * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include <gdk-pixbuf/gdk-pixdata.h>
33 #include <glib/gstdio.h>
34 #include <glib/gi18n.h>
48 #include "vikmapsourcedefault.h"
51 #include "background.h"
52 #include "preferences.h"
53 #include "vikmapslayer.h"
54 #include "icons/icons.h"
64 #define VIK_SETTINGS_MAP_MAX_TILES "maps_max_tiles"
65 static gint MAX_TILES = 1000;
67 #define VIK_SETTINGS_MAP_MIN_SHRINKFACTOR "maps_min_shrinkfactor"
68 #define VIK_SETTINGS_MAP_MAX_SHRINKFACTOR "maps_max_shrinkfactor"
69 static gdouble MAX_SHRINKFACTOR = 8.0000001; /* zoom 1 viewing 8-tiles */
70 static gdouble MIN_SHRINKFACTOR = 0.0312499; /* zoom 32 viewing 1-tiles */
72 #define VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR "maps_real_min_shrinkfactor"
73 static gdouble REAL_MIN_SHRINKFACTOR = 0.0039062499; /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
75 #define VIK_SETTINGS_MAP_SCALE_INC_UP "maps_scale_inc_up"
76 static guint SCALE_INC_UP = 2;
77 #define VIK_SETTINGS_MAP_SCALE_INC_DOWN "maps_scale_inc_down"
78 static guint SCALE_INC_DOWN = 4;
79 #define VIK_SETTINGS_MAP_SCALE_SMALLER_ZOOM_FIRST "maps_scale_smaller_zoom_first"
80 static gboolean SCALE_SMALLER_ZOOM_FIRST = TRUE;
82 /****** MAP TYPES ******/
84 static GList *__map_types = NULL;
86 #define NUM_MAP_TYPES g_list_length(__map_types)
88 /* List of label for each map type */
89 static gchar **params_maptypes = NULL;
91 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
92 static guint *params_maptypes_ids = NULL;
94 /******** MAPZOOMS *********/
96 static gchar *params_mapzooms[] = { N_("Use Viking Zoom Level"), "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "USGS 10k", "USGS 24k", "USGS 25k", "USGS 50k", "USGS 100k", "USGS 200k", "USGS 250k", NULL };
97 static gdouble __mapzooms_x[] = { 0.0, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 1.016, 2.4384, 2.54, 5.08, 10.16, 20.32, 25.4 };
98 static gdouble __mapzooms_y[] = { 0.0, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 1.016, 2.4384, 2.54, 5.08, 10.16, 20.32, 25.4 };
100 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
102 /**************************/
105 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
106 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
107 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
108 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
109 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
110 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
111 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values );
112 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
113 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
114 static void maps_layer_free ( VikMapsLayer *vml );
115 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
116 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
117 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
118 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
119 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
120 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
121 static guint map_uniq_id_to_index ( guint uniq_id );
124 static VikLayerParamScale params_scales[] = {
125 /* min, max, step, digits (decimal places) */
126 { 0, 255, 3, 0 }, /* alpha */
129 static VikLayerParamData id_default ( void ) { return VIK_LPD_UINT ( MAP_ID_MAPBOX_OUTDOORS ); }
130 static VikLayerParamData directory_default ( void )
132 VikLayerParamData data;
133 VikLayerParamData *pref = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir");
134 if (pref) data.s = g_strdup ( pref->s ); else data.s = "";
137 static VikLayerParamData file_default ( void )
139 VikLayerParamData data;
143 static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
144 static VikLayerParamData mapzoom_default ( void ) { return VIK_LPD_UINT ( 0 ); }
146 static gchar *cache_types[] = { "Viking", N_("OSM"), NULL };
147 static VikMapsCacheLayout cache_layout_default_value = VIK_MAPS_CACHE_LAYOUT_VIKING;
148 static VikLayerParamData cache_layout_default ( void ) { return VIK_LPD_UINT ( cache_layout_default_value ); }
150 VikLayerParam maps_layer_params[] = {
151 // NB mode => id - But can't break file format just to rename something better
152 { VIK_LAYER_MAPS, "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL, id_default, NULL, NULL },
153 { VIK_LAYER_MAPS, "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, NULL, directory_default, NULL, NULL },
154 { VIK_LAYER_MAPS, "cache_type", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Cache Layout:"), VIK_LAYER_WIDGET_COMBOBOX, cache_types, NULL,
155 N_("This determines the tile storage layout on disk"), cache_layout_default, NULL, NULL },
156 { VIK_LAYER_MAPS, "mapfile", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Map File:"), VIK_LAYER_WIDGET_FILEENTRY, GINT_TO_POINTER(VF_FILTER_MBTILES), NULL,
157 N_("An MBTiles file. Only applies when the map type method is 'MBTiles'"), file_default, NULL, NULL },
158 { VIK_LAYER_MAPS, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
159 N_("Control the Alpha value for transparency effects"), alpha_default, NULL, NULL },
160 { VIK_LAYER_MAPS, "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
161 { VIK_LAYER_MAPS, "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
162 N_("Using this option avoids attempting to update already acquired tiles. This can be useful if you want to restrict the network usage, without having to resort to manual control. Only applies when 'Autodownload Maps' is on."), vik_lpd_false_default, NULL, NULL },
163 { VIK_LAYER_MAPS, "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
164 N_("Determines the method of displaying map tiles for the current zoom level. 'Viking Zoom Level' uses the best matching level, otherwise setting a fixed value will always use map tiles of the specified value regardless of the actual zoom level."),
165 mapzoom_default, NULL, NULL },
180 void maps_layer_set_autodownload_default ( gboolean autodownload )
182 // Set appropriate function
184 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_true_default;
186 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_false_default;
189 void maps_layer_set_cache_default ( VikMapsCacheLayout layout )
191 // Override default value returned by the default param function
192 cache_layout_default_value = layout;
195 static VikToolInterface maps_tools[] = {
196 { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
197 (VikToolConstructorFunc) maps_layer_download_create,
201 (VikToolMouseFunc) maps_layer_download_click,
203 (VikToolMouseFunc) maps_layer_download_release,
206 GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf, NULL },
209 VikLayerInterface vik_maps_layer_interface = {
213 &vikmapslayer_pixbuf,
216 sizeof(maps_tools) / sizeof(maps_tools[0]),
225 (VikLayerFuncCreate) maps_layer_new,
226 (VikLayerFuncRealize) NULL,
227 (VikLayerFuncPostRead) maps_layer_post_read,
228 (VikLayerFuncFree) maps_layer_free,
230 (VikLayerFuncProperties) NULL,
231 (VikLayerFuncDraw) maps_layer_draw,
232 (VikLayerFuncChangeCoordMode) NULL,
234 (VikLayerFuncGetTimestamp) NULL,
236 (VikLayerFuncSetMenuItemsSelection) NULL,
237 (VikLayerFuncGetMenuItemsSelection) NULL,
239 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
240 (VikLayerFuncSublayerAddMenuItems) NULL,
242 (VikLayerFuncSublayerRenameRequest) NULL,
243 (VikLayerFuncSublayerToggleVisible) NULL,
244 (VikLayerFuncSublayerTooltip) NULL,
245 (VikLayerFuncLayerTooltip) maps_layer_tooltip,
246 (VikLayerFuncLayerSelected) NULL,
248 (VikLayerFuncMarshall) maps_layer_marshall,
249 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
251 (VikLayerFuncSetParam) maps_layer_set_param,
252 (VikLayerFuncGetParam) maps_layer_get_param,
253 (VikLayerFuncChangeParam) maps_layer_change_param,
255 (VikLayerFuncReadFileData) NULL,
256 (VikLayerFuncWriteFileData) NULL,
258 (VikLayerFuncDeleteItem) NULL,
259 (VikLayerFuncCutItem) NULL,
260 (VikLayerFuncCopyItem) NULL,
261 (VikLayerFuncPasteItem) NULL,
262 (VikLayerFuncFreeCopiedItem) NULL,
263 (VikLayerFuncDragDropRequest) NULL,
265 (VikLayerFuncSelectClick) NULL,
266 (VikLayerFuncSelectMove) NULL,
267 (VikLayerFuncSelectRelease) NULL,
268 (VikLayerFuncSelectedViewportMenu) NULL,
271 struct _VikMapsLayer {
275 VikMapsCacheLayout cache_layout;
278 gdouble xmapzoom, ymapzoom;
280 gboolean autodownload;
281 gboolean adl_only_missing;
282 VikCoord *last_center;
286 gint dl_tool_x, dl_tool_y;
288 GtkMenu *dl_right_click_menu;
289 VikCoord redownload_ul, redownload_br; /* right click menu only */
290 VikViewport *redownload_vvp;
292 #ifdef HAVE_SQLITE3_H
297 enum { REDOWNLOAD_NONE = 0, /* download only missing maps */
298 REDOWNLOAD_BAD, /* download missing and bad maps */
299 REDOWNLOAD_NEW, /* download missing maps that are newer on server only */
300 REDOWNLOAD_ALL, /* download all maps */
301 DOWNLOAD_OR_REFRESH }; /* download missing maps and refresh cache */
303 static VikLayerParam prefs[] = {
304 { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Default map layer directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, N_("Choose a directory to store cached Map tiles for this layer"), NULL, NULL, NULL },
307 void maps_layer_init ()
309 VikLayerParamData tmp;
310 tmp.s = maps_layer_default_dir();
311 a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
313 gint max_tiles = MAX_TILES;
314 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_MAX_TILES, &max_tiles ) )
315 MAX_TILES = max_tiles;
318 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MIN_SHRINKFACTOR, &gdtmp ) )
319 MIN_SHRINKFACTOR = gdtmp;
321 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MAX_SHRINKFACTOR, &gdtmp ) )
322 MAX_SHRINKFACTOR = gdtmp;
324 if ( a_settings_get_double ( VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR, &gdtmp ) )
325 REAL_MIN_SHRINKFACTOR = gdtmp;
328 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_UP, &gitmp ) )
329 SCALE_INC_UP = gitmp;
331 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_DOWN, &gitmp ) )
332 SCALE_INC_DOWN = gitmp;
334 gboolean gbtmp = TRUE;
335 if ( a_settings_get_boolean ( VIK_SETTINGS_MAP_SCALE_SMALLER_ZOOM_FIRST, &gbtmp ) )
336 SCALE_SMALLER_ZOOM_FIRST = gbtmp;
340 /****************************************/
341 /******** MAPS LAYER TYPES **************/
342 /****************************************/
344 void _add_map_source ( guint16 id, const char *label, VikMapSource *map )
348 len = g_strv_length (params_maptypes);
350 params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
351 params_maptypes[len] = g_strdup (label);
352 params_maptypes[len+1] = NULL;
355 params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
356 params_maptypes_ids[len] = id;
357 params_maptypes_ids[len+1] = 0;
359 /* We have to clone */
360 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
361 /* Register the clone in the list */
362 __map_types = g_list_append(__map_types, clone);
365 We have to ensure the mode LayerParam references the up-to-date
369 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
370 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
372 maps_layer_params[0].widget_data = params_maptypes;
373 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
376 void _update_map_source ( const char *label, VikMapSource *map, int index )
378 GList *item = g_list_nth (__map_types, index);
379 g_object_unref (item->data);
380 item->data = g_object_ref (map);
381 /* Change previous data */
382 g_free (params_maptypes[index]);
383 params_maptypes[index] = g_strdup (label);
387 * maps_layer_register_map_source:
388 * @map: the new VikMapSource
390 * Register a new VikMapSource.
391 * Override existing one (equality of id).
393 void maps_layer_register_map_source ( VikMapSource *map )
395 g_assert(map != NULL);
397 guint16 id = vik_map_source_get_uniq_id(map);
398 const char *label = vik_map_source_get_label(map);
399 g_assert(label != NULL);
401 int previous = map_uniq_id_to_index (id);
402 if (previous != NUM_MAP_TYPES)
404 _update_map_source (label, map, previous);
408 _add_map_source (id, label, map);
412 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
413 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
414 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
417 * vik_maps_layer_get_map_type:
419 * Returns the actual map id (rather than the internal type index value)
421 guint vik_maps_layer_get_map_type(VikMapsLayer *vml)
423 return MAPS_LAYER_NTH_ID(vml->maptype);
427 * vik_maps_layer_set_map_type:
430 void vik_maps_layer_set_map_type(VikMapsLayer *vml, guint map_type)
432 gint maptype = map_uniq_id_to_index(map_type);
433 if ( maptype == NUM_MAP_TYPES )
434 g_warning(_("Unknown map type"));
436 vml->maptype = maptype;
440 * vik_maps_layer_get_default_map_type:
443 guint vik_maps_layer_get_default_map_type ()
445 VikLayerInterface *vli = vik_layer_get_interface ( VIK_LAYER_MAPS );
446 VikLayerParamData vlpd = a_layer_defaults_get ( vli->fixed_layer_name, "mode", VIK_LAYER_PARAM_UINT );
452 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
454 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
457 /****************************************/
458 /******** CACHE DIR STUFF ***************/
459 /****************************************/
461 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
462 #define DIRECTDIRACCESS_WITH_NAME "%s%s" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
463 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
464 #define MAPS_CACHE_DIR maps_layer_default_dir()
468 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
469 #define LOCAL_MAPS_DIR "VIKING-MAPS"
470 #elif defined __APPLE__
472 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
473 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
476 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
477 #define LOCAL_MAPS_DIR ".viking-maps"
480 gchar *maps_layer_default_dir ()
482 static gchar *defaultdir = NULL;
485 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
486 const gchar *mapdir = g_getenv("VIKING_MAPS");
488 defaultdir = g_strdup ( mapdir );
489 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
490 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
492 const gchar *home = g_get_home_dir();
493 if (!home || g_access(home, W_OK))
494 home = g_get_home_dir ();
496 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
498 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
500 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
502 /* Add the separator at the end */
503 gchar *tmp = defaultdir;
504 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
507 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
512 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
514 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
516 if ( g_mkdir ( vml->cache_dir, 0777 ) != 0 )
517 g_warning ( "%s: Failed to create directory %s", __FUNCTION__, vml->cache_dir );
521 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
523 g_assert ( vml != NULL);
524 g_free ( vml->cache_dir );
525 vml->cache_dir = NULL;
526 const gchar *mydir = dir;
528 if ( dir == NULL || dir[0] == '\0' )
530 if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
531 mydir = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s;
534 gchar *canonical_dir = vu_get_canonical_filename ( VIK_LAYER(vml), mydir );
536 // Ensure cache_dir always ends with a separator
537 guint len = strlen(canonical_dir);
538 // Unless the dir is not valid
541 if ( canonical_dir[len-1] != G_DIR_SEPARATOR )
543 vml->cache_dir = g_strconcat ( canonical_dir, G_DIR_SEPARATOR_S, NULL );
544 g_free ( canonical_dir );
547 vml->cache_dir = canonical_dir;
550 maps_layer_mkdir_if_default_dir ( vml );
554 static void maps_layer_set_file ( VikMapsLayer *vml, const gchar *name )
557 g_free (vml->filename);
558 vml->filename = g_strdup (name);
561 /****************************************/
562 /******** GOBJECT STUFF *****************/
563 /****************************************/
565 GType vik_maps_layer_get_type ()
567 static GType vml_type = 0;
571 static const GTypeInfo vml_info =
573 sizeof (VikMapsLayerClass),
574 NULL, /* base_init */
575 NULL, /* base_finalize */
576 NULL, /* class init */
577 NULL, /* class_finalize */
578 NULL, /* class_data */
579 sizeof (VikMapsLayer),
581 NULL /* instance init */
583 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
589 /****************************************/
590 /************** PARAMETERS **************/
591 /****************************************/
593 static guint map_index_to_uniq_id (guint16 index)
595 g_assert ( index < NUM_MAP_TYPES );
596 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
599 static guint map_uniq_id_to_index ( guint uniq_id )
602 for ( i = 0; i < NUM_MAP_TYPES; i++ )
603 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
605 return NUM_MAP_TYPES; /* no such thing */
608 #define VIK_SETTINGS_MAP_LICENSE_SHOWN "map_license_shown"
611 * Convenience function to display the license
613 static void maps_show_license ( GtkWindow *parent, VikMapSource *map )
615 a_dialog_license ( parent,
616 vik_map_source_get_label (map),
617 vik_map_source_get_license (map),
618 vik_map_source_get_license_url (map) );
621 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
625 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
626 case PARAM_CACHE_LAYOUT: if ( data.u < VIK_MAPS_CACHE_LAYOUT_NUM ) vml->cache_layout = data.u; break;
627 case PARAM_FILE: maps_layer_set_file ( vml, data.s ); break;
628 case PARAM_MAPTYPE: {
629 gint maptype = map_uniq_id_to_index(data.u);
630 if ( maptype == NUM_MAP_TYPES )
631 g_warning(_("Unknown map type"));
633 vml->maptype = maptype;
635 // When loading from a file don't need the license reminder - ensure it's saved into the 'seen' list
636 if ( is_file_operation ) {
637 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u );
640 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
641 if (vik_map_source_get_license (map) != NULL) {
642 // Check if licence for this map type has been shown before
643 if ( ! a_settings_get_integer_list_contains ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u ) ) {
645 maps_show_license ( VIK_GTK_WINDOW_FROM_WIDGET(vvp), map );
646 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, data.u );
653 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
654 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
655 case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
656 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
657 vml->mapzoom_id = data.u;
658 vml->xmapzoom = __mapzooms_x [data.u];
659 vml->ymapzoom = __mapzooms_y [data.u];
660 }else g_warning (_("Unknown Map Zoom")); break;
666 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
668 VikLayerParamData rv;
671 case PARAM_CACHE_DIR:
673 gboolean set = FALSE;
674 /* Only save a blank when the map cache location equals the default
675 On reading in, when it is blank then the default is reconstructed
676 Since the default changes dependent on the user and OS, it means the resultant file is more portable */
677 if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
681 else if ( is_file_operation && vml->cache_dir ) {
682 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
683 gchar *cwd = g_get_current_dir();
685 rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
686 if ( !rv.s ) rv.s = "";
692 rv.s = vml->cache_dir ? vml->cache_dir : "";
695 case PARAM_CACHE_LAYOUT: rv.u = vml->cache_layout; break;
696 case PARAM_FILE: rv.s = vml->filename; break;
697 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
698 case PARAM_ALPHA: rv.u = vml->alpha; break;
699 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
700 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
701 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
707 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values )
709 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
710 // Alter sensitivity of download option widgets according to the maptype setting.
711 case PARAM_MAPTYPE: {
713 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
714 // Is it *not* the OSM On Disk Tile Layout or the MBTiles type or the OSM Metatiles type
715 gboolean sensitive = ( MAP_ID_OSM_ON_DISK != vlpd.u &&
716 MAP_ID_MBTILES != vlpd.u &&
717 MAP_ID_OSM_METATILES != vlpd.u );
718 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
719 GtkWidget **ww2 = values[UI_CHG_LABELS];
720 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
721 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
722 GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
723 GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
724 // Depends on autodownload value
725 gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
726 if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
727 if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
728 if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
729 if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
731 // Cache type not applicable either
732 GtkWidget *w9 = ww1[PARAM_CACHE_LAYOUT];
733 GtkWidget *w10 = ww2[PARAM_CACHE_LAYOUT];
734 if ( w9 ) gtk_widget_set_sensitive ( w9, sensitive );
735 if ( w10 ) gtk_widget_set_sensitive ( w10, sensitive );
737 // File only applicable for MBTiles type
738 // Directory for all other types
739 sensitive = ( MAP_ID_MBTILES == vlpd.u);
740 GtkWidget *w5 = ww1[PARAM_FILE];
741 GtkWidget *w6 = ww2[PARAM_FILE];
742 GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
743 GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
744 if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
745 if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
746 if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
747 if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
752 // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
753 case PARAM_AUTODOWNLOAD: {
755 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
756 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
757 GtkWidget **ww2 = values[UI_CHG_LABELS];
758 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
759 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
760 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
761 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
768 /****************************************/
769 /****** CREATING, COPYING, FREEING ******/
770 /****************************************/
772 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
774 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
775 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
777 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
779 vml->dl_tool_x = vml->dl_tool_y = -1;
780 vml->last_center = NULL;
781 vml->last_xmpp = 0.0;
782 vml->last_ympp = 0.0;
784 vml->dl_right_click_menu = NULL;
785 vml->filename = NULL;
789 static void maps_layer_free ( VikMapsLayer *vml )
791 g_free ( vml->cache_dir );
792 vml->cache_dir = NULL;
793 if ( vml->dl_right_click_menu )
794 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
795 g_free(vml->last_center);
796 vml->last_center = NULL;
797 g_free ( vml->filename );
798 vml->filename = NULL;
800 #ifdef HAVE_SQLITE3_H
801 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
802 if ( vik_map_source_is_mbtiles ( map ) ) {
803 if ( vml->mbtiles ) {
804 int ans = sqlite3_close ( vml->mbtiles );
805 if ( ans != SQLITE_OK ) {
806 // Only to console for information purposes only
807 g_warning ( "SQL Close problem: %d", ans );
814 static void maps_layer_mbtiles_open ( VikMapsLayer *vml, VikViewport *vp, VikMapSource *map )
816 #ifdef HAVE_SQLITE3_H
818 if ( vik_map_source_is_mbtiles ( map ) ) {
819 int ans = sqlite3_open_v2 ( vml->filename,
821 SQLITE_OPEN_READONLY,
823 if ( ans != SQLITE_OK ) {
824 // That didn't work, so here's why:
825 g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
827 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
828 _("Failed to open MBTiles file: %s"),
836 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
838 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
839 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
843 /* If this method is not called in file reading context
844 * it is called in GUI context.
845 * So, we can check if we have to inform the user about inconsistency */
846 VikViewportDrawMode vp_drawmode;
847 vp_drawmode = vik_viewport_get_drawmode ( vp );
849 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
850 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
851 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
852 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
857 // Performed in post read as we now know the map type
858 maps_layer_mbtiles_open ( vml, vp, map );
860 // If the on Disk OSM Tile Layout type
861 if ( vik_map_source_get_uniq_id(map) == MAP_ID_OSM_ON_DISK ) {
862 // Copy the directory into filename
863 // thus the mapcache look up will be unique when using more than one of these map types
864 g_free ( vml->filename );
865 vml->filename = g_strdup (vml->cache_dir);
869 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
871 return vik_maps_layer_get_map_label ( vml );
874 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
876 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
879 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
881 VikMapsLayer *rv = maps_layer_new ( vvp );
882 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
883 maps_layer_post_read ( VIK_LAYER(rv), vvp, FALSE );
887 /*********************/
888 /****** DRAWING ******/
889 /*********************/
891 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
894 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
895 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
896 g_object_unref ( G_OBJECT(pixbuf) );
900 #ifdef HAVE_SQLITE3_H
902 static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
904 g_warning ( "Found %d columns", cols );
906 for ( i = 0; i < cols; i++ ) {
907 g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
916 static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
918 GdkPixbuf *pixbuf = NULL;
920 // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
921 gint flip_y = (gint) pow(2, zoom)-1 - yy;
922 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 );
924 gboolean finished = FALSE;
926 sqlite3_stmt *sql_stmt = NULL;
927 int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
928 if ( ans != SQLITE_OK ) {
929 g_warning ( "%s: %s - %d: %s", __FUNCTION__, "prepare failure", ans, statement );
933 while ( !finished ) {
934 ans = sqlite3_step ( sql_stmt );
937 // Get tile_data blob
938 int count = sqlite3_column_count(sql_stmt);
940 g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
944 const void *data = sqlite3_column_blob ( sql_stmt, 0 );
945 int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
947 g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
951 // Convert these blob bytes into a pixbuf via these streaming operations
952 GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
953 GError *error = NULL;
954 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
956 g_warning ( "%s: %s", __FUNCTION__, error->message );
957 g_error_free ( error );
959 g_input_stream_close ( stream, NULL, NULL );
965 // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
967 // and give up on any errors
968 if ( ans != SQLITE_DONE )
969 g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
974 (void)sqlite3_finalize ( sql_stmt );
976 g_free ( statement );
982 static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
984 GdkPixbuf *pixbuf = NULL;
986 #ifdef HAVE_SQLITE3_H
987 if ( vml->mbtiles ) {
989 gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
991 int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
992 if ( ans != SQLITE_OK ) {
993 // Only to console for information purposes only
994 g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
995 sqlite3_free( errMsg );
997 g_free ( statement );
1000 // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
1001 // Hence this specific function
1002 pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
1009 static GdkPixbuf *get_pixbuf_from_metatile ( VikMapsLayer *vml, gint xx, gint yy, gint zz )
1011 const int tile_max = METATILE_MAX_SIZE;
1012 char err_msg[PATH_MAX];
1017 buf = g_malloc(tile_max);
1023 len = metatile_read(vml->cache_dir, xx, yy, zz, buf, tile_max, &compressed, err_msg);
1027 // Not handled yet - I don't think this is used often - so implement later if necessary
1028 g_warning ( "Compressed metatiles not implemented:%s\n", __FUNCTION__);
1033 // Convert these buf bytes into a pixbuf via these streaming operations
1034 GdkPixbuf *pixbuf = NULL;
1036 GInputStream *stream = g_memory_input_stream_new_from_data ( buf, len, NULL );
1037 GError *error = NULL;
1038 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
1040 g_warning ( "%s: %s", __FUNCTION__, error->message );
1041 g_error_free ( error );
1043 g_input_stream_close ( stream, NULL, NULL );
1050 g_warning ( "FAILED:%s %s", __FUNCTION__, err_msg);
1056 * Caller has to decrease reference counter of returned
1057 * GdkPixbuf, when buffer is no longer needed.
1059 static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
1061 // Apply alpha setting
1062 if ( pixbuf && vml->alpha < 255 )
1063 pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
1065 if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
1066 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
1069 a_mapcache_add ( pixbuf, (mapcache_extra_t) {0.0}, mapcoord->x, mapcoord->y,
1070 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
1071 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1076 static void get_filename ( const gchar *cache_dir,
1077 VikMapsCacheLayout cl,
1084 gchar *filename_buf,
1086 const gchar* file_extension )
1089 case VIK_MAPS_CACHE_LAYOUT_OSM:
1091 if ( g_strcmp0 ( cache_dir, MAPS_CACHE_DIR ) )
1092 // Cache dir not the default - assume it's been directed somewhere specific
1093 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1095 // Using default cache - so use the map name in the directory path
1096 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS_WITH_NAME, cache_dir, name, (17 - scale), x, y, file_extension );
1099 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1102 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE, cache_dir, id, scale, z, x, y );
1108 * Caller has to decrease reference counter of returned
1109 * GdkPixbuf, when buffer is no longer needed.
1111 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, guint16 id, const gchar* mapname, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
1116 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
1117 id, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1120 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1121 if ( vik_map_source_is_direct_file_access(map) ) {
1122 // ATM MBTiles must be 'a direct access type'
1123 if ( vik_map_source_is_mbtiles(map) ) {
1124 pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1125 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1126 // return now to avoid file tests that aren't appropriate for this map type
1129 else if ( vik_map_source_is_osm_meta_tiles(map) ) {
1130 pixbuf = get_pixbuf_from_metatile ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1131 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1135 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, NULL,
1136 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1137 vik_map_source_get_file_extension(map) );
1140 get_filename ( vml->cache_dir, vml->cache_layout, id, mapname,
1141 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1142 vik_map_source_get_file_extension(map) );
1144 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1147 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
1149 /* free the pixbuf on error */
1152 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE ) {
1154 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1155 gchar* msg = g_strdup_printf ( _("Couldn't open image file: %s"), gx->message );
1156 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1161 g_error_free ( gx );
1163 g_object_unref ( G_OBJECT(pixbuf) );
1166 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1173 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
1175 const VikCoord *center = vik_viewport_get_center ( vvp );
1177 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
1178 /* D'n'D pan in action: do not download */
1181 // Don't attempt to download unsupported zoom levels
1182 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1183 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1184 guint8 zl = map_utils_mpp_to_zoom_level ( xzoom );
1185 if ( zl < vik_map_source_get_zoom_min(map) || zl > vik_map_source_get_zoom_max(map) )
1188 if (vml->last_center == NULL) {
1189 VikCoord *new_center = g_malloc(sizeof(VikCoord));
1190 *new_center = *center;
1191 vml->last_center = new_center;
1192 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1193 vml->last_ympp = vik_viewport_get_ympp(vvp);
1197 /* TODO: perhaps vik_coord_diff() */
1198 if (vik_coord_equals(vml->last_center, center)
1199 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1200 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1203 *(vml->last_center) = *center;
1204 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1205 vml->last_ympp = vik_viewport_get_ympp(vvp);
1212 gboolean try_draw_scale_down (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1213 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1217 for (scale_inc = 1; scale_inc <= SCALE_INC_DOWN; scale_inc++) {
1218 // Try with smaller zooms
1219 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
1220 MapCoord ulm2 = ulm;
1221 ulm2.x = ulm.x / scale_factor;
1222 ulm2.y = ulm.y / scale_factor;
1223 ulm2.scale = ulm.scale + scale_inc;
1224 pixbuf = get_pixbuf ( vml, id, mapname, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1226 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1227 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1228 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1229 g_object_unref(pixbuf);
1239 gboolean try_draw_scale_up (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1240 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1243 // Try with bigger zooms
1245 for (scale_dec = 1; scale_dec <= SCALE_INC_UP; scale_dec++) {
1247 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
1248 MapCoord ulm2 = ulm;
1249 ulm2.x = ulm.x * scale_factor;
1250 ulm2.y = ulm.y * scale_factor;
1251 ulm2.scale = ulm.scale - scale_dec;
1252 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1253 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1254 MapCoord ulm3 = ulm2;
1257 pixbuf = get_pixbuf ( vml, id, mapname, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1261 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1262 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1263 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1264 g_object_unref(pixbuf);
1273 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1276 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1277 gdouble yzoom = vik_viewport_get_ympp ( vvp );
1278 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
1279 gboolean existence_only = FALSE;
1281 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1282 xshrinkfactor = vml->xmapzoom / xzoom;
1283 yshrinkfactor = vml->ymapzoom / yzoom;
1284 xzoom = vml->xmapzoom;
1285 yzoom = vml->xmapzoom;
1286 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1287 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
1288 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1289 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
1290 existence_only = TRUE;
1293 // Report the reason for not drawing
1294 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1295 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));
1296 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1305 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1306 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1307 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
1311 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1312 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
1313 guint16 id = vik_map_source_get_uniq_id(map);
1314 const gchar *mapname = vik_map_source_get_name(map);
1317 gint xx, yy, width, height;
1320 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1321 // which can happen when using a small fixed zoom level and viewing large areas.
1322 // Also prevents very large number of tile download requests
1323 gint tiles = (xmax-xmin) * (ymax-ymin);
1324 if ( tiles > MAX_TILES ) {
1325 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1326 existence_only = TRUE;
1329 guint max_path_len = strlen(vml->cache_dir) + 40;
1330 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1332 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
1333 g_debug("%s: Starting autodownload", __FUNCTION__);
1334 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
1335 // Try to download newer tiles
1336 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1338 // Download only missing tiles
1339 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
1342 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
1343 for ( x = xmin; x <= xmax; x++ ) {
1344 for ( y = ymin; y <= ymax; y++ ) {
1347 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1349 width = gdk_pixbuf_get_width ( pixbuf );
1350 height = gdk_pixbuf_get_height ( pixbuf );
1352 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1353 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1357 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1358 g_object_unref(pixbuf);
1362 } else { /* tilesize is known, don't have to keep converting coords */
1363 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1364 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
1365 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1366 gint tilesize_x_ceil = ceil ( tilesize_x );
1367 gint tilesize_y_ceil = ceil ( tilesize_y );
1368 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1369 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
1370 gint xx_tmp, yy_tmp;
1371 gint base_yy, xend, yend;
1373 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1374 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1376 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1377 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1378 xx = xx_tmp; yy = yy_tmp;
1379 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1380 * eg if tile size 128, shrinkfactor 0.333 */
1381 xx -= (tilesize_x/2);
1382 base_yy = yy - (tilesize_y/2);
1384 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1386 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1390 if ( existence_only ) {
1391 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1392 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, vik_map_source_get_name(map),
1393 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1395 get_filename ( vml->cache_dir, vml->cache_layout, id, vik_map_source_get_name(map),
1396 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1398 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1399 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1400 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1403 // Try correct scale first
1404 int scale_factor = 1;
1405 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1407 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1408 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1409 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1410 g_object_unref(pixbuf);
1413 // Otherwise try different scales
1414 if ( SCALE_SMALLER_ZOOM_FIRST ) {
1415 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) ) {
1416 try_draw_scale_up(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1420 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) ) {
1421 try_draw_scale_down(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1432 // ATM Only show tile grid lines in extreme debug mode
1433 if ( vik_debug && vik_verbose ) {
1434 /* Grid drawing here so it gets drawn on top of the map */
1435 /* Thus loop around x & y again, but this time separately */
1436 /* Only showing grid for the current scale */
1437 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
1438 /* Draw single grid lines across the whole screen */
1439 gint width = vik_viewport_get_width(vvp);
1440 gint height = vik_viewport_get_height(vvp);
1441 xx = xx_tmp; yy = yy_tmp;
1442 gint base_xx = xx - (tilesize_x/2);
1443 base_yy = yy - (tilesize_y/2);
1446 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1447 vik_viewport_draw_line ( vvp, black_gc, xx, base_yy, xx, height );
1452 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1453 vik_viewport_draw_line ( vvp, black_gc, base_xx, yy, width, yy );
1459 g_free ( path_buf );
1463 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1465 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
1470 gdouble level = vik_viewport_get_zoom ( vvp );
1472 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1473 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
1476 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1477 vik_viewport_add_logo ( vvp, logo );
1479 /* get corner coords */
1480 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1481 /* UTM multi-zone stuff by Kit Transue */
1482 gchar leftmost_zone, rightmost_zone, i;
1483 leftmost_zone = vik_viewport_leftmost_zone( vvp );
1484 rightmost_zone = vik_viewport_rightmost_zone( vvp );
1485 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1486 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1487 maps_layer_draw_section ( vml, vvp, &ul, &br );
1491 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1492 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1494 maps_layer_draw_section ( vml, vvp, &ul, &br );
1499 /*************************/
1500 /****** DOWNLOADING ******/
1501 /*************************/
1503 /* pass along data to thread, exists even if layer is deleted. */
1506 gchar *filename_buf;
1507 VikMapsCacheLayout cache_layout;
1508 gint x0, y0, xf, yf;
1514 gboolean refresh_display;
1517 gboolean map_layer_alive;
1521 static void mdi_free ( MapDownloadInfo *mdi )
1523 vik_mutex_free(mdi->mutex);
1524 g_free ( mdi->cache_dir );
1525 mdi->cache_dir = NULL;
1526 g_free ( mdi->filename_buf );
1527 mdi->filename_buf = NULL;
1531 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1533 MapDownloadInfo *mdi = ptr;
1534 g_mutex_lock(mdi->mutex);
1535 mdi->map_layer_alive = FALSE;
1536 g_mutex_unlock(mdi->mutex);
1539 static gboolean is_in_area (VikMapSource *map, MapCoord mc)
1542 vik_map_source_mapcoord_to_center_coord ( map, &mc, &vc );
1545 tl.lat = vik_map_source_get_lat_max(map);
1546 tl.lon = vik_map_source_get_lon_min(map);
1548 br.lat = vik_map_source_get_lat_min(map);
1549 br.lon = vik_map_source_get_lon_max(map);
1551 vik_coord_load_from_latlon (&vctl, VIK_COORD_LATLON, &tl);
1553 vik_coord_load_from_latlon (&vcbr, VIK_COORD_LATLON, &br);
1555 return vik_coord_inside ( &vc, &vctl, &vcbr );
1558 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1560 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1562 MapCoord mcoord = mdi->mapcoord;
1564 for ( x = mdi->x0; x <= mdi->xf; x++ )
1567 for ( y = mdi->y0; y <= mdi->yf; y++ )
1570 // Only attempt to download a tile from supported areas
1571 if ( is_in_area ( MAPS_LAYER_NTH_TYPE(mdi->maptype), mcoord ) )
1573 gboolean remove_mem_cache = FALSE;
1574 gboolean need_download = FALSE;
1576 get_filename ( mdi->cache_dir, mdi->cache_layout,
1577 vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1578 vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1579 mdi->mapcoord.scale, mdi->mapcoord.z, x, y, mdi->filename_buf, mdi->maxlen,
1580 vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1583 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1585 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1589 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1590 need_download = TRUE;
1591 remove_mem_cache = TRUE;
1593 } else { /* in case map file already exists */
1594 switch (mdi->redownload) {
1595 case REDOWNLOAD_NONE:
1598 case REDOWNLOAD_BAD:
1600 /* see if this one is bad or what */
1602 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1603 if (gx || (!pixbuf)) {
1604 if ( g_remove ( mdi->filename_buf ) )
1605 g_warning ( "REDOWNLOAD failed to remove: %s", mdi->filename_buf );
1606 need_download = TRUE;
1607 remove_mem_cache = TRUE;
1608 g_error_free ( gx );
1611 g_object_unref ( pixbuf );
1616 case REDOWNLOAD_NEW:
1617 need_download = TRUE;
1618 remove_mem_cache = TRUE;
1621 case REDOWNLOAD_ALL:
1622 /* FIXME: need a better way than to erase file in case of server/network problem */
1623 if ( g_remove ( mdi->filename_buf ) )
1624 g_warning ( "REDOWNLOAD failed to remove: %s", mdi->filename_buf );
1625 need_download = TRUE;
1626 remove_mem_cache = TRUE;
1629 case DOWNLOAD_OR_REFRESH:
1630 remove_mem_cache = TRUE;
1634 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1638 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1640 if (need_download) {
1641 DownloadResult_t dr = vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle);
1643 case DOWNLOAD_PARAMETERS_ERROR:
1644 case DOWNLOAD_HTTP_ERROR:
1645 case DOWNLOAD_CONTENT_ERROR: {
1646 // TODO: ?? count up the number of download errors somehow...
1647 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Failed to download tile") );
1648 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1652 case DOWNLOAD_FILE_WRITE_ERROR: {
1653 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Unable to save tile") );
1654 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1658 case DOWNLOAD_SUCCESS:
1659 case DOWNLOAD_NOT_REQUIRED:
1665 g_mutex_lock(mdi->mutex);
1666 if (remove_mem_cache)
1667 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 );
1668 if (mdi->refresh_display && mdi->map_layer_alive) {
1669 /* TODO: check if it's on visible area */
1670 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1672 g_mutex_unlock(mdi->mutex);
1673 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1677 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1678 g_mutex_lock(mdi->mutex);
1679 if (mdi->map_layer_alive)
1680 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1681 g_mutex_unlock(mdi->mutex);
1685 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1687 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1689 get_filename ( mdi->cache_dir, mdi->cache_layout,
1690 vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1691 vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1692 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y, mdi->filename_buf, mdi->maxlen,
1693 vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1694 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1696 if ( g_remove ( mdi->filename_buf ) )
1697 g_warning ( "Cleanup failed to remove: %s", mdi->filename_buf );
1702 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1704 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1705 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1707 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1709 // Don't ever attempt download on direct access
1710 if ( vik_map_source_is_direct_file_access ( map ) )
1713 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1714 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1716 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1721 mdi->map_layer_alive = TRUE;
1722 mdi->mutex = vik_mutex_new();
1723 mdi->refresh_display = TRUE;
1725 /* cache_dir and buffer for dest filename */
1726 mdi->cache_dir = g_strdup ( vml->cache_dir );
1727 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1728 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1729 mdi->cache_layout = vml->cache_layout;
1730 mdi->maptype = vml->maptype;
1732 mdi->mapcoord = ulm;
1733 mdi->redownload = redownload;
1735 mdi->x0 = MIN(ulm.x, brm.x);
1736 mdi->xf = MAX(ulm.x, brm.x);
1737 mdi->y0 = MIN(ulm.y, brm.y);
1738 mdi->yf = MAX(ulm.y, brm.y);
1742 MapCoord mcoord = mdi->mapcoord;
1744 if ( mdi->redownload ) {
1745 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1747 /* calculate how many we need */
1748 for ( a = mdi->x0; a <= mdi->xf; a++ )
1751 for ( b = mdi->y0; b <= mdi->yf; b++ )
1754 // Only count tiles from supported areas
1755 if ( is_in_area (map, mcoord) )
1757 get_filename ( mdi->cache_dir, mdi->cache_layout,
1758 vik_map_source_get_uniq_id(map),
1759 vik_map_source_get_name(map),
1760 ulm.scale, ulm.z, a, b, mdi->filename_buf, mdi->maxlen,
1761 vik_map_source_get_file_extension(map) );
1762 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1769 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1771 if ( mdi->mapstoget )
1773 const gchar *tmp_str;
1778 if (redownload == REDOWNLOAD_BAD)
1779 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1781 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1785 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1787 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1789 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1790 /* launch the thread */
1791 a_background_thread ( BACKGROUND_POOL_REMOTE,
1792 VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1793 tmp, /* description string */
1794 (vik_thr_func) map_download_thread, /* function to call within thread */
1795 mdi, /* pass along data */
1796 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1797 (vik_thr_free_func) mdi_cancel_cleanup,
1806 static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1809 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1811 // Don't ever attempt download on direct access
1812 if ( vik_map_source_is_direct_file_access ( map ) )
1815 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1816 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1817 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1821 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1826 mdi->map_layer_alive = TRUE;
1827 mdi->mutex = vik_mutex_new();
1828 mdi->refresh_display = TRUE;
1830 mdi->cache_dir = g_strdup ( vml->cache_dir );
1831 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1832 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1833 mdi->maptype = vml->maptype;
1834 mdi->cache_layout = vml->cache_layout;
1836 mdi->mapcoord = ulm;
1837 mdi->redownload = download_method;
1839 mdi->x0 = MIN(ulm.x, brm.x);
1840 mdi->xf = MAX(ulm.x, brm.x);
1841 mdi->y0 = MIN(ulm.y, brm.y);
1842 mdi->yf = MAX(ulm.y, brm.y);
1846 MapCoord mcoord = mdi->mapcoord;
1848 for (i = mdi->x0; i <= mdi->xf; i++) {
1850 for (j = mdi->y0; j <= mdi->yf; j++) {
1852 // Only count tiles from supported areas
1853 if ( is_in_area (map, mcoord) ) {
1854 get_filename ( mdi->cache_dir, mdi->cache_layout,
1855 vik_map_source_get_uniq_id(map),
1856 vik_map_source_get_name(map),
1857 ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
1858 vik_map_source_get_file_extension(map) );
1859 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1865 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1867 if (mdi->mapstoget) {
1870 fmt = ngettext("Downloading %d %s map...",
1871 "Downloading %d %s maps...",
1873 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1875 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1877 // launch the thread
1878 a_background_thread ( BACKGROUND_POOL_REMOTE,
1879 VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1880 tmp, /* description string */
1881 (vik_thr_func) map_download_thread, /* function to call within thread */
1882 mdi, /* pass along data */
1883 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1884 (vik_thr_free_func) mdi_cancel_cleanup,
1893 * vik_maps_layer_download_section:
1894 * @vml: The Map Layer
1895 * @vvp: The Viewport that the map is on
1896 * @ul: Upper left coordinate of the area to be downloaded
1897 * @br: Bottom right coordinate of the area to be downloaded
1898 * @zoom: The zoom level at which the maps are to be download
1900 * Download a specified map area at a certain zoom level
1902 void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1904 maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1907 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1909 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1912 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1914 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1917 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1919 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1923 * Display a simple dialog with information about this particular map tile
1925 static void maps_layer_tile_info ( VikMapsLayer *vml )
1927 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1929 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1930 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1933 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1936 gchar *filename = NULL;
1937 gchar *source = NULL;
1939 if ( vik_map_source_is_direct_file_access ( map ) ) {
1940 if ( vik_map_source_is_mbtiles ( map ) ) {
1941 filename = g_strdup ( vml->filename );
1942 #ifdef HAVE_SQLITE3_H
1943 // And whether to bother going into the SQL to check it's really there or not...
1944 gchar *exists = NULL;
1945 gint zoom = 17 - ulm.scale;
1946 if ( vml->mbtiles ) {
1947 GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, zoom );
1949 exists = g_strdup ( _("YES") );
1950 g_object_unref ( G_OBJECT(pixbuf) );
1953 exists = g_strdup ( _("NO") );
1957 exists = g_strdup ( _("NO") );
1958 gint flip_y = (gint) pow(2, zoom)-1 - ulm.y;
1959 // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1960 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 );
1963 source = g_strdup ( _("Source: Not available") );
1966 else if ( vik_map_source_is_osm_meta_tiles ( map ) ) {
1967 char path[PATH_MAX];
1968 xyz_to_meta(path, sizeof(path), vml->cache_dir, ulm.x, ulm.y, 17-ulm.scale );
1969 source = g_strdup ( path );
1970 filename = g_strdup ( path );
1973 guint max_path_len = strlen(vml->cache_dir) + 40;
1974 filename = g_malloc ( max_path_len * sizeof(char) );
1975 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM,
1976 vik_map_source_get_uniq_id(map),
1978 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1979 vik_map_source_get_file_extension(map) );
1980 source = g_strconcat ( _("Source: file://"), filename, NULL );
1984 guint max_path_len = strlen(vml->cache_dir) + 40;
1985 filename = g_malloc ( max_path_len * sizeof(char) );
1986 get_filename ( vml->cache_dir, vml->cache_layout,
1987 vik_map_source_get_uniq_id(map),
1988 vik_map_source_get_name(map),
1989 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1990 vik_map_source_get_file_extension(map) );
1991 gchar *url = vik_map_source_default_get_url_display ( VIK_MAP_SOURCE_DEFAULT(map), &ulm );
1992 source = g_markup_printf_escaped ( _("Source: %s"), url );
1996 GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
1997 g_array_append_val ( array, source );
1999 gchar *filemsg = NULL;
2000 gchar *timemsg = NULL;
2002 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
2003 filemsg = g_strconcat ( "Tile File: ", filename, NULL );
2004 // Get some timestamp information of the tile
2006 if ( g_stat ( filename, &stat_buf ) == 0 ) {
2008 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
2009 timemsg = g_strdup_printf ( _("Tile File Timestamp: %s"), time_buf );
2012 timemsg = g_strdup ( _("Tile File Timestamp: Not Available") );
2014 g_array_append_val ( array, filemsg );
2015 g_array_append_val ( array, timemsg );
2018 filemsg = g_strdup_printf ( _("Tile File: %s [Not Available]"), filename );
2019 g_array_append_val ( array, filemsg );
2022 a_dialog_list ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Tile Information"), array, 5 );
2023 g_array_free ( array, FALSE );
2028 g_free ( filename );
2031 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2033 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2035 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
2037 if ( event->button == 1 )
2040 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 );
2041 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 );
2042 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
2043 vml->dl_tool_x = vml->dl_tool_y = -1;
2048 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) );
2049 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) );
2051 vml->redownload_vvp = vvp;
2053 vml->dl_tool_x = vml->dl_tool_y = -1;
2055 if ( ! vml->dl_right_click_menu ) {
2057 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2059 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
2061 // Download options aren't for on disk only maps
2062 if ( ! (vik_map_source_is_mbtiles(map) ||
2063 vik_map_source_is_direct_file_access(map) ||
2064 vik_map_source_is_osm_meta_tiles(map)) ) {
2066 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
2067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
2068 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2070 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
2071 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
2072 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2074 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
2075 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
2076 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2079 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
2080 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
2081 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
2082 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
2085 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
2086 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
2092 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
2097 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2100 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2102 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2103 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
2104 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
2105 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
2106 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
2108 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
2114 // A slightly better way of defining the menu callback information
2115 // This should be easier to extend/rework compared to previously
2122 typedef gpointer menu_array_values[MA_LAST];
2124 static void download_onscreen_maps ( menu_array_values values, gint redownload )
2126 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2127 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2128 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
2130 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
2131 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
2136 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
2137 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
2139 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2140 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
2141 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
2142 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
2143 start_download_thread ( vml, vvp, &ul, &br, redownload );
2144 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
2145 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
2146 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
2147 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
2151 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
2155 static void maps_layer_download_missing_onscreen_maps ( menu_array_values values )
2157 download_onscreen_maps( values, REDOWNLOAD_NONE);
2160 static void maps_layer_download_new_onscreen_maps ( menu_array_values values )
2162 download_onscreen_maps( values, REDOWNLOAD_NEW);
2165 static void maps_layer_redownload_all_onscreen_maps ( menu_array_values values )
2167 download_onscreen_maps( values, REDOWNLOAD_ALL);
2170 static void maps_layer_about ( menu_array_values values )
2172 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2173 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2175 if ( vik_map_source_get_license (map) )
2176 maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
2178 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2179 vik_map_source_get_label (map) );
2182 static void maps_layer_mbtiles_open_cb ( menu_array_values values )
2184 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2185 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2186 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2188 maps_layer_mbtiles_open ( vml, vvp, map );
2192 * maps_layer_how_many_maps:
2193 * Copied from maps_layer_download_section but without the actual download and this returns a value
2195 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
2198 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2200 if ( vik_map_source_is_direct_file_access ( map ) )
2203 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
2204 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
2205 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
2209 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
2214 mdi->map_layer_alive = TRUE;
2215 mdi->mutex = vik_mutex_new();
2216 mdi->refresh_display = FALSE;
2218 mdi->cache_dir = g_strdup ( vml->cache_dir );
2219 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
2220 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
2221 mdi->maptype = vml->maptype;
2222 mdi->cache_layout = vml->cache_layout;
2224 mdi->mapcoord = ulm;
2225 mdi->redownload = redownload;
2227 mdi->x0 = MIN(ulm.x, brm.x);
2228 mdi->xf = MAX(ulm.x, brm.x);
2229 mdi->y0 = MIN(ulm.y, brm.y);
2230 mdi->yf = MAX(ulm.y, brm.y);
2234 if ( mdi->redownload == REDOWNLOAD_ALL ) {
2235 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
2238 /* calculate how many we need */
2239 MapCoord mcoord = mdi->mapcoord;
2240 for (i = mdi->x0; i <= mdi->xf; i++) {
2242 for (j = mdi->y0; j <= mdi->yf; j++) {
2244 // Only count tiles from supported areas
2245 if ( is_in_area ( map, mcoord ) ) {
2246 get_filename ( mdi->cache_dir, mdi->cache_layout,
2247 vik_map_source_get_uniq_id(map),
2248 vik_map_source_get_name(map),
2249 ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
2250 vik_map_source_get_file_extension(map) );
2251 if ( mdi->redownload == REDOWNLOAD_NEW ) {
2252 // Assume the worst - always a new file
2253 // Absolute value would require a server lookup - but that is too slow
2257 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
2262 if ( mdi->redownload == REDOWNLOAD_BAD ) {
2263 /* see if this one is bad or what */
2265 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
2266 if (gx || (!pixbuf)) {
2270 // Other download cases already considered or just ignored
2279 gint rv = mdi->mapstoget;
2287 * maps_dialog_zoom_between:
2288 * This dialog is specific to the map layer, so it's here rather than in dialog.c
2290 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
2295 gint *selected_zoom1,
2296 gint *selected_zoom2,
2297 gchar *download_list[],
2298 gint default_download,
2299 gint *selected_download )
2301 GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
2303 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2304 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2305 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2307 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2308 GtkWidget *response_w = NULL;
2309 #if GTK_CHECK_VERSION (2, 20, 0)
2310 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2312 GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
2313 GtkWidget *zoom_combo1 = vik_combo_box_text_new();
2315 for (s = zoom_list; *s; s++)
2316 vik_combo_box_text_append ( zoom_combo1, *s );
2317 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
2319 GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
2320 GtkWidget *zoom_combo2 = vik_combo_box_text_new();
2321 for (s = zoom_list; *s; s++)
2322 vik_combo_box_text_append ( zoom_combo2, *s );
2323 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
2325 GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
2326 GtkWidget *download_combo = vik_combo_box_text_new();
2327 for (s = download_list; *s; s++)
2328 vik_combo_box_text_append ( download_combo, *s );
2329 gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
2331 GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
2332 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
2333 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
2334 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
2335 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
2336 gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
2337 gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
2339 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
2342 gtk_widget_grab_focus ( response_w );
2344 gtk_widget_show_all ( dialog );
2345 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2346 gtk_widget_destroy(dialog);
2350 // Return selected options
2351 *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
2352 *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
2353 *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
2355 gtk_widget_destroy(dialog);
2359 // My best guess of sensible limits
2360 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
2361 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2364 * Get all maps in the region for zoom levels specified by the user
2365 * Sort of similar to trw_layer_download_map_along_track_cb function
2367 static void maps_layer_download_all ( menu_array_values values )
2369 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2370 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2372 // I don't think we should allow users to hammer the servers too much...
2373 // Delibrately not allowing lowest zoom levels
2374 // Still can give massive numbers to download
2375 // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2376 gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2377 gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2379 gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2380 gint selected_download_method;
2382 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2384 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2385 if (cur_zoom == zoom_vals[default_zoom])
2388 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2390 // Default to only 2 zoom levels below the current one
2391 if (default_zoom > 1 )
2392 lower_zoom = default_zoom - 2;
2394 lower_zoom = default_zoom;
2396 // redownload method - needs to align with REDOWNLOAD* macro values
2397 gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2399 gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2401 if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2409 REDOWNLOAD_NONE, // AKA Missing
2410 &selected_download_method ) ) {
2417 // Find out new current positions
2418 gdouble min_lat, max_lat, min_lon, max_lon;
2419 VikCoord vc_ul, vc_br;
2420 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2421 struct LatLon ll_ul = { max_lat, min_lon };
2422 struct LatLon ll_br = { min_lat, max_lon };
2423 vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2424 vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2426 // Get Maps Count - call for each zoom level (in reverse)
2427 // With REDOWNLOAD_NEW this is a possible maximum
2428 // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2431 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2432 map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2435 g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2437 // Absolute protection of hammering a map server
2438 if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2439 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);
2440 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2445 // Confirm really want to do this
2446 if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2447 gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2448 gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2454 // Get Maps - call for each zoom level (in reverse)
2455 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2456 maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2463 static void maps_layer_flush ( menu_array_values values )
2465 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2466 a_mapcache_flush_type ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)) );
2469 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2472 static menu_array_values values;
2473 values[MA_VML] = vml;
2474 values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2476 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2478 item = gtk_menu_item_new();
2479 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2480 gtk_widget_show ( item );
2482 // Download options aren't for on disk only maps
2483 if ( ! (vik_map_source_is_mbtiles(map) ||
2484 vik_map_source_is_direct_file_access(map) ||
2485 vik_map_source_is_osm_meta_tiles(map)) ) {
2486 /* Now with icons */
2487 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2488 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2489 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), values );
2490 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2491 gtk_widget_show ( item );
2493 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2494 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2495 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2496 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), values );
2497 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2498 gtk_widget_show ( item );
2501 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2502 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2503 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), values );
2504 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2505 gtk_widget_show ( item );
2507 item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2508 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2509 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), values );
2510 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2511 gtk_widget_show ( item );
2514 // Quick way to reopen MBTiles file - e.g. if it wasn't available at the time of a .vik file load
2515 if ( vik_map_source_is_mbtiles ( map ) ) {
2516 if ( !vml->mbtiles ) {
2517 item = gtk_image_menu_item_new_with_mnemonic ( _("_Open MBTiles Files") );
2518 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU) );
2519 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_mbtiles_open_cb), values );
2520 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2521 gtk_widget_show ( item );
2525 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2526 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_about), values );
2527 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2528 gtk_widget_show ( item );
2530 // Typical users shouldn't need to use this functionality - so debug only ATM
2532 item = gtk_image_menu_item_new_with_mnemonic ( _("Flush Map Cache") );
2533 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2534 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_flush), values );
2535 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2536 gtk_widget_show ( item );
2541 * Enable downloading maps of the current screen area either 'new' or 'everything'
2543 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2548 static menu_array_values values;
2549 values[MA_VML] = vml;
2550 values[MA_VVP] = vvp;
2553 // Get only new maps
2554 maps_layer_download_new_onscreen_maps ( values );
2556 // Redownload everything
2557 maps_layer_redownload_all_onscreen_maps ( values );