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 #define MAX_SHRINKFACTOR 8.0000001 /* zoom 1 viewing 8-tiles */
30 #define MIN_SHRINKFACTOR 0.0312499 /* zoom 32 viewing 1-tiles */
32 #define REAL_MIN_SHRINKFACTOR 0.0039062499 /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
35 #include <gdk-pixbuf/gdk-pixdata.h>
37 #include <glib/gstdio.h>
38 #include <glib/gi18n.h>
51 #include "viktreeview.h"
52 #include "vikviewport.h"
54 #include "vikmapslayer.h"
60 #include "vikmapsourcedefault.h"
62 /* only for dialog.h -- ugh */
63 #include "vikwaypoint.h"
65 #include "preferences.h"
67 #include "vikstatus.h"
68 #include "background.h"
70 #include "vikaggregatelayer.h"
71 #include "viklayerspanel.h"
75 #include "icons/icons.h"
77 /****** MAP TYPES ******/
79 static GList *__map_types = NULL;
81 #define NUM_MAP_TYPES g_list_length(__map_types)
83 /* List of label for each map type */
84 static gchar **params_maptypes = NULL;
86 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
87 static guint *params_maptypes_ids = NULL;
89 /******** MAPZOOMS *********/
91 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 };
92 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 };
93 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 };
95 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
97 /**************************/
100 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
101 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
102 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
103 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
104 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
105 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
106 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
107 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
108 static void maps_layer_free ( VikMapsLayer *vml );
109 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
110 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
111 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
112 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
113 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
114 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
115 static guint map_uniq_id_to_index ( guint uniq_id );
118 static VikLayerParamScale params_scales[] = {
119 /* min, max, step, digits (decimal places) */
120 { 0, 255, 3, 0 }, /* alpha */
123 VikLayerParam maps_layer_params[] = {
124 { "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL },
125 { "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, NULL },
126 { "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
127 N_("Control the Alpha value for transparency effects") },
128 { "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
129 { "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
130 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.") },
131 { "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
132 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.") },
145 static VikToolInterface maps_tools[] = {
146 { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
147 (VikToolConstructorFunc) maps_layer_download_create,
151 (VikToolMouseFunc) maps_layer_download_click,
153 (VikToolMouseFunc) maps_layer_download_release,
156 GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf },
159 VikLayerInterface vik_maps_layer_interface = {
163 &vikmapslayer_pixbuf,
166 sizeof(maps_tools) / sizeof(maps_tools[0]),
175 (VikLayerFuncCreate) maps_layer_new,
176 (VikLayerFuncRealize) NULL,
177 (VikLayerFuncPostRead) maps_layer_post_read,
178 (VikLayerFuncFree) maps_layer_free,
180 (VikLayerFuncProperties) NULL,
181 (VikLayerFuncDraw) maps_layer_draw,
182 (VikLayerFuncChangeCoordMode) NULL,
184 (VikLayerFuncSetMenuItemsSelection) NULL,
185 (VikLayerFuncGetMenuItemsSelection) NULL,
187 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
188 (VikLayerFuncSublayerAddMenuItems) NULL,
190 (VikLayerFuncSublayerRenameRequest) NULL,
191 (VikLayerFuncSublayerToggleVisible) NULL,
192 (VikLayerFuncSublayerTooltip) NULL,
193 (VikLayerFuncLayerTooltip) maps_layer_tooltip,
194 (VikLayerFuncLayerSelected) NULL,
196 (VikLayerFuncMarshall) maps_layer_marshall,
197 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
199 (VikLayerFuncSetParam) maps_layer_set_param,
200 (VikLayerFuncGetParam) maps_layer_get_param,
202 (VikLayerFuncReadFileData) NULL,
203 (VikLayerFuncWriteFileData) NULL,
205 (VikLayerFuncDeleteItem) NULL,
206 (VikLayerFuncCutItem) NULL,
207 (VikLayerFuncCopyItem) NULL,
208 (VikLayerFuncPasteItem) NULL,
209 (VikLayerFuncFreeCopiedItem) NULL,
210 (VikLayerFuncDragDropRequest) NULL,
212 (VikLayerFuncSelectClick) NULL,
213 (VikLayerFuncSelectMove) NULL,
214 (VikLayerFuncSelectRelease) NULL,
215 (VikLayerFuncSelectedViewportMenu) NULL,
218 struct _VikMapsLayer {
224 gdouble xmapzoom, ymapzoom;
226 gboolean autodownload;
227 gboolean adl_only_missing;
228 VikCoord *last_center;
232 gint dl_tool_x, dl_tool_y;
234 GtkMenu *dl_right_click_menu;
235 VikCoord redownload_ul, redownload_br; /* right click menu only */
236 VikViewport *redownload_vvp;
238 gboolean license_notice_shown; // FALSE for new maps only, otherwise
239 // TRUE for saved maps & other layer changes as we don't need to show it again
242 enum { REDOWNLOAD_NONE = 0, /* download only missing maps */
243 REDOWNLOAD_BAD, /* download missing and bad maps */
244 REDOWNLOAD_NEW, /* download missing maps that are newer on server only */
245 REDOWNLOAD_ALL, /* download all maps */
246 DOWNLOAD_OR_REFRESH }; /* download missing maps and refresh cache */
248 static VikLayerParam prefs[] = {
249 { VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Default maplayer directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, N_("Choose a directory to store cached Map tiles for this layer") },
252 void maps_layer_init ()
254 VikLayerParamData tmp;
255 tmp.s = maps_layer_default_dir();
256 a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
259 /****************************************/
260 /******** MAPS LAYER TYPES **************/
261 /****************************************/
263 int _get_index_for_id ( guint id )
266 while (params_maptypes_ids[index] != 0)
268 if (params_maptypes_ids[index] == id)
275 void _add_map_source ( guint id, const char *label, VikMapSource *map )
279 len = g_strv_length (params_maptypes);
281 params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
282 params_maptypes[len] = g_strdup (label);
283 params_maptypes[len+1] = NULL;
286 params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
287 params_maptypes_ids[len] = id;
288 params_maptypes_ids[len+1] = 0;
290 /* We have to clone */
291 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
292 /* Register the clone in the list */
293 __map_types = g_list_append(__map_types, clone);
296 We have to ensure the mode LayerParam references the up-to-date
300 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
301 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
303 maps_layer_params[0].widget_data = params_maptypes;
304 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
307 void _update_map_source ( const char *label, VikMapSource *map, int index )
309 GList *item = g_list_nth (__map_types, index);
310 g_object_unref (item->data);
311 item->data = g_object_ref (map);
312 /* Change previous data */
313 g_free (params_maptypes[index]);
314 params_maptypes[index] = g_strdup (label);
318 * maps_layer_register_map_source:
319 * @map: the new VikMapSource
321 * Register a new VikMapSource.
322 * Override existing one (equality of id).
324 void maps_layer_register_map_source ( VikMapSource *map )
326 g_assert(map != NULL);
328 guint id = vik_map_source_get_uniq_id(map);
329 const char *label = vik_map_source_get_label(map);
330 g_assert(label != NULL);
332 int previous = map_uniq_id_to_index (id);
333 if (previous != NUM_MAP_TYPES)
335 _update_map_source (label, map, previous);
339 _add_map_source (id, label, map);
343 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
344 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
345 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
347 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
349 return(vml->maptype);
352 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
354 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
357 /****************************************/
358 /******** CACHE DIR STUFF ***************/
359 /****************************************/
361 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
362 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
363 #define MAPS_CACHE_DIR maps_layer_default_dir()
367 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
368 #define LOCAL_MAPS_DIR "VIKING-MAPS"
369 #elif defined __APPLE__
371 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
372 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
375 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
376 #define LOCAL_MAPS_DIR ".viking-maps"
379 gchar *maps_layer_default_dir ()
381 static gchar *defaultdir = NULL;
384 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
385 const gchar *mapdir = g_getenv("VIKING_MAPS");
387 defaultdir = g_strdup ( mapdir );
388 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
389 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
391 const gchar *home = g_get_home_dir();
392 if (!home || g_access(home, W_OK))
393 home = g_get_home_dir ();
395 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
397 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
399 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
401 /* Add the separator at the end */
402 gchar *tmp = defaultdir;
403 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
406 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
411 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
413 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
415 g_mkdir ( vml->cache_dir, 0777 );
419 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
422 g_assert ( vml != NULL);
423 g_free ( vml->cache_dir );
424 vml->cache_dir = NULL;
426 if ( dir == NULL || dir[0] == '\0' )
428 if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
429 vml->cache_dir = g_strdup ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s );
434 if ( dir[len-1] != G_DIR_SEPARATOR )
436 vml->cache_dir = g_malloc ( len+2 );
437 strncpy ( vml->cache_dir, dir, len );
438 vml->cache_dir[len] = G_DIR_SEPARATOR;
439 vml->cache_dir[len+1] = '\0';
442 vml->cache_dir = g_strdup ( dir );
444 maps_layer_mkdir_if_default_dir ( vml );
447 /****************************************/
448 /******** GOBJECT STUFF *****************/
449 /****************************************/
451 GType vik_maps_layer_get_type ()
453 static GType vml_type = 0;
457 static const GTypeInfo vml_info =
459 sizeof (VikMapsLayerClass),
460 NULL, /* base_init */
461 NULL, /* base_finalize */
462 NULL, /* class init */
463 NULL, /* class_finalize */
464 NULL, /* class_data */
465 sizeof (VikMapsLayer),
467 NULL /* instance init */
469 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
475 /****************************************/
476 /************** PARAMETERS **************/
477 /****************************************/
479 static guint map_index_to_uniq_id (guint8 index)
481 g_assert ( index < NUM_MAP_TYPES );
482 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
485 static guint map_uniq_id_to_index ( guint uniq_id )
488 for ( i = 0; i < NUM_MAP_TYPES; i++ )
489 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
491 return NUM_MAP_TYPES; /* no such thing */
494 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
496 // When loading from a file don't need the license reminder
497 if ( is_file_operation )
498 vml->license_notice_shown = TRUE;
502 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
503 case PARAM_MAPTYPE: {
504 gint maptype = map_uniq_id_to_index(data.u);
505 if ( maptype == NUM_MAP_TYPES ) g_warning(_("Unknown map type"));
506 else vml->maptype = maptype;
509 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
510 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
511 case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
512 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
513 vml->mapzoom_id = data.u;
514 vml->xmapzoom = __mapzooms_x [data.u];
515 vml->ymapzoom = __mapzooms_y [data.u];
516 }else g_warning (_("Unknown Map Zoom")); break;
521 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
523 VikLayerParamData rv;
526 case PARAM_CACHE_DIR: rv.s = vml->cache_dir ? vml->cache_dir : ""; break;
527 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
528 case PARAM_ALPHA: rv.u = vml->alpha; break;
529 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
530 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
531 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
536 /****************************************/
537 /****** CREATING, COPYING, FREEING ******/
538 /****************************************/
540 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
543 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
544 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
545 idx = map_uniq_id_to_index(19); /* 19 is id for OSM MapQuest maps */
546 vml->maptype = (idx < NUM_MAP_TYPES) ? idx : 0;
547 vml->maptype = (idx < NUM_MAP_TYPES) ? idx : 0;
550 vml->dl_tool_x = vml->dl_tool_y = -1;
551 maps_layer_set_cache_dir ( vml, NULL );
552 vml->autodownload = FALSE;
553 vml->adl_only_missing = FALSE;
554 vml->last_center = NULL;
555 vml->last_xmpp = 0.0;
556 vml->last_ympp = 0.0;
558 vml->dl_right_click_menu = NULL;
559 vml->license_notice_shown = FALSE;
564 static void maps_layer_free ( VikMapsLayer *vml )
566 g_free ( vml->cache_dir );
567 vml->cache_dir = NULL;
568 if ( vml->dl_right_click_menu )
569 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
570 g_free(vml->last_center);
571 vml->last_center = NULL;
574 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
576 if (from_file != TRUE)
578 /* If this method is not called in file reading context
579 * it is called in GUI context.
580 * So, we can check if we have to inform the user about inconsistency */
581 VikViewportDrawMode vp_drawmode;
582 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
583 VikMapSource *map = NULL;
585 vp_drawmode = vik_viewport_get_drawmode ( vp );
586 map = MAPS_LAYER_NTH_TYPE(vml->maptype);
587 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
588 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
589 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
590 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
594 if (vik_map_source_get_license (map) != NULL) {
595 if ( ! vml->license_notice_shown ) {
596 a_dialog_license (VIK_GTK_WINDOW_FROM_WIDGET(vp), vik_map_source_get_label (map),
597 vik_map_source_get_license (map), vik_map_source_get_license_url (map) );
598 vml->license_notice_shown = TRUE;
604 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
606 return vik_maps_layer_get_map_label ( vml );
609 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
611 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
614 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
616 VikMapsLayer *rv = maps_layer_new ( vvp );
617 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
621 /*********************/
622 /****** DRAWING ******/
623 /*********************/
625 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
628 gint width, height, iii, jjj;
630 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
632 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
633 g_object_unref(G_OBJECT(pixbuf));
637 pixels = gdk_pixbuf_get_pixels(pixbuf);
638 width = gdk_pixbuf_get_width(pixbuf);
639 height = gdk_pixbuf_get_height(pixbuf);
641 /* r,g,b,a,r,g,b,a.... */
642 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
650 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
653 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
654 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_NEAREST);
655 g_object_unref ( G_OBJECT(pixbuf) );
659 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
664 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
665 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
668 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
669 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS,
670 vml->cache_dir, (17 - mapcoord->scale), mapcoord->x, mapcoord->y, ".png" );
672 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
673 vml->cache_dir, mode,
674 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
676 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
679 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
681 /* free the pixbuf on error */
684 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
685 g_warning ( _("Couldn't open image file: %s"), gx->message );
689 g_object_unref ( G_OBJECT(pixbuf) );
692 if ( vml->alpha < 255 )
693 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
694 if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
695 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
697 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
698 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
699 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
706 gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
708 const VikCoord *center = vik_viewport_get_center ( vvp );
710 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
711 /* D'n'D pan in action: do not download */
714 if (vml->last_center == NULL) {
715 VikCoord *new_center = g_malloc(sizeof(VikCoord));
716 *new_center = *center;
717 vml->last_center = new_center;
718 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
719 vml->last_ympp = vik_viewport_get_ympp(vvp);
723 /* TODO: perhaps vik_coord_diff() */
724 if (vik_coord_equals(vml->last_center, center)
725 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
726 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
729 *(vml->last_center) = *center;
730 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
731 vml->last_ympp = vik_viewport_get_ympp(vvp);
735 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
738 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
739 gdouble yzoom = vik_viewport_get_ympp ( vvp );
740 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
741 gboolean existence_only = FALSE;
743 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
744 xshrinkfactor = vml->xmapzoom / xzoom;
745 yshrinkfactor = vml->ymapzoom / yzoom;
746 xzoom = vml->xmapzoom;
747 yzoom = vml->xmapzoom;
748 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
749 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
750 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR )
751 existence_only = TRUE;
753 g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
760 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
761 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
762 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
766 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
767 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
768 gint mode = vik_map_source_get_uniq_id(map);
771 gint xx, yy, width, height;
774 guint max_path_len = strlen(vml->cache_dir) + 40;
775 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
777 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
778 g_debug("%s: Starting autodownload", __FUNCTION__);
779 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
780 // Try to download newer tiles
781 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
783 // Download only missing tiles
784 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
787 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
788 for ( x = xmin; x <= xmax; x++ ) {
789 for ( y = ymin; y <= ymax; y++ ) {
792 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
794 width = gdk_pixbuf_get_width ( pixbuf );
795 height = gdk_pixbuf_get_height ( pixbuf );
797 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
798 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
802 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
806 } else { /* tilesize is known, don't have to keep converting coords */
807 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
808 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
809 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
810 gint tilesize_x_ceil = ceil ( tilesize_x );
811 gint tilesize_y_ceil = ceil ( tilesize_y );
812 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
813 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
814 gdouble xx, yy; gint xx_tmp, yy_tmp;
815 gint base_yy, xend, yend;
817 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
818 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
820 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
821 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
822 xx = xx_tmp; yy = yy_tmp;
823 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
824 * eg if tile size 128, shrinkfactor 0.333 */
825 xx -= (tilesize_x/2);
826 base_yy = yy - (tilesize_y/2);
828 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
830 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
834 if ( existence_only ) {
835 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
836 g_snprintf ( path_buf, max_path_len, DIRECTDIRACCESS,
837 vml->cache_dir, (17 - ulm.scale), ulm.x, ulm.y, ".png" );
839 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
840 vml->cache_dir, mode,
841 ulm.scale, ulm.z, ulm.x, ulm.y );
842 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
843 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
844 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
848 for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
849 /* try with correct then smaller zooms */
850 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
852 ulm2.x = ulm.x / scale_factor;
853 ulm2.y = ulm.y / scale_factor;
854 ulm2.scale = ulm.scale + scale_inc;
855 pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
857 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
858 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
860 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);
862 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
867 /* retry with bigger zooms */
869 for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
871 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
873 ulm2.x = ulm.x * scale_factor;
874 ulm2.y = ulm.y * scale_factor;
875 ulm2.scale = ulm.scale - scale_dec;
876 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
877 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
878 MapCoord ulm3 = ulm2;
881 pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
885 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
886 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
887 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
905 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
907 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
912 gdouble level = vik_viewport_get_zoom ( vvp );
914 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
915 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
918 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
919 vik_viewport_add_logo ( vvp, logo );
921 /* get corner coords */
922 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
923 /* UTM multi-zone stuff by Kit Transue */
924 gchar leftmost_zone, rightmost_zone, i;
925 leftmost_zone = vik_viewport_leftmost_zone( vvp );
926 rightmost_zone = vik_viewport_rightmost_zone( vvp );
927 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
928 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
929 maps_layer_draw_section ( vml, vvp, &ul, &br );
933 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
934 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
936 maps_layer_draw_section ( vml, vvp, &ul, &br );
941 /*************************/
942 /****** DOWNLOADING ******/
943 /*************************/
945 /* pass along data to thread, exists even if layer is deleted. */
955 gboolean refresh_display;
958 gboolean map_layer_alive;
962 static void mdi_free ( MapDownloadInfo *mdi )
964 g_mutex_free(mdi->mutex);
965 g_free ( mdi->cache_dir );
966 mdi->cache_dir = NULL;
967 g_free ( mdi->filename_buf );
968 mdi->filename_buf = NULL;
972 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
974 MapDownloadInfo *mdi = ptr;
975 g_mutex_lock(mdi->mutex);
976 mdi->map_layer_alive = FALSE;
977 g_mutex_unlock(mdi->mutex);
980 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
982 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
985 for ( x = mdi->x0; x <= mdi->xf; x++ )
987 for ( y = mdi->y0; y <= mdi->yf; y++ )
989 gboolean remove_mem_cache = FALSE;
990 gboolean need_download = FALSE;
991 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
992 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
993 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
996 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
998 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1002 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1003 need_download = TRUE;
1004 remove_mem_cache = TRUE;
1006 } else { /* in case map file already exists */
1007 switch (mdi->redownload) {
1008 case REDOWNLOAD_NONE:
1011 case REDOWNLOAD_BAD:
1013 /* see if this one is bad or what */
1015 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1016 if (gx || (!pixbuf)) {
1017 g_remove ( mdi->filename_buf );
1018 need_download = TRUE;
1019 remove_mem_cache = TRUE;
1020 g_error_free ( gx );
1023 g_object_unref ( pixbuf );
1028 case REDOWNLOAD_NEW:
1029 need_download = TRUE;
1030 remove_mem_cache = TRUE;
1033 case REDOWNLOAD_ALL:
1034 /* FIXME: need a better way than to erase file in case of server/network problem */
1035 g_remove ( mdi->filename_buf );
1036 need_download = TRUE;
1037 remove_mem_cache = TRUE;
1040 case DOWNLOAD_OR_REFRESH:
1041 remove_mem_cache = TRUE;
1045 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1049 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1051 if (need_download) {
1052 if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
1056 g_mutex_lock(mdi->mutex);
1057 if (remove_mem_cache)
1058 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 );
1059 if (mdi->refresh_display && mdi->map_layer_alive) {
1060 /* TODO: check if it's on visible area */
1061 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1063 g_mutex_unlock(mdi->mutex);
1064 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1068 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1069 g_mutex_lock(mdi->mutex);
1070 if (mdi->map_layer_alive)
1071 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1072 g_mutex_unlock(mdi->mutex);
1076 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1078 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1080 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1081 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1082 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
1083 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1085 g_remove ( mdi->filename_buf );
1090 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1092 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1093 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1095 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1097 // Don't ever attempt download on direct access
1098 if ( vik_map_source_is_direct_file_access ( map ) )
1101 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1102 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1104 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1109 mdi->map_layer_alive = TRUE;
1110 mdi->mutex = g_mutex_new();
1111 mdi->refresh_display = TRUE;
1113 /* cache_dir and buffer for dest filename */
1114 mdi->cache_dir = g_strdup ( vml->cache_dir );
1115 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1116 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1117 mdi->maptype = vml->maptype;
1119 mdi->mapcoord = ulm;
1121 mdi->redownload = redownload;
1123 mdi->x0 = MIN(ulm.x, brm.x);
1124 mdi->xf = MAX(ulm.x, brm.x);
1125 mdi->y0 = MIN(ulm.y, brm.y);
1126 mdi->yf = MAX(ulm.y, brm.y);
1130 if ( mdi->redownload ) {
1131 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1133 /* calculate how many we need */
1134 for ( a = mdi->x0; a <= mdi->xf; a++ )
1136 for ( b = mdi->y0; b <= mdi->yf; b++ )
1138 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1139 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1141 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1147 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1149 if ( mdi->mapstoget )
1151 const gchar *tmp_str;
1156 if (redownload == REDOWNLOAD_BAD)
1157 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1159 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1163 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1165 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1167 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1168 /* launch the thread */
1169 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1170 tmp, /* description string */
1171 (vik_thr_func) map_download_thread, /* function to call within thread */
1172 mdi, /* pass along data */
1173 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1174 (vik_thr_free_func) mdi_cancel_cleanup,
1183 void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
1186 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1188 // Don't ever attempt download on direct access
1189 if ( vik_map_source_is_direct_file_access ( map ) )
1192 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1193 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1194 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1198 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1203 mdi->map_layer_alive = TRUE;
1204 mdi->mutex = g_mutex_new();
1205 mdi->refresh_display = TRUE;
1207 mdi->cache_dir = g_strdup ( vml->cache_dir );
1208 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1209 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1210 mdi->maptype = vml->maptype;
1212 mdi->mapcoord = ulm;
1214 mdi->redownload = REDOWNLOAD_NONE;
1216 mdi->x0 = MIN(ulm.x, brm.x);
1217 mdi->xf = MAX(ulm.x, brm.x);
1218 mdi->y0 = MIN(ulm.y, brm.y);
1219 mdi->yf = MAX(ulm.y, brm.y);
1223 for (i = mdi->x0; i <= mdi->xf; i++) {
1224 for (j = mdi->y0; j <= mdi->yf; j++) {
1225 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1226 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1228 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1233 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1235 if (mdi->mapstoget) {
1238 fmt = ngettext("Downloading %d %s map...",
1239 "Downloading %d %s maps...",
1241 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1243 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1244 /* launch the thread */
1245 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1246 tmp, /* description string */
1247 (vik_thr_func) map_download_thread, /* function to call within thread */
1248 mdi, /* pass along data */
1249 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1250 (vik_thr_free_func) mdi_cancel_cleanup,
1258 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1260 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1263 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1265 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1268 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1270 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1274 * Display a simple dialog with information about this particular map tile
1276 static void maps_layer_tile_info ( VikMapsLayer *vml )
1278 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1280 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1281 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1284 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1287 gchar *filename = NULL;
1288 gchar *message = NULL;
1289 gchar *source = NULL;
1291 if ( vik_map_source_is_direct_file_access ( map ) ) {
1292 filename = g_strdup_printf ( DIRECTDIRACCESS, vml->cache_dir, ulm.scale, ulm.x, ulm.y, ".png" );
1293 source = g_strconcat ( "file://", filename, NULL );
1296 filename = g_strdup_printf ( DIRSTRUCTURE, vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale, ulm.z, ulm.x, ulm.y );
1297 source = g_strdup_printf ( "http://%s%s",
1298 vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1299 vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1302 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1304 // Get some timestamp information of the tile
1305 struct stat stat_buf;
1306 if ( g_stat ( filename, &stat_buf ) == 0 ) {
1307 time_t file_time = stat_buf.st_mtime;
1308 #if GLIB_CHECK_VERSION(2,26,0)
1309 GDateTime* gdt = g_date_time_new_from_unix_utc ( file_time );
1310 gchar *time = g_date_time_format ( gdt, "%c" );
1313 strftime(time, 20, "%Y-%m-%d %H:%M:%S", localtime(&file_time));
1315 message = g_strdup_printf ( _("\nSource: %s\n\nTile File: %s\nTile File Timestamp: %s"), source, filename, time );
1317 #if GLIB_CHECK_VERSION(2,26,0)
1319 g_date_time_unref ( gdt);
1324 message = g_strdup_printf ( _("Source: %s\n\nNo Tile File!"), source );
1327 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), message );
1331 g_free ( filename );
1334 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1336 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1338 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1340 if ( event->button == 1 )
1343 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 );
1344 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 );
1345 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1346 vml->dl_tool_x = vml->dl_tool_y = -1;
1351 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) );
1352 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) );
1354 vml->redownload_vvp = vvp;
1356 vml->dl_tool_x = vml->dl_tool_y = -1;
1358 if ( ! vml->dl_right_click_menu ) {
1360 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1362 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1363 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1364 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1366 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1367 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1368 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1370 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1371 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1372 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1374 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
1375 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1376 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
1377 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
1380 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1381 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1387 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1392 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1395 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1397 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1398 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1399 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1400 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1401 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1403 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1410 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1414 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1415 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1416 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1417 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1419 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1420 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1421 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1423 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1424 g_free ( filename_buf );
1425 vik_layer_emit_update ( VIK_LAYER(vml) );
1433 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1435 VikMapsLayer *vml = vml_vvp[0];
1436 VikViewport *vvp = vml_vvp[1];
1437 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1439 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1440 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1445 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1446 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1448 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1449 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1450 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1451 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1452 start_download_thread ( vml, vvp, &ul, &br, redownload );
1453 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1454 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1455 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1456 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1460 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1464 static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
1466 download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1469 static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
1471 download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
1474 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1476 download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1479 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1481 static gpointer pass_along[2];
1483 pass_along[0] = vml;
1484 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1486 item = gtk_menu_item_new();
1487 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1488 gtk_widget_show ( item );
1490 /* Now with icons */
1491 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
1492 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
1493 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
1494 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1495 gtk_widget_show ( item );
1497 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
1498 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
1499 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
1500 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
1501 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1502 gtk_widget_show ( item );
1505 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
1506 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
1507 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1508 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1509 gtk_widget_show ( item );
1513 * Enable downloading maps of the current screen area either 'new' or 'everything'
1515 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
1520 static gpointer pass_along[2];
1521 pass_along[0] = vml;
1522 pass_along[1] = vvp;
1525 // Get only new maps
1526 maps_layer_download_new_onscreen_maps ( pass_along );
1528 // Redownload everything
1529 maps_layer_redownload_all_onscreen_maps ( pass_along );