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 */
28 #include <gdk-pixbuf/gdk-pixdata.h>
35 #include "viktreeview.h"
36 #include "vikviewport.h"
38 #include "vikmapslayer.h"
39 #include "vikmapslayer_pixmap.h"
43 #include <sys/types.h>
46 /* only for dialog.h -- ugh */
47 #include "vikwaypoint.h"
50 #include "vikstatus.h"
51 #include "background.h"
53 #include "vikaggregatelayer.h"
54 #include "viklayerspanel.h"
57 #include "terraserver.h"
59 /****** MAP TYPES ******/
61 static GList *__map_types = NULL;
63 #define NUM_MAP_TYPES g_list_length(__map_types)
65 /* List of label for each map type */
66 static GList *params_maptypes = NULL;
68 /* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
69 static GList *params_maptypes_ids = NULL;
71 /******** MAPZOOMS *********/
73 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 };
74 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 };
75 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 };
77 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
79 /**************************/
82 static VikMapsLayer *maps_layer_copy ( VikMapsLayer *vml, VikViewport *vvp );
83 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
84 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
85 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp );
86 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id );
87 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
88 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
89 static void maps_layer_free ( VikMapsLayer *vml );
90 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
91 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
92 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
93 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
94 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
95 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
98 static VikLayerParamScale params_scales[] = {
99 /* min, max, step, digits (decimal places) */
100 { 0, 255, 3, 0 }, /* alpha */
103 VikLayerParam maps_layer_params[] = {
104 { "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Map Type:", VIK_LAYER_WIDGET_RADIOGROUP, NULL, NULL },
105 { "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, "Maps Directory (Optional):", VIK_LAYER_WIDGET_FILEENTRY },
106 { "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Alpha:", VIK_LAYER_WIDGET_HSCALE, params_scales },
107 { "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, "Autodownload maps:", VIK_LAYER_WIDGET_CHECKBUTTON },
108 { "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Zoom Level:", VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms },
111 enum { PARAM_MAPTYPE=0, PARAM_CACHE_DIR, PARAM_ALPHA, PARAM_AUTODOWNLOAD, PARAM_MAPZOOM, NUM_PARAMS };
113 static VikToolInterface maps_tools[] = {
114 { "Maps Download", (VikToolConstructorFunc) maps_layer_download_create, NULL, NULL, NULL,
115 (VikToolMouseFunc) maps_layer_download_click, NULL, (VikToolMouseFunc) maps_layer_download_release },
118 VikLayerInterface vik_maps_layer_interface = {
123 sizeof(maps_tools) / sizeof(maps_tools[0]),
132 (VikLayerFuncCreate) maps_layer_new,
133 (VikLayerFuncRealize) NULL,
134 (VikLayerFuncPostRead) NULL,
135 (VikLayerFuncFree) maps_layer_free,
137 (VikLayerFuncProperties) NULL,
138 (VikLayerFuncDraw) maps_layer_draw,
139 (VikLayerFuncChangeCoordMode) NULL,
141 (VikLayerFuncSetMenuItemsSelection) NULL,
142 (VikLayerFuncGetMenuItemsSelection) NULL,
144 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
145 (VikLayerFuncSublayerAddMenuItems) NULL,
147 (VikLayerFuncSublayerRenameRequest) NULL,
148 (VikLayerFuncSublayerToggleVisible) NULL,
150 (VikLayerFuncCopy) maps_layer_copy,
151 (VikLayerFuncMarshall) maps_layer_marshall,
152 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
154 (VikLayerFuncSetParam) maps_layer_set_param,
155 (VikLayerFuncGetParam) maps_layer_get_param,
157 (VikLayerFuncReadFileData) NULL,
158 (VikLayerFuncWriteFileData) NULL,
160 (VikLayerFuncDeleteItem) NULL,
161 (VikLayerFuncCopyItem) NULL,
162 (VikLayerFuncPasteItem) NULL,
163 (VikLayerFuncFreeCopiedItem) NULL,
164 (VikLayerFuncDragDropRequest) NULL,
167 struct _VikMapsLayer {
173 gdouble xmapzoom, ymapzoom;
175 gboolean autodownload;
176 VikCoord *last_center;
180 gint dl_tool_x, dl_tool_y;
182 GtkMenu *dl_right_click_menu;
183 VikCoord redownload_ul, redownload_br; /* right click menu only */
184 VikViewport *redownload_vvp;
187 enum { REDOWNLOAD_NONE = 0, REDOWNLOAD_BAD, REDOWNLOAD_ALL, DOWNLOAD_OR_REFRESH };
190 /****************************************/
191 /******** MAPS LAYER TYPES **************/
192 /****************************************/
194 void maps_layer_register_type ( const char *label, guint id, VikMapsLayer_MapType *map_type )
196 g_assert(label != NULL);
197 g_assert(map_type != NULL);
198 g_assert(id == map_type->uniq_id);
201 params_maptypes = g_list_append(params_maptypes, g_strdup(label));
204 params_maptypes_ids = g_list_append(params_maptypes_ids, (gpointer)id);
206 /* We have to clone */
207 VikMapsLayer_MapType *clone = g_memdup(map_type, sizeof(VikMapsLayer_MapType));
208 /* Register the clone in the list */
209 __map_types = g_list_append(__map_types, clone);
212 We have to ensure the mode LayerParam reference the up-to-date
216 memcpy(&maps_layer_params[0].widget_data, ¶ms_maptypes, sizeof(gpointer));
217 memcpy(&maps_layer_params[0].extra_widget_data, ¶ms_maptypes_ids, sizeof(gpointer));
219 maps_layer_params[0].widget_data = params_maptypes;
220 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
223 #define MAPS_LAYER_NTH_LABEL(n) ((gchar*)g_list_nth_data(params_maptypes, (n)))
224 #define MAPS_LAYER_NTH_ID(n) ((guint)g_list_nth_data(params_maptypes_ids, (n)))
225 #define MAPS_LAYER_NTH_TYPE(n) ((VikMapsLayer_MapType*)g_list_nth_data(__map_types, (n)))
227 gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
229 return(vml->maptype);
232 gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
234 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
237 /****************************************/
238 /******** CACHE DIR STUFF ***************/
239 /****************************************/
242 #define MAPS_CACHE_DIR "C:\\VIKING-MAPS\\"
243 #define DIRSTRUCTURE "%st%ds%dz%d\\%d\\%d"
248 #define MAPS_CACHE_DIR maps_layer_default_dir()
249 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
250 #define DIRSTRUCTURE "%st%ds%dz%d/%d/%d"
252 static gchar *maps_layer_default_dir ()
254 static gchar defaultdir[512];
255 static gboolean already_run = 0;
258 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
259 gchar *mapdir = getenv("VIKING_MAPS");
260 if ( mapdir && strlen(mapdir) < 497 ) {
261 strcpy ( defaultdir, mapdir );
262 } else if ( access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
263 strcpy ( defaultdir, GLOBAL_MAPS_DIR );
265 gchar *home = getenv("HOME");
266 if ( home && strlen(home) < 497 )
268 strcpy ( defaultdir, home );
269 strcat ( defaultdir, "/.viking-maps/" );
273 strcpy ( defaultdir, ".viking-maps/" );
283 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
285 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && access ( vml->cache_dir, F_OK ) != 0 )
288 mkdir ( vml->cache_dir );
290 mkdir ( vml->cache_dir, 0777 );
295 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
298 g_assert ( vml != NULL);
299 if ( vml->cache_dir )
300 g_free ( vml->cache_dir );
302 if ( dir == NULL || dir[0] == '\0' )
303 vml->cache_dir = g_strdup ( MAPS_CACHE_DIR );
307 if ( dir[len-1] != VIKING_FILE_SEP )
309 vml->cache_dir = g_malloc ( len+2 );
310 strncpy ( vml->cache_dir, dir, len );
311 vml->cache_dir[len] = VIKING_FILE_SEP;
312 vml->cache_dir[len+1] = '\0';
315 vml->cache_dir = g_strdup ( dir );
317 maps_layer_mkdir_if_default_dir ( vml );
320 /****************************************/
321 /******** GOBJECT STUFF *****************/
322 /****************************************/
324 GType vik_maps_layer_get_type ()
326 static GType vml_type = 0;
330 static const GTypeInfo vml_info =
332 sizeof (VikMapsLayerClass),
333 NULL, /* base_init */
334 NULL, /* base_finalize */
335 NULL, /* class init */
336 NULL, /* class_finalize */
337 NULL, /* class_data */
338 sizeof (VikMapsLayer),
340 NULL /* instance init */
342 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
348 /****************************************/
349 /************** PARAMETERS **************/
350 /****************************************/
352 static guint map_index_to_uniq_id (guint8 index)
354 g_assert ( index < NUM_MAP_TYPES );
355 return MAPS_LAYER_NTH_TYPE(index)->uniq_id;
358 static guint map_uniq_id_to_index ( guint uniq_id )
361 for ( i = 0; i < NUM_MAP_TYPES; i++ )
362 if ( MAPS_LAYER_NTH_TYPE(i)->uniq_id == uniq_id )
364 return NUM_MAP_TYPES; /* no such thing */
367 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp )
371 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
372 case PARAM_MAPTYPE: {
373 gint maptype = map_uniq_id_to_index(data.u);
374 if ( maptype == NUM_MAP_TYPES ) g_warning("Unknown map type");
375 else vml->maptype = maptype;
378 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
379 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
380 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
381 vml->mapzoom_id = data.u;
382 vml->xmapzoom = __mapzooms_x [data.u];
383 vml->ymapzoom = __mapzooms_y [data.u];
384 }else g_warning ("Unknown Map Zoom"); break;
389 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id )
391 VikLayerParamData rv;
394 case PARAM_CACHE_DIR: rv.s = (vml->cache_dir && strcmp(vml->cache_dir, MAPS_CACHE_DIR) != 0) ? vml->cache_dir : ""; break;
395 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
396 case PARAM_ALPHA: rv.u = vml->alpha; break;
397 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
398 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
403 /****************************************/
404 /****** CREATING, COPYING, FREEING ******/
405 /****************************************/
407 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
409 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
410 vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS );
411 vml->maptype = 6; /* index to __map_types[] */
414 vml->dl_tool_x = vml->dl_tool_y = -1;
415 maps_layer_set_cache_dir ( vml, NULL );
416 vml->autodownload = FALSE;
417 vml->last_center = NULL;
418 vml->last_xmpp = 0.0;
419 vml->last_ympp = 0.0;
421 vml->dl_right_click_menu = NULL;
426 static void maps_layer_free ( VikMapsLayer *vml )
428 if ( vml->cache_dir )
429 g_free ( vml->cache_dir );
430 if ( vml->dl_right_click_menu )
431 gtk_object_sink ( GTK_OBJECT(vml->dl_right_click_menu) );
432 if (vml->last_center)
433 g_free(vml->last_center);
436 static VikMapsLayer *maps_layer_copy ( VikMapsLayer *vml, VikViewport *vvp )
438 VikMapsLayer *rv = maps_layer_new ( vvp );
440 rv->cache_dir = g_strdup(rv->cache_dir);
441 VIK_LAYER(rv)->name = NULL;
445 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
447 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
450 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
452 VikMapsLayer *rv = maps_layer_new ( vvp );
453 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
457 /*********************/
458 /****** DRAWING ******/
459 /*********************/
461 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
464 gint width, height, iii, jjj;
466 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
468 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
469 g_object_unref(G_OBJECT(pixbuf));
473 pixels = gdk_pixbuf_get_pixels(pixbuf);
474 width = gdk_pixbuf_get_width(pixbuf);
475 height = gdk_pixbuf_get_height(pixbuf);
477 /* r,g,b,a,r,g,b,a.... */
478 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
486 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
489 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
490 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
491 g_object_unref ( G_OBJECT(pixbuf) );
495 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
500 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
501 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
504 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
505 vml->cache_dir, mode,
506 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
507 if ( access ( filename_buf, R_OK ) == 0) {
510 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
514 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
515 g_warning ( "Couldn't open image file: %s", gx->message );
519 g_object_unref ( G_OBJECT(pixbuf) );
522 if ( vml->alpha < 255 )
523 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
524 if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
525 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
527 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
528 mapcoord->z, MAPS_LAYER_NTH_TYPE(vml->maptype)->uniq_id,
529 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
537 gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
539 const VikCoord *center = vik_viewport_get_center ( vvp );
541 if (vml->last_center == NULL) {
542 VikCoord *new_center = g_malloc(sizeof(VikCoord));
543 *new_center = *center;
544 vml->last_center = new_center;
545 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
546 vml->last_ympp = vik_viewport_get_ympp(vvp);
550 /* TODO: perhaps vik_coord_diff() */
551 if (vik_coord_equals(vml->last_center, center)
552 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
553 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
556 *(vml->last_center) = *center;
557 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
558 vml->last_ympp = vik_viewport_get_ympp(vvp);
562 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
565 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
566 gdouble yzoom = vik_viewport_get_ympp ( vvp );
567 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
569 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
570 xshrinkfactor = vml->xmapzoom / xzoom;
571 yshrinkfactor = vml->ymapzoom / yzoom;
572 if ( xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
573 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) {
574 xzoom = vml->xmapzoom;
575 yzoom = vml->xmapzoom;
577 g_warning ( "Cowardly refusing to draw tiles at a shrinkfactor more than %.3f (zoomed out) or less than %.3f (zoomed in).", 1/MIN_SHRINKFACTOR, 1/MAX_SHRINKFACTOR );
582 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
583 if ( map_type->coord_to_mapcoord ( ul, xzoom, yzoom, &ulm ) &&
584 map_type->coord_to_mapcoord ( br, xzoom, yzoom, &brm ) ) {
588 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
589 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
590 gint mode = map_type->uniq_id;
593 gint xx, yy, width, height;
596 guint max_path_len = strlen(vml->cache_dir) + 40;
597 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
599 if ( vml->autodownload && should_start_autodownload(vml, vvp)) {
601 fputs(stderr, "DEBUG: Starting autodownload\n");
603 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
606 if ( map_type->tilesize_x == 0 ) {
607 for ( x = xmin; x <= xmax; x++ ) {
608 for ( y = ymin; y <= ymax; y++ ) {
611 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
613 width = gdk_pixbuf_get_width ( pixbuf );
614 height = gdk_pixbuf_get_height ( pixbuf );
616 map_type->mapcoord_to_center_coord ( &ulm, &coord );
617 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
621 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
625 } else { /* tilesize is known, don't have to keep converting coords */
626 gdouble tilesize_x = map_type->tilesize_x * xshrinkfactor;
627 gdouble tilesize_y = map_type->tilesize_y * yshrinkfactor;
628 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
629 gint tilesize_x_ceil = ceil ( tilesize_x );
630 gint tilesize_y_ceil = ceil ( tilesize_y );
631 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
632 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
633 gdouble xx, yy; gint xx_tmp, yy_tmp;
634 gint base_yy, xend, yend;
635 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
636 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
638 map_type->mapcoord_to_center_coord ( &ulm, &coord );
639 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
640 xx = xx_tmp; yy = yy_tmp;
641 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
642 * eg if tile size 128, shrinkfactor 0.333 */
643 xx -= (tilesize_x/2);
644 base_yy = yy - (tilesize_y/2);
646 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
648 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
651 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
653 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
665 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
667 if ( MAPS_LAYER_NTH_TYPE(vml->maptype)->drawmode == vik_viewport_get_drawmode ( vvp ) )
671 /* get corner coords */
672 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
673 /* UTM multi-zone stuff by Kit Transue */
674 gchar leftmost_zone, rightmost_zone, i;
675 leftmost_zone = vik_viewport_leftmost_zone( vvp );
676 rightmost_zone = vik_viewport_rightmost_zone( vvp );
677 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
678 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
679 maps_layer_draw_section ( vml, vvp, &ul, &br );
683 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
684 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
686 maps_layer_draw_section ( vml, vvp, &ul, &br );
691 /*************************/
692 /****** DOWNLOADING ******/
693 /*************************/
695 /* pass along data to thread, exists even if layer is deleted. */
705 gboolean refresh_display;
708 gboolean map_layer_alive;
712 static void mdi_free ( MapDownloadInfo *mdi )
714 g_mutex_free(mdi->mutex);
715 g_free ( mdi->cache_dir );
716 g_free ( mdi->filename_buf );
720 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
722 MapDownloadInfo *mdi = ptr;
723 g_mutex_lock(mdi->mutex);
724 mdi->map_layer_alive = FALSE;
725 g_mutex_unlock(mdi->mutex);
728 static void map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
732 for ( x = mdi->x0; x <= mdi->xf; x++ )
734 for ( y = mdi->y0; y <= mdi->yf; y++ )
736 gboolean remove_mem_cache = FALSE;
737 gboolean need_download = FALSE;
738 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
739 mdi->cache_dir, MAPS_LAYER_NTH_TYPE(mdi->maptype)->uniq_id,
740 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
743 a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
745 if ( mdi->redownload == REDOWNLOAD_ALL)
746 remove ( mdi->filename_buf );
748 else if ( (mdi->redownload == REDOWNLOAD_BAD) && (access ( mdi->filename_buf, F_OK ) == 0) )
750 /* see if this one is bad or what */
752 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
754 remove ( mdi->filename_buf );
756 g_object_unref ( pixbuf );
761 if ( access ( mdi->filename_buf, F_OK ) != 0 )
763 need_download = TRUE;
764 if (( mdi->redownload != REDOWNLOAD_NONE ) &&
765 ( mdi->redownload != DOWNLOAD_OR_REFRESH ))
766 remove_mem_cache = TRUE;
767 } else if ( mdi->redownload == DOWNLOAD_OR_REFRESH ) {
768 remove_mem_cache = TRUE;
772 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
775 if ( MAPS_LAYER_NTH_TYPE(mdi->maptype)->download ( &(mdi->mapcoord), mdi->filename_buf ))
780 g_mutex_lock(mdi->mutex);
781 if (remove_mem_cache)
782 a_mapcache_remove_all_shrinkfactors ( x, y, mdi->mapcoord.z, MAPS_LAYER_NTH_TYPE(mdi->maptype)->uniq_id, mdi->mapcoord.scale );
783 if (mdi->refresh_display && mdi->map_layer_alive) {
784 /* TODO: check if it's on visible area */
785 vik_layer_emit_update ( VIK_LAYER(mdi->vml) );
787 g_mutex_unlock(mdi->mutex);
789 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
793 g_mutex_lock(mdi->mutex);
794 if (mdi->map_layer_alive)
795 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
796 g_mutex_unlock(mdi->mutex);
799 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
801 if ( mdi->mapcoord.x || mdi->mapcoord.y )
803 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
804 mdi->cache_dir, MAPS_LAYER_NTH_TYPE(mdi->maptype)->uniq_id,
805 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
806 if ( access ( mdi->filename_buf, F_OK ) == 0)
808 remove ( mdi->filename_buf );
813 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
815 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
816 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
818 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
819 if ( map_type->coord_to_mapcoord ( ul, xzoom, yzoom, &ulm )
820 && map_type->coord_to_mapcoord ( br, xzoom, yzoom, &brm ) )
822 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
827 mdi->map_layer_alive = TRUE;
828 mdi->mutex = g_mutex_new();
829 mdi->refresh_display = TRUE;
831 /* cache_dir and buffer for dest filename */
832 mdi->cache_dir = g_strdup ( vml->cache_dir );
833 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
834 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
835 mdi->maptype = vml->maptype;
839 mdi->redownload = redownload;
841 mdi->x0 = MIN(ulm.x, brm.x);
842 mdi->xf = MAX(ulm.x, brm.x);
843 mdi->y0 = MIN(ulm.y, brm.y);
844 mdi->yf = MAX(ulm.y, brm.y);
848 if ( mdi->redownload ) {
849 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
851 /* calculate how many we need */
852 for ( a = mdi->x0; a <= mdi->xf; a++ )
854 for ( b = mdi->y0; b <= mdi->yf; b++ )
856 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
857 vml->cache_dir, map_type->uniq_id, ulm.scale,
859 if ( access ( mdi->filename_buf, F_OK ) != 0)
865 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
867 if ( mdi->mapstoget )
869 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" );
871 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
872 /* launch the thread */
873 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
874 tmp, /* description string */
875 (vik_thr_func) map_download_thread, /* function to call within thread */
876 mdi, /* pass along data */
877 (vik_thr_free_func) mdi_free, /* function to free pass along data */
878 (vik_thr_free_func) mdi_cancel_cleanup,
887 void maps_layer_download_section_without_redraw( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
890 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
892 if (!map_type->coord_to_mapcoord(ul, zoom, zoom, &ulm)
893 || !map_type->coord_to_mapcoord(br, zoom, zoom, &brm)) {
894 g_warning("%s() coord_to_mapcoord() failed\n", __PRETTY_FUNCTION__);
898 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
903 mdi->map_layer_alive = TRUE;
904 mdi->mutex = g_mutex_new();
905 mdi->refresh_display = FALSE;
907 mdi->cache_dir = g_strdup ( vml->cache_dir );
908 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
909 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
910 mdi->maptype = vml->maptype;
914 mdi->redownload = REDOWNLOAD_NONE;
916 mdi->x0 = MIN(ulm.x, brm.x);
917 mdi->xf = MAX(ulm.x, brm.x);
918 mdi->y0 = MIN(ulm.y, brm.y);
919 mdi->yf = MAX(ulm.y, brm.y);
923 for (i = mdi->x0; i <= mdi->xf; i++) {
924 for (j = mdi->y0; j <= mdi->yf; j++) {
925 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
926 vml->cache_dir, map_type->uniq_id, ulm.scale,
928 if ( access ( mdi->filename_buf, F_OK ) != 0)
933 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
935 if (mdi->mapstoget) {
936 gchar *tmp = g_strdup_printf ( "%s %d %s %s...", "Downloading", mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype), (mdi->mapstoget == 1) ? "map" : "maps" );
938 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
939 /* launch the thread */
940 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
941 tmp, /* description string */
942 (vik_thr_func) map_download_thread, /* function to call within thread */
943 mdi, /* pass along data */
944 (vik_thr_free_func) mdi_free, /* function to free pass along data */
945 (vik_thr_free_func) mdi_cancel_cleanup,
953 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
955 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
957 static void maps_layer_redownload_all ( VikMapsLayer *vml )
959 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
962 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
964 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
966 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
968 if ( event->button == 1 )
971 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 );
972 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 );
973 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
974 vml->dl_tool_x = vml->dl_tool_y = -1;
979 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) );
980 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) );
982 vml->redownload_vvp = vvp;
984 vml->dl_tool_x = vml->dl_tool_y = -1;
986 if ( ! vml->dl_right_click_menu ) {
988 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
990 item = gtk_menu_item_new_with_label ( "Redownload bad map(s)" );
991 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
992 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
994 item = gtk_menu_item_new_with_label ( "Redownload all map(s)" );
995 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
996 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
999 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1000 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1006 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1011 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1014 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1016 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
1017 if ( map_type->drawmode == vik_viewport_get_drawmode ( vvp ) &&
1018 map_type->coord_to_mapcoord ( vik_viewport_get_center ( vvp ),
1019 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1020 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1022 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1029 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1033 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1034 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1035 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1036 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1038 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1039 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1040 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1042 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1043 g_free ( filename_buf );
1044 vik_layer_emit_update ( VIK_LAYER(vml) );
1052 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1054 VikMapsLayer *vml = vml_vvp[0];
1055 VikViewport *vvp = vml_vvp[1];
1057 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1058 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1063 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1064 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1066 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
1067 if ( map_type->drawmode == vik_viewport_get_drawmode ( vvp ) &&
1068 map_type->coord_to_mapcoord ( &ul, xzoom, yzoom, &ulm ) &&
1069 map_type->coord_to_mapcoord ( &br, xzoom, yzoom, &brm ) )
1070 start_download_thread ( vml, vvp, &ul, &br, redownload );
1072 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), "Wrong drawmode / zoom level for this map." );
1076 static void maps_layer_download_onscreen_maps ( gpointer vml_vvp[2] )
1078 download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1081 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1083 download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1086 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1088 static gpointer pass_along[2];
1090 pass_along[0] = vml;
1091 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1093 item = gtk_menu_item_new();
1094 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1095 gtk_widget_show ( item );
1097 item = gtk_menu_item_new_with_label ( "Download Onscreen Maps" );
1098 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_onscreen_maps), pass_along );
1099 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1100 gtk_widget_show ( item );
1102 item = gtk_menu_item_new_with_label ( "Refresh Onscreen Tiles" );
1103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1104 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1105 gtk_widget_show ( item );