Alex Foobarian's track duration patch
[viking/gosmore.git] / src / vikmapslayer.c
blob4a2139c15565192b3d47136838511d9a5b5cdda5
1 /*
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>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #define MAX_SHRINKFACTOR 8.0000001 /* zoom 1 viewing 8-tiles */
24 #define MIN_SHRINKFACTOR 0.0312499 /* zoom 32 viewing 1-tiles */
26 #include <gtk/gtk.h>
27 #include <gdk-pixbuf/gdk-pixdata.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <math.h>
31 #include "globals.h"
32 #include "coords.h"
33 #include "vikcoord.h"
34 #include "viktreeview.h"
35 #include "vikviewport.h"
36 #include "viklayer.h"
37 #include "vikmapslayer.h"
38 #include "vikmapslayer_pixmap.h"
40 #include <unistd.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
44 #include "mapcache.h"
45 /* only for dialog.h -- ugh */
46 #include "vikwaypoint.h"
47 #include "dialog.h"
49 #include "vikstatus.h"
50 #include "background.h"
52 #include "vikaggregatelayer.h"
53 #include "viklayerspanel.h"
55 #include "mapcoord.h"
56 #include "terraserver.h"
57 #include "googlemaps.h"
58 #include "google.h"
59 #include "khmaps.h"
60 #include "expedia.h"
61 #include "usgs.h"
63 typedef struct {
64 guint8 uniq_id;
65 guint16 tilesize_x;
66 guint16 tilesize_y;
67 guint drawmode;
68 gboolean (*coord_to_mapcoord) ( const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest );
69 void (*mapcoord_to_center_coord) ( MapCoord *src, VikCoord *dest );
70 void (*download) ( MapCoord *src, const gchar *dest_fn );
71 /* TODO: constant size (yay!) */
72 } VikMapsLayer_MapType;
75 /****** MAP TYPES ******/
77 static const VikMapsLayer_MapType __map_types[] = {
79 { 2, 200, 200, VIK_VIEWPORT_DRAWMODE_UTM, terraserver_topo_coord_to_mapcoord, terraserver_mapcoord_to_center_coord, terraserver_topo_download },
80 { 1, 200, 200, VIK_VIEWPORT_DRAWMODE_UTM, terraserver_aerial_coord_to_mapcoord, terraserver_mapcoord_to_center_coord, terraserver_aerial_download },
81 { 4, 200, 200, VIK_VIEWPORT_DRAWMODE_UTM, terraserver_urban_coord_to_mapcoord, terraserver_mapcoord_to_center_coord, terraserver_urban_download },
82 { 5, 0, 0, VIK_VIEWPORT_DRAWMODE_EXPEDIA, expedia_coord_to_mapcoord, expedia_mapcoord_to_center_coord, expedia_download },
83 { 9, 128, 128, VIK_VIEWPORT_DRAWMODE_GOOGLE, googlemaps_coord_to_mapcoord, googlemaps_mapcoord_to_center_coord, googlemaps_download },
84 { 6, 800, 600, VIK_VIEWPORT_DRAWMODE_UTM, usgs_coord_to_mapcoord, usgs_mapcoord_to_center_coord, usgs_download },
85 { 8, 256, 256, VIK_VIEWPORT_DRAWMODE_KH, khmaps_coord_to_mapcoord, khmaps_mapcoord_to_center_coord, khmaps_download },
86 { 7, 256, 256, VIK_VIEWPORT_DRAWMODE_MERCATOR, google_coord_to_mapcoord, google_mapcoord_to_center_coord, google_download },
87 { 10, 256, 256, VIK_VIEWPORT_DRAWMODE_MERCATOR, google_coord_to_mapcoord, google_mapcoord_to_center_coord, google_trans_download },
88 { 11, 256, 256, VIK_VIEWPORT_DRAWMODE_MERCATOR, google_coord_to_mapcoord, google_mapcoord_to_center_coord, google_kh_download },
91 #define NUM_MAP_TYPES (sizeof(__map_types)/sizeof(__map_types[0]))
93 /******** MAPZOOMS *********/
95 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" };
96 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 };
97 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 };
99 static gchar *params_maptypes[] = { "Terraserver Topos", "Terraserver Aerials", "Terraserver Urban Areas", "Expedia (Street Maps)", "Google Maps (Street)", "USGS", "KH Maps", "New (Mercator) Google", "Transparent Google", "New (Mercator) KH" };
100 static guint params_maptypes_ids[] = { 2, 1, 4, 5, 9, 6, 8, 7, 10, 11 };
101 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]))
103 /**************************/
106 static VikMapsLayer *maps_layer_copy ( VikMapsLayer *vml, VikViewport *vvp );
107 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp );
108 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id );
109 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
110 static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
111 static void maps_layer_free ( VikMapsLayer *vml );
112 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
113 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
114 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
115 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
116 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
119 static VikLayerParamScale params_scales[] = {
120 /* min, max, step, digits (decimal places) */
121 { 0, 255, 3, 0 }, /* alpha */
124 VikLayerParam maps_layer_params[] = {
125 { "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Map Type:", VIK_LAYER_WIDGET_RADIOGROUP, params_maptypes, params_maptypes_ids },
126 { "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, "Maps Directory (Optional):", VIK_LAYER_WIDGET_FILEENTRY },
127 { "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Alpha:", VIK_LAYER_WIDGET_HSCALE, params_scales },
128 { "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, "Autodownload maps:", VIK_LAYER_WIDGET_CHECKBUTTON },
129 { "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Zoom Level:", VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms },
132 enum { PARAM_MAPTYPE=0, PARAM_CACHE_DIR, PARAM_ALPHA, PARAM_AUTODOWNLOAD, PARAM_MAPZOOM, NUM_PARAMS };
134 static VikToolInterface maps_tools[] = {
135 { "Maps Download", (VikToolInterfaceFunc) maps_layer_download_click, (VikToolInterfaceFunc) maps_layer_download_release },
138 VikLayerInterface vik_maps_layer_interface = {
139 "Map",
140 &mapslayer_pixbuf,
142 maps_tools,
143 sizeof(maps_tools) / sizeof(maps_tools[0]),
145 maps_layer_params,
146 NUM_PARAMS,
147 NULL,
150 (VikLayerFuncCreate) maps_layer_new,
151 (VikLayerFuncRealize) NULL,
152 (VikLayerFuncPostRead) NULL,
153 (VikLayerFuncFree) maps_layer_free,
155 (VikLayerFuncProperties) NULL,
156 (VikLayerFuncDraw) maps_layer_draw,
157 (VikLayerFuncChangeCoordMode) NULL,
159 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
160 (VikLayerFuncSublayerAddMenuItems) NULL,
162 (VikLayerFuncSublayerRenameRequest) NULL,
163 (VikLayerFuncSublayerToggleVisible) NULL,
165 (VikLayerFuncCopy) maps_layer_copy,
167 (VikLayerFuncSetParam) maps_layer_set_param,
168 (VikLayerFuncGetParam) maps_layer_get_param,
170 (VikLayerFuncReadFileData) NULL,
171 (VikLayerFuncWriteFileData) NULL,
173 (VikLayerFuncCopyItem) NULL,
174 (VikLayerFuncPasteItem) NULL,
175 (VikLayerFuncFreeCopiedItem) NULL,
178 struct _VikMapsLayer {
179 VikLayer vl;
180 guint maptype;
181 gchar *cache_dir;
182 guint8 alpha;
183 guint mapzoom_id;
184 gdouble xmapzoom, ymapzoom;
186 gboolean autodownload;
188 gint dl_tool_x, dl_tool_y;
190 GtkMenu *dl_right_click_menu;
191 VikCoord redownload_ul, redownload_br; /* right click menu only */
192 VikViewport *redownload_vvp;
195 enum { REDOWNLOAD_NONE = 0, REDOWNLOAD_BAD, REDOWNLOAD_ALL };
198 /****************************************/
199 /******** CACHE DIR STUFF ***************/
200 /****************************************/
202 #ifdef WINDOWS
203 #define MAPS_CACHE_DIR "C:\\VIKING-MAPS\\"
204 #define DIRSTRUCTURE "%st%ds%dz%d\\%d\\%d"
205 #else /* POSIX */
207 #include <stdlib.h>
209 #define MAPS_CACHE_DIR maps_layer_default_dir()
210 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
211 #define DIRSTRUCTURE "%st%ds%dz%d/%d/%d"
213 static gchar *maps_layer_default_dir ()
215 static gchar defaultdir[512];
216 static gboolean already_run = 0;
217 if ( ! already_run )
219 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
220 gchar *mapdir = getenv("VIKING_MAPS");
221 if ( mapdir && strlen(mapdir) < 497 ) {
222 strcpy ( defaultdir, mapdir );
223 } else if ( access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
224 strcpy ( defaultdir, GLOBAL_MAPS_DIR );
225 } else {
226 gchar *home = getenv("HOME");
227 if ( home && strlen(home) < 497 )
229 strcpy ( defaultdir, home );
230 strcat ( defaultdir, "/.viking-maps/" );
232 else
234 strcpy ( defaultdir, ".viking-maps/" );
237 already_run = 1;
239 return defaultdir;
242 #endif
244 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
246 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && access ( vml->cache_dir, F_OK ) != 0 )
248 #ifdef WINDOWS
249 mkdir ( vml->cache_dir );
250 #else
251 mkdir ( vml->cache_dir, 0777 );
252 #endif
256 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
258 guint len;
259 g_assert ( vml != NULL);
260 if ( vml->cache_dir )
261 g_free ( vml->cache_dir );
263 if ( dir == NULL || dir[0] == '\0' )
264 vml->cache_dir = g_strdup ( MAPS_CACHE_DIR );
265 else
267 len = strlen(dir);
268 if ( dir[len-1] != VIKING_FILE_SEP )
270 vml->cache_dir = g_malloc ( len+2 );
271 strncpy ( vml->cache_dir, dir, len );
272 vml->cache_dir[len] = VIKING_FILE_SEP;
273 vml->cache_dir[len+1] = '\0';
275 else
276 vml->cache_dir = g_strdup ( dir );
278 maps_layer_mkdir_if_default_dir ( vml );
281 /****************************************/
282 /******** GOBJECT STUFF *****************/
283 /****************************************/
285 GType vik_maps_layer_get_type ()
287 static GType vml_type = 0;
289 if (!vml_type)
291 static const GTypeInfo vml_info =
293 sizeof (VikMapsLayerClass),
294 NULL, /* base_init */
295 NULL, /* base_finalize */
296 NULL, /* class init */
297 NULL, /* class_finalize */
298 NULL, /* class_data */
299 sizeof (VikMapsLayer),
301 NULL /* instance init */
303 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
306 return vml_type;
309 /****************************************/
310 /************** PARAMETERS **************/
311 /****************************************/
313 static guint map_index_to_uniq_id (guint8 index)
315 g_assert ( index < NUM_MAP_TYPES );
316 return __map_types[index].uniq_id;
319 static guint map_uniq_id_to_index ( guint uniq_id )
321 gint i;
322 for ( i = 0; i < NUM_MAP_TYPES; i++ )
323 if ( __map_types[i].uniq_id == uniq_id )
324 return i;
325 return NUM_MAP_TYPES; /* no such thing */
328 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp )
330 switch ( id )
332 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
333 case PARAM_MAPTYPE: {
334 gint maptype = map_uniq_id_to_index(data.u);
335 if ( maptype == NUM_MAP_TYPES ) g_warning("Unknown map type");
336 else vml->maptype = maptype;
337 break;
339 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
340 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
341 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
342 vml->mapzoom_id = data.u;
343 vml->xmapzoom = __mapzooms_x [data.u];
344 vml->ymapzoom = __mapzooms_y [data.u];
345 }else g_warning ("Unknown Map Zoom"); break;
347 return TRUE;
350 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id )
352 VikLayerParamData rv;
353 switch ( id )
355 case PARAM_CACHE_DIR: rv.s = (vml->cache_dir && strcmp(vml->cache_dir, MAPS_CACHE_DIR) != 0) ? vml->cache_dir : ""; break;
356 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
357 case PARAM_ALPHA: rv.u = vml->alpha; break;
358 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
359 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
361 return rv;
364 /****************************************/
365 /****** CREATING, COPYING, FREEING ******/
366 /****************************************/
368 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
370 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
371 vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS );
372 vml->maptype = 0;
373 vml->alpha = 255;
374 vml->mapzoom_id = 0;
375 vml->dl_tool_x = vml->dl_tool_y = -1;
376 maps_layer_set_cache_dir ( vml, NULL );
377 vml->autodownload = FALSE;
379 vml->dl_right_click_menu = NULL;
381 return vml;
384 static void maps_layer_free ( VikMapsLayer *vml )
386 if ( vml->cache_dir )
387 g_free ( vml->cache_dir );
388 if ( vml->dl_right_click_menu )
389 gtk_object_sink ( GTK_OBJECT(vml->dl_right_click_menu) );
392 static VikMapsLayer *maps_layer_copy ( VikMapsLayer *vml, VikViewport *vvp )
394 VikMapsLayer *rv = maps_layer_new ( vvp );
395 *rv = *vml;
396 rv->cache_dir = g_strdup(rv->cache_dir);
397 VIK_LAYER(rv)->name = NULL;
398 return rv;
401 /*********************/
402 /****** DRAWING ******/
403 /*********************/
405 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
407 guchar *pixels;
408 gint width, height, iii, jjj;
410 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
412 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
413 g_object_unref(G_OBJECT(pixbuf));
414 pixbuf = tmp;
417 pixels = gdk_pixbuf_get_pixels(pixbuf);
418 width = gdk_pixbuf_get_width(pixbuf);
419 height = gdk_pixbuf_get_height(pixbuf);
421 /* r,g,b,a,r,g,b,a.... */
422 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
424 pixels += 3;
425 *pixels++ = alpha;
427 return pixbuf;
430 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
432 GdkPixbuf *tmp;
433 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
434 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
435 g_object_unref ( G_OBJECT(pixbuf) );
436 return tmp;
439 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
441 GdkPixbuf *pixbuf;
443 /* get the thing */
444 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
445 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
447 if ( ! pixbuf ) {
448 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
449 vml->cache_dir, mode,
450 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
451 if ( access ( filename_buf, R_OK ) == 0) {
453 GError *gx = NULL;
454 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
456 if (gx)
458 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
459 g_warning ( "Couldn't open image file: %s", gx->message );
461 g_error_free ( gx );
462 if ( pixbuf )
463 g_object_unref ( G_OBJECT(pixbuf) );
464 pixbuf = NULL;
465 } else {
466 if ( vml->alpha < 255 )
467 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
468 if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
469 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
471 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
472 mapcoord->z, __map_types[vml->maptype].uniq_id,
473 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
478 return pixbuf;
481 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
483 MapCoord ulm, brm;
484 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
485 gdouble yzoom = vik_viewport_get_ympp ( vvp );
486 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
488 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
489 xshrinkfactor = vml->xmapzoom / xzoom;
490 yshrinkfactor = vml->ymapzoom / yzoom;
491 if ( xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
492 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) {
493 xzoom = vml->xmapzoom;
494 yzoom = vml->xmapzoom;
495 } else {
496 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 );
500 /* coord -> ID */
501 if ( __map_types[vml->maptype].coord_to_mapcoord ( ul, xzoom, yzoom, &ulm ) &&
502 __map_types[vml->maptype].coord_to_mapcoord ( br, xzoom, yzoom, &brm ) ) {
504 /* loop & draw */
505 gint x, y;
506 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
507 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
508 gint mode = __map_types[vml->maptype].uniq_id;
510 VikCoord coord;
511 gint xx, yy, width, height;
512 GdkPixbuf *pixbuf;
514 guint max_path_len = strlen(vml->cache_dir) + 40;
515 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
517 if ( vml->autodownload )
518 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
520 if ( __map_types[vml->maptype].tilesize_x == 0 ) {
521 for ( x = xmin; x <= xmax; x++ ) {
522 for ( y = ymin; y <= ymax; y++ ) {
523 ulm.x = x;
524 ulm.y = y;
525 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
526 if ( pixbuf ) {
527 width = gdk_pixbuf_get_width ( pixbuf );
528 height = gdk_pixbuf_get_height ( pixbuf );
530 __map_types[vml->maptype].mapcoord_to_center_coord ( &ulm, &coord );
531 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
532 xx -= (width/2);
533 yy -= (height/2);
535 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
539 } else { /* tilesize is known, don't have to keep converting coords */
540 gdouble tilesize_x = __map_types[vml->maptype].tilesize_x * xshrinkfactor;
541 gdouble tilesize_y = __map_types[vml->maptype].tilesize_y * yshrinkfactor;
542 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
543 gint tilesize_x_ceil = ceil ( tilesize_x );
544 gint tilesize_y_ceil = ceil ( tilesize_y );
545 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
546 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
547 gdouble xx, yy; gint xx_tmp, yy_tmp;
548 gint base_yy, xend, yend;
549 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
550 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
552 __map_types[vml->maptype].mapcoord_to_center_coord ( &ulm, &coord );
553 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
554 xx = xx_tmp; yy = yy_tmp;
555 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
556 * eg if tile size 128, shrinkfactor 0.333 */
557 xx -= (tilesize_x/2);
558 base_yy = yy - (tilesize_y/2);
560 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
561 yy = base_yy;
562 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
563 ulm.x = x;
564 ulm.y = y;
565 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
566 if ( pixbuf )
567 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
569 yy += tilesize_y;
571 xx += tilesize_x;
575 g_free ( path_buf );
579 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
581 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
583 VikCoord ul, br;
585 /* get corner coords */
586 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
587 /* UTM multi-zone stuff by Kit Transue */
588 gchar leftmost_zone, rightmost_zone, i;
589 leftmost_zone = vik_viewport_leftmost_zone( vvp );
590 rightmost_zone = vik_viewport_rightmost_zone( vvp );
591 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
592 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
593 maps_layer_draw_section ( vml, vvp, &ul, &br );
596 else {
597 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
598 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
600 maps_layer_draw_section ( vml, vvp, &ul, &br );
605 /*************************/
606 /****** DOWNLOADING ******/
607 /*************************/
609 /* pass along data to thread, exists even if layer is deleted. */
610 typedef struct {
611 gchar *cache_dir;
612 gchar *filename_buf;
613 gint x0, y0, xf, yf;
614 MapCoord mapcoord;
615 gint maptype;
616 gint maxlen;
617 gint mapstoget;
618 gint redownload;
619 } MapDownloadInfo;
621 static void mdi_free ( MapDownloadInfo *mdi )
623 g_free ( mdi->cache_dir );
624 g_free ( mdi->filename_buf );
625 g_free ( mdi );
628 static void map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
630 guint donemaps = 0;
631 gint x, y;
632 for ( x = mdi->x0; x <= mdi->xf; x++ )
634 for ( y = mdi->y0; y <= mdi->yf; y++ )
636 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
637 mdi->cache_dir, __map_types[mdi->maptype].uniq_id,
638 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
640 if ( mdi->redownload == REDOWNLOAD_ALL)
641 remove ( mdi->filename_buf );
642 else if ( mdi->redownload == REDOWNLOAD_BAD && access ( mdi->filename_buf, F_OK ) == 0 )
644 /* see if this one is bad or what */
645 GError *gx = NULL;
646 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
647 if (gx || (!pixbuf))
648 remove ( mdi->filename_buf );
649 if ( pixbuf )
650 g_object_unref ( pixbuf );
651 if ( gx )
652 g_error_free ( gx );
655 if ( access ( mdi->filename_buf, F_OK ) != 0 )
657 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
658 __map_types[mdi->maptype].download ( &(mdi->mapcoord), mdi->filename_buf );
659 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
661 if ( mdi->redownload !=- REDOWNLOAD_NONE )
662 a_mapcache_remove_all_shrinkfactors ( x, y, mdi->mapcoord.z, mdi->maptype, mdi->mapcoord.scale );
665 donemaps++;
666 a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
672 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
674 if ( mdi->mapcoord.x || mdi->mapcoord.y )
676 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
677 mdi->cache_dir, __map_types[mdi->maptype].uniq_id,
678 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
679 if ( access ( mdi->filename_buf, F_OK ) == 0)
681 remove ( mdi->filename_buf );
686 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
688 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
689 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
690 MapCoord ulm, brm;
691 if ( __map_types[vml->maptype].coord_to_mapcoord ( ul, xzoom, yzoom, &ulm )
692 && __map_types[vml->maptype].coord_to_mapcoord ( br, xzoom, yzoom, &brm ) )
694 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
695 gint a, b;
697 /* cache_dir and buffer for dest filename */
698 mdi->cache_dir = g_strdup ( vml->cache_dir );
699 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
700 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
701 mdi->maptype = vml->maptype;
703 mdi->mapcoord = ulm;
705 mdi->redownload = redownload;
707 mdi->x0 = MIN(ulm.x, brm.x);
708 mdi->xf = MAX(ulm.x, brm.x);
709 mdi->y0 = MIN(ulm.y, brm.y);
710 mdi->yf = MAX(ulm.y, brm.y);
712 mdi->mapstoget = 0;
714 if ( mdi->redownload ) {
715 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
716 } else {
717 /* calculate how many we need */
718 for ( a = mdi->x0; a <= mdi->xf; a++ )
720 for ( b = mdi->y0; b <= mdi->yf; b++ )
722 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
723 vml->cache_dir, __map_types[vml->maptype].uniq_id, ulm.scale,
724 ulm.z, a, b );
725 if ( access ( mdi->filename_buf, F_OK ) != 0)
726 mdi->mapstoget++;
731 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
733 if ( mdi->mapstoget )
735 gchar *tmp = g_strdup_printf ( "%s %s%d %s %s...", redownload ? "Redownloading" : "Downloading", redownload == REDOWNLOAD_BAD ? "up to " : "", mdi->mapstoget, params_maptypes[vml->maptype], (mdi->mapstoget == 1) ? "map" : "maps" );
737 /* launch the thread */
738 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
739 tmp, /* description string */
740 (vik_thr_func) map_download_thread, /* function to call within thread */
741 mdi, /* pass along data */
742 (vik_thr_free_func) mdi_free, /* function to free pass along data */
743 (vik_thr_free_func) mdi_cancel_cleanup,
744 mdi->mapstoget );
745 g_free ( tmp );
747 else
748 mdi_free ( mdi );
752 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
754 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
756 static void maps_layer_redownload_all ( VikMapsLayer *vml )
758 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
761 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
764 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
766 if ( event->button == 1 )
768 VikCoord ul, br;
769 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 );
770 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 );
771 start_download_thread ( vml, vvp, &ul, &br, REDOWNLOAD_NONE );
772 vml->dl_tool_x = vml->dl_tool_y = -1;
773 return TRUE;
775 else
777 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) );
778 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) );
780 vml->redownload_vvp = vvp;
782 vml->dl_tool_x = vml->dl_tool_y = -1;
784 if ( ! vml->dl_right_click_menu ) {
785 GtkWidget *item;
786 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
788 item = gtk_menu_item_new_with_label ( "Redownload bad map(s)" );
789 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
790 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
792 item = gtk_menu_item_new_with_label ( "Redownload all map(s)" );
793 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
794 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
797 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
798 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
801 return FALSE;
804 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
806 MapCoord tmp;
807 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) &&
808 __map_types[vml->maptype].coord_to_mapcoord ( vik_viewport_get_center ( vvp ),
809 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
810 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
811 &tmp ) ) {
812 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
813 return TRUE;
815 return FALSE;
818 #if 0
819 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
821 VikCoord coord;
822 MapCoord mapcoord;
823 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
824 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
825 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
826 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
827 &mapcoord ) ) {
828 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
829 vml->cache_dir, __map_types[vml->maptype].uniq_id,
830 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
832 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
833 g_free ( filename_buf );
834 vik_layer_emit_update ( VIK_LAYER(vml) );
835 return TRUE;
838 return FALSE;
839 #endif
842 static void maps_layer_download_onscreen_maps ( gpointer vml_vvp[2] )
844 VikMapsLayer *vml = vml_vvp[0];
845 VikViewport *vvp = vml_vvp[1];
847 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
848 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
850 VikCoord ul, br;
851 MapCoord ulm, brm;
853 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
854 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
856 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) &&
857 __map_types[vml->maptype].coord_to_mapcoord ( &ul, xzoom, yzoom, &ulm ) &&
858 __map_types[vml->maptype].coord_to_mapcoord ( &br, xzoom, yzoom, &brm ) )
859 start_download_thread ( vml, vvp, &ul, &br, REDOWNLOAD_NONE );
860 else
861 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), "Wrong drawmode / zoom level for this map." );
865 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
867 static gpointer pass_along[2];
868 GtkWidget *item;
869 pass_along[0] = vml;
870 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
872 item = gtk_menu_item_new();
873 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
874 gtk_widget_show ( item );
876 item = gtk_menu_item_new_with_label ( "Download Onscreen Maps" );
877 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_onscreen_maps), pass_along );
878 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
879 gtk_widget_show ( item );