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