2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2005, Evan Battaglia <viking@greentorch.org>
5 * Copyright (C) 2010, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
6 * Copyright (c) 2013, Rob Norris <rw_norris@hotmail.com>
7 * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
8 * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include <gdk-pixbuf/gdk-pixdata.h>
33 #include <glib/gstdio.h>
34 #include <glib/gi18n.h>
48 #include "vikmapsourcedefault.h"
51 #include "background.h"
52 #include "preferences.h"
53 #include "vikmapslayer.h"
54 #include "icons/icons.h"
61 #define VIK_SETTINGS_MAP_MAX_TILES "maps_max_tiles"
62 static gint MAX_TILES = 1000;
64 #define VIK_SETTINGS_MAP_MIN_SHRINKFACTOR "maps_min_shrinkfactor"
65 #define VIK_SETTINGS_MAP_MAX_SHRINKFACTOR "maps_max_shrinkfactor"
66 static gdouble MAX_SHRINKFACTOR = 8.0000001; /* zoom 1 viewing 8-tiles */
67 static gdouble MIN_SHRINKFACTOR = 0.0312499; /* zoom 32 viewing 1-tiles */
69 #define VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR "maps_real_min_shrinkfactor"
70 static gdouble REAL_MIN_SHRINKFACTOR = 0.0039062499; /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
72 /****** MAP TYPES ******/
74 static GList *__map_types = NULL;
76 #define NUM_MAP_TYPES g_list_length(__map_types)
78 /* List of label for each map type */
79 static gchar **params_maptypes = NULL;
81 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
82 static guint *params_maptypes_ids = NULL;
84 /******** MAPZOOMS *********/
86 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 };
87 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 };
88 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 };
90 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
92 /**************************/
95 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
96 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
97 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
98 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
99 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
100 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
101 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values );
102 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
103 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
104 static void maps_layer_free ( VikMapsLayer *vml );
105 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
106 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
107 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
108 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
109 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
110 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
111 static guint map_uniq_id_to_index ( guint uniq_id );
114 static VikLayerParamScale params_scales[] = {
115 /* min, max, step, digits (decimal places) */
116 { 0, 255, 3, 0 }, /* alpha */
119 static VikLayerParamData mode_default ( void ) { return VIK_LPD_UINT ( 19 ); } // OSM MapQuest maps
120 static VikLayerParamData directory_default ( void )
122 VikLayerParamData data;
123 VikLayerParamData *pref = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir");
124 if (pref) data.s = g_strdup ( pref->s ); else data.s = "";
127 static VikLayerParamData file_default ( void )
129 VikLayerParamData data;
133 static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
134 static VikLayerParamData mapzoom_default ( void ) { return VIK_LPD_UINT ( 0 ); }
136 VikLayerParam maps_layer_params[] = {
137 { 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 },
138 { 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 },
139 { 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,
140 N_("An MBTiles file. Only applies when the map type method is 'MBTiles'"), file_default, NULL, NULL },
141 { VIK_LAYER_MAPS, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
142 N_("Control the Alpha value for transparency effects"), alpha_default, NULL, NULL },
143 { 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 },
144 { VIK_LAYER_MAPS, "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
145 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 },
146 { VIK_LAYER_MAPS, "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
147 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."),
148 mapzoom_default, NULL, NULL },
162 void maps_layer_set_autodownload_default ( gboolean autodownload )
164 // Set appropriate function
166 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_true_default;
168 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_false_default;
171 static VikToolInterface maps_tools[] = {
172 { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
173 (VikToolConstructorFunc) maps_layer_download_create,
177 (VikToolMouseFunc) maps_layer_download_click,
179 (VikToolMouseFunc) maps_layer_download_release,
182 GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf },
185 VikLayerInterface vik_maps_layer_interface = {
189 &vikmapslayer_pixbuf,
192 sizeof(maps_tools) / sizeof(maps_tools[0]),
201 (VikLayerFuncCreate) maps_layer_new,
202 (VikLayerFuncRealize) NULL,
203 (VikLayerFuncPostRead) maps_layer_post_read,
204 (VikLayerFuncFree) maps_layer_free,
206 (VikLayerFuncProperties) NULL,
207 (VikLayerFuncDraw) maps_layer_draw,
208 (VikLayerFuncChangeCoordMode) NULL,
210 (VikLayerFuncSetMenuItemsSelection) NULL,
211 (VikLayerFuncGetMenuItemsSelection) NULL,
213 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
214 (VikLayerFuncSublayerAddMenuItems) NULL,
216 (VikLayerFuncSublayerRenameRequest) NULL,
217 (VikLayerFuncSublayerToggleVisible) NULL,
218 (VikLayerFuncSublayerTooltip) NULL,
219 (VikLayerFuncLayerTooltip) maps_layer_tooltip,
220 (VikLayerFuncLayerSelected) NULL,
222 (VikLayerFuncMarshall) maps_layer_marshall,
223 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
225 (VikLayerFuncSetParam) maps_layer_set_param,
226 (VikLayerFuncGetParam) maps_layer_get_param,
227 (VikLayerFuncChangeParam) maps_layer_change_param,
229 (VikLayerFuncReadFileData) NULL,
230 (VikLayerFuncWriteFileData) NULL,
232 (VikLayerFuncDeleteItem) NULL,
233 (VikLayerFuncCutItem) NULL,
234 (VikLayerFuncCopyItem) NULL,
235 (VikLayerFuncPasteItem) NULL,
236 (VikLayerFuncFreeCopiedItem) NULL,
237 (VikLayerFuncDragDropRequest) NULL,
239 (VikLayerFuncSelectClick) NULL,
240 (VikLayerFuncSelectMove) NULL,
241 (VikLayerFuncSelectRelease) NULL,
242 (VikLayerFuncSelectedViewportMenu) NULL,
245 struct _VikMapsLayer {
251 gdouble xmapzoom, ymapzoom;
253 gboolean autodownload;
254 gboolean adl_only_missing;
255 VikCoord *last_center;
259 gint dl_tool_x, dl_tool_y;
261 GtkMenu *dl_right_click_menu;
262 VikCoord redownload_ul, redownload_br; /* right click menu only */
263 VikViewport *redownload_vvp;
265 #ifdef HAVE_SQLITE3_H
270 enum { REDOWNLOAD_NONE = 0, /* download only missing maps */
271 REDOWNLOAD_BAD, /* download missing and bad maps */
272 REDOWNLOAD_NEW, /* download missing maps that are newer on server only */
273 REDOWNLOAD_ALL, /* download all maps */
274 DOWNLOAD_OR_REFRESH }; /* download missing maps and refresh cache */
276 static VikLayerParam prefs[] = {
277 { 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") },
280 void maps_layer_init ()
282 VikLayerParamData tmp;
283 tmp.s = maps_layer_default_dir();
284 a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
286 gint max_tiles = MAX_TILES;
287 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_MAX_TILES, &max_tiles ) )
288 MAX_TILES = max_tiles;
291 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MIN_SHRINKFACTOR, &gdtmp ) )
292 MIN_SHRINKFACTOR = gdtmp;
294 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MAX_SHRINKFACTOR, &gdtmp ) )
295 MAX_SHRINKFACTOR = gdtmp;
297 if ( a_settings_get_double ( VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR, &gdtmp ) )
298 REAL_MIN_SHRINKFACTOR = gdtmp;
301 /****************************************/
302 /******** MAPS LAYER TYPES **************/
303 /****************************************/
305 void _add_map_source ( guint id, const char *label, VikMapSource *map )
309 len = g_strv_length (params_maptypes);
311 params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
312 params_maptypes[len] = g_strdup (label);
313 params_maptypes[len+1] = NULL;
316 params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
317 params_maptypes_ids[len] = id;
318 params_maptypes_ids[len+1] = 0;
320 /* We have to clone */
321 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
322 /* Register the clone in the list */
323 __map_types = g_list_append(__map_types, clone);
326 We have to ensure the mode LayerParam references the up-to-date
330 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
331 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
333 maps_layer_params[0].widget_data = params_maptypes;
334 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
337 void _update_map_source ( const char *label, VikMapSource *map, int index )
339 GList *item = g_list_nth (__map_types, index);
340 g_object_unref (item->data);
341 item->data = g_object_ref (map);
342 /* Change previous data */
343 g_free (params_maptypes[index]);
344 params_maptypes[index] = g_strdup (label);
348 * maps_layer_register_map_source:
349 * @map: the new VikMapSource
351 * Register a new VikMapSource.
352 * Override existing one (equality of id).
354 void maps_layer_register_map_source ( VikMapSource *map )
356 g_assert(map != NULL);
358 guint id = vik_map_source_get_uniq_id(map);
359 const char *label = vik_map_source_get_label(map);
360 g_assert(label != NULL);
362 int previous = map_uniq_id_to_index (id);
363 if (previous != NUM_MAP_TYPES)
365 _update_map_source (label, map, previous);
369 _add_map_source (id, label, map);
373 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
374 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
375 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
377 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
379 return(vml->maptype);
382 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
384 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
387 /****************************************/
388 /******** CACHE DIR STUFF ***************/
389 /****************************************/
391 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
392 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
393 #define MAPS_CACHE_DIR maps_layer_default_dir()
397 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
398 #define LOCAL_MAPS_DIR "VIKING-MAPS"
399 #elif defined __APPLE__
401 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
402 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
405 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
406 #define LOCAL_MAPS_DIR ".viking-maps"
409 gchar *maps_layer_default_dir ()
411 static gchar *defaultdir = NULL;
414 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
415 const gchar *mapdir = g_getenv("VIKING_MAPS");
417 defaultdir = g_strdup ( mapdir );
418 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
419 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
421 const gchar *home = g_get_home_dir();
422 if (!home || g_access(home, W_OK))
423 home = g_get_home_dir ();
425 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
427 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
429 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
431 /* Add the separator at the end */
432 gchar *tmp = defaultdir;
433 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
436 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
441 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
443 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
445 g_mkdir ( vml->cache_dir, 0777 );
449 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 // Ensure cache_dir always ends with a separator
465 if ( mydir[len-1] != G_DIR_SEPARATOR )
467 vml->cache_dir = g_malloc ( len+2 );
468 strncpy ( vml->cache_dir, mydir, len );
469 vml->cache_dir[len] = G_DIR_SEPARATOR;
470 vml->cache_dir[len+1] = '\0';
473 vml->cache_dir = g_strdup ( mydir );
475 maps_layer_mkdir_if_default_dir ( vml );
478 static void maps_layer_set_file ( VikMapsLayer *vml, const gchar *name )
481 g_free (vml->filename);
482 vml->filename = g_strdup (name);
485 /****************************************/
486 /******** GOBJECT STUFF *****************/
487 /****************************************/
489 GType vik_maps_layer_get_type ()
491 static GType vml_type = 0;
495 static const GTypeInfo vml_info =
497 sizeof (VikMapsLayerClass),
498 NULL, /* base_init */
499 NULL, /* base_finalize */
500 NULL, /* class init */
501 NULL, /* class_finalize */
502 NULL, /* class_data */
503 sizeof (VikMapsLayer),
505 NULL /* instance init */
507 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
513 /****************************************/
514 /************** PARAMETERS **************/
515 /****************************************/
517 static guint map_index_to_uniq_id (guint16 index)
519 g_assert ( index < NUM_MAP_TYPES );
520 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
523 static guint map_uniq_id_to_index ( guint uniq_id )
526 for ( i = 0; i < NUM_MAP_TYPES; i++ )
527 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
529 return NUM_MAP_TYPES; /* no such thing */
532 #define VIK_SETTINGS_MAP_LICENSE_SHOWN "map_license_shown"
535 * Convenience function to display the license
537 static void maps_show_license ( GtkWindow *parent, VikMapSource *map )
539 a_dialog_license ( parent,
540 vik_map_source_get_label (map),
541 vik_map_source_get_license (map),
542 vik_map_source_get_license_url (map) );
545 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
549 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
550 case PARAM_FILE: maps_layer_set_file ( vml, data.s ); break;
551 case PARAM_MAPTYPE: {
552 gint maptype = map_uniq_id_to_index(data.u);
553 if ( maptype == NUM_MAP_TYPES )
554 g_warning(_("Unknown map type"));
556 vml->maptype = maptype;
558 // When loading from a file don't need the license reminder - ensure it's saved into the 'seen' list
559 if ( is_file_operation ) {
560 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, maptype );
563 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
564 if (vik_map_source_get_license (map) != NULL) {
565 // Check if licence for this map type has been shown before
566 if ( ! a_settings_get_integer_list_contains ( VIK_SETTINGS_MAP_LICENSE_SHOWN, maptype ) ) {
568 maps_show_license ( VIK_GTK_WINDOW_FROM_WIDGET(vvp), map );
569 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, maptype );
576 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
577 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
578 case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
579 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
580 vml->mapzoom_id = data.u;
581 vml->xmapzoom = __mapzooms_x [data.u];
582 vml->ymapzoom = __mapzooms_y [data.u];
583 }else g_warning (_("Unknown Map Zoom")); break;
588 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
590 VikLayerParamData rv;
593 case PARAM_CACHE_DIR:
595 gboolean set = FALSE;
596 /* Only save a blank when the map cache location equals the default
597 On reading in, when it is blank then the default is reconstructed
598 Since the default changes dependent on the user and OS, it means the resultant file is more portable */
599 if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
603 else if ( is_file_operation ) {
604 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
605 gchar *cwd = g_get_current_dir();
607 rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
608 if ( !rv.s ) rv.s = "";
614 rv.s = vml->cache_dir ? vml->cache_dir : "";
617 case PARAM_FILE: rv.s = vml->filename; break;
618 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
619 case PARAM_ALPHA: rv.u = vml->alpha; break;
620 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
621 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
622 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
627 static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values )
629 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
630 // Alter sensitivity of download option widgets according to the maptype setting.
631 case PARAM_MAPTYPE: {
633 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
634 // Is it *not* the OSM On Disk Tile Layout or the MBTiles type
635 gboolean sensitive = ( 21 != vlpd.u && 23 != vlpd.u);
636 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
637 GtkWidget **ww2 = values[UI_CHG_LABELS];
638 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
639 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
640 GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
641 GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
642 // Depends on autodownload value
643 gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
644 if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
645 if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
646 if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
647 if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
649 // File only applicable for MBTiles type
650 // Directory for all other types
651 sensitive = ( 23 == vlpd.u);
652 GtkWidget *w5 = ww1[PARAM_FILE];
653 GtkWidget *w6 = ww2[PARAM_FILE];
654 GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
655 GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
656 if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
657 if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
658 if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
659 if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
664 // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
665 case PARAM_AUTODOWNLOAD: {
667 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
668 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
669 GtkWidget **ww2 = values[UI_CHG_LABELS];
670 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
671 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
672 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
673 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
680 /****************************************/
681 /****** CREATING, COPYING, FREEING ******/
682 /****************************************/
684 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
686 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
687 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
689 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
691 vml->dl_tool_x = vml->dl_tool_y = -1;
692 vml->last_center = NULL;
693 vml->last_xmpp = 0.0;
694 vml->last_ympp = 0.0;
696 vml->dl_right_click_menu = NULL;
697 vml->filename = NULL;
701 static void maps_layer_free ( VikMapsLayer *vml )
703 g_free ( vml->cache_dir );
704 vml->cache_dir = NULL;
705 if ( vml->dl_right_click_menu )
706 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
707 g_free(vml->last_center);
708 vml->last_center = NULL;
709 g_free ( vml->filename );
710 vml->filename = NULL;
712 #ifdef HAVE_SQLITE3_H
713 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
714 if ( vik_map_source_is_mbtiles ( map ) ) {
715 if ( vml->mbtiles ) {
716 int ans = sqlite3_close ( vml->mbtiles );
717 if ( ans != SQLITE_OK ) {
718 // Only to console for information purposes only
719 g_warning ( "SQL Close problem: %d", ans );
726 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
728 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
729 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
731 if (from_file != TRUE)
733 /* If this method is not called in file reading context
734 * it is called in GUI context.
735 * So, we can check if we have to inform the user about inconsistency */
736 VikViewportDrawMode vp_drawmode;
737 vp_drawmode = vik_viewport_get_drawmode ( vp );
739 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
740 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
741 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
742 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
747 // Performed in post read as we now know the map type
748 #ifdef HAVE_SQLITE3_H
750 if ( vik_map_source_is_mbtiles ( map ) ) {
751 int ans = sqlite3_open_v2 ( vml->filename,
753 SQLITE_OPEN_READONLY,
755 if ( ans != SQLITE_OK ) {
756 // That didn't work, so here's why:
757 g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
759 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
760 _("Failed to open MBTiles file: %s"),
767 // If the on Disk OSM Tile Layout type
768 if ( vml->maptype == 21 )
769 // Copy the directory into filename
770 // thus the mapcache look up will be unique when using more than one of these map types
771 vml->filename = g_strdup (vml->cache_dir);
774 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
776 return vik_maps_layer_get_map_label ( vml );
779 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
781 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
784 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
786 VikMapsLayer *rv = maps_layer_new ( vvp );
787 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
791 /*********************/
792 /****** DRAWING ******/
793 /*********************/
795 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
798 gint width, height, iii, jjj;
800 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
802 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
803 g_object_unref(G_OBJECT(pixbuf));
809 pixels = gdk_pixbuf_get_pixels(pixbuf);
810 width = gdk_pixbuf_get_width(pixbuf);
811 height = gdk_pixbuf_get_height(pixbuf);
813 /* r,g,b,a,r,g,b,a.... */
814 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
822 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
825 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
826 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
827 g_object_unref ( G_OBJECT(pixbuf) );
831 #ifdef HAVE_SQLITE3_H
833 static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
835 g_warning ( "Found %d columns", cols );
837 for ( i = 0; i < cols; i++ ) {
838 g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
847 static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
849 GdkPixbuf *pixbuf = NULL;
851 // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
852 gint flip_y = (gint) pow(2, zoom)-1 - yy;
853 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 );
855 gboolean finished = FALSE;
857 sqlite3_stmt *sql_stmt = NULL;
858 int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
859 if ( ans != SQLITE_OK ) {
860 g_warning ( "%s: %s - %d", __FUNCTION__, "prepare failure", ans );
864 while ( !finished ) {
865 ans = sqlite3_step ( sql_stmt );
868 // Get tile_data blob
869 int count = sqlite3_column_count(sql_stmt);
871 g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
875 const void *data = sqlite3_column_blob ( sql_stmt, 0 );
876 int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
878 g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
882 // Convert these blob bytes into a pixbuf via these streaming operations
883 GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
884 GError *error = NULL;
885 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
886 if (error || (!pixbuf)) {
887 g_warning ( "%s: %s", __FUNCTION__, error->message );
888 g_error_free ( error );
890 g_input_stream_close ( stream, NULL, NULL );
896 // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
898 // and give up on any errors
899 if ( ans != SQLITE_DONE )
900 g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
905 ans = sqlite3_finalize ( sql_stmt );
907 g_free ( statement );
913 static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
915 GdkPixbuf *pixbuf = NULL;
917 #ifdef HAVE_SQLITE3_H
918 if ( vml->mbtiles ) {
920 gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
922 int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
923 if ( ans != SQLITE_OK ) {
924 // Only to console for information purposes only
925 g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
926 sqlite3_free( errMsg );
928 g_free ( statement );
931 // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
932 // Hence this specific function
933 pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
940 static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
942 // Apply alpha setting
943 if ( pixbuf && vml->alpha < 255 )
944 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
946 if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
947 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
950 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
951 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
952 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
957 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
962 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
963 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
966 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
967 // ATM MBTiles must be 'a direct access type'
968 if ( vik_map_source_is_mbtiles (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
969 pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
970 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
971 // return now to avoid file tests that aren't appropriate for this map type
975 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS,
976 vml->cache_dir, (17 - mapcoord->scale), mapcoord->x, mapcoord->y, ".png" );
979 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
980 vml->cache_dir, mode,
981 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
983 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
986 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
988 /* free the pixbuf on error */
991 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
992 g_warning ( _("Couldn't open image file: %s"), gx->message );
996 g_object_unref ( G_OBJECT(pixbuf) );
999 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
1006 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
1008 const VikCoord *center = vik_viewport_get_center ( vvp );
1010 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
1011 /* D'n'D pan in action: do not download */
1015 // Prevent requests for downloading tiles at Zoom Level 19 and above for most map types
1016 // Allow MapQuest Zoom Level up to 19
1017 // TODO: This should be made a property of the map source and then use that value
1018 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1019 if ( (vml->maptype != 19 && map_utils_mpp_to_scale (xzoom) < -1) || (vml->maptype == 19 && map_utils_mpp_to_scale (xzoom) < -2) )
1022 if (vml->last_center == NULL) {
1023 VikCoord *new_center = g_malloc(sizeof(VikCoord));
1024 *new_center = *center;
1025 vml->last_center = new_center;
1026 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1027 vml->last_ympp = vik_viewport_get_ympp(vvp);
1031 /* TODO: perhaps vik_coord_diff() */
1032 if (vik_coord_equals(vml->last_center, center)
1033 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1034 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1037 *(vml->last_center) = *center;
1038 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1039 vml->last_ympp = vik_viewport_get_ympp(vvp);
1043 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1046 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1047 gdouble yzoom = vik_viewport_get_ympp ( vvp );
1048 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
1049 gboolean existence_only = FALSE;
1051 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1052 xshrinkfactor = vml->xmapzoom / xzoom;
1053 yshrinkfactor = vml->ymapzoom / yzoom;
1054 xzoom = vml->xmapzoom;
1055 yzoom = vml->xmapzoom;
1056 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1057 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
1058 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1059 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
1060 existence_only = TRUE;
1063 g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
1070 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1071 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1072 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
1076 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1077 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
1078 gint mode = vik_map_source_get_uniq_id(map);
1081 gint xx, yy, width, height;
1084 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1085 // which can happen when using a small fixed zoom level and viewing large areas.
1086 // Also prevents very large number of tile download requests
1087 gint tiles = (xmax-xmin) * (ymax-ymin);
1088 if ( tiles > MAX_TILES ) {
1089 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1090 existence_only = TRUE;
1093 guint max_path_len = strlen(vml->cache_dir) + 40;
1094 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1096 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
1097 g_debug("%s: Starting autodownload", __FUNCTION__);
1098 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
1099 // Try to download newer tiles
1100 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1102 // Download only missing tiles
1103 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
1106 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
1107 for ( x = xmin; x <= xmax; x++ ) {
1108 for ( y = ymin; y <= ymax; y++ ) {
1111 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1113 width = gdk_pixbuf_get_width ( pixbuf );
1114 height = gdk_pixbuf_get_height ( pixbuf );
1116 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1117 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1121 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1125 } else { /* tilesize is known, don't have to keep converting coords */
1126 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1127 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
1128 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1129 gint tilesize_x_ceil = ceil ( tilesize_x );
1130 gint tilesize_y_ceil = ceil ( tilesize_y );
1131 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1132 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
1133 gdouble xx, yy; gint xx_tmp, yy_tmp;
1134 gint base_yy, xend, yend;
1136 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1137 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1139 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
1140 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1141 xx = xx_tmp; yy = yy_tmp;
1142 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1143 * eg if tile size 128, shrinkfactor 0.333 */
1144 xx -= (tilesize_x/2);
1145 base_yy = yy - (tilesize_y/2);
1147 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1149 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1153 if ( existence_only ) {
1154 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1155 g_snprintf ( path_buf, max_path_len, DIRECTDIRACCESS,
1156 vml->cache_dir, (17 - ulm.scale), ulm.x, ulm.y, ".png" );
1158 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
1159 vml->cache_dir, mode,
1160 ulm.scale, ulm.z, ulm.x, ulm.y );
1161 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1162 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1163 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1167 for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
1168 /* try with correct then smaller zooms */
1169 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
1170 MapCoord ulm2 = ulm;
1171 ulm2.x = ulm.x / scale_factor;
1172 ulm2.y = ulm.y / scale_factor;
1173 ulm2.scale = ulm.scale + scale_inc;
1174 pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1176 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1177 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
1179 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);
1181 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1186 /* retry with bigger zooms */
1188 for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
1190 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
1191 MapCoord ulm2 = ulm;
1192 ulm2.x = ulm.x * scale_factor;
1193 ulm2.y = ulm.y * scale_factor;
1194 ulm2.scale = ulm.scale - scale_dec;
1195 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1196 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1197 MapCoord ulm3 = ulm2;
1200 pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1204 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1205 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1206 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1220 g_free ( path_buf );
1224 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1226 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
1231 gdouble level = vik_viewport_get_zoom ( vvp );
1233 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1234 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
1237 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1238 vik_viewport_add_logo ( vvp, logo );
1240 /* get corner coords */
1241 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1242 /* UTM multi-zone stuff by Kit Transue */
1243 gchar leftmost_zone, rightmost_zone, i;
1244 leftmost_zone = vik_viewport_leftmost_zone( vvp );
1245 rightmost_zone = vik_viewport_rightmost_zone( vvp );
1246 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1247 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1248 maps_layer_draw_section ( vml, vvp, &ul, &br );
1252 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1253 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1255 maps_layer_draw_section ( vml, vvp, &ul, &br );
1260 /*************************/
1261 /****** DOWNLOADING ******/
1262 /*************************/
1264 /* pass along data to thread, exists even if layer is deleted. */
1267 gchar *filename_buf;
1268 gint x0, y0, xf, yf;
1274 gboolean refresh_display;
1277 gboolean map_layer_alive;
1281 static void mdi_free ( MapDownloadInfo *mdi )
1283 g_mutex_free(mdi->mutex);
1284 g_free ( mdi->cache_dir );
1285 mdi->cache_dir = NULL;
1286 g_free ( mdi->filename_buf );
1287 mdi->filename_buf = NULL;
1291 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1293 MapDownloadInfo *mdi = ptr;
1294 g_mutex_lock(mdi->mutex);
1295 mdi->map_layer_alive = FALSE;
1296 g_mutex_unlock(mdi->mutex);
1299 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1301 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1304 for ( x = mdi->x0; x <= mdi->xf; x++ )
1306 for ( y = mdi->y0; y <= mdi->yf; y++ )
1308 gboolean remove_mem_cache = FALSE;
1309 gboolean need_download = FALSE;
1310 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1311 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1312 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
1315 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1317 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1321 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1322 need_download = TRUE;
1323 remove_mem_cache = TRUE;
1325 } else { /* in case map file already exists */
1326 switch (mdi->redownload) {
1327 case REDOWNLOAD_NONE:
1330 case REDOWNLOAD_BAD:
1332 /* see if this one is bad or what */
1334 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1335 if (gx || (!pixbuf)) {
1336 g_remove ( mdi->filename_buf );
1337 need_download = TRUE;
1338 remove_mem_cache = TRUE;
1339 g_error_free ( gx );
1342 g_object_unref ( pixbuf );
1347 case REDOWNLOAD_NEW:
1348 need_download = TRUE;
1349 remove_mem_cache = TRUE;
1352 case REDOWNLOAD_ALL:
1353 /* FIXME: need a better way than to erase file in case of server/network problem */
1354 g_remove ( mdi->filename_buf );
1355 need_download = TRUE;
1356 remove_mem_cache = TRUE;
1359 case DOWNLOAD_OR_REFRESH:
1360 remove_mem_cache = TRUE;
1364 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1368 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1370 if (need_download) {
1371 if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
1375 g_mutex_lock(mdi->mutex);
1376 if (remove_mem_cache)
1377 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 );
1378 if (mdi->refresh_display && mdi->map_layer_alive) {
1379 /* TODO: check if it's on visible area */
1380 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1382 g_mutex_unlock(mdi->mutex);
1383 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1387 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1388 g_mutex_lock(mdi->mutex);
1389 if (mdi->map_layer_alive)
1390 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1391 g_mutex_unlock(mdi->mutex);
1395 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1397 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1399 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1400 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1401 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
1402 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1404 g_remove ( mdi->filename_buf );
1409 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1411 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1412 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1414 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1416 // Don't ever attempt download on direct access
1417 if ( vik_map_source_is_direct_file_access ( map ) )
1420 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1421 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1423 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1428 mdi->map_layer_alive = TRUE;
1429 mdi->mutex = g_mutex_new();
1430 mdi->refresh_display = TRUE;
1432 /* cache_dir and buffer for dest filename */
1433 mdi->cache_dir = g_strdup ( vml->cache_dir );
1434 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1435 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1436 mdi->maptype = vml->maptype;
1438 mdi->mapcoord = ulm;
1439 mdi->redownload = redownload;
1441 mdi->x0 = MIN(ulm.x, brm.x);
1442 mdi->xf = MAX(ulm.x, brm.x);
1443 mdi->y0 = MIN(ulm.y, brm.y);
1444 mdi->yf = MAX(ulm.y, brm.y);
1448 if ( mdi->redownload ) {
1449 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1451 /* calculate how many we need */
1452 for ( a = mdi->x0; a <= mdi->xf; a++ )
1454 for ( b = mdi->y0; b <= mdi->yf; b++ )
1456 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1457 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1459 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1465 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1467 if ( mdi->mapstoget )
1469 const gchar *tmp_str;
1474 if (redownload == REDOWNLOAD_BAD)
1475 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1477 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1481 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1483 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1485 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1486 /* launch the thread */
1487 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1488 tmp, /* description string */
1489 (vik_thr_func) map_download_thread, /* function to call within thread */
1490 mdi, /* pass along data */
1491 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1492 (vik_thr_free_func) mdi_cancel_cleanup,
1501 static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
1504 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1506 // Don't ever attempt download on direct access
1507 if ( vik_map_source_is_direct_file_access ( map ) )
1510 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1511 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1512 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1516 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1521 mdi->map_layer_alive = TRUE;
1522 mdi->mutex = g_mutex_new();
1523 mdi->refresh_display = TRUE;
1525 mdi->cache_dir = g_strdup ( vml->cache_dir );
1526 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1527 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1528 mdi->maptype = vml->maptype;
1530 mdi->mapcoord = ulm;
1531 mdi->redownload = download_method;
1533 mdi->x0 = MIN(ulm.x, brm.x);
1534 mdi->xf = MAX(ulm.x, brm.x);
1535 mdi->y0 = MIN(ulm.y, brm.y);
1536 mdi->yf = MAX(ulm.y, brm.y);
1540 for (i = mdi->x0; i <= mdi->xf; i++) {
1541 for (j = mdi->y0; j <= mdi->yf; j++) {
1542 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1543 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1545 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1550 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1552 if (mdi->mapstoget) {
1555 fmt = ngettext("Downloading %d %s map...",
1556 "Downloading %d %s maps...",
1558 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1560 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1561 /* launch the thread */
1562 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1563 tmp, /* description string */
1564 (vik_thr_func) map_download_thread, /* function to call within thread */
1565 mdi, /* pass along data */
1566 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1567 (vik_thr_free_func) mdi_cancel_cleanup,
1576 * vik_maps_layer_download_section:
1577 * @vml: The Map Layer
1578 * @vvp: The Viewport that the map is on
1579 * @ul: Upper left coordinate of the area to be downloaded
1580 * @br: Bottom right coordinate of the area to be downloaded
1581 * @zoom: The zoom level at which the maps are to be download
1583 * Download a specified map area at a certain zoom level
1585 void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1587 maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1590 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1592 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1595 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1597 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1600 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1602 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1606 * Display a simple dialog with information about this particular map tile
1608 static void maps_layer_tile_info ( VikMapsLayer *vml )
1610 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1612 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1613 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1616 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1619 gchar *filename = NULL;
1620 gchar *message = NULL;
1621 gchar *source = NULL;
1623 if ( vik_map_source_is_direct_file_access ( map ) ) {
1624 if ( vik_map_source_is_mbtiles ( map ) ) {
1625 filename = g_strdup ( vml->filename );
1626 #ifdef HAVE_SQLITE3_H
1627 // And whether to bother going into the SQL to check it's really there or not...
1628 gchar *exists = NULL;
1629 if ( vml->mbtiles ) {
1630 GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, (17 - ulm.scale) );
1632 exists = g_strdup ( _("YES") );
1633 g_object_unref ( G_OBJECT(pixbuf) );
1636 exists = g_strdup ( _("NO") );
1640 exists = g_strdup ( _("NO") );
1641 gint flip_y = (gint) pow(2, (17 - ulm.scale))-1 - ulm.y;
1642 // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
1643 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 );
1646 source = g_strdup ( _("Not available") );
1650 filename = g_strdup_printf ( DIRECTDIRACCESS, vml->cache_dir, ulm.scale, ulm.x, ulm.y, ".png" );
1651 source = g_strconcat ( "file://", filename, NULL );
1655 filename = g_strdup_printf ( DIRSTRUCTURE, vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale, ulm.z, ulm.x, ulm.y );
1656 source = g_strdup_printf ( "http://%s%s",
1657 vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1658 vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1661 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1663 // Get some timestamp information of the tile
1664 struct stat stat_buf;
1665 if ( g_stat ( filename, &stat_buf ) == 0 ) {
1667 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1668 message = g_strdup_printf ( _("\nSource: %s\n\nTile File: %s\nTile File Timestamp: %s"), source, filename, time_buf );
1672 message = g_strdup_printf ( _("Source: %s\n\nNo Tile File!"), source );
1675 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), message );
1679 g_free ( filename );
1682 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1684 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1686 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1688 if ( event->button == 1 )
1691 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 );
1692 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 );
1693 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1694 vml->dl_tool_x = vml->dl_tool_y = -1;
1699 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) );
1700 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) );
1702 vml->redownload_vvp = vvp;
1704 vml->dl_tool_x = vml->dl_tool_y = -1;
1706 if ( ! vml->dl_right_click_menu ) {
1708 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1710 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1711 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1712 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1714 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1716 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1718 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1719 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1720 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1722 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
1723 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1724 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
1725 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
1728 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1729 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1735 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1740 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1743 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1745 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1746 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1747 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1748 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1749 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1751 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1758 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1762 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1763 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1764 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1765 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1767 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1768 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1769 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1771 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1772 g_free ( filename_buf );
1773 vik_layer_emit_update ( VIK_LAYER(vml) );
1781 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1783 VikMapsLayer *vml = vml_vvp[0];
1784 VikViewport *vvp = vml_vvp[1];
1785 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1787 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1788 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1793 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1794 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1796 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1797 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1798 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1799 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1800 start_download_thread ( vml, vvp, &ul, &br, redownload );
1801 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1802 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1803 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1804 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1808 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1812 static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
1814 download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1817 static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
1819 download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
1822 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1824 download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1827 static void maps_layers_about ( gpointer vml_vvp[2] )
1829 VikMapsLayer *vml = vml_vvp[0];
1830 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1832 if ( vik_map_source_get_license (map) )
1833 maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
1835 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
1836 vik_map_source_get_label (map) );
1840 * maps_layer_how_many_maps:
1841 * Copied from maps_layer_download_section but without the actual download and this returns a value
1843 static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
1846 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1848 if ( vik_map_source_is_direct_file_access ( map ) )
1851 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1852 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1853 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1857 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1862 mdi->map_layer_alive = TRUE;
1863 mdi->mutex = g_mutex_new();
1864 mdi->refresh_display = FALSE;
1866 mdi->cache_dir = g_strdup ( vml->cache_dir );
1867 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1868 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1869 mdi->maptype = vml->maptype;
1871 mdi->mapcoord = ulm;
1872 mdi->redownload = redownload;
1874 mdi->x0 = MIN(ulm.x, brm.x);
1875 mdi->xf = MAX(ulm.x, brm.x);
1876 mdi->y0 = MIN(ulm.y, brm.y);
1877 mdi->yf = MAX(ulm.y, brm.y);
1881 if ( mdi->redownload == REDOWNLOAD_ALL ) {
1882 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1885 /* calculate how many we need */
1886 for (i = mdi->x0; i <= mdi->xf; i++) {
1887 for (j = mdi->y0; j <= mdi->yf; j++) {
1888 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1889 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1891 if ( mdi->redownload == REDOWNLOAD_NEW ) {
1892 // Assume the worst - always a new file
1893 // Absolute value would requires server lookup - but that is too slow
1897 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1902 if ( mdi->redownload == REDOWNLOAD_BAD ) {
1903 /* see if this one is bad or what */
1905 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1906 if (gx || (!pixbuf)) {
1910 // Other download cases already considered or just ignored
1918 gint rv = mdi->mapstoget;
1926 * maps_dialog_zoom_between:
1927 * This dialog is specific to the map layer, so it's here rather than in dialog.c
1929 gboolean maps_dialog_zoom_between ( GtkWindow *parent,
1934 gint *selected_zoom1,
1935 gint *selected_zoom2,
1936 gchar *download_list[],
1937 gint default_download,
1938 gint *selected_download )
1940 GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
1942 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1943 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1944 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1946 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1947 GtkWidget *response_w = NULL;
1948 #if GTK_CHECK_VERSION (2, 20, 0)
1949 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1951 GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
1952 GtkWidget *zoom_combo1 = vik_combo_box_text_new();
1954 for (s = zoom_list; *s; s++)
1955 vik_combo_box_text_append ( zoom_combo1, *s );
1956 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
1958 GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
1959 GtkWidget *zoom_combo2 = vik_combo_box_text_new();
1960 for (s = zoom_list; *s; s++)
1961 vik_combo_box_text_append ( zoom_combo2, *s );
1962 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
1964 GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
1965 GtkWidget *download_combo = vik_combo_box_text_new();
1966 for (s = download_list; *s; s++)
1967 vik_combo_box_text_append ( download_combo, *s );
1968 gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
1970 GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
1971 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
1972 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
1973 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
1974 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
1975 gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
1976 gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
1978 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
1981 gtk_widget_grab_focus ( response_w );
1983 gtk_widget_show_all ( dialog );
1984 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
1985 gtk_widget_destroy(dialog);
1989 // Return selected options
1990 *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
1991 *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
1992 *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
1994 gtk_widget_destroy(dialog);
1998 // My best guess of sensible limits
1999 #define REALLY_LARGE_AMOUNT_OF_TILES 5000
2000 #define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2003 * Get all maps in the region for zoom levels specified by the user
2004 * Sort of similar to trw_layer_download_map_along_track_cb function
2006 static void maps_layer_download_all ( gpointer vml_vvp[2] )
2008 VikMapsLayer *vml = vml_vvp[0];
2009 VikViewport *vvp = vml_vvp[1];
2011 // I don't think we should allow users to hammer the servers too much...
2012 // Delibrately not allowing lowest zoom levels
2013 // Still can give massive numbers to download
2014 // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2015 gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2016 gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2018 gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2019 gint selected_download_method;
2021 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2023 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2024 if (cur_zoom == zoom_vals[default_zoom])
2027 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2029 // Default to only 2 zoom levels below the current one
2030 if (default_zoom > 1 )
2031 lower_zoom = default_zoom - 2;
2033 lower_zoom = default_zoom;
2035 // redownload method - needs to align with REDOWNLOAD* macro values
2036 gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2038 gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2040 if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2048 REDOWNLOAD_NONE, // AKA Missing
2049 &selected_download_method ) ) {
2056 // Find out new current positions
2057 gdouble min_lat, max_lat, min_lon, max_lon;
2058 VikCoord vc_ul, vc_br;
2059 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2060 struct LatLon ll_ul = { max_lat, min_lon };
2061 struct LatLon ll_br = { min_lat, max_lon };
2062 vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2063 vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2065 // Get Maps Count - call for each zoom level (in reverse)
2066 // With REDOWNLOAD_NEW this is a possible maximum
2067 // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2070 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2071 map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2074 g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2076 // Absolute protection of hammering a map server
2077 if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2078 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);
2079 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2084 // Confirm really want to do this
2085 if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2086 gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2087 gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2093 // Get Maps - call for each zoom level (in reverse)
2094 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2095 maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2099 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2101 static gpointer pass_along[2];
2103 pass_along[0] = vml;
2104 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
2106 item = gtk_menu_item_new();
2107 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2108 gtk_widget_show ( item );
2110 /* Now with icons */
2111 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2112 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2113 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
2114 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2115 gtk_widget_show ( item );
2117 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
2118 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2119 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
2120 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
2121 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2122 gtk_widget_show ( item );
2125 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2126 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
2127 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
2128 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2129 gtk_widget_show ( item );
2131 item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2132 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
2133 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), pass_along );
2134 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2135 gtk_widget_show ( item );
2137 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
2138 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layers_about), pass_along );
2139 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2140 gtk_widget_show ( item );
2144 * Enable downloading maps of the current screen area either 'new' or 'everything'
2146 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2151 static gpointer pass_along[2];
2152 pass_along[0] = vml;
2153 pass_along[1] = vvp;
2156 // Get only new maps
2157 maps_layer_download_new_onscreen_maps ( pass_along );
2159 // Redownload everything
2160 maps_layer_redownload_all_onscreen_maps ( pass_along );