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>
44 #include "viktreeview.h"
45 #include "vikviewport.h"
47 #include "vikmapslayer.h"
54 /* only for dialog.h -- ugh */
55 #include "vikwaypoint.h"
58 #include "vikstatus.h"
59 #include "background.h"
61 #include "vikaggregatelayer.h"
62 #include "viklayerspanel.h"
65 #include "terraserver.h"
67 #include "icons/icons.h"
69 /****** MAP TYPES ******/
71 static GList *__map_types = NULL;
73 #define NUM_MAP_TYPES g_list_length(__map_types)
75 /* List of label for each map type */
76 static GList *params_maptypes = NULL;
78 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
79 static GList *params_maptypes_ids = NULL;
81 /******** MAPZOOMS *********/
83 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 };
84 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 };
85 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 };
87 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
89 /**************************/
92 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
93 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
94 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
95 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp );
96 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id );
97 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
98 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
99 static void maps_layer_free ( VikMapsLayer *vml );
100 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
101 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
102 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
103 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
104 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
105 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
108 static VikLayerParamScale params_scales[] = {
109 /* min, max, step, digits (decimal places) */
110 { 0, 255, 3, 0 }, /* alpha */
113 VikLayerParam maps_layer_params[] = {
114 { "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL, NULL },
115 { "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory (Optional):"), VIK_LAYER_WIDGET_FILEENTRY },
116 { "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales },
117 { "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload maps:"), VIK_LAYER_WIDGET_CHECKBUTTON },
118 { "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms },
121 enum { PARAM_MAPTYPE=0, PARAM_CACHE_DIR, PARAM_ALPHA, PARAM_AUTODOWNLOAD, PARAM_MAPZOOM, NUM_PARAMS };
123 static VikToolInterface maps_tools[] = {
124 { N_("Maps Download"), (VikToolConstructorFunc) maps_layer_download_create, NULL, NULL, NULL,
125 (VikToolMouseFunc) maps_layer_download_click, NULL, (VikToolMouseFunc) maps_layer_download_release,
126 (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf },
129 VikLayerInterface vik_maps_layer_interface = {
131 &vikmapslayer_pixbuf,
134 sizeof(maps_tools) / sizeof(maps_tools[0]),
143 (VikLayerFuncCreate) maps_layer_new,
144 (VikLayerFuncRealize) NULL,
145 maps_layer_post_read,
146 (VikLayerFuncFree) maps_layer_free,
148 (VikLayerFuncProperties) NULL,
149 (VikLayerFuncDraw) maps_layer_draw,
150 (VikLayerFuncChangeCoordMode) NULL,
152 (VikLayerFuncSetMenuItemsSelection) NULL,
153 (VikLayerFuncGetMenuItemsSelection) NULL,
155 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
156 (VikLayerFuncSublayerAddMenuItems) NULL,
158 (VikLayerFuncSublayerRenameRequest) NULL,
159 (VikLayerFuncSublayerToggleVisible) NULL,
161 (VikLayerFuncMarshall) maps_layer_marshall,
162 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
164 (VikLayerFuncSetParam) maps_layer_set_param,
165 (VikLayerFuncGetParam) maps_layer_get_param,
167 (VikLayerFuncReadFileData) NULL,
168 (VikLayerFuncWriteFileData) NULL,
170 (VikLayerFuncDeleteItem) NULL,
171 (VikLayerFuncCopyItem) NULL,
172 (VikLayerFuncPasteItem) NULL,
173 (VikLayerFuncFreeCopiedItem) NULL,
174 (VikLayerFuncDragDropRequest) NULL,
177 struct _VikMapsLayer {
183 gdouble xmapzoom, ymapzoom;
185 gboolean autodownload;
186 VikCoord *last_center;
190 gint dl_tool_x, dl_tool_y;
192 GtkMenu *dl_right_click_menu;
193 VikCoord redownload_ul, redownload_br; /* right click menu only */
194 VikViewport *redownload_vvp;
197 enum { REDOWNLOAD_NONE = 0, REDOWNLOAD_BAD, REDOWNLOAD_ALL, DOWNLOAD_OR_REFRESH };
200 /****************************************/
201 /******** MAPS LAYER TYPES **************/
202 /****************************************/
204 void maps_layer_register_type ( const char *label, guint id, VikMapType *map_type )
206 g_assert(label != NULL);
207 g_assert(map_type != NULL);
208 g_assert(id == vik_map_type_get_uniq_id(map_type));
211 params_maptypes = g_list_append(params_maptypes, g_strdup(label));
214 params_maptypes_ids = g_list_append(params_maptypes_ids, GUINT_TO_POINTER (id));
216 /* We have to clone */
217 VikMapType *clone = VIK_MAP_TYPE(g_object_ref(map_type));
218 /* Register the clone in the list */
219 __map_types = g_list_append(__map_types, clone);
222 We have to ensure the mode LayerParam reference the up-to-date
226 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
227 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
229 maps_layer_params[0].widget_data = params_maptypes;
230 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
233 #define MAPS_LAYER_NTH_LABEL(n) ((gchar*)g_list_nth_data(params_maptypes, (n)))
234 #define MAPS_LAYER_NTH_ID(n) ((guint)g_list_nth_data(params_maptypes_ids, (n)))
235 #define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_TYPE(g_list_nth_data(__map_types, (n))))
237 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
239 return(vml->maptype);
242 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
244 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
247 /****************************************/
248 /******** CACHE DIR STUFF ***************/
249 /****************************************/
251 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
252 #define MAPS_CACHE_DIR maps_layer_default_dir()
256 #define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
257 #define LOCAL_MAPS_DIR "VIKING-MAPS"
260 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
261 #define LOCAL_MAPS_DIR ".viking-maps"
264 gchar *maps_layer_default_dir ()
266 static gchar *defaultdir = NULL;
269 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
270 const gchar *mapdir = g_getenv("VIKING_MAPS");
272 defaultdir = g_strdup ( mapdir );
273 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
274 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
276 const gchar *home = g_get_home_dir();
277 if (!home || g_access(home, W_OK))
278 home = g_get_home_dir ();
280 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
282 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
284 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
286 /* Add the separator at the end */
287 gchar *tmp = defaultdir;
288 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
291 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
296 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
298 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
300 g_mkdir ( vml->cache_dir, 0777 );
304 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
307 g_assert ( vml != NULL);
308 g_free ( vml->cache_dir );
309 vml->cache_dir = NULL;
311 if ( dir == NULL || dir[0] == '\0' )
312 vml->cache_dir = g_strdup ( MAPS_CACHE_DIR );
316 if ( dir[len-1] != G_DIR_SEPARATOR )
318 vml->cache_dir = g_malloc ( len+2 );
319 strncpy ( vml->cache_dir, dir, len );
320 vml->cache_dir[len] = G_DIR_SEPARATOR;
321 vml->cache_dir[len+1] = '\0';
324 vml->cache_dir = g_strdup ( dir );
326 maps_layer_mkdir_if_default_dir ( vml );
329 /****************************************/
330 /******** GOBJECT STUFF *****************/
331 /****************************************/
333 GType vik_maps_layer_get_type ()
335 static GType vml_type = 0;
339 static const GTypeInfo vml_info =
341 sizeof (VikMapsLayerClass),
342 NULL, /* base_init */
343 NULL, /* base_finalize */
344 NULL, /* class init */
345 NULL, /* class_finalize */
346 NULL, /* class_data */
347 sizeof (VikMapsLayer),
349 NULL /* instance init */
351 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
357 /****************************************/
358 /************** PARAMETERS **************/
359 /****************************************/
361 static guint map_index_to_uniq_id (guint8 index)
363 g_assert ( index < NUM_MAP_TYPES );
364 return vik_map_type_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
367 static guint map_uniq_id_to_index ( guint uniq_id )
370 for ( i = 0; i < NUM_MAP_TYPES; i++ )
371 if ( vik_map_type_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
373 return NUM_MAP_TYPES; /* no such thing */
376 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp )
380 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
381 case PARAM_MAPTYPE: {
382 gint maptype = map_uniq_id_to_index(data.u);
383 if ( maptype == NUM_MAP_TYPES ) g_warning(_("Unknown map type"));
384 else vml->maptype = maptype;
387 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
388 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
389 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
390 vml->mapzoom_id = data.u;
391 vml->xmapzoom = __mapzooms_x [data.u];
392 vml->ymapzoom = __mapzooms_y [data.u];
393 }else g_warning (_("Unknown Map Zoom")); break;
398 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id )
400 VikLayerParamData rv;
403 case PARAM_CACHE_DIR: rv.s = (vml->cache_dir && strcmp(vml->cache_dir, MAPS_CACHE_DIR) != 0) ? vml->cache_dir : ""; break;
404 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
405 case PARAM_ALPHA: rv.u = vml->alpha; break;
406 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
407 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
412 /****************************************/
413 /****** CREATING, COPYING, FREEING ******/
414 /****************************************/
416 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
419 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
420 vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS );
421 idx = map_uniq_id_to_index(7); /* 7 is id for google maps */
422 vml->maptype = (idx < NUM_MAP_TYPES) ? idx : 0;
425 vml->dl_tool_x = vml->dl_tool_y = -1;
426 maps_layer_set_cache_dir ( vml, NULL );
427 vml->autodownload = FALSE;
428 vml->last_center = NULL;
429 vml->last_xmpp = 0.0;
430 vml->last_ympp = 0.0;
432 vml->dl_right_click_menu = NULL;
437 static void maps_layer_free ( VikMapsLayer *vml )
439 g_free ( vml->cache_dir );
440 vml->cache_dir = NULL;
441 if ( vml->dl_right_click_menu )
442 gtk_object_sink ( GTK_OBJECT(vml->dl_right_click_menu) );
443 g_free(vml->last_center);
444 vml->last_center = NULL;
447 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
449 if (from_file != TRUE)
451 /* If this method is not called in file reading context
452 * it is called in GUI context.
453 * So, we can check if we have to inform the user about inconsistency */
454 VikViewportDrawMode vp_drawmode;
455 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
456 VikMapType *map_type = NULL;
458 vp_drawmode = vik_viewport_get_drawmode ( VIK_VIEWPORT(vp) );
459 map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
460 if (vik_map_type_get_drawmode(map_type) != vp_drawmode) {
461 const gchar *drawmode_name = vik_viewport_get_drawmode_name (VIK_VIEWPORT(vp), vik_map_type_get_drawmode(map_type));
462 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
463 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), msg );
469 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
471 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
474 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
476 VikMapsLayer *rv = maps_layer_new ( vvp );
477 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
481 /*********************/
482 /****** DRAWING ******/
483 /*********************/
485 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
488 gint width, height, iii, jjj;
490 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
492 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
493 g_object_unref(G_OBJECT(pixbuf));
497 pixels = gdk_pixbuf_get_pixels(pixbuf);
498 width = gdk_pixbuf_get_width(pixbuf);
499 height = gdk_pixbuf_get_height(pixbuf);
501 /* r,g,b,a,r,g,b,a.... */
502 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
510 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
513 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
514 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
515 g_object_unref ( G_OBJECT(pixbuf) );
519 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
524 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
525 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
528 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
529 vml->cache_dir, mode,
530 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
531 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE) {
534 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
538 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
539 g_warning ( _("Couldn't open image file: %s"), gx->message );
543 g_object_unref ( G_OBJECT(pixbuf) );
546 if ( vml->alpha < 255 )
547 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
548 if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
549 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
551 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
552 mapcoord->z, vik_map_type_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
553 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
561 gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
563 const VikCoord *center = vik_viewport_get_center ( vvp );
565 if (vml->last_center == NULL) {
566 VikCoord *new_center = g_malloc(sizeof(VikCoord));
567 *new_center = *center;
568 vml->last_center = new_center;
569 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
570 vml->last_ympp = vik_viewport_get_ympp(vvp);
574 /* TODO: perhaps vik_coord_diff() */
575 if (vik_coord_equals(vml->last_center, center)
576 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
577 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
580 *(vml->last_center) = *center;
581 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
582 vml->last_ympp = vik_viewport_get_ympp(vvp);
586 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
589 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
590 gdouble yzoom = vik_viewport_get_ympp ( vvp );
591 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
592 gdouble existence_only = FALSE;
594 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
595 xshrinkfactor = vml->xmapzoom / xzoom;
596 yshrinkfactor = vml->ymapzoom / yzoom;
597 xzoom = vml->xmapzoom;
598 yzoom = vml->xmapzoom;
599 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
600 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
601 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR )
602 existence_only = TRUE;
604 g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
611 VikMapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
612 if ( vik_map_type_coord_to_mapcoord ( map_type, ul, xzoom, yzoom, &ulm ) &&
613 vik_map_type_coord_to_mapcoord ( map_type, br, xzoom, yzoom, &brm ) ) {
617 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
618 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
619 gint mode = vik_map_type_get_uniq_id(map_type);
622 gint xx, yy, width, height;
625 guint max_path_len = strlen(vml->cache_dir) + 40;
626 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
628 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
630 fputs(stderr, "DEBUG: Starting autodownload\n");
632 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
635 if ( vik_map_type_get_tilesize_x(map_type) == 0 && !existence_only ) {
636 for ( x = xmin; x <= xmax; x++ ) {
637 for ( y = ymin; y <= ymax; y++ ) {
640 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
642 width = gdk_pixbuf_get_width ( pixbuf );
643 height = gdk_pixbuf_get_height ( pixbuf );
645 vik_map_type_mapcoord_to_center_coord ( map_type, &ulm, &coord );
646 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
650 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
654 } else { /* tilesize is known, don't have to keep converting coords */
655 gdouble tilesize_x = vik_map_type_get_tilesize_x(map_type) * xshrinkfactor;
656 gdouble tilesize_y = vik_map_type_get_tilesize_y(map_type) * yshrinkfactor;
657 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
658 gint tilesize_x_ceil = ceil ( tilesize_x );
659 gint tilesize_y_ceil = ceil ( tilesize_y );
660 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
661 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
662 gdouble xx, yy; gint xx_tmp, yy_tmp;
663 gint base_yy, xend, yend;
665 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
667 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
668 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
670 vik_map_type_mapcoord_to_center_coord ( map_type, &ulm, &coord );
671 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
672 xx = xx_tmp; yy = yy_tmp;
673 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
674 * eg if tile size 128, shrinkfactor 0.333 */
675 xx -= (tilesize_x/2);
676 base_yy = yy - (tilesize_y/2);
678 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
680 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
684 if ( existence_only ) {
685 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
686 vml->cache_dir, mode,
687 ulm.scale, ulm.z, ulm.x, ulm.y );
688 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
689 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
692 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
694 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
707 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
709 if ( vik_map_type_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
713 /* get corner coords */
714 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
715 /* UTM multi-zone stuff by Kit Transue */
716 gchar leftmost_zone, rightmost_zone, i;
717 leftmost_zone = vik_viewport_leftmost_zone( vvp );
718 rightmost_zone = vik_viewport_rightmost_zone( vvp );
719 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
720 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
721 maps_layer_draw_section ( vml, vvp, &ul, &br );
725 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
726 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
728 maps_layer_draw_section ( vml, vvp, &ul, &br );
733 /*************************/
734 /****** DOWNLOADING ******/
735 /*************************/
737 /* pass along data to thread, exists even if layer is deleted. */
747 gboolean refresh_display;
750 gboolean map_layer_alive;
754 static void mdi_free ( MapDownloadInfo *mdi )
756 g_mutex_free(mdi->mutex);
757 g_free ( mdi->cache_dir );
758 mdi->cache_dir = NULL;
759 g_free ( mdi->filename_buf );
760 mdi->filename_buf = NULL;
764 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
766 MapDownloadInfo *mdi = ptr;
767 g_mutex_lock(mdi->mutex);
768 mdi->map_layer_alive = FALSE;
769 g_mutex_unlock(mdi->mutex);
772 static void map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
776 for ( x = mdi->x0; x <= mdi->xf; x++ )
778 for ( y = mdi->y0; y <= mdi->yf; y++ )
780 gboolean remove_mem_cache = FALSE;
781 gboolean need_download = FALSE;
782 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
783 mdi->cache_dir, vik_map_type_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
784 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
787 a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
789 if ( mdi->redownload == REDOWNLOAD_ALL)
790 g_remove ( mdi->filename_buf );
792 else if ( (mdi->redownload == REDOWNLOAD_BAD) && (g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE) )
794 /* see if this one is bad or what */
796 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
798 g_remove ( mdi->filename_buf );
800 g_object_unref ( pixbuf );
805 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
807 need_download = TRUE;
808 if (( mdi->redownload != REDOWNLOAD_NONE ) &&
809 ( mdi->redownload != DOWNLOAD_OR_REFRESH ))
810 remove_mem_cache = TRUE;
811 } else if ( mdi->redownload == DOWNLOAD_OR_REFRESH ) {
812 remove_mem_cache = TRUE;
816 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
819 if ( vik_map_type_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf ))
824 g_mutex_lock(mdi->mutex);
825 if (remove_mem_cache)
826 a_mapcache_remove_all_shrinkfactors ( x, y, mdi->mapcoord.z, vik_map_type_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)), mdi->mapcoord.scale );
827 if (mdi->refresh_display && mdi->map_layer_alive) {
828 /* TODO: check if it's on visible area */
829 vik_layer_emit_update ( VIK_LAYER(mdi->vml) );
831 g_mutex_unlock(mdi->mutex);
833 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
837 g_mutex_lock(mdi->mutex);
838 if (mdi->map_layer_alive)
839 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
840 g_mutex_unlock(mdi->mutex);
843 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
845 if ( mdi->mapcoord.x || mdi->mapcoord.y )
847 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
848 mdi->cache_dir, vik_map_type_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
849 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
850 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
852 g_remove ( mdi->filename_buf );
857 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
859 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
860 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
862 VikMapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
863 if ( vik_map_type_coord_to_mapcoord ( map_type, ul, xzoom, yzoom, &ulm )
864 && vik_map_type_coord_to_mapcoord ( map_type, br, xzoom, yzoom, &brm ) )
866 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
871 mdi->map_layer_alive = TRUE;
872 mdi->mutex = g_mutex_new();
873 mdi->refresh_display = TRUE;
875 /* cache_dir and buffer for dest filename */
876 mdi->cache_dir = g_strdup ( vml->cache_dir );
877 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
878 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
879 mdi->maptype = vml->maptype;
883 mdi->redownload = redownload;
885 mdi->x0 = MIN(ulm.x, brm.x);
886 mdi->xf = MAX(ulm.x, brm.x);
887 mdi->y0 = MIN(ulm.y, brm.y);
888 mdi->yf = MAX(ulm.y, brm.y);
892 if ( mdi->redownload ) {
893 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
895 /* calculate how many we need */
896 for ( a = mdi->x0; a <= mdi->xf; a++ )
898 for ( b = mdi->y0; b <= mdi->yf; b++ )
900 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
901 vml->cache_dir, vik_map_type_get_uniq_id(map_type), ulm.scale,
903 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
909 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
911 if ( mdi->mapstoget )
913 const gchar *tmp_str;
918 if (redownload == REDOWNLOAD_BAD)
919 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
921 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
925 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
927 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
929 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
930 /* launch the thread */
931 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
932 tmp, /* description string */
933 (vik_thr_func) map_download_thread, /* function to call within thread */
934 mdi, /* pass along data */
935 (vik_thr_free_func) mdi_free, /* function to free pass along data */
936 (vik_thr_free_func) mdi_cancel_cleanup,
945 void maps_layer_download_section_without_redraw( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
948 VikMapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
950 if (!vik_map_type_coord_to_mapcoord(map_type, ul, zoom, zoom, &ulm)
951 || !vik_map_type_coord_to_mapcoord(map_type, br, zoom, zoom, &brm)) {
952 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
956 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
961 mdi->map_layer_alive = TRUE;
962 mdi->mutex = g_mutex_new();
963 mdi->refresh_display = FALSE;
965 mdi->cache_dir = g_strdup ( vml->cache_dir );
966 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
967 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
968 mdi->maptype = vml->maptype;
972 mdi->redownload = REDOWNLOAD_NONE;
974 mdi->x0 = MIN(ulm.x, brm.x);
975 mdi->xf = MAX(ulm.x, brm.x);
976 mdi->y0 = MIN(ulm.y, brm.y);
977 mdi->yf = MAX(ulm.y, brm.y);
981 for (i = mdi->x0; i <= mdi->xf; i++) {
982 for (j = mdi->y0; j <= mdi->yf; j++) {
983 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
984 vml->cache_dir, vik_map_type_get_uniq_id(map_type), ulm.scale,
986 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
991 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
993 if (mdi->mapstoget) {
996 fmt = ngettext("Downloading %d %s map...",
997 "Downloading %d %s maps...",
999 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1001 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1002 /* launch the thread */
1003 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1004 tmp, /* description string */
1005 (vik_thr_func) map_download_thread, /* function to call within thread */
1006 mdi, /* pass along data */
1007 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1008 (vik_thr_free_func) mdi_cancel_cleanup,
1016 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1018 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1020 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1022 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1025 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1027 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1029 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1031 if ( event->button == 1 )
1034 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 );
1035 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 );
1036 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1037 vml->dl_tool_x = vml->dl_tool_y = -1;
1042 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) );
1043 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) );
1045 vml->redownload_vvp = vvp;
1047 vml->dl_tool_x = vml->dl_tool_y = -1;
1049 if ( ! vml->dl_right_click_menu ) {
1051 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1053 item = gtk_menu_item_new_with_label ( _("Redownload bad map(s)") );
1054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1055 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1057 item = gtk_menu_item_new_with_label ( _("Redownload all map(s)") );
1058 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1059 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1062 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1063 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1069 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1074 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1077 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1079 VikMapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
1080 if ( vik_map_type_get_drawmode(map_type) == vik_viewport_get_drawmode ( vvp ) &&
1081 vik_map_type_coord_to_mapcoord ( map_type, vik_viewport_get_center ( vvp ),
1082 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1083 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1085 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1092 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1096 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1097 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1098 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1099 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1101 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1102 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1103 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1105 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1106 g_free ( filename_buf );
1107 vik_layer_emit_update ( VIK_LAYER(vml) );
1115 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1117 VikMapsLayer *vml = vml_vvp[0];
1118 VikViewport *vvp = vml_vvp[1];
1119 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1121 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1122 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1127 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1128 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1130 VikMapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
1131 if ( vik_map_type_get_drawmode(map_type) == vp_drawmode &&
1132 vik_map_type_coord_to_mapcoord ( map_type, &ul, xzoom, yzoom, &ulm ) &&
1133 vik_map_type_coord_to_mapcoord ( map_type, &br, xzoom, yzoom, &brm ) )
1134 start_download_thread ( vml, vvp, &ul, &br, redownload );
1135 else if (vik_map_type_get_drawmode(map_type) != vp_drawmode) {
1136 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_type_get_drawmode(map_type));
1137 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1138 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1142 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1146 static void maps_layer_download_onscreen_maps ( gpointer vml_vvp[2] )
1148 download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1151 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1153 download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1156 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1158 static gpointer pass_along[2];
1160 pass_along[0] = vml;
1161 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1163 item = gtk_menu_item_new();
1164 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1165 gtk_widget_show ( item );
1167 item = gtk_menu_item_new_with_label ( _("Download Onscreen Maps") );
1168 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_onscreen_maps), pass_along );
1169 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1170 gtk_widget_show ( item );
1172 item = gtk_menu_item_new_with_label ( _("Refresh Onscreen Tiles") );
1173 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1174 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1175 gtk_widget_show ( item );