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, VikLayerSetParam *vlsp );
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[] = {
197 { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
198 (VikToolConstructorFunc) maps_layer_download_create,
202 (VikToolMouseFunc) maps_layer_download_click,
204 (VikToolMouseFunc) maps_layer_download_release,
207 GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf, NULL },
210 VikLayerInterface vik_maps_layer_interface = {
214 &vikmapslayer_pixbuf,
217 sizeof(maps_tools) / sizeof(maps_tools[0]),
226 (VikLayerFuncCreate) maps_layer_new,
227 (VikLayerFuncRealize) NULL,
228 (VikLayerFuncPostRead) maps_layer_post_read,
229 (VikLayerFuncFree) maps_layer_free,
231 (VikLayerFuncProperties) NULL,
232 (VikLayerFuncDraw) maps_layer_draw,
233 (VikLayerFuncChangeCoordMode) NULL,
235 (VikLayerFuncGetTimestamp) NULL,
237 (VikLayerFuncSetMenuItemsSelection) NULL,
238 (VikLayerFuncGetMenuItemsSelection) NULL,
240 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
241 (VikLayerFuncSublayerAddMenuItems) NULL,
243 (VikLayerFuncSublayerRenameRequest) NULL,
244 (VikLayerFuncSublayerToggleVisible) NULL,
245 (VikLayerFuncSublayerTooltip) NULL,
246 (VikLayerFuncLayerTooltip) maps_layer_tooltip,
247 (VikLayerFuncLayerSelected) NULL,
249 (VikLayerFuncMarshall) maps_layer_marshall,
250 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
252 (VikLayerFuncSetParam) maps_layer_set_param,
253 (VikLayerFuncGetParam) maps_layer_get_param,
254 (VikLayerFuncChangeParam) maps_layer_change_param,
256 (VikLayerFuncReadFileData) NULL,
257 (VikLayerFuncWriteFileData) NULL,
259 (VikLayerFuncDeleteItem) NULL,
260 (VikLayerFuncCutItem) NULL,
261 (VikLayerFuncCopyItem) NULL,
262 (VikLayerFuncPasteItem) NULL,
263 (VikLayerFuncFreeCopiedItem) NULL,
264 (VikLayerFuncDragDropRequest) NULL,
266 (VikLayerFuncSelectClick) NULL,
267 (VikLayerFuncSelectMove) NULL,
268 (VikLayerFuncSelectRelease) NULL,
269 (VikLayerFuncSelectedViewportMenu) NULL,
272 struct _VikMapsLayer {
276 VikMapsCacheLayout cache_layout;
279 gdouble xmapzoom, ymapzoom;
281 gboolean autodownload;
282 gboolean adl_only_missing;
283 VikCoord *last_center;
287 gint dl_tool_x, dl_tool_y;
289 GtkMenu *dl_right_click_menu;
290 VikCoord redownload_ul, redownload_br; /* right click menu only */
291 VikViewport *redownload_vvp;
293 #ifdef HAVE_SQLITE3_H
298 enum { REDOWNLOAD_NONE = 0, /* download only missing maps */
299 REDOWNLOAD_BAD, /* download missing and bad maps */
300 REDOWNLOAD_NEW, /* download missing maps that are newer on server only */
301 REDOWNLOAD_ALL, /* download all maps */
302 DOWNLOAD_OR_REFRESH }; /* download missing maps and refresh cache */
304 static VikLayerParam prefs[] = {
305 { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Default map layer directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, N_("Choose a directory to store cached Map tiles for this layer"), NULL, NULL, NULL },
308 void maps_layer_init ()
310 VikLayerParamData tmp;
311 tmp.s = maps_layer_default_dir();
312 a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
314 gint max_tiles = MAX_TILES;
315 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_MAX_TILES, &max_tiles ) )
316 MAX_TILES = max_tiles;
319 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MIN_SHRINKFACTOR, &gdtmp ) )
320 MIN_SHRINKFACTOR = gdtmp;
322 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MAX_SHRINKFACTOR, &gdtmp ) )
323 MAX_SHRINKFACTOR = gdtmp;
325 if ( a_settings_get_double ( VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR, &gdtmp ) )
326 REAL_MIN_SHRINKFACTOR = gdtmp;
329 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_UP, &gitmp ) )
330 SCALE_INC_UP = gitmp;
332 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_SCALE_INC_DOWN, &gitmp ) )
333 SCALE_INC_DOWN = gitmp;
335 gboolean gbtmp = TRUE;
336 if ( a_settings_get_boolean ( VIK_SETTINGS_MAP_SCALE_SMALLER_ZOOM_FIRST, &gbtmp ) )
337 SCALE_SMALLER_ZOOM_FIRST = gbtmp;
341 /****************************************/
342 /******** MAPS LAYER TYPES **************/
343 /****************************************/
345 void _add_map_source ( guint16 id, const char *label, VikMapSource *map )
349 len = g_strv_length (params_maptypes);
351 params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
352 params_maptypes[len] = g_strdup (label);
353 params_maptypes[len+1] = NULL;
356 params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
357 params_maptypes_ids[len] = id;
358 params_maptypes_ids[len+1] = 0;
360 /* We have to clone */
361 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
362 /* Register the clone in the list */
363 __map_types = g_list_append(__map_types, clone);
366 We have to ensure the mode LayerParam references the up-to-date
370 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
371 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
373 maps_layer_params[0].widget_data = params_maptypes;
374 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
377 void _update_map_source ( const char *label, VikMapSource *map, int index )
379 GList *item = g_list_nth (__map_types, index);
380 g_object_unref (item->data);
381 item->data = g_object_ref (map);
382 /* Change previous data */
383 g_free (params_maptypes[index]);
384 params_maptypes[index] = g_strdup (label);
388 * maps_layer_register_map_source:
389 * @map: the new VikMapSource
391 * Register a new VikMapSource.
392 * Override existing one (equality of id).
394 void maps_layer_register_map_source ( VikMapSource *map )
396 g_assert(map != NULL);
398 guint16 id = vik_map_source_get_uniq_id(map);
399 const char *label = vik_map_source_get_label(map);
400 g_assert(label != NULL);
402 int previous = map_uniq_id_to_index (id);
403 if (previous != NUM_MAP_TYPES)
405 _update_map_source (label, map, previous);
409 _add_map_source (id, label, map);
413 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
414 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
415 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
418 * vik_maps_layer_get_map_type:
420 * Returns the actual map id (rather than the internal type index value)
422 guint vik_maps_layer_get_map_type(VikMapsLayer *vml)
424 return MAPS_LAYER_NTH_ID(vml->maptype);
428 * vik_maps_layer_set_map_type:
431 void vik_maps_layer_set_map_type(VikMapsLayer *vml, guint map_type)
433 gint maptype = map_uniq_id_to_index(map_type);
434 if ( maptype == NUM_MAP_TYPES )
435 g_warning(_("Unknown map type"));
437 vml->maptype = maptype;
441 * vik_maps_layer_get_default_map_type:
444 guint vik_maps_layer_get_default_map_type ()
446 VikLayerInterface *vli = vik_layer_get_interface ( VIK_LAYER_MAPS );
447 VikLayerParamData vlpd = a_layer_defaults_get ( vli->fixed_layer_name, "mode", VIK_LAYER_PARAM_UINT );
453 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
455 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
458 /****************************************/
459 /******** CACHE DIR STUFF ***************/
460 /****************************************/
462 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
463 #define DIRECTDIRACCESS_WITH_NAME "%s%s" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
464 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
465 #define MAPS_CACHE_DIR maps_layer_default_dir()
469 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
470 #define LOCAL_MAPS_DIR "VIKING-MAPS"
471 #elif defined __APPLE__
473 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
474 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
477 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
478 #define LOCAL_MAPS_DIR ".viking-maps"
481 gchar *maps_layer_default_dir ()
483 static gchar *defaultdir = NULL;
486 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
487 const gchar *mapdir = g_getenv("VIKING_MAPS");
489 defaultdir = g_strdup ( mapdir );
490 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
491 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
493 const gchar *home = g_get_home_dir();
494 if (!home || g_access(home, W_OK))
495 home = g_get_home_dir ();
497 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
499 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
501 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
503 /* Add the separator at the end */
504 gchar *tmp = defaultdir;
505 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
508 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
513 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
515 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
517 if ( g_mkdir ( vml->cache_dir, 0777 ) != 0 )
518 g_warning ( "%s: Failed to create directory %s", __FUNCTION__, vml->cache_dir );
522 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
524 g_assert ( vml != NULL);
525 g_free ( vml->cache_dir );
526 vml->cache_dir = NULL;
527 const gchar *mydir = dir;
529 if ( dir == NULL || dir[0] == '\0' )
531 if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
532 mydir = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s;
535 gchar *canonical_dir = vu_get_canonical_filename ( VIK_LAYER(vml), mydir );
537 // Ensure cache_dir always ends with a separator
538 guint len = strlen(canonical_dir);
539 // Unless the dir is not valid
542 if ( canonical_dir[len-1] != G_DIR_SEPARATOR )
544 vml->cache_dir = g_strconcat ( canonical_dir, G_DIR_SEPARATOR_S, NULL );
545 g_free ( canonical_dir );
548 vml->cache_dir = canonical_dir;
551 maps_layer_mkdir_if_default_dir ( vml );
555 static void maps_layer_set_file ( VikMapsLayer *vml, const gchar *name )
558 g_free (vml->filename);
559 vml->filename = g_strdup (name);
562 /****************************************/
563 /******** GOBJECT STUFF *****************/
564 /****************************************/
566 GType vik_maps_layer_get_type ()
568 static GType vml_type = 0;
572 static const GTypeInfo vml_info =
574 sizeof (VikMapsLayerClass),
575 NULL, /* base_init */
576 NULL, /* base_finalize */
577 NULL, /* class init */
578 NULL, /* class_finalize */
579 NULL, /* class_data */
580 sizeof (VikMapsLayer),
582 NULL /* instance init */
584 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
590 /****************************************/
591 /************** PARAMETERS **************/
592 /****************************************/
594 static guint map_index_to_uniq_id (guint16 index)
596 g_assert ( index < NUM_MAP_TYPES );
597 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
600 static guint map_uniq_id_to_index ( guint uniq_id )
603 for ( i = 0; i < NUM_MAP_TYPES; i++ )
604 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
606 return NUM_MAP_TYPES; /* no such thing */
609 #define VIK_SETTINGS_MAP_LICENSE_SHOWN "map_license_shown"
612 * Convenience function to display the license
614 static void maps_show_license ( GtkWindow *parent, VikMapSource *map )
616 a_dialog_license ( parent,
617 vik_map_source_get_label (map),
618 vik_map_source_get_license (map),
619 vik_map_source_get_license_url (map) );
622 static gboolean maps_layer_set_param ( VikMapsLayer *vml, VikLayerSetParam *vlsp )
626 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, vlsp->data.s ); break;
627 case PARAM_CACHE_LAYOUT: if ( vlsp->data.u < VIK_MAPS_CACHE_LAYOUT_NUM ) vml->cache_layout = vlsp->data.u; break;
628 case PARAM_FILE: maps_layer_set_file ( vml, vlsp->data.s ); break;
629 case PARAM_MAPTYPE: {
630 gint maptype = map_uniq_id_to_index(vlsp->data.u);
631 if ( maptype == NUM_MAP_TYPES )
632 g_warning(_("Unknown map type"));
634 vml->maptype = maptype;
636 // When loading from a file don't need the license reminder - ensure it's saved into the 'seen' list
637 if ( vlsp->is_file_operation ) {
638 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, vlsp->data.u );
641 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
642 if (vik_map_source_get_license (map) != NULL) {
643 // Check if licence for this map type has been shown before
644 if ( ! a_settings_get_integer_list_contains ( VIK_SETTINGS_MAP_LICENSE_SHOWN, vlsp->data.u ) ) {
646 maps_show_license ( VIK_GTK_WINDOW_FROM_WIDGET(vlsp->vp), map );
647 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, vlsp->data.u );
654 case PARAM_ALPHA: if ( vlsp->data.u <= 255 ) vml->alpha = vlsp->data.u; break;
655 case PARAM_AUTODOWNLOAD: vml->autodownload = vlsp->data.b; break;
656 case PARAM_ONLYMISSING: vml->adl_only_missing = vlsp->data.b; break;
658 if ( vlsp->data.u < NUM_MAPZOOMS ) {
659 vml->mapzoom_id = vlsp->data.u;
660 vml->xmapzoom = __mapzooms_x [vlsp->data.u];
661 vml->ymapzoom = __mapzooms_y [vlsp->data.u];
663 g_warning (_("Unknown Map Zoom"));
670 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
672 VikLayerParamData rv;
675 case PARAM_CACHE_DIR:
677 gboolean set = FALSE;
678 /* Only save a blank when the map cache location equals the default
679 On reading in, when it is blank then the default is reconstructed
680 Since the default changes dependent on the user and OS, it means the resultant file is more portable */
681 if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
685 else if ( is_file_operation && vml->cache_dir ) {
686 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
687 gchar *cwd = g_get_current_dir();
689 rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
690 if ( !rv.s ) rv.s = "";
696 rv.s = vml->cache_dir ? vml->cache_dir : "";
699 case PARAM_CACHE_LAYOUT: rv.u = vml->cache_layout; break;
700 case PARAM_FILE: rv.s = vml->filename; break;
701 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
702 case PARAM_ALPHA: rv.u = vml->alpha; break;
703 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
704 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
705 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
711 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values )
713 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
714 // Alter sensitivity of download option widgets according to the maptype setting.
715 case PARAM_MAPTYPE: {
717 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
718 // Is it *not* the OSM On Disk Tile Layout or the MBTiles type or the OSM Metatiles type
719 gboolean sensitive = ( MAP_ID_OSM_ON_DISK != vlpd.u &&
720 MAP_ID_MBTILES != vlpd.u &&
721 MAP_ID_OSM_METATILES != vlpd.u );
722 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
723 GtkWidget **ww2 = values[UI_CHG_LABELS];
724 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
725 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
726 GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
727 GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
728 // Depends on autodownload value
729 gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
730 if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
731 if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
732 if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
733 if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
735 // Cache type not applicable either
736 GtkWidget *w9 = ww1[PARAM_CACHE_LAYOUT];
737 GtkWidget *w10 = ww2[PARAM_CACHE_LAYOUT];
738 if ( w9 ) gtk_widget_set_sensitive ( w9, sensitive );
739 if ( w10 ) gtk_widget_set_sensitive ( w10, sensitive );
741 // File only applicable for MBTiles type
742 // Directory for all other types
743 sensitive = ( MAP_ID_MBTILES == vlpd.u);
744 GtkWidget *w5 = ww1[PARAM_FILE];
745 GtkWidget *w6 = ww2[PARAM_FILE];
746 GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
747 GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
748 if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
749 if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
750 if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
751 if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
756 // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
757 case PARAM_AUTODOWNLOAD: {
759 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
760 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
761 GtkWidget **ww2 = values[UI_CHG_LABELS];
762 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
763 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
764 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
765 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
772 /****************************************/
773 /****** CREATING, COPYING, FREEING ******/
774 /****************************************/
776 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
778 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
779 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
781 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
783 vml->dl_tool_x = vml->dl_tool_y = -1;
784 vml->last_center = NULL;
785 vml->last_xmpp = 0.0;
786 vml->last_ympp = 0.0;
788 vml->dl_right_click_menu = NULL;
789 vml->filename = NULL;
793 static void maps_layer_free ( VikMapsLayer *vml )
795 g_free ( vml->cache_dir );
796 vml->cache_dir = NULL;
797 if ( vml->dl_right_click_menu )
798 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
799 g_free(vml->last_center);
800 vml->last_center = NULL;
801 g_free ( vml->filename );
802 vml->filename = NULL;
804 #ifdef HAVE_SQLITE3_H
805 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
806 if ( vik_map_source_is_mbtiles ( map ) ) {
807 if ( vml->mbtiles ) {
808 int ans = sqlite3_close ( vml->mbtiles );
809 if ( ans != SQLITE_OK ) {
810 // Only to console for information purposes only
811 g_warning ( "SQL Close problem: %d", ans );
818 static void maps_layer_mbtiles_open ( VikMapsLayer *vml, VikViewport *vp, VikMapSource *map )
820 #ifdef HAVE_SQLITE3_H
822 if ( vik_map_source_is_mbtiles ( map ) ) {
823 int ans = sqlite3_open_v2 ( vml->filename,
825 SQLITE_OPEN_READONLY,
827 if ( ans != SQLITE_OK ) {
828 // That didn't work, so here's why:
829 g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
831 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
832 _("Failed to open MBTiles file: %s"),
840 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
842 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
843 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
847 /* If this method is not called in file reading context
848 * it is called in GUI context.
849 * So, we can check if we have to inform the user about inconsistency */
850 VikViewportDrawMode vp_drawmode;
851 vp_drawmode = vik_viewport_get_drawmode ( vp );
853 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
854 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
855 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
856 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
861 // Performed in post read as we now know the map type
862 maps_layer_mbtiles_open ( vml, vp, map );
864 // If the on Disk OSM Tile Layout type
865 if ( vik_map_source_get_uniq_id(map) == MAP_ID_OSM_ON_DISK ) {
866 // Copy the directory into filename
867 // thus the mapcache look up will be unique when using more than one of these map types
868 g_free ( vml->filename );
869 vml->filename = g_strdup (vml->cache_dir);
873 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
875 return vik_maps_layer_get_map_label ( vml );
878 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
880 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
883 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
885 VikMapsLayer *rv = maps_layer_new ( vvp );
886 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
887 maps_layer_post_read ( VIK_LAYER(rv), vvp, FALSE );
891 /*********************/
892 /****** DRAWING ******/
893 /*********************/
895 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
898 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
899 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
900 g_object_unref ( G_OBJECT(pixbuf) );
904 #ifdef HAVE_SQLITE3_H
906 static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
908 g_warning ( "Found %d columns", cols );
910 for ( i = 0; i < cols; i++ ) {
911 g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
920 static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
922 GdkPixbuf *pixbuf = NULL;
924 // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
925 gint flip_y = (gint) pow(2, zoom)-1 - yy;
926 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 );
928 gboolean finished = FALSE;
930 sqlite3_stmt *sql_stmt = NULL;
931 int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
932 if ( ans != SQLITE_OK ) {
933 g_warning ( "%s: %s - %d: %s", __FUNCTION__, "prepare failure", ans, statement );
937 while ( !finished ) {
938 ans = sqlite3_step ( sql_stmt );
941 // Get tile_data blob
942 int count = sqlite3_column_count(sql_stmt);
944 g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
948 const void *data = sqlite3_column_blob ( sql_stmt, 0 );
949 int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
951 g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
955 // Convert these blob bytes into a pixbuf via these streaming operations
956 GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
957 GError *error = NULL;
958 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
960 g_warning ( "%s: %s", __FUNCTION__, error->message );
961 g_error_free ( error );
963 g_input_stream_close ( stream, NULL, NULL );
969 // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
971 // and give up on any errors
972 if ( ans != SQLITE_DONE )
973 g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
978 (void)sqlite3_finalize ( sql_stmt );
980 g_free ( statement );
986 static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
988 GdkPixbuf *pixbuf = NULL;
990 #ifdef HAVE_SQLITE3_H
991 if ( vml->mbtiles ) {
993 gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
995 int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
996 if ( ans != SQLITE_OK ) {
997 // Only to console for information purposes only
998 g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
999 sqlite3_free( errMsg );
1001 g_free ( statement );
1004 // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
1005 // Hence this specific function
1006 pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
1013 static GdkPixbuf *get_pixbuf_from_metatile ( VikMapsLayer *vml, gint xx, gint yy, gint zz )
1015 const int tile_max = METATILE_MAX_SIZE;
1016 char err_msg[PATH_MAX];
1021 buf = g_malloc(tile_max);
1027 len = metatile_read(vml->cache_dir, xx, yy, zz, buf, tile_max, &compressed, err_msg);
1031 // Not handled yet - I don't think this is used often - so implement later if necessary
1032 g_warning ( "Compressed metatiles not implemented:%s\n", __FUNCTION__);
1037 // Convert these buf bytes into a pixbuf via these streaming operations
1038 GdkPixbuf *pixbuf = NULL;
1040 GInputStream *stream = g_memory_input_stream_new_from_data ( buf, len, NULL );
1041 GError *error = NULL;
1042 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
1044 g_warning ( "%s: %s", __FUNCTION__, error->message );
1045 g_error_free ( error );
1047 g_input_stream_close ( stream, NULL, NULL );
1054 g_warning ( "FAILED:%s %s", __FUNCTION__, err_msg);
1060 * Caller has to decrease reference counter of returned
1061 * GdkPixbuf, when buffer is no longer needed.
1063 static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
1065 // Apply alpha setting
1066 if ( pixbuf && vml->alpha < 255 )
1067 pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
1069 if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
1070 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
1073 a_mapcache_add ( pixbuf, (mapcache_extra_t) {0.0}, mapcoord->x, mapcoord->y,
1074 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
1075 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1080 static void get_filename ( const gchar *cache_dir,
1081 VikMapsCacheLayout cl,
1088 gchar *filename_buf,
1090 const gchar* file_extension )
1093 case VIK_MAPS_CACHE_LAYOUT_OSM:
1095 if ( g_strcmp0 ( cache_dir, MAPS_CACHE_DIR ) )
1096 // Cache dir not the default - assume it's been directed somewhere specific
1097 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1099 // Using default cache - so use the map name in the directory path
1100 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS_WITH_NAME, cache_dir, name, (17 - scale), x, y, file_extension );
1103 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS, cache_dir, (17 - scale), x, y, file_extension );
1106 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE, cache_dir, id, scale, z, x, y );
1112 * Caller has to decrease reference counter of returned
1113 * GdkPixbuf, when buffer is no longer needed.
1115 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, guint16 id, const gchar* mapname, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
1120 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
1121 id, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
1124 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1125 if ( vik_map_source_is_direct_file_access(map) ) {
1126 // ATM MBTiles must be 'a direct access type'
1127 if ( vik_map_source_is_mbtiles(map) ) {
1128 pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1129 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1130 // return now to avoid file tests that aren't appropriate for this map type
1133 else if ( vik_map_source_is_osm_meta_tiles(map) ) {
1134 pixbuf = get_pixbuf_from_metatile ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
1135 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1139 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, NULL,
1140 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1141 vik_map_source_get_file_extension(map) );
1144 get_filename ( vml->cache_dir, vml->cache_layout, id, mapname,
1145 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y, filename_buf, buf_len,
1146 vik_map_source_get_file_extension(map) );
1148 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1151 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
1153 /* free the pixbuf on error */
1156 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE ) {
1158 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1159 gchar* msg = g_strdup_printf ( _("Couldn't open image file: %s"), gx->message );
1160 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1165 g_error_free ( gx );
1167 g_object_unref ( G_OBJECT(pixbuf) );
1170 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1177 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
1179 const VikCoord *center = vik_viewport_get_center ( vvp );
1181 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
1182 /* D'n'D pan in action: do not download */
1185 // Don't attempt to download unsupported zoom levels
1186 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1187 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1188 guint8 zl = map_utils_mpp_to_zoom_level ( xzoom );
1189 if ( zl < vik_map_source_get_zoom_min(map) || zl > vik_map_source_get_zoom_max(map) )
1192 if (vml->last_center == NULL) {
1193 VikCoord *new_center = g_malloc(sizeof(VikCoord));
1194 *new_center = *center;
1195 vml->last_center = new_center;
1196 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1197 vml->last_ympp = vik_viewport_get_ympp(vvp);
1201 /* TODO: perhaps vik_coord_diff() */
1202 if (vik_coord_equals(vml->last_center, center)
1203 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1204 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1207 *(vml->last_center) = *center;
1208 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1209 vml->last_ympp = vik_viewport_get_ympp(vvp);
1216 gboolean try_draw_scale_down (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1217 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1221 for (scale_inc = 1; scale_inc <= SCALE_INC_DOWN; scale_inc++) {
1222 // Try with smaller zooms
1223 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
1224 MapCoord ulm2 = ulm;
1225 ulm2.x = ulm.x / scale_factor;
1226 ulm2.y = ulm.y / scale_factor;
1227 ulm2.scale = ulm.scale + scale_inc;
1228 pixbuf = get_pixbuf ( vml, id, mapname, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1230 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1231 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1232 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1233 g_object_unref(pixbuf);
1243 gboolean try_draw_scale_up (VikMapsLayer *vml, VikViewport *vvp, MapCoord ulm, gint xx, gint yy, gint tilesize_x_ceil, gint tilesize_y_ceil,
1244 gdouble xshrinkfactor, gdouble yshrinkfactor, guint id, const gchar *mapname, gchar *path_buf, guint max_path_len)
1247 // Try with bigger zooms
1249 for (scale_dec = 1; scale_dec <= SCALE_INC_UP; scale_dec++) {
1251 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
1252 MapCoord ulm2 = ulm;
1253 ulm2.x = ulm.x * scale_factor;
1254 ulm2.y = ulm.y * scale_factor;
1255 ulm2.scale = ulm.scale - scale_dec;
1256 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1257 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1258 MapCoord ulm3 = ulm2;
1261 pixbuf = get_pixbuf ( vml, id, mapname, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1265 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1266 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1267 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1268 g_object_unref(pixbuf);
1277 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1280 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1281 gdouble yzoom = vik_viewport_get_ympp ( vvp );
1282 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
1283 gboolean existence_only = FALSE;
1285 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1286 xshrinkfactor = vml->xmapzoom / xzoom;
1287 yshrinkfactor = vml->ymapzoom / yzoom;
1288 xzoom = vml->xmapzoom;
1289 yzoom = vml->xmapzoom;
1290 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1291 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
1292 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1293 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
1294 existence_only = TRUE;
1297 // Report the reason for not drawing
1298 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1299 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));
1300 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1309 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1310 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1311 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
1315 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1316 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
1317 guint16 id = vik_map_source_get_uniq_id(map);
1318 const gchar *mapname = vik_map_source_get_name(map);
1321 gint xx, yy, width, height;
1324 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1325 // which can happen when using a small fixed zoom level and viewing large areas.
1326 // Also prevents very large number of tile download requests
1327 gint tiles = (xmax-xmin) * (ymax-ymin);
1328 if ( tiles > MAX_TILES ) {
1329 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1330 existence_only = TRUE;
1333 guint max_path_len = strlen(vml->cache_dir) + 40;
1334 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1336 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
1337 g_debug("%s: Starting autodownload", __FUNCTION__);
1338 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
1339 // Try to download newer tiles
1340 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1342 // Download only missing tiles
1343 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
1346 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
1347 for ( x = xmin; x <= xmax; x++ ) {
1348 for ( y = ymin; y <= ymax; y++ ) {
1351 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1353 width = gdk_pixbuf_get_width ( pixbuf );
1354 height = gdk_pixbuf_get_height ( pixbuf );
1356 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1357 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1361 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1362 g_object_unref(pixbuf);
1366 } else { /* tilesize is known, don't have to keep converting coords */
1367 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1368 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
1369 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1370 gint tilesize_x_ceil = ceil ( tilesize_x );
1371 gint tilesize_y_ceil = ceil ( tilesize_y );
1372 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1373 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
1374 gint xx_tmp, yy_tmp;
1375 gint base_yy, xend, yend;
1377 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1378 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1380 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1381 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1382 xx = xx_tmp; yy = yy_tmp;
1383 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1384 * eg if tile size 128, shrinkfactor 0.333 */
1385 xx -= (tilesize_x/2);
1386 base_yy = yy - (tilesize_y/2);
1388 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1390 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1394 if ( existence_only ) {
1395 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1396 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM, id, vik_map_source_get_name(map),
1397 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1399 get_filename ( vml->cache_dir, vml->cache_layout, id, vik_map_source_get_name(map),
1400 ulm.scale, ulm.z, ulm.x, ulm.y, path_buf, max_path_len, vik_map_source_get_file_extension(map) );
1402 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1403 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1404 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1407 // Try correct scale first
1408 int scale_factor = 1;
1409 pixbuf = get_pixbuf ( vml, id, mapname, &ulm, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1411 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1412 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1413 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1414 g_object_unref(pixbuf);
1417 // Otherwise try different scales
1418 if ( SCALE_SMALLER_ZOOM_FIRST ) {
1419 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) ) {
1420 try_draw_scale_up(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1424 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) ) {
1425 try_draw_scale_down(vml,vvp,ulm,xx,yy,tilesize_x_ceil,tilesize_y_ceil,xshrinkfactor,yshrinkfactor,id,mapname,path_buf,max_path_len);
1436 // ATM Only show tile grid lines in extreme debug mode
1437 if ( vik_debug && vik_verbose ) {
1438 /* Grid drawing here so it gets drawn on top of the map */
1439 /* Thus loop around x & y again, but this time separately */
1440 /* Only showing grid for the current scale */
1441 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
1442 /* Draw single grid lines across the whole screen */
1443 gint width = vik_viewport_get_width(vvp);
1444 gint height = vik_viewport_get_height(vvp);
1445 xx = xx_tmp; yy = yy_tmp;
1446 gint base_xx = xx - (tilesize_x/2);
1447 base_yy = yy - (tilesize_y/2);
1450 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1451 vik_viewport_draw_line ( vvp, black_gc, xx, base_yy, xx, height );
1456 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1457 vik_viewport_draw_line ( vvp, black_gc, base_xx, yy, width, yy );
1463 g_free ( path_buf );
1467 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1469 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
1474 gdouble level = vik_viewport_get_zoom ( vvp );
1476 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1477 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
1480 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1481 vik_viewport_add_logo ( vvp, logo );
1483 /* get corner coords */
1484 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1485 /* UTM multi-zone stuff by Kit Transue */
1486 gchar leftmost_zone, rightmost_zone, i;
1487 leftmost_zone = vik_viewport_leftmost_zone( vvp );
1488 rightmost_zone = vik_viewport_rightmost_zone( vvp );
1489 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1490 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1491 maps_layer_draw_section ( vml, vvp, &ul, &br );
1495 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1496 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1498 maps_layer_draw_section ( vml, vvp, &ul, &br );
1503 /*************************/
1504 /****** DOWNLOADING ******/
1505 /*************************/
1507 /* pass along data to thread, exists even if layer is deleted. */
1510 gchar *filename_buf;
1511 VikMapsCacheLayout cache_layout;
1512 gint x0, y0, xf, yf;
1518 gboolean refresh_display;
1521 gboolean map_layer_alive;
1525 static void mdi_free ( MapDownloadInfo *mdi )
1527 vik_mutex_free(mdi->mutex);
1528 g_free ( mdi->cache_dir );
1529 mdi->cache_dir = NULL;
1530 g_free ( mdi->filename_buf );
1531 mdi->filename_buf = NULL;
1535 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1537 MapDownloadInfo *mdi = ptr;
1538 g_mutex_lock(mdi->mutex);
1539 mdi->map_layer_alive = FALSE;
1540 g_mutex_unlock(mdi->mutex);
1543 static gboolean is_in_area (VikMapSource *map, MapCoord mc)
1546 vik_map_source_mapcoord_to_center_coord ( map, &mc, &vc );
1549 tl.lat = vik_map_source_get_lat_max(map);
1550 tl.lon = vik_map_source_get_lon_min(map);
1552 br.lat = vik_map_source_get_lat_min(map);
1553 br.lon = vik_map_source_get_lon_max(map);
1555 vik_coord_load_from_latlon (&vctl, VIK_COORD_LATLON, &tl);
1557 vik_coord_load_from_latlon (&vcbr, VIK_COORD_LATLON, &br);
1559 return vik_coord_inside ( &vc, &vctl, &vcbr );
1562 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1564 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1566 MapCoord mcoord = mdi->mapcoord;
1568 for ( x = mdi->x0; x <= mdi->xf; x++ )
1571 for ( y = mdi->y0; y <= mdi->yf; y++ )
1574 // Only attempt to download a tile from supported areas
1575 if ( is_in_area ( MAPS_LAYER_NTH_TYPE(mdi->maptype), mcoord ) )
1577 gboolean remove_mem_cache = FALSE;
1578 gboolean need_download = FALSE;
1580 get_filename ( mdi->cache_dir, mdi->cache_layout,
1581 vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1582 vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1583 mdi->mapcoord.scale, mdi->mapcoord.z, x, y, mdi->filename_buf, mdi->maxlen,
1584 vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1587 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1589 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1593 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1594 need_download = TRUE;
1595 remove_mem_cache = TRUE;
1597 } else { /* in case map file already exists */
1598 switch (mdi->redownload) {
1599 case REDOWNLOAD_NONE:
1602 case REDOWNLOAD_BAD:
1604 /* see if this one is bad or what */
1606 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1607 if (gx || (!pixbuf)) {
1608 if ( g_remove ( mdi->filename_buf ) )
1609 g_warning ( "REDOWNLOAD failed to remove: %s", mdi->filename_buf );
1610 need_download = TRUE;
1611 remove_mem_cache = TRUE;
1612 g_error_free ( gx );
1615 g_object_unref ( pixbuf );
1620 case REDOWNLOAD_NEW:
1621 need_download = TRUE;
1622 remove_mem_cache = TRUE;
1625 case REDOWNLOAD_ALL:
1626 /* FIXME: need a better way than to erase file in case of server/network problem */
1627 if ( g_remove ( mdi->filename_buf ) )
1628 g_warning ( "REDOWNLOAD failed to remove: %s", mdi->filename_buf );
1629 need_download = TRUE;
1630 remove_mem_cache = TRUE;
1633 case DOWNLOAD_OR_REFRESH:
1634 remove_mem_cache = TRUE;
1638 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1642 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1644 if (need_download) {
1645 DownloadResult_t dr = vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle);
1647 case DOWNLOAD_PARAMETERS_ERROR:
1648 case DOWNLOAD_HTTP_ERROR:
1649 case DOWNLOAD_CONTENT_ERROR: {
1650 // TODO: ?? count up the number of download errors somehow...
1651 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Failed to download tile") );
1652 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1656 case DOWNLOAD_FILE_WRITE_ERROR: {
1657 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Unable to save tile") );
1658 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1662 case DOWNLOAD_SUCCESS:
1663 case DOWNLOAD_NOT_REQUIRED:
1669 g_mutex_lock(mdi->mutex);
1670 if (remove_mem_cache)
1671 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 );
1672 if (mdi->refresh_display && mdi->map_layer_alive) {
1673 /* TODO: check if it's on visible area */
1674 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1676 g_mutex_unlock(mdi->mutex);
1677 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1681 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1682 g_mutex_lock(mdi->mutex);
1683 if (mdi->map_layer_alive)
1684 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1685 g_mutex_unlock(mdi->mutex);
1689 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1691 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1693 get_filename ( mdi->cache_dir, mdi->cache_layout,
1694 vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1695 vik_map_source_get_name(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1696 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y, mdi->filename_buf, mdi->maxlen,
1697 vik_map_source_get_file_extension(MAPS_LAYER_NTH_TYPE(mdi->maptype)) );
1698 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1700 if ( g_remove ( mdi->filename_buf ) )
1701 g_warning ( "Cleanup failed to remove: %s", mdi->filename_buf );
1706 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1708 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1709 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1711 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1713 // Don't ever attempt download on direct access
1714 if ( vik_map_source_is_direct_file_access ( map ) )
1717 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1718 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1720 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1725 mdi->map_layer_alive = TRUE;
1726 mdi->mutex = vik_mutex_new();
1727 mdi->refresh_display = TRUE;
1729 /* cache_dir and buffer for dest filename */
1730 mdi->cache_dir = g_strdup ( vml->cache_dir );
1731 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1732 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1733 mdi->cache_layout = vml->cache_layout;
1734 mdi->maptype = vml->maptype;
1736 mdi->mapcoord = ulm;
1737 mdi->redownload = redownload;
1739 mdi->x0 = MIN(ulm.x, brm.x);
1740 mdi->xf = MAX(ulm.x, brm.x);
1741 mdi->y0 = MIN(ulm.y, brm.y);
1742 mdi->yf = MAX(ulm.y, brm.y);
1746 MapCoord mcoord = mdi->mapcoord;
1748 if ( mdi->redownload ) {
1749 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1751 /* calculate how many we need */
1752 for ( a = mdi->x0; a <= mdi->xf; a++ )
1755 for ( b = mdi->y0; b <= mdi->yf; b++ )
1758 // Only count tiles from supported areas
1759 if ( is_in_area (map, mcoord) )
1761 get_filename ( mdi->cache_dir, mdi->cache_layout,
1762 vik_map_source_get_uniq_id(map),
1763 vik_map_source_get_name(map),
1764 ulm.scale, ulm.z, a, b, mdi->filename_buf, mdi->maxlen,
1765 vik_map_source_get_file_extension(map) );
1766 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1773 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1775 if ( mdi->mapstoget )
1777 const gchar *tmp_str;
1782 if (redownload == REDOWNLOAD_BAD)
1783 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1785 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1789 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1791 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1793 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1794 /* launch the thread */
1795 a_background_thread ( BACKGROUND_POOL_REMOTE,
1796 VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1797 tmp, /* description string */
1798 (vik_thr_func) map_download_thread, /* function to call within thread */
1799 mdi, /* pass along data */
1800 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1801 (vik_thr_free_func) mdi_cancel_cleanup,
1810 static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1813 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1815 // Don't ever attempt download on direct access
1816 if ( vik_map_source_is_direct_file_access ( map ) )
1819 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1820 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1821 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1825 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1830 mdi->map_layer_alive = TRUE;
1831 mdi->mutex = vik_mutex_new();
1832 mdi->refresh_display = TRUE;
1834 mdi->cache_dir = g_strdup ( vml->cache_dir );
1835 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1836 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1837 mdi->maptype = vml->maptype;
1838 mdi->cache_layout = vml->cache_layout;
1840 mdi->mapcoord = ulm;
1841 mdi->redownload = download_method;
1843 mdi->x0 = MIN(ulm.x, brm.x);
1844 mdi->xf = MAX(ulm.x, brm.x);
1845 mdi->y0 = MIN(ulm.y, brm.y);
1846 mdi->yf = MAX(ulm.y, brm.y);
1850 MapCoord mcoord = mdi->mapcoord;
1852 for (i = mdi->x0; i <= mdi->xf; i++) {
1854 for (j = mdi->y0; j <= mdi->yf; j++) {
1856 // Only count tiles from supported areas
1857 if ( is_in_area (map, mcoord) ) {
1858 get_filename ( mdi->cache_dir, mdi->cache_layout,
1859 vik_map_source_get_uniq_id(map),
1860 vik_map_source_get_name(map),
1861 ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
1862 vik_map_source_get_file_extension(map) );
1863 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1869 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1871 if (mdi->mapstoget) {
1874 fmt = ngettext("Downloading %d %s map...",
1875 "Downloading %d %s maps...",
1877 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1879 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1881 // launch the thread
1882 a_background_thread ( BACKGROUND_POOL_REMOTE,
1883 VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1884 tmp, /* description string */
1885 (vik_thr_func) map_download_thread, /* function to call within thread */
1886 mdi, /* pass along data */
1887 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1888 (vik_thr_free_func) mdi_cancel_cleanup,
1897 * vik_maps_layer_download_section:
1898 * @vml: The Map Layer
1899 * @vvp: The Viewport that the map is on
1900 * @ul: Upper left coordinate of the area to be downloaded
1901 * @br: Bottom right coordinate of the area to be downloaded
1902 * @zoom: The zoom level at which the maps are to be download
1904 * Download a specified map area at a certain zoom level
1906 void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1908 maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1911 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1913 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1916 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1918 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1921 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1923 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1927 * Display a simple dialog with information about this particular map tile
1929 static void maps_layer_tile_info ( VikMapsLayer *vml )
1931 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1933 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1934 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1937 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1940 gchar *filename = NULL;
1941 gchar *source = NULL;
1943 if ( vik_map_source_is_direct_file_access ( map ) ) {
1944 if ( vik_map_source_is_mbtiles ( map ) ) {
1945 filename = g_strdup ( vml->filename );
1946 #ifdef HAVE_SQLITE3_H
1947 // And whether to bother going into the SQL to check it's really there or not...
1948 gchar *exists = NULL;
1949 gint zoom = 17 - ulm.scale;
1950 if ( vml->mbtiles ) {
1951 GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, zoom );
1953 exists = g_strdup ( _("YES") );
1954 g_object_unref ( G_OBJECT(pixbuf) );
1957 exists = g_strdup ( _("NO") );
1961 exists = g_strdup ( _("NO") );
1962 gint flip_y = (gint) pow(2, zoom)-1 - ulm.y;
1963 // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1964 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 );
1967 source = g_strdup ( _("Source: Not available") );
1970 else if ( vik_map_source_is_osm_meta_tiles ( map ) ) {
1971 char path[PATH_MAX];
1972 xyz_to_meta(path, sizeof(path), vml->cache_dir, ulm.x, ulm.y, 17-ulm.scale );
1973 source = g_strdup ( path );
1974 filename = g_strdup ( path );
1977 guint max_path_len = strlen(vml->cache_dir) + 40;
1978 filename = g_malloc ( max_path_len * sizeof(char) );
1979 get_filename ( vml->cache_dir, VIK_MAPS_CACHE_LAYOUT_OSM,
1980 vik_map_source_get_uniq_id(map),
1982 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1983 vik_map_source_get_file_extension(map) );
1984 source = g_strconcat ( _("Source: file://"), filename, NULL );
1988 guint max_path_len = strlen(vml->cache_dir) + 40;
1989 filename = g_malloc ( max_path_len * sizeof(char) );
1990 get_filename ( vml->cache_dir, vml->cache_layout,
1991 vik_map_source_get_uniq_id(map),
1992 vik_map_source_get_name(map),
1993 ulm.scale, ulm.z, ulm.x, ulm.y, filename, max_path_len,
1994 vik_map_source_get_file_extension(map) );
1995 gchar *url = vik_map_source_default_get_url_display ( VIK_MAP_SOURCE_DEFAULT(map), &ulm );
1996 source = g_markup_printf_escaped ( _("Source: %s"), url );
2000 GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
2001 g_array_append_val ( array, source );
2003 gchar *filemsg = NULL;
2004 gchar *timemsg = NULL;
2006 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
2007 filemsg = g_strconcat ( "Tile File: ", filename, NULL );
2008 // Get some timestamp information of the tile
2010 if ( g_stat ( filename, &stat_buf ) == 0 ) {
2012 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
2013 timemsg = g_strdup_printf ( _("Tile File Timestamp: %s"), time_buf );
2016 timemsg = g_strdup ( _("Tile File Timestamp: Not Available") );
2018 g_array_append_val ( array, filemsg );
2019 g_array_append_val ( array, timemsg );
2022 filemsg = g_strdup_printf ( _("Tile File: %s [Not Available]"), filename );
2023 g_array_append_val ( array, filemsg );
2026 a_dialog_list ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Tile Information"), array, 5 );
2027 g_array_free ( array, FALSE );
2032 g_free ( filename );
2035 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2037 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2039 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
2041 if ( event->button == 1 )
2044 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 );
2045 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 );
2046 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
2047 vml->dl_tool_x = vml->dl_tool_y = -1;
2052 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) );
2053 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) );
2055 vml->redownload_vvp = vvp;
2057 vml->dl_tool_x = vml->dl_tool_y = -1;
2059 if ( ! vml->dl_right_click_menu ) {
2061 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2063 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
2065 // Download options aren't for on disk only maps
2066 if ( ! (vik_map_source_is_mbtiles(map) ||
2067 vik_map_source_is_direct_file_access(map) ||
2068 vik_map_source_is_osm_meta_tiles(map)) ) {
2070 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
2071 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
2072 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2074 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
2075 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
2076 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2078 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
2079 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
2080 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
2083 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
2084 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
2085 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
2086 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
2089 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
2090 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
2096 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
2101 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
2104 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
2106 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2107 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
2108 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
2109 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
2110 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
2112 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
2118 // A slightly better way of defining the menu callback information
2119 // This should be easier to extend/rework compared to previously
2126 typedef gpointer menu_array_values[MA_LAST];
2128 static void download_onscreen_maps ( menu_array_values values, gint redownload )
2130 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2131 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2132 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
2134 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
2135 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
2140 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
2141 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
2143 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2144 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
2145 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
2146 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
2147 start_download_thread ( vml, vvp, &ul, &br, redownload );
2148 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
2149 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
2150 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
2151 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
2155 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
2159 static void maps_layer_download_missing_onscreen_maps ( menu_array_values values )
2161 download_onscreen_maps( values, REDOWNLOAD_NONE);
2164 static void maps_layer_download_new_onscreen_maps ( menu_array_values values )
2166 download_onscreen_maps( values, REDOWNLOAD_NEW);
2169 static void maps_layer_redownload_all_onscreen_maps ( menu_array_values values )
2171 download_onscreen_maps( values, REDOWNLOAD_ALL);
2174 static void maps_layer_about ( menu_array_values values )
2176 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2177 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2179 if ( vik_map_source_get_license (map) )
2180 maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
2182 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2183 vik_map_source_get_label (map) );
2186 static void maps_layer_mbtiles_open_cb ( menu_array_values values )
2188 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2189 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2190 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2192 maps_layer_mbtiles_open ( vml, vvp, map );
2196 * maps_layer_how_many_maps:
2197 * Copied from maps_layer_download_section but without the actual download and this returns a value
2199 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
2202 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2204 if ( vik_map_source_is_direct_file_access ( map ) )
2207 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
2208 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
2209 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
2213 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
2218 mdi->map_layer_alive = TRUE;
2219 mdi->mutex = vik_mutex_new();
2220 mdi->refresh_display = FALSE;
2222 mdi->cache_dir = g_strdup ( vml->cache_dir );
2223 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
2224 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
2225 mdi->maptype = vml->maptype;
2226 mdi->cache_layout = vml->cache_layout;
2228 mdi->mapcoord = ulm;
2229 mdi->redownload = redownload;
2231 mdi->x0 = MIN(ulm.x, brm.x);
2232 mdi->xf = MAX(ulm.x, brm.x);
2233 mdi->y0 = MIN(ulm.y, brm.y);
2234 mdi->yf = MAX(ulm.y, brm.y);
2238 if ( mdi->redownload == REDOWNLOAD_ALL ) {
2239 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
2242 /* calculate how many we need */
2243 MapCoord mcoord = mdi->mapcoord;
2244 for (i = mdi->x0; i <= mdi->xf; i++) {
2246 for (j = mdi->y0; j <= mdi->yf; j++) {
2248 // Only count tiles from supported areas
2249 if ( is_in_area ( map, mcoord ) ) {
2250 get_filename ( mdi->cache_dir, mdi->cache_layout,
2251 vik_map_source_get_uniq_id(map),
2252 vik_map_source_get_name(map),
2253 ulm.scale, ulm.z, i, j, mdi->filename_buf, mdi->maxlen,
2254 vik_map_source_get_file_extension(map) );
2255 if ( mdi->redownload == REDOWNLOAD_NEW ) {
2256 // Assume the worst - always a new file
2257 // Absolute value would require a server lookup - but that is too slow
2261 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
2266 if ( mdi->redownload == REDOWNLOAD_BAD ) {
2267 /* see if this one is bad or what */
2268 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, NULL );
2272 g_object_unref ( pixbuf );
2275 // Other download cases already considered or just ignored
2284 gint rv = mdi->mapstoget;
2292 * maps_dialog_zoom_between:
2293 * This dialog is specific to the map layer, so it's here rather than in dialog.c
2295 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
2300 gint *selected_zoom1,
2301 gint *selected_zoom2,
2302 gchar *download_list[],
2303 gint default_download,
2304 gint *selected_download )
2306 GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
2308 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2309 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2310 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2312 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2313 GtkWidget *response_w = NULL;
2314 #if GTK_CHECK_VERSION (2, 20, 0)
2315 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2317 GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
2318 GtkWidget *zoom_combo1 = vik_combo_box_text_new();
2320 for (s = zoom_list; *s; s++)
2321 vik_combo_box_text_append ( zoom_combo1, *s );
2322 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
2324 GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
2325 GtkWidget *zoom_combo2 = vik_combo_box_text_new();
2326 for (s = zoom_list; *s; s++)
2327 vik_combo_box_text_append ( zoom_combo2, *s );
2328 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
2330 GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
2331 GtkWidget *download_combo = vik_combo_box_text_new();
2332 for (s = download_list; *s; s++)
2333 vik_combo_box_text_append ( download_combo, *s );
2334 gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
2336 GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
2337 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
2338 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
2339 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
2340 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
2341 gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
2342 gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
2344 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
2347 gtk_widget_grab_focus ( response_w );
2349 gtk_widget_show_all ( dialog );
2350 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2351 gtk_widget_destroy(dialog);
2355 // Return selected options
2356 *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
2357 *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
2358 *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
2360 gtk_widget_destroy(dialog);
2364 // My best guess of sensible limits
2365 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
2366 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2369 * Get all maps in the region for zoom levels specified by the user
2370 * Sort of similar to trw_layer_download_map_along_track_cb function
2372 static void maps_layer_download_all ( menu_array_values values )
2374 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2375 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2377 // I don't think we should allow users to hammer the servers too much...
2378 // Delibrately not allowing lowest zoom levels
2379 // Still can give massive numbers to download
2380 // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2381 gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2382 gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2384 gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2385 gint selected_download_method;
2387 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2389 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2390 if (cur_zoom == zoom_vals[default_zoom])
2393 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2395 // Default to only 2 zoom levels below the current one
2396 if (default_zoom > 1 )
2397 lower_zoom = default_zoom - 2;
2399 lower_zoom = default_zoom;
2401 // redownload method - needs to align with REDOWNLOAD* macro values
2402 gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2404 gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2406 if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2414 REDOWNLOAD_NONE, // AKA Missing
2415 &selected_download_method ) ) {
2422 // Find out new current positions
2423 gdouble min_lat, max_lat, min_lon, max_lon;
2424 VikCoord vc_ul, vc_br;
2425 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2426 struct LatLon ll_ul = { max_lat, min_lon };
2427 struct LatLon ll_br = { min_lat, max_lon };
2428 vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2429 vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2431 // Get Maps Count - call for each zoom level (in reverse)
2432 // With REDOWNLOAD_NEW this is a possible maximum
2433 // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2436 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2437 map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2440 g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2442 // Absolute protection of hammering a map server
2443 if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2444 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);
2445 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2450 // Confirm really want to do this
2451 if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2452 gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2453 gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2459 // Get Maps - call for each zoom level (in reverse)
2460 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2461 maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2468 static void maps_layer_flush ( menu_array_values values )
2470 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2471 a_mapcache_flush_type ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)) );
2474 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2477 static menu_array_values values;
2478 values[MA_VML] = vml;
2479 values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2481 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2483 item = gtk_menu_item_new();
2484 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2485 gtk_widget_show ( item );
2487 // Download options aren't for on disk only maps
2488 if ( ! (vik_map_source_is_mbtiles(map) ||
2489 vik_map_source_is_direct_file_access(map) ||
2490 vik_map_source_is_osm_meta_tiles(map)) ) {
2491 /* Now with icons */
2492 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2493 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2494 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), values );
2495 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2496 gtk_widget_show ( item );
2498 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2499 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2500 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2501 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), values );
2502 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2503 gtk_widget_show ( item );
2506 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2507 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2508 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), values );
2509 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2510 gtk_widget_show ( item );
2512 item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2513 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2514 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), values );
2515 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2516 gtk_widget_show ( item );
2519 // Quick way to reopen MBTiles file - e.g. if it wasn't available at the time of a .vik file load
2520 if ( vik_map_source_is_mbtiles ( map ) ) {
2521 if ( !vml->mbtiles ) {
2522 item = gtk_image_menu_item_new_with_mnemonic ( _("_Open MBTiles Files") );
2523 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU) );
2524 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_mbtiles_open_cb), values );
2525 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2526 gtk_widget_show ( item );
2530 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2531 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_about), values );
2532 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2533 gtk_widget_show ( item );
2535 // Typical users shouldn't need to use this functionality - so debug only ATM
2537 item = gtk_image_menu_item_new_with_mnemonic ( _("Flush Map Cache") );
2538 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2539 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_flush), values );
2540 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2541 gtk_widget_show ( item );
2546 * Enable downloading maps of the current screen area either 'new' or 'everything'
2548 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2553 static menu_array_values values;
2554 values[MA_VML] = vml;
2555 values[MA_VVP] = vvp;
2558 // Get only new maps
2559 maps_layer_download_new_onscreen_maps ( values );
2561 // Redownload everything
2562 maps_layer_redownload_all_onscreen_maps ( values );