2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2005, Evan Battaglia <viking@greentorch.org>
5 * Copyright (C) 2010, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
6 * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
7 * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 // TODO: Make these configurable
30 #define MAX_TILES 1000
32 #define MAX_SHRINKFACTOR 8.0000001 /* zoom 1 viewing 8-tiles */
33 #define MIN_SHRINKFACTOR 0.0312499 /* zoom 32 viewing 1-tiles */
35 #define REAL_MIN_SHRINKFACTOR 0.0039062499 /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
38 #include <gdk-pixbuf/gdk-pixdata.h>
40 #include <glib/gstdio.h>
41 #include <glib/gi18n.h>
55 #include "vikmapsourcedefault.h"
58 #include "background.h"
59 #include "preferences.h"
60 #include "vikmapslayer.h"
61 #include "icons/icons.h"
63 /****** MAP TYPES ******/
65 static GList *__map_types = NULL;
67 #define NUM_MAP_TYPES g_list_length(__map_types)
69 /* List of label for each map type */
70 static gchar **params_maptypes = NULL;
72 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
73 static guint *params_maptypes_ids = NULL;
75 /******** MAPZOOMS *********/
77 static gchar *params_mapzooms[] = { N_("Use Viking Zoom Level"), "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "USGS 10k", "USGS 24k", "USGS 25k", "USGS 50k", "USGS 100k", "USGS 200k", "USGS 250k", NULL };
78 static gdouble __mapzooms_x[] = { 0.0, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 1.016, 2.4384, 2.54, 5.08, 10.16, 20.32, 25.4 };
79 static gdouble __mapzooms_y[] = { 0.0, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 1.016, 2.4384, 2.54, 5.08, 10.16, 20.32, 25.4 };
81 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
83 /**************************/
86 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
87 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
88 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
89 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
90 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
91 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
92 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
93 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
94 static void maps_layer_free ( VikMapsLayer *vml );
95 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
96 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
97 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
98 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
99 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
100 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
101 static guint map_uniq_id_to_index ( guint uniq_id );
104 static VikLayerParamScale params_scales[] = {
105 /* min, max, step, digits (decimal places) */
106 { 0, 255, 3, 0 }, /* alpha */
109 static VikLayerParamData mode_default ( void ) { return VIK_LPD_UINT ( 19 ); } // OSM MapQuest maps
110 static VikLayerParamData directory_default ( void )
112 VikLayerParamData data;
113 data.s = g_strdup ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s );
116 static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
117 static VikLayerParamData mapzoom_default ( void ) { return VIK_LPD_UINT ( 0 ); }
119 VikLayerParam maps_layer_params[] = {
120 { VIK_LAYER_MAPS, "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL, mode_default },
121 { VIK_LAYER_MAPS, "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, NULL, directory_default },
122 { VIK_LAYER_MAPS, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
123 N_("Control the Alpha value for transparency effects"), alpha_default },
124 { VIK_LAYER_MAPS, "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default },
125 { VIK_LAYER_MAPS, "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
126 N_("Using this option avoids attempting to update already acquired tiles. This can be useful if you want to restrict the network usage, without having to resort to manual control. Only applies when 'Autodownload Maps' is on."), vik_lpd_false_default },
127 { VIK_LAYER_MAPS, "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
128 N_("Determines the method of displaying map tiles for the current zoom level. 'Viking Zoom Level' uses the best matching level, otherwise setting a fixed value will always use map tiles of the specified value regardless of the actual zoom level."),
142 static VikToolInterface maps_tools[] = {
143 { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
144 (VikToolConstructorFunc) maps_layer_download_create,
148 (VikToolMouseFunc) maps_layer_download_click,
150 (VikToolMouseFunc) maps_layer_download_release,
153 GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf },
156 VikLayerInterface vik_maps_layer_interface = {
160 &vikmapslayer_pixbuf,
163 sizeof(maps_tools) / sizeof(maps_tools[0]),
172 (VikLayerFuncCreate) maps_layer_new,
173 (VikLayerFuncRealize) NULL,
174 (VikLayerFuncPostRead) maps_layer_post_read,
175 (VikLayerFuncFree) maps_layer_free,
177 (VikLayerFuncProperties) NULL,
178 (VikLayerFuncDraw) maps_layer_draw,
179 (VikLayerFuncChangeCoordMode) NULL,
181 (VikLayerFuncSetMenuItemsSelection) NULL,
182 (VikLayerFuncGetMenuItemsSelection) NULL,
184 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
185 (VikLayerFuncSublayerAddMenuItems) NULL,
187 (VikLayerFuncSublayerRenameRequest) NULL,
188 (VikLayerFuncSublayerToggleVisible) NULL,
189 (VikLayerFuncSublayerTooltip) NULL,
190 (VikLayerFuncLayerTooltip) maps_layer_tooltip,
191 (VikLayerFuncLayerSelected) NULL,
193 (VikLayerFuncMarshall) maps_layer_marshall,
194 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
196 (VikLayerFuncSetParam) maps_layer_set_param,
197 (VikLayerFuncGetParam) maps_layer_get_param,
199 (VikLayerFuncReadFileData) NULL,
200 (VikLayerFuncWriteFileData) NULL,
202 (VikLayerFuncDeleteItem) NULL,
203 (VikLayerFuncCutItem) NULL,
204 (VikLayerFuncCopyItem) NULL,
205 (VikLayerFuncPasteItem) NULL,
206 (VikLayerFuncFreeCopiedItem) NULL,
207 (VikLayerFuncDragDropRequest) NULL,
209 (VikLayerFuncSelectClick) NULL,
210 (VikLayerFuncSelectMove) NULL,
211 (VikLayerFuncSelectRelease) NULL,
212 (VikLayerFuncSelectedViewportMenu) NULL,
215 struct _VikMapsLayer {
221 gdouble xmapzoom, ymapzoom;
223 gboolean autodownload;
224 gboolean adl_only_missing;
225 VikCoord *last_center;
229 gint dl_tool_x, dl_tool_y;
231 GtkMenu *dl_right_click_menu;
232 VikCoord redownload_ul, redownload_br; /* right click menu only */
233 VikViewport *redownload_vvp;
235 gboolean license_notice_shown; // FALSE for new maps only, otherwise
236 // TRUE for saved maps & other layer changes as we don't need to show it again
239 enum { REDOWNLOAD_NONE = 0, /* download only missing maps */
240 REDOWNLOAD_BAD, /* download missing and bad maps */
241 REDOWNLOAD_NEW, /* download missing maps that are newer on server only */
242 REDOWNLOAD_ALL, /* download all maps */
243 DOWNLOAD_OR_REFRESH }; /* download missing maps and refresh cache */
245 static VikLayerParam prefs[] = {
246 { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Default map layer directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, N_("Choose a directory to store cached Map tiles for this layer") },
249 void maps_layer_init ()
251 VikLayerParamData tmp;
252 tmp.s = maps_layer_default_dir();
253 a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
256 /****************************************/
257 /******** MAPS LAYER TYPES **************/
258 /****************************************/
260 int _get_index_for_id ( guint id )
263 while (params_maptypes_ids[index] != 0)
265 if (params_maptypes_ids[index] == id)
272 void _add_map_source ( guint id, const char *label, VikMapSource *map )
276 len = g_strv_length (params_maptypes);
278 params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
279 params_maptypes[len] = g_strdup (label);
280 params_maptypes[len+1] = NULL;
283 params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
284 params_maptypes_ids[len] = id;
285 params_maptypes_ids[len+1] = 0;
287 /* We have to clone */
288 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
289 /* Register the clone in the list */
290 __map_types = g_list_append(__map_types, clone);
293 We have to ensure the mode LayerParam references the up-to-date
297 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
298 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
300 maps_layer_params[0].widget_data = params_maptypes;
301 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
304 void _update_map_source ( const char *label, VikMapSource *map, int index )
306 GList *item = g_list_nth (__map_types, index);
307 g_object_unref (item->data);
308 item->data = g_object_ref (map);
309 /* Change previous data */
310 g_free (params_maptypes[index]);
311 params_maptypes[index] = g_strdup (label);
315 * maps_layer_register_map_source:
316 * @map: the new VikMapSource
318 * Register a new VikMapSource.
319 * Override existing one (equality of id).
321 void maps_layer_register_map_source ( VikMapSource *map )
323 g_assert(map != NULL);
325 guint id = vik_map_source_get_uniq_id(map);
326 const char *label = vik_map_source_get_label(map);
327 g_assert(label != NULL);
329 int previous = map_uniq_id_to_index (id);
330 if (previous != NUM_MAP_TYPES)
332 _update_map_source (label, map, previous);
336 _add_map_source (id, label, map);
340 #define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
341 #define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
342 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
344 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
346 return(vml->maptype);
349 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
351 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
354 /****************************************/
355 /******** CACHE DIR STUFF ***************/
356 /****************************************/
358 #define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
359 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
360 #define MAPS_CACHE_DIR maps_layer_default_dir()
364 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
365 #define LOCAL_MAPS_DIR "VIKING-MAPS"
366 #elif defined __APPLE__
368 #define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
369 #define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
372 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
373 #define LOCAL_MAPS_DIR ".viking-maps"
376 gchar *maps_layer_default_dir ()
378 static gchar *defaultdir = NULL;
381 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
382 const gchar *mapdir = g_getenv("VIKING_MAPS");
384 defaultdir = g_strdup ( mapdir );
385 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
386 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
388 const gchar *home = g_get_home_dir();
389 if (!home || g_access(home, W_OK))
390 home = g_get_home_dir ();
392 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
394 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
396 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
398 /* Add the separator at the end */
399 gchar *tmp = defaultdir;
400 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
403 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
408 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
410 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
412 g_mkdir ( vml->cache_dir, 0777 );
416 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
419 g_assert ( vml != NULL);
420 g_free ( vml->cache_dir );
421 vml->cache_dir = NULL;
423 if ( dir == NULL || dir[0] == '\0' )
425 if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
426 vml->cache_dir = g_strdup ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s );
431 if ( dir[len-1] != G_DIR_SEPARATOR )
433 vml->cache_dir = g_malloc ( len+2 );
434 strncpy ( vml->cache_dir, dir, len );
435 vml->cache_dir[len] = G_DIR_SEPARATOR;
436 vml->cache_dir[len+1] = '\0';
439 vml->cache_dir = g_strdup ( dir );
441 maps_layer_mkdir_if_default_dir ( vml );
444 /****************************************/
445 /******** GOBJECT STUFF *****************/
446 /****************************************/
448 GType vik_maps_layer_get_type ()
450 static GType vml_type = 0;
454 static const GTypeInfo vml_info =
456 sizeof (VikMapsLayerClass),
457 NULL, /* base_init */
458 NULL, /* base_finalize */
459 NULL, /* class init */
460 NULL, /* class_finalize */
461 NULL, /* class_data */
462 sizeof (VikMapsLayer),
464 NULL /* instance init */
466 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
472 /****************************************/
473 /************** PARAMETERS **************/
474 /****************************************/
476 static guint map_index_to_uniq_id (guint8 index)
478 g_assert ( index < NUM_MAP_TYPES );
479 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
482 static guint map_uniq_id_to_index ( guint uniq_id )
485 for ( i = 0; i < NUM_MAP_TYPES; i++ )
486 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
488 return NUM_MAP_TYPES; /* no such thing */
491 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
493 // When loading from a file don't need the license reminder
494 if ( is_file_operation )
495 vml->license_notice_shown = TRUE;
499 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
500 case PARAM_MAPTYPE: {
501 gint maptype = map_uniq_id_to_index(data.u);
502 if ( maptype == NUM_MAP_TYPES ) g_warning(_("Unknown map type"));
503 else vml->maptype = maptype;
506 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
507 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
508 case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
509 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
510 vml->mapzoom_id = data.u;
511 vml->xmapzoom = __mapzooms_x [data.u];
512 vml->ymapzoom = __mapzooms_y [data.u];
513 }else g_warning (_("Unknown Map Zoom")); break;
518 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
520 VikLayerParamData rv;
523 case PARAM_CACHE_DIR:
524 /* Only save a blank when the map cache location equals the default
525 On reading in, when it is blank then the default is reconstructed
526 Since the default changes dependent on the user and OS, it means the resultant file is more portable */
527 if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 )
530 rv.s = vml->cache_dir ? vml->cache_dir : "";
532 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
533 case PARAM_ALPHA: rv.u = vml->alpha; break;
534 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
535 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
536 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
541 /****************************************/
542 /****** CREATING, COPYING, FREEING ******/
543 /****************************************/
545 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
547 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
548 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
550 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
552 vml->dl_tool_x = vml->dl_tool_y = -1;
553 vml->last_center = NULL;
554 vml->last_xmpp = 0.0;
555 vml->last_ympp = 0.0;
557 vml->dl_right_click_menu = NULL;
558 vml->license_notice_shown = FALSE;
563 static void maps_layer_free ( VikMapsLayer *vml )
565 g_free ( vml->cache_dir );
566 vml->cache_dir = NULL;
567 if ( vml->dl_right_click_menu )
568 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
569 g_free(vml->last_center);
570 vml->last_center = NULL;
573 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
575 if (from_file != TRUE)
577 /* If this method is not called in file reading context
578 * it is called in GUI context.
579 * So, we can check if we have to inform the user about inconsistency */
580 VikViewportDrawMode vp_drawmode;
581 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
582 VikMapSource *map = NULL;
584 vp_drawmode = vik_viewport_get_drawmode ( vp );
585 map = MAPS_LAYER_NTH_TYPE(vml->maptype);
586 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
587 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
588 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
589 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
593 if (vik_map_source_get_license (map) != NULL) {
594 if ( ! vml->license_notice_shown ) {
595 a_dialog_license (VIK_GTK_WINDOW_FROM_WIDGET(vp), vik_map_source_get_label (map),
596 vik_map_source_get_license (map), vik_map_source_get_license_url (map) );
597 vml->license_notice_shown = TRUE;
603 static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
605 return vik_maps_layer_get_map_label ( vml );
608 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
610 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
613 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
615 VikMapsLayer *rv = maps_layer_new ( vvp );
616 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
620 /*********************/
621 /****** DRAWING ******/
622 /*********************/
624 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
627 gint width, height, iii, jjj;
629 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
631 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
632 g_object_unref(G_OBJECT(pixbuf));
636 pixels = gdk_pixbuf_get_pixels(pixbuf);
637 width = gdk_pixbuf_get_width(pixbuf);
638 height = gdk_pixbuf_get_height(pixbuf);
640 /* r,g,b,a,r,g,b,a.... */
641 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
649 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
652 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
653 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
654 g_object_unref ( G_OBJECT(pixbuf) );
658 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
663 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
664 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
667 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
668 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS,
669 vml->cache_dir, (17 - mapcoord->scale), mapcoord->x, mapcoord->y, ".png" );
671 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
672 vml->cache_dir, mode,
673 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
675 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
678 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
680 /* free the pixbuf on error */
683 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
684 g_warning ( _("Couldn't open image file: %s"), gx->message );
688 g_object_unref ( G_OBJECT(pixbuf) );
691 if ( vml->alpha < 255 )
692 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
693 if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
694 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
696 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
697 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
698 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
705 static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
707 const VikCoord *center = vik_viewport_get_center ( vvp );
709 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
710 /* D'n'D pan in action: do not download */
714 // Prevent requests for downloading tiles at Zoom Level 19 and above for most map types
715 // Allow MapQuest Zoom Level up to 19
716 // TODO: This should be made a property of the map source and then use that value
717 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
718 if ( (vml->maptype != 19 && map_utils_mpp_to_scale (xzoom) < -1) || (vml->maptype == 19 && map_utils_mpp_to_scale (xzoom) < -2) )
721 if (vml->last_center == NULL) {
722 VikCoord *new_center = g_malloc(sizeof(VikCoord));
723 *new_center = *center;
724 vml->last_center = new_center;
725 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
726 vml->last_ympp = vik_viewport_get_ympp(vvp);
730 /* TODO: perhaps vik_coord_diff() */
731 if (vik_coord_equals(vml->last_center, center)
732 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
733 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
736 *(vml->last_center) = *center;
737 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
738 vml->last_ympp = vik_viewport_get_ympp(vvp);
742 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
745 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
746 gdouble yzoom = vik_viewport_get_ympp ( vvp );
747 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
748 gboolean existence_only = FALSE;
750 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
751 xshrinkfactor = vml->xmapzoom / xzoom;
752 yshrinkfactor = vml->ymapzoom / yzoom;
753 xzoom = vml->xmapzoom;
754 yzoom = vml->xmapzoom;
755 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
756 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
757 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
758 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
759 existence_only = TRUE;
762 g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
769 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
770 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
771 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
775 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
776 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
777 gint mode = vik_map_source_get_uniq_id(map);
780 gint xx, yy, width, height;
783 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
784 // which can happen when using a small fixed zoom level and viewing large areas.
785 // Also prevents very large number of tile download requests
786 gint tiles = (xmax-xmin) * (ymax-ymin);
787 if ( tiles > MAX_TILES ) {
788 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
789 existence_only = TRUE;
792 guint max_path_len = strlen(vml->cache_dir) + 40;
793 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
795 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
796 g_debug("%s: Starting autodownload", __FUNCTION__);
797 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
798 // Try to download newer tiles
799 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
801 // Download only missing tiles
802 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
805 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
806 for ( x = xmin; x <= xmax; x++ ) {
807 for ( y = ymin; y <= ymax; y++ ) {
810 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
812 width = gdk_pixbuf_get_width ( pixbuf );
813 height = gdk_pixbuf_get_height ( pixbuf );
815 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
816 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
820 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
824 } else { /* tilesize is known, don't have to keep converting coords */
825 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
826 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
827 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
828 gint tilesize_x_ceil = ceil ( tilesize_x );
829 gint tilesize_y_ceil = ceil ( tilesize_y );
830 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
831 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
832 gdouble xx, yy; gint xx_tmp, yy_tmp;
833 gint base_yy, xend, yend;
835 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
836 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
838 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
839 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
840 xx = xx_tmp; yy = yy_tmp;
841 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
842 * eg if tile size 128, shrinkfactor 0.333 */
843 xx -= (tilesize_x/2);
844 base_yy = yy - (tilesize_y/2);
846 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
848 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
852 if ( existence_only ) {
853 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
854 g_snprintf ( path_buf, max_path_len, DIRECTDIRACCESS,
855 vml->cache_dir, (17 - ulm.scale), ulm.x, ulm.y, ".png" );
857 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
858 vml->cache_dir, mode,
859 ulm.scale, ulm.z, ulm.x, ulm.y );
860 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
861 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
862 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
866 for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
867 /* try with correct then smaller zooms */
868 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
870 ulm2.x = ulm.x / scale_factor;
871 ulm2.y = ulm.y / scale_factor;
872 ulm2.scale = ulm.scale + scale_inc;
873 pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
875 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
876 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
878 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);
880 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
885 /* retry with bigger zooms */
887 for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
889 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
891 ulm2.x = ulm.x * scale_factor;
892 ulm2.y = ulm.y * scale_factor;
893 ulm2.scale = ulm.scale - scale_dec;
894 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
895 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
896 MapCoord ulm3 = ulm2;
899 pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
903 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
904 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
905 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
923 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
925 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
930 gdouble level = vik_viewport_get_zoom ( vvp );
932 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
933 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
936 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
937 vik_viewport_add_logo ( vvp, logo );
939 /* get corner coords */
940 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
941 /* UTM multi-zone stuff by Kit Transue */
942 gchar leftmost_zone, rightmost_zone, i;
943 leftmost_zone = vik_viewport_leftmost_zone( vvp );
944 rightmost_zone = vik_viewport_rightmost_zone( vvp );
945 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
946 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
947 maps_layer_draw_section ( vml, vvp, &ul, &br );
951 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
952 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
954 maps_layer_draw_section ( vml, vvp, &ul, &br );
959 /*************************/
960 /****** DOWNLOADING ******/
961 /*************************/
963 /* pass along data to thread, exists even if layer is deleted. */
973 gboolean refresh_display;
976 gboolean map_layer_alive;
980 static void mdi_free ( MapDownloadInfo *mdi )
982 g_mutex_free(mdi->mutex);
983 g_free ( mdi->cache_dir );
984 mdi->cache_dir = NULL;
985 g_free ( mdi->filename_buf );
986 mdi->filename_buf = NULL;
990 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
992 MapDownloadInfo *mdi = ptr;
993 g_mutex_lock(mdi->mutex);
994 mdi->map_layer_alive = FALSE;
995 g_mutex_unlock(mdi->mutex);
998 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
1000 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
1003 for ( x = mdi->x0; x <= mdi->xf; x++ )
1005 for ( y = mdi->y0; y <= mdi->yf; y++ )
1007 gboolean remove_mem_cache = FALSE;
1008 gboolean need_download = FALSE;
1009 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1010 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1011 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
1014 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
1016 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1020 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1021 need_download = TRUE;
1022 remove_mem_cache = TRUE;
1024 } else { /* in case map file already exists */
1025 switch (mdi->redownload) {
1026 case REDOWNLOAD_NONE:
1029 case REDOWNLOAD_BAD:
1031 /* see if this one is bad or what */
1033 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1034 if (gx || (!pixbuf)) {
1035 g_remove ( mdi->filename_buf );
1036 need_download = TRUE;
1037 remove_mem_cache = TRUE;
1038 g_error_free ( gx );
1041 g_object_unref ( pixbuf );
1046 case REDOWNLOAD_NEW:
1047 need_download = TRUE;
1048 remove_mem_cache = TRUE;
1051 case REDOWNLOAD_ALL:
1052 /* FIXME: need a better way than to erase file in case of server/network problem */
1053 g_remove ( mdi->filename_buf );
1054 need_download = TRUE;
1055 remove_mem_cache = TRUE;
1058 case DOWNLOAD_OR_REFRESH:
1059 remove_mem_cache = TRUE;
1063 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1067 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
1069 if (need_download) {
1070 if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
1074 g_mutex_lock(mdi->mutex);
1075 if (remove_mem_cache)
1076 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 );
1077 if (mdi->refresh_display && mdi->map_layer_alive) {
1078 /* TODO: check if it's on visible area */
1079 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
1081 g_mutex_unlock(mdi->mutex);
1082 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1086 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
1087 g_mutex_lock(mdi->mutex);
1088 if (mdi->map_layer_alive)
1089 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1090 g_mutex_unlock(mdi->mutex);
1094 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1096 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1098 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1099 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
1100 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
1101 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
1103 g_remove ( mdi->filename_buf );
1108 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1110 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1111 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1113 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1115 // Don't ever attempt download on direct access
1116 if ( vik_map_source_is_direct_file_access ( map ) )
1119 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1120 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
1122 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1127 mdi->map_layer_alive = TRUE;
1128 mdi->mutex = g_mutex_new();
1129 mdi->refresh_display = TRUE;
1131 /* cache_dir and buffer for dest filename */
1132 mdi->cache_dir = g_strdup ( vml->cache_dir );
1133 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1134 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1135 mdi->maptype = vml->maptype;
1137 mdi->mapcoord = ulm;
1139 mdi->redownload = redownload;
1141 mdi->x0 = MIN(ulm.x, brm.x);
1142 mdi->xf = MAX(ulm.x, brm.x);
1143 mdi->y0 = MIN(ulm.y, brm.y);
1144 mdi->yf = MAX(ulm.y, brm.y);
1148 if ( mdi->redownload ) {
1149 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1151 /* calculate how many we need */
1152 for ( a = mdi->x0; a <= mdi->xf; a++ )
1154 for ( b = mdi->y0; b <= mdi->yf; b++ )
1156 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1157 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1159 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1165 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1167 if ( mdi->mapstoget )
1169 const gchar *tmp_str;
1174 if (redownload == REDOWNLOAD_BAD)
1175 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1177 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1181 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1183 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1185 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1186 /* launch the thread */
1187 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1188 tmp, /* description string */
1189 (vik_thr_func) map_download_thread, /* function to call within thread */
1190 mdi, /* pass along data */
1191 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1192 (vik_thr_free_func) mdi_cancel_cleanup,
1201 void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
1204 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1206 // Don't ever attempt download on direct access
1207 if ( vik_map_source_is_direct_file_access ( map ) )
1210 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1211 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1212 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1216 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1221 mdi->map_layer_alive = TRUE;
1222 mdi->mutex = g_mutex_new();
1223 mdi->refresh_display = TRUE;
1225 mdi->cache_dir = g_strdup ( vml->cache_dir );
1226 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1227 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1228 mdi->maptype = vml->maptype;
1230 mdi->mapcoord = ulm;
1232 mdi->redownload = REDOWNLOAD_NONE;
1234 mdi->x0 = MIN(ulm.x, brm.x);
1235 mdi->xf = MAX(ulm.x, brm.x);
1236 mdi->y0 = MIN(ulm.y, brm.y);
1237 mdi->yf = MAX(ulm.y, brm.y);
1241 for (i = mdi->x0; i <= mdi->xf; i++) {
1242 for (j = mdi->y0; j <= mdi->yf; j++) {
1243 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1244 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1246 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1251 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1253 if (mdi->mapstoget) {
1256 fmt = ngettext("Downloading %d %s map...",
1257 "Downloading %d %s maps...",
1259 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1261 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1262 /* launch the thread */
1263 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1264 tmp, /* description string */
1265 (vik_thr_func) map_download_thread, /* function to call within thread */
1266 mdi, /* pass along data */
1267 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1268 (vik_thr_free_func) mdi_cancel_cleanup,
1276 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1278 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1281 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1283 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1286 static void maps_layer_redownload_new ( VikMapsLayer *vml )
1288 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1292 * Display a simple dialog with information about this particular map tile
1294 static void maps_layer_tile_info ( VikMapsLayer *vml )
1296 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1298 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1299 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1302 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1305 gchar *filename = NULL;
1306 gchar *message = NULL;
1307 gchar *source = NULL;
1309 if ( vik_map_source_is_direct_file_access ( map ) ) {
1310 filename = g_strdup_printf ( DIRECTDIRACCESS, vml->cache_dir, ulm.scale, ulm.x, ulm.y, ".png" );
1311 source = g_strconcat ( "file://", filename, NULL );
1314 filename = g_strdup_printf ( DIRSTRUCTURE, vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale, ulm.z, ulm.x, ulm.y );
1315 source = g_strdup_printf ( "http://%s%s",
1316 vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1317 vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1320 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1322 // Get some timestamp information of the tile
1323 struct stat stat_buf;
1324 if ( g_stat ( filename, &stat_buf ) == 0 ) {
1325 time_t file_time = stat_buf.st_mtime;
1326 #if GLIB_CHECK_VERSION(2,26,0)
1327 GDateTime* gdt = g_date_time_new_from_unix_utc ( file_time );
1328 gchar *time = g_date_time_format ( gdt, "%c" );
1330 GDate* gdate = g_date_new ();
1331 g_date_set_time_t ( gdate, file_time );
1333 g_date_strftime ( time, sizeof(time), "%c", gdate );
1334 g_date_free ( gdate );
1336 message = g_strdup_printf ( _("\nSource: %s\n\nTile File: %s\nTile File Timestamp: %s"), source, filename, time );
1338 #if GLIB_CHECK_VERSION(2,26,0)
1340 g_date_time_unref ( gdt);
1345 message = g_strdup_printf ( _("Source: %s\n\nNo Tile File!"), source );
1348 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), message );
1352 g_free ( filename );
1355 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1357 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1359 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1361 if ( event->button == 1 )
1364 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 );
1365 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 );
1366 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1367 vml->dl_tool_x = vml->dl_tool_y = -1;
1372 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) );
1373 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) );
1375 vml->redownload_vvp = vvp;
1377 vml->dl_tool_x = vml->dl_tool_y = -1;
1379 if ( ! vml->dl_right_click_menu ) {
1381 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1383 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
1384 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1385 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1387 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
1388 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1389 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1391 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
1392 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1393 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1395 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
1396 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1397 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
1398 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
1401 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1402 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1408 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1413 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1416 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1418 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1419 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1420 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1421 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1422 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1424 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1431 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1435 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1436 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1437 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1438 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1440 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1441 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1442 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1444 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1445 g_free ( filename_buf );
1446 vik_layer_emit_update ( VIK_LAYER(vml) );
1454 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1456 VikMapsLayer *vml = vml_vvp[0];
1457 VikViewport *vvp = vml_vvp[1];
1458 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1460 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1461 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1466 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1467 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1469 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1470 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1471 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1472 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1473 start_download_thread ( vml, vvp, &ul, &br, redownload );
1474 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1475 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1476 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1477 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1481 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1485 static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
1487 download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1490 static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
1492 download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
1495 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1497 download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1500 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1502 static gpointer pass_along[2];
1504 pass_along[0] = vml;
1505 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1507 item = gtk_menu_item_new();
1508 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1509 gtk_widget_show ( item );
1511 /* Now with icons */
1512 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
1513 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
1514 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
1515 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1516 gtk_widget_show ( item );
1518 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
1519 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
1520 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
1521 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
1522 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1523 gtk_widget_show ( item );
1526 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
1527 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
1528 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1529 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1530 gtk_widget_show ( item );
1534 * Enable downloading maps of the current screen area either 'new' or 'everything'
1536 void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
1541 static gpointer pass_along[2];
1542 pass_along[0] = vml;
1543 pass_along[1] = vvp;
1546 // Get only new maps
1547 maps_layer_download_new_onscreen_maps ( pass_along );
1549 // Redownload everything
1550 maps_layer_redownload_all_onscreen_maps ( pass_along );