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 maplayer 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: rv.s = vml->cache_dir ? vml->cache_dir : ""; break;
524 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
525 case PARAM_ALPHA: rv.u = vml->alpha; break;
526 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
527 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
528 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
533 /****************************************/
534 /****** CREATING, COPYING, FREEING ******/
535 /****************************************/
537 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
539 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
540 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
542 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
544 vml->dl_tool_x = vml->dl_tool_y = -1;
545 vml->last_center = NULL;
546 vml->last_xmpp = 0.0;
547 vml->last_ympp = 0.0;
549 vml->dl_right_click_menu = NULL;
550 vml->license_notice_shown = FALSE;
555 static void maps_layer_free ( VikMapsLayer *vml )
557 g_free ( vml->cache_dir );
558 vml->cache_dir = NULL;
559 if ( vml->dl_right_click_menu )
560 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
561 g_free(vml->last_center);
562 vml->last_center = NULL;
565 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
567 if (from_file != TRUE)
569 /* If this method is not called in file reading context
570 * it is called in GUI context.
571 * So, we can check if we have to inform the user about inconsistency */
572 VikViewportDrawMode vp_drawmode;
573 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
574 VikMapSource *map = NULL;
576 vp_drawmode = vik_viewport_get_drawmode ( vp );
577 map = MAPS_LAYER_NTH_TYPE(vml->maptype);
578 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
579 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
580 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
581 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
585 if (vik_map_source_get_license (map) != NULL) {
586 if ( ! vml->license_notice_shown ) {
587 a_dialog_license (VIK_GTK_WINDOW_FROM_WIDGET(vp), vik_map_source_get_label (map),
588 vik_map_source_get_license (map), vik_map_source_get_license_url (map) );
589 vml->license_notice_shown = TRUE;
595 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
597 return vik_maps_layer_get_map_label ( vml );
600 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
602 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
605 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
607 VikMapsLayer *rv = maps_layer_new ( vvp );
608 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
612 /*********************/
613 /****** DRAWING ******/
614 /*********************/
616 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
619 gint width, height, iii, jjj;
621 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
623 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
624 g_object_unref(G_OBJECT(pixbuf));
628 pixels = gdk_pixbuf_get_pixels(pixbuf);
629 width = gdk_pixbuf_get_width(pixbuf);
630 height = gdk_pixbuf_get_height(pixbuf);
632 /* r,g,b,a,r,g,b,a.... */
633 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
641 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
644 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
645 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_NEAREST);
646 g_object_unref ( G_OBJECT(pixbuf) );
650 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
655 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
656 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
659 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
660 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS,
661 vml->cache_dir, (17 - mapcoord->scale), mapcoord->x, mapcoord->y, ".png" );
663 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
664 vml->cache_dir, mode,
665 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
667 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
670 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
672 /* free the pixbuf on error */
675 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
676 g_warning ( _("Couldn't open image file: %s"), gx->message );
680 g_object_unref ( G_OBJECT(pixbuf) );
683 if ( vml->alpha < 255 )
684 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
685 if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
686 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
688 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
689 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
690 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
697 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
699 const VikCoord *center = vik_viewport_get_center ( vvp );
701 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
702 /* D'n'D pan in action: do not download */
706 // Prevent requests for downloading tiles at Zoom Level 19 and above for most map types
707 // Allow MapQuest Zoom Level up to 19
708 // TODO: This should be made a property of the map source and then use that value
709 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
710 if ( (vml->maptype != 19 && map_utils_mpp_to_scale (xzoom) < -1) || (vml->maptype == 19 && map_utils_mpp_to_scale (xzoom) < -2) )
713 if (vml->last_center == NULL) {
714 VikCoord *new_center = g_malloc(sizeof(VikCoord));
715 *new_center = *center;
716 vml->last_center = new_center;
717 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
718 vml->last_ympp = vik_viewport_get_ympp(vvp);
722 /* TODO: perhaps vik_coord_diff() */
723 if (vik_coord_equals(vml->last_center, center)
724 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
725 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
728 *(vml->last_center) = *center;
729 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
730 vml->last_ympp = vik_viewport_get_ympp(vvp);
734 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
737 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
738 gdouble yzoom = vik_viewport_get_ympp ( vvp );
739 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
740 gboolean existence_only = FALSE;
742 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
743 xshrinkfactor = vml->xmapzoom / xzoom;
744 yshrinkfactor = vml->ymapzoom / yzoom;
745 xzoom = vml->xmapzoom;
746 yzoom = vml->xmapzoom;
747 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
748 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
749 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
750 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
751 existence_only = TRUE;
754 g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
761 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
762 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
763 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
767 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
768 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
769 gint mode = vik_map_source_get_uniq_id(map);
772 gint xx, yy, width, height;
775 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
776 // which can happen when using a small fixed zoom level and viewing large areas.
777 // Also prevents very large number of tile download requests
778 gint tiles = (xmax-xmin) * (ymax-ymin);
779 if ( tiles > MAX_TILES ) {
780 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
781 existence_only = TRUE;
784 guint max_path_len = strlen(vml->cache_dir) + 40;
785 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
787 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
788 g_debug("%s: Starting autodownload", __FUNCTION__);
789 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
790 // Try to download newer tiles
791 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
793 // Download only missing tiles
794 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
797 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
798 for ( x = xmin; x <= xmax; x++ ) {
799 for ( y = ymin; y <= ymax; y++ ) {
802 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
804 width = gdk_pixbuf_get_width ( pixbuf );
805 height = gdk_pixbuf_get_height ( pixbuf );
807 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
808 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
812 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
816 } else { /* tilesize is known, don't have to keep converting coords */
817 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
818 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
819 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
820 gint tilesize_x_ceil = ceil ( tilesize_x );
821 gint tilesize_y_ceil = ceil ( tilesize_y );
822 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
823 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
824 gdouble xx, yy; gint xx_tmp, yy_tmp;
825 gint base_yy, xend, yend;
827 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
828 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
830 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
831 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
832 xx = xx_tmp; yy = yy_tmp;
833 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
834 * eg if tile size 128, shrinkfactor 0.333 */
835 xx -= (tilesize_x/2);
836 base_yy = yy - (tilesize_y/2);
838 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
840 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
844 if ( existence_only ) {
845 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
846 g_snprintf ( path_buf, max_path_len, DIRECTDIRACCESS,
847 vml->cache_dir, (17 - ulm.scale), ulm.x, ulm.y, ".png" );
849 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
850 vml->cache_dir, mode,
851 ulm.scale, ulm.z, ulm.x, ulm.y );
852 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
853 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
854 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
858 for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
859 /* try with correct then smaller zooms */
860 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
862 ulm2.x = ulm.x / scale_factor;
863 ulm2.y = ulm.y / scale_factor;
864 ulm2.scale = ulm.scale + scale_inc;
865 pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
867 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
868 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
870 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);
872 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
877 /* retry with bigger zooms */
879 for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
881 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
883 ulm2.x = ulm.x * scale_factor;
884 ulm2.y = ulm.y * scale_factor;
885 ulm2.scale = ulm.scale - scale_dec;
886 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
887 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
888 MapCoord ulm3 = ulm2;
891 pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
895 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
896 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
897 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
915 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
917 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
922 gdouble level = vik_viewport_get_zoom ( vvp );
924 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
925 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
928 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
929 vik_viewport_add_logo ( vvp, logo );
931 /* get corner coords */
932 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
933 /* UTM multi-zone stuff by Kit Transue */
934 gchar leftmost_zone, rightmost_zone, i;
935 leftmost_zone = vik_viewport_leftmost_zone( vvp );
936 rightmost_zone = vik_viewport_rightmost_zone( vvp );
937 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
938 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
939 maps_layer_draw_section ( vml, vvp, &ul, &br );
943 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
944 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
946 maps_layer_draw_section ( vml, vvp, &ul, &br );
951 /*************************/
952 /****** DOWNLOADING ******/
953 /*************************/
955 /* pass along data to thread, exists even if layer is deleted. */
965 gboolean refresh_display;
968 gboolean map_layer_alive;
972 static void mdi_free ( MapDownloadInfo *mdi )
974 g_mutex_free(mdi->mutex);
975 g_free ( mdi->cache_dir );
976 mdi->cache_dir = NULL;
977 g_free ( mdi->filename_buf );
978 mdi->filename_buf = NULL;
982 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
984 MapDownloadInfo *mdi = ptr;
985 g_mutex_lock(mdi->mutex);
986 mdi->map_layer_alive = FALSE;
987 g_mutex_unlock(mdi->mutex);
990 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
992 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
995 for ( x = mdi->x0; x <= mdi->xf; x++ )
997 for ( y = mdi->y0; y <= mdi->yf; y++ )
999 gboolean remove_mem_cache = FALSE;
1000 gboolean need_download = FALSE;
1001 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1002 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1003 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
1006 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1008 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1012 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1013 need_download = TRUE;
1014 remove_mem_cache = TRUE;
1016 } else { /* in case map file already exists */
1017 switch (mdi->redownload) {
1018 case REDOWNLOAD_NONE:
1021 case REDOWNLOAD_BAD:
1023 /* see if this one is bad or what */
1025 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1026 if (gx || (!pixbuf)) {
1027 g_remove ( mdi->filename_buf );
1028 need_download = TRUE;
1029 remove_mem_cache = TRUE;
1030 g_error_free ( gx );
1033 g_object_unref ( pixbuf );
1038 case REDOWNLOAD_NEW:
1039 need_download = TRUE;
1040 remove_mem_cache = TRUE;
1043 case REDOWNLOAD_ALL:
1044 /* FIXME: need a better way than to erase file in case of server/network problem */
1045 g_remove ( mdi->filename_buf );
1046 need_download = TRUE;
1047 remove_mem_cache = TRUE;
1050 case DOWNLOAD_OR_REFRESH:
1051 remove_mem_cache = TRUE;
1055 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1059 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1061 if (need_download) {
1062 if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
1066 g_mutex_lock(mdi->mutex);
1067 if (remove_mem_cache)
1068 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 );
1069 if (mdi->refresh_display && mdi->map_layer_alive) {
1070 /* TODO: check if it's on visible area */
1071 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1073 g_mutex_unlock(mdi->mutex);
1074 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1078 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1079 g_mutex_lock(mdi->mutex);
1080 if (mdi->map_layer_alive)
1081 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1082 g_mutex_unlock(mdi->mutex);
1086 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1088 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1090 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1091 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1092 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
1093 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1095 g_remove ( mdi->filename_buf );
1100 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1102 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1103 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1105 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1107 // Don't ever attempt download on direct access
1108 if ( vik_map_source_is_direct_file_access ( map ) )
1111 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1112 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1114 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1119 mdi->map_layer_alive = TRUE;
1120 mdi->mutex = g_mutex_new();
1121 mdi->refresh_display = TRUE;
1123 /* cache_dir and buffer for dest filename */
1124 mdi->cache_dir = g_strdup ( vml->cache_dir );
1125 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1126 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1127 mdi->maptype = vml->maptype;
1129 mdi->mapcoord = ulm;
1131 mdi->redownload = redownload;
1133 mdi->x0 = MIN(ulm.x, brm.x);
1134 mdi->xf = MAX(ulm.x, brm.x);
1135 mdi->y0 = MIN(ulm.y, brm.y);
1136 mdi->yf = MAX(ulm.y, brm.y);
1140 if ( mdi->redownload ) {
1141 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1143 /* calculate how many we need */
1144 for ( a = mdi->x0; a <= mdi->xf; a++ )
1146 for ( b = mdi->y0; b <= mdi->yf; b++ )
1148 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1149 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1151 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1157 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1159 if ( mdi->mapstoget )
1161 const gchar *tmp_str;
1166 if (redownload == REDOWNLOAD_BAD)
1167 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1169 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1173 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1175 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1177 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1178 /* launch the thread */
1179 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1180 tmp, /* description string */
1181 (vik_thr_func) map_download_thread, /* function to call within thread */
1182 mdi, /* pass along data */
1183 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1184 (vik_thr_free_func) mdi_cancel_cleanup,
1193 void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
1196 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1198 // Don't ever attempt download on direct access
1199 if ( vik_map_source_is_direct_file_access ( map ) )
1202 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1203 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1204 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1208 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1213 mdi->map_layer_alive = TRUE;
1214 mdi->mutex = g_mutex_new();
1215 mdi->refresh_display = TRUE;
1217 mdi->cache_dir = g_strdup ( vml->cache_dir );
1218 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1219 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1220 mdi->maptype = vml->maptype;
1222 mdi->mapcoord = ulm;
1224 mdi->redownload = REDOWNLOAD_NONE;
1226 mdi->x0 = MIN(ulm.x, brm.x);
1227 mdi->xf = MAX(ulm.x, brm.x);
1228 mdi->y0 = MIN(ulm.y, brm.y);
1229 mdi->yf = MAX(ulm.y, brm.y);
1233 for (i = mdi->x0; i <= mdi->xf; i++) {
1234 for (j = mdi->y0; j <= mdi->yf; j++) {
1235 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1236 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1238 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1243 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1245 if (mdi->mapstoget) {
1248 fmt = ngettext("Downloading %d %s map...",
1249 "Downloading %d %s maps...",
1251 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1253 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1254 /* launch the thread */
1255 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1256 tmp, /* description string */
1257 (vik_thr_func) map_download_thread, /* function to call within thread */
1258 mdi, /* pass along data */
1259 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1260 (vik_thr_free_func) mdi_cancel_cleanup,
1268 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1270 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1273 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1275 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1278 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1280 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1284 * Display a simple dialog with information about this particular map tile
1286 static void maps_layer_tile_info ( VikMapsLayer *vml )
1288 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1290 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1291 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1294 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1297 gchar *filename = NULL;
1298 gchar *message = NULL;
1299 gchar *source = NULL;
1301 if ( vik_map_source_is_direct_file_access ( map ) ) {
1302 filename = g_strdup_printf ( DIRECTDIRACCESS, vml->cache_dir, ulm.scale, ulm.x, ulm.y, ".png" );
1303 source = g_strconcat ( "file://", filename, NULL );
1306 filename = g_strdup_printf ( DIRSTRUCTURE, vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale, ulm.z, ulm.x, ulm.y );
1307 source = g_strdup_printf ( "http://%s%s",
1308 vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1309 vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1312 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1314 // Get some timestamp information of the tile
1315 struct stat stat_buf;
1316 if ( g_stat ( filename, &stat_buf ) == 0 ) {
1317 time_t file_time = stat_buf.st_mtime;
1318 #if GLIB_CHECK_VERSION(2,26,0)
1319 GDateTime* gdt = g_date_time_new_from_unix_utc ( file_time );
1320 gchar *time = g_date_time_format ( gdt, "%c" );
1323 strftime(time, 20, "%Y-%m-%d %H:%M:%S", localtime(&file_time));
1325 message = g_strdup_printf ( _("\nSource: %s\n\nTile File: %s\nTile File Timestamp: %s"), source, filename, time );
1327 #if GLIB_CHECK_VERSION(2,26,0)
1329 g_date_time_unref ( gdt);
1334 message = g_strdup_printf ( _("Source: %s\n\nNo Tile File!"), source );
1337 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), message );
1341 g_free ( filename );
1344 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1346 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1348 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1350 if ( event->button == 1 )
1353 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 );
1354 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 );
1355 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1356 vml->dl_tool_x = vml->dl_tool_y = -1;
1361 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) );
1362 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) );
1364 vml->redownload_vvp = vvp;
1366 vml->dl_tool_x = vml->dl_tool_y = -1;
1368 if ( ! vml->dl_right_click_menu ) {
1370 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1372 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1373 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1374 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1376 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1377 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1378 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1380 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1381 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1382 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1384 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
1385 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1386 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
1387 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
1390 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1391 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1397 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1402 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1405 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1407 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1408 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1409 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1410 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1411 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1413 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1420 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1424 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1425 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1426 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1427 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1429 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1430 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1431 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1433 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1434 g_free ( filename_buf );
1435 vik_layer_emit_update ( VIK_LAYER(vml) );
1443 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1445 VikMapsLayer *vml = vml_vvp[0];
1446 VikViewport *vvp = vml_vvp[1];
1447 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1449 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1450 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1455 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1456 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1458 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1459 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1460 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1461 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1462 start_download_thread ( vml, vvp, &ul, &br, redownload );
1463 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1464 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1465 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1466 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1470 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1474 static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
1476 download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1479 static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
1481 download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
1484 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1486 download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1489 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1491 static gpointer pass_along[2];
1493 pass_along[0] = vml;
1494 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1496 item = gtk_menu_item_new();
1497 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1498 gtk_widget_show ( item );
1500 /* Now with icons */
1501 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
1502 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
1503 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
1504 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1505 gtk_widget_show ( item );
1507 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
1508 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
1509 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
1510 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
1511 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1512 gtk_widget_show ( item );
1515 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
1516 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
1517 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1518 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1519 gtk_widget_show ( item );
1523 * Enable downloading maps of the current screen area either 'new' or 'everything'
1525 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
1530 static gpointer pass_along[2];
1531 pass_along[0] = vml;
1532 pass_along[1] = vvp;
1535 // Get only new maps
1536 maps_layer_download_new_onscreen_maps ( pass_along );
1538 // Redownload everything
1539 maps_layer_redownload_all_onscreen_maps ( pass_along );