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 (Optional):"), VIK_LAYER_WIDGET_FILEENTRY },
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 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 ( const char *label, VikMapSource *map )
211 g_assert(label != NULL);
212 g_assert(map != NULL);
214 guint id = vik_map_source_get_uniq_id(map);
217 params_maptypes = g_list_append(params_maptypes, g_strdup(label));
220 params_maptypes_ids = g_list_append(params_maptypes_ids, GUINT_TO_POINTER (id));
222 /* We have to clone */
223 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
224 /* Register the clone in the list */
225 __map_types = g_list_append(__map_types, clone);
228 We have to ensure the mode LayerParam reference the up-to-date
232 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
233 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
235 maps_layer_params[0].widget_data = params_maptypes;
236 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
239 #define MAPS_LAYER_NTH_LABEL(n) ((gchar*)g_list_nth_data(params_maptypes, (n)))
240 #define MAPS_LAYER_NTH_ID(n) ((guint)g_list_nth_data(params_maptypes_ids, (n)))
241 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
243 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
245 return(vml->maptype);
248 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
250 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
253 /****************************************/
254 /******** CACHE DIR STUFF ***************/
255 /****************************************/
257 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
258 #define MAPS_CACHE_DIR maps_layer_default_dir()
262 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
263 #define LOCAL_MAPS_DIR "VIKING-MAPS"
266 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
267 #define LOCAL_MAPS_DIR ".viking-maps"
270 gchar *maps_layer_default_dir ()
272 static gchar *defaultdir = NULL;
275 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
276 const gchar *mapdir = g_getenv("VIKING_MAPS");
278 defaultdir = g_strdup ( mapdir );
279 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
280 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
282 const gchar *home = g_get_home_dir();
283 if (!home || g_access(home, W_OK))
284 home = g_get_home_dir ();
286 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
288 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
290 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
292 /* Add the separator at the end */
293 gchar *tmp = defaultdir;
294 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
297 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
302 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
304 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
306 g_mkdir ( vml->cache_dir, 0777 );
310 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
313 g_assert ( vml != NULL);
314 g_free ( vml->cache_dir );
315 vml->cache_dir = NULL;
317 if ( dir == NULL || dir[0] == '\0' )
318 vml->cache_dir = g_strdup ( MAPS_CACHE_DIR );
322 if ( dir[len-1] != G_DIR_SEPARATOR )
324 vml->cache_dir = g_malloc ( len+2 );
325 strncpy ( vml->cache_dir, dir, len );
326 vml->cache_dir[len] = G_DIR_SEPARATOR;
327 vml->cache_dir[len+1] = '\0';
330 vml->cache_dir = g_strdup ( dir );
332 maps_layer_mkdir_if_default_dir ( vml );
335 /****************************************/
336 /******** GOBJECT STUFF *****************/
337 /****************************************/
339 GType vik_maps_layer_get_type ()
341 static GType vml_type = 0;
345 static const GTypeInfo vml_info =
347 sizeof (VikMapsLayerClass),
348 NULL, /* base_init */
349 NULL, /* base_finalize */
350 NULL, /* class init */
351 NULL, /* class_finalize */
352 NULL, /* class_data */
353 sizeof (VikMapsLayer),
355 NULL /* instance init */
357 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
363 /****************************************/
364 /************** PARAMETERS **************/
365 /****************************************/
367 static guint map_index_to_uniq_id (guint8 index)
369 g_assert ( index < NUM_MAP_TYPES );
370 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
373 static guint map_uniq_id_to_index ( guint uniq_id )
376 for ( i = 0; i < NUM_MAP_TYPES; i++ )
377 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
379 return NUM_MAP_TYPES; /* no such thing */
382 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp )
386 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
387 case PARAM_MAPTYPE: {
388 gint maptype = map_uniq_id_to_index(data.u);
389 if ( maptype == NUM_MAP_TYPES ) g_warning(_("Unknown map type"));
390 else vml->maptype = maptype;
393 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
394 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
395 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
396 vml->mapzoom_id = data.u;
397 vml->xmapzoom = __mapzooms_x [data.u];
398 vml->ymapzoom = __mapzooms_y [data.u];
399 }else g_warning (_("Unknown Map Zoom")); break;
404 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id )
406 VikLayerParamData rv;
409 case PARAM_CACHE_DIR: rv.s = (vml->cache_dir && strcmp(vml->cache_dir, MAPS_CACHE_DIR) != 0) ? vml->cache_dir : ""; break;
410 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
411 case PARAM_ALPHA: rv.u = vml->alpha; break;
412 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
413 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
418 /****************************************/
419 /****** CREATING, COPYING, FREEING ******/
420 /****************************************/
422 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
425 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
426 vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS );
427 idx = map_uniq_id_to_index(7); /* 7 is id for google maps */
428 vml->maptype = (idx < NUM_MAP_TYPES) ? idx : 0;
431 vml->dl_tool_x = vml->dl_tool_y = -1;
432 maps_layer_set_cache_dir ( vml, NULL );
433 vml->autodownload = FALSE;
434 vml->last_center = NULL;
435 vml->last_xmpp = 0.0;
436 vml->last_ympp = 0.0;
438 vml->dl_right_click_menu = NULL;
443 static void maps_layer_free ( VikMapsLayer *vml )
445 g_free ( vml->cache_dir );
446 vml->cache_dir = NULL;
447 if ( vml->dl_right_click_menu )
448 gtk_object_sink ( GTK_OBJECT(vml->dl_right_click_menu) );
449 g_free(vml->last_center);
450 vml->last_center = NULL;
453 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
455 if (from_file != TRUE)
457 /* If this method is not called in file reading context
458 * it is called in GUI context.
459 * So, we can check if we have to inform the user about inconsistency */
460 VikViewportDrawMode vp_drawmode;
461 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
462 VikMapSource *map = NULL;
464 vp_drawmode = vik_viewport_get_drawmode ( VIK_VIEWPORT(vp) );
465 map = MAPS_LAYER_NTH_TYPE(vml->maptype);
466 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
467 const gchar *drawmode_name = vik_viewport_get_drawmode_name (VIK_VIEWPORT(vp), vik_map_source_get_drawmode(map));
468 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
469 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), msg );
475 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
477 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
480 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
482 VikMapsLayer *rv = maps_layer_new ( vvp );
483 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
487 /*********************/
488 /****** DRAWING ******/
489 /*********************/
491 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
494 gint width, height, iii, jjj;
496 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
498 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
499 g_object_unref(G_OBJECT(pixbuf));
503 pixels = gdk_pixbuf_get_pixels(pixbuf);
504 width = gdk_pixbuf_get_width(pixbuf);
505 height = gdk_pixbuf_get_height(pixbuf);
507 /* r,g,b,a,r,g,b,a.... */
508 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
516 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
519 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
520 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
521 g_object_unref ( G_OBJECT(pixbuf) );
525 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
530 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
531 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
534 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
535 vml->cache_dir, mode,
536 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
537 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE) {
540 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
544 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
545 g_warning ( _("Couldn't open image file: %s"), gx->message );
549 g_object_unref ( G_OBJECT(pixbuf) );
552 if ( vml->alpha < 255 )
553 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
554 if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
555 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
557 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
558 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
559 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
567 gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
569 const VikCoord *center = vik_viewport_get_center ( vvp );
571 if (vml->last_center == NULL) {
572 VikCoord *new_center = g_malloc(sizeof(VikCoord));
573 *new_center = *center;
574 vml->last_center = new_center;
575 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
576 vml->last_ympp = vik_viewport_get_ympp(vvp);
580 /* TODO: perhaps vik_coord_diff() */
581 if (vik_coord_equals(vml->last_center, center)
582 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
583 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
586 *(vml->last_center) = *center;
587 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
588 vml->last_ympp = vik_viewport_get_ympp(vvp);
592 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
595 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
596 gdouble yzoom = vik_viewport_get_ympp ( vvp );
597 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
598 gdouble existence_only = FALSE;
600 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
601 xshrinkfactor = vml->xmapzoom / xzoom;
602 yshrinkfactor = vml->ymapzoom / yzoom;
603 xzoom = vml->xmapzoom;
604 yzoom = vml->xmapzoom;
605 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
606 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
607 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR )
608 existence_only = TRUE;
610 g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
617 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
618 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
619 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
623 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
624 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
625 gint mode = vik_map_source_get_uniq_id(map);
628 gint xx, yy, width, height;
631 guint max_path_len = strlen(vml->cache_dir) + 40;
632 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
634 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
636 fputs(stderr, "DEBUG: Starting autodownload\n");
638 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
641 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
642 for ( x = xmin; x <= xmax; x++ ) {
643 for ( y = ymin; y <= ymax; y++ ) {
646 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
648 width = gdk_pixbuf_get_width ( pixbuf );
649 height = gdk_pixbuf_get_height ( pixbuf );
651 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
652 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
656 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
660 } else { /* tilesize is known, don't have to keep converting coords */
661 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
662 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
663 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
664 gint tilesize_x_ceil = ceil ( tilesize_x );
665 gint tilesize_y_ceil = ceil ( tilesize_y );
666 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
667 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
668 gdouble xx, yy; gint xx_tmp, yy_tmp;
669 gint base_yy, xend, yend;
671 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
673 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
674 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
676 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
677 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
678 xx = xx_tmp; yy = yy_tmp;
679 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
680 * eg if tile size 128, shrinkfactor 0.333 */
681 xx -= (tilesize_x/2);
682 base_yy = yy - (tilesize_y/2);
684 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
686 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
690 if ( existence_only ) {
691 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
692 vml->cache_dir, mode,
693 ulm.scale, ulm.z, ulm.x, ulm.y );
694 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
695 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
698 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
700 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
713 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
715 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
719 /* get corner coords */
720 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
721 /* UTM multi-zone stuff by Kit Transue */
722 gchar leftmost_zone, rightmost_zone, i;
723 leftmost_zone = vik_viewport_leftmost_zone( vvp );
724 rightmost_zone = vik_viewport_rightmost_zone( vvp );
725 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
726 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
727 maps_layer_draw_section ( vml, vvp, &ul, &br );
731 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
732 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
734 maps_layer_draw_section ( vml, vvp, &ul, &br );
739 /*************************/
740 /****** DOWNLOADING ******/
741 /*************************/
743 /* pass along data to thread, exists even if layer is deleted. */
753 gboolean refresh_display;
756 gboolean map_layer_alive;
760 static void mdi_free ( MapDownloadInfo *mdi )
762 g_mutex_free(mdi->mutex);
763 g_free ( mdi->cache_dir );
764 mdi->cache_dir = NULL;
765 g_free ( mdi->filename_buf );
766 mdi->filename_buf = NULL;
770 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
772 MapDownloadInfo *mdi = ptr;
773 g_mutex_lock(mdi->mutex);
774 mdi->map_layer_alive = FALSE;
775 g_mutex_unlock(mdi->mutex);
778 static void map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
782 for ( x = mdi->x0; x <= mdi->xf; x++ )
784 for ( y = mdi->y0; y <= mdi->yf; y++ )
786 gboolean remove_mem_cache = FALSE;
787 gboolean need_download = FALSE;
788 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
789 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
790 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
793 a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
795 if ( mdi->redownload == REDOWNLOAD_ALL)
796 g_remove ( mdi->filename_buf );
798 else if ( (mdi->redownload == REDOWNLOAD_BAD) && (g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE) )
800 /* see if this one is bad or what */
802 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
804 g_remove ( mdi->filename_buf );
806 g_object_unref ( pixbuf );
811 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
813 need_download = TRUE;
814 if (( mdi->redownload != REDOWNLOAD_NONE ) &&
815 ( mdi->redownload != DOWNLOAD_OR_REFRESH ))
816 remove_mem_cache = TRUE;
817 } else if ( mdi->redownload == DOWNLOAD_OR_REFRESH ) {
818 remove_mem_cache = TRUE;
822 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
825 if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf ))
830 g_mutex_lock(mdi->mutex);
831 if (remove_mem_cache)
832 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 );
833 if (mdi->refresh_display && mdi->map_layer_alive) {
834 /* TODO: check if it's on visible area */
835 vik_layer_emit_update ( VIK_LAYER(mdi->vml) );
837 g_mutex_unlock(mdi->mutex);
839 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
843 g_mutex_lock(mdi->mutex);
844 if (mdi->map_layer_alive)
845 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
846 g_mutex_unlock(mdi->mutex);
849 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
851 if ( mdi->mapcoord.x || mdi->mapcoord.y )
853 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
854 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
855 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
856 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
858 g_remove ( mdi->filename_buf );
863 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
865 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
866 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
868 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
869 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
870 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
872 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
877 mdi->map_layer_alive = TRUE;
878 mdi->mutex = g_mutex_new();
879 mdi->refresh_display = TRUE;
881 /* cache_dir and buffer for dest filename */
882 mdi->cache_dir = g_strdup ( vml->cache_dir );
883 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
884 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
885 mdi->maptype = vml->maptype;
889 mdi->redownload = redownload;
891 mdi->x0 = MIN(ulm.x, brm.x);
892 mdi->xf = MAX(ulm.x, brm.x);
893 mdi->y0 = MIN(ulm.y, brm.y);
894 mdi->yf = MAX(ulm.y, brm.y);
898 if ( mdi->redownload ) {
899 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
901 /* calculate how many we need */
902 for ( a = mdi->x0; a <= mdi->xf; a++ )
904 for ( b = mdi->y0; b <= mdi->yf; b++ )
906 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
907 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
909 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
915 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
917 if ( mdi->mapstoget )
919 const gchar *tmp_str;
924 if (redownload == REDOWNLOAD_BAD)
925 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
927 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
931 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
933 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
935 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
936 /* launch the thread */
937 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
938 tmp, /* description string */
939 (vik_thr_func) map_download_thread, /* function to call within thread */
940 mdi, /* pass along data */
941 (vik_thr_free_func) mdi_free, /* function to free pass along data */
942 (vik_thr_free_func) mdi_cancel_cleanup,
951 void maps_layer_download_section_without_redraw( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
954 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
956 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
957 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
958 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
962 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
967 mdi->map_layer_alive = TRUE;
968 mdi->mutex = g_mutex_new();
969 mdi->refresh_display = FALSE;
971 mdi->cache_dir = g_strdup ( vml->cache_dir );
972 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
973 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
974 mdi->maptype = vml->maptype;
978 mdi->redownload = REDOWNLOAD_NONE;
980 mdi->x0 = MIN(ulm.x, brm.x);
981 mdi->xf = MAX(ulm.x, brm.x);
982 mdi->y0 = MIN(ulm.y, brm.y);
983 mdi->yf = MAX(ulm.y, brm.y);
987 for (i = mdi->x0; i <= mdi->xf; i++) {
988 for (j = mdi->y0; j <= mdi->yf; j++) {
989 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
990 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
992 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
997 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
999 if (mdi->mapstoget) {
1002 fmt = ngettext("Downloading %d %s map...",
1003 "Downloading %d %s maps...",
1005 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1007 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1008 /* launch the thread */
1009 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1010 tmp, /* description string */
1011 (vik_thr_func) map_download_thread, /* function to call within thread */
1012 mdi, /* pass along data */
1013 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1014 (vik_thr_free_func) mdi_cancel_cleanup,
1022 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1024 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1026 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1028 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1031 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1033 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1035 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1037 if ( event->button == 1 )
1040 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 );
1041 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 );
1042 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1043 vml->dl_tool_x = vml->dl_tool_y = -1;
1048 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) );
1049 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) );
1051 vml->redownload_vvp = vvp;
1053 vml->dl_tool_x = vml->dl_tool_y = -1;
1055 if ( ! vml->dl_right_click_menu ) {
1057 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1059 item = gtk_menu_item_new_with_label ( _("Redownload bad map(s)") );
1060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1061 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1063 item = gtk_menu_item_new_with_label ( _("Redownload all map(s)") );
1064 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1065 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1068 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1069 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1075 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1080 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1083 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1085 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1086 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1087 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1088 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1089 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1091 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1098 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1102 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1103 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1104 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1105 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1107 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1108 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1109 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1111 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1112 g_free ( filename_buf );
1113 vik_layer_emit_update ( VIK_LAYER(vml) );
1121 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1123 VikMapsLayer *vml = vml_vvp[0];
1124 VikViewport *vvp = vml_vvp[1];
1125 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1127 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1128 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1133 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1134 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1136 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1137 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1138 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1139 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1140 start_download_thread ( vml, vvp, &ul, &br, redownload );
1141 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1142 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1143 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1144 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1148 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1152 static void maps_layer_download_onscreen_maps ( gpointer vml_vvp[2] )
1154 download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1157 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1159 download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1162 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1164 static gpointer pass_along[2];
1166 pass_along[0] = vml;
1167 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1169 item = gtk_menu_item_new();
1170 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1171 gtk_widget_show ( item );
1173 item = gtk_menu_item_new_with_label ( _("Download Onscreen Maps") );
1174 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_onscreen_maps), pass_along );
1175 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1176 gtk_widget_show ( item );
1178 /* TODO Add GTK_STOCK_REFRESH icon */
1179 item = gtk_menu_item_new_with_label ( _("Refresh Onscreen Tiles") );
1180 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1181 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1182 gtk_widget_show ( item );