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