]> git.street.me.uk Git - andy/viking.git/blame_incremental - src/vikmapslayer.c
When manually creating a track, automatically give it a default name.
[andy/viking.git] / src / vikmapslayer.c
... / ...
CommitLineData
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
4 * Copyright (C) 2005, Evan Battaglia <viking@greentorch.org>
5 * Copyright (C) 2010, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
6 * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
7 * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#define MAX_SHRINKFACTOR 8.0000001 /* zoom 1 viewing 8-tiles */
30#define MIN_SHRINKFACTOR 0.0312499 /* zoom 32 viewing 1-tiles */
31
32#define REAL_MIN_SHRINKFACTOR 0.0039062499 /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
33
34#include <gtk/gtk.h>
35#include <gdk-pixbuf/gdk-pixdata.h>
36#include <glib.h>
37#include <glib/gstdio.h>
38#include <glib/gi18n.h>
39
40#ifdef HAVE_STRING_H
41#include <string.h>
42#endif
43#ifdef HAVE_MATH_H
44#include <math.h>
45#endif
46
47#include "globals.h"
48#include "coords.h"
49#include "vikcoord.h"
50#include "viktreeview.h"
51#include "vikviewport.h"
52#include "viklayer.h"
53#include "vikmapslayer.h"
54
55#ifdef HAVE_UNISTD_H
56#include <unistd.h>
57#endif
58
59#include "mapcache.h"
60/* only for dialog.h -- ugh */
61#include "vikwaypoint.h"
62#include "dialog.h"
63#include "preferences.h"
64
65#include "vikstatus.h"
66#include "background.h"
67
68#include "vikaggregatelayer.h"
69#include "viklayerspanel.h"
70
71#include "mapcoord.h"
72#include "terraserver.h"
73
74#include "icons/icons.h"
75
76/****** MAP TYPES ******/
77
78static GList *__map_types = NULL;
79
80#define NUM_MAP_TYPES g_list_length(__map_types)
81
82/* List of label for each map type */
83static gchar **params_maptypes = NULL;
84
85/* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
86static guint *params_maptypes_ids = NULL;
87
88/******** MAPZOOMS *********/
89
90static gchar *params_mapzooms[] = { N_("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 };
91static 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 };
92static 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 };
93
94#define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
95
96/**************************/
97
98
99static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
100static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
101static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
102static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp );
103static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id );
104static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
105static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
106static void maps_layer_free ( VikMapsLayer *vml );
107static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
108static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
109static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
110static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
111static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
112static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
113static guint map_uniq_id_to_index ( guint uniq_id );
114
115
116static VikLayerParamScale params_scales[] = {
117 /* min, max, step, digits (decimal places) */
118 { 0, 255, 3, 0 }, /* alpha */
119};
120
121VikLayerParam maps_layer_params[] = {
122 { "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL },
123 { "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY },
124 { "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales },
125 { "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload maps:"), VIK_LAYER_WIDGET_CHECKBUTTON },
126 { "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL },
127};
128
129enum { PARAM_MAPTYPE=0, PARAM_CACHE_DIR, PARAM_ALPHA, PARAM_AUTODOWNLOAD, PARAM_MAPZOOM, NUM_PARAMS };
130
131static VikToolInterface maps_tools[] = {
132 { N_("Maps Download"), (VikToolConstructorFunc) maps_layer_download_create, NULL, NULL, NULL,
133 (VikToolMouseFunc) maps_layer_download_click, NULL, (VikToolMouseFunc) maps_layer_download_release,
134 (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf },
135};
136
137VikLayerInterface vik_maps_layer_interface = {
138 N_("Map"),
139 &vikmapslayer_pixbuf,
140
141 maps_tools,
142 sizeof(maps_tools) / sizeof(maps_tools[0]),
143
144 maps_layer_params,
145 NUM_PARAMS,
146 NULL,
147 0,
148
149 VIK_MENU_ITEM_ALL,
150
151 (VikLayerFuncCreate) maps_layer_new,
152 (VikLayerFuncRealize) NULL,
153 (VikLayerFuncPostRead) maps_layer_post_read,
154 (VikLayerFuncFree) maps_layer_free,
155
156 (VikLayerFuncProperties) NULL,
157 (VikLayerFuncDraw) maps_layer_draw,
158 (VikLayerFuncChangeCoordMode) NULL,
159
160 (VikLayerFuncSetMenuItemsSelection) NULL,
161 (VikLayerFuncGetMenuItemsSelection) NULL,
162
163 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
164 (VikLayerFuncSublayerAddMenuItems) NULL,
165
166 (VikLayerFuncSublayerRenameRequest) NULL,
167 (VikLayerFuncSublayerToggleVisible) NULL,
168
169 (VikLayerFuncMarshall) maps_layer_marshall,
170 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
171
172 (VikLayerFuncSetParam) maps_layer_set_param,
173 (VikLayerFuncGetParam) maps_layer_get_param,
174
175 (VikLayerFuncReadFileData) NULL,
176 (VikLayerFuncWriteFileData) NULL,
177
178 (VikLayerFuncDeleteItem) NULL,
179 (VikLayerFuncCopyItem) NULL,
180 (VikLayerFuncPasteItem) NULL,
181 (VikLayerFuncFreeCopiedItem) NULL,
182 (VikLayerFuncDragDropRequest) NULL,
183};
184
185struct _VikMapsLayer {
186 VikLayer vl;
187 guint maptype;
188 gchar *cache_dir;
189 guint8 alpha;
190 guint mapzoom_id;
191 gdouble xmapzoom, ymapzoom;
192
193 gboolean autodownload;
194 VikCoord *last_center;
195 gdouble last_xmpp;
196 gdouble last_ympp;
197
198 gint dl_tool_x, dl_tool_y;
199
200 GtkMenu *dl_right_click_menu;
201 VikCoord redownload_ul, redownload_br; /* right click menu only */
202 VikViewport *redownload_vvp;
203};
204
205enum { REDOWNLOAD_NONE = 0, /* download only missing maps */
206 REDOWNLOAD_BAD, /* download missing and bad maps */
207 REDOWNLOAD_NEW, /* download missing maps that are newer on server only */
208 REDOWNLOAD_ALL, /* download all maps */
209 DOWNLOAD_OR_REFRESH }; /* download missing maps and refresh cache */
210
211static VikLayerParam prefs[] = {
212 { VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Default maplayer directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL },
213};
214
215void maps_layer_init ()
216{
217 VikLayerParamData tmp;
218 tmp.s = maps_layer_default_dir();
219 a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
220}
221
222/****************************************/
223/******** MAPS LAYER TYPES **************/
224/****************************************/
225
226int _get_index_for_id ( guint id )
227{
228 int index = 0 ;
229 while (params_maptypes_ids[index] != 0)
230 {
231 if (params_maptypes_ids[index] == id)
232 return index;
233 index++;
234 }
235 return -1;
236}
237
238void _add_map_source ( guint id, const char *label, VikMapSource *map )
239{
240 gsize len = 0;
241 if (params_maptypes)
242 len = g_strv_length (params_maptypes);
243 /* Add the label */
244 params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
245 params_maptypes[len] = g_strdup (label);
246 params_maptypes[len+1] = NULL;
247
248 /* Add the id */
249 params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
250 params_maptypes_ids[len] = id;
251 params_maptypes_ids[len+1] = 0;
252
253 /* We have to clone */
254 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
255 /* Register the clone in the list */
256 __map_types = g_list_append(__map_types, clone);
257
258 /* Hack
259 We have to ensure the mode LayerParam references the up-to-date
260 GLists.
261 */
262 /*
263 memcpy(&maps_layer_params[0].widget_data, &params_maptypes, sizeof(gpointer));
264 memcpy(&maps_layer_params[0].extra_widget_data, &params_maptypes_ids, sizeof(gpointer));
265 */
266 maps_layer_params[0].widget_data = params_maptypes;
267 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
268}
269
270void _update_map_source ( const char *label, VikMapSource *map, int index )
271{
272 GList *item = g_list_nth (__map_types, index);
273 g_object_unref (item->data);
274 item->data = g_object_ref (map);
275 /* Change previous data */
276 g_free (params_maptypes[index]);
277 params_maptypes[index] = g_strdup (label);
278}
279
280void maps_layer_register_map_source ( VikMapSource *map )
281{
282 g_assert(map != NULL);
283
284 guint id = vik_map_source_get_uniq_id(map);
285 const char *label = vik_map_source_get_label(map);
286 g_assert(label != NULL);
287
288 int previous = map_uniq_id_to_index (id);
289 if (previous != NUM_MAP_TYPES)
290 {
291 _update_map_source (label, map, previous);
292 }
293 else
294 {
295 _add_map_source (id, label, map);
296 }
297}
298
299#define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
300#define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
301#define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
302
303gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
304{
305 return(vml->maptype);
306}
307
308gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
309{
310 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
311}
312
313/****************************************/
314/******** CACHE DIR STUFF ***************/
315/****************************************/
316
317#define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
318#define MAPS_CACHE_DIR maps_layer_default_dir()
319
320#ifdef WINDOWS
321#include <io.h>
322#define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
323#define LOCAL_MAPS_DIR "VIKING-MAPS"
324#else /* POSIX */
325#include <stdlib.h>
326#define GLOBAL_MAPS_DIR "/var/cache/maps/"
327#define LOCAL_MAPS_DIR ".viking-maps"
328#endif
329
330gchar *maps_layer_default_dir ()
331{
332 static gchar *defaultdir = NULL;
333 if ( ! defaultdir )
334 {
335 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
336 const gchar *mapdir = g_getenv("VIKING_MAPS");
337 if ( mapdir ) {
338 defaultdir = g_strdup ( mapdir );
339 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
340 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
341 } else {
342 const gchar *home = g_get_home_dir();
343 if (!home || g_access(home, W_OK))
344 home = g_get_home_dir ();
345 if ( home )
346 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
347 else
348 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
349 }
350 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
351 {
352 /* Add the separator at the end */
353 gchar *tmp = defaultdir;
354 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
355 g_free(tmp);
356 }
357 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
358 }
359 return defaultdir;
360}
361
362static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
363{
364 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
365 {
366 g_mkdir ( vml->cache_dir, 0777 );
367 }
368}
369
370static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
371{
372 guint len;
373 g_assert ( vml != NULL);
374 g_free ( vml->cache_dir );
375 vml->cache_dir = NULL;
376
377 if ( dir == NULL || dir[0] == '\0' )
378 vml->cache_dir = g_strdup ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s );
379 else
380 {
381 len = strlen(dir);
382 if ( dir[len-1] != G_DIR_SEPARATOR )
383 {
384 vml->cache_dir = g_malloc ( len+2 );
385 strncpy ( vml->cache_dir, dir, len );
386 vml->cache_dir[len] = G_DIR_SEPARATOR;
387 vml->cache_dir[len+1] = '\0';
388 }
389 else
390 vml->cache_dir = g_strdup ( dir );
391 }
392 maps_layer_mkdir_if_default_dir ( vml );
393}
394
395/****************************************/
396/******** GOBJECT STUFF *****************/
397/****************************************/
398
399GType vik_maps_layer_get_type ()
400{
401 static GType vml_type = 0;
402
403 if (!vml_type)
404 {
405 static const GTypeInfo vml_info =
406 {
407 sizeof (VikMapsLayerClass),
408 NULL, /* base_init */
409 NULL, /* base_finalize */
410 NULL, /* class init */
411 NULL, /* class_finalize */
412 NULL, /* class_data */
413 sizeof (VikMapsLayer),
414 0,
415 NULL /* instance init */
416 };
417 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
418 }
419
420 return vml_type;
421}
422
423/****************************************/
424/************** PARAMETERS **************/
425/****************************************/
426
427static guint map_index_to_uniq_id (guint8 index)
428{
429 g_assert ( index < NUM_MAP_TYPES );
430 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
431}
432
433static guint map_uniq_id_to_index ( guint uniq_id )
434{
435 gint i;
436 for ( i = 0; i < NUM_MAP_TYPES; i++ )
437 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
438 return i;
439 return NUM_MAP_TYPES; /* no such thing */
440}
441
442static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp )
443{
444 switch ( id )
445 {
446 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
447 case PARAM_MAPTYPE: {
448 gint maptype = map_uniq_id_to_index(data.u);
449 if ( maptype == NUM_MAP_TYPES ) g_warning(_("Unknown map type"));
450 else vml->maptype = maptype;
451 break;
452 }
453 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
454 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
455 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
456 vml->mapzoom_id = data.u;
457 vml->xmapzoom = __mapzooms_x [data.u];
458 vml->ymapzoom = __mapzooms_y [data.u];
459 }else g_warning (_("Unknown Map Zoom")); break;
460 }
461 return TRUE;
462}
463
464static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id )
465{
466 VikLayerParamData rv;
467 switch ( id )
468 {
469 case PARAM_CACHE_DIR: rv.s = vml->cache_dir ? vml->cache_dir : ""; break;
470 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
471 case PARAM_ALPHA: rv.u = vml->alpha; break;
472 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
473 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
474 }
475 return rv;
476}
477
478/****************************************/
479/****** CREATING, COPYING, FREEING ******/
480/****************************************/
481
482static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
483{
484 int idx;
485 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
486 vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS );
487 idx = map_uniq_id_to_index(13); /* 13 is id for OSM Mapnik maps */
488 vml->maptype = (idx < NUM_MAP_TYPES) ? idx : 0;
489 vml->alpha = 255;
490 vml->mapzoom_id = 0;
491 vml->dl_tool_x = vml->dl_tool_y = -1;
492 maps_layer_set_cache_dir ( vml, NULL );
493 vml->autodownload = FALSE;
494 vml->last_center = NULL;
495 vml->last_xmpp = 0.0;
496 vml->last_ympp = 0.0;
497
498 vml->dl_right_click_menu = NULL;
499
500 return vml;
501}
502
503static void maps_layer_free ( VikMapsLayer *vml )
504{
505 g_free ( vml->cache_dir );
506 vml->cache_dir = NULL;
507 if ( vml->dl_right_click_menu )
508 gtk_object_sink ( GTK_OBJECT(vml->dl_right_click_menu) );
509 g_free(vml->last_center);
510 vml->last_center = NULL;
511}
512
513static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
514{
515 if (from_file != TRUE)
516 {
517 /* If this method is not called in file reading context
518 * it is called in GUI context.
519 * So, we can check if we have to inform the user about inconsistency */
520 VikViewportDrawMode vp_drawmode;
521 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
522 VikMapSource *map = NULL;
523
524 vp_drawmode = vik_viewport_get_drawmode ( VIK_VIEWPORT(vp) );
525 map = MAPS_LAYER_NTH_TYPE(vml->maptype);
526 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
527 const gchar *drawmode_name = vik_viewport_get_drawmode_name (VIK_VIEWPORT(vp), vik_map_source_get_drawmode(map));
528 gchar *msg = g_strdup_printf(_("New map cannot be displayed in the current drawmode.\nSelect \"%s\" from View menu to view it."), drawmode_name);
529 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), msg );
530 g_free(msg);
531 }
532 }
533}
534
535static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
536{
537 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
538}
539
540static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
541{
542 VikMapsLayer *rv = maps_layer_new ( vvp );
543 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
544 return rv;
545}
546
547/*********************/
548/****** DRAWING ******/
549/*********************/
550
551static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
552{
553 guchar *pixels;
554 gint width, height, iii, jjj;
555
556 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
557 {
558 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
559 g_object_unref(G_OBJECT(pixbuf));
560 pixbuf = tmp;
561 }
562
563 pixels = gdk_pixbuf_get_pixels(pixbuf);
564 width = gdk_pixbuf_get_width(pixbuf);
565 height = gdk_pixbuf_get_height(pixbuf);
566
567 /* r,g,b,a,r,g,b,a.... */
568 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
569 {
570 pixels += 3;
571 *pixels++ = alpha;
572 }
573 return pixbuf;
574}
575
576static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
577{
578 GdkPixbuf *tmp;
579 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
580 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_NEAREST);
581 g_object_unref ( G_OBJECT(pixbuf) );
582 return tmp;
583}
584
585static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
586{
587 GdkPixbuf *pixbuf;
588
589 /* get the thing */
590 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
591 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
592
593 if ( ! pixbuf ) {
594 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
595 vml->cache_dir, mode,
596 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
597 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
598 {
599 GError *gx = NULL;
600 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
601
602 /* free the pixbuf on error */
603 if (gx)
604 {
605 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE )
606 g_warning ( _("Couldn't open image file: %s"), gx->message );
607
608 g_error_free ( gx );
609 if ( pixbuf )
610 g_object_unref ( G_OBJECT(pixbuf) );
611 pixbuf = NULL;
612 } else {
613 if ( vml->alpha < 255 )
614 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
615 if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 )
616 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
617
618 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
619 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
620 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor );
621 }
622 }
623 }
624 return pixbuf;
625}
626
627gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
628{
629 const VikCoord *center = vik_viewport_get_center ( vvp );
630
631 if (vml->last_center == NULL) {
632 VikCoord *new_center = g_malloc(sizeof(VikCoord));
633 *new_center = *center;
634 vml->last_center = new_center;
635 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
636 vml->last_ympp = vik_viewport_get_ympp(vvp);
637 return TRUE;
638 }
639
640 /* TODO: perhaps vik_coord_diff() */
641 if (vik_coord_equals(vml->last_center, center)
642 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
643 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
644 return FALSE;
645
646 *(vml->last_center) = *center;
647 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
648 vml->last_ympp = vik_viewport_get_ympp(vvp);
649 return TRUE;
650}
651
652static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
653{
654 MapCoord ulm, brm;
655 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
656 gdouble yzoom = vik_viewport_get_ympp ( vvp );
657 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
658 gdouble existence_only = FALSE;
659
660 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
661 xshrinkfactor = vml->xmapzoom / xzoom;
662 yshrinkfactor = vml->ymapzoom / yzoom;
663 xzoom = vml->xmapzoom;
664 yzoom = vml->xmapzoom;
665 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
666 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
667 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR )
668 existence_only = TRUE;
669 else {
670 g_warning ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
671 return;
672 }
673 }
674 }
675
676 /* coord -> ID */
677 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
678 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
679 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
680
681 /* loop & draw */
682 gint x, y;
683 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
684 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
685 gint mode = vik_map_source_get_uniq_id(map);
686
687 VikCoord coord;
688 gint xx, yy, width, height;
689 GdkPixbuf *pixbuf;
690
691 guint max_path_len = strlen(vml->cache_dir) + 40;
692 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
693
694 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
695#ifdef DEBUG
696 fputs(stderr, "DEBUG: Starting autodownload\n");
697#endif
698 if ( vik_map_source_supports_if_modified_since (map) )
699 // Try to download newer tiles
700 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
701 else
702 // Download only missing tiles
703 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
704 }
705
706 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
707 for ( x = xmin; x <= xmax; x++ ) {
708 for ( y = ymin; y <= ymax; y++ ) {
709 ulm.x = x;
710 ulm.y = y;
711 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
712 if ( pixbuf ) {
713 width = gdk_pixbuf_get_width ( pixbuf );
714 height = gdk_pixbuf_get_height ( pixbuf );
715
716 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
717 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
718 xx -= (width/2);
719 yy -= (height/2);
720
721 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
722 }
723 }
724 }
725 } else { /* tilesize is known, don't have to keep converting coords */
726 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
727 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
728 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
729 gint tilesize_x_ceil = ceil ( tilesize_x );
730 gint tilesize_y_ceil = ceil ( tilesize_y );
731 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
732 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
733 gdouble xx, yy; gint xx_tmp, yy_tmp;
734 gint base_yy, xend, yend;
735
736 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
737
738 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
739 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
740
741 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
742 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
743 xx = xx_tmp; yy = yy_tmp;
744 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
745 * eg if tile size 128, shrinkfactor 0.333 */
746 xx -= (tilesize_x/2);
747 base_yy = yy - (tilesize_y/2);
748
749 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
750 yy = base_yy;
751 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
752 ulm.x = x;
753 ulm.y = y;
754
755 if ( existence_only ) {
756 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
757 vml->cache_dir, mode,
758 ulm.scale, ulm.z, ulm.x, ulm.y );
759 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
760 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
761 }
762 } else {
763 int scale_inc;
764 for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
765 /* try with correct then smaller zooms */
766 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
767 MapCoord ulm2 = ulm;
768 ulm2.x = ulm.x / scale_factor;
769 ulm2.y = ulm.y / scale_factor;
770 ulm2.scale = ulm.scale + scale_inc;
771 pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
772 if ( pixbuf ) {
773 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
774 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
775#ifdef DEBUG
776 printf("maps_layer_draw_section - x=%d, y=%d, z=%d, src_x=%d, src_y=%d, xx=%d, yy=%d - %x\n", ulm.x, ulm.y, ulm.scale, src_x, src_y, (int)xx, (int)yy, vvp);
777#endif
778 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
779 break;
780 }
781 }
782 if ( !pixbuf ) {
783 /* retry with bigger zooms */
784 int scale_dec;
785 for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
786 int pict_x, pict_y;
787 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
788 MapCoord ulm2 = ulm;
789 ulm2.x = ulm.x * scale_factor;
790 ulm2.y = ulm.y * scale_factor;
791 ulm2.scale = ulm.scale - scale_dec;
792 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
793 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
794 MapCoord ulm3 = ulm2;
795 ulm3.x += pict_x;
796 ulm3.y += pict_y;
797 pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
798 if ( pixbuf ) {
799 gint src_x = 0;
800 gint src_y = 0;
801 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
802 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
803 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
804 }
805 }
806 }
807 }
808 }
809 }
810
811 yy += tilesize_y;
812 }
813 xx += tilesize_x;
814 }
815 }
816
817 g_free ( path_buf );
818 }
819}
820
821static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
822{
823 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
824 {
825 VikCoord ul, br;
826
827 /* get corner coords */
828 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
829 /* UTM multi-zone stuff by Kit Transue */
830 gchar leftmost_zone, rightmost_zone, i;
831 leftmost_zone = vik_viewport_leftmost_zone( vvp );
832 rightmost_zone = vik_viewport_rightmost_zone( vvp );
833 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
834 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
835 maps_layer_draw_section ( vml, vvp, &ul, &br );
836 }
837 }
838 else {
839 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
840 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
841
842 maps_layer_draw_section ( vml, vvp, &ul, &br );
843 }
844 }
845}
846
847/*************************/
848/****** DOWNLOADING ******/
849/*************************/
850
851/* pass along data to thread, exists even if layer is deleted. */
852typedef struct {
853 gchar *cache_dir;
854 gchar *filename_buf;
855 gint x0, y0, xf, yf;
856 MapCoord mapcoord;
857 gint maptype;
858 gint maxlen;
859 gint mapstoget;
860 gint redownload;
861 gboolean refresh_display;
862 VikMapsLayer *vml;
863 VikViewport *vvp;
864 gboolean map_layer_alive;
865 GMutex *mutex;
866} MapDownloadInfo;
867
868static void mdi_free ( MapDownloadInfo *mdi )
869{
870 g_mutex_free(mdi->mutex);
871 g_free ( mdi->cache_dir );
872 mdi->cache_dir = NULL;
873 g_free ( mdi->filename_buf );
874 mdi->filename_buf = NULL;
875 g_free ( mdi );
876}
877
878static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
879{
880 MapDownloadInfo *mdi = ptr;
881 g_mutex_lock(mdi->mutex);
882 mdi->map_layer_alive = FALSE;
883 g_mutex_unlock(mdi->mutex);
884}
885
886static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
887{
888 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
889 guint donemaps = 0;
890 gint x, y;
891 for ( x = mdi->x0; x <= mdi->xf; x++ )
892 {
893 for ( y = mdi->y0; y <= mdi->yf; y++ )
894 {
895 gboolean remove_mem_cache = FALSE;
896 gboolean need_download = FALSE;
897 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
898 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
899 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
900
901 donemaps++;
902 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
903 if (res != 0) {
904 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
905 return -1;
906 }
907
908 if ( mdi->redownload == REDOWNLOAD_ALL)
909 g_remove ( mdi->filename_buf );
910
911 else if ( (mdi->redownload == REDOWNLOAD_BAD) && (g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE) )
912 {
913 /* see if this one is bad or what */
914 GError *gx = NULL;
915 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
916 if (gx || (!pixbuf))
917 g_remove ( mdi->filename_buf );
918 if ( pixbuf )
919 g_object_unref ( pixbuf );
920 if ( gx )
921 g_error_free ( gx );
922 }
923
924 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
925 {
926 need_download = TRUE;
927 if (( mdi->redownload != REDOWNLOAD_NONE ) &&
928 ( mdi->redownload != DOWNLOAD_OR_REFRESH ))
929 remove_mem_cache = TRUE;
930 } else if ( mdi->redownload == DOWNLOAD_OR_REFRESH ) {
931 remove_mem_cache = TRUE;
932 } else if ( mdi->redownload == REDOWNLOAD_NEW) {
933 need_download = TRUE;
934 remove_mem_cache = TRUE;
935 } else
936 continue;
937
938 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
939
940 if (need_download) {
941 if ( vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle))
942 continue;
943 }
944
945 gdk_threads_enter();
946 g_mutex_lock(mdi->mutex);
947 if (remove_mem_cache)
948 a_mapcache_remove_all_shrinkfactors ( x, y, mdi->mapcoord.z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)), mdi->mapcoord.scale );
949 if (mdi->refresh_display && mdi->map_layer_alive) {
950 /* TODO: check if it's on visible area */
951 vik_layer_emit_update ( VIK_LAYER(mdi->vml) );
952 }
953 g_mutex_unlock(mdi->mutex);
954 gdk_threads_leave();
955 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
956
957 }
958 }
959 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
960 g_mutex_lock(mdi->mutex);
961 if (mdi->map_layer_alive)
962 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
963 g_mutex_unlock(mdi->mutex);
964 return 0;
965}
966
967static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
968{
969 if ( mdi->mapcoord.x || mdi->mapcoord.y )
970 {
971 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
972 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
973 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
974 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
975 {
976 g_remove ( mdi->filename_buf );
977 }
978 }
979}
980
981static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
982{
983 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
984 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
985 MapCoord ulm, brm;
986 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
987 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
988 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
989 {
990 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
991 gint a, b;
992
993 mdi->vml = vml;
994 mdi->vvp = vvp;
995 mdi->map_layer_alive = TRUE;
996 mdi->mutex = g_mutex_new();
997 mdi->refresh_display = TRUE;
998
999 /* cache_dir and buffer for dest filename */
1000 mdi->cache_dir = g_strdup ( vml->cache_dir );
1001 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1002 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1003 mdi->maptype = vml->maptype;
1004
1005 mdi->mapcoord = ulm;
1006
1007 mdi->redownload = redownload;
1008
1009 mdi->x0 = MIN(ulm.x, brm.x);
1010 mdi->xf = MAX(ulm.x, brm.x);
1011 mdi->y0 = MIN(ulm.y, brm.y);
1012 mdi->yf = MAX(ulm.y, brm.y);
1013
1014 mdi->mapstoget = 0;
1015
1016 if ( mdi->redownload ) {
1017 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1018 } else {
1019 /* calculate how many we need */
1020 for ( a = mdi->x0; a <= mdi->xf; a++ )
1021 {
1022 for ( b = mdi->y0; b <= mdi->yf; b++ )
1023 {
1024 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1025 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1026 ulm.z, a, b );
1027 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1028 mdi->mapstoget++;
1029 }
1030 }
1031 }
1032
1033 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1034
1035 if ( mdi->mapstoget )
1036 {
1037 const gchar *tmp_str;
1038 gchar *tmp;
1039
1040 if (redownload)
1041 {
1042 if (redownload == REDOWNLOAD_BAD)
1043 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1044 else
1045 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1046 }
1047 else
1048 {
1049 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1050 }
1051 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1052
1053 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1054 /* launch the thread */
1055 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1056 tmp, /* description string */
1057 (vik_thr_func) map_download_thread, /* function to call within thread */
1058 mdi, /* pass along data */
1059 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1060 (vik_thr_free_func) mdi_cancel_cleanup,
1061 mdi->mapstoget );
1062 g_free ( tmp );
1063 }
1064 else
1065 mdi_free ( mdi );
1066 }
1067}
1068
1069void maps_layer_download_section_without_redraw( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom)
1070{
1071 MapCoord ulm, brm;
1072 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1073
1074 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1075 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1076 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1077 return;
1078 }
1079
1080 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1081 gint i, j;
1082
1083 mdi->vml = vml;
1084 mdi->vvp = vvp;
1085 mdi->map_layer_alive = TRUE;
1086 mdi->mutex = g_mutex_new();
1087 mdi->refresh_display = FALSE;
1088
1089 mdi->cache_dir = g_strdup ( vml->cache_dir );
1090 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1091 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1092 mdi->maptype = vml->maptype;
1093
1094 mdi->mapcoord = ulm;
1095
1096 mdi->redownload = REDOWNLOAD_NONE;
1097
1098 mdi->x0 = MIN(ulm.x, brm.x);
1099 mdi->xf = MAX(ulm.x, brm.x);
1100 mdi->y0 = MIN(ulm.y, brm.y);
1101 mdi->yf = MAX(ulm.y, brm.y);
1102
1103 mdi->mapstoget = 0;
1104
1105 for (i = mdi->x0; i <= mdi->xf; i++) {
1106 for (j = mdi->y0; j <= mdi->yf; j++) {
1107 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1108 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1109 ulm.z, i, j );
1110 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
1111 mdi->mapstoget++;
1112 }
1113 }
1114
1115 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1116
1117 if (mdi->mapstoget) {
1118 gchar *tmp;
1119 const gchar *fmt;
1120 fmt = ngettext("Downloading %d %s map...",
1121 "Downloading %d %s maps...",
1122 mdi->mapstoget);
1123 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
1124
1125 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1126 /* launch the thread */
1127 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1128 tmp, /* description string */
1129 (vik_thr_func) map_download_thread, /* function to call within thread */
1130 mdi, /* pass along data */
1131 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1132 (vik_thr_free_func) mdi_cancel_cleanup,
1133 mdi->mapstoget );
1134 g_free ( tmp );
1135 }
1136 else
1137 mdi_free ( mdi );
1138}
1139
1140static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1141{
1142 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1143}
1144
1145static void maps_layer_redownload_all ( VikMapsLayer *vml )
1146{
1147 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1148}
1149
1150static void maps_layer_redownload_new ( VikMapsLayer *vml )
1151{
1152 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1153}
1154
1155static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1156{
1157 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1158 return FALSE;
1159 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1160 {
1161 if ( event->button == 1 )
1162 {
1163 VikCoord ul, br;
1164 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 );
1165 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 );
1166 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
1167 vml->dl_tool_x = vml->dl_tool_y = -1;
1168 return TRUE;
1169 }
1170 else
1171 {
1172 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) );
1173 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) );
1174
1175 vml->redownload_vvp = vvp;
1176
1177 vml->dl_tool_x = vml->dl_tool_y = -1;
1178
1179 if ( ! vml->dl_right_click_menu ) {
1180 GtkWidget *item;
1181 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1182
1183 item = gtk_menu_item_new_with_label ( _("Redownload bad map(s)") );
1184 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1185 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1186
1187 item = gtk_menu_item_new_with_label ( _("Redownload new map(s)") );
1188 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1189 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1190
1191 item = gtk_menu_item_new_with_label ( _("Redownload all map(s)") );
1192 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1193 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1194 }
1195
1196 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1197 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1198 }
1199 }
1200 return FALSE;
1201}
1202
1203static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1204{
1205 return vvp;
1206}
1207
1208static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1209{
1210 MapCoord tmp;
1211 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1212 return FALSE;
1213 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1214 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1215 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
1216 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1217 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1218 &tmp ) ) {
1219 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1220 return TRUE;
1221 }
1222 return FALSE;
1223
1224
1225#if 0
1226 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1227 {
1228 VikCoord coord;
1229 MapCoord mapcoord;
1230 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1231 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1232 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1233 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1234 &mapcoord ) ) {
1235 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1236 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1237 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1238
1239 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1240 g_free ( filename_buf );
1241 vik_layer_emit_update ( VIK_LAYER(vml) );
1242 return TRUE;
1243 }
1244 }
1245 return FALSE;
1246#endif
1247}
1248
1249static void download_onscreen_maps ( gpointer vml_vvp[2], gint redownload )
1250{
1251 VikMapsLayer *vml = vml_vvp[0];
1252 VikViewport *vvp = vml_vvp[1];
1253 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
1254
1255 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1256 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1257
1258 VikCoord ul, br;
1259 MapCoord ulm, brm;
1260
1261 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1262 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1263
1264 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1265 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1266 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1267 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
1268 start_download_thread ( vml, vvp, &ul, &br, redownload );
1269 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1270 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
1271 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
1272 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1273 g_free(err);
1274 }
1275 else
1276 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
1277
1278}
1279
1280static void maps_layer_download_missing_onscreen_maps ( gpointer vml_vvp[2] )
1281{
1282 download_onscreen_maps( vml_vvp, REDOWNLOAD_NONE);
1283}
1284
1285static void maps_layer_download_new_onscreen_maps ( gpointer vml_vvp[2] )
1286{
1287 download_onscreen_maps( vml_vvp, REDOWNLOAD_NEW);
1288}
1289
1290static void maps_layer_redownload_all_onscreen_maps ( gpointer vml_vvp[2] )
1291{
1292 download_onscreen_maps( vml_vvp, REDOWNLOAD_ALL);
1293}
1294
1295static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
1296{
1297 static gpointer pass_along[2];
1298 GtkWidget *item;
1299 pass_along[0] = vml;
1300 pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1301
1302 item = gtk_menu_item_new();
1303 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1304 gtk_widget_show ( item );
1305
1306 item = gtk_menu_item_new_with_label ( _("Download missing Onscreen Maps") );
1307 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), pass_along );
1308 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1309 gtk_widget_show ( item );
1310
1311 if ( vik_map_source_supports_if_modified_since (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
1312 item = gtk_menu_item_new_with_label ( _("Download new Onscreen Maps from server") );
1313 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), pass_along );
1314 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1315 gtk_widget_show ( item );
1316 }
1317
1318 /* TODO Add GTK_STOCK_REFRESH icon */
1319 item = gtk_menu_item_new_with_label ( _("Refresh Onscreen Tiles") );
1320 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), pass_along );
1321 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1322 gtk_widget_show ( item );
1323}