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