2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2005, Evan Battaglia <viking@greentorch.org>
5 * Copyright (C) 2010, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
6 * Copyright (c) 2013, Rob Norris <rw_norris@hotmail.com>
7 * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
8 * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include <gdk-pixbuf/gdk-pixdata.h>
33 #include <glib/gstdio.h>
34 #include <glib/gi18n.h>
48 #include "vikmapsourcedefault.h"
52 #include "background.h"
53 #include "preferences.h"
54 #include "vikmapslayer.h"
55 #include "icons/icons.h"
62 #define VIK_SETTINGS_MAP_MAX_TILES "maps_max_tiles"
63 static gint MAX_TILES = 1000;
65 #define VIK_SETTINGS_MAP_MIN_SHRINKFACTOR "maps_min_shrinkfactor"
66 #define VIK_SETTINGS_MAP_MAX_SHRINKFACTOR "maps_max_shrinkfactor"
67 static gdouble MAX_SHRINKFACTOR = 8.0000001; /* zoom 1 viewing 8-tiles */
68 static gdouble MIN_SHRINKFACTOR = 0.0312499; /* zoom 32 viewing 1-tiles */
70 #define VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR "maps_real_min_shrinkfactor"
71 static gdouble REAL_MIN_SHRINKFACTOR = 0.0039062499; /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
73 /****** MAP TYPES ******/
75 static GList *__map_types = NULL;
77 #define NUM_MAP_TYPES g_list_length(__map_types)
79 /* List of label for each map type */
80 static gchar **params_maptypes = NULL;
82 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
83 static guint *params_maptypes_ids = NULL;
85 /******** MAPZOOMS *********/
87 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 };
88 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 };
89 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 };
91 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
93 /**************************/
96 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
97 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
98 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
99 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
100 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
101 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
102 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values );
103 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
104 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
105 static void maps_layer_free ( VikMapsLayer *vml );
106 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
107 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
108 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
109 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
110 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
111 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
112 static guint map_uniq_id_to_index ( guint uniq_id );
115 static VikLayerParamScale params_scales[] = {
116 /* min, max, step, digits (decimal places) */
117 { 0, 255, 3, 0 }, /* alpha */
120 static VikLayerParamData mode_default ( void ) { return VIK_LPD_UINT ( 19 ); } // OSM MapQuest maps
121 static VikLayerParamData directory_default ( void )
123 VikLayerParamData data;
124 VikLayerParamData *pref = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir");
125 if (pref) data.s = g_strdup ( pref->s ); else data.s = "";
128 static VikLayerParamData file_default ( void )
130 VikLayerParamData data;
134 static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
135 static VikLayerParamData mapzoom_default ( void ) { return VIK_LPD_UINT ( 0 ); }
137 VikLayerParam maps_layer_params[] = {
138 { VIK_LAYER_MAPS, "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL, mode_default, NULL, NULL },
139 { 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 },
140 { 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,
141 N_("An MBTiles file. Only applies when the map type method is 'MBTiles'"), file_default, NULL, NULL },
142 { VIK_LAYER_MAPS, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
143 N_("Control the Alpha value for transparency effects"), alpha_default, NULL, NULL },
144 { 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 },
145 { VIK_LAYER_MAPS, "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
146 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 },
147 { VIK_LAYER_MAPS, "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
148 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."),
149 mapzoom_default, NULL, NULL },
163 void maps_layer_set_autodownload_default ( gboolean autodownload )
165 // Set appropriate function
167 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_true_default;
169 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_false_default;
172 static VikToolInterface maps_tools[] = {
173 { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
174 (VikToolConstructorFunc) maps_layer_download_create,
178 (VikToolMouseFunc) maps_layer_download_click,
180 (VikToolMouseFunc) maps_layer_download_release,
183 GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf, NULL },
186 VikLayerInterface vik_maps_layer_interface = {
190 &vikmapslayer_pixbuf,
193 sizeof(maps_tools) / sizeof(maps_tools[0]),
202 (VikLayerFuncCreate) maps_layer_new,
203 (VikLayerFuncRealize) NULL,
204 (VikLayerFuncPostRead) maps_layer_post_read,
205 (VikLayerFuncFree) maps_layer_free,
207 (VikLayerFuncProperties) NULL,
208 (VikLayerFuncDraw) maps_layer_draw,
209 (VikLayerFuncChangeCoordMode) NULL,
211 (VikLayerFuncSetMenuItemsSelection) NULL,
212 (VikLayerFuncGetMenuItemsSelection) NULL,
214 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
215 (VikLayerFuncSublayerAddMenuItems) NULL,
217 (VikLayerFuncSublayerRenameRequest) NULL,
218 (VikLayerFuncSublayerToggleVisible) NULL,
219 (VikLayerFuncSublayerTooltip) NULL,
220 (VikLayerFuncLayerTooltip) maps_layer_tooltip,
221 (VikLayerFuncLayerSelected) NULL,
223 (VikLayerFuncMarshall) maps_layer_marshall,
224 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
226 (VikLayerFuncSetParam) maps_layer_set_param,
227 (VikLayerFuncGetParam) maps_layer_get_param,
228 (VikLayerFuncChangeParam) maps_layer_change_param,
230 (VikLayerFuncReadFileData) NULL,
231 (VikLayerFuncWriteFileData) NULL,
233 (VikLayerFuncDeleteItem) NULL,
234 (VikLayerFuncCutItem) NULL,
235 (VikLayerFuncCopyItem) NULL,
236 (VikLayerFuncPasteItem) NULL,
237 (VikLayerFuncFreeCopiedItem) NULL,
238 (VikLayerFuncDragDropRequest) NULL,
240 (VikLayerFuncSelectClick) NULL,
241 (VikLayerFuncSelectMove) NULL,
242 (VikLayerFuncSelectRelease) NULL,
243 (VikLayerFuncSelectedViewportMenu) NULL,
246 struct _VikMapsLayer {
252 gdouble xmapzoom, ymapzoom;
254 gboolean autodownload;
255 gboolean adl_only_missing;
256 VikCoord *last_center;
260 gint dl_tool_x, dl_tool_y;
262 GtkMenu *dl_right_click_menu;
263 VikCoord redownload_ul, redownload_br; /* right click menu only */
264 VikViewport *redownload_vvp;
266 #ifdef HAVE_SQLITE3_H
271 enum { REDOWNLOAD_NONE = 0, /* download only missing maps */
272 REDOWNLOAD_BAD, /* download missing and bad maps */
273 REDOWNLOAD_NEW, /* download missing maps that are newer on server only */
274 REDOWNLOAD_ALL, /* download all maps */
275 DOWNLOAD_OR_REFRESH }; /* download missing maps and refresh cache */
277 static VikLayerParam prefs[] = {
278 { 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 },
281 void maps_layer_init ()
283 VikLayerParamData tmp;
284 tmp.s = maps_layer_default_dir();
285 a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
287 gint max_tiles = MAX_TILES;
288 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_MAX_TILES, &max_tiles ) )
289 MAX_TILES = max_tiles;
292 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MIN_SHRINKFACTOR, &gdtmp ) )
293 MIN_SHRINKFACTOR = gdtmp;
295 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MAX_SHRINKFACTOR, &gdtmp ) )
296 MAX_SHRINKFACTOR = gdtmp;
298 if ( a_settings_get_double ( VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR, &gdtmp ) )
299 REAL_MIN_SHRINKFACTOR = gdtmp;
302 /****************************************/
303 /******** MAPS LAYER TYPES **************/
304 /****************************************/
306 void _add_map_source ( guint id, const char *label, VikMapSource *map )
310 len = g_strv_length (params_maptypes);
312 params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
313 params_maptypes[len] = g_strdup (label);
314 params_maptypes[len+1] = NULL;
317 params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
318 params_maptypes_ids[len] = id;
319 params_maptypes_ids[len+1] = 0;
321 /* We have to clone */
322 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
323 /* Register the clone in the list */
324 __map_types = g_list_append(__map_types, clone);
327 We have to ensure the mode LayerParam references the up-to-date
331 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
332 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
334 maps_layer_params[0].widget_data = params_maptypes;
335 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
338 void _update_map_source ( const char *label, VikMapSource *map, int index )
340 GList *item = g_list_nth (__map_types, index);
341 g_object_unref (item->data);
342 item->data = g_object_ref (map);
343 /* Change previous data */
344 g_free (params_maptypes[index]);
345 params_maptypes[index] = g_strdup (label);
349 * maps_layer_register_map_source:
350 * @map: the new VikMapSource
352 * Register a new VikMapSource.
353 * Override existing one (equality of id).
355 void maps_layer_register_map_source ( VikMapSource *map )
357 g_assert(map != NULL);
359 guint id = vik_map_source_get_uniq_id(map);
360 const char *label = vik_map_source_get_label(map);
361 g_assert(label != NULL);
363 int previous = map_uniq_id_to_index (id);
364 if (previous != NUM_MAP_TYPES)
366 _update_map_source (label, map, previous);
370 _add_map_source (id, label, map);
374 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
375 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
376 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
378 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
380 return(vml->maptype);
383 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
385 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
388 /****************************************/
389 /******** CACHE DIR STUFF ***************/
390 /****************************************/
392 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
393 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
394 #define MAPS_CACHE_DIR maps_layer_default_dir()
398 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
399 #define LOCAL_MAPS_DIR "VIKING-MAPS"
400 #elif defined __APPLE__
402 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
403 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
406 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
407 #define LOCAL_MAPS_DIR ".viking-maps"
410 gchar *maps_layer_default_dir ()
412 static gchar *defaultdir = NULL;
415 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
416 const gchar *mapdir = g_getenv("VIKING_MAPS");
418 defaultdir = g_strdup ( mapdir );
419 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
420 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
422 const gchar *home = g_get_home_dir();
423 if (!home || g_access(home, W_OK))
424 home = g_get_home_dir ();
426 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
428 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
430 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
432 /* Add the separator at the end */
433 gchar *tmp = defaultdir;
434 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
437 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
442 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
444 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
446 g_mkdir ( vml->cache_dir, 0777 );
450 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
452 g_assert ( vml != NULL);
453 g_free ( vml->cache_dir );
454 vml->cache_dir = NULL;
455 const gchar *mydir = dir;
457 if ( dir == NULL || dir[0] == '\0' )
459 if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
460 mydir = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s;
463 gchar *canonical_dir = vu_get_canonical_filename ( VIK_LAYER(vml), mydir );
465 // Ensure cache_dir always ends with a separator
466 guint len = strlen(canonical_dir);
467 if ( canonical_dir[len-1] != G_DIR_SEPARATOR )
469 vml->cache_dir = g_strconcat ( canonical_dir, G_DIR_SEPARATOR_S, NULL );
470 g_free ( canonical_dir );
473 vml->cache_dir = canonical_dir;
476 maps_layer_mkdir_if_default_dir ( vml );
479 static void maps_layer_set_file ( VikMapsLayer *vml, const gchar *name )
482 g_free (vml->filename);
483 vml->filename = g_strdup (name);
486 /****************************************/
487 /******** GOBJECT STUFF *****************/
488 /****************************************/
490 GType vik_maps_layer_get_type ()
492 static GType vml_type = 0;
496 static const GTypeInfo vml_info =
498 sizeof (VikMapsLayerClass),
499 NULL, /* base_init */
500 NULL, /* base_finalize */
501 NULL, /* class init */
502 NULL, /* class_finalize */
503 NULL, /* class_data */
504 sizeof (VikMapsLayer),
506 NULL /* instance init */
508 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
514 /****************************************/
515 /************** PARAMETERS **************/
516 /****************************************/
518 static guint map_index_to_uniq_id (guint16 index)
520 g_assert ( index < NUM_MAP_TYPES );
521 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
524 static guint map_uniq_id_to_index ( guint uniq_id )
527 for ( i = 0; i < NUM_MAP_TYPES; i++ )
528 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
530 return NUM_MAP_TYPES; /* no such thing */
533 #define VIK_SETTINGS_MAP_LICENSE_SHOWN "map_license_shown"
536 * Convenience function to display the license
538 static void maps_show_license ( GtkWindow *parent, VikMapSource *map )
540 a_dialog_license ( parent,
541 vik_map_source_get_label (map),
542 vik_map_source_get_license (map),
543 vik_map_source_get_license_url (map) );
546 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
550 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
551 case PARAM_FILE: maps_layer_set_file ( vml, data.s ); break;
552 case PARAM_MAPTYPE: {
553 gint maptype = map_uniq_id_to_index(data.u);
554 if ( maptype == NUM_MAP_TYPES )
555 g_warning(_("Unknown map type"));
557 vml->maptype = maptype;
559 // When loading from a file don't need the license reminder - ensure it's saved into the 'seen' list
560 if ( is_file_operation ) {
561 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, maptype );
564 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
565 if (vik_map_source_get_license (map) != NULL) {
566 // Check if licence for this map type has been shown before
567 if ( ! a_settings_get_integer_list_contains ( VIK_SETTINGS_MAP_LICENSE_SHOWN, maptype ) ) {
569 maps_show_license ( VIK_GTK_WINDOW_FROM_WIDGET(vvp), map );
570 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, maptype );
577 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
578 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
579 case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
580 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
581 vml->mapzoom_id = data.u;
582 vml->xmapzoom = __mapzooms_x [data.u];
583 vml->ymapzoom = __mapzooms_y [data.u];
584 }else g_warning (_("Unknown Map Zoom")); break;
590 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
592 VikLayerParamData rv;
595 case PARAM_CACHE_DIR:
597 gboolean set = FALSE;
598 /* Only save a blank when the map cache location equals the default
599 On reading in, when it is blank then the default is reconstructed
600 Since the default changes dependent on the user and OS, it means the resultant file is more portable */
601 if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
605 else if ( is_file_operation ) {
606 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
607 gchar *cwd = g_get_current_dir();
609 rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
610 if ( !rv.s ) rv.s = "";
616 rv.s = vml->cache_dir ? vml->cache_dir : "";
619 case PARAM_FILE: rv.s = vml->filename; break;
620 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
621 case PARAM_ALPHA: rv.u = vml->alpha; break;
622 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
623 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
624 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
630 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values )
632 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
633 // Alter sensitivity of download option widgets according to the maptype setting.
634 case PARAM_MAPTYPE: {
636 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
637 // Is it *not* the OSM On Disk Tile Layout or the MBTiles type
638 gboolean sensitive = ( 21 != vlpd.u && 23 != vlpd.u);
639 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
640 GtkWidget **ww2 = values[UI_CHG_LABELS];
641 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
642 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
643 GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
644 GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
645 // Depends on autodownload value
646 gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
647 if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
648 if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
649 if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
650 if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
652 // File only applicable for MBTiles type
653 // Directory for all other types
654 sensitive = ( 23 == vlpd.u);
655 GtkWidget *w5 = ww1[PARAM_FILE];
656 GtkWidget *w6 = ww2[PARAM_FILE];
657 GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
658 GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
659 if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
660 if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
661 if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
662 if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
667 // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
668 case PARAM_AUTODOWNLOAD: {
670 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
671 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
672 GtkWidget **ww2 = values[UI_CHG_LABELS];
673 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
674 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
675 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
676 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
683 /****************************************/
684 /****** CREATING, COPYING, FREEING ******/
685 /****************************************/
687 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
689 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
690 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
692 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
694 vml->dl_tool_x = vml->dl_tool_y = -1;
695 vml->last_center = NULL;
696 vml->last_xmpp = 0.0;
697 vml->last_ympp = 0.0;
699 vml->dl_right_click_menu = NULL;
700 vml->filename = NULL;
704 static void maps_layer_free ( VikMapsLayer *vml )
706 g_free ( vml->cache_dir );
707 vml->cache_dir = NULL;
708 if ( vml->dl_right_click_menu )
709 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
710 g_free(vml->last_center);
711 vml->last_center = NULL;
712 g_free ( vml->filename );
713 vml->filename = NULL;
715 #ifdef HAVE_SQLITE3_H
716 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
717 if ( vik_map_source_is_mbtiles ( map ) ) {
718 if ( vml->mbtiles ) {
719 int ans = sqlite3_close ( vml->mbtiles );
720 if ( ans != SQLITE_OK ) {
721 // Only to console for information purposes only
722 g_warning ( "SQL Close problem: %d", ans );
729 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
731 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
732 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
734 if (from_file != TRUE)
736 /* If this method is not called in file reading context
737 * it is called in GUI context.
738 * So, we can check if we have to inform the user about inconsistency */
739 VikViewportDrawMode vp_drawmode;
740 vp_drawmode = vik_viewport_get_drawmode ( vp );
742 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
743 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
744 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
745 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
750 // Performed in post read as we now know the map type
751 #ifdef HAVE_SQLITE3_H
753 if ( vik_map_source_is_mbtiles ( map ) ) {
754 int ans = sqlite3_open_v2 ( vml->filename,
756 SQLITE_OPEN_READONLY,
758 if ( ans != SQLITE_OK ) {
759 // That didn't work, so here's why:
760 g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
762 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
763 _("Failed to open MBTiles file: %s"),
770 // If the on Disk OSM Tile Layout type
771 if ( vml->maptype == 21 )
772 // Copy the directory into filename
773 // thus the mapcache look up will be unique when using more than one of these map types
774 vml->filename = g_strdup (vml->cache_dir);
777 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
779 return vik_maps_layer_get_map_label ( vml );
782 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
784 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
787 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
789 VikMapsLayer *rv = maps_layer_new ( vvp );
790 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
794 /*********************/
795 /****** DRAWING ******/
796 /*********************/
798 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
801 gint width, height, iii, jjj;
803 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
805 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
806 g_object_unref(G_OBJECT(pixbuf));
812 pixels = gdk_pixbuf_get_pixels(pixbuf);
813 width = gdk_pixbuf_get_width(pixbuf);
814 height = gdk_pixbuf_get_height(pixbuf);
816 /* r,g,b,a,r,g,b,a.... */
817 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
825 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
828 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
829 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
830 g_object_unref ( G_OBJECT(pixbuf) );
834 #ifdef HAVE_SQLITE3_H
836 static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
838 g_warning ( "Found %d columns", cols );
840 for ( i = 0; i < cols; i++ ) {
841 g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
850 static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
852 GdkPixbuf *pixbuf = NULL;
854 // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
855 gint flip_y = (gint) pow(2, zoom)-1 - yy;
856 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 );
858 gboolean finished = FALSE;
860 sqlite3_stmt *sql_stmt = NULL;
861 int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
862 if ( ans != SQLITE_OK ) {
863 g_warning ( "%s: %s - %d", __FUNCTION__, "prepare failure", ans );
867 while ( !finished ) {
868 ans = sqlite3_step ( sql_stmt );
871 // Get tile_data blob
872 int count = sqlite3_column_count(sql_stmt);
874 g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
878 const void *data = sqlite3_column_blob ( sql_stmt, 0 );
879 int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
881 g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
885 // Convert these blob bytes into a pixbuf via these streaming operations
886 GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
887 GError *error = NULL;
888 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
889 if (error || (!pixbuf)) {
890 g_warning ( "%s: %s", __FUNCTION__, error->message );
891 g_error_free ( error );
893 g_input_stream_close ( stream, NULL, NULL );
899 // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
901 // and give up on any errors
902 if ( ans != SQLITE_DONE )
903 g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
908 ans = sqlite3_finalize ( sql_stmt );
910 g_free ( statement );
916 static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
918 GdkPixbuf *pixbuf = NULL;
920 #ifdef HAVE_SQLITE3_H
921 if ( vml->mbtiles ) {
923 gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
925 int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
926 if ( ans != SQLITE_OK ) {
927 // Only to console for information purposes only
928 g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
929 sqlite3_free( errMsg );
931 g_free ( statement );
934 // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
935 // Hence this specific function
936 pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
943 static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
945 // Apply alpha setting
946 if ( pixbuf && vml->alpha < 255 )
947 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
949 if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
950 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
953 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
954 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
955 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
960 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
965 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
966 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
969 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
970 // ATM MBTiles must be 'a direct access type'
971 if ( vik_map_source_is_mbtiles (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
972 pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
973 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
974 // return now to avoid file tests that aren't appropriate for this map type
978 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS,
979 vml->cache_dir, (17 - mapcoord->scale), mapcoord->x, mapcoord->y, ".png" );
982 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
983 vml->cache_dir, mode,
984 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
986 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
989 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
991 /* free the pixbuf on error */
994 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE ) {
996 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
997 gchar* msg = g_strdup_printf ( _("Couldn't open image file: %s"), gx->message );
998 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1003 g_error_free ( gx );
1005 g_object_unref ( G_OBJECT(pixbuf) );
1008 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1015 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
1017 const VikCoord *center = vik_viewport_get_center ( vvp );
1019 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
1020 /* D'n'D pan in action: do not download */
1024 // Prevent requests for downloading tiles at Zoom Level 19 and above for most map types
1025 // Allow MapQuest Zoom Level up to 19
1026 // TODO: This should be made a property of the map source and then use that value
1027 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1028 if ( (vml->maptype != 19 && map_utils_mpp_to_scale (xzoom) < -1) || (vml->maptype == 19 && map_utils_mpp_to_scale (xzoom) < -2) )
1031 if (vml->last_center == NULL) {
1032 VikCoord *new_center = g_malloc(sizeof(VikCoord));
1033 *new_center = *center;
1034 vml->last_center = new_center;
1035 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1036 vml->last_ympp = vik_viewport_get_ympp(vvp);
1040 /* TODO: perhaps vik_coord_diff() */
1041 if (vik_coord_equals(vml->last_center, center)
1042 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1043 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1046 *(vml->last_center) = *center;
1047 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1048 vml->last_ympp = vik_viewport_get_ympp(vvp);
1052 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1055 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1056 gdouble yzoom = vik_viewport_get_ympp ( vvp );
1057 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
1058 gboolean existence_only = FALSE;
1060 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1061 xshrinkfactor = vml->xmapzoom / xzoom;
1062 yshrinkfactor = vml->ymapzoom / yzoom;
1063 xzoom = vml->xmapzoom;
1064 yzoom = vml->xmapzoom;
1065 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1066 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
1067 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1068 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
1069 existence_only = TRUE;
1072 // Report the reason for not drawing
1073 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1074 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));
1075 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1084 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1085 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1086 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
1090 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1091 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
1092 gint mode = vik_map_source_get_uniq_id(map);
1095 gint xx, yy, width, height;
1098 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1099 // which can happen when using a small fixed zoom level and viewing large areas.
1100 // Also prevents very large number of tile download requests
1101 gint tiles = (xmax-xmin) * (ymax-ymin);
1102 if ( tiles > MAX_TILES ) {
1103 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1104 existence_only = TRUE;
1107 guint max_path_len = strlen(vml->cache_dir) + 40;
1108 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1110 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
1111 g_debug("%s: Starting autodownload", __FUNCTION__);
1112 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
1113 // Try to download newer tiles
1114 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1116 // Download only missing tiles
1117 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
1120 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
1121 for ( x = xmin; x <= xmax; x++ ) {
1122 for ( y = ymin; y <= ymax; y++ ) {
1125 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1127 width = gdk_pixbuf_get_width ( pixbuf );
1128 height = gdk_pixbuf_get_height ( pixbuf );
1130 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1131 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1135 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1139 } else { /* tilesize is known, don't have to keep converting coords */
1140 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1141 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
1142 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1143 gint tilesize_x_ceil = ceil ( tilesize_x );
1144 gint tilesize_y_ceil = ceil ( tilesize_y );
1145 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1146 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
1147 gint xx_tmp, yy_tmp;
1148 gint base_yy, xend, yend;
1150 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1151 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1153 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1154 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1155 xx = xx_tmp; yy = yy_tmp;
1156 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1157 * eg if tile size 128, shrinkfactor 0.333 */
1158 xx -= (tilesize_x/2);
1159 base_yy = yy - (tilesize_y/2);
1161 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1163 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1167 if ( existence_only ) {
1168 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1169 g_snprintf ( path_buf, max_path_len, DIRECTDIRACCESS,
1170 vml->cache_dir, (17 - ulm.scale), ulm.x, ulm.y, ".png" );
1172 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
1173 vml->cache_dir, mode,
1174 ulm.scale, ulm.z, ulm.x, ulm.y );
1175 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1176 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1177 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1181 for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
1182 /* try with correct then smaller zooms */
1183 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
1184 MapCoord ulm2 = ulm;
1185 ulm2.x = ulm.x / scale_factor;
1186 ulm2.y = ulm.y / scale_factor;
1187 ulm2.scale = ulm.scale + scale_inc;
1188 pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1190 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1191 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1193 printf("maps_layer_draw_section - x=%d, y=%d, z=%d, src_x=%d, src_y=%d, xx=%d, yy=%d - %x\n", ulm.x, ulm.y, ulm.scale, src_x, src_y, (int)xx, (int)yy, vvp);
1195 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1200 /* retry with bigger zooms */
1202 for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
1204 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
1205 MapCoord ulm2 = ulm;
1206 ulm2.x = ulm.x * scale_factor;
1207 ulm2.y = ulm.y * scale_factor;
1208 ulm2.scale = ulm.scale - scale_dec;
1209 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1210 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1211 MapCoord ulm3 = ulm2;
1214 pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1218 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1219 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1220 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1234 g_free ( path_buf );
1238 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1240 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
1245 gdouble level = vik_viewport_get_zoom ( vvp );
1247 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1248 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
1251 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1252 vik_viewport_add_logo ( vvp, logo );
1254 /* get corner coords */
1255 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1256 /* UTM multi-zone stuff by Kit Transue */
1257 gchar leftmost_zone, rightmost_zone, i;
1258 leftmost_zone = vik_viewport_leftmost_zone( vvp );
1259 rightmost_zone = vik_viewport_rightmost_zone( vvp );
1260 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1261 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1262 maps_layer_draw_section ( vml, vvp, &ul, &br );
1266 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1267 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1269 maps_layer_draw_section ( vml, vvp, &ul, &br );
1274 /*************************/
1275 /****** DOWNLOADING ******/
1276 /*************************/
1278 /* pass along data to thread, exists even if layer is deleted. */
1281 gchar *filename_buf;
1282 gint x0, y0, xf, yf;
1288 gboolean refresh_display;
1291 gboolean map_layer_alive;
1295 static void mdi_free ( MapDownloadInfo *mdi )
1297 g_mutex_free(mdi->mutex);
1298 g_free ( mdi->cache_dir );
1299 mdi->cache_dir = NULL;
1300 g_free ( mdi->filename_buf );
1301 mdi->filename_buf = NULL;
1305 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1307 MapDownloadInfo *mdi = ptr;
1308 g_mutex_lock(mdi->mutex);
1309 mdi->map_layer_alive = FALSE;
1310 g_mutex_unlock(mdi->mutex);
1313 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1315 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1318 for ( x = mdi->x0; x <= mdi->xf; x++ )
1320 for ( y = mdi->y0; y <= mdi->yf; y++ )
1322 gboolean remove_mem_cache = FALSE;
1323 gboolean need_download = FALSE;
1324 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1325 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1326 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
1329 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1331 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1335 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1336 need_download = TRUE;
1337 remove_mem_cache = TRUE;
1339 } else { /* in case map file already exists */
1340 switch (mdi->redownload) {
1341 case REDOWNLOAD_NONE:
1344 case REDOWNLOAD_BAD:
1346 /* see if this one is bad or what */
1348 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1349 if (gx || (!pixbuf)) {
1350 g_remove ( mdi->filename_buf );
1351 need_download = TRUE;
1352 remove_mem_cache = TRUE;
1353 g_error_free ( gx );
1356 g_object_unref ( pixbuf );
1361 case REDOWNLOAD_NEW:
1362 need_download = TRUE;
1363 remove_mem_cache = TRUE;
1366 case REDOWNLOAD_ALL:
1367 /* FIXME: need a better way than to erase file in case of server/network problem */
1368 g_remove ( mdi->filename_buf );
1369 need_download = TRUE;
1370 remove_mem_cache = TRUE;
1373 case DOWNLOAD_OR_REFRESH:
1374 remove_mem_cache = TRUE;
1378 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1382 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1384 if (need_download) {
1385 DownloadResult_t dr = vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle);
1387 case DOWNLOAD_HTTP_ERROR:
1388 case DOWNLOAD_CONTENT_ERROR: {
1389 // TODO: ?? count up the number of download errors somehow...
1390 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Failed to download tile") );
1391 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1395 case DOWNLOAD_FILE_WRITE_ERROR: {
1396 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Unable to save tile") );
1397 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1401 case DOWNLOAD_SUCCESS:
1402 case DOWNLOAD_NOT_REQUIRED:
1408 g_mutex_lock(mdi->mutex);
1409 if (remove_mem_cache)
1410 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 );
1411 if (mdi->refresh_display && mdi->map_layer_alive) {
1412 /* TODO: check if it's on visible area */
1413 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1415 g_mutex_unlock(mdi->mutex);
1416 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1420 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1421 g_mutex_lock(mdi->mutex);
1422 if (mdi->map_layer_alive)
1423 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1424 g_mutex_unlock(mdi->mutex);
1428 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1430 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1432 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1433 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1434 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
1435 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1437 g_remove ( mdi->filename_buf );
1442 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1444 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1445 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1447 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1449 // Don't ever attempt download on direct access
1450 if ( vik_map_source_is_direct_file_access ( map ) )
1453 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1454 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1456 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1461 mdi->map_layer_alive = TRUE;
1462 mdi->mutex = g_mutex_new();
1463 mdi->refresh_display = TRUE;
1465 /* cache_dir and buffer for dest filename */
1466 mdi->cache_dir = g_strdup ( vml->cache_dir );
1467 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1468 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1469 mdi->maptype = vml->maptype;
1471 mdi->mapcoord = ulm;
1472 mdi->redownload = redownload;
1474 mdi->x0 = MIN(ulm.x, brm.x);
1475 mdi->xf = MAX(ulm.x, brm.x);
1476 mdi->y0 = MIN(ulm.y, brm.y);
1477 mdi->yf = MAX(ulm.y, brm.y);
1481 if ( mdi->redownload ) {
1482 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1484 /* calculate how many we need */
1485 for ( a = mdi->x0; a <= mdi->xf; a++ )
1487 for ( b = mdi->y0; b <= mdi->yf; b++ )
1489 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1490 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1492 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1498 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1500 if ( mdi->mapstoget )
1502 const gchar *tmp_str;
1507 if (redownload == REDOWNLOAD_BAD)
1508 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1510 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1514 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1516 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1518 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1519 /* launch the thread */
1520 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1521 tmp, /* description string */
1522 (vik_thr_func) map_download_thread, /* function to call within thread */
1523 mdi, /* pass along data */
1524 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1525 (vik_thr_free_func) mdi_cancel_cleanup,
1534 static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1537 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1539 // Don't ever attempt download on direct access
1540 if ( vik_map_source_is_direct_file_access ( map ) )
1543 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1544 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1545 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1549 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1554 mdi->map_layer_alive = TRUE;
1555 mdi->mutex = g_mutex_new();
1556 mdi->refresh_display = TRUE;
1558 mdi->cache_dir = g_strdup ( vml->cache_dir );
1559 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1560 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1561 mdi->maptype = vml->maptype;
1563 mdi->mapcoord = ulm;
1564 mdi->redownload = download_method;
1566 mdi->x0 = MIN(ulm.x, brm.x);
1567 mdi->xf = MAX(ulm.x, brm.x);
1568 mdi->y0 = MIN(ulm.y, brm.y);
1569 mdi->yf = MAX(ulm.y, brm.y);
1573 for (i = mdi->x0; i <= mdi->xf; i++) {
1574 for (j = mdi->y0; j <= mdi->yf; j++) {
1575 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1576 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1578 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1583 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1585 if (mdi->mapstoget) {
1588 fmt = ngettext("Downloading %d %s map...",
1589 "Downloading %d %s maps...",
1591 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1593 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1594 /* launch the thread */
1595 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1596 tmp, /* description string */
1597 (vik_thr_func) map_download_thread, /* function to call within thread */
1598 mdi, /* pass along data */
1599 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1600 (vik_thr_free_func) mdi_cancel_cleanup,
1609 * vik_maps_layer_download_section:
1610 * @vml: The Map Layer
1611 * @vvp: The Viewport that the map is on
1612 * @ul: Upper left coordinate of the area to be downloaded
1613 * @br: Bottom right coordinate of the area to be downloaded
1614 * @zoom: The zoom level at which the maps are to be download
1616 * Download a specified map area at a certain zoom level
1618 void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1620 maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1623 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1625 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1628 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1630 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1633 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1635 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1639 * Display a simple dialog with information about this particular map tile
1641 static void maps_layer_tile_info ( VikMapsLayer *vml )
1643 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1645 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1646 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1649 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1652 gchar *filename = NULL;
1653 gchar *message = NULL;
1654 gchar *source = NULL;
1656 if ( vik_map_source_is_direct_file_access ( map ) ) {
1657 if ( vik_map_source_is_mbtiles ( map ) ) {
1658 filename = g_strdup ( vml->filename );
1659 #ifdef HAVE_SQLITE3_H
1660 // And whether to bother going into the SQL to check it's really there or not...
1661 gchar *exists = NULL;
1662 gint zoom = 17 - ulm.scale;
1663 if ( vml->mbtiles ) {
1664 GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, zoom );
1666 exists = g_strdup ( _("YES") );
1667 g_object_unref ( G_OBJECT(pixbuf) );
1670 exists = g_strdup ( _("NO") );
1674 exists = g_strdup ( _("NO") );
1675 gint flip_y = (gint) pow(2, zoom)-1 - ulm.y;
1676 // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1677 source = g_strdup_printf ( "%s (%d%s%d%s%d.%s %s)", filename, zoom, G_DIR_SEPARATOR_S, ulm.x, G_DIR_SEPARATOR_S, flip_y, "png", exists );
1680 source = g_strdup ( _("Not available") );
1684 filename = g_strdup_printf ( DIRECTDIRACCESS, vml->cache_dir, ulm.scale, ulm.x, ulm.y, ".png" );
1685 source = g_strconcat ( "file://", filename, NULL );
1689 filename = g_strdup_printf ( DIRSTRUCTURE, vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale, ulm.z, ulm.x, ulm.y );
1690 source = g_strdup_printf ( "http://%s%s",
1691 vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1692 vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1695 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1697 // Get some timestamp information of the tile
1698 struct stat stat_buf;
1699 if ( g_stat ( filename, &stat_buf ) == 0 ) {
1701 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1702 message = g_strdup_printf ( _("\nSource: %s\n\nTile File: %s\nTile File Timestamp: %s"), source, filename, time_buf );
1706 message = g_strdup_printf ( _("Source: %s\n\nNo Tile File!"), source );
1709 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), message );
1713 g_free ( filename );
1716 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1718 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1720 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1722 if ( event->button == 1 )
1725 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 );
1726 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 );
1727 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1728 vml->dl_tool_x = vml->dl_tool_y = -1;
1733 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) );
1734 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) );
1736 vml->redownload_vvp = vvp;
1738 vml->dl_tool_x = vml->dl_tool_y = -1;
1740 if ( ! vml->dl_right_click_menu ) {
1742 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1744 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1745 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1746 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1748 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1749 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1750 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1752 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1753 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1754 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1756 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
1757 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1758 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
1759 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
1762 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1763 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1769 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1774 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1777 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1779 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1780 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1781 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1782 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1783 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1785 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1791 // A slightly better way of defining the menu callback information
1792 // This should be easier to extend/rework compared to previously
1799 typedef gpointer menu_array_values[MA_LAST];
1801 static void download_onscreen_maps ( menu_array_values values, gint redownload )
1803 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
1804 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
1805 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1807 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1808 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1813 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1814 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1816 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1817 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1818 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1819 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1820 start_download_thread ( vml, vvp, &ul, &br, redownload );
1821 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1822 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1823 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1824 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1828 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1832 static void maps_layer_download_missing_onscreen_maps ( menu_array_values values )
1834 download_onscreen_maps( values, REDOWNLOAD_NONE);
1837 static void maps_layer_download_new_onscreen_maps ( menu_array_values values )
1839 download_onscreen_maps( values, REDOWNLOAD_NEW);
1842 static void maps_layer_redownload_all_onscreen_maps ( menu_array_values values )
1844 download_onscreen_maps( values, REDOWNLOAD_ALL);
1847 static void maps_layers_about ( gpointer vml_vvp[2] )
1849 VikMapsLayer *vml = vml_vvp[0];
1850 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1852 if ( vik_map_source_get_license (map) )
1853 maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
1855 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
1856 vik_map_source_get_label (map) );
1860 * maps_layer_how_many_maps:
1861 * Copied from maps_layer_download_section but without the actual download and this returns a value
1863 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
1866 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1868 if ( vik_map_source_is_direct_file_access ( map ) )
1871 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1872 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1873 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1877 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1882 mdi->map_layer_alive = TRUE;
1883 mdi->mutex = g_mutex_new();
1884 mdi->refresh_display = FALSE;
1886 mdi->cache_dir = g_strdup ( vml->cache_dir );
1887 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1888 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1889 mdi->maptype = vml->maptype;
1891 mdi->mapcoord = ulm;
1892 mdi->redownload = redownload;
1894 mdi->x0 = MIN(ulm.x, brm.x);
1895 mdi->xf = MAX(ulm.x, brm.x);
1896 mdi->y0 = MIN(ulm.y, brm.y);
1897 mdi->yf = MAX(ulm.y, brm.y);
1901 if ( mdi->redownload == REDOWNLOAD_ALL ) {
1902 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1905 /* calculate how many we need */
1906 for (i = mdi->x0; i <= mdi->xf; i++) {
1907 for (j = mdi->y0; j <= mdi->yf; j++) {
1908 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1909 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1911 if ( mdi->redownload == REDOWNLOAD_NEW ) {
1912 // Assume the worst - always a new file
1913 // Absolute value would requires server lookup - but that is too slow
1917 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1922 if ( mdi->redownload == REDOWNLOAD_BAD ) {
1923 /* see if this one is bad or what */
1925 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1926 if (gx || (!pixbuf)) {
1930 // Other download cases already considered or just ignored
1938 gint rv = mdi->mapstoget;
1946 * maps_dialog_zoom_between:
1947 * This dialog is specific to the map layer, so it's here rather than in dialog.c
1949 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
1954 gint *selected_zoom1,
1955 gint *selected_zoom2,
1956 gchar *download_list[],
1957 gint default_download,
1958 gint *selected_download )
1960 GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
1962 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1963 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1964 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1966 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1967 GtkWidget *response_w = NULL;
1968 #if GTK_CHECK_VERSION (2, 20, 0)
1969 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1971 GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
1972 GtkWidget *zoom_combo1 = vik_combo_box_text_new();
1974 for (s = zoom_list; *s; s++)
1975 vik_combo_box_text_append ( zoom_combo1, *s );
1976 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
1978 GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
1979 GtkWidget *zoom_combo2 = vik_combo_box_text_new();
1980 for (s = zoom_list; *s; s++)
1981 vik_combo_box_text_append ( zoom_combo2, *s );
1982 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
1984 GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
1985 GtkWidget *download_combo = vik_combo_box_text_new();
1986 for (s = download_list; *s; s++)
1987 vik_combo_box_text_append ( download_combo, *s );
1988 gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
1990 GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
1991 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
1992 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
1993 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
1994 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
1995 gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
1996 gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
1998 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
2001 gtk_widget_grab_focus ( response_w );
2003 gtk_widget_show_all ( dialog );
2004 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2005 gtk_widget_destroy(dialog);
2009 // Return selected options
2010 *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
2011 *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
2012 *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
2014 gtk_widget_destroy(dialog);
2018 // My best guess of sensible limits
2019 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
2020 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2023 * Get all maps in the region for zoom levels specified by the user
2024 * Sort of similar to trw_layer_download_map_along_track_cb function
2026 static void maps_layer_download_all ( menu_array_values values )
2028 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2029 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2031 // I don't think we should allow users to hammer the servers too much...
2032 // Delibrately not allowing lowest zoom levels
2033 // Still can give massive numbers to download
2034 // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2035 gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2036 gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2038 gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2039 gint selected_download_method;
2041 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2043 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2044 if (cur_zoom == zoom_vals[default_zoom])
2047 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2049 // Default to only 2 zoom levels below the current one
2050 if (default_zoom > 1 )
2051 lower_zoom = default_zoom - 2;
2053 lower_zoom = default_zoom;
2055 // redownload method - needs to align with REDOWNLOAD* macro values
2056 gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2058 gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2060 if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2068 REDOWNLOAD_NONE, // AKA Missing
2069 &selected_download_method ) ) {
2076 // Find out new current positions
2077 gdouble min_lat, max_lat, min_lon, max_lon;
2078 VikCoord vc_ul, vc_br;
2079 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2080 struct LatLon ll_ul = { max_lat, min_lon };
2081 struct LatLon ll_br = { min_lat, max_lon };
2082 vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2083 vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2085 // Get Maps Count - call for each zoom level (in reverse)
2086 // With REDOWNLOAD_NEW this is a possible maximum
2087 // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2090 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2091 map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2094 g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2096 // Absolute protection of hammering a map server
2097 if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2098 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);
2099 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2104 // Confirm really want to do this
2105 if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2106 gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2107 gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2113 // Get Maps - call for each zoom level (in reverse)
2114 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2115 maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2119 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2122 static menu_array_values values;
2123 values[MA_VML] = vml;
2124 values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2126 item = gtk_menu_item_new();
2127 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2128 gtk_widget_show ( item );
2130 /* Now with icons */
2131 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2132 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2133 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), values );
2134 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2135 gtk_widget_show ( item );
2137 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2138 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2139 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2140 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), values );
2141 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2142 gtk_widget_show ( item );
2145 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2146 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2147 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), values );
2148 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2149 gtk_widget_show ( item );
2151 item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2152 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), values );
2154 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2155 gtk_widget_show ( item );
2157 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2158 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layers_about), values );
2159 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2160 gtk_widget_show ( item );
2164 * Enable downloading maps of the current screen area either 'new' or 'everything'
2166 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2171 static menu_array_values values;
2172 values[MA_VML] = vml;
2173 values[MA_VVP] = vvp;
2176 // Get only new maps
2177 maps_layer_download_new_onscreen_maps ( values );
2179 // Redownload everything
2180 maps_layer_redownload_all_onscreen_maps ( values );