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 },
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") },
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;
589 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
591 VikLayerParamData rv;
594 case PARAM_CACHE_DIR:
596 gboolean set = FALSE;
597 /* Only save a blank when the map cache location equals the default
598 On reading in, when it is blank then the default is reconstructed
599 Since the default changes dependent on the user and OS, it means the resultant file is more portable */
600 if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
604 else if ( is_file_operation ) {
605 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
606 gchar *cwd = g_get_current_dir();
608 rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
609 if ( !rv.s ) rv.s = "";
615 rv.s = vml->cache_dir ? vml->cache_dir : "";
618 case PARAM_FILE: rv.s = vml->filename; break;
619 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
620 case PARAM_ALPHA: rv.u = vml->alpha; break;
621 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
622 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
623 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
628 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values )
630 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
631 // Alter sensitivity of download option widgets according to the maptype setting.
632 case PARAM_MAPTYPE: {
634 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
635 // Is it *not* the OSM On Disk Tile Layout or the MBTiles type
636 gboolean sensitive = ( 21 != vlpd.u && 23 != vlpd.u);
637 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
638 GtkWidget **ww2 = values[UI_CHG_LABELS];
639 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
640 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
641 GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
642 GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
643 // Depends on autodownload value
644 gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
645 if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
646 if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
647 if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
648 if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
650 // File only applicable for MBTiles type
651 // Directory for all other types
652 sensitive = ( 23 == vlpd.u);
653 GtkWidget *w5 = ww1[PARAM_FILE];
654 GtkWidget *w6 = ww2[PARAM_FILE];
655 GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
656 GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
657 if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
658 if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
659 if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
660 if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
665 // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
666 case PARAM_AUTODOWNLOAD: {
668 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
669 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
670 GtkWidget **ww2 = values[UI_CHG_LABELS];
671 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
672 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
673 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
674 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
681 /****************************************/
682 /****** CREATING, COPYING, FREEING ******/
683 /****************************************/
685 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
687 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
688 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
690 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
692 vml->dl_tool_x = vml->dl_tool_y = -1;
693 vml->last_center = NULL;
694 vml->last_xmpp = 0.0;
695 vml->last_ympp = 0.0;
697 vml->dl_right_click_menu = NULL;
698 vml->filename = NULL;
702 static void maps_layer_free ( VikMapsLayer *vml )
704 g_free ( vml->cache_dir );
705 vml->cache_dir = NULL;
706 if ( vml->dl_right_click_menu )
707 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
708 g_free(vml->last_center);
709 vml->last_center = NULL;
710 g_free ( vml->filename );
711 vml->filename = NULL;
713 #ifdef HAVE_SQLITE3_H
714 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
715 if ( vik_map_source_is_mbtiles ( map ) ) {
716 if ( vml->mbtiles ) {
717 int ans = sqlite3_close ( vml->mbtiles );
718 if ( ans != SQLITE_OK ) {
719 // Only to console for information purposes only
720 g_warning ( "SQL Close problem: %d", ans );
727 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
729 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
730 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
732 if (from_file != TRUE)
734 /* If this method is not called in file reading context
735 * it is called in GUI context.
736 * So, we can check if we have to inform the user about inconsistency */
737 VikViewportDrawMode vp_drawmode;
738 vp_drawmode = vik_viewport_get_drawmode ( vp );
740 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
741 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
742 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
743 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
748 // Performed in post read as we now know the map type
749 #ifdef HAVE_SQLITE3_H
751 if ( vik_map_source_is_mbtiles ( map ) ) {
752 int ans = sqlite3_open_v2 ( vml->filename,
754 SQLITE_OPEN_READONLY,
756 if ( ans != SQLITE_OK ) {
757 // That didn't work, so here's why:
758 g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
760 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
761 _("Failed to open MBTiles file: %s"),
768 // If the on Disk OSM Tile Layout type
769 if ( vml->maptype == 21 )
770 // Copy the directory into filename
771 // thus the mapcache look up will be unique when using more than one of these map types
772 vml->filename = g_strdup (vml->cache_dir);
775 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
777 return vik_maps_layer_get_map_label ( vml );
780 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
782 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
785 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
787 VikMapsLayer *rv = maps_layer_new ( vvp );
788 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
792 /*********************/
793 /****** DRAWING ******/
794 /*********************/
796 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
799 gint width, height, iii, jjj;
801 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
803 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
804 g_object_unref(G_OBJECT(pixbuf));
810 pixels = gdk_pixbuf_get_pixels(pixbuf);
811 width = gdk_pixbuf_get_width(pixbuf);
812 height = gdk_pixbuf_get_height(pixbuf);
814 /* r,g,b,a,r,g,b,a.... */
815 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
823 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
826 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
827 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
828 g_object_unref ( G_OBJECT(pixbuf) );
832 #ifdef HAVE_SQLITE3_H
834 static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
836 g_warning ( "Found %d columns", cols );
838 for ( i = 0; i < cols; i++ ) {
839 g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
848 static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
850 GdkPixbuf *pixbuf = NULL;
852 // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
853 gint flip_y = (gint) pow(2, zoom)-1 - yy;
854 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 );
856 gboolean finished = FALSE;
858 sqlite3_stmt *sql_stmt = NULL;
859 int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
860 if ( ans != SQLITE_OK ) {
861 g_warning ( "%s: %s - %d", __FUNCTION__, "prepare failure", ans );
865 while ( !finished ) {
866 ans = sqlite3_step ( sql_stmt );
869 // Get tile_data blob
870 int count = sqlite3_column_count(sql_stmt);
872 g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
876 const void *data = sqlite3_column_blob ( sql_stmt, 0 );
877 int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
879 g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
883 // Convert these blob bytes into a pixbuf via these streaming operations
884 GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
885 GError *error = NULL;
886 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
887 if (error || (!pixbuf)) {
888 g_warning ( "%s: %s", __FUNCTION__, error->message );
889 g_error_free ( error );
891 g_input_stream_close ( stream, NULL, NULL );
897 // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
899 // and give up on any errors
900 if ( ans != SQLITE_DONE )
901 g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
906 ans = sqlite3_finalize ( sql_stmt );
908 g_free ( statement );
914 static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
916 GdkPixbuf *pixbuf = NULL;
918 #ifdef HAVE_SQLITE3_H
919 if ( vml->mbtiles ) {
921 gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
923 int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
924 if ( ans != SQLITE_OK ) {
925 // Only to console for information purposes only
926 g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
927 sqlite3_free( errMsg );
929 g_free ( statement );
932 // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
933 // Hence this specific function
934 pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
941 static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
943 // Apply alpha setting
944 if ( pixbuf && vml->alpha < 255 )
945 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
947 if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
948 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
951 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
952 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
953 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
958 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
963 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
964 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
967 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
968 // ATM MBTiles must be 'a direct access type'
969 if ( vik_map_source_is_mbtiles (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
970 pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
971 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
972 // return now to avoid file tests that aren't appropriate for this map type
976 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS,
977 vml->cache_dir, (17 - mapcoord->scale), mapcoord->x, mapcoord->y, ".png" );
980 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
981 vml->cache_dir, mode,
982 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
984 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
987 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
989 /* free the pixbuf on error */
992 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
993 g_warning ( _("Couldn't open image file: %s"), gx->message );
997 g_object_unref ( G_OBJECT(pixbuf) );
1000 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1007 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
1009 const VikCoord *center = vik_viewport_get_center ( vvp );
1011 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
1012 /* D'n'D pan in action: do not download */
1016 // Prevent requests for downloading tiles at Zoom Level 19 and above for most map types
1017 // Allow MapQuest Zoom Level up to 19
1018 // TODO: This should be made a property of the map source and then use that value
1019 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1020 if ( (vml->maptype != 19 && map_utils_mpp_to_scale (xzoom) < -1) || (vml->maptype == 19 && map_utils_mpp_to_scale (xzoom) < -2) )
1023 if (vml->last_center == NULL) {
1024 VikCoord *new_center = g_malloc(sizeof(VikCoord));
1025 *new_center = *center;
1026 vml->last_center = new_center;
1027 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1028 vml->last_ympp = vik_viewport_get_ympp(vvp);
1032 /* TODO: perhaps vik_coord_diff() */
1033 if (vik_coord_equals(vml->last_center, center)
1034 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1035 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1038 *(vml->last_center) = *center;
1039 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1040 vml->last_ympp = vik_viewport_get_ympp(vvp);
1044 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1047 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1048 gdouble yzoom = vik_viewport_get_ympp ( vvp );
1049 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
1050 gboolean existence_only = FALSE;
1052 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1053 xshrinkfactor = vml->xmapzoom / xzoom;
1054 yshrinkfactor = vml->ymapzoom / yzoom;
1055 xzoom = vml->xmapzoom;
1056 yzoom = vml->xmapzoom;
1057 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1058 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
1059 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1060 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
1061 existence_only = TRUE;
1064 g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
1071 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1072 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1073 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
1077 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1078 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
1079 gint mode = vik_map_source_get_uniq_id(map);
1082 gint xx, yy, width, height;
1085 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1086 // which can happen when using a small fixed zoom level and viewing large areas.
1087 // Also prevents very large number of tile download requests
1088 gint tiles = (xmax-xmin) * (ymax-ymin);
1089 if ( tiles > MAX_TILES ) {
1090 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1091 existence_only = TRUE;
1094 guint max_path_len = strlen(vml->cache_dir) + 40;
1095 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1097 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
1098 g_debug("%s: Starting autodownload", __FUNCTION__);
1099 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
1100 // Try to download newer tiles
1101 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1103 // Download only missing tiles
1104 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
1107 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
1108 for ( x = xmin; x <= xmax; x++ ) {
1109 for ( y = ymin; y <= ymax; y++ ) {
1112 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1114 width = gdk_pixbuf_get_width ( pixbuf );
1115 height = gdk_pixbuf_get_height ( pixbuf );
1117 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1118 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1122 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1126 } else { /* tilesize is known, don't have to keep converting coords */
1127 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1128 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
1129 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1130 gint tilesize_x_ceil = ceil ( tilesize_x );
1131 gint tilesize_y_ceil = ceil ( tilesize_y );
1132 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1133 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
1134 gdouble xx, yy; gint xx_tmp, yy_tmp;
1135 gint base_yy, xend, yend;
1137 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1138 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1140 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1141 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1142 xx = xx_tmp; yy = yy_tmp;
1143 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1144 * eg if tile size 128, shrinkfactor 0.333 */
1145 xx -= (tilesize_x/2);
1146 base_yy = yy - (tilesize_y/2);
1148 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1150 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1154 if ( existence_only ) {
1155 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1156 g_snprintf ( path_buf, max_path_len, DIRECTDIRACCESS,
1157 vml->cache_dir, (17 - ulm.scale), ulm.x, ulm.y, ".png" );
1159 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
1160 vml->cache_dir, mode,
1161 ulm.scale, ulm.z, ulm.x, ulm.y );
1162 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1163 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1164 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1168 for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
1169 /* try with correct then smaller zooms */
1170 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
1171 MapCoord ulm2 = ulm;
1172 ulm2.x = ulm.x / scale_factor;
1173 ulm2.y = ulm.y / scale_factor;
1174 ulm2.scale = ulm.scale + scale_inc;
1175 pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1177 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1178 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1180 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);
1182 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1187 /* retry with bigger zooms */
1189 for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
1191 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
1192 MapCoord ulm2 = ulm;
1193 ulm2.x = ulm.x * scale_factor;
1194 ulm2.y = ulm.y * scale_factor;
1195 ulm2.scale = ulm.scale - scale_dec;
1196 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1197 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1198 MapCoord ulm3 = ulm2;
1201 pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1205 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1206 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1207 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1221 g_free ( path_buf );
1225 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1227 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
1232 gdouble level = vik_viewport_get_zoom ( vvp );
1234 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1235 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
1238 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1239 vik_viewport_add_logo ( vvp, logo );
1241 /* get corner coords */
1242 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1243 /* UTM multi-zone stuff by Kit Transue */
1244 gchar leftmost_zone, rightmost_zone, i;
1245 leftmost_zone = vik_viewport_leftmost_zone( vvp );
1246 rightmost_zone = vik_viewport_rightmost_zone( vvp );
1247 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1248 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1249 maps_layer_draw_section ( vml, vvp, &ul, &br );
1253 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1254 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1256 maps_layer_draw_section ( vml, vvp, &ul, &br );
1261 /*************************/
1262 /****** DOWNLOADING ******/
1263 /*************************/
1265 /* pass along data to thread, exists even if layer is deleted. */
1268 gchar *filename_buf;
1269 gint x0, y0, xf, yf;
1275 gboolean refresh_display;
1278 gboolean map_layer_alive;
1282 static void mdi_free ( MapDownloadInfo *mdi )
1284 g_mutex_free(mdi->mutex);
1285 g_free ( mdi->cache_dir );
1286 mdi->cache_dir = NULL;
1287 g_free ( mdi->filename_buf );
1288 mdi->filename_buf = NULL;
1292 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1294 MapDownloadInfo *mdi = ptr;
1295 g_mutex_lock(mdi->mutex);
1296 mdi->map_layer_alive = FALSE;
1297 g_mutex_unlock(mdi->mutex);
1300 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1302 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1305 for ( x = mdi->x0; x <= mdi->xf; x++ )
1307 for ( y = mdi->y0; y <= mdi->yf; y++ )
1309 gboolean remove_mem_cache = FALSE;
1310 gboolean need_download = FALSE;
1311 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1312 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1313 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
1316 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1318 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1322 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1323 need_download = TRUE;
1324 remove_mem_cache = TRUE;
1326 } else { /* in case map file already exists */
1327 switch (mdi->redownload) {
1328 case REDOWNLOAD_NONE:
1331 case REDOWNLOAD_BAD:
1333 /* see if this one is bad or what */
1335 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1336 if (gx || (!pixbuf)) {
1337 g_remove ( mdi->filename_buf );
1338 need_download = TRUE;
1339 remove_mem_cache = TRUE;
1340 g_error_free ( gx );
1343 g_object_unref ( pixbuf );
1348 case REDOWNLOAD_NEW:
1349 need_download = TRUE;
1350 remove_mem_cache = TRUE;
1353 case REDOWNLOAD_ALL:
1354 /* FIXME: need a better way than to erase file in case of server/network problem */
1355 g_remove ( mdi->filename_buf );
1356 need_download = TRUE;
1357 remove_mem_cache = TRUE;
1360 case DOWNLOAD_OR_REFRESH:
1361 remove_mem_cache = TRUE;
1365 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1369 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1371 if (need_download) {
1372 if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
1376 g_mutex_lock(mdi->mutex);
1377 if (remove_mem_cache)
1378 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 );
1379 if (mdi->refresh_display && mdi->map_layer_alive) {
1380 /* TODO: check if it's on visible area */
1381 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1383 g_mutex_unlock(mdi->mutex);
1384 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1388 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1389 g_mutex_lock(mdi->mutex);
1390 if (mdi->map_layer_alive)
1391 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1392 g_mutex_unlock(mdi->mutex);
1396 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1398 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1400 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1401 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1402 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
1403 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1405 g_remove ( mdi->filename_buf );
1410 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1412 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1413 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1415 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1417 // Don't ever attempt download on direct access
1418 if ( vik_map_source_is_direct_file_access ( map ) )
1421 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1422 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1424 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1429 mdi->map_layer_alive = TRUE;
1430 mdi->mutex = g_mutex_new();
1431 mdi->refresh_display = TRUE;
1433 /* cache_dir and buffer for dest filename */
1434 mdi->cache_dir = g_strdup ( vml->cache_dir );
1435 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1436 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1437 mdi->maptype = vml->maptype;
1439 mdi->mapcoord = ulm;
1440 mdi->redownload = redownload;
1442 mdi->x0 = MIN(ulm.x, brm.x);
1443 mdi->xf = MAX(ulm.x, brm.x);
1444 mdi->y0 = MIN(ulm.y, brm.y);
1445 mdi->yf = MAX(ulm.y, brm.y);
1449 if ( mdi->redownload ) {
1450 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1452 /* calculate how many we need */
1453 for ( a = mdi->x0; a <= mdi->xf; a++ )
1455 for ( b = mdi->y0; b <= mdi->yf; b++ )
1457 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1458 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1460 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1466 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1468 if ( mdi->mapstoget )
1470 const gchar *tmp_str;
1475 if (redownload == REDOWNLOAD_BAD)
1476 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1478 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1482 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1484 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1486 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1487 /* launch the thread */
1488 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1489 tmp, /* description string */
1490 (vik_thr_func) map_download_thread, /* function to call within thread */
1491 mdi, /* pass along data */
1492 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1493 (vik_thr_free_func) mdi_cancel_cleanup,
1502 static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1505 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1507 // Don't ever attempt download on direct access
1508 if ( vik_map_source_is_direct_file_access ( map ) )
1511 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1512 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1513 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1517 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1522 mdi->map_layer_alive = TRUE;
1523 mdi->mutex = g_mutex_new();
1524 mdi->refresh_display = TRUE;
1526 mdi->cache_dir = g_strdup ( vml->cache_dir );
1527 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1528 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1529 mdi->maptype = vml->maptype;
1531 mdi->mapcoord = ulm;
1532 mdi->redownload = download_method;
1534 mdi->x0 = MIN(ulm.x, brm.x);
1535 mdi->xf = MAX(ulm.x, brm.x);
1536 mdi->y0 = MIN(ulm.y, brm.y);
1537 mdi->yf = MAX(ulm.y, brm.y);
1541 for (i = mdi->x0; i <= mdi->xf; i++) {
1542 for (j = mdi->y0; j <= mdi->yf; j++) {
1543 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1544 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1546 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1551 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1553 if (mdi->mapstoget) {
1556 fmt = ngettext("Downloading %d %s map...",
1557 "Downloading %d %s maps...",
1559 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1561 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1562 /* launch the thread */
1563 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1564 tmp, /* description string */
1565 (vik_thr_func) map_download_thread, /* function to call within thread */
1566 mdi, /* pass along data */
1567 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1568 (vik_thr_free_func) mdi_cancel_cleanup,
1577 * vik_maps_layer_download_section:
1578 * @vml: The Map Layer
1579 * @vvp: The Viewport that the map is on
1580 * @ul: Upper left coordinate of the area to be downloaded
1581 * @br: Bottom right coordinate of the area to be downloaded
1582 * @zoom: The zoom level at which the maps are to be download
1584 * Download a specified map area at a certain zoom level
1586 void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1588 maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1591 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1593 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1596 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1598 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1601 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1603 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1607 * Display a simple dialog with information about this particular map tile
1609 static void maps_layer_tile_info ( VikMapsLayer *vml )
1611 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1613 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1614 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1617 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1620 gchar *filename = NULL;
1621 gchar *message = NULL;
1622 gchar *source = NULL;
1624 if ( vik_map_source_is_direct_file_access ( map ) ) {
1625 if ( vik_map_source_is_mbtiles ( map ) ) {
1626 filename = g_strdup ( vml->filename );
1627 #ifdef HAVE_SQLITE3_H
1628 // And whether to bother going into the SQL to check it's really there or not...
1629 gchar *exists = NULL;
1630 if ( vml->mbtiles ) {
1631 GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, (17 - ulm.scale) );
1633 exists = g_strdup ( _("YES") );
1634 g_object_unref ( G_OBJECT(pixbuf) );
1637 exists = g_strdup ( _("NO") );
1641 exists = g_strdup ( _("NO") );
1642 gint flip_y = (gint) pow(2, (17 - ulm.scale))-1 - ulm.y;
1643 // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1644 source = g_strdup_printf ( "%s (%d%s%d%s%d.%s %s)", filename, ulm.scale, G_DIR_SEPARATOR_S, ulm.x, G_DIR_SEPARATOR_S, flip_y, "png", exists );
1647 source = g_strdup ( _("Not available") );
1651 filename = g_strdup_printf ( DIRECTDIRACCESS, vml->cache_dir, ulm.scale, ulm.x, ulm.y, ".png" );
1652 source = g_strconcat ( "file://", filename, NULL );
1656 filename = g_strdup_printf ( DIRSTRUCTURE, vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale, ulm.z, ulm.x, ulm.y );
1657 source = g_strdup_printf ( "http://%s%s",
1658 vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1659 vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1662 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1664 // Get some timestamp information of the tile
1665 struct stat stat_buf;
1666 if ( g_stat ( filename, &stat_buf ) == 0 ) {
1668 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1669 message = g_strdup_printf ( _("\nSource: %s\n\nTile File: %s\nTile File Timestamp: %s"), source, filename, time_buf );
1673 message = g_strdup_printf ( _("Source: %s\n\nNo Tile File!"), source );
1676 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), message );
1680 g_free ( filename );
1683 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1685 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1687 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1689 if ( event->button == 1 )
1692 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 );
1693 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 );
1694 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1695 vml->dl_tool_x = vml->dl_tool_y = -1;
1700 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) );
1701 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) );
1703 vml->redownload_vvp = vvp;
1705 vml->dl_tool_x = vml->dl_tool_y = -1;
1707 if ( ! vml->dl_right_click_menu ) {
1709 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1711 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1712 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1713 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1715 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1716 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1717 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1719 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1720 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1721 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1723 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
1724 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1725 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
1726 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
1729 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1730 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1736 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1741 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1744 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1746 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1747 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1748 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1749 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1750 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1752 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1759 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1763 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1764 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1765 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1766 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1768 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1769 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1770 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1772 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1773 g_free ( filename_buf );
1774 vik_layer_emit_update ( VIK_LAYER(vml) );
1782 // A slightly better way of defining the menu callback information
1783 // This should be easier to extend/rework compared to previously
1790 typedef gpointer menu_array_values[MA_LAST];
1792 static void download_onscreen_maps ( menu_array_values values, gint redownload )
1794 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
1795 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
1796 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1798 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1799 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1804 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1805 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1807 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1808 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1809 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1810 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1811 start_download_thread ( vml, vvp, &ul, &br, redownload );
1812 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1813 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1814 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1815 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1819 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1823 static void maps_layer_download_missing_onscreen_maps ( menu_array_values values )
1825 download_onscreen_maps( values, REDOWNLOAD_NONE);
1828 static void maps_layer_download_new_onscreen_maps ( menu_array_values values )
1830 download_onscreen_maps( values, REDOWNLOAD_NEW);
1833 static void maps_layer_redownload_all_onscreen_maps ( menu_array_values values )
1835 download_onscreen_maps( values, REDOWNLOAD_ALL);
1838 static void maps_layers_about ( gpointer vml_vvp[2] )
1840 VikMapsLayer *vml = vml_vvp[0];
1841 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1843 if ( vik_map_source_get_license (map) )
1844 maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
1846 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
1847 vik_map_source_get_label (map) );
1851 * maps_layer_how_many_maps:
1852 * Copied from maps_layer_download_section but without the actual download and this returns a value
1854 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
1857 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1859 if ( vik_map_source_is_direct_file_access ( map ) )
1862 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1863 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1864 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1868 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1873 mdi->map_layer_alive = TRUE;
1874 mdi->mutex = g_mutex_new();
1875 mdi->refresh_display = FALSE;
1877 mdi->cache_dir = g_strdup ( vml->cache_dir );
1878 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1879 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1880 mdi->maptype = vml->maptype;
1882 mdi->mapcoord = ulm;
1883 mdi->redownload = redownload;
1885 mdi->x0 = MIN(ulm.x, brm.x);
1886 mdi->xf = MAX(ulm.x, brm.x);
1887 mdi->y0 = MIN(ulm.y, brm.y);
1888 mdi->yf = MAX(ulm.y, brm.y);
1892 if ( mdi->redownload == REDOWNLOAD_ALL ) {
1893 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1896 /* calculate how many we need */
1897 for (i = mdi->x0; i <= mdi->xf; i++) {
1898 for (j = mdi->y0; j <= mdi->yf; j++) {
1899 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1900 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1902 if ( mdi->redownload == REDOWNLOAD_NEW ) {
1903 // Assume the worst - always a new file
1904 // Absolute value would requires server lookup - but that is too slow
1908 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1913 if ( mdi->redownload == REDOWNLOAD_BAD ) {
1914 /* see if this one is bad or what */
1916 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1917 if (gx || (!pixbuf)) {
1921 // Other download cases already considered or just ignored
1929 gint rv = mdi->mapstoget;
1937 * maps_dialog_zoom_between:
1938 * This dialog is specific to the map layer, so it's here rather than in dialog.c
1940 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
1945 gint *selected_zoom1,
1946 gint *selected_zoom2,
1947 gchar *download_list[],
1948 gint default_download,
1949 gint *selected_download )
1951 GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
1953 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1954 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1955 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1957 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1958 GtkWidget *response_w = NULL;
1959 #if GTK_CHECK_VERSION (2, 20, 0)
1960 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1962 GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
1963 GtkWidget *zoom_combo1 = vik_combo_box_text_new();
1965 for (s = zoom_list; *s; s++)
1966 vik_combo_box_text_append ( zoom_combo1, *s );
1967 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
1969 GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
1970 GtkWidget *zoom_combo2 = vik_combo_box_text_new();
1971 for (s = zoom_list; *s; s++)
1972 vik_combo_box_text_append ( zoom_combo2, *s );
1973 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
1975 GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
1976 GtkWidget *download_combo = vik_combo_box_text_new();
1977 for (s = download_list; *s; s++)
1978 vik_combo_box_text_append ( download_combo, *s );
1979 gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
1981 GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
1982 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
1983 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
1984 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
1985 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
1986 gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
1987 gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
1989 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
1992 gtk_widget_grab_focus ( response_w );
1994 gtk_widget_show_all ( dialog );
1995 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
1996 gtk_widget_destroy(dialog);
2000 // Return selected options
2001 *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
2002 *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
2003 *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
2005 gtk_widget_destroy(dialog);
2009 // My best guess of sensible limits
2010 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
2011 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2014 * Get all maps in the region for zoom levels specified by the user
2015 * Sort of similar to trw_layer_download_map_along_track_cb function
2017 static void maps_layer_download_all ( menu_array_values values )
2019 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2020 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
2022 // I don't think we should allow users to hammer the servers too much...
2023 // Delibrately not allowing lowest zoom levels
2024 // Still can give massive numbers to download
2025 // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2026 gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2027 gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2029 gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2030 gint selected_download_method;
2032 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2034 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2035 if (cur_zoom == zoom_vals[default_zoom])
2038 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2040 // Default to only 2 zoom levels below the current one
2041 if (default_zoom > 1 )
2042 lower_zoom = default_zoom - 2;
2044 lower_zoom = default_zoom;
2046 // redownload method - needs to align with REDOWNLOAD* macro values
2047 gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2049 gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2051 if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2059 REDOWNLOAD_NONE, // AKA Missing
2060 &selected_download_method ) ) {
2067 // Find out new current positions
2068 gdouble min_lat, max_lat, min_lon, max_lon;
2069 VikCoord vc_ul, vc_br;
2070 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2071 struct LatLon ll_ul = { max_lat, min_lon };
2072 struct LatLon ll_br = { min_lat, max_lon };
2073 vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2074 vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2076 // Get Maps Count - call for each zoom level (in reverse)
2077 // With REDOWNLOAD_NEW this is a possible maximum
2078 // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2081 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2082 map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2085 g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2087 // Absolute protection of hammering a map server
2088 if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2089 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);
2090 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2095 // Confirm really want to do this
2096 if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2097 gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2098 gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2104 // Get Maps - call for each zoom level (in reverse)
2105 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2106 maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2110 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2113 static menu_array_values values;
2114 values[MA_VML] = vml;
2115 values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2117 item = gtk_menu_item_new();
2118 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2119 gtk_widget_show ( item );
2121 /* Now with icons */
2122 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2123 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2124 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), values );
2125 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2126 gtk_widget_show ( item );
2128 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2129 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2130 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2131 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), values );
2132 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2133 gtk_widget_show ( item );
2136 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2137 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2138 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), values );
2139 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2140 gtk_widget_show ( item );
2142 item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2143 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), values );
2145 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2146 gtk_widget_show ( item );
2148 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2149 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layers_about), values );
2150 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2151 gtk_widget_show ( item );
2155 * Enable downloading maps of the current screen area either 'new' or 'everything'
2157 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2162 static menu_array_values values;
2163 values[MA_VML] = vml;
2164 values[MA_VVP] = vvp;
2167 // Get only new maps
2168 maps_layer_download_new_onscreen_maps ( values );
2170 // Redownload everything
2171 maps_layer_redownload_all_onscreen_maps ( values );