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