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>
35 #include <glib/gi18n.h>
43 #include "viktreeview.h"
44 #include "vikviewport.h"
46 #include "vikmapslayer.h"
47 #include "vikmapslayer_pixmap.h"
51 #include <sys/types.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 },
129 VikLayerInterface vik_maps_layer_interface = {
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, VikMapsLayer_MapType *map_type )
206 g_assert(label != NULL);
207 g_assert(map_type != NULL);
208 g_assert(id == map_type->uniq_id);
211 params_maptypes = g_list_append(params_maptypes, g_strdup(label));
214 params_maptypes_ids = g_list_append(params_maptypes_ids, (gpointer)id);
216 /* We have to clone */
217 VikMapsLayer_MapType *clone = g_memdup(map_type, sizeof(VikMapsLayer_MapType));
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) ((VikMapsLayer_MapType*)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"
253 #define MAPS_CACHE_DIR "C:\\VIKING-MAPS\\"
258 #define MAPS_CACHE_DIR maps_layer_default_dir()
259 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
261 gchar *maps_layer_default_dir ()
263 static gchar *defaultdir = NULL;
266 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
267 const gchar *mapdir = g_getenv("VIKING_MAPS");
269 defaultdir = g_strdup ( mapdir );
270 } else if ( access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
271 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
273 const gchar *home = g_getenv("HOME");
274 if (!home || access(home, W_OK))
275 home = g_get_home_dir ();
277 defaultdir = g_build_filename ( home, ".viking-maps", NULL );
279 defaultdir = g_strdup ( ".viking-maps" );
281 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
283 /* Add the separator at the end */
284 gchar *tmp = defaultdir;
285 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
288 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
295 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
297 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && access ( vml->cache_dir, F_OK ) != 0 )
300 mkdir ( vml->cache_dir );
302 mkdir ( vml->cache_dir, 0777 );
307 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
310 g_assert ( vml != NULL);
311 g_free ( vml->cache_dir );
312 vml->cache_dir = NULL;
314 if ( dir == NULL || dir[0] == '\0' )
315 vml->cache_dir = g_strdup ( MAPS_CACHE_DIR );
319 if ( dir[len-1] != G_DIR_SEPARATOR )
321 vml->cache_dir = g_malloc ( len+2 );
322 strncpy ( vml->cache_dir, dir, len );
323 vml->cache_dir[len] = G_DIR_SEPARATOR;
324 vml->cache_dir[len+1] = '\0';
327 vml->cache_dir = g_strdup ( dir );
329 maps_layer_mkdir_if_default_dir ( vml );
332 /****************************************/
333 /******** GOBJECT STUFF *****************/
334 /****************************************/
336 GType vik_maps_layer_get_type ()
338 static GType vml_type = 0;
342 static const GTypeInfo vml_info =
344 sizeof (VikMapsLayerClass),
345 NULL, /* base_init */
346 NULL, /* base_finalize */
347 NULL, /* class init */
348 NULL, /* class_finalize */
349 NULL, /* class_data */
350 sizeof (VikMapsLayer),
352 NULL /* instance init */
354 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
360 /****************************************/
361 /************** PARAMETERS **************/
362 /****************************************/
364 static guint map_index_to_uniq_id (guint8 index)
366 g_assert ( index < NUM_MAP_TYPES );
367 return MAPS_LAYER_NTH_TYPE(index)->uniq_id;
370 static guint map_uniq_id_to_index ( guint uniq_id )
373 for ( i = 0; i < NUM_MAP_TYPES; i++ )
374 if ( MAPS_LAYER_NTH_TYPE(i)->uniq_id == uniq_id )
376 return NUM_MAP_TYPES; /* no such thing */
379 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp )
383 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
384 case PARAM_MAPTYPE: {
385 gint maptype = map_uniq_id_to_index(data.u);
386 if ( maptype == NUM_MAP_TYPES ) g_warning(_("Unknown map type"));
387 else vml->maptype = maptype;
390 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
391 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
392 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
393 vml->mapzoom_id = data.u;
394 vml->xmapzoom = __mapzooms_x [data.u];
395 vml->ymapzoom = __mapzooms_y [data.u];
396 }else g_warning (_("Unknown Map Zoom")); break;
401 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id )
403 VikLayerParamData rv;
406 case PARAM_CACHE_DIR: rv.s = (vml->cache_dir && strcmp(vml->cache_dir, MAPS_CACHE_DIR) != 0) ? vml->cache_dir : ""; break;
407 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
408 case PARAM_ALPHA: rv.u = vml->alpha; break;
409 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
410 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
415 /****************************************/
416 /****** CREATING, COPYING, FREEING ******/
417 /****************************************/
419 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
422 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
423 vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS );
424 idx = map_uniq_id_to_index(7); /* 7 is id for google maps */
425 vml->maptype = (idx < NUM_MAP_TYPES) ? idx : 0;
428 vml->dl_tool_x = vml->dl_tool_y = -1;
429 maps_layer_set_cache_dir ( vml, NULL );
430 vml->autodownload = FALSE;
431 vml->last_center = NULL;
432 vml->last_xmpp = 0.0;
433 vml->last_ympp = 0.0;
435 vml->dl_right_click_menu = NULL;
440 static void maps_layer_free ( VikMapsLayer *vml )
442 g_free ( vml->cache_dir );
443 vml->cache_dir = NULL;
444 if ( vml->dl_right_click_menu )
445 gtk_object_sink ( GTK_OBJECT(vml->dl_right_click_menu) );
446 g_free(vml->last_center);
447 vml->last_center = NULL;
450 static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
452 if (from_file != TRUE)
454 /* If this method is not called in file reading context
455 * it is called in GUI context.
456 * So, we can check if we have to inform the user about inconsistency */
457 VikViewportDrawMode vp_drawmode;
458 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
459 VikMapsLayer_MapType *map_type = NULL;
461 vp_drawmode = vik_viewport_get_drawmode ( VIK_VIEWPORT(vp) );
462 map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
463 if (map_type->drawmode != vp_drawmode) {
464 const gchar *drawmode_name = vik_viewport_get_drawmode_name (VIK_VIEWPORT(vp), map_type->drawmode);
465 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
466 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), msg );
472 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
474 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
477 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
479 VikMapsLayer *rv = maps_layer_new ( vvp );
480 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
484 /*********************/
485 /****** DRAWING ******/
486 /*********************/
488 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
491 gint width, height, iii, jjj;
493 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
495 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
496 g_object_unref(G_OBJECT(pixbuf));
500 pixels = gdk_pixbuf_get_pixels(pixbuf);
501 width = gdk_pixbuf_get_width(pixbuf);
502 height = gdk_pixbuf_get_height(pixbuf);
504 /* r,g,b,a,r,g,b,a.... */
505 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
513 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
516 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
517 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
518 g_object_unref ( G_OBJECT(pixbuf) );
522 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
527 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
528 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
531 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
532 vml->cache_dir, mode,
533 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
534 if ( access ( filename_buf, R_OK ) == 0) {
537 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
541 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
542 g_warning ( _("Couldn't open image file: %s"), gx->message );
546 g_object_unref ( G_OBJECT(pixbuf) );
549 if ( vml->alpha < 255 )
550 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
551 if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
552 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
554 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
555 mapcoord->z, MAPS_LAYER_NTH_TYPE(vml->maptype)->uniq_id,
556 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
564 gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
566 const VikCoord *center = vik_viewport_get_center ( vvp );
568 if (vml->last_center == NULL) {
569 VikCoord *new_center = g_malloc(sizeof(VikCoord));
570 *new_center = *center;
571 vml->last_center = new_center;
572 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
573 vml->last_ympp = vik_viewport_get_ympp(vvp);
577 /* TODO: perhaps vik_coord_diff() */
578 if (vik_coord_equals(vml->last_center, center)
579 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
580 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
583 *(vml->last_center) = *center;
584 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
585 vml->last_ympp = vik_viewport_get_ympp(vvp);
589 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
592 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
593 gdouble yzoom = vik_viewport_get_ympp ( vvp );
594 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
595 gdouble existence_only = FALSE;
597 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
598 xshrinkfactor = vml->xmapzoom / xzoom;
599 yshrinkfactor = vml->ymapzoom / yzoom;
600 xzoom = vml->xmapzoom;
601 yzoom = vml->xmapzoom;
602 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
603 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
604 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR )
605 existence_only = TRUE;
607 g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
614 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
615 if ( map_type->coord_to_mapcoord ( ul, xzoom, yzoom, &ulm ) &&
616 map_type->coord_to_mapcoord ( br, xzoom, yzoom, &brm ) ) {
620 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
621 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
622 gint mode = map_type->uniq_id;
625 gint xx, yy, width, height;
628 guint max_path_len = strlen(vml->cache_dir) + 40;
629 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
631 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
633 fputs(stderr, "DEBUG: Starting autodownload\n");
635 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
638 if ( map_type->tilesize_x == 0 && !existence_only ) {
639 for ( x = xmin; x <= xmax; x++ ) {
640 for ( y = ymin; y <= ymax; y++ ) {
643 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
645 width = gdk_pixbuf_get_width ( pixbuf );
646 height = gdk_pixbuf_get_height ( pixbuf );
648 map_type->mapcoord_to_center_coord ( &ulm, &coord );
649 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
653 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
657 } else { /* tilesize is known, don't have to keep converting coords */
658 gdouble tilesize_x = map_type->tilesize_x * xshrinkfactor;
659 gdouble tilesize_y = map_type->tilesize_y * yshrinkfactor;
660 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
661 gint tilesize_x_ceil = ceil ( tilesize_x );
662 gint tilesize_y_ceil = ceil ( tilesize_y );
663 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
664 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
665 gdouble xx, yy; gint xx_tmp, yy_tmp;
666 gint base_yy, xend, yend;
668 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
670 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
671 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
673 map_type->mapcoord_to_center_coord ( &ulm, &coord );
674 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
675 xx = xx_tmp; yy = yy_tmp;
676 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
677 * eg if tile size 128, shrinkfactor 0.333 */
678 xx -= (tilesize_x/2);
679 base_yy = yy - (tilesize_y/2);
681 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
683 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
687 if ( existence_only ) {
688 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
689 vml->cache_dir, mode,
690 ulm.scale, ulm.z, ulm.x, ulm.y );
691 if ( access ( path_buf, F_OK ) == 0 ) {
692 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
695 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
697 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
710 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
712 if ( MAPS_LAYER_NTH_TYPE(vml->maptype)->drawmode == vik_viewport_get_drawmode ( vvp ) )
716 /* get corner coords */
717 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
718 /* UTM multi-zone stuff by Kit Transue */
719 gchar leftmost_zone, rightmost_zone, i;
720 leftmost_zone = vik_viewport_leftmost_zone( vvp );
721 rightmost_zone = vik_viewport_rightmost_zone( vvp );
722 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
723 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
724 maps_layer_draw_section ( vml, vvp, &ul, &br );
728 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
729 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
731 maps_layer_draw_section ( vml, vvp, &ul, &br );
736 /*************************/
737 /****** DOWNLOADING ******/
738 /*************************/
740 /* pass along data to thread, exists even if layer is deleted. */
750 gboolean refresh_display;
753 gboolean map_layer_alive;
757 static void mdi_free ( MapDownloadInfo *mdi )
759 g_mutex_free(mdi->mutex);
760 g_free ( mdi->cache_dir );
761 mdi->cache_dir = NULL;
762 g_free ( mdi->filename_buf );
763 mdi->filename_buf = NULL;
767 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
769 MapDownloadInfo *mdi = ptr;
770 g_mutex_lock(mdi->mutex);
771 mdi->map_layer_alive = FALSE;
772 g_mutex_unlock(mdi->mutex);
775 static void map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
779 for ( x = mdi->x0; x <= mdi->xf; x++ )
781 for ( y = mdi->y0; y <= mdi->yf; y++ )
783 gboolean remove_mem_cache = FALSE;
784 gboolean need_download = FALSE;
785 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
786 mdi->cache_dir, MAPS_LAYER_NTH_TYPE(mdi->maptype)->uniq_id,
787 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
790 a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
792 if ( mdi->redownload == REDOWNLOAD_ALL)
793 remove ( mdi->filename_buf );
795 else if ( (mdi->redownload == REDOWNLOAD_BAD) && (access ( mdi->filename_buf, F_OK ) == 0) )
797 /* see if this one is bad or what */
799 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
801 remove ( mdi->filename_buf );
803 g_object_unref ( pixbuf );
808 if ( access ( mdi->filename_buf, F_OK ) != 0 )
810 need_download = TRUE;
811 if (( mdi->redownload != REDOWNLOAD_NONE ) &&
812 ( mdi->redownload != DOWNLOAD_OR_REFRESH ))
813 remove_mem_cache = TRUE;
814 } else if ( mdi->redownload == DOWNLOAD_OR_REFRESH ) {
815 remove_mem_cache = TRUE;
819 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
822 if ( MAPS_LAYER_NTH_TYPE(mdi->maptype)->download ( &(mdi->mapcoord), mdi->filename_buf ))
827 g_mutex_lock(mdi->mutex);
828 if (remove_mem_cache)
829 a_mapcache_remove_all_shrinkfactors ( x, y, mdi->mapcoord.z, MAPS_LAYER_NTH_TYPE(mdi->maptype)->uniq_id, mdi->mapcoord.scale );
830 if (mdi->refresh_display && mdi->map_layer_alive) {
831 /* TODO: check if it's on visible area */
832 vik_layer_emit_update ( VIK_LAYER(mdi->vml) );
834 g_mutex_unlock(mdi->mutex);
836 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
840 g_mutex_lock(mdi->mutex);
841 if (mdi->map_layer_alive)
842 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
843 g_mutex_unlock(mdi->mutex);
846 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
848 if ( mdi->mapcoord.x || mdi->mapcoord.y )
850 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
851 mdi->cache_dir, MAPS_LAYER_NTH_TYPE(mdi->maptype)->uniq_id,
852 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
853 if ( access ( mdi->filename_buf, F_OK ) == 0)
855 remove ( mdi->filename_buf );
860 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
862 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
863 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
865 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
866 if ( map_type->coord_to_mapcoord ( ul, xzoom, yzoom, &ulm )
867 && map_type->coord_to_mapcoord ( br, xzoom, yzoom, &brm ) )
869 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
874 mdi->map_layer_alive = TRUE;
875 mdi->mutex = g_mutex_new();
876 mdi->refresh_display = TRUE;
878 /* cache_dir and buffer for dest filename */
879 mdi->cache_dir = g_strdup ( vml->cache_dir );
880 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
881 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
882 mdi->maptype = vml->maptype;
886 mdi->redownload = redownload;
888 mdi->x0 = MIN(ulm.x, brm.x);
889 mdi->xf = MAX(ulm.x, brm.x);
890 mdi->y0 = MIN(ulm.y, brm.y);
891 mdi->yf = MAX(ulm.y, brm.y);
895 if ( mdi->redownload ) {
896 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
898 /* calculate how many we need */
899 for ( a = mdi->x0; a <= mdi->xf; a++ )
901 for ( b = mdi->y0; b <= mdi->yf; b++ )
903 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
904 vml->cache_dir, map_type->uniq_id, ulm.scale,
906 if ( access ( mdi->filename_buf, F_OK ) != 0)
912 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
914 if ( mdi->mapstoget )
916 const gchar *tmp_str;
921 if (redownload == REDOWNLOAD_BAD)
922 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
924 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
928 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
930 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
932 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
933 /* launch the thread */
934 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
935 tmp, /* description string */
936 (vik_thr_func) map_download_thread, /* function to call within thread */
937 mdi, /* pass along data */
938 (vik_thr_free_func) mdi_free, /* function to free pass along data */
939 (vik_thr_free_func) mdi_cancel_cleanup,
948 void maps_layer_download_section_without_redraw( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
951 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
953 if (!map_type->coord_to_mapcoord(ul, zoom, zoom, &ulm)
954 || !map_type->coord_to_mapcoord(br, zoom, zoom, &brm)) {
955 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
959 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
964 mdi->map_layer_alive = TRUE;
965 mdi->mutex = g_mutex_new();
966 mdi->refresh_display = FALSE;
968 mdi->cache_dir = g_strdup ( vml->cache_dir );
969 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
970 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
971 mdi->maptype = vml->maptype;
975 mdi->redownload = REDOWNLOAD_NONE;
977 mdi->x0 = MIN(ulm.x, brm.x);
978 mdi->xf = MAX(ulm.x, brm.x);
979 mdi->y0 = MIN(ulm.y, brm.y);
980 mdi->yf = MAX(ulm.y, brm.y);
984 for (i = mdi->x0; i <= mdi->xf; i++) {
985 for (j = mdi->y0; j <= mdi->yf; j++) {
986 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
987 vml->cache_dir, map_type->uniq_id, ulm.scale,
989 if ( access ( mdi->filename_buf, F_OK ) != 0)
994 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
996 if (mdi->mapstoget) {
999 fmt = ngettext("Downloading %d %s map...",
1000 "Downloading %d %s maps...",
1002 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1004 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1005 /* launch the thread */
1006 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1007 tmp, /* description string */
1008 (vik_thr_func) map_download_thread, /* function to call within thread */
1009 mdi, /* pass along data */
1010 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1011 (vik_thr_free_func) mdi_cancel_cleanup,
1019 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1021 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1023 static void maps_layer_redownload_all ( VikMapsLayer *vml )
1025 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1028 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1030 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1032 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1034 if ( event->button == 1 )
1037 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 );
1038 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 );
1039 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1040 vml->dl_tool_x = vml->dl_tool_y = -1;
1045 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) );
1046 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) );
1048 vml->redownload_vvp = vvp;
1050 vml->dl_tool_x = vml->dl_tool_y = -1;
1052 if ( ! vml->dl_right_click_menu ) {
1054 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1056 item = gtk_menu_item_new_with_label ( _("Redownload bad map(s)") );
1057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1058 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1060 item = gtk_menu_item_new_with_label ( _("Redownload all map(s)") );
1061 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1062 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1065 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1066 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1072 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1077 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1080 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1082 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
1083 if ( map_type->drawmode == vik_viewport_get_drawmode ( vvp ) &&
1084 map_type->coord_to_mapcoord ( vik_viewport_get_center ( vvp ),
1085 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1086 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1088 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1095 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1099 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1100 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1101 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1102 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1104 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1105 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1106 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1108 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1109 g_free ( filename_buf );
1110 vik_layer_emit_update ( VIK_LAYER(vml) );
1118 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1120 VikMapsLayer *vml = vml_vvp[0];
1121 VikViewport *vvp = vml_vvp[1];
1122 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1124 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1125 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1130 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1131 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1133 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
1134 if ( map_type->drawmode == vp_drawmode &&
1135 map_type->coord_to_mapcoord ( &ul, xzoom, yzoom, &ulm ) &&
1136 map_type->coord_to_mapcoord ( &br, xzoom, yzoom, &brm ) )
1137 start_download_thread ( vml, vvp, &ul, &br, redownload );
1138 else if (map_type->drawmode != vp_drawmode) {
1139 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, map_type->drawmode);
1140 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1141 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1145 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1149 static void maps_layer_download_onscreen_maps ( gpointer vml_vvp[2] )
1151 download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1154 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1156 download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1159 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1161 static gpointer pass_along[2];
1163 pass_along[0] = vml;
1164 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1166 item = gtk_menu_item_new();
1167 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1168 gtk_widget_show ( item );
1170 item = gtk_menu_item_new_with_label ( _("Download Onscreen Maps") );
1171 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_onscreen_maps), pass_along );
1172 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1173 gtk_widget_show ( item );
1175 item = gtk_menu_item_new_with_label ( _("Refresh Onscreen Tiles") );
1176 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1177 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1178 gtk_widget_show ( item );