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
24 #define MAX_SHRINKFACTOR 8.0000001 /* zoom 1 viewing 8-tiles */
25 #define MIN_SHRINKFACTOR 0.0312499 /* zoom 32 viewing 1-tiles */
27 #define REAL_MIN_SHRINKFACTOR 0.0039062499 /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
30 #include <gdk-pixbuf/gdk-pixdata.h>
37 #include "viktreeview.h"
38 #include "vikviewport.h"
40 #include "vikmapslayer.h"
41 #include "vikmapslayer_pixmap.h"
45 #include <sys/types.h>
48 /* only for dialog.h -- ugh */
49 #include "vikwaypoint.h"
52 #include "vikstatus.h"
53 #include "background.h"
55 #include "vikaggregatelayer.h"
56 #include "viklayerspanel.h"
59 #include "terraserver.h"
61 #include "icons/icons.h"
63 /****** MAP TYPES ******/
65 static GList *__map_types = NULL;
67 #define NUM_MAP_TYPES g_list_length(__map_types)
69 /* List of label for each map type */
70 static GList *params_maptypes = NULL;
72 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
73 static GList *params_maptypes_ids = NULL;
75 /******** MAPZOOMS *********/
77 static gchar *params_mapzooms[] = { "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 };
78 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 };
79 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 };
81 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
83 /**************************/
86 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
87 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
88 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
89 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp );
90 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id );
91 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
92 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
93 static void maps_layer_free ( VikMapsLayer *vml );
94 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
95 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
96 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
97 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
98 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
99 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
102 static VikLayerParamScale params_scales[] = {
103 /* min, max, step, digits (decimal places) */
104 { 0, 255, 3, 0 }, /* alpha */
107 VikLayerParam maps_layer_params[] = {
108 { "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Map Type:", VIK_LAYER_WIDGET_RADIOGROUP, NULL, NULL },
109 { "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, "Maps Directory (Optional):", VIK_LAYER_WIDGET_FILEENTRY },
110 { "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Alpha:", VIK_LAYER_WIDGET_HSCALE, params_scales },
111 { "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, "Autodownload maps:", VIK_LAYER_WIDGET_CHECKBUTTON },
112 { "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Zoom Level:", VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms },
115 enum { PARAM_MAPTYPE=0, PARAM_CACHE_DIR, PARAM_ALPHA, PARAM_AUTODOWNLOAD, PARAM_MAPZOOM, NUM_PARAMS };
117 static VikToolInterface maps_tools[] = {
118 { "Maps Download", (VikToolConstructorFunc) maps_layer_download_create, NULL, NULL, NULL,
119 (VikToolMouseFunc) maps_layer_download_click, NULL, (VikToolMouseFunc) maps_layer_download_release,
120 (VikToolKeyFunc) NULL, &cursor_mapdl },
123 VikLayerInterface vik_maps_layer_interface = {
128 sizeof(maps_tools) / sizeof(maps_tools[0]),
137 (VikLayerFuncCreate) maps_layer_new,
138 (VikLayerFuncRealize) NULL,
139 maps_layer_post_read,
140 (VikLayerFuncFree) maps_layer_free,
142 (VikLayerFuncProperties) NULL,
143 (VikLayerFuncDraw) maps_layer_draw,
144 (VikLayerFuncChangeCoordMode) NULL,
146 (VikLayerFuncSetMenuItemsSelection) NULL,
147 (VikLayerFuncGetMenuItemsSelection) NULL,
149 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
150 (VikLayerFuncSublayerAddMenuItems) NULL,
152 (VikLayerFuncSublayerRenameRequest) NULL,
153 (VikLayerFuncSublayerToggleVisible) NULL,
155 (VikLayerFuncMarshall) maps_layer_marshall,
156 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
158 (VikLayerFuncSetParam) maps_layer_set_param,
159 (VikLayerFuncGetParam) maps_layer_get_param,
161 (VikLayerFuncReadFileData) NULL,
162 (VikLayerFuncWriteFileData) NULL,
164 (VikLayerFuncDeleteItem) NULL,
165 (VikLayerFuncCopyItem) NULL,
166 (VikLayerFuncPasteItem) NULL,
167 (VikLayerFuncFreeCopiedItem) NULL,
168 (VikLayerFuncDragDropRequest) NULL,
171 struct _VikMapsLayer {
177 gdouble xmapzoom, ymapzoom;
179 gboolean autodownload;
180 VikCoord *last_center;
184 gint dl_tool_x, dl_tool_y;
186 GtkMenu *dl_right_click_menu;
187 VikCoord redownload_ul, redownload_br; /* right click menu only */
188 VikViewport *redownload_vvp;
191 enum { REDOWNLOAD_NONE = 0, REDOWNLOAD_BAD, REDOWNLOAD_ALL, DOWNLOAD_OR_REFRESH };
194 /****************************************/
195 /******** MAPS LAYER TYPES **************/
196 /****************************************/
198 void maps_layer_register_type ( const char *label, guint id, VikMapsLayer_MapType *map_type )
200 g_assert(label != NULL);
201 g_assert(map_type != NULL);
202 g_assert(id == map_type->uniq_id);
205 params_maptypes = g_list_append(params_maptypes, g_strdup(label));
208 params_maptypes_ids = g_list_append(params_maptypes_ids, (gpointer)id);
210 /* We have to clone */
211 VikMapsLayer_MapType *clone = g_memdup(map_type, sizeof(VikMapsLayer_MapType));
212 /* Register the clone in the list */
213 __map_types = g_list_append(__map_types, clone);
216 We have to ensure the mode LayerParam reference the up-to-date
220 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
221 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
223 maps_layer_params[0].widget_data = params_maptypes;
224 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
227 #define MAPS_LAYER_NTH_LABEL(n) ((gchar*)g_list_nth_data(params_maptypes, (n)))
228 #define MAPS_LAYER_NTH_ID(n) ((guint)g_list_nth_data(params_maptypes_ids, (n)))
229 #define MAPS_LAYER_NTH_TYPE(n) ((VikMapsLayer_MapType*)g_list_nth_data(__map_types, (n)))
231 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
233 return(vml->maptype);
236 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
238 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
241 /****************************************/
242 /******** CACHE DIR STUFF ***************/
243 /****************************************/
245 #define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
247 #define MAPS_CACHE_DIR "C:\\VIKING-MAPS\\"
252 #define MAPS_CACHE_DIR maps_layer_default_dir()
253 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
255 gchar *maps_layer_default_dir ()
257 static gchar *defaultdir = NULL;
260 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
261 const gchar *mapdir = g_getenv("VIKING_MAPS");
263 defaultdir = g_strdup ( mapdir );
264 } else if ( access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
265 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
267 const gchar *home = g_getenv("HOME");
268 if (!home || access(home, W_OK))
269 home = g_get_home_dir ();
271 defaultdir = g_build_filename ( home, ".viking-maps", NULL );
273 defaultdir = g_strdup ( ".viking-maps" );
275 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
277 /* Add the separator at the end */
278 gchar *tmp = defaultdir;
279 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
282 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
289 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
291 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && access ( vml->cache_dir, F_OK ) != 0 )
294 mkdir ( vml->cache_dir );
296 mkdir ( vml->cache_dir, 0777 );
301 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
304 g_assert ( vml != NULL);
305 g_free ( vml->cache_dir );
306 vml->cache_dir = NULL;
308 if ( dir == NULL || dir[0] == '\0' )
309 vml->cache_dir = g_strdup ( MAPS_CACHE_DIR );
313 if ( dir[len-1] != G_DIR_SEPARATOR )
315 vml->cache_dir = g_malloc ( len+2 );
316 strncpy ( vml->cache_dir, dir, len );
317 vml->cache_dir[len] = G_DIR_SEPARATOR;
318 vml->cache_dir[len+1] = '\0';
321 vml->cache_dir = g_strdup ( dir );
323 maps_layer_mkdir_if_default_dir ( vml );
326 /****************************************/
327 /******** GOBJECT STUFF *****************/
328 /****************************************/
330 GType vik_maps_layer_get_type ()
332 static GType vml_type = 0;
336 static const GTypeInfo vml_info =
338 sizeof (VikMapsLayerClass),
339 NULL, /* base_init */
340 NULL, /* base_finalize */
341 NULL, /* class init */
342 NULL, /* class_finalize */
343 NULL, /* class_data */
344 sizeof (VikMapsLayer),
346 NULL /* instance init */
348 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
354 /****************************************/
355 /************** PARAMETERS **************/
356 /****************************************/
358 static guint map_index_to_uniq_id (guint8 index)
360 g_assert ( index < NUM_MAP_TYPES );
361 return MAPS_LAYER_NTH_TYPE(index)->uniq_id;
364 static guint map_uniq_id_to_index ( guint uniq_id )
367 for ( i = 0; i < NUM_MAP_TYPES; i++ )
368 if ( MAPS_LAYER_NTH_TYPE(i)->uniq_id == uniq_id )
370 return NUM_MAP_TYPES; /* no such thing */
373 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp )
377 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
378 case PARAM_MAPTYPE: {
379 gint maptype = map_uniq_id_to_index(data.u);
380 if ( maptype == NUM_MAP_TYPES ) g_warning("Unknown map type");
381 else vml->maptype = maptype;
384 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
385 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
386 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
387 vml->mapzoom_id = data.u;
388 vml->xmapzoom = __mapzooms_x [data.u];
389 vml->ymapzoom = __mapzooms_y [data.u];
390 }else g_warning ("Unknown Map Zoom"); break;
395 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id )
397 VikLayerParamData rv;
400 case PARAM_CACHE_DIR: rv.s = (vml->cache_dir && strcmp(vml->cache_dir, MAPS_CACHE_DIR) != 0) ? vml->cache_dir : ""; break;
401 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
402 case PARAM_ALPHA: rv.u = vml->alpha; break;
403 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
404 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
409 /****************************************/
410 /****** CREATING, COPYING, FREEING ******/
411 /****************************************/
413 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
416 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
417 vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS );
418 idx = map_uniq_id_to_index(7); /* 7 is id for google maps */
419 vml->maptype = (idx < NUM_MAP_TYPES) ? idx : 0;
422 vml->dl_tool_x = vml->dl_tool_y = -1;
423 maps_layer_set_cache_dir ( vml, NULL );
424 vml->autodownload = FALSE;
425 vml->last_center = NULL;
426 vml->last_xmpp = 0.0;
427 vml->last_ympp = 0.0;
429 vml->dl_right_click_menu = NULL;
434 static void maps_layer_free ( VikMapsLayer *vml )
436 g_free ( vml->cache_dir );
437 vml->cache_dir = NULL;
438 if ( vml->dl_right_click_menu )
439 gtk_object_sink ( GTK_OBJECT(vml->dl_right_click_menu) );
440 g_free(vml->last_center);
441 vml->last_center = NULL;
444 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
446 if (from_file != TRUE)
448 /* If this method is not called in file reading context
449 * it is called in GUI context.
450 * So, we can check if we have to inform the user about inconsistency */
451 VikViewportDrawMode vp_drawmode;
452 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
453 VikMapsLayer_MapType *map_type = NULL;
455 vp_drawmode = vik_viewport_get_drawmode ( VIK_VIEWPORT(vp) );
456 map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
457 if (map_type->drawmode != vp_drawmode) {
458 const gchar *drawmode_name = vik_viewport_get_drawmode_name (VIK_VIEWPORT(vp), map_type->drawmode);
459 gchar *msg = g_strdup_printf("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it.", drawmode_name);
460 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), msg );
466 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
468 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
471 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
473 VikMapsLayer *rv = maps_layer_new ( vvp );
474 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
478 /*********************/
479 /****** DRAWING ******/
480 /*********************/
482 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
485 gint width, height, iii, jjj;
487 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
489 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
490 g_object_unref(G_OBJECT(pixbuf));
494 pixels = gdk_pixbuf_get_pixels(pixbuf);
495 width = gdk_pixbuf_get_width(pixbuf);
496 height = gdk_pixbuf_get_height(pixbuf);
498 /* r,g,b,a,r,g,b,a.... */
499 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
507 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
510 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
511 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
512 g_object_unref ( G_OBJECT(pixbuf) );
516 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
521 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
522 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
525 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
526 vml->cache_dir, mode,
527 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
528 if ( access ( filename_buf, R_OK ) == 0) {
531 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
535 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
536 g_warning ( "Couldn't open image file: %s", gx->message );
540 g_object_unref ( G_OBJECT(pixbuf) );
543 if ( vml->alpha < 255 )
544 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
545 if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
546 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
548 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
549 mapcoord->z, MAPS_LAYER_NTH_TYPE(vml->maptype)->uniq_id,
550 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
558 gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
560 const VikCoord *center = vik_viewport_get_center ( vvp );
562 if (vml->last_center == NULL) {
563 VikCoord *new_center = g_malloc(sizeof(VikCoord));
564 *new_center = *center;
565 vml->last_center = new_center;
566 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
567 vml->last_ympp = vik_viewport_get_ympp(vvp);
571 /* TODO: perhaps vik_coord_diff() */
572 if (vik_coord_equals(vml->last_center, center)
573 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
574 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
577 *(vml->last_center) = *center;
578 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
579 vml->last_ympp = vik_viewport_get_ympp(vvp);
583 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
586 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
587 gdouble yzoom = vik_viewport_get_ympp ( vvp );
588 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
589 gdouble existence_only = FALSE;
591 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
592 xshrinkfactor = vml->xmapzoom / xzoom;
593 yshrinkfactor = vml->ymapzoom / yzoom;
594 xzoom = vml->xmapzoom;
595 yzoom = vml->xmapzoom;
596 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
597 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
598 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR )
599 existence_only = TRUE;
601 g_warning ( "Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor", (int)( 1.0/REAL_MIN_SHRINKFACTOR));
608 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
609 if ( map_type->coord_to_mapcoord ( ul, xzoom, yzoom, &ulm ) &&
610 map_type->coord_to_mapcoord ( br, xzoom, yzoom, &brm ) ) {
614 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
615 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
616 gint mode = map_type->uniq_id;
619 gint xx, yy, width, height;
622 guint max_path_len = strlen(vml->cache_dir) + 40;
623 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
625 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
627 fputs(stderr, "DEBUG: Starting autodownload\n");
629 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
632 if ( map_type->tilesize_x == 0 && !existence_only ) {
633 for ( x = xmin; x <= xmax; x++ ) {
634 for ( y = ymin; y <= ymax; y++ ) {
637 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
639 width = gdk_pixbuf_get_width ( pixbuf );
640 height = gdk_pixbuf_get_height ( pixbuf );
642 map_type->mapcoord_to_center_coord ( &ulm, &coord );
643 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
647 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
651 } else { /* tilesize is known, don't have to keep converting coords */
652 gdouble tilesize_x = map_type->tilesize_x * xshrinkfactor;
653 gdouble tilesize_y = map_type->tilesize_y * yshrinkfactor;
654 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
655 gint tilesize_x_ceil = ceil ( tilesize_x );
656 gint tilesize_y_ceil = ceil ( tilesize_y );
657 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
658 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
659 gdouble xx, yy; gint xx_tmp, yy_tmp;
660 gint base_yy, xend, yend;
662 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
664 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
665 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
667 map_type->mapcoord_to_center_coord ( &ulm, &coord );
668 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
669 xx = xx_tmp; yy = yy_tmp;
670 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
671 * eg if tile size 128, shrinkfactor 0.333 */
672 xx -= (tilesize_x/2);
673 base_yy = yy - (tilesize_y/2);
675 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
677 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
681 if ( existence_only ) {
682 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
683 vml->cache_dir, mode,
684 ulm.scale, ulm.z, ulm.x, ulm.y );
685 if ( access ( path_buf, F_OK ) == 0 ) {
686 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
689 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
691 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
704 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
706 if ( MAPS_LAYER_NTH_TYPE(vml->maptype)->drawmode == vik_viewport_get_drawmode ( vvp ) )
710 /* get corner coords */
711 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
712 /* UTM multi-zone stuff by Kit Transue */
713 gchar leftmost_zone, rightmost_zone, i;
714 leftmost_zone = vik_viewport_leftmost_zone( vvp );
715 rightmost_zone = vik_viewport_rightmost_zone( vvp );
716 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
717 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
718 maps_layer_draw_section ( vml, vvp, &ul, &br );
722 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
723 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
725 maps_layer_draw_section ( vml, vvp, &ul, &br );
730 /*************************/
731 /****** DOWNLOADING ******/
732 /*************************/
734 /* pass along data to thread, exists even if layer is deleted. */
744 gboolean refresh_display;
747 gboolean map_layer_alive;
751 static void mdi_free ( MapDownloadInfo *mdi )
753 g_mutex_free(mdi->mutex);
754 g_free ( mdi->cache_dir );
755 mdi->cache_dir = NULL;
756 g_free ( mdi->filename_buf );
757 mdi->filename_buf = NULL;
761 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
763 MapDownloadInfo *mdi = ptr;
764 g_mutex_lock(mdi->mutex);
765 mdi->map_layer_alive = FALSE;
766 g_mutex_unlock(mdi->mutex);
769 static void map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
773 for ( x = mdi->x0; x <= mdi->xf; x++ )
775 for ( y = mdi->y0; y <= mdi->yf; y++ )
777 gboolean remove_mem_cache = FALSE;
778 gboolean need_download = FALSE;
779 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
780 mdi->cache_dir, MAPS_LAYER_NTH_TYPE(mdi->maptype)->uniq_id,
781 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
784 a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
786 if ( mdi->redownload == REDOWNLOAD_ALL)
787 remove ( mdi->filename_buf );
789 else if ( (mdi->redownload == REDOWNLOAD_BAD) && (access ( mdi->filename_buf, F_OK ) == 0) )
791 /* see if this one is bad or what */
793 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
795 remove ( mdi->filename_buf );
797 g_object_unref ( pixbuf );
802 if ( access ( mdi->filename_buf, F_OK ) != 0 )
804 need_download = TRUE;
805 if (( mdi->redownload != REDOWNLOAD_NONE ) &&
806 ( mdi->redownload != DOWNLOAD_OR_REFRESH ))
807 remove_mem_cache = TRUE;
808 } else if ( mdi->redownload == DOWNLOAD_OR_REFRESH ) {
809 remove_mem_cache = TRUE;
813 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
816 if ( MAPS_LAYER_NTH_TYPE(mdi->maptype)->download ( &(mdi->mapcoord), mdi->filename_buf ))
821 g_mutex_lock(mdi->mutex);
822 if (remove_mem_cache)
823 a_mapcache_remove_all_shrinkfactors ( x, y, mdi->mapcoord.z, MAPS_LAYER_NTH_TYPE(mdi->maptype)->uniq_id, mdi->mapcoord.scale );
824 if (mdi->refresh_display && mdi->map_layer_alive) {
825 /* TODO: check if it's on visible area */
826 vik_layer_emit_update ( VIK_LAYER(mdi->vml) );
828 g_mutex_unlock(mdi->mutex);
830 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
834 g_mutex_lock(mdi->mutex);
835 if (mdi->map_layer_alive)
836 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
837 g_mutex_unlock(mdi->mutex);
840 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
842 if ( mdi->mapcoord.x || mdi->mapcoord.y )
844 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
845 mdi->cache_dir, MAPS_LAYER_NTH_TYPE(mdi->maptype)->uniq_id,
846 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
847 if ( access ( mdi->filename_buf, F_OK ) == 0)
849 remove ( mdi->filename_buf );
854 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
856 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
857 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
859 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
860 if ( map_type->coord_to_mapcoord ( ul, xzoom, yzoom, &ulm )
861 && map_type->coord_to_mapcoord ( br, xzoom, yzoom, &brm ) )
863 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
868 mdi->map_layer_alive = TRUE;
869 mdi->mutex = g_mutex_new();
870 mdi->refresh_display = TRUE;
872 /* cache_dir and buffer for dest filename */
873 mdi->cache_dir = g_strdup ( vml->cache_dir );
874 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
875 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
876 mdi->maptype = vml->maptype;
880 mdi->redownload = redownload;
882 mdi->x0 = MIN(ulm.x, brm.x);
883 mdi->xf = MAX(ulm.x, brm.x);
884 mdi->y0 = MIN(ulm.y, brm.y);
885 mdi->yf = MAX(ulm.y, brm.y);
889 if ( mdi->redownload ) {
890 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
892 /* calculate how many we need */
893 for ( a = mdi->x0; a <= mdi->xf; a++ )
895 for ( b = mdi->y0; b <= mdi->yf; b++ )
897 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
898 vml->cache_dir, map_type->uniq_id, ulm.scale,
900 if ( access ( mdi->filename_buf, F_OK ) != 0)
906 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
908 if ( mdi->mapstoget )
910 gchar *tmp = g_strdup_printf ( "%s %s%d %s %s...", redownload ? "Redownloading" : "Downloading", redownload == REDOWNLOAD_BAD ? "up to " : "", mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype), (mdi->mapstoget == 1) ? "map" : "maps" );
912 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
913 /* launch the thread */
914 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
915 tmp, /* description string */
916 (vik_thr_func) map_download_thread, /* function to call within thread */
917 mdi, /* pass along data */
918 (vik_thr_free_func) mdi_free, /* function to free pass along data */
919 (vik_thr_free_func) mdi_cancel_cleanup,
928 void maps_layer_download_section_without_redraw( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
931 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
933 if (!map_type->coord_to_mapcoord(ul, zoom, zoom, &ulm)
934 || !map_type->coord_to_mapcoord(br, zoom, zoom, &brm)) {
935 g_warning("%s() coord_to_mapcoord() failed\n", __PRETTY_FUNCTION__);
939 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
944 mdi->map_layer_alive = TRUE;
945 mdi->mutex = g_mutex_new();
946 mdi->refresh_display = FALSE;
948 mdi->cache_dir = g_strdup ( vml->cache_dir );
949 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
950 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
951 mdi->maptype = vml->maptype;
955 mdi->redownload = REDOWNLOAD_NONE;
957 mdi->x0 = MIN(ulm.x, brm.x);
958 mdi->xf = MAX(ulm.x, brm.x);
959 mdi->y0 = MIN(ulm.y, brm.y);
960 mdi->yf = MAX(ulm.y, brm.y);
964 for (i = mdi->x0; i <= mdi->xf; i++) {
965 for (j = mdi->y0; j <= mdi->yf; j++) {
966 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
967 vml->cache_dir, map_type->uniq_id, ulm.scale,
969 if ( access ( mdi->filename_buf, F_OK ) != 0)
974 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
976 if (mdi->mapstoget) {
977 gchar *tmp = g_strdup_printf ( "%s %d %s %s...", "Downloading", mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype), (mdi->mapstoget == 1) ? "map" : "maps" );
979 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
980 /* launch the thread */
981 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
982 tmp, /* description string */
983 (vik_thr_func) map_download_thread, /* function to call within thread */
984 mdi, /* pass along data */
985 (vik_thr_free_func) mdi_free, /* function to free pass along data */
986 (vik_thr_free_func) mdi_cancel_cleanup,
994 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
996 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
998 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1000 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1003 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1005 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1007 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1009 if ( event->button == 1 )
1012 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 );
1013 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 );
1014 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1015 vml->dl_tool_x = vml->dl_tool_y = -1;
1020 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) );
1021 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) );
1023 vml->redownload_vvp = vvp;
1025 vml->dl_tool_x = vml->dl_tool_y = -1;
1027 if ( ! vml->dl_right_click_menu ) {
1029 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1031 item = gtk_menu_item_new_with_label ( "Redownload bad map(s)" );
1032 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1033 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1035 item = gtk_menu_item_new_with_label ( "Redownload all map(s)" );
1036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1037 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1040 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1041 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1047 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1052 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1055 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1057 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
1058 if ( map_type->drawmode == vik_viewport_get_drawmode ( vvp ) &&
1059 map_type->coord_to_mapcoord ( vik_viewport_get_center ( vvp ),
1060 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1061 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1063 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1070 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1074 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1075 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1076 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1077 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1079 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1080 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1081 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1083 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1084 g_free ( filename_buf );
1085 vik_layer_emit_update ( VIK_LAYER(vml) );
1093 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1095 VikMapsLayer *vml = vml_vvp[0];
1096 VikViewport *vvp = vml_vvp[1];
1097 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1099 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1100 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1105 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1106 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1108 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
1109 if ( map_type->drawmode == vp_drawmode &&
1110 map_type->coord_to_mapcoord ( &ul, xzoom, yzoom, &ulm ) &&
1111 map_type->coord_to_mapcoord ( &br, xzoom, yzoom, &brm ) )
1112 start_download_thread ( vml, vvp, &ul, &br, redownload );
1113 else if (map_type->drawmode != vp_drawmode) {
1114 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, map_type->drawmode);
1115 gchar *err = g_strdup_printf("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again.", drawmode_name);
1116 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1120 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), "Wrong zoom level for this map." );
1124 static void maps_layer_download_onscreen_maps ( gpointer vml_vvp[2] )
1126 download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1129 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1131 download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1134 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1136 static gpointer pass_along[2];
1138 pass_along[0] = vml;
1139 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1141 item = gtk_menu_item_new();
1142 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1143 gtk_widget_show ( item );
1145 item = gtk_menu_item_new_with_label ( "Download Onscreen Maps" );
1146 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_onscreen_maps), pass_along );
1147 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1148 gtk_widget_show ( item );
1150 item = gtk_menu_item_new_with_label ( "Refresh Onscreen Tiles" );
1151 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1152 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1153 gtk_widget_show ( item );