]> git.street.me.uk Git - andy/viking.git/blame - src/vikmapslayer.c
Enable i18n on 'Routing' preferences tab name.
[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>
0fb11294 6 * Copyright (c) 2013, Rob Norris <rw_norris@hotmail.com>
50a14534 7 * UTM multi-zone stuff by Kit Transue <notlostyet@didactek.com>
cdcaf41c 8 * Dynamic map type by Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
50a14534
EB
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
4c77d5e0
GB
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif
29
50a14534
EB
30#include <gtk/gtk.h>
31#include <gdk-pixbuf/gdk-pixdata.h>
45acf79e
MA
32#include <glib.h>
33#include <glib/gstdio.h>
4c77d5e0
GB
34#include <glib/gi18n.h>
35
8c00358d 36#ifdef HAVE_STRING_H
50a14534 37#include <string.h>
8c00358d
GB
38#endif
39#ifdef HAVE_MATH_H
50a14534 40#include <math.h>
8c00358d
GB
41#endif
42
45acf79e 43#ifdef HAVE_UNISTD_H
50a14534 44#include <unistd.h>
45acf79e 45#endif
50a14534 46
a7023a1b 47#include "viking.h"
450d8665 48#include "vikmapsourcedefault.h"
1b14d0d2 49#include "vikutils.h"
9979924f 50#include "maputils.h"
50a14534 51#include "mapcache.h"
50a14534 52#include "background.h"
a7023a1b
RN
53#include "preferences.h"
54#include "vikmapslayer.h"
bce3a7b0
EB
55#include "icons/icons.h"
56
0fb11294
RN
57#ifdef HAVE_SQLITE3_H
58#include "sqlite3.h"
59#include <gio/gio.h>
60#endif
61
0f7e9133
RN
62#define VIK_SETTINGS_MAP_MAX_TILES "maps_max_tiles"
63static gint MAX_TILES = 1000;
64
65#define VIK_SETTINGS_MAP_MIN_SHRINKFACTOR "maps_min_shrinkfactor"
66#define VIK_SETTINGS_MAP_MAX_SHRINKFACTOR "maps_max_shrinkfactor"
67static gdouble MAX_SHRINKFACTOR = 8.0000001; /* zoom 1 viewing 8-tiles */
68static gdouble MIN_SHRINKFACTOR = 0.0312499; /* zoom 32 viewing 1-tiles */
69
70#define VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR "maps_real_min_shrinkfactor"
71static gdouble REAL_MIN_SHRINKFACTOR = 0.0039062499; /* if shrinkfactor is between MAX and REAL_MAX, will only check for existence */
72
50a14534
EB
73/****** MAP TYPES ******/
74
cdcaf41c
QT
75static GList *__map_types = NULL;
76
77#define NUM_MAP_TYPES g_list_length(__map_types)
50a14534 78
cdcaf41c 79/* List of label for each map type */
a8876892 80static gchar **params_maptypes = NULL;
cdcaf41c
QT
81
82/* Corresponding IDS. (Cf. field uniq_id in VikMapsLayer struct) */
a8876892 83static guint *params_maptypes_ids = NULL;
50a14534
EB
84
85/******** MAPZOOMS *********/
86
fcc2786b
SW
87static 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 };
88static 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 };
89static 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 90
d756fb5c 91#define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0]) - 1)
50a14534
EB
92
93/**************************/
94
95
07059501 96static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file);
7924723c 97static const gchar* maps_layer_tooltip ( VikMapsLayer *vml );
911400b5
AF
98static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len );
99static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
158b3642
RN
100static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation );
101static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation );
db43cfa4 102static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values );
50a14534
EB
103static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp );
104static VikMapsLayer *maps_layer_new ( VikViewport *vvp );
105static void maps_layer_free ( VikMapsLayer *vml );
106static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
107static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp );
941aa6e9 108static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp );
50a14534
EB
109static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir );
110static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload );
111static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp );
e2cb421f 112static guint map_uniq_id_to_index ( guint uniq_id );
50a14534
EB
113
114
115static VikLayerParamScale params_scales[] = {
116 /* min, max, step, digits (decimal places) */
117 { 0, 255, 3, 0 }, /* alpha */
118};
119
a7023a1b
RN
120static VikLayerParamData mode_default ( void ) { return VIK_LPD_UINT ( 19 ); } // OSM MapQuest maps
121static VikLayerParamData directory_default ( void )
122{
123 VikLayerParamData data;
b781c9db 124 VikLayerParamData *pref = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir");
9414408e 125 if (pref) data.s = g_strdup ( pref->s ); else data.s = "";
a7023a1b
RN
126 return data;
127}
0fb11294
RN
128static VikLayerParamData file_default ( void )
129{
130 VikLayerParamData data;
131 data.s = "";
132 return data;
133}
a7023a1b
RN
134static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
135static VikLayerParamData mapzoom_default ( void ) { return VIK_LPD_UINT ( 0 ); }
136
50a14534 137VikLayerParam maps_layer_params[] = {
a87f8fa1
RN
138 { VIK_LAYER_MAPS, "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Map Type:"), VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL, mode_default, NULL, NULL },
139 { VIK_LAYER_MAPS, "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Maps Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, NULL, directory_default, NULL, NULL },
857baaa3 140 { VIK_LAYER_MAPS, "mapfile", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Map File:"), VIK_LAYER_WIDGET_FILEENTRY, GINT_TO_POINTER(VF_FILTER_MBTILES), NULL,
0fb11294 141 N_("An MBTiles file. Only applies when the map type method is 'MBTiles'"), file_default, NULL, NULL },
a7023a1b 142 { VIK_LAYER_MAPS, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales, NULL,
a87f8fa1
RN
143 N_("Control the Alpha value for transparency effects"), alpha_default, NULL, NULL },
144 { VIK_LAYER_MAPS, "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
56c6d6b2 145 { VIK_LAYER_MAPS, "adlonlymissing", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Autodownload Only Gets Missing Maps:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
a87f8fa1 146 N_("Using this option avoids attempting to update already acquired tiles. This can be useful if you want to restrict the network usage, without having to resort to manual control. Only applies when 'Autodownload Maps' is on."), vik_lpd_false_default, NULL, NULL },
a7023a1b
RN
147 { VIK_LAYER_MAPS, "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Zoom Level:"), VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms, NULL,
148 N_("Determines the method of displaying map tiles for the current zoom level. 'Viking Zoom Level' uses the best matching level, otherwise setting a fixed value will always use map tiles of the specified value regardless of the actual zoom level."),
a87f8fa1 149 mapzoom_default, NULL, NULL },
50a14534
EB
150};
151
382566c3
RN
152enum {
153 PARAM_MAPTYPE=0,
154 PARAM_CACHE_DIR,
0fb11294 155 PARAM_FILE,
382566c3
RN
156 PARAM_ALPHA,
157 PARAM_AUTODOWNLOAD,
158 PARAM_ONLYMISSING,
159 PARAM_MAPZOOM,
160 NUM_PARAMS
161};
50a14534 162
0d66c56c
RN
163void maps_layer_set_autodownload_default ( gboolean autodownload )
164{
165 // Set appropriate function
166 if ( autodownload )
167 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_true_default;
168 else
169 maps_layer_params[PARAM_AUTODOWNLOAD].default_value = vik_lpd_false_default;
170}
171
50a14534 172static VikToolInterface maps_tools[] = {
79dce0cb 173 { { "MapsDownload", "vik-icon-Maps Download", N_("_Maps Download"), NULL, N_("Maps Download"), 0 },
6b59f63d
RN
174 (VikToolConstructorFunc) maps_layer_download_create,
175 NULL,
176 NULL,
177 NULL,
178 (VikToolMouseFunc) maps_layer_download_click,
179 NULL,
180 (VikToolMouseFunc) maps_layer_download_release,
181 NULL,
ef5e8132 182 FALSE,
63959706 183 GDK_CURSOR_IS_PIXMAP, &cursor_mapdl_pixbuf, NULL },
50a14534
EB
184};
185
186VikLayerInterface vik_maps_layer_interface = {
db386630 187 "Map",
4c77d5e0 188 N_("Map"),
75078768 189 "<control><shift>M",
5bfafde9 190 &vikmapslayer_pixbuf,
50a14534
EB
191
192 maps_tools,
193 sizeof(maps_tools) / sizeof(maps_tools[0]),
194
195 maps_layer_params,
196 NUM_PARAMS,
197 NULL,
198 0,
199
5a4a28bf
QT
200 VIK_MENU_ITEM_ALL,
201
50a14534
EB
202 (VikLayerFuncCreate) maps_layer_new,
203 (VikLayerFuncRealize) NULL,
b112cbf5 204 (VikLayerFuncPostRead) maps_layer_post_read,
50a14534
EB
205 (VikLayerFuncFree) maps_layer_free,
206
207 (VikLayerFuncProperties) NULL,
208 (VikLayerFuncDraw) maps_layer_draw,
209 (VikLayerFuncChangeCoordMode) NULL,
210
20c7a3a0
QT
211 (VikLayerFuncSetMenuItemsSelection) NULL,
212 (VikLayerFuncGetMenuItemsSelection) NULL,
213
50a14534
EB
214 (VikLayerFuncAddMenuItems) maps_layer_add_menu_items,
215 (VikLayerFuncSublayerAddMenuItems) NULL,
216
217 (VikLayerFuncSublayerRenameRequest) NULL,
218 (VikLayerFuncSublayerToggleVisible) NULL,
9da7faf2 219 (VikLayerFuncSublayerTooltip) NULL,
7924723c 220 (VikLayerFuncLayerTooltip) maps_layer_tooltip,
a5dcfdb7 221 (VikLayerFuncLayerSelected) NULL,
50a14534 222
911400b5
AF
223 (VikLayerFuncMarshall) maps_layer_marshall,
224 (VikLayerFuncUnmarshall) maps_layer_unmarshall,
50a14534
EB
225
226 (VikLayerFuncSetParam) maps_layer_set_param,
227 (VikLayerFuncGetParam) maps_layer_get_param,
db43cfa4 228 (VikLayerFuncChangeParam) maps_layer_change_param,
50a14534
EB
229
230 (VikLayerFuncReadFileData) NULL,
231 (VikLayerFuncWriteFileData) NULL,
232
33534cd8 233 (VikLayerFuncDeleteItem) NULL,
d5874ef9 234 (VikLayerFuncCutItem) NULL,
50a14534
EB
235 (VikLayerFuncCopyItem) NULL,
236 (VikLayerFuncPasteItem) NULL,
237 (VikLayerFuncFreeCopiedItem) NULL,
70a23263 238 (VikLayerFuncDragDropRequest) NULL,
77ad64fa
RN
239
240 (VikLayerFuncSelectClick) NULL,
08f14055
RN
241 (VikLayerFuncSelectMove) NULL,
242 (VikLayerFuncSelectRelease) NULL,
e46f259a 243 (VikLayerFuncSelectedViewportMenu) NULL,
50a14534
EB
244};
245
246struct _VikMapsLayer {
247 VikLayer vl;
248 guint maptype;
249 gchar *cache_dir;
250 guint8 alpha;
251 guint mapzoom_id;
252 gdouble xmapzoom, ymapzoom;
253
254 gboolean autodownload;
382566c3 255 gboolean adl_only_missing;
10ca2bfe
QT
256 VikCoord *last_center;
257 gdouble last_xmpp;
258 gdouble last_ympp;
50a14534
EB
259
260 gint dl_tool_x, dl_tool_y;
261
262 GtkMenu *dl_right_click_menu;
263 VikCoord redownload_ul, redownload_br; /* right click menu only */
264 VikViewport *redownload_vvp;
0fb11294
RN
265 gchar *filename;
266#ifdef HAVE_SQLITE3_H
267 sqlite3 *mbtiles;
268#endif
50a14534
EB
269};
270
6a4a29aa
JJ
271enum { REDOWNLOAD_NONE = 0, /* download only missing maps */
272 REDOWNLOAD_BAD, /* download missing and bad maps */
273 REDOWNLOAD_NEW, /* download missing maps that are newer on server only */
274 REDOWNLOAD_ALL, /* download all maps */
275 DOWNLOAD_OR_REFRESH }; /* download missing maps and refresh cache */
50a14534 276
55ddef4e 277static VikLayerParam prefs[] = {
63959706 278 { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Default map layer directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, N_("Choose a directory to store cached Map tiles for this layer"), NULL, NULL, NULL },
55ddef4e
JJ
279};
280
281void maps_layer_init ()
282{
283 VikLayerParamData tmp;
284 tmp.s = maps_layer_default_dir();
285 a_preferences_register(prefs, tmp, VIKING_PREFERENCES_GROUP_KEY);
0f7e9133
RN
286
287 gint max_tiles = MAX_TILES;
288 if ( a_settings_get_integer ( VIK_SETTINGS_MAP_MAX_TILES, &max_tiles ) )
289 MAX_TILES = max_tiles;
290
291 gdouble gdtmp;
292 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MIN_SHRINKFACTOR, &gdtmp ) )
293 MIN_SHRINKFACTOR = gdtmp;
294
295 if ( a_settings_get_double ( VIK_SETTINGS_MAP_MAX_SHRINKFACTOR, &gdtmp ) )
296 MAX_SHRINKFACTOR = gdtmp;
297
298 if ( a_settings_get_double ( VIK_SETTINGS_MAP_REAL_MIN_SHRINKFACTOR, &gdtmp ) )
299 REAL_MIN_SHRINKFACTOR = gdtmp;
55ddef4e 300}
50a14534 301
cdcaf41c
QT
302/****************************************/
303/******** MAPS LAYER TYPES **************/
304/****************************************/
305
e2cb421f
GB
306void _add_map_source ( guint id, const char *label, VikMapSource *map )
307{
a8876892
GB
308 gsize len = 0;
309 if (params_maptypes)
310 len = g_strv_length (params_maptypes);
cdcaf41c 311 /* Add the label */
a8876892
GB
312 params_maptypes = g_realloc (params_maptypes, (len+2)*sizeof(gchar*));
313 params_maptypes[len] = g_strdup (label);
314 params_maptypes[len+1] = NULL;
cdcaf41c
QT
315
316 /* Add the id */
a8876892
GB
317 params_maptypes_ids = g_realloc (params_maptypes_ids, (len+2)*sizeof(guint));
318 params_maptypes_ids[len] = id;
319 params_maptypes_ids[len+1] = 0;
cdcaf41c
QT
320
321 /* We have to clone */
820c59f4 322 VikMapSource *clone = VIK_MAP_SOURCE(g_object_ref(map));
cdcaf41c
QT
323 /* Register the clone in the list */
324 __map_types = g_list_append(__map_types, clone);
325
326 /* Hack
e2cb421f 327 We have to ensure the mode LayerParam references the up-to-date
cdcaf41c
QT
328 GLists.
329 */
330 /*
331 memcpy(&maps_layer_params[0].widget_data, &params_maptypes, sizeof(gpointer));
332 memcpy(&maps_layer_params[0].extra_widget_data, &params_maptypes_ids, sizeof(gpointer));
333 */
334 maps_layer_params[0].widget_data = params_maptypes;
335 maps_layer_params[0].extra_widget_data = params_maptypes_ids;
336}
337
e2cb421f
GB
338void _update_map_source ( const char *label, VikMapSource *map, int index )
339{
340 GList *item = g_list_nth (__map_types, index);
341 g_object_unref (item->data);
342 item->data = g_object_ref (map);
343 /* Change previous data */
344 g_free (params_maptypes[index]);
345 params_maptypes[index] = g_strdup (label);
346}
347
17281ebd
GB
348/**
349 * maps_layer_register_map_source:
350 * @map: the new VikMapSource
351 *
352 * Register a new VikMapSource.
353 * Override existing one (equality of id).
354 */
e2cb421f
GB
355void maps_layer_register_map_source ( VikMapSource *map )
356{
357 g_assert(map != NULL);
358
359 guint id = vik_map_source_get_uniq_id(map);
360 const char *label = vik_map_source_get_label(map);
361 g_assert(label != NULL);
362
363 int previous = map_uniq_id_to_index (id);
364 if (previous != NUM_MAP_TYPES)
365 {
366 _update_map_source (label, map, previous);
367 }
368 else
369 {
370 _add_map_source (id, label, map);
371 }
372}
373
a8876892
GB
374#define MAPS_LAYER_NTH_LABEL(n) (params_maptypes[n])
375#define MAPS_LAYER_NTH_ID(n) (params_maptypes_ids[n])
820c59f4 376#define MAPS_LAYER_NTH_TYPE(n) (VIK_MAP_SOURCE(g_list_nth_data(__map_types, (n))))
cdcaf41c 377
7114e879
QT
378gint vik_maps_layer_get_map_type(VikMapsLayer *vml)
379{
380 return(vml->maptype);
381}
382
383gchar *vik_maps_layer_get_map_label(VikMapsLayer *vml)
384{
385 return(g_strdup(MAPS_LAYER_NTH_LABEL(vml->maptype)));
386}
387
50a14534
EB
388/****************************************/
389/******** CACHE DIR STUFF ***************/
390/****************************************/
391
2673b29d 392#define DIRECTDIRACCESS "%s%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d%s"
3d9454e6 393#define DIRSTRUCTURE "%st%ds%dz%d" G_DIR_SEPARATOR_S "%d" G_DIR_SEPARATOR_S "%d"
6a27c1bc
MA
394#define MAPS_CACHE_DIR maps_layer_default_dir()
395
50a14534 396#ifdef WINDOWS
6a27c1bc
MA
397#include <io.h>
398#define GLOBAL_MAPS_DIR "C:\\VIKING-MAPS\\"
399#define LOCAL_MAPS_DIR "VIKING-MAPS"
42293fc2
RN
400#elif defined __APPLE__
401#include <stdlib.h>
402#define GLOBAL_MAPS_DIR "/Library/cache/Viking/maps/"
403#define LOCAL_MAPS_DIR "/Library/Application Support/Viking/viking-maps"
50a14534 404#else /* POSIX */
50a14534 405#include <stdlib.h>
50a14534 406#define GLOBAL_MAPS_DIR "/var/cache/maps/"
6a27c1bc
MA
407#define LOCAL_MAPS_DIR ".viking-maps"
408#endif
50a14534 409
0c1044e9 410gchar *maps_layer_default_dir ()
50a14534 411{
3d9454e6
GB
412 static gchar *defaultdir = NULL;
413 if ( ! defaultdir )
50a14534
EB
414 {
415 /* Thanks to Mike Davison for the $VIKING_MAPS usage */
3d9454e6
GB
416 const gchar *mapdir = g_getenv("VIKING_MAPS");
417 if ( mapdir ) {
418 defaultdir = g_strdup ( mapdir );
45acf79e 419 } else if ( g_access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) {
3d9454e6 420 defaultdir = g_strdup ( GLOBAL_MAPS_DIR );
50a14534 421 } else {
6a27c1bc 422 const gchar *home = g_get_home_dir();
45acf79e 423 if (!home || g_access(home, W_OK))
3d9454e6
GB
424 home = g_get_home_dir ();
425 if ( home )
6a27c1bc 426 defaultdir = g_build_filename ( home, LOCAL_MAPS_DIR, NULL );
50a14534 427 else
6a27c1bc 428 defaultdir = g_strdup ( LOCAL_MAPS_DIR );
50a14534 429 }
3d9454e6
GB
430 if (defaultdir && (defaultdir[strlen(defaultdir)-1] != G_DIR_SEPARATOR))
431 {
432 /* Add the separator at the end */
433 gchar *tmp = defaultdir;
434 defaultdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, NULL);
435 g_free(tmp);
436 }
437 g_debug("%s: defaultdir=%s", __FUNCTION__, defaultdir);
50a14534
EB
438 }
439 return defaultdir;
440}
441
50a14534
EB
442static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml )
443{
45acf79e 444 if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && g_file_test ( vml->cache_dir, G_FILE_TEST_EXISTS ) == FALSE )
50a14534 445 {
f83131b9 446 g_mkdir ( vml->cache_dir, 0777 );
50a14534
EB
447 }
448}
449
450static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir )
451{
50a14534 452 g_assert ( vml != NULL);
e65028db
GB
453 g_free ( vml->cache_dir );
454 vml->cache_dir = NULL;
32f1d63a 455 const gchar *mydir = dir;
50a14534
EB
456
457 if ( dir == NULL || dir[0] == '\0' )
6926d79a
RN
458 {
459 if ( a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir") )
32f1d63a 460 mydir = a_preferences_get(VIKING_PREFERENCES_NAMESPACE "maplayer_default_dir")->s;
6926d79a 461 }
32f1d63a 462
1b14d0d2
RN
463 gchar *canonical_dir = vu_get_canonical_filename ( VIK_LAYER(vml), mydir );
464
32f1d63a 465 // Ensure cache_dir always ends with a separator
1b14d0d2
RN
466 guint len = strlen(canonical_dir);
467 if ( canonical_dir[len-1] != G_DIR_SEPARATOR )
50a14534 468 {
1b14d0d2
RN
469 vml->cache_dir = g_strconcat ( canonical_dir, G_DIR_SEPARATOR_S, NULL );
470 g_free ( canonical_dir );
471 }
472 else {
473 vml->cache_dir = canonical_dir;
50a14534 474 }
32f1d63a 475
50a14534
EB
476 maps_layer_mkdir_if_default_dir ( vml );
477}
478
0fb11294
RN
479static void maps_layer_set_file ( VikMapsLayer *vml, const gchar *name )
480{
481 if ( vml->filename )
482 g_free (vml->filename);
483 vml->filename = g_strdup (name);
484}
485
50a14534
EB
486/****************************************/
487/******** GOBJECT STUFF *****************/
488/****************************************/
489
490GType vik_maps_layer_get_type ()
491{
492 static GType vml_type = 0;
493
494 if (!vml_type)
495 {
496 static const GTypeInfo vml_info =
497 {
498 sizeof (VikMapsLayerClass),
499 NULL, /* base_init */
500 NULL, /* base_finalize */
501 NULL, /* class init */
502 NULL, /* class_finalize */
503 NULL, /* class_data */
504 sizeof (VikMapsLayer),
505 0,
506 NULL /* instance init */
507 };
508 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 );
509 }
510
511 return vml_type;
512}
513
514/****************************************/
515/************** PARAMETERS **************/
516/****************************************/
517
d7e495b2 518static guint map_index_to_uniq_id (guint16 index)
50a14534
EB
519{
520 g_assert ( index < NUM_MAP_TYPES );
820c59f4 521 return vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(index));
50a14534
EB
522}
523
524static guint map_uniq_id_to_index ( guint uniq_id )
525{
526 gint i;
527 for ( i = 0; i < NUM_MAP_TYPES; i++ )
820c59f4 528 if ( vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(i)) == uniq_id )
50a14534
EB
529 return i;
530 return NUM_MAP_TYPES; /* no such thing */
531}
532
b03ae39b
RN
533#define VIK_SETTINGS_MAP_LICENSE_SHOWN "map_license_shown"
534
535/**
536 * Convenience function to display the license
537 */
538static void maps_show_license ( GtkWindow *parent, VikMapSource *map )
192e40fc 539{
b03ae39b
RN
540 a_dialog_license ( parent,
541 vik_map_source_get_label (map),
542 vik_map_source_get_license (map),
543 vik_map_source_get_license_url (map) );
192e40fc
RN
544}
545
158b3642 546static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp, gboolean is_file_operation )
50a14534
EB
547{
548 switch ( id )
549 {
550 case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break;
0fb11294 551 case PARAM_FILE: maps_layer_set_file ( vml, data.s ); break;
50a14534
EB
552 case PARAM_MAPTYPE: {
553 gint maptype = map_uniq_id_to_index(data.u);
b03ae39b
RN
554 if ( maptype == NUM_MAP_TYPES )
555 g_warning(_("Unknown map type"));
556 else {
557 vml->maptype = maptype;
558
559 // When loading from a file don't need the license reminder - ensure it's saved into the 'seen' list
560 if ( is_file_operation ) {
561 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, maptype );
562 }
563 else {
564 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
565 if (vik_map_source_get_license (map) != NULL) {
566 // Check if licence for this map type has been shown before
567 if ( ! a_settings_get_integer_list_contains ( VIK_SETTINGS_MAP_LICENSE_SHOWN, maptype ) ) {
568 if ( vvp )
569 maps_show_license ( VIK_GTK_WINDOW_FROM_WIDGET(vvp), map );
570 a_settings_set_integer_list_containing ( VIK_SETTINGS_MAP_LICENSE_SHOWN, maptype );
571 }
572 }
573 }
574 }
50a14534
EB
575 break;
576 }
577 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
578 case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break;
382566c3 579 case PARAM_ONLYMISSING: vml->adl_only_missing = data.b; break;
50a14534
EB
580 case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) {
581 vml->mapzoom_id = data.u;
582 vml->xmapzoom = __mapzooms_x [data.u];
583 vml->ymapzoom = __mapzooms_y [data.u];
4c77d5e0 584 }else g_warning (_("Unknown Map Zoom")); break;
9351abc4 585 default: break;
50a14534
EB
586 }
587 return TRUE;
588}
589
158b3642 590static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id, gboolean is_file_operation )
50a14534
EB
591{
592 VikLayerParamData rv;
593 switch ( id )
594 {
cde1e50e 595 case PARAM_CACHE_DIR:
88542aa9
RN
596 {
597 gboolean set = FALSE;
cde1e50e
RN
598 /* Only save a blank when the map cache location equals the default
599 On reading in, when it is blank then the default is reconstructed
600 Since the default changes dependent on the user and OS, it means the resultant file is more portable */
88542aa9 601 if ( is_file_operation && vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 ) {
cde1e50e 602 rv.s = "";
88542aa9
RN
603 set = TRUE;
604 }
605 else if ( is_file_operation ) {
606 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
607 gchar *cwd = g_get_current_dir();
608 if ( cwd ) {
609 rv.s = file_GetRelativeFilename ( cwd, vml->cache_dir );
610 if ( !rv.s ) rv.s = "";
611 set = TRUE;
612 }
613 }
614 }
615 if ( !set )
616 rv.s = vml->cache_dir ? vml->cache_dir : "";
cde1e50e 617 break;
88542aa9 618 }
0fb11294 619 case PARAM_FILE: rv.s = vml->filename; break;
50a14534
EB
620 case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break;
621 case PARAM_ALPHA: rv.u = vml->alpha; break;
622 case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break;
382566c3 623 case PARAM_ONLYMISSING: rv.u = vml->adl_only_missing; break;
50a14534 624 case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break;
9351abc4 625 default: break;
50a14534
EB
626 }
627 return rv;
628}
629
db43cfa4
RN
630static void maps_layer_change_param ( GtkWidget *widget, ui_change_values values )
631{
632 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
0fb11294
RN
633 // Alter sensitivity of download option widgets according to the maptype setting.
634 case PARAM_MAPTYPE: {
635 // Get new value
636 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
637 // Is it *not* the OSM On Disk Tile Layout or the MBTiles type
638 gboolean sensitive = ( 21 != vlpd.u && 23 != vlpd.u);
639 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
640 GtkWidget **ww2 = values[UI_CHG_LABELS];
641 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
642 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
643 GtkWidget *w3 = ww1[PARAM_AUTODOWNLOAD];
644 GtkWidget *w4 = ww2[PARAM_AUTODOWNLOAD];
645 // Depends on autodownload value
646 gboolean missing_sense = sensitive && VIK_MAPS_LAYER(values[UI_CHG_LAYER])->autodownload;
647 if ( w1 ) gtk_widget_set_sensitive ( w1, missing_sense );
648 if ( w2 ) gtk_widget_set_sensitive ( w2, missing_sense );
649 if ( w3 ) gtk_widget_set_sensitive ( w3, sensitive );
650 if ( w4 ) gtk_widget_set_sensitive ( w4, sensitive );
651
652 // File only applicable for MBTiles type
653 // Directory for all other types
654 sensitive = ( 23 == vlpd.u);
655 GtkWidget *w5 = ww1[PARAM_FILE];
656 GtkWidget *w6 = ww2[PARAM_FILE];
657 GtkWidget *w7 = ww1[PARAM_CACHE_DIR];
658 GtkWidget *w8 = ww2[PARAM_CACHE_DIR];
659 if ( w5 ) gtk_widget_set_sensitive ( w5, sensitive );
660 if ( w6 ) gtk_widget_set_sensitive ( w6, sensitive );
661 if ( w7 ) gtk_widget_set_sensitive ( w7, !sensitive );
662 if ( w8 ) gtk_widget_set_sensitive ( w8, !sensitive );
663
664 break;
665 }
666
db43cfa4
RN
667 // Alter sensitivity of 'download only missing' widgets according to the autodownload setting.
668 case PARAM_AUTODOWNLOAD: {
669 // Get new value
670 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
671 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
672 GtkWidget **ww2 = values[UI_CHG_LABELS];
673 GtkWidget *w1 = ww1[PARAM_ONLYMISSING];
674 GtkWidget *w2 = ww2[PARAM_ONLYMISSING];
675 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
676 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
677 break;
678 }
679 default: break;
680 }
681}
682
50a14534
EB
683/****************************************/
684/****** CREATING, COPYING, FREEING ******/
685/****************************************/
686
687static VikMapsLayer *maps_layer_new ( VikViewport *vvp )
688{
689 VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) );
a0c65899 690 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPS );
a7023a1b
RN
691
692 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
693
50a14534 694 vml->dl_tool_x = vml->dl_tool_y = -1;
10ca2bfe
QT
695 vml->last_center = NULL;
696 vml->last_xmpp = 0.0;
697 vml->last_ympp = 0.0;
50a14534
EB
698
699 vml->dl_right_click_menu = NULL;
0fb11294 700 vml->filename = NULL;
50a14534
EB
701 return vml;
702}
703
704static void maps_layer_free ( VikMapsLayer *vml )
705{
e65028db
GB
706 g_free ( vml->cache_dir );
707 vml->cache_dir = NULL;
50a14534 708 if ( vml->dl_right_click_menu )
4f14a010 709 g_object_ref_sink ( G_OBJECT(vml->dl_right_click_menu) );
e65028db
GB
710 g_free(vml->last_center);
711 vml->last_center = NULL;
0fb11294
RN
712 g_free ( vml->filename );
713 vml->filename = NULL;
714
715#ifdef HAVE_SQLITE3_H
716 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
717 if ( vik_map_source_is_mbtiles ( map ) ) {
718 if ( vml->mbtiles ) {
719 int ans = sqlite3_close ( vml->mbtiles );
720 if ( ans != SQLITE_OK ) {
721 // Only to console for information purposes only
722 g_warning ( "SQL Close problem: %d", ans );
723 }
724 }
725 }
726#endif
50a14534
EB
727}
728
07059501 729static void maps_layer_post_read (VikLayer *vl, VikViewport *vp, gboolean from_file)
dc5758d3 730{
0fb11294
RN
731 VikMapsLayer *vml = VIK_MAPS_LAYER(vl);
732 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
733
07059501
GB
734 if (from_file != TRUE)
735 {
736 /* If this method is not called in file reading context
737 * it is called in GUI context.
738 * So, we can check if we have to inform the user about inconsistency */
739 VikViewportDrawMode vp_drawmode;
405b74ed 740 vp_drawmode = vik_viewport_get_drawmode ( vp );
0fb11294 741
820c59f4 742 if (vik_map_source_get_drawmode(map) != vp_drawmode) {
405b74ed 743 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vp, vik_map_source_get_drawmode(map));
4c77d5e0 744 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 745 a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vp), msg );
07059501
GB
746 g_free(msg);
747 }
dc5758d3 748 }
0fb11294
RN
749
750 // Performed in post read as we now know the map type
751#ifdef HAVE_SQLITE3_H
752 // Do some SQL stuff
753 if ( vik_map_source_is_mbtiles ( map ) ) {
754 int ans = sqlite3_open_v2 ( vml->filename,
755 &(vml->mbtiles),
756 SQLITE_OPEN_READONLY,
757 NULL );
758 if ( ans != SQLITE_OK ) {
759 // That didn't work, so here's why:
760 g_warning ( "%s: %s", __FUNCTION__, sqlite3_errmsg ( vml->mbtiles ) );
761
762 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
763 _("Failed to open MBTiles file: %s"),
764 vml->filename );
765 vml->mbtiles = NULL;
766 }
767 }
768#endif
df5b2993
RN
769
770 // If the on Disk OSM Tile Layout type
771 if ( vml->maptype == 21 )
772 // Copy the directory into filename
773 // thus the mapcache look up will be unique when using more than one of these map types
774 vml->filename = g_strdup (vml->cache_dir);
dc5758d3
GB
775}
776
7924723c
RN
777static const gchar* maps_layer_tooltip ( VikMapsLayer *vml )
778{
779 return vik_maps_layer_get_map_label ( vml );
780}
781
911400b5
AF
782static void maps_layer_marshall( VikMapsLayer *vml, guint8 **data, gint *len )
783{
784 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
785}
786
787static VikMapsLayer *maps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
788{
789 VikMapsLayer *rv = maps_layer_new ( vvp );
790 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
791 return rv;
792}
793
50a14534
EB
794/*********************/
795/****** DRAWING ******/
796/*********************/
797
798static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha )
799{
800 guchar *pixels;
801 gint width, height, iii, jjj;
802
803 if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) )
804 {
805 GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0);
806 g_object_unref(G_OBJECT(pixbuf));
807 pixbuf = tmp;
45b0031e
RN
808 if ( !pixbuf )
809 return NULL;
50a14534
EB
810 }
811
812 pixels = gdk_pixbuf_get_pixels(pixbuf);
813 width = gdk_pixbuf_get_width(pixbuf);
814 height = gdk_pixbuf_get_height(pixbuf);
815
816 /* r,g,b,a,r,g,b,a.... */
817 for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++)
818 {
819 pixels += 3;
820 *pixels++ = alpha;
821 }
822 return pixbuf;
823}
824
825static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor )
826{
827 GdkPixbuf *tmp;
828 guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf);
2d5d32c1 829 tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR);
50a14534
EB
830 g_object_unref ( G_OBJECT(pixbuf) );
831 return tmp;
832}
833
0fb11294
RN
834#ifdef HAVE_SQLITE3_H
835/*
836static int sql_select_tile_dump_cb (void *data, int cols, char **fields, char **col_names )
837{
838 g_warning ( "Found %d columns", cols );
839 int i;
840 for ( i = 0; i < cols; i++ ) {
841 g_warning ( "SQL processing %s = %s", col_names[i], fields[i] );
842 }
843 return 0;
844}
845*/
846
847/**
848 *
849 */
850static GdkPixbuf *get_pixbuf_sql_exec ( sqlite3 *sql, gint xx, gint yy, gint zoom )
851{
852 GdkPixbuf *pixbuf = NULL;
853
854 // MBTiles stored internally with the flipping y thingy (i.e. TMS scheme).
855 gint flip_y = (gint) pow(2, zoom)-1 - yy;
856 gchar *statement = g_strdup_printf ( "SELECT tile_data FROM tiles WHERE zoom_level=%d AND tile_column=%d AND tile_row=%d;", zoom, xx, flip_y );
857
858 gboolean finished = FALSE;
859
860 sqlite3_stmt *sql_stmt = NULL;
861 int ans = sqlite3_prepare_v2 ( sql, statement, -1, &sql_stmt, NULL );
862 if ( ans != SQLITE_OK ) {
863 g_warning ( "%s: %s - %d", __FUNCTION__, "prepare failure", ans );
864 finished = TRUE;
865 }
866
867 while ( !finished ) {
868 ans = sqlite3_step ( sql_stmt );
869 switch (ans) {
870 case SQLITE_ROW: {
871 // Get tile_data blob
872 int count = sqlite3_column_count(sql_stmt);
873 if ( count != 1 ) {
874 g_warning ( "%s: %s - %d", __FUNCTION__, "count not one", count );
875 finished = TRUE;
876 }
877 else {
878 const void *data = sqlite3_column_blob ( sql_stmt, 0 );
879 int bytes = sqlite3_column_bytes ( sql_stmt, 0 );
880 if ( bytes < 1 ) {
881 g_warning ( "%s: %s (%d)", __FUNCTION__, "not enough bytes", bytes );
882 finished = TRUE;
883 }
884 else {
885 // Convert these blob bytes into a pixbuf via these streaming operations
886 GInputStream *stream = g_memory_input_stream_new_from_data ( data, bytes, NULL );
887 GError *error = NULL;
888 pixbuf = gdk_pixbuf_new_from_stream ( stream, NULL, &error );
889 if (error || (!pixbuf)) {
890 g_warning ( "%s: %s", __FUNCTION__, error->message );
891 g_error_free ( error );
892 }
893 g_input_stream_close ( stream, NULL, NULL );
894 }
895 }
896 break;
897 }
898 default:
899 // e.g. SQLITE_DONE | SQLITE_ERROR | SQLITE_MISUSE | etc...
900 // Finished normally
901 // and give up on any errors
902 if ( ans != SQLITE_DONE )
903 g_warning ( "%s: %s - %d", __FUNCTION__, "step issue", ans );
904 finished = TRUE;
905 break;
906 }
907 }
908 ans = sqlite3_finalize ( sql_stmt );
909
910 g_free ( statement );
911
912 return pixbuf;
913}
914#endif
915
916static GdkPixbuf *get_mbtiles_pixbuf ( VikMapsLayer *vml, gint xx, gint yy, gint zoom )
917{
918 GdkPixbuf *pixbuf = NULL;
919
920#ifdef HAVE_SQLITE3_H
921 if ( vml->mbtiles ) {
922 /*
923 gchar *statement = g_strdup_printf ( "SELECT name FROM sqlite_master WHERE type='table';" );
924 char *errMsg = NULL;
925 int ans = sqlite3_exec ( vml->mbtiles, statement, sql_select_tile_dump_cb, pixbuf, &errMsg );
926 if ( ans != SQLITE_OK ) {
927 // Only to console for information purposes only
928 g_warning ( "SQL problem: %d for %s - error: %s", ans, statement, errMsg );
929 sqlite3_free( errMsg );
930 }
931 g_free ( statement );
932 */
933
934 // Reading BLOBS is a bit more involved and so can't use the simpler sqlite3_exec ()
935 // Hence this specific function
936 pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, xx, yy, zoom );
937 }
938#endif
939
940 return pixbuf;
941}
942
943static GdkPixbuf *pixbuf_apply_settings ( GdkPixbuf *pixbuf, VikMapsLayer *vml, MapCoord *mapcoord, gdouble xshrinkfactor, gdouble yshrinkfactor )
944{
945 // Apply alpha setting
946 if ( pixbuf && vml->alpha < 255 )
947 pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha );
948
949 if ( pixbuf && ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) )
950 pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor );
951
952 if ( pixbuf )
953 a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y,
954 mapcoord->z, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(vml->maptype)),
df5b2993 955 mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
0fb11294
RN
956
957 return pixbuf;
958}
959
50a14534
EB
960static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor )
961{
962 GdkPixbuf *pixbuf;
963
964 /* get the thing */
965 pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z,
df5b2993 966 mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor, vml->filename );
50a14534
EB
967
968 if ( ! pixbuf ) {
0fb11294
RN
969 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
970 // ATM MBTiles must be 'a direct access type'
971 if ( vik_map_source_is_mbtiles (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
972 pixbuf = get_mbtiles_pixbuf ( vml, mapcoord->x, mapcoord->y, (17 - mapcoord->scale) );
973 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
974 // return now to avoid file tests that aren't appropriate for this map type
975 return pixbuf;
976 }
977 else
978 g_snprintf ( filename_buf, buf_len, DIRECTDIRACCESS,
979 vml->cache_dir, (17 - mapcoord->scale), mapcoord->x, mapcoord->y, ".png" );
980 }
2673b29d
RN
981 else
982 g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE,
983 vml->cache_dir, mode,
984 mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y );
985
01ecda7e 986 if ( g_file_test ( filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
50a14534
EB
987 {
988 GError *gx = NULL;
989 pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx );
990
01ecda7e 991 /* free the pixbuf on error */
50a14534
EB
992 if (gx)
993 {
d17be106
RN
994 if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE ) {
995 // Report a warning
996 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
997 gchar* msg = g_strdup_printf ( _("Couldn't open image file: %s"), gx->message );
998 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
999 g_free (msg);
1000 }
1001 }
50a14534 1002
01ecda7e
MR
1003 g_error_free ( gx );
1004 if ( pixbuf )
1005 g_object_unref ( G_OBJECT(pixbuf) );
1006 pixbuf = NULL;
1007 } else {
0fb11294 1008 pixbuf = pixbuf_apply_settings ( pixbuf, vml, mapcoord, xshrinkfactor, yshrinkfactor );
50a14534
EB
1009 }
1010 }
1011 }
1012 return pixbuf;
1013}
1014
9979924f 1015static gboolean should_start_autodownload(VikMapsLayer *vml, VikViewport *vvp)
10ca2bfe
QT
1016{
1017 const VikCoord *center = vik_viewport_get_center ( vvp );
1018
bbf2149b 1019 if (vik_window_get_pan_move (VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vvp)))))
01da6b4d 1020 /* D'n'D pan in action: do not download */
1c6a6010
GB
1021 return FALSE;
1022
9979924f
RN
1023 // TEMPORARY HACK
1024 // Prevent requests for downloading tiles at Zoom Level 19 and above for most map types
1025 // Allow MapQuest Zoom Level up to 19
1026 // TODO: This should be made a property of the map source and then use that value
1027 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1028 if ( (vml->maptype != 19 && map_utils_mpp_to_scale (xzoom) < -1) || (vml->maptype == 19 && map_utils_mpp_to_scale (xzoom) < -2) )
1029 return FALSE;
1030
10ca2bfe
QT
1031 if (vml->last_center == NULL) {
1032 VikCoord *new_center = g_malloc(sizeof(VikCoord));
1033 *new_center = *center;
1034 vml->last_center = new_center;
1035 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1036 vml->last_ympp = vik_viewport_get_ympp(vvp);
1037 return TRUE;
1038 }
1039
1040 /* TODO: perhaps vik_coord_diff() */
1041 if (vik_coord_equals(vml->last_center, center)
1042 && (vml->last_xmpp == vik_viewport_get_xmpp(vvp))
1043 && (vml->last_ympp == vik_viewport_get_ympp(vvp)))
1044 return FALSE;
1045
1046 *(vml->last_center) = *center;
1047 vml->last_xmpp = vik_viewport_get_xmpp(vvp);
1048 vml->last_ympp = vik_viewport_get_ympp(vvp);
1049 return TRUE;
1050}
1051
50a14534
EB
1052static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br )
1053{
1054 MapCoord ulm, brm;
1055 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
1056 gdouble yzoom = vik_viewport_get_ympp ( vvp );
1057 gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0;
2673b29d 1058 gboolean existence_only = FALSE;
50a14534
EB
1059
1060 if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) {
1061 xshrinkfactor = vml->xmapzoom / xzoom;
1062 yshrinkfactor = vml->ymapzoom / yzoom;
732d1e25
EB
1063 xzoom = vml->xmapzoom;
1064 yzoom = vml->xmapzoom;
1065 if ( ! (xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR &&
1066 yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) ) {
6de95419
RN
1067 if ( xshrinkfactor > REAL_MIN_SHRINKFACTOR && yshrinkfactor > REAL_MIN_SHRINKFACTOR ) {
1068 g_debug ( "%s: existence_only due to SHRINKFACTORS", __FUNCTION__ );
732d1e25 1069 existence_only = TRUE;
6de95419 1070 }
732d1e25 1071 else {
d17be106
RN
1072 // Report the reason for not drawing
1073 if ( IS_VIK_WINDOW ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml)) ) {
1074 gchar* msg = g_strdup_printf ( _("Cowardly refusing to draw tiles or existence of tiles beyond %d zoom out factor"), (int)( 1.0/REAL_MIN_SHRINKFACTOR));
1075 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(vml), msg, VIK_STATUSBAR_INFO );
1076 g_free (msg);
1077 }
732d1e25
EB
1078 return;
1079 }
50a14534
EB
1080 }
1081 }
1082
1083 /* coord -> ID */
820c59f4
GB
1084 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1085 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm ) &&
1086 vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) ) {
50a14534
EB
1087
1088 /* loop & draw */
1089 gint x, y;
1090 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
1091 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
820c59f4 1092 gint mode = vik_map_source_get_uniq_id(map);
50a14534
EB
1093
1094 VikCoord coord;
1095 gint xx, yy, width, height;
1096 GdkPixbuf *pixbuf;
1097
6de95419
RN
1098 // Prevent the program grinding to a halt if trying to deal with thousands of tiles
1099 // which can happen when using a small fixed zoom level and viewing large areas.
1100 // Also prevents very large number of tile download requests
1101 gint tiles = (xmax-xmin) * (ymax-ymin);
1102 if ( tiles > MAX_TILES ) {
1103 g_debug ( "%s: existence_only due to wanting too many tiles (%d)", __FUNCTION__, tiles );
1104 existence_only = TRUE;
1105 }
1106
50a14534
EB
1107 guint max_path_len = strlen(vml->cache_dir) + 40;
1108 gchar *path_buf = g_malloc ( max_path_len * sizeof(char) );
1109
732d1e25 1110 if ( (!existence_only) && vml->autodownload && should_start_autodownload(vml, vvp)) {
33be396e 1111 g_debug("%s: Starting autodownload", __FUNCTION__);
382566c3 1112 if ( !vml->adl_only_missing && vik_map_source_supports_download_only_new (map) )
245f48f5
GB
1113 // Try to download newer tiles
1114 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NEW );
1115 else
1116 // Download only missing tiles
1117 start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE );
10ca2bfe 1118 }
50a14534 1119
820c59f4 1120 if ( vik_map_source_get_tilesize_x(map) == 0 && !existence_only ) {
50a14534
EB
1121 for ( x = xmin; x <= xmax; x++ ) {
1122 for ( y = ymin; y <= ymax; y++ ) {
1123 ulm.x = x;
1124 ulm.y = y;
1125 pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor );
1126 if ( pixbuf ) {
1127 width = gdk_pixbuf_get_width ( pixbuf );
1128 height = gdk_pixbuf_get_height ( pixbuf );
1129
820c59f4 1130 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
50a14534
EB
1131 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
1132 xx -= (width/2);
1133 yy -= (height/2);
1134
1135 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height );
1136 }
1137 }
1138 }
1139 } else { /* tilesize is known, don't have to keep converting coords */
820c59f4
GB
1140 gdouble tilesize_x = vik_map_source_get_tilesize_x(map) * xshrinkfactor;
1141 gdouble tilesize_y = vik_map_source_get_tilesize_y(map) * yshrinkfactor;
50a14534
EB
1142 /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */
1143 gint tilesize_x_ceil = ceil ( tilesize_x );
1144 gint tilesize_y_ceil = ceil ( tilesize_y );
1145 gint8 xinc = (ulm.x == xmin) ? 1 : -1;
1146 gint8 yinc = (ulm.y == ymin) ? 1 : -1;
c60f82df 1147 gint xx_tmp, yy_tmp;
50a14534 1148 gint base_yy, xend, yend;
732d1e25 1149
50a14534
EB
1150 xend = (xinc == 1) ? (xmax+1) : (xmin-1);
1151 yend = (yinc == 1) ? (ymax+1) : (ymin-1);
1152
820c59f4 1153 vik_map_source_mapcoord_to_center_coord ( map, &ulm, &coord );
50a14534
EB
1154 vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp );
1155 xx = xx_tmp; yy = yy_tmp;
1156 /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off
1157 * eg if tile size 128, shrinkfactor 0.333 */
1158 xx -= (tilesize_x/2);
1159 base_yy = yy - (tilesize_y/2);
1160
1161 for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) {
1162 yy = base_yy;
1163 for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) {
1164 ulm.x = x;
1165 ulm.y = y;
732d1e25
EB
1166
1167 if ( existence_only ) {
2673b29d
RN
1168 if ( vik_map_source_is_direct_file_access (MAPS_LAYER_NTH_TYPE(vml->maptype)) )
1169 g_snprintf ( path_buf, max_path_len, DIRECTDIRACCESS,
1170 vml->cache_dir, (17 - ulm.scale), ulm.x, ulm.y, ".png" );
1171 else
1172 g_snprintf ( path_buf, max_path_len, DIRSTRUCTURE,
1173 vml->cache_dir, mode,
1174 ulm.scale, ulm.z, ulm.x, ulm.y );
45acf79e 1175 if ( g_file_test ( path_buf, G_FILE_TEST_EXISTS ) == TRUE ) {
ff37db21 1176 GdkGC *black_gc = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
732d1e25
EB
1177 vik_viewport_draw_line ( vvp, black_gc, xx+tilesize_x_ceil, yy, xx, yy+tilesize_y_ceil );
1178 }
1179 } else {
6f0d1bea
JJ
1180 int scale_inc;
1181 for (scale_inc = 0; scale_inc < 4; scale_inc ++) {
1182 /* try with correct then smaller zooms */
1183 int scale_factor = 1 << scale_inc; /* 2^scale_inc */
1184 MapCoord ulm2 = ulm;
1185 ulm2.x = ulm.x / scale_factor;
1186 ulm2.y = ulm.y / scale_factor;
1187 ulm2.scale = ulm.scale + scale_inc;
1188 pixbuf = get_pixbuf ( vml, mode, &ulm2, path_buf, max_path_len, xshrinkfactor * scale_factor, yshrinkfactor * scale_factor );
1189 if ( pixbuf ) {
1190 gint src_x = (ulm.x % scale_factor) * tilesize_x_ceil;
1191 gint src_y = (ulm.y % scale_factor) * tilesize_y_ceil;
40cfc175
JJ
1192#ifdef DEBUG
1193 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);
1194#endif
6f0d1bea
JJ
1195 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, xx, yy, tilesize_x_ceil, tilesize_y_ceil );
1196 break;
1197 }
1198 }
1199 if ( !pixbuf ) {
1200 /* retry with bigger zooms */
1201 int scale_dec;
1202 for (scale_dec = 1; scale_dec < 2; scale_dec ++) {
1203 int pict_x, pict_y;
1204 int scale_factor = 1 << scale_dec; /* 2^scale_dec */
91dc4449 1205 MapCoord ulm2 = ulm;
6f0d1bea
JJ
1206 ulm2.x = ulm.x * scale_factor;
1207 ulm2.y = ulm.y * scale_factor;
1208 ulm2.scale = ulm.scale - scale_dec;
1209 for (pict_x = 0; pict_x < scale_factor; pict_x ++) {
1210 for (pict_y = 0; pict_y < scale_factor; pict_y ++) {
1211 MapCoord ulm3 = ulm2;
1212 ulm3.x += pict_x;
1213 ulm3.y += pict_y;
1214 pixbuf = get_pixbuf ( vml, mode, &ulm3, path_buf, max_path_len, xshrinkfactor / scale_factor, yshrinkfactor / scale_factor );
1215 if ( pixbuf ) {
1216 gint src_x = 0;
1217 gint src_y = 0;
1218 gint dest_x = xx + pict_x * (tilesize_x_ceil / scale_factor);
1219 gint dest_y = yy + pict_y * (tilesize_y_ceil / scale_factor);
1220 vik_viewport_draw_pixbuf ( vvp, pixbuf, src_x, src_y, dest_x, dest_y, tilesize_x_ceil / scale_factor, tilesize_y_ceil / scale_factor );
1221 }
1222 }
91dc4449
JJ
1223 }
1224 }
1225 }
732d1e25 1226 }
50a14534
EB
1227
1228 yy += tilesize_y;
1229 }
1230 xx += tilesize_x;
1231 }
1232 }
1233
1234 g_free ( path_buf );
1235 }
1236}
1237
1238static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp )
1239{
820c59f4 1240 if ( vik_map_source_get_drawmode(MAPS_LAYER_NTH_TYPE(vml->maptype)) == vik_viewport_get_drawmode ( vvp ) )
50a14534
EB
1241 {
1242 VikCoord ul, br;
1243
82aa018d 1244 /* Copyright */
68b1d6c0
GB
1245 gdouble level = vik_viewport_get_zoom ( vvp );
1246 LatLonBBox bbox;
1247 vik_viewport_get_min_max_lat_lon ( vvp, &bbox.south, &bbox.north, &bbox.west, &bbox.east );
1248 vik_map_source_get_copyright ( MAPS_LAYER_NTH_TYPE(vml->maptype), bbox, level, vik_viewport_add_copyright, vvp );
82aa018d 1249
26336cf0
GB
1250 /* Logo */
1251 const GdkPixbuf *logo = vik_map_source_get_logo ( MAPS_LAYER_NTH_TYPE(vml->maptype) );
1252 vik_viewport_add_logo ( vvp, logo );
1253
50a14534
EB
1254 /* get corner coords */
1255 if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) {
1256 /* UTM multi-zone stuff by Kit Transue */
1257 gchar leftmost_zone, rightmost_zone, i;
1258 leftmost_zone = vik_viewport_leftmost_zone( vvp );
1259 rightmost_zone = vik_viewport_rightmost_zone( vvp );
1260 for ( i = leftmost_zone; i <= rightmost_zone; ++i ) {
1261 vik_viewport_corners_for_zonen ( vvp, i, &ul, &br );
1262 maps_layer_draw_section ( vml, vvp, &ul, &br );
1263 }
1264 }
1265 else {
1266 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1267 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1268
1269 maps_layer_draw_section ( vml, vvp, &ul, &br );
1270 }
1271 }
1272}
1273
1274/*************************/
1275/****** DOWNLOADING ******/
1276/*************************/
1277
1278/* pass along data to thread, exists even if layer is deleted. */
1279typedef struct {
1280 gchar *cache_dir;
1281 gchar *filename_buf;
1282 gint x0, y0, xf, yf;
1283 MapCoord mapcoord;
1284 gint maptype;
1285 gint maxlen;
1286 gint mapstoget;
1287 gint redownload;
7114e879 1288 gboolean refresh_display;
550fd035
QT
1289 VikMapsLayer *vml;
1290 VikViewport *vvp;
1291 gboolean map_layer_alive;
1292 GMutex *mutex;
50a14534
EB
1293} MapDownloadInfo;
1294
1295static void mdi_free ( MapDownloadInfo *mdi )
1296{
550fd035 1297 g_mutex_free(mdi->mutex);
50a14534 1298 g_free ( mdi->cache_dir );
e65028db 1299 mdi->cache_dir = NULL;
50a14534 1300 g_free ( mdi->filename_buf );
e65028db 1301 mdi->filename_buf = NULL;
50a14534
EB
1302 g_free ( mdi );
1303}
1304
7bb60307 1305static void weak_ref_cb(gpointer ptr, GObject * dead_vml)
550fd035 1306{
7bb60307 1307 MapDownloadInfo *mdi = ptr;
550fd035
QT
1308 g_mutex_lock(mdi->mutex);
1309 mdi->map_layer_alive = FALSE;
1310 g_mutex_unlock(mdi->mutex);
1311}
1312
634eca0a 1313static int map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata )
50a14534 1314{
825413ba 1315 void *handle = vik_map_source_download_handle_init(MAPS_LAYER_NTH_TYPE(mdi->maptype));
50a14534
EB
1316 guint donemaps = 0;
1317 gint x, y;
1318 for ( x = mdi->x0; x <= mdi->xf; x++ )
1319 {
1320 for ( y = mdi->y0; y <= mdi->yf; y++ )
1321 {
94493114 1322 gboolean remove_mem_cache = FALSE;
84628352 1323 gboolean need_download = FALSE;
50a14534 1324 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
820c59f4 1325 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
50a14534
EB
1326 mdi->mapcoord.scale, mdi->mapcoord.z, x, y );
1327
84628352 1328 donemaps++;
634eca0a 1329 int res = a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */
825413ba
SW
1330 if (res != 0) {
1331 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
634eca0a 1332 return -1;
825413ba 1333 }
84628352 1334
a0058e48 1335 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
6a4a29aa
JJ
1336 need_download = TRUE;
1337 remove_mem_cache = TRUE;
a0058e48
JJ
1338
1339 } else { /* in case map file already exists */
1340 switch (mdi->redownload) {
1341 case REDOWNLOAD_NONE:
1342 continue;
1343
1344 case REDOWNLOAD_BAD:
1345 {
1346 /* see if this one is bad or what */
1347 GError *gx = NULL;
1348 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1349 if (gx || (!pixbuf)) {
1350 g_remove ( mdi->filename_buf );
1351 need_download = TRUE;
1352 remove_mem_cache = TRUE;
1353 g_error_free ( gx );
1354
1355 } else {
1356 g_object_unref ( pixbuf );
1357 }
1358 break;
1359 }
1360
1361 case REDOWNLOAD_NEW:
1362 need_download = TRUE;
1363 remove_mem_cache = TRUE;
1364 break;
1365
1366 case REDOWNLOAD_ALL:
1367 /* FIXME: need a better way than to erase file in case of server/network problem */
1368 g_remove ( mdi->filename_buf );
1369 need_download = TRUE;
1370 remove_mem_cache = TRUE;
1371 break;
1372
1373 case DOWNLOAD_OR_REFRESH:
1374 remove_mem_cache = TRUE;
1375 break;
1376
1377 default:
1378 g_warning ( "redownload state %d unknown\n", mdi->redownload);
1379 }
1380 }
94493114 1381
94493114 1382 mdi->mapcoord.x = x; mdi->mapcoord.y = y;
84628352
QT
1383
1384 if (need_download) {
4e815e90
RN
1385 DownloadResult_t dr = vik_map_source_download( MAPS_LAYER_NTH_TYPE(mdi->maptype), &(mdi->mapcoord), mdi->filename_buf, handle);
1386 switch ( dr ) {
1387 case DOWNLOAD_HTTP_ERROR:
1388 case DOWNLOAD_CONTENT_ERROR: {
1389 // TODO: ?? count up the number of download errors somehow...
1390 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Failed to download tile") );
1391 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1392 g_free (msg);
1393 break;
1394 }
1395 case DOWNLOAD_FILE_WRITE_ERROR: {
1396 gchar* msg = g_strdup_printf ( "%s: %s", vik_maps_layer_get_map_label (mdi->vml), _("Unable to save tile") );
1397 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(mdi->vml), msg, VIK_STATUSBAR_INFO );
1398 g_free (msg);
1399 break;
1400 }
1401 case DOWNLOAD_SUCCESS:
1402 case DOWNLOAD_NOT_REQUIRED:
1403 default:
1404 break;
1405 }
84628352 1406 }
94493114 1407
94493114
QT
1408 g_mutex_lock(mdi->mutex);
1409 if (remove_mem_cache)
820c59f4 1410 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 1411 if (mdi->refresh_display && mdi->map_layer_alive) {
94493114 1412 /* TODO: check if it's on visible area */
da121f9b 1413 vik_layer_emit_update ( VIK_LAYER(mdi->vml) ); // NB update display from background
a0b59f2f 1414 }
94493114 1415 g_mutex_unlock(mdi->mutex);
94493114
QT
1416 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */
1417
50a14534
EB
1418 }
1419 }
825413ba 1420 vik_map_source_download_handle_cleanup(MAPS_LAYER_NTH_TYPE(mdi->maptype), handle);
04e54492
QT
1421 g_mutex_lock(mdi->mutex);
1422 if (mdi->map_layer_alive)
7bb60307 1423 g_object_weak_unref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
04e54492 1424 g_mutex_unlock(mdi->mutex);
634eca0a 1425 return 0;
50a14534
EB
1426}
1427
1428static void mdi_cancel_cleanup ( MapDownloadInfo *mdi )
1429{
1430 if ( mdi->mapcoord.x || mdi->mapcoord.y )
1431 {
1432 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
820c59f4 1433 mdi->cache_dir, vik_map_source_get_uniq_id(MAPS_LAYER_NTH_TYPE(mdi->maptype)),
50a14534 1434 mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y );
45acf79e 1435 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == TRUE)
50a14534 1436 {
8c060406 1437 g_remove ( mdi->filename_buf );
50a14534
EB
1438 }
1439 }
1440}
1441
1442static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload )
1443{
1444 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1445 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1446 MapCoord ulm, brm;
820c59f4 1447 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
2673b29d
RN
1448
1449 // Don't ever attempt download on direct access
1450 if ( vik_map_source_is_direct_file_access ( map ) )
1451 return;
1452
820c59f4
GB
1453 if ( vik_map_source_coord_to_mapcoord ( map, ul, xzoom, yzoom, &ulm )
1454 && vik_map_source_coord_to_mapcoord ( map, br, xzoom, yzoom, &brm ) )
50a14534
EB
1455 {
1456 MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) );
1457 gint a, b;
1458
550fd035
QT
1459 mdi->vml = vml;
1460 mdi->vvp = vvp;
1461 mdi->map_layer_alive = TRUE;
1462 mdi->mutex = g_mutex_new();
7114e879 1463 mdi->refresh_display = TRUE;
550fd035 1464
50a14534
EB
1465 /* cache_dir and buffer for dest filename */
1466 mdi->cache_dir = g_strdup ( vml->cache_dir );
1467 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1468 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1469 mdi->maptype = vml->maptype;
1470
1471 mdi->mapcoord = ulm;
50a14534
EB
1472 mdi->redownload = redownload;
1473
1474 mdi->x0 = MIN(ulm.x, brm.x);
1475 mdi->xf = MAX(ulm.x, brm.x);
1476 mdi->y0 = MIN(ulm.y, brm.y);
1477 mdi->yf = MAX(ulm.y, brm.y);
1478
1479 mdi->mapstoget = 0;
1480
1481 if ( mdi->redownload ) {
1482 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1483 } else {
1484 /* calculate how many we need */
1485 for ( a = mdi->x0; a <= mdi->xf; a++ )
1486 {
1487 for ( b = mdi->y0; b <= mdi->yf; b++ )
1488 {
1489 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
820c59f4 1490 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
50a14534 1491 ulm.z, a, b );
45acf79e 1492 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
50a14534
EB
1493 mdi->mapstoget++;
1494 }
1495 }
1496 }
1497
1498 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1499
1500 if ( mdi->mapstoget )
1501 {
97634600
GB
1502 const gchar *tmp_str;
1503 gchar *tmp;
50a14534 1504
97634600
GB
1505 if (redownload)
1506 {
1507 if (redownload == REDOWNLOAD_BAD)
1508 tmp_str = ngettext("Redownloading up to %d %s map...", "Redownloading up to %d %s maps...", mdi->mapstoget);
1509 else
1510 tmp_str = ngettext("Redownloading %d %s map...", "Redownloading %d %s maps...", mdi->mapstoget);
1511 }
1512 else
1513 {
1514 tmp_str = ngettext("Downloading %d %s map...", "Downloading %d %s maps...", mdi->mapstoget);
1515 }
1516 tmp = g_strdup_printf ( tmp_str, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype));
1517
7bb60307 1518 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
50a14534
EB
1519 /* launch the thread */
1520 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1521 tmp, /* description string */
1522 (vik_thr_func) map_download_thread, /* function to call within thread */
1523 mdi, /* pass along data */
1524 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1525 (vik_thr_free_func) mdi_cancel_cleanup,
1526 mdi->mapstoget );
1527 g_free ( tmp );
1528 }
1529 else
1530 mdi_free ( mdi );
1531 }
1532}
1533
3ac548fa 1534static void maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint download_method )
7114e879 1535{
7114e879 1536 MapCoord ulm, brm;
820c59f4 1537 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
7114e879 1538
2673b29d
RN
1539 // Don't ever attempt download on direct access
1540 if ( vik_map_source_is_direct_file_access ( map ) )
1541 return;
1542
820c59f4
GB
1543 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1544 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
4258f4e2 1545 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
7114e879
QT
1546 return;
1547 }
1548
1549 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1550 gint i, j;
1551
1552 mdi->vml = vml;
1553 mdi->vvp = vvp;
1554 mdi->map_layer_alive = TRUE;
1555 mdi->mutex = g_mutex_new();
903dc0b4 1556 mdi->refresh_display = TRUE;
7114e879
QT
1557
1558 mdi->cache_dir = g_strdup ( vml->cache_dir );
1559 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1560 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1561 mdi->maptype = vml->maptype;
1562
1563 mdi->mapcoord = ulm;
3ac548fa 1564 mdi->redownload = download_method;
7114e879
QT
1565
1566 mdi->x0 = MIN(ulm.x, brm.x);
1567 mdi->xf = MAX(ulm.x, brm.x);
1568 mdi->y0 = MIN(ulm.y, brm.y);
1569 mdi->yf = MAX(ulm.y, brm.y);
1570
1571 mdi->mapstoget = 0;
1572
1573 for (i = mdi->x0; i <= mdi->xf; i++) {
1574 for (j = mdi->y0; j <= mdi->yf; j++) {
1575 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
820c59f4 1576 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
7114e879 1577 ulm.z, i, j );
45acf79e 1578 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE )
7114e879
QT
1579 mdi->mapstoget++;
1580 }
1581 }
1582
1583 mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */
1584
1585 if (mdi->mapstoget) {
4c77d5e0
GB
1586 gchar *tmp;
1587 const gchar *fmt;
eb6b0125
JJ
1588 fmt = ngettext("Downloading %d %s map...",
1589 "Downloading %d %s maps...",
1590 mdi->mapstoget);
4c77d5e0 1591 tmp = g_strdup_printf ( fmt, mdi->mapstoget, MAPS_LAYER_NTH_LABEL(vml->maptype) );
7114e879
QT
1592
1593 g_object_weak_ref(G_OBJECT(mdi->vml), weak_ref_cb, mdi);
1594 /* launch the thread */
1595 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */
1596 tmp, /* description string */
1597 (vik_thr_func) map_download_thread, /* function to call within thread */
1598 mdi, /* pass along data */
1599 (vik_thr_free_func) mdi_free, /* function to free pass along data */
1600 (vik_thr_free_func) mdi_cancel_cleanup,
1601 mdi->mapstoget );
1602 g_free ( tmp );
1603 }
1604 else
1605 mdi_free ( mdi );
1606}
1607
3ac548fa
RN
1608/**
1609 * vik_maps_layer_download_section:
1610 * @vml: The Map Layer
1611 * @vvp: The Viewport that the map is on
1612 * @ul: Upper left coordinate of the area to be downloaded
1613 * @br: Bottom right coordinate of the area to be downloaded
1614 * @zoom: The zoom level at which the maps are to be download
1615 *
1616 * Download a specified map area at a certain zoom level
1617 */
1618void vik_maps_layer_download_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom )
1619{
1620 maps_layer_download_section (vml, vvp, ul, br, zoom, REDOWNLOAD_NONE);
1621}
1622
50a14534
EB
1623static void maps_layer_redownload_bad ( VikMapsLayer *vml )
1624{
1625 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD );
1626}
a7c1acf1 1627
50a14534
EB
1628static void maps_layer_redownload_all ( VikMapsLayer *vml )
1629{
1630 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL );
1631}
1632
a7c1acf1
GB
1633static void maps_layer_redownload_new ( VikMapsLayer *vml )
1634{
1635 start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_NEW );
1636}
1637
450d8665
RN
1638/**
1639 * Display a simple dialog with information about this particular map tile
1640 */
1641static void maps_layer_tile_info ( VikMapsLayer *vml )
1642{
1643 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1644
1645 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vml->redownload_vvp );
1646 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vml->redownload_vvp );
1647 MapCoord ulm;
1648
1649 if ( !vik_map_source_coord_to_mapcoord ( map, &(vml->redownload_ul), xzoom, yzoom, &ulm ) )
1650 return;
1651
1652 gchar *filename = NULL;
1653 gchar *message = NULL;
1654 gchar *source = NULL;
1655
1656 if ( vik_map_source_is_direct_file_access ( map ) ) {
0fb11294
RN
1657 if ( vik_map_source_is_mbtiles ( map ) ) {
1658 filename = g_strdup ( vml->filename );
1659#ifdef HAVE_SQLITE3_H
1660 // And whether to bother going into the SQL to check it's really there or not...
1661 gchar *exists = NULL;
8c778528 1662 gint zoom = 17 - ulm.scale;
0fb11294 1663 if ( vml->mbtiles ) {
8c778528 1664 GdkPixbuf *pixbuf = get_pixbuf_sql_exec ( vml->mbtiles, ulm.x, ulm.y, zoom );
0fb11294
RN
1665 if ( pixbuf ) {
1666 exists = g_strdup ( _("YES") );
1667 g_object_unref ( G_OBJECT(pixbuf) );
1668 }
1669 else {
1670 exists = g_strdup ( _("NO") );
1671 }
1672 }
1673 else
1674 exists = g_strdup ( _("NO") );
8c778528 1675 gint flip_y = (gint) pow(2, zoom)-1 - ulm.y;
0fb11294 1676 // NB Also handles .jpg automatically due to pixbuf_new_from () support - although just print png for now.
8c778528 1677 source = g_strdup_printf ( "%s (%d%s%d%s%d.%s %s)", filename, zoom, G_DIR_SEPARATOR_S, ulm.x, G_DIR_SEPARATOR_S, flip_y, "png", exists );
0fb11294
RN
1678 g_free ( exists );
1679#else
1680 source = g_strdup ( _("Not available") );
1681#endif
1682 }
1683 else {
1684 filename = g_strdup_printf ( DIRECTDIRACCESS, vml->cache_dir, ulm.scale, ulm.x, ulm.y, ".png" );
1685 source = g_strconcat ( "file://", filename, NULL );
1686 }
450d8665
RN
1687 }
1688 else {
1689 filename = g_strdup_printf ( DIRSTRUCTURE, vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale, ulm.z, ulm.x, ulm.y );
1690 source = g_strdup_printf ( "http://%s%s",
1691 vik_map_source_default_get_hostname ( VIK_MAP_SOURCE_DEFAULT(map) ),
1692 vik_map_source_default_get_uri ( VIK_MAP_SOURCE_DEFAULT(map), &ulm ) );
1693 }
1694
1695 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1696
1697 // Get some timestamp information of the tile
1698 struct stat stat_buf;
1699 if ( g_stat ( filename, &stat_buf ) == 0 ) {
6be843d5
RN
1700 gchar time_buf[64];
1701 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1702 message = g_strdup_printf ( _("\nSource: %s\n\nTile File: %s\nTile File Timestamp: %s"), source, filename, time_buf );
450d8665
RN
1703 }
1704 }
1705 else
1706 message = g_strdup_printf ( _("Source: %s\n\nNo Tile File!"), source );
1707
1708 // Show the info
1709 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), message );
1710
1711 g_free ( message );
1712 g_free ( source );
1713 g_free ( filename );
1714}
1715
50a14534
EB
1716static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1717{
941aa6e9
AF
1718 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1719 return FALSE;
50a14534
EB
1720 if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 )
1721 {
1722 if ( event->button == 1 )
1723 {
1724 VikCoord ul, br;
1725 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 );
1726 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 1727 start_download_thread ( vml, vvp, &ul, &br, DOWNLOAD_OR_REFRESH );
50a14534
EB
1728 vml->dl_tool_x = vml->dl_tool_y = -1;
1729 return TRUE;
1730 }
1731 else
1732 {
1733 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) );
1734 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) );
1735
1736 vml->redownload_vvp = vvp;
1737
1738 vml->dl_tool_x = vml->dl_tool_y = -1;
1739
1740 if ( ! vml->dl_right_click_menu ) {
1741 GtkWidget *item;
1742 vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () );
1743
94ee2264 1744 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _Bad Map(s)") );
50a14534
EB
1745 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml );
1746 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1747
94ee2264 1748 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _New Map(s)") );
a7c1acf1
GB
1749 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_new), vml );
1750 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
1751
94ee2264 1752 item = gtk_menu_item_new_with_mnemonic ( _("Redownload _All Map(s)") );
50a14534
EB
1753 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml );
1754 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item );
450d8665
RN
1755
1756 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Tile Information") );
1757 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1758 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_tile_info), vml );
1759 gtk_menu_shell_append (GTK_MENU_SHELL(vml->dl_right_click_menu), item);
50a14534
EB
1760 }
1761
1762 gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1763 gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) );
1764 }
1765 }
1766 return FALSE;
1767}
1768
941aa6e9
AF
1769static gpointer maps_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1770{
1771 return vvp;
1772}
1773
50a14534
EB
1774static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp )
1775{
1776 MapCoord tmp;
941aa6e9
AF
1777 if (!vml || vml->vl.type != VIK_LAYER_MAPS)
1778 return FALSE;
820c59f4
GB
1779 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1780 if ( vik_map_source_get_drawmode(map) == vik_viewport_get_drawmode ( vvp ) &&
1781 vik_map_source_coord_to_mapcoord ( map, vik_viewport_get_center ( vvp ),
50a14534
EB
1782 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1783 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1784 &tmp ) ) {
1785 vml->dl_tool_x = event->x, vml->dl_tool_y = event->y;
1786 return TRUE;
1787 }
1788 return FALSE;
1789
1790
1791#if 0
1792 if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) )
1793 {
1794 VikCoord coord;
1795 MapCoord mapcoord;
1796 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1797 if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord,
1798 vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ),
1799 vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ),
1800 &mapcoord ) ) {
1801 gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE,
1802 vml->cache_dir, __map_types[vml->maptype].uniq_id,
1803 mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y );
1804
1805 __map_types[vml->maptype].download ( &mapcoord, filename_buf );
1806 g_free ( filename_buf );
1807 vik_layer_emit_update ( VIK_LAYER(vml) );
1808 return TRUE;
1809 }
1810 }
1811 return FALSE;
1812#endif
1813}
1814
7a7ba2f1
RN
1815// A slightly better way of defining the menu callback information
1816// This should be easier to extend/rework compared to previously
1817typedef enum {
1818 MA_VML = 0,
1819 MA_VVP,
1820 MA_LAST
1821} menu_array_index;
1822
1823typedef gpointer menu_array_values[MA_LAST];
1824
1825static void download_onscreen_maps ( menu_array_values values, gint redownload )
50a14534 1826{
7a7ba2f1
RN
1827 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
1828 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
314c1ccc 1829 VikViewportDrawMode vp_drawmode = vik_viewport_get_drawmode ( vvp );
50a14534
EB
1830
1831 gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp );
1832 gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp );
1833
1834 VikCoord ul, br;
1835 MapCoord ulm, brm;
1836
1837 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
1838 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
1839
820c59f4
GB
1840 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1841 if ( vik_map_source_get_drawmode(map) == vp_drawmode &&
1842 vik_map_source_coord_to_mapcoord ( map, &ul, xzoom, yzoom, &ulm ) &&
1843 vik_map_source_coord_to_mapcoord ( map, &br, xzoom, yzoom, &brm ) )
50817314 1844 start_download_thread ( vml, vvp, &ul, &br, redownload );
820c59f4
GB
1845 else if (vik_map_source_get_drawmode(map) != vp_drawmode) {
1846 const gchar *drawmode_name = vik_viewport_get_drawmode_name (vvp, vik_map_source_get_drawmode(map));
4c77d5e0 1847 gchar *err = g_strdup_printf(_("Wrong drawmode for this map.\nSelect \"%s\" from View menu and try again."), _(drawmode_name));
314c1ccc
QT
1848 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), err );
1849 g_free(err);
1850 }
50a14534 1851 else
4c77d5e0 1852 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Wrong zoom level for this map.") );
50a14534
EB
1853
1854}
1855
7a7ba2f1 1856static void maps_layer_download_missing_onscreen_maps ( menu_array_values values )
50817314 1857{
7a7ba2f1 1858 download_onscreen_maps( values, REDOWNLOAD_NONE);
50817314
GB
1859}
1860
7a7ba2f1 1861static void maps_layer_download_new_onscreen_maps ( menu_array_values values )
6a4a29aa 1862{
7a7ba2f1 1863 download_onscreen_maps( values, REDOWNLOAD_NEW);
6a4a29aa
JJ
1864}
1865
7a7ba2f1 1866static void maps_layer_redownload_all_onscreen_maps ( menu_array_values values )
50817314 1867{
7a7ba2f1 1868 download_onscreen_maps( values, REDOWNLOAD_ALL);
50817314
GB
1869}
1870
b03ae39b
RN
1871static void maps_layers_about ( gpointer vml_vvp[2] )
1872{
1873 VikMapsLayer *vml = vml_vvp[0];
1874 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1875
1876 if ( vik_map_source_get_license (map) )
1877 maps_show_license ( VIK_GTK_WINDOW_FROM_LAYER(vml), map );
1878 else
1879 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),
1880 vik_map_source_get_label (map) );
1881}
1882
3ac548fa
RN
1883/**
1884 * maps_layer_how_many_maps:
1885 * Copied from maps_layer_download_section but without the actual download and this returns a value
1886 */
1887static gint maps_layer_how_many_maps ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br, gdouble zoom, gint redownload )
1888{
1889 MapCoord ulm, brm;
1890 VikMapSource *map = MAPS_LAYER_NTH_TYPE(vml->maptype);
1891
1892 if ( vik_map_source_is_direct_file_access ( map ) )
1893 return 0;
1894
1895 if (!vik_map_source_coord_to_mapcoord(map, ul, zoom, zoom, &ulm)
1896 || !vik_map_source_coord_to_mapcoord(map, br, zoom, zoom, &brm)) {
1897 g_warning("%s() coord_to_mapcoord() failed", __PRETTY_FUNCTION__);
1898 return 0;
1899 }
1900
1901 MapDownloadInfo *mdi = g_malloc(sizeof(MapDownloadInfo));
1902 gint i, j;
1903
1904 mdi->vml = vml;
1905 mdi->vvp = vvp;
1906 mdi->map_layer_alive = TRUE;
1907 mdi->mutex = g_mutex_new();
1908 mdi->refresh_display = FALSE;
1909
1910 mdi->cache_dir = g_strdup ( vml->cache_dir );
1911 mdi->maxlen = strlen ( vml->cache_dir ) + 40;
1912 mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) );
1913 mdi->maptype = vml->maptype;
1914
1915 mdi->mapcoord = ulm;
1916 mdi->redownload = redownload;
1917
1918 mdi->x0 = MIN(ulm.x, brm.x);
1919 mdi->xf = MAX(ulm.x, brm.x);
1920 mdi->y0 = MIN(ulm.y, brm.y);
1921 mdi->yf = MAX(ulm.y, brm.y);
1922
1923 mdi->mapstoget = 0;
1924
1925 if ( mdi->redownload == REDOWNLOAD_ALL ) {
1926 mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1);
1927 }
1928 else {
1929 /* calculate how many we need */
1930 for (i = mdi->x0; i <= mdi->xf; i++) {
1931 for (j = mdi->y0; j <= mdi->yf; j++) {
1932 g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE,
1933 vml->cache_dir, vik_map_source_get_uniq_id(map), ulm.scale,
1934 ulm.z, i, j );
1935 if ( mdi->redownload == REDOWNLOAD_NEW ) {
1936 // Assume the worst - always a new file
1937 // Absolute value would requires server lookup - but that is too slow
1938 mdi->mapstoget++;
1939 }
1940 else {
1941 if ( g_file_test ( mdi->filename_buf, G_FILE_TEST_EXISTS ) == FALSE ) {
1942 // Missing
1943 mdi->mapstoget++;
1944 }
1945 else {
1946 if ( mdi->redownload == REDOWNLOAD_BAD ) {
1947 /* see if this one is bad or what */
1948 GError *gx = NULL;
1949 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx );
1950 if (gx || (!pixbuf)) {
1951 mdi->mapstoget++;
1952 }
1953 break;
1954 // Other download cases already considered or just ignored
1955 }
1956 }
1957 }
1958 }
1959 }
1960 }
1961
1962 gint rv = mdi->mapstoget;
1963
1964 mdi_free ( mdi );
1965
1966 return rv;
1967}
1968
1969/**
1970 * maps_dialog_zoom_between:
1971 * This dialog is specific to the map layer, so it's here rather than in dialog.c
1972 */
1973gboolean maps_dialog_zoom_between ( GtkWindow *parent,
1974 gchar *title,
1975 gchar *zoom_list[],
1976 gint default_zoom1,
1977 gint default_zoom2,
1978 gint *selected_zoom1,
1979 gint *selected_zoom2,
1980 gchar *download_list[],
1981 gint default_download,
1982 gint *selected_download )
1983{
1984 GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
1985 parent,
1986 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1987 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1988 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1989 NULL );
1990 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1991 GtkWidget *response_w = NULL;
1992#if GTK_CHECK_VERSION (2, 20, 0)
1993 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1994#endif
1995 GtkWidget *zoom_label1 = gtk_label_new ( _("Zoom Start:") );
1996 GtkWidget *zoom_combo1 = vik_combo_box_text_new();
1997 gchar **s;
1998 for (s = zoom_list; *s; s++)
1999 vik_combo_box_text_append ( zoom_combo1, *s );
2000 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo1), default_zoom1 );
2001
2002 GtkWidget *zoom_label2 = gtk_label_new ( _("Zoom End:") );
2003 GtkWidget *zoom_combo2 = vik_combo_box_text_new();
2004 for (s = zoom_list; *s; s++)
2005 vik_combo_box_text_append ( zoom_combo2, *s );
2006 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo2), default_zoom2 );
2007
2008 GtkWidget *download_label = gtk_label_new(_("Download Maps Method:"));
2009 GtkWidget *download_combo = vik_combo_box_text_new();
2010 for (s = download_list; *s; s++)
2011 vik_combo_box_text_append ( download_combo, *s );
2012 gtk_combo_box_set_active ( GTK_COMBO_BOX(download_combo), default_download );
2013
2014 GtkTable *box = GTK_TABLE(gtk_table_new(3, 2, FALSE));
2015 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label1), 0, 1, 0, 1);
2016 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo1), 1, 2, 0, 1);
2017 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_label2), 0, 1, 1, 2);
2018 gtk_table_attach_defaults (box, GTK_WIDGET(zoom_combo2), 1, 2, 1, 2);
2019 gtk_table_attach_defaults (box, GTK_WIDGET(download_label), 0, 1, 2, 3);
2020 gtk_table_attach_defaults (box, GTK_WIDGET(download_combo), 1, 2, 2, 3);
2021
2022 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
2023
2024 if ( response_w )
2025 gtk_widget_grab_focus ( response_w );
2026
2027 gtk_widget_show_all ( dialog );
2028 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2029 gtk_widget_destroy(dialog);
2030 return FALSE;
2031 }
2032
2033 // Return selected options
2034 *selected_zoom1 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo1) );
2035 *selected_zoom2 = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo2) );
2036 *selected_download = gtk_combo_box_get_active ( GTK_COMBO_BOX(download_combo) );
2037
2038 gtk_widget_destroy(dialog);
2039 return TRUE;
2040}
2041
2042// My best guess of sensible limits
2043#define REALLY_LARGE_AMOUNT_OF_TILES 5000
2044#define CONFIRM_LARGE_AMOUNT_OF_TILES 500
2045
2046/**
2047 * Get all maps in the region for zoom levels specified by the user
2048 * Sort of similar to trw_layer_download_map_along_track_cb function
2049 */
7a7ba2f1 2050static void maps_layer_download_all ( menu_array_values values )
3ac548fa 2051{
7a7ba2f1
RN
2052 VikMapsLayer *vml = VIK_MAPS_LAYER(values[MA_VML]);
2053 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
3ac548fa
RN
2054
2055 // I don't think we should allow users to hammer the servers too much...
2056 // Delibrately not allowing lowest zoom levels
2057 // Still can give massive numbers to download
2058 // A screen size of 1600x1200 gives around 300,000 tiles between 1..128 when none exist before !!
2059 gchar *zoom_list[] = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
2060 gdouble zoom_vals[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
2061
2062 gint selected_zoom1, selected_zoom2, default_zoom, lower_zoom;
2063 gint selected_download_method;
2064
2065 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
2066
2067 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
2068 if (cur_zoom == zoom_vals[default_zoom])
2069 break;
2070 }
2071 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
2072
2073 // Default to only 2 zoom levels below the current one
2074 if (default_zoom > 1 )
2075 lower_zoom = default_zoom - 2;
2076 else
2077 lower_zoom = default_zoom;
2078
2079 // redownload method - needs to align with REDOWNLOAD* macro values
2080 gchar *download_list[] = { _("Missing"), _("Bad"), _("New"), _("Reload All"), NULL };
2081
2082 gchar *title = g_strdup_printf ( ("%s: %s"), vik_maps_layer_get_map_label (vml), _("Download for Zoom Levels") );
2083
2084 if ( ! maps_dialog_zoom_between ( VIK_GTK_WINDOW_FROM_LAYER(vml),
2085 title,
2086 zoom_list,
2087 lower_zoom,
2088 default_zoom,
2089 &selected_zoom1,
2090 &selected_zoom2,
2091 download_list,
2092 REDOWNLOAD_NONE, // AKA Missing
2093 &selected_download_method ) ) {
2094 // Cancelled
2095 g_free ( title );
2096 return;
2097 }
2098 g_free ( title );
2099
2100 // Find out new current positions
2101 gdouble min_lat, max_lat, min_lon, max_lon;
2102 VikCoord vc_ul, vc_br;
2103 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2104 struct LatLon ll_ul = { max_lat, min_lon };
2105 struct LatLon ll_br = { min_lat, max_lon };
2106 vik_coord_load_from_latlon ( &vc_ul, vik_viewport_get_coord_mode (vvp), &ll_ul );
2107 vik_coord_load_from_latlon ( &vc_br, vik_viewport_get_coord_mode (vvp), &ll_br );
2108
2109 // Get Maps Count - call for each zoom level (in reverse)
2110 // With REDOWNLOAD_NEW this is a possible maximum
2111 // With REDOWNLOAD_NONE this only missing ones - however still has a server lookup per tile
2112 gint map_count = 0;
2113 gint zz;
2114 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2115 map_count = map_count + maps_layer_how_many_maps ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2116 }
2117
2118 g_debug ("vikmapslayer: download request map count %d for method %d", map_count, selected_download_method);
2119
2120 // Absolute protection of hammering a map server
2121 if ( map_count > REALLY_LARGE_AMOUNT_OF_TILES ) {
2122 gchar *str = g_strdup_printf (_("You are not allowed to download more than %d tiles in one go (requested %d)"), REALLY_LARGE_AMOUNT_OF_TILES, map_count);
2123 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), str );
2124 g_free (str);
2125 return;
2126 }
2127
2128 // Confirm really want to do this
2129 if ( map_count > CONFIRM_LARGE_AMOUNT_OF_TILES ) {
2130 gchar *str = g_strdup_printf (_("Do you really want to download %d tiles?"), map_count);
2131 gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vml), str, NULL );
2132 g_free (str);
2133 if ( ! ans )
2134 return;
2135 }
2136
2137 // Get Maps - call for each zoom level (in reverse)
2138 for ( zz = selected_zoom2; zz >= selected_zoom1; zz-- ) {
2139 maps_layer_download_section ( vml, vvp, &vc_ul, &vc_br, zoom_vals[zz], selected_download_method );
2140 }
2141}
2142
50a14534
EB
2143static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp )
2144{
50a14534 2145 GtkWidget *item;
7a7ba2f1
RN
2146 static menu_array_values values;
2147 values[MA_VML] = vml;
2148 values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
50a14534
EB
2149
2150 item = gtk_menu_item_new();
2151 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2152 gtk_widget_show ( item );
2153
555ec6f6
RN
2154 /* Now with icons */
2155 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _Missing Onscreen Maps") );
2156 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7a7ba2f1 2157 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_missing_onscreen_maps), values );
50a14534
EB
2158 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2159 gtk_widget_show ( item );
50817314 2160
c81ded98 2161 if ( vik_map_source_supports_download_only_new (MAPS_LAYER_NTH_TYPE(vml->maptype)) ) {
555ec6f6
RN
2162 item = gtk_image_menu_item_new_with_mnemonic ( _("Download _New Onscreen Maps") );
2163 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REDO, GTK_ICON_SIZE_MENU) );
7a7ba2f1 2164 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_new_onscreen_maps), values );
6a4a29aa
JJ
2165 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2166 gtk_widget_show ( item );
2167 }
2168
555ec6f6
RN
2169 item = gtk_image_menu_item_new_with_mnemonic ( _("Reload _All Onscreen Maps") );
2170 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7a7ba2f1 2171 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all_onscreen_maps), values );
50817314
GB
2172 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2173 gtk_widget_show ( item );
3ac548fa
RN
2174
2175 item = gtk_image_menu_item_new_with_mnemonic ( _("Download Maps in _Zoom Levels...") );
2176 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DND_MULTIPLE, GTK_ICON_SIZE_MENU) );
7a7ba2f1 2177 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_all), values );
3ac548fa
RN
2178 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2179 gtk_widget_show ( item );
b03ae39b
RN
2180
2181 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
7a7ba2f1 2182 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layers_about), values );
b03ae39b
RN
2183 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2184 gtk_widget_show ( item );
50a14534 2185}
6b59f63d
RN
2186
2187/**
2188 * Enable downloading maps of the current screen area either 'new' or 'everything'
2189 */
2190void vik_maps_layer_download ( VikMapsLayer *vml, VikViewport *vvp, gboolean only_new )
2191{
2192 if ( !vml ) return;
2193 if ( !vvp ) return;
2194
7a7ba2f1
RN
2195 static menu_array_values values;
2196 values[MA_VML] = vml;
2197 values[MA_VVP] = vvp;
6b59f63d
RN
2198
2199 if ( only_new )
2200 // Get only new maps
7a7ba2f1 2201 maps_layer_download_new_onscreen_maps ( values );
6b59f63d
RN
2202 else
2203 // Redownload everything
7a7ba2f1 2204 maps_layer_redownload_all_onscreen_maps ( values );
6b59f63d 2205}