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 * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
7 * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 // TODO: Make these configurable
30 #define MAX_TILES 1000
32 #define MAX_SHRINKFACTOR 8.0000001 /* zoom 1 viewing 8-tiles */
33 #define MIN_SHRINKFACTOR 0.0312499 /* zoom 32 viewing 1-tiles */
35 #define REAL_MIN_SHRINKFACTOR 0.0039062499 /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
38 #include <gdk-pixbuf/gdk-pixdata.h>
40 #include <glib/gstdio.h>
41 #include <glib/gi18n.h>
55 #include "vikmapsourcedefault.h"
58 #include "background.h"
59 #include "preferences.h"
60 #include "vikmapslayer.h"
61 #include "icons/icons.h"
63 /****** MAP TYPES ******/
65 static GList *__map_types = NULL;
67 #define NUM_MAP_TYPES g_list_length(__map_types)
69 /* List of label for each map type */
70 static gchar **params_maptypes = NULL;
72 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
73 static guint *params_maptypes_ids = NULL;
75 /******** MAPZOOMS *********/
77 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 };
78 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 };
79 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 };
81 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
83 /**************************/
86 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
87 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
88 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
89 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
90 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
91 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
92 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
93 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
94 static void maps_layer_free ( VikMapsLayer *vml );
95 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
96 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
97 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
98 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
99 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
100 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
101 static guint map_uniq_id_to_index ( guint uniq_id );
104 static VikLayerParamScale params_scales[] = {
105 /* min, max, step, digits (decimal places) */
106 { 0, 255, 3, 0 }, /* alpha */
109 static VikLayerParamData mode_default ( void ) { return VIK_LPD_UINT ( 19 ); } // OSM MapQuest maps
110 static VikLayerParamData directory_default ( void )
112 VikLayerParamData data;
113 data.s = g_strdup ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s );
116 static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
117 static VikLayerParamData mapzoom_default ( void ) { return VIK_LPD_UINT ( 0 ); }
119 VikLayerParam maps_layer_params[] = {
120 { VIK_LAYER_MAPS, "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL, mode_default },
121 { VIK_LAYER_MAPS, "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, NULL, directory_default },
122 { VIK_LAYER_MAPS, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
123 N_("Control the Alpha value for transparency effects"), alpha_default },
124 { 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 },
125 { VIK_LAYER_MAPS, "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
126 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 },
127 { VIK_LAYER_MAPS, "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
128 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."),
142 static VikToolInterface maps_tools[] = {
143 { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
144 (VikToolConstructorFunc) maps_layer_download_create,
148 (VikToolMouseFunc) maps_layer_download_click,
150 (VikToolMouseFunc) maps_layer_download_release,
153 GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf },
156 VikLayerInterface vik_maps_layer_interface = {
160 &vikmapslayer_pixbuf,
163 sizeof(maps_tools) / sizeof(maps_tools[0]),
172 (VikLayerFuncCreate) maps_layer_new,
173 (VikLayerFuncRealize) NULL,
174 (VikLayerFuncPostRead) maps_layer_post_read,
175 (VikLayerFuncFree) maps_layer_free,
177 (VikLayerFuncProperties) NULL,
178 (VikLayerFuncDraw) maps_layer_draw,
179 (VikLayerFuncChangeCoordMode) NULL,
181 (VikLayerFuncSetMenuItemsSelection) NULL,
182 (VikLayerFuncGetMenuItemsSelection) NULL,
184 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
185 (VikLayerFuncSublayerAddMenuItems) NULL,
187 (VikLayerFuncSublayerRenameRequest) NULL,
188 (VikLayerFuncSublayerToggleVisible) NULL,
189 (VikLayerFuncSublayerTooltip) NULL,
190 (VikLayerFuncLayerTooltip) maps_layer_tooltip,
191 (VikLayerFuncLayerSelected) NULL,
193 (VikLayerFuncMarshall) maps_layer_marshall,
194 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
196 (VikLayerFuncSetParam) maps_layer_set_param,
197 (VikLayerFuncGetParam) maps_layer_get_param,
199 (VikLayerFuncReadFileData) NULL,
200 (VikLayerFuncWriteFileData) NULL,
202 (VikLayerFuncDeleteItem) NULL,
203 (VikLayerFuncCutItem) NULL,
204 (VikLayerFuncCopyItem) NULL,
205 (VikLayerFuncPasteItem) NULL,
206 (VikLayerFuncFreeCopiedItem) NULL,
207 (VikLayerFuncDragDropRequest) NULL,
209 (VikLayerFuncSelectClick) NULL,
210 (VikLayerFuncSelectMove) NULL,
211 (VikLayerFuncSelectRelease) NULL,
212 (VikLayerFuncSelectedViewportMenu) NULL,
215 struct _VikMapsLayer {
221 gdouble xmapzoom, ymapzoom;
223 gboolean autodownload;
224 gboolean adl_only_missing;
225 VikCoord *last_center;
229 gint dl_tool_x, dl_tool_y;
231 GtkMenu *dl_right_click_menu;
232 VikCoord redownload_ul, redownload_br; /* right click menu only */
233 VikViewport *redownload_vvp;
235 gboolean license_notice_shown; // FALSE for new maps only, otherwise
236 // TRUE for saved maps & other layer changes as we don't need to show it again
239 enum { REDOWNLOAD_NONE = 0, /* download only missing maps */
240 REDOWNLOAD_BAD, /* download missing and bad maps */
241 REDOWNLOAD_NEW, /* download missing maps that are newer on server only */
242 REDOWNLOAD_ALL, /* download all maps */
243 DOWNLOAD_OR_REFRESH }; /* download missing maps and refresh cache */
245 static VikLayerParam prefs[] = {
246 { 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") },
249 void maps_layer_init ()
251 VikLayerParamData tmp;
252 tmp.s = maps_layer_default_dir();
253 a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
256 /****************************************/
257 /******** MAPS LAYER TYPES **************/
258 /****************************************/
260 int _get_index_for_id ( guint id )
263 while (params_maptypes_ids[index] != 0)
265 if (params_maptypes_ids[index] == id)
272 void _add_map_source ( guint id, const char *label, VikMapSource *map )
276 len = g_strv_length (params_maptypes);
278 params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
279 params_maptypes[len] = g_strdup (label);
280 params_maptypes[len+1] = NULL;
283 params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
284 params_maptypes_ids[len] = id;
285 params_maptypes_ids[len+1] = 0;
287 /* We have to clone */
288 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
289 /* Register the clone in the list */
290 __map_types = g_list_append(__map_types, clone);
293 We have to ensure the mode LayerParam references the up-to-date
297 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
298 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
300 maps_layer_params[0].widget_data = params_maptypes;
301 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
304 void _update_map_source ( const char *label, VikMapSource *map, int index )
306 GList *item = g_list_nth (__map_types, index);
307 g_object_unref (item->data);
308 item->data = g_object_ref (map);
309 /* Change previous data */
310 g_free (params_maptypes[index]);
311 params_maptypes[index] = g_strdup (label);
315 * maps_layer_register_map_source:
316 * @map: the new VikMapSource
318 * Register a new VikMapSource.
319 * Override existing one (equality of id).
321 void maps_layer_register_map_source ( VikMapSource *map )
323 g_assert(map != NULL);
325 guint id = vik_map_source_get_uniq_id(map);
326 const char *label = vik_map_source_get_label(map);
327 g_assert(label != NULL);
329 int previous = map_uniq_id_to_index (id);
330 if (previous != NUM_MAP_TYPES)
332 _update_map_source (label, map, previous);
336 _add_map_source (id, label, map);
340 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
341 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
342 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
344 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
346 return(vml->maptype);
349 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
351 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
354 /****************************************/
355 /******** CACHE DIR STUFF ***************/
356 /****************************************/
358 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
359 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
360 #define MAPS_CACHE_DIR maps_layer_default_dir()
364 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
365 #define LOCAL_MAPS_DIR "VIKING-MAPS"
366 #elif defined __APPLE__
368 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
369 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
372 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
373 #define LOCAL_MAPS_DIR ".viking-maps"
376 gchar *maps_layer_default_dir ()
378 static gchar *defaultdir = NULL;
381 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
382 const gchar *mapdir = g_getenv("VIKING_MAPS");
384 defaultdir = g_strdup ( mapdir );
385 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
386 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
388 const gchar *home = g_get_home_dir();
389 if (!home || g_access(home, W_OK))
390 home = g_get_home_dir ();
392 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
394 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
396 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
398 /* Add the separator at the end */
399 gchar *tmp = defaultdir;
400 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
403 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
408 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
410 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
412 g_mkdir ( vml->cache_dir, 0777 );
416 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
419 g_assert ( vml != NULL);
420 g_free ( vml->cache_dir );
421 vml->cache_dir = NULL;
423 if ( dir == NULL || dir[0] == '\0' )
425 if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
426 vml->cache_dir = g_strdup ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s );
431 if ( dir[len-1] != G_DIR_SEPARATOR )
433 vml->cache_dir = g_malloc ( len+2 );
434 strncpy ( vml->cache_dir, dir, len );
435 vml->cache_dir[len] = G_DIR_SEPARATOR;
436 vml->cache_dir[len+1] = '\0';
439 vml->cache_dir = g_strdup ( dir );
441 maps_layer_mkdir_if_default_dir ( vml );
444 /****************************************/
445 /******** GOBJECT STUFF *****************/
446 /****************************************/
448 GType vik_maps_layer_get_type ()
450 static GType vml_type = 0;
454 static const GTypeInfo vml_info =
456 sizeof (VikMapsLayerClass),
457 NULL, /* base_init */
458 NULL, /* base_finalize */
459 NULL, /* class init */
460 NULL, /* class_finalize */
461 NULL, /* class_data */
462 sizeof (VikMapsLayer),
464 NULL /* instance init */
466 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
472 /****************************************/
473 /************** PARAMETERS **************/
474 /****************************************/
476 static guint map_index_to_uniq_id (guint8 index)
478 g_assert ( index < NUM_MAP_TYPES );
479 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
482 static guint map_uniq_id_to_index ( guint uniq_id )
485 for ( i = 0; i < NUM_MAP_TYPES; i++ )
486 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
488 return NUM_MAP_TYPES; /* no such thing */
491 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
493 // When loading from a file don't need the license reminder
494 if ( is_file_operation )
495 vml->license_notice_shown = TRUE;
499 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
500 case PARAM_MAPTYPE: {
501 gint maptype = map_uniq_id_to_index(data.u);
502 if ( maptype == NUM_MAP_TYPES ) g_warning(_("Unknown map type"));
503 else vml->maptype = maptype;
506 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
507 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
508 case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
509 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
510 vml->mapzoom_id = data.u;
511 vml->xmapzoom = __mapzooms_x [data.u];
512 vml->ymapzoom = __mapzooms_y [data.u];
513 }else g_warning (_("Unknown Map Zoom")); break;
518 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
520 VikLayerParamData rv;
523 case PARAM_CACHE_DIR:
525 gboolean set = FALSE;
526 /* Only save a blank when the map cache location equals the default
527 On reading in, when it is blank then the default is reconstructed
528 Since the default changes dependent on the user and OS, it means the resultant file is more portable */
529 if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
533 else if ( is_file_operation ) {
534 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
535 gchar *cwd = g_get_current_dir();
537 rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
538 if ( !rv.s ) rv.s = "";
544 rv.s = vml->cache_dir ? vml->cache_dir : "";
547 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
548 case PARAM_ALPHA: rv.u = vml->alpha; break;
549 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
550 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
551 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
556 /****************************************/
557 /****** CREATING, COPYING, FREEING ******/
558 /****************************************/
560 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
562 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
563 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
565 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
567 vml->dl_tool_x = vml->dl_tool_y = -1;
568 vml->last_center = NULL;
569 vml->last_xmpp = 0.0;
570 vml->last_ympp = 0.0;
572 vml->dl_right_click_menu = NULL;
573 vml->license_notice_shown = FALSE;
578 static void maps_layer_free ( VikMapsLayer *vml )
580 g_free ( vml->cache_dir );
581 vml->cache_dir = NULL;
582 if ( vml->dl_right_click_menu )
583 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
584 g_free(vml->last_center);
585 vml->last_center = NULL;
588 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
590 if (from_file != TRUE)
592 /* If this method is not called in file reading context
593 * it is called in GUI context.
594 * So, we can check if we have to inform the user about inconsistency */
595 VikViewportDrawMode vp_drawmode;
596 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
597 VikMapSource *map = NULL;
599 vp_drawmode = vik_viewport_get_drawmode ( vp );
600 map = MAPS_LAYER_NTH_TYPE(vml->maptype);
601 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
602 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
603 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
604 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
608 if (vik_map_source_get_license (map) != NULL) {
609 if ( ! vml->license_notice_shown ) {
610 a_dialog_license (VIK_GTK_WINDOW_FROM_WIDGET(vp), vik_map_source_get_label (map),
611 vik_map_source_get_license (map), vik_map_source_get_license_url (map) );
612 vml->license_notice_shown = TRUE;
618 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
620 return vik_maps_layer_get_map_label ( vml );
623 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
625 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
628 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
630 VikMapsLayer *rv = maps_layer_new ( vvp );
631 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
635 /*********************/
636 /****** DRAWING ******/
637 /*********************/
639 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
642 gint width, height, iii, jjj;
644 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
646 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
647 g_object_unref(G_OBJECT(pixbuf));
651 pixels = gdk_pixbuf_get_pixels(pixbuf);
652 width = gdk_pixbuf_get_width(pixbuf);
653 height = gdk_pixbuf_get_height(pixbuf);
655 /* r,g,b,a,r,g,b,a.... */
656 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
664 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
667 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
668 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
669 g_object_unref ( G_OBJECT(pixbuf) );
673 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
678 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
679 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
682 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
683 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS,
684 vml->cache_dir, (17 - mapcoord->scale), mapcoord->x, mapcoord->y, ".png" );
686 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
687 vml->cache_dir, mode,
688 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
690 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
693 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
695 /* free the pixbuf on error */
698 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
699 g_warning ( _("Couldn't open image file: %s"), gx->message );
703 g_object_unref ( G_OBJECT(pixbuf) );
706 if ( vml->alpha < 255 )
707 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
708 if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
709 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
711 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
712 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
713 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
720 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
722 const VikCoord *center = vik_viewport_get_center ( vvp );
724 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
725 /* D'n'D pan in action: do not download */
729 // Prevent requests for downloading tiles at Zoom Level 19 and above for most map types
730 // Allow MapQuest Zoom Level up to 19
731 // TODO: This should be made a property of the map source and then use that value
732 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
733 if ( (vml->maptype != 19 && map_utils_mpp_to_scale (xzoom) < -1) || (vml->maptype == 19 && map_utils_mpp_to_scale (xzoom) < -2) )
736 if (vml->last_center == NULL) {
737 VikCoord *new_center = g_malloc(sizeof(VikCoord));
738 *new_center = *center;
739 vml->last_center = new_center;
740 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
741 vml->last_ympp = vik_viewport_get_ympp(vvp);
745 /* TODO: perhaps vik_coord_diff() */
746 if (vik_coord_equals(vml->last_center, center)
747 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
748 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
751 *(vml->last_center) = *center;
752 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
753 vml->last_ympp = vik_viewport_get_ympp(vvp);
757 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
760 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
761 gdouble yzoom = vik_viewport_get_ympp ( vvp );
762 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
763 gboolean existence_only = FALSE;
765 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
766 xshrinkfactor = vml->xmapzoom / xzoom;
767 yshrinkfactor = vml->ymapzoom / yzoom;
768 xzoom = vml->xmapzoom;
769 yzoom = vml->xmapzoom;
770 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
771 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
772 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
773 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
774 existence_only = TRUE;
777 g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
784 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
785 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
786 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
790 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
791 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
792 gint mode = vik_map_source_get_uniq_id(map);
795 gint xx, yy, width, height;
798 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
799 // which can happen when using a small fixed zoom level and viewing large areas.
800 // Also prevents very large number of tile download requests
801 gint tiles = (xmax-xmin) * (ymax-ymin);
802 if ( tiles > MAX_TILES ) {
803 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
804 existence_only = TRUE;
807 guint max_path_len = strlen(vml->cache_dir) + 40;
808 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
810 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
811 g_debug("%s: Starting autodownload", __FUNCTION__);
812 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
813 // Try to download newer tiles
814 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
816 // Download only missing tiles
817 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
820 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
821 for ( x = xmin; x <= xmax; x++ ) {
822 for ( y = ymin; y <= ymax; y++ ) {
825 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
827 width = gdk_pixbuf_get_width ( pixbuf );
828 height = gdk_pixbuf_get_height ( pixbuf );
830 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
831 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
835 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
839 } else { /* tilesize is known, don't have to keep converting coords */
840 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
841 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
842 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
843 gint tilesize_x_ceil = ceil ( tilesize_x );
844 gint tilesize_y_ceil = ceil ( tilesize_y );
845 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
846 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
847 gdouble xx, yy; gint xx_tmp, yy_tmp;
848 gint base_yy, xend, yend;
850 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
851 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
853 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
854 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
855 xx = xx_tmp; yy = yy_tmp;
856 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
857 * eg if tile size 128, shrinkfactor 0.333 */
858 xx -= (tilesize_x/2);
859 base_yy = yy - (tilesize_y/2);
861 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
863 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
867 if ( existence_only ) {
868 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
869 g_snprintf ( path_buf, max_path_len, DIRECTDIRACCESS,
870 vml->cache_dir, (17 - ulm.scale), ulm.x, ulm.y, ".png" );
872 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
873 vml->cache_dir, mode,
874 ulm.scale, ulm.z, ulm.x, ulm.y );
875 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
876 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
877 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
881 for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
882 /* try with correct then smaller zooms */
883 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
885 ulm2.x = ulm.x / scale_factor;
886 ulm2.y = ulm.y / scale_factor;
887 ulm2.scale = ulm.scale + scale_inc;
888 pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
890 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
891 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
893 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);
895 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
900 /* retry with bigger zooms */
902 for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
904 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
906 ulm2.x = ulm.x * scale_factor;
907 ulm2.y = ulm.y * scale_factor;
908 ulm2.scale = ulm.scale - scale_dec;
909 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
910 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
911 MapCoord ulm3 = ulm2;
914 pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
918 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
919 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
920 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
938 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
940 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
945 gdouble level = vik_viewport_get_zoom ( vvp );
947 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
948 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
951 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
952 vik_viewport_add_logo ( vvp, logo );
954 /* get corner coords */
955 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
956 /* UTM multi-zone stuff by Kit Transue */
957 gchar leftmost_zone, rightmost_zone, i;
958 leftmost_zone = vik_viewport_leftmost_zone( vvp );
959 rightmost_zone = vik_viewport_rightmost_zone( vvp );
960 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
961 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
962 maps_layer_draw_section ( vml, vvp, &ul, &br );
966 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
967 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
969 maps_layer_draw_section ( vml, vvp, &ul, &br );
974 /*************************/
975 /****** DOWNLOADING ******/
976 /*************************/
978 /* pass along data to thread, exists even if layer is deleted. */
988 gboolean refresh_display;
991 gboolean map_layer_alive;
995 static void mdi_free ( MapDownloadInfo *mdi )
997 g_mutex_free(mdi->mutex);
998 g_free ( mdi->cache_dir );
999 mdi->cache_dir = NULL;
1000 g_free ( mdi->filename_buf );
1001 mdi->filename_buf = NULL;
1005 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
1007 MapDownloadInfo *mdi = ptr;
1008 g_mutex_lock(mdi->mutex);
1009 mdi->map_layer_alive = FALSE;
1010 g_mutex_unlock(mdi->mutex);
1013 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1015 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1018 for ( x = mdi->x0; x <= mdi->xf; x++ )
1020 for ( y = mdi->y0; y <= mdi->yf; y++ )
1022 gboolean remove_mem_cache = FALSE;
1023 gboolean need_download = FALSE;
1024 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1025 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1026 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
1029 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1031 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1035 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1036 need_download = TRUE;
1037 remove_mem_cache = TRUE;
1039 } else { /* in case map file already exists */
1040 switch (mdi->redownload) {
1041 case REDOWNLOAD_NONE:
1044 case REDOWNLOAD_BAD:
1046 /* see if this one is bad or what */
1048 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1049 if (gx || (!pixbuf)) {
1050 g_remove ( mdi->filename_buf );
1051 need_download = TRUE;
1052 remove_mem_cache = TRUE;
1053 g_error_free ( gx );
1056 g_object_unref ( pixbuf );
1061 case REDOWNLOAD_NEW:
1062 need_download = TRUE;
1063 remove_mem_cache = TRUE;
1066 case REDOWNLOAD_ALL:
1067 /* FIXME: need a better way than to erase file in case of server/network problem */
1068 g_remove ( mdi->filename_buf );
1069 need_download = TRUE;
1070 remove_mem_cache = TRUE;
1073 case DOWNLOAD_OR_REFRESH:
1074 remove_mem_cache = TRUE;
1078 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1082 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1084 if (need_download) {
1085 if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
1089 g_mutex_lock(mdi->mutex);
1090 if (remove_mem_cache)
1091 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 );
1092 if (mdi->refresh_display && mdi->map_layer_alive) {
1093 /* TODO: check if it's on visible area */
1094 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1096 g_mutex_unlock(mdi->mutex);
1097 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1101 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1102 g_mutex_lock(mdi->mutex);
1103 if (mdi->map_layer_alive)
1104 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1105 g_mutex_unlock(mdi->mutex);
1109 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1111 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1113 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1114 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1115 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
1116 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1118 g_remove ( mdi->filename_buf );
1123 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1125 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1126 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1128 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1130 // Don't ever attempt download on direct access
1131 if ( vik_map_source_is_direct_file_access ( map ) )
1134 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1135 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1137 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1142 mdi->map_layer_alive = TRUE;
1143 mdi->mutex = g_mutex_new();
1144 mdi->refresh_display = TRUE;
1146 /* cache_dir and buffer for dest filename */
1147 mdi->cache_dir = g_strdup ( vml->cache_dir );
1148 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1149 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1150 mdi->maptype = vml->maptype;
1152 mdi->mapcoord = ulm;
1154 mdi->redownload = redownload;
1156 mdi->x0 = MIN(ulm.x, brm.x);
1157 mdi->xf = MAX(ulm.x, brm.x);
1158 mdi->y0 = MIN(ulm.y, brm.y);
1159 mdi->yf = MAX(ulm.y, brm.y);
1163 if ( mdi->redownload ) {
1164 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1166 /* calculate how many we need */
1167 for ( a = mdi->x0; a <= mdi->xf; a++ )
1169 for ( b = mdi->y0; b <= mdi->yf; b++ )
1171 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1172 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1174 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1180 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1182 if ( mdi->mapstoget )
1184 const gchar *tmp_str;
1189 if (redownload == REDOWNLOAD_BAD)
1190 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1192 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1196 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1198 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1200 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1201 /* launch the thread */
1202 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1203 tmp, /* description string */
1204 (vik_thr_func) map_download_thread, /* function to call within thread */
1205 mdi, /* pass along data */
1206 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1207 (vik_thr_free_func) mdi_cancel_cleanup,
1216 void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
1219 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1221 // Don't ever attempt download on direct access
1222 if ( vik_map_source_is_direct_file_access ( map ) )
1225 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1226 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1227 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1231 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1236 mdi->map_layer_alive = TRUE;
1237 mdi->mutex = g_mutex_new();
1238 mdi->refresh_display = TRUE;
1240 mdi->cache_dir = g_strdup ( vml->cache_dir );
1241 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1242 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1243 mdi->maptype = vml->maptype;
1245 mdi->mapcoord = ulm;
1247 mdi->redownload = REDOWNLOAD_NONE;
1249 mdi->x0 = MIN(ulm.x, brm.x);
1250 mdi->xf = MAX(ulm.x, brm.x);
1251 mdi->y0 = MIN(ulm.y, brm.y);
1252 mdi->yf = MAX(ulm.y, brm.y);
1256 for (i = mdi->x0; i <= mdi->xf; i++) {
1257 for (j = mdi->y0; j <= mdi->yf; j++) {
1258 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1259 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1261 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1266 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1268 if (mdi->mapstoget) {
1271 fmt = ngettext("Downloading %d %s map...",
1272 "Downloading %d %s maps...",
1274 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1276 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1277 /* launch the thread */
1278 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1279 tmp, /* description string */
1280 (vik_thr_func) map_download_thread, /* function to call within thread */
1281 mdi, /* pass along data */
1282 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1283 (vik_thr_free_func) mdi_cancel_cleanup,
1291 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1293 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1296 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1298 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1301 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1303 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1307 * Display a simple dialog with information about this particular map tile
1309 static void maps_layer_tile_info ( VikMapsLayer *vml )
1311 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1313 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1314 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1317 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1320 gchar *filename = NULL;
1321 gchar *message = NULL;
1322 gchar *source = NULL;
1324 if ( vik_map_source_is_direct_file_access ( map ) ) {
1325 filename = g_strdup_printf ( DIRECTDIRACCESS, vml->cache_dir, ulm.scale, ulm.x, ulm.y, ".png" );
1326 source = g_strconcat ( "file://", filename, NULL );
1329 filename = g_strdup_printf ( DIRSTRUCTURE, vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale, ulm.z, ulm.x, ulm.y );
1330 source = g_strdup_printf ( "http://%s%s",
1331 vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1332 vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1335 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1337 // Get some timestamp information of the tile
1338 struct stat stat_buf;
1339 if ( g_stat ( filename, &stat_buf ) == 0 ) {
1341 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1342 message = g_strdup_printf ( _("\nSource: %s\n\nTile File: %s\nTile File Timestamp: %s"), source, filename, time_buf );
1346 message = g_strdup_printf ( _("Source: %s\n\nNo Tile File!"), source );
1349 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), message );
1353 g_free ( filename );
1356 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1358 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1360 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1362 if ( event->button == 1 )
1365 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 );
1366 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 );
1367 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1368 vml->dl_tool_x = vml->dl_tool_y = -1;
1373 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) );
1374 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) );
1376 vml->redownload_vvp = vvp;
1378 vml->dl_tool_x = vml->dl_tool_y = -1;
1380 if ( ! vml->dl_right_click_menu ) {
1382 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1384 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1385 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1386 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1388 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1389 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1390 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1392 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1393 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1394 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1396 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
1397 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1398 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
1399 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
1402 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1403 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1409 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1414 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1417 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1419 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1420 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1421 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1422 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1423 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1425 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1432 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1436 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1437 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1438 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1439 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1441 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1442 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1443 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1445 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1446 g_free ( filename_buf );
1447 vik_layer_emit_update ( VIK_LAYER(vml) );
1455 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1457 VikMapsLayer *vml = vml_vvp[0];
1458 VikViewport *vvp = vml_vvp[1];
1459 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1461 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1462 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1467 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1468 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1470 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1471 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1472 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1473 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1474 start_download_thread ( vml, vvp, &ul, &br, redownload );
1475 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1476 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1477 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1478 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1482 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1486 static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
1488 download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1491 static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
1493 download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
1496 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1498 download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1501 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1503 static gpointer pass_along[2];
1505 pass_along[0] = vml;
1506 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1508 item = gtk_menu_item_new();
1509 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1510 gtk_widget_show ( item );
1512 /* Now with icons */
1513 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
1514 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
1515 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
1516 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1517 gtk_widget_show ( item );
1519 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
1520 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
1521 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
1522 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
1523 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1524 gtk_widget_show ( item );
1527 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
1528 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
1529 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1530 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1531 gtk_widget_show ( item );
1535 * Enable downloading maps of the current screen area either 'new' or 'everything'
1537 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
1542 static gpointer pass_along[2];
1543 pass_along[0] = vml;
1544 pass_along[1] = vvp;
1547 // Get only new maps
1548 maps_layer_download_new_onscreen_maps ( pass_along );
1550 // Redownload everything
1551 maps_layer_redownload_all_onscreen_maps ( pass_along );