]> git.street.me.uk Git - andy/viking.git/blob - src/vikmapslayer.c
Update TODO/ChangeLog
[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 #include "usgs.h"
62
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;
73
74
75 /****** MAP TYPES ******/
76
77 static const VikMapsLayer_MapType __map_types[] = {
78
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 },
89 };
90
91 #define NUM_MAP_TYPES (sizeof(__map_types)/sizeof(__map_types[0]))
92
93 /******** MAPZOOMS *********/
94
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 };
98
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]))
102
103 /**************************/
104
105
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 );
117
118
119 static VikLayerParamScale params_scales[] = {
120   /* min, max, step, digits (decimal places) */
121  { 0, 255, 3, 0 }, /* alpha */
122 };
123
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 },
130 };
131
132 enum { PARAM_MAPTYPE=0, PARAM_CACHE_DIR, PARAM_ALPHA, PARAM_AUTODOWNLOAD, PARAM_MAPZOOM, NUM_PARAMS };
133
134 static VikToolInterface maps_tools[] = {
135   { "Maps Download", (VikToolInterfaceFunc) maps_layer_download_click, (VikToolInterfaceFunc) maps_layer_download_release },
136 };
137
138 VikLayerInterface vik_maps_layer_interface = {
139   "Map",
140   &mapslayer_pixbuf,
141
142   maps_tools,
143   sizeof(maps_tools) / sizeof(maps_tools[0]),
144
145   maps_layer_params,
146   NUM_PARAMS,
147   NULL,
148   0,
149
150   (VikLayerFuncCreate)                  maps_layer_new,
151   (VikLayerFuncRealize)                 NULL,
152   (VikLayerFuncPostRead)                NULL,
153   (VikLayerFuncFree)                    maps_layer_free,
154
155   (VikLayerFuncProperties)              NULL,
156   (VikLayerFuncDraw)                    maps_layer_draw,
157   (VikLayerFuncChangeCoordMode)         NULL,
158
159   (VikLayerFuncAddMenuItems)            maps_layer_add_menu_items,
160   (VikLayerFuncSublayerAddMenuItems)    NULL,
161
162   (VikLayerFuncSublayerRenameRequest)   NULL,
163   (VikLayerFuncSublayerToggleVisible)   NULL,
164
165   (VikLayerFuncCopy)                    maps_layer_copy,
166
167   (VikLayerFuncSetParam)                maps_layer_set_param,
168   (VikLayerFuncGetParam)                maps_layer_get_param,
169
170   (VikLayerFuncReadFileData)            NULL,
171   (VikLayerFuncWriteFileData)           NULL,
172
173   (VikLayerFuncCopyItem)                NULL,
174   (VikLayerFuncPasteItem)               NULL,
175   (VikLayerFuncFreeCopiedItem)          NULL,
176 };
177
178 struct _VikMapsLayer {
179   VikLayer vl;
180   guint maptype;
181   gchar *cache_dir;
182   guint8 alpha;
183   guint mapzoom_id;
184   gdouble xmapzoom, ymapzoom;
185
186   gboolean autodownload;
187
188   gint dl_tool_x, dl_tool_y;
189
190   GtkMenu *dl_right_click_menu;
191   VikCoord redownload_ul, redownload_br; /* right click menu only */
192   VikViewport *redownload_vvp;
193 };
194
195 enum { REDOWNLOAD_NONE = 0, REDOWNLOAD_BAD, REDOWNLOAD_ALL };
196
197
198 /****************************************/
199 /******** CACHE DIR STUFF ***************/
200 /****************************************/
201
202 #ifdef WINDOWS
203 #define MAPS_CACHE_DIR "C:\\VIKING-MAPS\\"
204 #define DIRSTRUCTURE "%st%ds%dz%d\\%d\\%d"
205 #else /* POSIX */
206
207 #include <stdlib.h>
208
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"
212
213 static gchar *maps_layer_default_dir ()
214 {
215   static gchar defaultdir[512];
216   static gboolean already_run = 0;
217   if ( ! already_run )
218   {
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 )
228       {
229         strcpy ( defaultdir, home );
230         strcat ( defaultdir, "/.viking-maps/" );
231       }
232       else
233       {
234         strcpy ( defaultdir, ".viking-maps/" );
235       }
236     }
237     already_run = 1;
238   }
239   return defaultdir;
240 }
241
242 #endif
243
244 static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
245 {
246   if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && access ( vml->cache_dir, F_OK ) != 0 )
247   {
248 #ifdef WINDOWS
249     mkdir ( vml->cache_dir );
250 #else
251     mkdir ( vml->cache_dir, 0777 );
252 #endif
253   }
254 }
255
256 static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
257 {
258   guint len;
259   g_assert ( vml != NULL);
260   if ( vml->cache_dir )
261     g_free ( vml->cache_dir );
262
263   if ( dir == NULL || dir[0] == '\0' )
264     vml->cache_dir = g_strdup ( MAPS_CACHE_DIR );
265   else
266   {
267     len = strlen(dir);
268     if ( dir[len-1] != VIKING_FILE_SEP )
269     {
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';
274     }
275     else
276       vml->cache_dir = g_strdup ( dir );
277   }
278   maps_layer_mkdir_if_default_dir ( vml );
279 }
280
281 /****************************************/
282 /******** GOBJECT STUFF *****************/
283 /****************************************/
284
285 GType vik_maps_layer_get_type ()
286 {
287   static GType vml_type = 0;
288
289   if (!vml_type)
290   {
291     static const GTypeInfo vml_info =
292     {
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),
300       0,
301       NULL /* instance init */
302     };
303     vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
304   }
305
306   return vml_type;
307 }
308
309 /****************************************/
310 /************** PARAMETERS **************/
311 /****************************************/
312
313 static guint map_index_to_uniq_id (guint8 index)
314 {
315   g_assert ( index < NUM_MAP_TYPES );
316   return __map_types[index].uniq_id;
317 }
318
319 static guint map_uniq_id_to_index ( guint uniq_id )
320 {
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 */
326 }
327
328 static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp )
329 {
330   switch ( id )
331   {
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;
338     }
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;
346   }
347   return TRUE;
348 }
349
350 static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id )
351 {
352   VikLayerParamData rv;
353   switch ( id )
354   {
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;
360   }
361   return rv;
362 }
363
364 /****************************************/
365 /****** CREATING, COPYING, FREEING ******/
366 /****************************************/
367
368 static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
369 {
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;
378
379   vml->dl_right_click_menu = NULL;
380
381   return vml;
382 }
383
384 static void maps_layer_free ( VikMapsLayer *vml )
385 {
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) );
390 }
391
392 static VikMapsLayer *maps_layer_copy ( VikMapsLayer *vml, VikViewport *vvp )
393 {
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;
399 }
400
401 /*********************/
402 /****** DRAWING ******/
403 /*********************/
404
405 static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
406 {
407   guchar *pixels;
408   gint width, height, iii, jjj;
409
410   if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
411   {
412     GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
413     g_object_unref(G_OBJECT(pixbuf));
414     pixbuf = tmp;
415   }
416
417   pixels = gdk_pixbuf_get_pixels(pixbuf);
418   width = gdk_pixbuf_get_width(pixbuf);
419   height = gdk_pixbuf_get_height(pixbuf);
420
421   /* r,g,b,a,r,g,b,a.... */
422   for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
423   {
424     pixels += 3;
425     *pixels++ = alpha;
426   }
427   return pixbuf;
428 }
429
430 static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
431 {
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;
437 }
438
439 static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
440 {
441   GdkPixbuf *pixbuf;
442
443   /* get the thing */
444   pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
445                             mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
446
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) {
452     {
453       GError *gx = NULL;
454       pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
455
456       if (gx)
457       {
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 );
460
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 );
470
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 );
474         }
475       }
476     }
477   }
478   return pixbuf;
479 }
480
481 static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
482 {
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;
487
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 );
497     }
498   }
499
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 ) ) {
503
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;
509
510     VikCoord coord;
511     gint xx, yy, width, height;
512     GdkPixbuf *pixbuf;
513
514     guint max_path_len = strlen(vml->cache_dir) + 40;
515     gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
516
517     if ( vml->autodownload )
518       start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
519
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 );
529
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);
534
535             vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
536           }
537         }
538       }
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);
551
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);
559
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 );
568
569           yy += tilesize_y;
570         }
571         xx += tilesize_x;
572       }
573     }
574
575     g_free ( path_buf );
576   }
577 }
578
579 static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
580 {
581   if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
582   {
583     VikCoord ul, br;
584
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 );
594       }
595     }
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 );
599
600       maps_layer_draw_section ( vml, vvp, &ul, &br );
601     }
602   }
603 }
604
605 /*************************/
606 /****** DOWNLOADING ******/
607 /*************************/
608
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;
620
621 static void mdi_free ( MapDownloadInfo *mdi )
622 {
623   g_free ( mdi->cache_dir );
624   g_free ( mdi->filename_buf );
625   g_free ( mdi );
626 }
627
628 static void map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
629 {
630   guint donemaps = 0;
631   gint x, y;
632   for ( x = mdi->x0; x <= mdi->xf; x++ )
633   {
634     for ( y = mdi->y0; y <= mdi->yf; y++ )
635     {
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 );
639
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 )
643       {
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 );
653       }
654
655       if ( access ( mdi->filename_buf, F_OK ) != 0 )
656       {
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 */
660
661        if ( mdi->redownload !=- REDOWNLOAD_NONE )
662          a_mapcache_remove_all_shrinkfactors ( x, y, mdi->mapcoord.z, mdi->maptype, mdi->mapcoord.scale );
663
664
665         donemaps++;
666         a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
667       }
668     }
669   }
670 }
671
672 static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
673 {
674   if ( mdi->mapcoord.x || mdi->mapcoord.y )
675   {
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)
680     {
681       remove ( mdi->filename_buf );
682     }
683   }
684 }
685
686 static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
687 {
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 ) )
693   {
694     MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
695     gint a, b;
696
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;
702
703     mdi->mapcoord = ulm;
704
705     mdi->redownload = redownload;
706
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);
711
712     mdi->mapstoget = 0;
713
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++ )
719       {
720         for ( b = mdi->y0; b <= mdi->yf; b++ )
721         {
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++;
727         }
728       }
729     }
730
731     mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
732
733     if ( mdi->mapstoget )
734     {
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" );
736
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 );
746     }
747     else
748       mdi_free ( mdi );
749   }
750 }
751
752 static void maps_layer_redownload_bad ( VikMapsLayer *vml )
753 {
754   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
755 }
756 static void maps_layer_redownload_all ( VikMapsLayer *vml )
757 {
758   start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
759 }
760
761 static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
762 {
763
764   if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
765   {
766     if ( event->button == 1 )
767     {
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;
774     }
775     else
776     {
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) );
779
780       vml->redownload_vvp = vvp;
781
782       vml->dl_tool_x = vml->dl_tool_y = -1;
783
784       if ( ! vml->dl_right_click_menu ) {
785         GtkWidget *item;
786         vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
787
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 );
791
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 );
795       }
796
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) );
799     }
800   }
801   return FALSE;
802 }
803
804 static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
805 {
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;
814   }
815   return FALSE;
816
817  
818 #if 0
819   if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
820   {
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 );
831
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;
836     }
837   }
838   return FALSE;
839 #endif
840 }
841
842 static void maps_layer_download_onscreen_maps ( gpointer vml_vvp[2] )
843 {
844   VikMapsLayer *vml = vml_vvp[0];
845   VikViewport *vvp = vml_vvp[1];
846
847   gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
848   gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
849
850   VikCoord ul, br;
851   MapCoord ulm, brm;
852
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 );
855
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." );
862
863 }
864
865 static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
866 {
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) );
871
872   item = gtk_menu_item_new();
873   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
874   gtk_widget_show ( item );
875
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 );
880 }