]> git.street.me.uk Git - andy/viking.git/blob - src/vikmapslayer.c
toolbox tool improvements
[andy/viking.git] / src / vikmapslayer.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2005, Evan Battaglia <viking@greentorch.org>
5  * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
6  *
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.
11  *
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.
16  *
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
20  *
21  */
22
23 #define MAX_SHRINKFACTOR 8.0000001 /* zoom 1 viewing 8-tiles */
24 #define MIN_SHRINKFACTOR 0.0312499 /* zoom 32 viewing 1-tiles */
25
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"
39
40 #include <unistd.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43
44 #include "mapcache.h"
45 /* only for dialog.h -- ugh */
46 #include "vikwaypoint.h"
47 #include "dialog.h"
48
49 #include "vikstatus.h"
50 #include "background.h"
51
52 #include "vikaggregatelayer.h"
53 #include "viklayerspanel.h"
54
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
62 typedef struct {
63   guint8 uniq_id;
64   guint16 tilesize_x;
65   guint16 tilesize_y;
66   guint drawmode;
67   gboolean (*coord_to_mapcoord) ( const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest );
68   void (*mapcoord_to_center_coord) ( MapCoord *src, VikCoord *dest );
69   void (*download) ( MapCoord *src, const gchar *dest_fn );
70   /* TODO: constant size (yay!) */
71 } VikMapsLayer_MapType;
72
73
74 /****** MAP TYPES ******/
75
76 static const VikMapsLayer_MapType __map_types[] = {
77
78 { 2, 200, 200, VIK_VIEWPORT_DRAWMODE_UTM, terraserver_topo_coord_to_mapcoord, terraserver_mapcoord_to_center_coord, terraserver_topo_download },
79 { 1, 200, 200, VIK_VIEWPORT_DRAWMODE_UTM, terraserver_aerial_coord_to_mapcoord, terraserver_mapcoord_to_center_coord, terraserver_aerial_download },
80 { 4, 200, 200, VIK_VIEWPORT_DRAWMODE_UTM, terraserver_urban_coord_to_mapcoord, terraserver_mapcoord_to_center_coord, terraserver_urban_download },
81 { 5, 0, 0, VIK_VIEWPORT_DRAWMODE_EXPEDIA, expedia_coord_to_mapcoord, expedia_mapcoord_to_center_coord, expedia_download },
82 { 9, 128, 128, VIK_VIEWPORT_DRAWMODE_GOOGLE, googlemaps_coord_to_mapcoord, googlemaps_mapcoord_to_center_coord, googlemaps_download },
83 { 8, 256, 256, VIK_VIEWPORT_DRAWMODE_KH, khmaps_coord_to_mapcoord, khmaps_mapcoord_to_center_coord, khmaps_download },
84 { 7, 256, 256, VIK_VIEWPORT_DRAWMODE_MERCATOR, google_coord_to_mapcoord, google_mapcoord_to_center_coord, google_download },
85 { 10, 256, 256, VIK_VIEWPORT_DRAWMODE_MERCATOR, google_coord_to_mapcoord, google_mapcoord_to_center_coord, google_trans_download },
86 { 11, 256, 256, VIK_VIEWPORT_DRAWMODE_MERCATOR, google_coord_to_mapcoord, google_mapcoord_to_center_coord, google_kh_download },
87 };
88
89 #define NUM_MAP_TYPES (sizeof(__map_types)/sizeof(__map_types[0]))
90
91 /******** MAPZOOMS *********/
92
93 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" };
94 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 };
95 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 };
96
97 static gchar *params_maptypes[] = { "Terraserver Topos", "Terraserver Aerials", "Terraserver Urban Areas", "Expedia (Street Maps)", "Google Maps (Street)", "KH Maps", "New (Mercator) Google", "Transparent Google", "New (Mercator) KH" };
98 static guint params_maptypes_ids[] = { 2, 1, 4, 5, 9, 8, 7, 10, 11 };
99 #define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]))
100
101 /**************************/
102
103
104 static VikMapsLayer *maps_layer_copy ( VikMapsLayer *vml, VikViewport *vvp );
105 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
106 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, 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 gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
115 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
116 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
117 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
118
119
120 static VikLayerParamScale params_scales[] = {
121   /* min, max, step, digits (decimal places) */
122  { 0, 255, 3, 0 }, /* alpha */
123 };
124
125 VikLayerParam maps_layer_params[] = {
126   { "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Map Type:", VIK_LAYER_WIDGET_RADIOGROUP, params_maptypes, params_maptypes_ids },
127   { "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, "Maps Directory (Optional):", VIK_LAYER_WIDGET_FILEENTRY },
128   { "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Alpha:", VIK_LAYER_WIDGET_HSCALE, params_scales },
129   { "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, "Autodownload maps:", VIK_LAYER_WIDGET_CHECKBUTTON },
130   { "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Zoom Level:", VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms },
131 };
132
133 enum { PARAM_MAPTYPE=0, PARAM_CACHE_DIR, PARAM_ALPHA, PARAM_AUTODOWNLOAD, PARAM_MAPZOOM, NUM_PARAMS };
134
135 static VikToolInterface maps_tools[] = {
136   { "Maps Download", (VikToolConstructorFunc) maps_layer_download_create, NULL, NULL, NULL,  
137     (VikToolMouseFunc) maps_layer_download_click, NULL,  (VikToolMouseFunc) maps_layer_download_release },
138 };
139
140 VikLayerInterface vik_maps_layer_interface = {
141   "Map",
142   &mapslayer_pixbuf,
143
144   maps_tools,
145   sizeof(maps_tools) / sizeof(maps_tools[0]),
146
147   maps_layer_params,
148   NUM_PARAMS,
149   NULL,
150   0,
151
152   (VikLayerFuncCreate)                  maps_layer_new,
153   (VikLayerFuncRealize)                 NULL,
154   (VikLayerFuncPostRead)                NULL,
155   (VikLayerFuncFree)                    maps_layer_free,
156
157   (VikLayerFuncProperties)              NULL,
158   (VikLayerFuncDraw)                    maps_layer_draw,
159   (VikLayerFuncChangeCoordMode)         NULL,
160
161   (VikLayerFuncAddMenuItems)            maps_layer_add_menu_items,
162   (VikLayerFuncSublayerAddMenuItems)    NULL,
163
164   (VikLayerFuncSublayerRenameRequest)   NULL,
165   (VikLayerFuncSublayerToggleVisible)   NULL,
166
167   (VikLayerFuncCopy)                    maps_layer_copy,
168   (VikLayerFuncMarshall)                maps_layer_marshall,
169   (VikLayerFuncUnmarshall)              maps_layer_unmarshall,
170
171   (VikLayerFuncSetParam)                maps_layer_set_param,
172   (VikLayerFuncGetParam)                maps_layer_get_param,
173
174   (VikLayerFuncReadFileData)            NULL,
175   (VikLayerFuncWriteFileData)           NULL,
176
177   (VikLayerFuncCopyItem)                NULL,
178   (VikLayerFuncPasteItem)               NULL,
179   (VikLayerFuncFreeCopiedItem)          NULL,
180   (VikLayerFuncDragDropRequest)         NULL,
181 };
182
183 struct _VikMapsLayer {
184   VikLayer vl;
185   guint maptype;
186   gchar *cache_dir;
187   guint8 alpha;
188   guint mapzoom_id;
189   gdouble xmapzoom, ymapzoom;
190
191   gboolean autodownload;
192
193   gint dl_tool_x, dl_tool_y;
194
195   GtkMenu *dl_right_click_menu;
196   VikCoord redownload_ul, redownload_br; /* right click menu only */
197   VikViewport *redownload_vvp;
198 };
199
200 enum { REDOWNLOAD_NONE = 0, REDOWNLOAD_BAD, REDOWNLOAD_ALL };
201
202
203 /****************************************/
204 /******** CACHE DIR STUFF ***************/
205 /****************************************/
206
207 #ifdef WINDOWS
208 #define MAPS_CACHE_DIR "C:\\VIKING-MAPS\\"
209 #define DIRSTRUCTURE "%st%ds%dz%d\\%d\\%d"
210 #else /* POSIX */
211
212 #include <stdlib.h>
213
214 #define MAPS_CACHE_DIR maps_layer_default_dir()
215 #define GLOBAL_MAPS_DIR "/var/cache/maps/"
216 #define DIRSTRUCTURE "%st%ds%dz%d/%d/%d"
217
218 static gchar *maps_layer_default_dir ()
219 {
220   static gchar defaultdir[512];
221   static gboolean already_run = 0;
222   if ( ! already_run )
223   {
224     /* Thanks to Mike Davison for the $VIKING_MAPS usage */
225     gchar *mapdir = getenv("VIKING_MAPS");
226     if ( mapdir && strlen(mapdir) < 497 ) {
227       strcpy ( defaultdir, mapdir );
228     } else if ( access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
229       strcpy ( defaultdir, GLOBAL_MAPS_DIR );
230     } else {
231       gchar *home = getenv("HOME");
232       if ( home && strlen(home) < 497 )
233       {
234         strcpy ( defaultdir, home );
235         strcat ( defaultdir, "/.viking-maps/" );
236       }
237       else
238       {
239         strcpy ( defaultdir, ".viking-maps/" );
240       }
241     }
242     already_run = 1;
243   }
244   return defaultdir;
245 }
246
247 #endif
248
249 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
250 {
251   if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && access ( vml->cache_dir, F_OK ) != 0 )
252   {
253 #ifdef WINDOWS
254     mkdir ( vml->cache_dir );
255 #else
256     mkdir ( vml->cache_dir, 0777 );
257 #endif
258   }
259 }
260
261 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
262 {
263   guint len;
264   g_assert ( vml != NULL);
265   if ( vml->cache_dir )
266     g_free ( vml->cache_dir );
267
268   if ( dir == NULL || dir[0] == '\0' )
269     vml->cache_dir = g_strdup ( MAPS_CACHE_DIR );
270   else
271   {
272     len = strlen(dir);
273     if ( dir[len-1] != VIKING_FILE_SEP )
274     {
275       vml->cache_dir = g_malloc ( len+2 );
276       strncpy ( vml->cache_dir, dir, len );
277       vml->cache_dir[len] = VIKING_FILE_SEP;
278       vml->cache_dir[len+1] = '\0';
279     }
280     else
281       vml->cache_dir = g_strdup ( dir );
282   }
283   maps_layer_mkdir_if_default_dir ( vml );
284 }
285
286 /****************************************/
287 /******** GOBJECT STUFF *****************/
288 /****************************************/
289
290 GType vik_maps_layer_get_type ()
291 {
292   static GType vml_type = 0;
293
294   if (!vml_type)
295   {
296     static const GTypeInfo vml_info =
297     {
298       sizeof (VikMapsLayerClass),
299       NULL, /* base_init */
300       NULL, /* base_finalize */
301       NULL, /* class init */
302       NULL, /* class_finalize */
303       NULL, /* class_data */
304       sizeof (VikMapsLayer),
305       0,
306       NULL /* instance init */
307     };
308     vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
309   }
310
311   return vml_type;
312 }
313
314 /****************************************/
315 /************** PARAMETERS **************/
316 /****************************************/
317
318 static guint map_index_to_uniq_id (guint8 index)
319 {
320   g_assert ( index < NUM_MAP_TYPES );
321   return __map_types[index].uniq_id;
322 }
323
324 static guint map_uniq_id_to_index ( guint uniq_id )
325 {
326   gint i;
327   for ( i = 0; i < NUM_MAP_TYPES; i++ )
328     if ( __map_types[i].uniq_id == uniq_id )
329       return i;
330   return NUM_MAP_TYPES; /* no such thing */
331 }
332
333 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp )
334 {
335   switch ( id )
336   {
337     case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
338     case PARAM_MAPTYPE: {
339       gint maptype = map_uniq_id_to_index(data.u);
340       if ( maptype == NUM_MAP_TYPES ) g_warning("Unknown map type");
341       else vml->maptype = maptype;
342       break;
343     }
344     case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
345     case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
346     case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
347                           vml->mapzoom_id = data.u;
348                           vml->xmapzoom = __mapzooms_x [data.u];
349                           vml->ymapzoom = __mapzooms_y [data.u];
350                         }else g_warning ("Unknown Map Zoom"); break;
351   }
352   return TRUE;
353 }
354
355 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id )
356 {
357   VikLayerParamData rv;
358   switch ( id )
359   {
360     case PARAM_CACHE_DIR: rv.s = (vml->cache_dir && strcmp(vml->cache_dir, MAPS_CACHE_DIR) != 0) ? vml->cache_dir : ""; break;
361     case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
362     case PARAM_ALPHA: rv.u = vml->alpha; break;
363     case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
364     case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
365   }
366   return rv;
367 }
368
369 /****************************************/
370 /****** CREATING, COPYING, FREEING ******/
371 /****************************************/
372
373 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
374 {
375   VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
376   vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS );
377   vml->maptype = 0;
378   vml->alpha = 255;
379   vml->mapzoom_id = 0;
380   vml->dl_tool_x = vml->dl_tool_y = -1;
381   maps_layer_set_cache_dir ( vml, NULL );
382   vml->autodownload = FALSE;
383
384   vml->dl_right_click_menu = NULL;
385
386   return vml;
387 }
388
389 static void maps_layer_free ( VikMapsLayer *vml )
390 {
391   if ( vml->cache_dir )
392     g_free ( vml->cache_dir );
393   if ( vml->dl_right_click_menu )
394     gtk_object_sink ( GTK_OBJECT(vml->dl_right_click_menu) );
395 }
396
397 static VikMapsLayer *maps_layer_copy ( VikMapsLayer *vml, VikViewport *vvp )
398 {
399   VikMapsLayer *rv = maps_layer_new ( vvp );
400   *rv = *vml;
401   rv->cache_dir = g_strdup(rv->cache_dir);
402   VIK_LAYER(rv)->name = NULL;
403   return rv;
404 }
405
406 static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
407 {
408   vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
409 }
410
411 static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
412 {
413   VikMapsLayer *rv = maps_layer_new ( vvp );
414   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
415   return rv;
416 }
417
418 /*********************/
419 /****** DRAWING ******/
420 /*********************/
421
422 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
423 {
424   guchar *pixels;
425   gint width, height, iii, jjj;
426
427   if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
428   {
429     GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
430     g_object_unref(G_OBJECT(pixbuf));
431     pixbuf = tmp;
432   }
433
434   pixels = gdk_pixbuf_get_pixels(pixbuf);
435   width = gdk_pixbuf_get_width(pixbuf);
436   height = gdk_pixbuf_get_height(pixbuf);
437
438   /* r,g,b,a,r,g,b,a.... */
439   for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
440   {
441     pixels += 3;
442     *pixels++ = alpha;
443   }
444   return pixbuf;
445 }
446
447 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
448 {
449   GdkPixbuf *tmp;
450   guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
451   tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
452   g_object_unref ( G_OBJECT(pixbuf) );
453   return tmp;
454 }
455
456 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
457 {
458   GdkPixbuf *pixbuf;
459
460   /* get the thing */
461   pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
462                             mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
463
464   if ( ! pixbuf ) {
465     g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
466                      vml->cache_dir, mode,
467                      mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
468     if ( access ( filename_buf, R_OK ) == 0) {
469     {
470       GError *gx = NULL;
471       pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
472
473       if (gx)
474       {
475         if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
476           g_warning ( "Couldn't open image file: %s", gx->message );
477
478           g_error_free ( gx );
479           if ( pixbuf )
480             g_object_unref ( G_OBJECT(pixbuf) );
481           pixbuf = NULL;
482         } else {
483           if ( vml->alpha < 255 )
484             pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
485           if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
486             pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
487
488           a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y, 
489               mapcoord->z, __map_types[vml->maptype].uniq_id, 
490               mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
491         }
492       }
493     }
494   }
495   return pixbuf;
496 }
497
498 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
499 {
500   MapCoord ulm, brm;
501   gdouble xzoom = vik_viewport_get_xmpp ( vvp );
502   gdouble yzoom = vik_viewport_get_ympp ( vvp );
503   gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
504
505   if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
506     xshrinkfactor = vml->xmapzoom / xzoom;
507     yshrinkfactor = vml->ymapzoom / yzoom;
508     if ( xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
509          yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) {
510       xzoom = vml->xmapzoom;
511       yzoom = vml->xmapzoom;
512     } else {
513       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 );
514     }
515   }
516
517   /* coord -> ID */
518   if ( __map_types[vml->maptype].coord_to_mapcoord ( ul, xzoom, yzoom, &ulm ) &&
519        __map_types[vml->maptype].coord_to_mapcoord ( br, xzoom, yzoom, &brm ) ) {
520
521     /* loop & draw */
522     gint x, y;
523     gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
524     gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
525     gint mode = __map_types[vml->maptype].uniq_id;
526
527     VikCoord coord;
528     gint xx, yy, width, height;
529     GdkPixbuf *pixbuf;
530
531     guint max_path_len = strlen(vml->cache_dir) + 40;
532     gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
533
534     if ( vml->autodownload )
535       start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
536
537     if ( __map_types[vml->maptype].tilesize_x == 0 ) {
538       for ( x = xmin; x <= xmax; x++ ) {
539         for ( y = ymin; y <= ymax; y++ ) {
540           ulm.x = x;
541           ulm.y = y;
542           pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
543           if ( pixbuf ) {
544             width = gdk_pixbuf_get_width ( pixbuf );
545             height = gdk_pixbuf_get_height ( pixbuf );
546
547             __map_types[vml->maptype].mapcoord_to_center_coord ( &ulm, &coord );
548             vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
549             xx -= (width/2);
550             yy -= (height/2);
551
552             vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
553           }
554         }
555       }
556     } else { /* tilesize is known, don't have to keep converting coords */
557       gdouble tilesize_x = __map_types[vml->maptype].tilesize_x * xshrinkfactor;
558       gdouble tilesize_y = __map_types[vml->maptype].tilesize_y * yshrinkfactor;
559       /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
560       gint tilesize_x_ceil = ceil ( tilesize_x );
561       gint tilesize_y_ceil = ceil ( tilesize_y );
562       gint8 xinc = (ulm.x == xmin) ? 1 : -1;
563       gint8 yinc = (ulm.y == ymin) ? 1 : -1;
564       gdouble xx, yy; gint xx_tmp, yy_tmp;
565       gint base_yy, xend, yend;
566       xend = (xinc == 1) ? (xmax+1) : (xmin-1);
567       yend = (yinc == 1) ? (ymax+1) : (ymin-1);
568
569       __map_types[vml->maptype].mapcoord_to_center_coord ( &ulm, &coord );
570       vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
571       xx = xx_tmp; yy = yy_tmp;
572       /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
573        * eg if tile size 128, shrinkfactor 0.333 */
574       xx -= (tilesize_x/2);
575       base_yy = yy - (tilesize_y/2);
576
577       for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
578         yy = base_yy;
579         for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
580           ulm.x = x;
581           ulm.y = y;
582           pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
583           if ( pixbuf )
584             vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
585
586           yy += tilesize_y;
587         }
588         xx += tilesize_x;
589       }
590     }
591
592     g_free ( path_buf );
593   }
594 }
595
596 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
597 {
598   if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
599   {
600     VikCoord ul, br;
601
602     /* get corner coords */
603     if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
604       /* UTM multi-zone stuff by Kit Transue */
605       gchar leftmost_zone, rightmost_zone, i;
606       leftmost_zone = vik_viewport_leftmost_zone( vvp );
607       rightmost_zone = vik_viewport_rightmost_zone( vvp );
608       for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
609         vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
610         maps_layer_draw_section ( vml, vvp, &ul, &br );
611       }
612     }
613     else {
614       vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
615       vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
616
617       maps_layer_draw_section ( vml, vvp, &ul, &br );
618     }
619   }
620 }
621
622 /*************************/
623 /****** DOWNLOADING ******/
624 /*************************/
625
626 /* pass along data to thread, exists even if layer is deleted. */
627 typedef struct {
628   gchar *cache_dir;
629   gchar *filename_buf;
630   gint x0, y0, xf, yf;
631   MapCoord mapcoord;
632   gint maptype;
633   gint maxlen;
634   gint mapstoget;
635   gint redownload;
636 } MapDownloadInfo;
637
638 static void mdi_free ( MapDownloadInfo *mdi )
639 {
640   g_free ( mdi->cache_dir );
641   g_free ( mdi->filename_buf );
642   g_free ( mdi );
643 }
644
645 static void map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
646 {
647   guint donemaps = 0;
648   gint x, y;
649   for ( x = mdi->x0; x <= mdi->xf; x++ )
650   {
651     for ( y = mdi->y0; y <= mdi->yf; y++ )
652     {
653       g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
654                      mdi->cache_dir, __map_types[mdi->maptype].uniq_id,
655                      mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
656
657       if ( mdi->redownload == REDOWNLOAD_ALL)
658         remove ( mdi->filename_buf );
659       else if ( mdi->redownload == REDOWNLOAD_BAD && access ( mdi->filename_buf, F_OK ) == 0 )
660       {
661         /* see if this one is bad or what */
662         GError *gx = NULL;
663         GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
664         if (gx || (!pixbuf))
665           remove ( mdi->filename_buf );
666         if ( pixbuf )
667           g_object_unref ( pixbuf );
668         if ( gx )
669           g_error_free ( gx );
670       }
671
672       if ( access ( mdi->filename_buf, F_OK ) != 0 )
673       {
674         mdi->mapcoord.x = x; mdi->mapcoord.y = y;
675         __map_types[mdi->maptype].download ( &(mdi->mapcoord), mdi->filename_buf );
676         mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
677
678        if ( mdi->redownload !=- REDOWNLOAD_NONE )
679          a_mapcache_remove_all_shrinkfactors ( x, y, mdi->mapcoord.z, mdi->maptype, mdi->mapcoord.scale );
680
681
682         donemaps++;
683         a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
684       }
685     }
686   }
687 }
688
689 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
690 {
691   if ( mdi->mapcoord.x || mdi->mapcoord.y )
692   {
693     g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
694                      mdi->cache_dir, __map_types[mdi->maptype].uniq_id,
695                      mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
696     if ( access ( mdi->filename_buf, F_OK ) == 0)
697     {
698       remove ( mdi->filename_buf );
699     }
700   }
701 }
702
703 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
704 {
705   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
706   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
707   MapCoord ulm, brm;
708   if ( __map_types[vml->maptype].coord_to_mapcoord ( ul, xzoom, yzoom, &ulm ) 
709     && __map_types[vml->maptype].coord_to_mapcoord ( br, xzoom, yzoom, &brm ) )
710   {
711     MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
712     gint a, b;
713
714     /* cache_dir and buffer for dest filename */
715     mdi->cache_dir = g_strdup ( vml->cache_dir );
716     mdi->maxlen = strlen ( vml->cache_dir ) + 40;
717     mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
718     mdi->maptype = vml->maptype;
719
720     mdi->mapcoord = ulm;
721
722     mdi->redownload = redownload;
723
724     mdi->x0 = MIN(ulm.x, brm.x);
725     mdi->xf = MAX(ulm.x, brm.x);
726     mdi->y0 = MIN(ulm.y, brm.y);
727     mdi->yf = MAX(ulm.y, brm.y);
728
729     mdi->mapstoget = 0;
730
731     if ( mdi->redownload ) {
732       mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
733     } else {
734       /* calculate how many we need */
735       for ( a = mdi->x0; a <= mdi->xf; a++ )
736       {
737         for ( b = mdi->y0; b <= mdi->yf; b++ )
738         {
739           g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
740                        vml->cache_dir, __map_types[vml->maptype].uniq_id, ulm.scale,
741                        ulm.z, a, b );
742           if ( access ( mdi->filename_buf, F_OK ) != 0)
743             mdi->mapstoget++;
744         }
745       }
746     }
747
748     mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
749
750     if ( mdi->mapstoget )
751     {
752       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" );
753
754       /* launch the thread */
755       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
756                             tmp,                                              /* description string */
757                             (vik_thr_func) map_download_thread,               /* function to call within thread */
758                             mdi,                                              /* pass along data */
759                             (vik_thr_free_func) mdi_free,                     /* function to free pass along data */
760                             (vik_thr_free_func) mdi_cancel_cleanup,
761                             mdi->mapstoget );
762       g_free ( tmp );
763     }
764     else
765       mdi_free ( mdi );
766   }
767 }
768
769 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
770 {
771   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
772 }
773 static void maps_layer_redownload_all ( VikMapsLayer *vml )
774 {
775   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
776 }
777
778 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
779 {
780   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
781     return FALSE;
782   if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
783   {
784     if ( event->button == 1 )
785     {
786       VikCoord ul, br;
787       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 );
788       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 );
789       start_download_thread ( vml, vvp, &ul, &br, REDOWNLOAD_NONE );
790       vml->dl_tool_x = vml->dl_tool_y = -1;
791       return TRUE;
792     }
793     else
794     {
795       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) );
796       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) );
797
798       vml->redownload_vvp = vvp;
799
800       vml->dl_tool_x = vml->dl_tool_y = -1;
801
802       if ( ! vml->dl_right_click_menu ) {
803         GtkWidget *item;
804         vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
805
806         item = gtk_menu_item_new_with_label ( "Redownload bad map(s)" );
807         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
808         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
809
810         item = gtk_menu_item_new_with_label ( "Redownload all map(s)" );
811         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
812         gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
813       }
814
815       gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
816       gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
817     }
818   }
819   return FALSE;
820 }
821
822 static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
823 {
824   return vvp;
825 }
826
827 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
828 {
829   MapCoord tmp;
830   if (!vml || vml->vl.type != VIK_LAYER_MAPS)
831     return FALSE;
832   if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) &&
833     __map_types[vml->maptype].coord_to_mapcoord ( vik_viewport_get_center ( vvp ),
834            vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
835            vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
836            &tmp ) ) {
837     vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
838     return TRUE;
839   }
840   return FALSE;
841
842  
843 #if 0
844   if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
845   {
846     VikCoord coord;
847     MapCoord mapcoord;
848     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
849     if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
850                 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
851                 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
852                 &mapcoord ) ) {
853       gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
854                      vml->cache_dir, __map_types[vml->maptype].uniq_id,
855                      mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
856
857       __map_types[vml->maptype].download ( &mapcoord, filename_buf );
858       g_free ( filename_buf );
859       vik_layer_emit_update ( VIK_LAYER(vml) );
860       return TRUE;
861     }
862   }
863   return FALSE;
864 #endif
865 }
866
867 static void maps_layer_download_onscreen_maps ( gpointer vml_vvp[2] )
868 {
869   VikMapsLayer *vml = vml_vvp[0];
870   VikViewport *vvp = vml_vvp[1];
871
872   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
873   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
874
875   VikCoord ul, br;
876   MapCoord ulm, brm;
877
878   vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
879   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
880
881   if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) &&
882        __map_types[vml->maptype].coord_to_mapcoord ( &ul, xzoom, yzoom, &ulm ) &&
883        __map_types[vml->maptype].coord_to_mapcoord ( &br, xzoom, yzoom, &brm ) )
884     start_download_thread ( vml, vvp, &ul, &br, REDOWNLOAD_NONE );
885   else
886     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), "Wrong drawmode / zoom level for this map." );
887
888 }
889
890 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
891 {
892   static gpointer pass_along[2];
893   GtkWidget *item;
894   pass_along[0] = vml;
895   pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
896
897   item = gtk_menu_item_new();
898   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
899   gtk_widget_show ( item );
900
901   item = gtk_menu_item_new_with_label ( "Download Onscreen Maps" );
902   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_onscreen_maps), pass_along );
903   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
904   gtk_widget_show ( item );
905 }