2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2005, Evan Battaglia <viking@greentorch.org>
5 * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
6 * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #define MAX_SHRINKFACTOR 8.0000001 /* zoom 1 viewing 8-tiles */
29 #define MIN_SHRINKFACTOR 0.0312499 /* zoom 32 viewing 1-tiles */
31 #define REAL_MIN_SHRINKFACTOR 0.0039062499 /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
34 #include <gdk-pixbuf/gdk-pixdata.h>
36 #include <glib/gstdio.h>
37 #include <glib/gi18n.h>
49 #include "viktreeview.h"
50 #include "vikviewport.h"
52 #include "vikmapslayer.h"
59 /* only for dialog.h -- ugh */
60 #include "vikwaypoint.h"
63 #include "vikstatus.h"
64 #include "background.h"
66 #include "vikaggregatelayer.h"
67 #include "viklayerspanel.h"
70 #include "terraserver.h"
72 #include "icons/icons.h"
74 /****** MAP TYPES ******/
76 static GList *__map_types = NULL;
78 #define NUM_MAP_TYPES g_list_length(__map_types)
80 /* List of label for each map type */
81 static GList *params_maptypes = NULL;
83 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
84 static GList *params_maptypes_ids = NULL;
86 /******** MAPZOOMS *********/
88 static gchar *params_mapzooms[] = { N_("Use Viking Zoom Level"), "0.25", "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 };
89 static gdouble __mapzooms_x[] = { 0.0, 0.25, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 1.016, 2.4384, 2.54, 5.08, 10.16, 20.32, 25.4 };
90 static gdouble __mapzooms_y[] = { 0.0, 0.25, 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 };
92 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
94 /**************************/
97 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
98 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
99 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
100 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp );
101 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id );
102 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
103 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
104 static void maps_layer_free ( VikMapsLayer *vml );
105 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
106 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
107 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
108 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
109 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
110 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
113 static VikLayerParamScale params_scales[] = {
114 /* min, max, step, digits (decimal places) */
115 { 0, 255, 3, 0 }, /* alpha */
118 VikLayerParam maps_layer_params[] = {
119 { "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL, NULL },
120 { "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY },
121 { "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales },
122 { "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload maps:"), VIK_LAYER_WIDGET_CHECKBUTTON },
123 { "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms },
126 enum { PARAM_MAPTYPE=0, PARAM_CACHE_DIR, PARAM_ALPHA, PARAM_AUTODOWNLOAD, PARAM_MAPZOOM, NUM_PARAMS };
128 static VikToolInterface maps_tools[] = {
129 { N_("Maps Download"), (VikToolConstructorFunc) maps_layer_download_create, NULL, NULL, NULL,
130 (VikToolMouseFunc) maps_layer_download_click, NULL, (VikToolMouseFunc) maps_layer_download_release,
131 (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf },
134 VikLayerInterface vik_maps_layer_interface = {
136 &vikmapslayer_pixbuf,
139 sizeof(maps_tools) / sizeof(maps_tools[0]),
148 (VikLayerFuncCreate) maps_layer_new,
149 (VikLayerFuncRealize) NULL,
150 (VikLayerFuncPostRead) maps_layer_post_read,
151 (VikLayerFuncFree) maps_layer_free,
153 (VikLayerFuncProperties) NULL,
154 (VikLayerFuncDraw) maps_layer_draw,
155 (VikLayerFuncChangeCoordMode) NULL,
157 (VikLayerFuncSetMenuItemsSelection) NULL,
158 (VikLayerFuncGetMenuItemsSelection) NULL,
160 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
161 (VikLayerFuncSublayerAddMenuItems) NULL,
163 (VikLayerFuncSublayerRenameRequest) NULL,
164 (VikLayerFuncSublayerToggleVisible) NULL,
166 (VikLayerFuncMarshall) maps_layer_marshall,
167 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
169 (VikLayerFuncSetParam) maps_layer_set_param,
170 (VikLayerFuncGetParam) maps_layer_get_param,
172 (VikLayerFuncReadFileData) NULL,
173 (VikLayerFuncWriteFileData) NULL,
175 (VikLayerFuncDeleteItem) NULL,
176 (VikLayerFuncCopyItem) NULL,
177 (VikLayerFuncPasteItem) NULL,
178 (VikLayerFuncFreeCopiedItem) NULL,
179 (VikLayerFuncDragDropRequest) NULL,
182 struct _VikMapsLayer {
188 gdouble xmapzoom, ymapzoom;
190 gboolean autodownload;
191 VikCoord *last_center;
195 gint dl_tool_x, dl_tool_y;
197 GtkMenu *dl_right_click_menu;
198 VikCoord redownload_ul, redownload_br; /* right click menu only */
199 VikViewport *redownload_vvp;
202 enum { REDOWNLOAD_NONE = 0, REDOWNLOAD_BAD, REDOWNLOAD_ALL, DOWNLOAD_OR_REFRESH };
205 /****************************************/
206 /******** MAPS LAYER TYPES **************/
207 /****************************************/
209 void maps_layer_register_map_source ( VikMapSource *map )
211 g_assert(map != NULL);
213 guint id = vik_map_source_get_uniq_id(map);
214 const char *label = vik_map_source_get_label(map);
215 g_assert(label != NULL);
218 params_maptypes = g_list_append(params_maptypes, g_strdup(label));
221 params_maptypes_ids = g_list_append(params_maptypes_ids, GUINT_TO_POINTER (id));
223 /* We have to clone */
224 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
225 /* Register the clone in the list */
226 __map_types = g_list_append(__map_types, clone);
229 We have to ensure the mode LayerParam reference the up-to-date
233 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
234 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
236 maps_layer_params[0].widget_data = params_maptypes;
237 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
240 #define MAPS_LAYER_NTH_LABEL(n) ((gchar*)g_list_nth_data(params_maptypes, (n)))
241 #define MAPS_LAYER_NTH_ID(n) ((guint)g_list_nth_data(params_maptypes_ids, (n)))
242 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
244 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
246 return(vml->maptype);
249 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
251 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
254 /****************************************/
255 /******** CACHE DIR STUFF ***************/
256 /****************************************/
258 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
259 #define MAPS_CACHE_DIR maps_layer_default_dir()
263 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
264 #define LOCAL_MAPS_DIR "VIKING-MAPS"
267 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
268 #define LOCAL_MAPS_DIR ".viking-maps"
271 gchar *maps_layer_default_dir ()
273 static gchar *defaultdir = NULL;
276 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
277 const gchar *mapdir = g_getenv("VIKING_MAPS");
279 defaultdir = g_strdup ( mapdir );
280 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
281 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
283 const gchar *home = g_get_home_dir();
284 if (!home || g_access(home, W_OK))
285 home = g_get_home_dir ();
287 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
289 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
291 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
293 /* Add the separator at the end */
294 gchar *tmp = defaultdir;
295 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
298 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
303 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
305 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
307 g_mkdir ( vml->cache_dir, 0777 );
311 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
314 g_assert ( vml != NULL);
315 g_free ( vml->cache_dir );
316 vml->cache_dir = NULL;
318 if ( dir == NULL || dir[0] == '\0' )
319 vml->cache_dir = g_strdup ( MAPS_CACHE_DIR );
323 if ( dir[len-1] != G_DIR_SEPARATOR )
325 vml->cache_dir = g_malloc ( len+2 );
326 strncpy ( vml->cache_dir, dir, len );
327 vml->cache_dir[len] = G_DIR_SEPARATOR;
328 vml->cache_dir[len+1] = '\0';
331 vml->cache_dir = g_strdup ( dir );
333 maps_layer_mkdir_if_default_dir ( vml );
336 /****************************************/
337 /******** GOBJECT STUFF *****************/
338 /****************************************/
340 GType vik_maps_layer_get_type ()
342 static GType vml_type = 0;
346 static const GTypeInfo vml_info =
348 sizeof (VikMapsLayerClass),
349 NULL, /* base_init */
350 NULL, /* base_finalize */
351 NULL, /* class init */
352 NULL, /* class_finalize */
353 NULL, /* class_data */
354 sizeof (VikMapsLayer),
356 NULL /* instance init */
358 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
364 /****************************************/
365 /************** PARAMETERS **************/
366 /****************************************/
368 static guint map_index_to_uniq_id (guint8 index)
370 g_assert ( index < NUM_MAP_TYPES );
371 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
374 static guint map_uniq_id_to_index ( guint uniq_id )
377 for ( i = 0; i < NUM_MAP_TYPES; i++ )
378 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
380 return NUM_MAP_TYPES; /* no such thing */
383 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp )
387 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
388 case PARAM_MAPTYPE: {
389 gint maptype = map_uniq_id_to_index(data.u);
390 if ( maptype == NUM_MAP_TYPES ) g_warning(_("Unknown map type"));
391 else vml->maptype = maptype;
394 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
395 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
396 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
397 vml->mapzoom_id = data.u;
398 vml->xmapzoom = __mapzooms_x [data.u];
399 vml->ymapzoom = __mapzooms_y [data.u];
400 }else g_warning (_("Unknown Map Zoom")); break;
405 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id )
407 VikLayerParamData rv;
410 case PARAM_CACHE_DIR: rv.s = vml->cache_dir ? vml->cache_dir : ""; break;
411 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
412 case PARAM_ALPHA: rv.u = vml->alpha; break;
413 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
414 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
419 /****************************************/
420 /****** CREATING, COPYING, FREEING ******/
421 /****************************************/
423 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
426 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
427 vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS );
428 idx = map_uniq_id_to_index(7); /* 7 is id for google maps */
429 vml->maptype = (idx < NUM_MAP_TYPES) ? idx : 0;
432 vml->dl_tool_x = vml->dl_tool_y = -1;
433 maps_layer_set_cache_dir ( vml, NULL );
434 vml->autodownload = FALSE;
435 vml->last_center = NULL;
436 vml->last_xmpp = 0.0;
437 vml->last_ympp = 0.0;
439 vml->dl_right_click_menu = NULL;
444 static void maps_layer_free ( VikMapsLayer *vml )
446 g_free ( vml->cache_dir );
447 vml->cache_dir = NULL;
448 if ( vml->dl_right_click_menu )
449 gtk_object_sink ( GTK_OBJECT(vml->dl_right_click_menu) );
450 g_free(vml->last_center);
451 vml->last_center = NULL;
454 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
456 if (from_file != TRUE)
458 /* If this method is not called in file reading context
459 * it is called in GUI context.
460 * So, we can check if we have to inform the user about inconsistency */
461 VikViewportDrawMode vp_drawmode;
462 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
463 VikMapSource *map = NULL;
465 vp_drawmode = vik_viewport_get_drawmode ( VIK_VIEWPORT(vp) );
466 map = MAPS_LAYER_NTH_TYPE(vml->maptype);
467 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
468 const gchar *drawmode_name = vik_viewport_get_drawmode_name (VIK_VIEWPORT(vp), vik_map_source_get_drawmode(map));
469 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
470 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), msg );
476 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
478 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
481 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
483 VikMapsLayer *rv = maps_layer_new ( vvp );
484 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
488 /*********************/
489 /****** DRAWING ******/
490 /*********************/
492 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
495 gint width, height, iii, jjj;
497 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
499 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
500 g_object_unref(G_OBJECT(pixbuf));
504 pixels = gdk_pixbuf_get_pixels(pixbuf);
505 width = gdk_pixbuf_get_width(pixbuf);
506 height = gdk_pixbuf_get_height(pixbuf);
508 /* r,g,b,a,r,g,b,a.... */
509 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
517 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
520 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
521 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
522 g_object_unref ( G_OBJECT(pixbuf) );
526 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
531 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
532 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
535 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
536 vml->cache_dir, mode,
537 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
538 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE) {
541 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
545 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
546 g_warning ( _("Couldn't open image file: %s"), gx->message );
550 g_object_unref ( G_OBJECT(pixbuf) );
553 if ( vml->alpha < 255 )
554 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
555 if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
556 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
558 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
559 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
560 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
568 gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
570 const VikCoord *center = vik_viewport_get_center ( vvp );
572 if (vml->last_center == NULL) {
573 VikCoord *new_center = g_malloc(sizeof(VikCoord));
574 *new_center = *center;
575 vml->last_center = new_center;
576 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
577 vml->last_ympp = vik_viewport_get_ympp(vvp);
581 /* TODO: perhaps vik_coord_diff() */
582 if (vik_coord_equals(vml->last_center, center)
583 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
584 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
587 *(vml->last_center) = *center;
588 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
589 vml->last_ympp = vik_viewport_get_ympp(vvp);
593 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
596 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
597 gdouble yzoom = vik_viewport_get_ympp ( vvp );
598 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
599 gdouble existence_only = FALSE;
601 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
602 xshrinkfactor = vml->xmapzoom / xzoom;
603 yshrinkfactor = vml->ymapzoom / yzoom;
604 xzoom = vml->xmapzoom;
605 yzoom = vml->xmapzoom;
606 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
607 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
608 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR )
609 existence_only = TRUE;
611 g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
618 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
619 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
620 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
624 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
625 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
626 gint mode = vik_map_source_get_uniq_id(map);
629 gint xx, yy, width, height;
632 guint max_path_len = strlen(vml->cache_dir) + 40;
633 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
635 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
637 fputs(stderr, "DEBUG: Starting autodownload\n");
639 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
642 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
643 for ( x = xmin; x <= xmax; x++ ) {
644 for ( y = ymin; y <= ymax; y++ ) {
647 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
649 width = gdk_pixbuf_get_width ( pixbuf );
650 height = gdk_pixbuf_get_height ( pixbuf );
652 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
653 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
657 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
661 } else { /* tilesize is known, don't have to keep converting coords */
662 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
663 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
664 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
665 gint tilesize_x_ceil = ceil ( tilesize_x );
666 gint tilesize_y_ceil = ceil ( tilesize_y );
667 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
668 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
669 gdouble xx, yy; gint xx_tmp, yy_tmp;
670 gint base_yy, xend, yend;
672 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
674 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
675 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
677 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
678 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
679 xx = xx_tmp; yy = yy_tmp;
680 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
681 * eg if tile size 128, shrinkfactor 0.333 */
682 xx -= (tilesize_x/2);
683 base_yy = yy - (tilesize_y/2);
685 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
687 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
691 if ( existence_only ) {
692 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
693 vml->cache_dir, mode,
694 ulm.scale, ulm.z, ulm.x, ulm.y );
695 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
696 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
699 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
701 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
714 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
716 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
720 /* get corner coords */
721 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
722 /* UTM multi-zone stuff by Kit Transue */
723 gchar leftmost_zone, rightmost_zone, i;
724 leftmost_zone = vik_viewport_leftmost_zone( vvp );
725 rightmost_zone = vik_viewport_rightmost_zone( vvp );
726 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
727 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
728 maps_layer_draw_section ( vml, vvp, &ul, &br );
732 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
733 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
735 maps_layer_draw_section ( vml, vvp, &ul, &br );
740 /*************************/
741 /****** DOWNLOADING ******/
742 /*************************/
744 /* pass along data to thread, exists even if layer is deleted. */
754 gboolean refresh_display;
757 gboolean map_layer_alive;
761 static void mdi_free ( MapDownloadInfo *mdi )
763 g_mutex_free(mdi->mutex);
764 g_free ( mdi->cache_dir );
765 mdi->cache_dir = NULL;
766 g_free ( mdi->filename_buf );
767 mdi->filename_buf = NULL;
771 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
773 MapDownloadInfo *mdi = ptr;
774 g_mutex_lock(mdi->mutex);
775 mdi->map_layer_alive = FALSE;
776 g_mutex_unlock(mdi->mutex);
779 static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
783 for ( x = mdi->x0; x <= mdi->xf; x++ )
785 for ( y = mdi->y0; y <= mdi->yf; y++ )
787 gboolean remove_mem_cache = FALSE;
788 gboolean need_download = FALSE;
789 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
790 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
791 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
794 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
798 if ( mdi->redownload == REDOWNLOAD_ALL)
799 g_remove ( mdi->filename_buf );
801 else if ( (mdi->redownload == REDOWNLOAD_BAD) && (g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE) )
803 /* see if this one is bad or what */
805 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
807 g_remove ( mdi->filename_buf );
809 g_object_unref ( pixbuf );
814 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
816 need_download = TRUE;
817 if (( mdi->redownload != REDOWNLOAD_NONE ) &&
818 ( mdi->redownload != DOWNLOAD_OR_REFRESH ))
819 remove_mem_cache = TRUE;
820 } else if ( mdi->redownload == DOWNLOAD_OR_REFRESH ) {
821 remove_mem_cache = TRUE;
825 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
828 if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf ))
833 g_mutex_lock(mdi->mutex);
834 if (remove_mem_cache)
835 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 );
836 if (mdi->refresh_display && mdi->map_layer_alive) {
837 /* TODO: check if it's on visible area */
838 vik_layer_emit_update ( VIK_LAYER(mdi->vml) );
840 g_mutex_unlock(mdi->mutex);
842 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
846 g_mutex_lock(mdi->mutex);
847 if (mdi->map_layer_alive)
848 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
849 g_mutex_unlock(mdi->mutex);
853 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
855 if ( mdi->mapcoord.x || mdi->mapcoord.y )
857 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
858 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
859 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
860 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
862 g_remove ( mdi->filename_buf );
867 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
869 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
870 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
872 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
873 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
874 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
876 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
881 mdi->map_layer_alive = TRUE;
882 mdi->mutex = g_mutex_new();
883 mdi->refresh_display = TRUE;
885 /* cache_dir and buffer for dest filename */
886 mdi->cache_dir = g_strdup ( vml->cache_dir );
887 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
888 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
889 mdi->maptype = vml->maptype;
893 mdi->redownload = redownload;
895 mdi->x0 = MIN(ulm.x, brm.x);
896 mdi->xf = MAX(ulm.x, brm.x);
897 mdi->y0 = MIN(ulm.y, brm.y);
898 mdi->yf = MAX(ulm.y, brm.y);
902 if ( mdi->redownload ) {
903 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
905 /* calculate how many we need */
906 for ( a = mdi->x0; a <= mdi->xf; a++ )
908 for ( b = mdi->y0; b <= mdi->yf; b++ )
910 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
911 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
913 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
919 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
921 if ( mdi->mapstoget )
923 const gchar *tmp_str;
928 if (redownload == REDOWNLOAD_BAD)
929 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
931 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
935 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
937 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
939 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
940 /* launch the thread */
941 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
942 tmp, /* description string */
943 (vik_thr_func) map_download_thread, /* function to call within thread */
944 mdi, /* pass along data */
945 (vik_thr_free_func) mdi_free, /* function to free pass along data */
946 (vik_thr_free_func) mdi_cancel_cleanup,
955 void maps_layer_download_section_without_redraw( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
958 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
960 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
961 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
962 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
966 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
971 mdi->map_layer_alive = TRUE;
972 mdi->mutex = g_mutex_new();
973 mdi->refresh_display = FALSE;
975 mdi->cache_dir = g_strdup ( vml->cache_dir );
976 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
977 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
978 mdi->maptype = vml->maptype;
982 mdi->redownload = REDOWNLOAD_NONE;
984 mdi->x0 = MIN(ulm.x, brm.x);
985 mdi->xf = MAX(ulm.x, brm.x);
986 mdi->y0 = MIN(ulm.y, brm.y);
987 mdi->yf = MAX(ulm.y, brm.y);
991 for (i = mdi->x0; i <= mdi->xf; i++) {
992 for (j = mdi->y0; j <= mdi->yf; j++) {
993 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
994 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
996 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1001 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1003 if (mdi->mapstoget) {
1006 fmt = ngettext("Downloading %d %s map...",
1007 "Downloading %d %s maps...",
1009 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1011 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1012 /* launch the thread */
1013 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1014 tmp, /* description string */
1015 (vik_thr_func) map_download_thread, /* function to call within thread */
1016 mdi, /* pass along data */
1017 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1018 (vik_thr_free_func) mdi_cancel_cleanup,
1026 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1028 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1030 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1032 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1035 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1037 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1039 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1041 if ( event->button == 1 )
1044 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 );
1045 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 );
1046 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1047 vml->dl_tool_x = vml->dl_tool_y = -1;
1052 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) );
1053 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) );
1055 vml->redownload_vvp = vvp;
1057 vml->dl_tool_x = vml->dl_tool_y = -1;
1059 if ( ! vml->dl_right_click_menu ) {
1061 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1063 item = gtk_menu_item_new_with_label ( _("Redownload bad map(s)") );
1064 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1065 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1067 item = gtk_menu_item_new_with_label ( _("Redownload all map(s)") );
1068 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1069 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1072 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1073 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1079 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1084 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1087 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1089 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1090 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1091 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1092 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1093 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1095 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1102 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1106 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1107 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1108 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1109 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1111 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1112 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1113 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1115 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1116 g_free ( filename_buf );
1117 vik_layer_emit_update ( VIK_LAYER(vml) );
1125 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1127 VikMapsLayer *vml = vml_vvp[0];
1128 VikViewport *vvp = vml_vvp[1];
1129 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1131 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1132 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1137 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1138 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1140 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1141 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1142 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1143 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1144 start_download_thread ( vml, vvp, &ul, &br, redownload );
1145 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1146 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1147 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1148 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1152 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1156 static void maps_layer_download_onscreen_maps ( gpointer vml_vvp[2] )
1158 download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1161 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1163 download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1166 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1168 static gpointer pass_along[2];
1170 pass_along[0] = vml;
1171 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1173 item = gtk_menu_item_new();
1174 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1175 gtk_widget_show ( item );
1177 item = gtk_menu_item_new_with_label ( _("Download Onscreen Maps") );
1178 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_onscreen_maps), pass_along );
1179 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1180 gtk_widget_show ( item );
1182 /* TODO Add GTK_STOCK_REFRESH icon */
1183 item = gtk_menu_item_new_with_label ( _("Refresh Onscreen Tiles") );
1184 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1185 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1186 gtk_widget_show ( item );