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 /****************************************/
228 /******** CACHE DIR STUFF ***************/
229 /****************************************/
232 #define MAPS_CACHE_DIR "C:\\VIKING-MAPS\\"
233 #define DIRSTRUCTURE "%st%ds%dz%d\\%d\\%d"
238 #define MAPS_CACHE_DIR maps_layer_default_dir()
239 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
240 #define DIRSTRUCTURE "%st%ds%dz%d/%d/%d"
242 static gchar *maps_layer_default_dir ()
244 static gchar defaultdir[512];
245 static gboolean already_run = 0;
248 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
249 gchar *mapdir = getenv("VIKING_MAPS");
250 if ( mapdir && strlen(mapdir) < 497 ) {
251 strcpy ( defaultdir, mapdir );
252 } else if ( access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
253 strcpy ( defaultdir, GLOBAL_MAPS_DIR );
255 gchar *home = getenv("HOME");
256 if ( home && strlen(home) < 497 )
258 strcpy ( defaultdir, home );
259 strcat ( defaultdir, "/.viking-maps/" );
263 strcpy ( defaultdir, ".viking-maps/" );
273 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
275 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && access ( vml->cache_dir, F_OK ) != 0 )
278 mkdir ( vml->cache_dir );
280 mkdir ( vml->cache_dir, 0777 );
285 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
288 g_assert ( vml != NULL);
289 if ( vml->cache_dir )
290 g_free ( vml->cache_dir );
292 if ( dir == NULL || dir[0] == '\0' )
293 vml->cache_dir = g_strdup ( MAPS_CACHE_DIR );
297 if ( dir[len-1] != VIKING_FILE_SEP )
299 vml->cache_dir = g_malloc ( len+2 );
300 strncpy ( vml->cache_dir, dir, len );
301 vml->cache_dir[len] = VIKING_FILE_SEP;
302 vml->cache_dir[len+1] = '\0';
305 vml->cache_dir = g_strdup ( dir );
307 maps_layer_mkdir_if_default_dir ( vml );
310 /****************************************/
311 /******** GOBJECT STUFF *****************/
312 /****************************************/
314 GType vik_maps_layer_get_type ()
316 static GType vml_type = 0;
320 static const GTypeInfo vml_info =
322 sizeof (VikMapsLayerClass),
323 NULL, /* base_init */
324 NULL, /* base_finalize */
325 NULL, /* class init */
326 NULL, /* class_finalize */
327 NULL, /* class_data */
328 sizeof (VikMapsLayer),
330 NULL /* instance init */
332 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
338 /****************************************/
339 /************** PARAMETERS **************/
340 /****************************************/
342 static guint map_index_to_uniq_id (guint8 index)
344 g_assert ( index < NUM_MAP_TYPES );
345 return MAPS_LAYER_NTH_TYPE(index)->uniq_id;
348 static guint map_uniq_id_to_index ( guint uniq_id )
351 for ( i = 0; i < NUM_MAP_TYPES; i++ )
352 if ( MAPS_LAYER_NTH_TYPE(i)->uniq_id == uniq_id )
354 return NUM_MAP_TYPES; /* no such thing */
357 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp )
361 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
362 case PARAM_MAPTYPE: {
363 gint maptype = map_uniq_id_to_index(data.u);
364 if ( maptype == NUM_MAP_TYPES ) g_warning("Unknown map type");
365 else vml->maptype = maptype;
368 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
369 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
370 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
371 vml->mapzoom_id = data.u;
372 vml->xmapzoom = __mapzooms_x [data.u];
373 vml->ymapzoom = __mapzooms_y [data.u];
374 }else g_warning ("Unknown Map Zoom"); break;
379 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id )
381 VikLayerParamData rv;
384 case PARAM_CACHE_DIR: rv.s = (vml->cache_dir && strcmp(vml->cache_dir, MAPS_CACHE_DIR) != 0) ? vml->cache_dir : ""; break;
385 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
386 case PARAM_ALPHA: rv.u = vml->alpha; break;
387 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
388 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
393 /****************************************/
394 /****** CREATING, COPYING, FREEING ******/
395 /****************************************/
397 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
399 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
400 vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS );
404 vml->dl_tool_x = vml->dl_tool_y = -1;
405 maps_layer_set_cache_dir ( vml, NULL );
406 vml->autodownload = FALSE;
407 vml->last_center = NULL;
408 vml->last_xmpp = 0.0;
409 vml->last_ympp = 0.0;
411 vml->dl_right_click_menu = NULL;
416 static void maps_layer_free ( VikMapsLayer *vml )
418 if ( vml->cache_dir )
419 g_free ( vml->cache_dir );
420 if ( vml->dl_right_click_menu )
421 gtk_object_sink ( GTK_OBJECT(vml->dl_right_click_menu) );
422 if (vml->last_center)
423 g_free(vml->last_center);
426 static VikMapsLayer *maps_layer_copy ( VikMapsLayer *vml, VikViewport *vvp )
428 VikMapsLayer *rv = maps_layer_new ( vvp );
430 rv->cache_dir = g_strdup(rv->cache_dir);
431 VIK_LAYER(rv)->name = NULL;
435 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
437 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
440 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
442 VikMapsLayer *rv = maps_layer_new ( vvp );
443 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
447 /*********************/
448 /****** DRAWING ******/
449 /*********************/
451 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
454 gint width, height, iii, jjj;
456 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
458 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
459 g_object_unref(G_OBJECT(pixbuf));
463 pixels = gdk_pixbuf_get_pixels(pixbuf);
464 width = gdk_pixbuf_get_width(pixbuf);
465 height = gdk_pixbuf_get_height(pixbuf);
467 /* r,g,b,a,r,g,b,a.... */
468 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
476 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
479 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
480 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
481 g_object_unref ( G_OBJECT(pixbuf) );
485 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
490 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
491 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
494 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
495 vml->cache_dir, mode,
496 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
497 if ( access ( filename_buf, R_OK ) == 0) {
500 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
504 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
505 g_warning ( "Couldn't open image file: %s", gx->message );
509 g_object_unref ( G_OBJECT(pixbuf) );
512 if ( vml->alpha < 255 )
513 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
514 if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
515 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
517 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
518 mapcoord->z, MAPS_LAYER_NTH_TYPE(vml->maptype)->uniq_id,
519 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
527 gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
529 const VikCoord *center = vik_viewport_get_center ( vvp );
531 if (vml->last_center == NULL) {
532 VikCoord *new_center = g_malloc(sizeof(VikCoord));
533 *new_center = *center;
534 vml->last_center = new_center;
535 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
536 vml->last_ympp = vik_viewport_get_ympp(vvp);
540 /* TODO: perhaps vik_coord_diff() */
541 if (vik_coord_equals(vml->last_center, center)
542 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
543 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
546 *(vml->last_center) = *center;
547 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
548 vml->last_ympp = vik_viewport_get_ympp(vvp);
552 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
555 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
556 gdouble yzoom = vik_viewport_get_ympp ( vvp );
557 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
559 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
560 xshrinkfactor = vml->xmapzoom / xzoom;
561 yshrinkfactor = vml->ymapzoom / yzoom;
562 if ( xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
563 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) {
564 xzoom = vml->xmapzoom;
565 yzoom = vml->xmapzoom;
567 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 );
572 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
573 if ( map_type->coord_to_mapcoord ( ul, xzoom, yzoom, &ulm ) &&
574 map_type->coord_to_mapcoord ( br, xzoom, yzoom, &brm ) ) {
578 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
579 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
580 gint mode = map_type->uniq_id;
583 gint xx, yy, width, height;
586 guint max_path_len = strlen(vml->cache_dir) + 40;
587 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
589 if ( vml->autodownload && should_start_autodownload(vml, vvp)) {
590 fprintf(stderr, "DEBUG: Starting autodownload\n");
591 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
594 if ( map_type->tilesize_x == 0 ) {
595 for ( x = xmin; x <= xmax; x++ ) {
596 for ( y = ymin; y <= ymax; y++ ) {
599 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
601 width = gdk_pixbuf_get_width ( pixbuf );
602 height = gdk_pixbuf_get_height ( pixbuf );
604 map_type->mapcoord_to_center_coord ( &ulm, &coord );
605 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
609 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
613 } else { /* tilesize is known, don't have to keep converting coords */
614 gdouble tilesize_x = map_type->tilesize_x * xshrinkfactor;
615 gdouble tilesize_y = map_type->tilesize_y * yshrinkfactor;
616 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
617 gint tilesize_x_ceil = ceil ( tilesize_x );
618 gint tilesize_y_ceil = ceil ( tilesize_y );
619 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
620 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
621 gdouble xx, yy; gint xx_tmp, yy_tmp;
622 gint base_yy, xend, yend;
623 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
624 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
626 map_type->mapcoord_to_center_coord ( &ulm, &coord );
627 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
628 xx = xx_tmp; yy = yy_tmp;
629 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
630 * eg if tile size 128, shrinkfactor 0.333 */
631 xx -= (tilesize_x/2);
632 base_yy = yy - (tilesize_y/2);
634 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
636 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
639 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
641 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
653 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
655 if ( MAPS_LAYER_NTH_TYPE(vml->maptype)->drawmode == vik_viewport_get_drawmode ( vvp ) )
659 /* get corner coords */
660 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
661 /* UTM multi-zone stuff by Kit Transue */
662 gchar leftmost_zone, rightmost_zone, i;
663 leftmost_zone = vik_viewport_leftmost_zone( vvp );
664 rightmost_zone = vik_viewport_rightmost_zone( vvp );
665 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
666 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
667 maps_layer_draw_section ( vml, vvp, &ul, &br );
671 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
672 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
674 maps_layer_draw_section ( vml, vvp, &ul, &br );
679 /*************************/
680 /****** DOWNLOADING ******/
681 /*************************/
683 /* pass along data to thread, exists even if layer is deleted. */
695 gboolean map_layer_alive;
699 static void mdi_free ( MapDownloadInfo *mdi )
701 g_mutex_free(mdi->mutex);
702 g_free ( mdi->cache_dir );
703 g_free ( mdi->filename_buf );
707 static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
709 MapDownloadInfo *mdi = ptr;
710 g_mutex_lock(mdi->mutex);
711 mdi->map_layer_alive = FALSE;
712 g_mutex_unlock(mdi->mutex);
715 static void map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
719 for ( x = mdi->x0; x <= mdi->xf; x++ )
721 for ( y = mdi->y0; y <= mdi->yf; y++ )
723 gboolean remove_mem_cache = FALSE;
724 gboolean need_download = FALSE;
725 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
726 mdi->cache_dir, MAPS_LAYER_NTH_TYPE(mdi->maptype)->uniq_id,
727 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
730 a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
732 if ( mdi->redownload == REDOWNLOAD_ALL)
733 remove ( mdi->filename_buf );
735 else if ( (mdi->redownload == REDOWNLOAD_BAD) && (access ( mdi->filename_buf, F_OK ) == 0) )
737 /* see if this one is bad or what */
739 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
741 remove ( mdi->filename_buf );
743 g_object_unref ( pixbuf );
748 if ( access ( mdi->filename_buf, F_OK ) != 0 )
750 need_download = TRUE;
751 if (( mdi->redownload != REDOWNLOAD_NONE ) &&
752 ( mdi->redownload != DOWNLOAD_OR_REFRESH ))
753 remove_mem_cache = TRUE;
754 } else if ( mdi->redownload == DOWNLOAD_OR_REFRESH ) {
755 remove_mem_cache = TRUE;
759 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
762 if ( MAPS_LAYER_NTH_TYPE(mdi->maptype)->download ( &(mdi->mapcoord), mdi->filename_buf ))
767 g_mutex_lock(mdi->mutex);
768 if (remove_mem_cache)
769 a_mapcache_remove_all_shrinkfactors ( x, y, mdi->mapcoord.z, MAPS_LAYER_NTH_TYPE(mdi->maptype)->uniq_id, mdi->mapcoord.scale );
770 if (mdi->map_layer_alive) {
771 /* TODO: check if it's on visible area */
772 vik_layer_emit_update ( VIK_LAYER(mdi->vml) );
774 g_mutex_unlock(mdi->mutex);
776 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
780 g_mutex_lock(mdi->mutex);
781 if (mdi->map_layer_alive)
782 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
783 g_mutex_unlock(mdi->mutex);
786 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
788 if ( mdi->mapcoord.x || mdi->mapcoord.y )
790 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
791 mdi->cache_dir, MAPS_LAYER_NTH_TYPE(mdi->maptype)->uniq_id,
792 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
793 if ( access ( mdi->filename_buf, F_OK ) == 0)
795 remove ( mdi->filename_buf );
800 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
802 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
803 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
805 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
806 if ( map_type->coord_to_mapcoord ( ul, xzoom, yzoom, &ulm )
807 && map_type->coord_to_mapcoord ( br, xzoom, yzoom, &brm ) )
809 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
814 mdi->map_layer_alive = TRUE;
815 mdi->mutex = g_mutex_new();
817 /* cache_dir and buffer for dest filename */
818 mdi->cache_dir = g_strdup ( vml->cache_dir );
819 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
820 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
821 mdi->maptype = vml->maptype;
825 mdi->redownload = redownload;
827 mdi->x0 = MIN(ulm.x, brm.x);
828 mdi->xf = MAX(ulm.x, brm.x);
829 mdi->y0 = MIN(ulm.y, brm.y);
830 mdi->yf = MAX(ulm.y, brm.y);
834 if ( mdi->redownload ) {
835 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
837 /* calculate how many we need */
838 for ( a = mdi->x0; a <= mdi->xf; a++ )
840 for ( b = mdi->y0; b <= mdi->yf; b++ )
842 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
843 vml->cache_dir, map_type->uniq_id, ulm.scale,
845 if ( access ( mdi->filename_buf, F_OK ) != 0)
851 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
853 if ( mdi->mapstoget )
855 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" );
857 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
858 /* launch the thread */
859 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
860 tmp, /* description string */
861 (vik_thr_func) map_download_thread, /* function to call within thread */
862 mdi, /* pass along data */
863 (vik_thr_free_func) mdi_free, /* function to free pass along data */
864 (vik_thr_free_func) mdi_cancel_cleanup,
873 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
875 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
877 static void maps_layer_redownload_all ( VikMapsLayer *vml )
879 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
882 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
884 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
886 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
888 if ( event->button == 1 )
891 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 );
892 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 );
893 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
894 vml->dl_tool_x = vml->dl_tool_y = -1;
899 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) );
900 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) );
902 vml->redownload_vvp = vvp;
904 vml->dl_tool_x = vml->dl_tool_y = -1;
906 if ( ! vml->dl_right_click_menu ) {
908 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
910 item = gtk_menu_item_new_with_label ( "Redownload bad map(s)" );
911 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
912 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
914 item = gtk_menu_item_new_with_label ( "Redownload all map(s)" );
915 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
916 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
919 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
920 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
926 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
931 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
934 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
936 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
937 if ( map_type->drawmode == vik_viewport_get_drawmode ( vvp ) &&
938 map_type->coord_to_mapcoord ( vik_viewport_get_center ( vvp ),
939 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
940 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
942 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
949 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
953 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
954 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
955 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
956 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
958 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
959 vml->cache_dir, __map_types[vml->maptype].uniq_id,
960 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
962 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
963 g_free ( filename_buf );
964 vik_layer_emit_update ( VIK_LAYER(vml) );
972 static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
974 VikMapsLayer *vml = vml_vvp[0];
975 VikViewport *vvp = vml_vvp[1];
977 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
978 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
983 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
984 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
986 VikMapsLayer_MapType *map_type = MAPS_LAYER_NTH_TYPE(vml->maptype);
987 if ( map_type->drawmode == vik_viewport_get_drawmode ( vvp ) &&
988 map_type->coord_to_mapcoord ( &ul, xzoom, yzoom, &ulm ) &&
989 map_type->coord_to_mapcoord ( &br, xzoom, yzoom, &brm ) )
990 start_download_thread ( vml, vvp, &ul, &br, redownload );
992 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), "Wrong drawmode / zoom level for this map." );
996 static void maps_layer_download_onscreen_maps ( gpointer vml_vvp[2] )
998 download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1001 static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1003 download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1006 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1008 static gpointer pass_along[2];
1010 pass_along[0] = vml;
1011 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1013 item = gtk_menu_item_new();
1014 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1015 gtk_widget_show ( item );
1017 item = gtk_menu_item_new_with_label ( "Download Onscreen Maps" );
1018 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_onscreen_maps), pass_along );
1019 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1020 gtk_widget_show ( item );
1022 item = gtk_menu_item_new_with_label ( "Refresh Onscreen Tiles" );
1023 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1024 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1025 gtk_widget_show ( item );