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