]> git.street.me.uk Git - andy/viking.git/blame - src/vikmapniklayer.c
Improved feedback/feature availability when no direction routing engines are available.
[andy/viking.git] / src / vikmapniklayer.c
CommitLineData
5fa4fe86
RN
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
4 * Copyright (c) 2015, Rob Norris <rw_norris@hotmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
25
26#include "viking.h"
27#include "vikutils.h"
28#include <glib.h>
29#include <glib/gstdio.h>
30#include <glib/gi18n.h>
31#include <string.h>
32#include <math.h>
33#include <stdlib.h>
34#include <ctype.h>
35
36#include "map_ids.h"
37#include "maputils.h"
38#include "mapcoord.h"
39#include "mapcache.h"
0a25e232 40#include "dir.h"
5fa4fe86
RN
41#include "util.h"
42#include "ui_util.h"
43#include "preferences.h"
44#include "icons/icons.h"
45#include "mapnik_interface.h"
892a9205 46#include "background.h"
5fa4fe86 47
0a25e232
RN
48#include "vikmapslayer.h"
49
5fa4fe86
RN
50struct _VikMapnikLayerClass
51{
52 VikLayerClass object_class;
53};
54
55static VikLayerParamData file_default ( void )
56{
57 VikLayerParamData data;
58 data.s = "";
59 return data;
60}
61
62static VikLayerParamData size_default ( void ) { return VIK_LPD_UINT ( 256 ); }
63static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
64
0a25e232
RN
65static VikLayerParamData cache_dir_default ( void )
66{
67 VikLayerParamData data;
68 data.s = g_strconcat ( maps_layer_default_dir(), "MapnikRendering", NULL );
69 return data;
70}
71
5fa4fe86
RN
72static VikLayerParamScale scales[] = {
73 { 0, 255, 5, 0 }, // Alpha
74 { 64, 1024, 8, 0 }, // Tile size
0a25e232 75 { 0, 1024, 12, 0 }, // Rerender timeout hours
5fa4fe86
RN
76};
77
78VikLayerParam mapnik_layer_params[] = {
b47af40d
RN
79 { VIK_LAYER_MAPNIK, "config-file-mml", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("CSS (MML) Config File:"), VIK_LAYER_WIDGET_FILEENTRY, GINT_TO_POINTER(VF_FILTER_CARTO), NULL,
80 N_("CartoCSS configuration file"), file_default, NULL, NULL },
5fa4fe86
RN
81 { VIK_LAYER_MAPNIK, "config-file-xml", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("XML Config File:"), VIK_LAYER_WIDGET_FILEENTRY, GINT_TO_POINTER(VF_FILTER_XML), NULL,
82 N_("Mapnik XML configuration file"), file_default, NULL, NULL },
83 { VIK_LAYER_MAPNIK, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, &scales[0], NULL,
84 NULL, alpha_default, NULL, NULL },
0a25e232
RN
85 { VIK_LAYER_MAPNIK, "use-file-cache", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Use File Cache:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
86 NULL, vik_lpd_true_default, NULL, NULL },
87 { VIK_LAYER_MAPNIK, "file-cache-dir", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("File Cache Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL,
88 NULL, cache_dir_default, NULL, NULL },
5fa4fe86
RN
89};
90
91enum {
b47af40d
RN
92 PARAM_CONFIG_CSS = 0,
93 PARAM_CONFIG_XML,
94 PARAM_ALPHA,
0a25e232
RN
95 PARAM_USE_FILE_CACHE,
96 PARAM_FILE_CACHE_DIR,
b47af40d 97 NUM_PARAMS };
5fa4fe86
RN
98
99static const gchar* mapnik_layer_tooltip ( VikMapnikLayer *vml );
100static void mapnik_layer_marshall( VikMapnikLayer *vml, guint8 **data, gint *len );
101static VikMapnikLayer *mapnik_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
102static gboolean mapnik_layer_set_param ( VikMapnikLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
103static VikLayerParamData mapnik_layer_get_param ( VikMapnikLayer *vml, guint16 id, gboolean is_file_operation );
104static VikMapnikLayer *mapnik_layer_new ( VikViewport *vvp );
105static VikMapnikLayer *mapnik_layer_create ( VikViewport *vp );
106static void mapnik_layer_free ( VikMapnikLayer *vml );
107static void mapnik_layer_draw ( VikMapnikLayer *vml, VikViewport *vp );
108static void mapnik_layer_add_menu_items ( VikMapnikLayer *vml, GtkMenu *menu, gpointer vlp );
109
93eac03d
RN
110static gpointer mapnik_feature_create ( VikWindow *vw, VikViewport *vvp)
111{
112 return vvp;
113}
114
115static gboolean mapnik_feature_release ( VikMapnikLayer *vml, GdkEventButton *event, VikViewport *vvp );
116
5fa4fe86
RN
117// See comment in viktrwlayer.c for advice on values used
118// FUTURE:
119static VikToolInterface mapnik_tools[] = {
120 // Layer Info
121 // Zoom All?
93eac03d
RN
122 { { "MapnikFeatures", GTK_STOCK_INFO, N_("_Mapnik Features"), NULL, N_("Mapnik Features"), 0 },
123 (VikToolConstructorFunc) mapnik_feature_create,
124 NULL,
125 NULL,
126 NULL,
127 NULL,
128 NULL,
129 (VikToolMouseFunc) mapnik_feature_release,
130 NULL,
131 FALSE,
132 GDK_LEFT_PTR, NULL, NULL },
5fa4fe86
RN
133};
134
135static void mapnik_layer_post_read (VikLayer *vl, VikViewport *vvp, gboolean from_file);
136
137VikLayerInterface vik_mapnik_layer_interface = {
138 "Mapnik Rendering",
139 N_("Mapnik Rendering"),
140 NULL,
141 &vikmapniklayer_pixbuf, // icon
142
143 mapnik_tools,
144 sizeof(mapnik_tools) / sizeof(VikToolInterface),
145
146 mapnik_layer_params,
147 NUM_PARAMS,
148 NULL,
149 0,
150
151 VIK_MENU_ITEM_ALL,
152
153 (VikLayerFuncCreate) mapnik_layer_create,
154 (VikLayerFuncRealize) NULL,
155 (VikLayerFuncPostRead) mapnik_layer_post_read,
156 (VikLayerFuncFree) mapnik_layer_free,
157
158 (VikLayerFuncProperties) NULL,
159 (VikLayerFuncDraw) mapnik_layer_draw,
160 (VikLayerFuncChangeCoordMode) NULL,
161
03c97bc3
RN
162 (VikLayerFuncGetTimestamp) NULL,
163
5fa4fe86
RN
164 (VikLayerFuncSetMenuItemsSelection) NULL,
165 (VikLayerFuncGetMenuItemsSelection) NULL,
166
167 (VikLayerFuncAddMenuItems) mapnik_layer_add_menu_items,
168 (VikLayerFuncSublayerAddMenuItems) NULL,
169
170 (VikLayerFuncSublayerRenameRequest) NULL,
171 (VikLayerFuncSublayerToggleVisible) NULL,
172 (VikLayerFuncSublayerTooltip) NULL,
173 (VikLayerFuncLayerTooltip) mapnik_layer_tooltip,
174 (VikLayerFuncLayerSelected) NULL,
175
176 (VikLayerFuncMarshall) mapnik_layer_marshall,
177 (VikLayerFuncUnmarshall) mapnik_layer_unmarshall,
178
179 (VikLayerFuncSetParam) mapnik_layer_set_param,
180 (VikLayerFuncGetParam) mapnik_layer_get_param,
181 (VikLayerFuncChangeParam) NULL,
182
183 (VikLayerFuncReadFileData) NULL,
184 (VikLayerFuncWriteFileData) NULL,
185
186 (VikLayerFuncDeleteItem) NULL,
187 (VikLayerFuncCutItem) NULL,
188 (VikLayerFuncCopyItem) NULL,
189 (VikLayerFuncPasteItem) NULL,
190 (VikLayerFuncFreeCopiedItem) NULL,
191 (VikLayerFuncDragDropRequest) NULL,
192
193 (VikLayerFuncSelectClick) NULL,
194 (VikLayerFuncSelectMove) NULL,
195 (VikLayerFuncSelectRelease) NULL,
196 (VikLayerFuncSelectedViewportMenu) NULL,
197};
198
199struct _VikMapnikLayer {
200 VikLayer vl;
b47af40d 201 gchar *filename_css; // CartoCSS MML File - use 'carto' to convert into xml
5fa4fe86
RN
202 gchar *filename_xml;
203 guint8 alpha;
204
205 guint tile_size_x; // Y is the same as X ATM
206 gboolean loaded;
0658c2f1 207 MapnikInterface* mi;
0a25e232
RN
208 guint rerender_timeout;
209
210 gboolean use_file_cache;
211 gchar *file_cache_dir;
93eac03d
RN
212
213 VikCoord rerender_ul;
214 VikCoord rerender_br;
215 gdouble rerender_zoom;
216 GtkWidget *right_click_menu;
5fa4fe86
RN
217};
218
219#define MAPNIK_PREFS_GROUP_KEY "mapnik"
220#define MAPNIK_PREFS_NAMESPACE "mapnik."
221
222static VikLayerParamData plugins_default ( void )
223{
224 VikLayerParamData data;
225#ifdef WINDOWS
226 data.s = g_strdup ( "input" );
227#else
228 if ( g_file_test ( "/usr/lib/mapnik/input", G_FILE_TEST_EXISTS ) )
229 data.s = g_strdup ( "/usr/lib/mapnik/input" );
616812b7
RN
230 // Current Debian locations
231 else if ( g_file_test ( "/usr/lib/mapnik/3.0/input", G_FILE_TEST_EXISTS ) )
232 data.s = g_strdup ( "/usr/lib/mapnik/3.0/input" );
5fa4fe86 233 else if ( g_file_test ( "/usr/lib/mapnik/2.2/input", G_FILE_TEST_EXISTS ) )
5fa4fe86
RN
234 data.s = g_strdup ( "/usr/lib/mapnik/2.2/input" );
235 else
236 data.s = g_strdup ( "" );
237#endif
238 return data;
239}
240
241static VikLayerParamData fonts_default ( void )
242{
243 // Possibly should be string list to allow loading from multiple directories
244 VikLayerParamData data;
245#ifdef WINDOWS
246 data.s = g_strdup ( "C:\\Windows\\Fonts" );
247#elif defined __APPLE__
248 data.s = g_strdup ( "/Library/Fonts" );
249#else
250 data.s = g_strdup ( "/usr/share/fonts" );
251#endif
252 return data;
253}
254
255static VikLayerParam prefs[] = {
256 // Changing these values only applies before first mapnik layer is 'created'
257 { VIK_LAYER_NUM_TYPES, MAPNIK_PREFS_NAMESPACE"plugins_directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Plugins Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, N_("You need to restart Viking for a change to this value to be used"), plugins_default, NULL, NULL },
258 { VIK_LAYER_NUM_TYPES, MAPNIK_PREFS_NAMESPACE"fonts_directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("Fonts Directory:"), VIK_LAYER_WIDGET_FOLDERENTRY, NULL, NULL, N_("You need to restart Viking for a change to this value to be used"), fonts_default, NULL, NULL },
259 { VIK_LAYER_NUM_TYPES, MAPNIK_PREFS_NAMESPACE"recurse_fonts_directory", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Recurse Fonts Directory:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, N_("You need to restart Viking for a change to this value to be used"), vik_lpd_true_default, NULL, NULL },
0a25e232 260 { VIK_LAYER_NUM_TYPES, MAPNIK_PREFS_NAMESPACE"rerender_after", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Rerender Timeout (hours):"), VIK_LAYER_WIDGET_SPINBUTTON, &scales[2], NULL, N_("You need to restart Viking for a change to this value to be used"), NULL, NULL, NULL },
b47af40d
RN
261 // Changeable any time
262 { VIK_LAYER_NUM_TYPES, MAPNIK_PREFS_NAMESPACE"carto", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, N_("CartoCSS:"), VIK_LAYER_WIDGET_FILEENTRY, NULL, NULL, N_("The program to convert CartoCSS files into Mapnik XML"), NULL, NULL, NULL },
5fa4fe86
RN
263};
264
0a25e232
RN
265static time_t planet_import_time;
266
892a9205
RN
267static GMutex *tp_mutex;
268static GHashTable *requests = NULL;
5fa4fe86 269
892a9205
RN
270/**
271 * vik_mapnik_layer_init:
272 *
e530cc9f 273 * Just initialize preferences
892a9205 274 */
5fa4fe86
RN
275void vik_mapnik_layer_init (void)
276{
277 a_preferences_register_group ( MAPNIK_PREFS_GROUP_KEY, "Mapnik" );
278
279 guint i = 0;
280 VikLayerParamData tmp = plugins_default();
281 a_preferences_register(&prefs[i++], tmp, MAPNIK_PREFS_GROUP_KEY);
282
283 tmp = fonts_default();
284 a_preferences_register(&prefs[i++], tmp, MAPNIK_PREFS_GROUP_KEY);
285
286 tmp.b = TRUE;
287 a_preferences_register(&prefs[i++], tmp, MAPNIK_PREFS_GROUP_KEY);
b47af40d 288
0a25e232 289 tmp.u = 168; // One week
892a9205
RN
290 a_preferences_register(&prefs[i++], tmp, MAPNIK_PREFS_GROUP_KEY);
291
b47af40d
RN
292 tmp.s = "carto";
293 a_preferences_register(&prefs[i++], tmp, MAPNIK_PREFS_GROUP_KEY);
e530cc9f 294}
892a9205 295
e530cc9f
RN
296/**
297 * vik_mapnik_layer_post_init:
298 *
299 * Initialize data structures - now that reading preferences is OK to perform
300 */
301void vik_mapnik_layer_post_init (void)
302{
892a9205
RN
303 tp_mutex = vik_mutex_new();
304
305 // Just storing keys only
306 requests = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, NULL );
0a25e232
RN
307
308 guint hours = a_preferences_get (MAPNIK_PREFS_NAMESPACE"rerender_after")->u;
309 GDateTime *now = g_date_time_new_now_local ();
310 GDateTime *then = g_date_time_add_hours (now, -hours);
311 planet_import_time = g_date_time_to_unix (then);
312 g_date_time_unref ( now );
313 g_date_time_unref ( then );
314
315 GStatBuf gsb;
316 // Similar to mod_tile method to mark DB has been imported/significantly changed to cause a rerendering of all tiles
317 gchar *import_time_file = g_strconcat ( a_get_viking_dir(), G_DIR_SEPARATOR_S, "planet-import-complete", NULL );
318 if ( g_stat ( import_time_file, &gsb ) == 0 ) {
319 // Only update if newer
320 if ( planet_import_time > gsb.st_mtime )
321 planet_import_time = gsb.st_mtime;
322 }
323 g_free ( import_time_file );
892a9205
RN
324}
325
326void vik_mapnik_layer_uninit ()
327{
328 vik_mutex_free (tp_mutex);
329}
330
331// NB Only performed once per program run
332static void mapnik_layer_class_init ( VikMapnikLayerClass *klass )
333{
5b1e4483
RN
334 VikLayerParamData *pd = a_preferences_get (MAPNIK_PREFS_NAMESPACE"plugins_directory");
335 VikLayerParamData *fd = a_preferences_get (MAPNIK_PREFS_NAMESPACE"fonts_directory");
336 VikLayerParamData *rfd = a_preferences_get (MAPNIK_PREFS_NAMESPACE"recurse_fonts_directory");
337
338 if ( pd && fd && rfd )
339 mapnik_interface_initialize ( pd->s, fd->s, rfd->b );
340 else
341 g_critical ( "Unable to initialize mapnik interface from preferences" );
5fa4fe86
RN
342}
343
344GType vik_mapnik_layer_get_type ()
345{
346 static GType vml_type = 0;
347
348 if (!vml_type) {
349 static const GTypeInfo vml_info = {
350 sizeof (VikMapnikLayerClass),
351 NULL, /* base_init */
352 NULL, /* base_finalize */
353 (GClassInitFunc) mapnik_layer_class_init, /* class init */
354 NULL, /* class_finalize */
355 NULL, /* class_data */
356 sizeof (VikMapnikLayer),
357 0,
358 NULL /* instance init */
359 };
360 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapnikLayer", &vml_info, 0 );
361 }
362
363 return vml_type;
364}
365
366static const gchar* mapnik_layer_tooltip ( VikMapnikLayer *vml )
367{
368 return vml->filename_xml;
369}
370
371static void mapnik_layer_set_file_xml ( VikMapnikLayer *vml, const gchar *name )
372{
373 if ( vml->filename_xml )
374 g_free (vml->filename_xml);
e8bab170
RN
375 // Mapnik doesn't seem to cope with relative filenames
376 if ( g_strcmp0 (name, "" ) )
377 vml->filename_xml = vu_get_canonical_filename ( VIK_LAYER(vml), name);
378 else
379 vml->filename_xml = g_strdup (name);
5fa4fe86
RN
380}
381
b47af40d
RN
382static void mapnik_layer_set_file_css ( VikMapnikLayer *vml, const gchar *name )
383{
384 if ( vml->filename_css )
385 g_free (vml->filename_css);
386 vml->filename_css = g_strdup (name);
387}
388
0a25e232
RN
389static void mapnik_layer_set_cache_dir ( VikMapnikLayer *vml, const gchar *name )
390{
391 if ( vml->file_cache_dir )
392 g_free (vml->file_cache_dir);
393 vml->file_cache_dir = g_strdup (name);
394}
395
5fa4fe86
RN
396static void mapnik_layer_marshall( VikMapnikLayer *vml, guint8 **data, gint *len )
397{
398 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
399}
400
401static VikMapnikLayer *mapnik_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
402{
403 VikMapnikLayer *rv = mapnik_layer_new ( vvp );
404 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
405 return rv;
406}
407
408static gboolean mapnik_layer_set_param ( VikMapnikLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
409{
410 switch ( id ) {
b47af40d 411 case PARAM_CONFIG_CSS: mapnik_layer_set_file_css (vml, data.s); break;
5fa4fe86
RN
412 case PARAM_CONFIG_XML: mapnik_layer_set_file_xml (vml, data.s); break;
413 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
0a25e232
RN
414 case PARAM_USE_FILE_CACHE: vml->use_file_cache = data.b; break;
415 case PARAM_FILE_CACHE_DIR: mapnik_layer_set_cache_dir (vml, data.s); break;
5fa4fe86
RN
416 default: break;
417 }
418 return TRUE;
419}
420
421static VikLayerParamData mapnik_layer_get_param ( VikMapnikLayer *vml, guint16 id, gboolean is_file_operation )
422{
423 VikLayerParamData data;
424 switch ( id ) {
b47af40d
RN
425 case PARAM_CONFIG_CSS: {
426 data.s = vml->filename_css;
427 gboolean set = FALSE;
428 if ( is_file_operation ) {
429 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
430 gchar *cwd = g_get_current_dir();
431 if ( cwd ) {
432 data.s = file_GetRelativeFilename ( cwd, vml->filename_css );
433 if ( !data.s ) data.s = "";
434 set = TRUE;
435 }
436 }
437 }
438 if ( !set )
439 data.s = vml->filename_css ? vml->filename_css : "";
440 break;
441 }
5fa4fe86
RN
442 case PARAM_CONFIG_XML: {
443 data.s = vml->filename_xml;
444 gboolean set = FALSE;
445 if ( is_file_operation ) {
446 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
447 gchar *cwd = g_get_current_dir();
448 if ( cwd ) {
449 data.s = file_GetRelativeFilename ( cwd, vml->filename_xml );
450 if ( !data.s ) data.s = "";
451 set = TRUE;
452 }
453 }
454 }
455 if ( !set )
456 data.s = vml->filename_xml ? vml->filename_xml : "";
457 break;
458 }
459 case PARAM_ALPHA: data.u = vml->alpha; break;
0a25e232
RN
460 case PARAM_USE_FILE_CACHE: data.b = vml->use_file_cache; break;
461 case PARAM_FILE_CACHE_DIR: data.s = vml->file_cache_dir; break;
5fa4fe86
RN
462 default: break;
463 }
464 return data;
465}
466
467/**
468 *
469 */
470static VikMapnikLayer *mapnik_layer_new ( VikViewport *vvp )
471{
472 VikMapnikLayer *vml = VIK_MAPNIK_LAYER ( g_object_new ( VIK_MAPNIK_LAYER_TYPE, NULL ) );
473 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPNIK );
474 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
475 vml->tile_size_x = size_default().u; // FUTURE: Is there any use in this being configurable?
476 vml->loaded = FALSE;
0658c2f1 477 vml->mi = mapnik_interface_new();
5fa4fe86
RN
478 return vml;
479}
480
b47af40d
RN
481/**
482 * Run carto command
483 * ATM don't have any version issues AFAIK
484 * Tested with carto 0.14.0
485 */
486gboolean carto_load ( VikMapnikLayer *vml, VikViewport *vvp )
487{
488 gchar *mystdout = NULL;
489 gchar *mystderr = NULL;
490 GError *error = NULL;
491
492 VikLayerParamData *vlpd = a_preferences_get(MAPNIK_PREFS_NAMESPACE"carto");
493 gchar *command = g_strdup_printf ( "%s %s", vlpd->s, vml->filename_css );
494
495 gboolean answer = TRUE;
496 //gchar *args[2]; args[0] = vlpd->s; args[1] = vml->filename_css;
497 //GPid pid;
498 //if ( g_spawn_async_with_pipes ( NULL, args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, NULL, &carto_stdout, &carto_error, &error ) ) {
499 // cf code in babel.c to handle stdout
500
501 // NB Running carto may take several seconds
502 // especially for large style sheets like the default OSM Mapnik style (~6 seconds on my system)
503 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(vvp));
504 if ( vw ) {
505 gchar *msg = g_strdup_printf ( "%s: %s", _("Running"), command );
506 vik_window_statusbar_update ( vw, msg, VIK_STATUSBAR_INFO );
507 vik_window_set_busy_cursor ( vw );
ecf05926 508 g_free ( msg );
b47af40d
RN
509 }
510
511 gint64 tt1 = 0;
512 gint64 tt2 = 0;
513 // You won't get a sensible timing measurement if running too old a GLIB
514#if GLIB_CHECK_VERSION (2, 28, 0)
515 tt1 = g_get_real_time ();
516#endif
517
518 if ( g_spawn_command_line_sync ( command, &mystdout, &mystderr, NULL, &error ) ) {
519#if GLIB_CHECK_VERSION (2, 28, 0)
520 tt2 = g_get_real_time ();
521#endif
522 if ( mystderr )
523 if ( strlen(mystderr) > 1 ) {
524 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vvp), _("Error running carto command:\n%s"), mystderr );
525 answer = FALSE;
526 }
527 if ( mystdout ) {
528 // NB This will overwrite the specified XML file
529 if ( ! ( vml->filename_xml && strlen (vml->filename_xml) > 1 ) ) {
530 // XML Not specified so try to create based on CSS file name
531 GRegex *regex = g_regex_new ( "\\.mml$|\\.mss|\\.css$", G_REGEX_CASELESS, 0, &error );
532 if ( error )
533 g_critical ("%s: %s", __FUNCTION__, error->message );
534 if ( vml->filename_xml )
535 g_free (vml->filename_xml);
536 vml->filename_xml = g_regex_replace_literal ( regex, vml->filename_css, -1, 0, ".xml", 0, &error );
537 if ( error )
538 g_warning ("%s: %s", __FUNCTION__, error->message );
539 // Prevent overwriting self
540 if ( !g_strcmp0 ( vml->filename_xml, vml->filename_css ) ) {
541 vml->filename_xml = g_strconcat ( vml->filename_css, ".xml", NULL );
542 }
fbe73a7a 543 g_regex_unref ( regex );
b47af40d
RN
544 }
545 if ( !g_file_set_contents (vml->filename_xml, mystdout, -1, &error) ) {
546 g_warning ("%s: %s", __FUNCTION__, error->message );
547 g_error_free (error);
548 }
549 }
550 g_free ( mystdout );
551 g_free ( mystderr );
552 }
553 else {
554 g_warning ("%s: %s", __FUNCTION__, error->message );
555 g_error_free (error);
556 }
557 g_free ( command );
558
559 if ( vw ) {
560 gchar *msg = g_strdup_printf ( "%s %s %.1f %s", vlpd->s, _(" completed in "), (gdouble)(tt2-tt1)/G_USEC_PER_SEC, _("seconds") );
561 vik_window_statusbar_update ( vw, msg, VIK_STATUSBAR_INFO );
562 g_free ( msg );
563 vik_window_clear_busy_cursor ( vw );
564 }
565 return answer;
566}
567
5fa4fe86
RN
568/**
569 *
570 */
571static void mapnik_layer_post_read (VikLayer *vl, VikViewport *vvp, gboolean from_file)
572{
573 VikMapnikLayer *vml = VIK_MAPNIK_LAYER(vl);
574
b47af40d
RN
575 // Determine if carto needs to be run
576 gboolean do_carto = FALSE;
577 if ( vml->filename_css && strlen(vml->filename_css) > 1 ) {
578 if ( vml->filename_xml && strlen(vml->filename_xml) > 1) {
579 // Compare timestamps
580 GStatBuf gsb1;
581 if ( g_stat ( vml->filename_xml, &gsb1 ) == 0 ) {
582 GStatBuf gsb2;
583 if ( g_stat ( vml->filename_css, &gsb2 ) == 0 ) {
584 // Is CSS file newer than the XML file
585 if ( gsb2.st_mtime > gsb1.st_mtime )
586 do_carto = TRUE;
587 else
588 g_debug ( "No need to run carto" );
589 }
590 }
591 else {
592 // XML file doesn't exist
593 do_carto = TRUE;
594 }
595 }
596 else {
597 // No XML specified thus need to generate
598 do_carto = TRUE;
599 }
600 }
601
602 if ( do_carto )
603 // Don't load the XML config if carto load fails
604 if ( !carto_load ( vml, vvp ) )
605 return;
76bb97a4
RN
606
607 gchar* ans = mapnik_interface_load_map_file ( vml->mi, vml->filename_xml, vml->tile_size_x, vml->tile_size_x );
608 if ( ans ) {
609 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vvp),
610 _("Mapnik error loading configuration file:\n%s"),
611 ans );
612 g_free ( ans );
5fa4fe86
RN
613 }
614 else {
615 vml->loaded = TRUE;
616 if ( !from_file )
617 ui_add_recent_file ( vml->filename_xml );
618 }
619}
620
0a25e232
RN
621#define MAPNIK_LAYER_FILE_CACHE_LAYOUT "%s"G_DIR_SEPARATOR_S"%d"G_DIR_SEPARATOR_S"%d"G_DIR_SEPARATOR_S"%d.png"
622
623// Free returned string after use
624static gchar *get_filename ( gchar *dir, guint x, guint y, guint z)
625{
626 return g_strdup_printf ( MAPNIK_LAYER_FILE_CACHE_LAYOUT, dir, (17-z), x, y );
627}
628
629static void possibly_save_pixbuf ( VikMapnikLayer *vml, GdkPixbuf *pixbuf, MapCoord *ulm )
630{
631 if ( vml->use_file_cache ) {
632 if ( vml->file_cache_dir ) {
633 GError *error = NULL;
634 gchar *filename = get_filename ( vml->file_cache_dir, ulm->x, ulm->y, ulm->scale );
635
636 gchar *dir = g_path_get_dirname ( filename );
637 if ( !g_file_test ( filename, G_FILE_TEST_EXISTS ) )
3959d3ab 638 if ( g_mkdir_with_parents ( dir , 0777 ) != 0 )
5e4cce8f 639 g_warning ("%s: Failed to mkdir %s", __FUNCTION__, dir );
0a25e232
RN
640 g_free ( dir );
641
642 if ( !gdk_pixbuf_save (pixbuf, filename, "png", &error, NULL ) ) {
643 g_warning ("%s: %s", __FUNCTION__, error->message );
644 g_error_free (error);
645 }
646 g_free (filename);
647 }
648 }
649}
650
892a9205
RN
651typedef struct
652{
653 VikMapnikLayer *vml;
654 VikCoord *ul;
655 VikCoord *br;
656 MapCoord *ulmc;
657 const gchar* request;
658} RenderInfo;
659
660/**
661 * render:
662 *
663 * Common render function which can run in separate thread
664 */
665static void render ( VikMapnikLayer *vml, VikCoord *ul, VikCoord *br, MapCoord *ulm )
666{
667 gint64 tt1 = g_get_real_time ();
668 GdkPixbuf *pixbuf = mapnik_interface_render ( vml->mi, ul->north_south, ul->east_west, br->north_south, br->east_west );
669 gint64 tt2 = g_get_real_time ();
19c3c24c
RN
670 gdouble tt = (gdouble)(tt2-tt1)/1000000;
671 g_debug ( "Mapnik rendering completed in %.3f seconds", tt );
892a9205
RN
672 if ( !pixbuf ) {
673 // A pixbuf to stick into cache incase of an unrenderable area - otherwise will get continually re-requested
674 pixbuf = gdk_pixbuf_scale_simple ( gdk_pixbuf_from_pixdata(&vikmapniklayer_pixbuf, FALSE, NULL), vml->tile_size_x, vml->tile_size_x, GDK_INTERP_BILINEAR );
675 }
0a25e232 676 possibly_save_pixbuf ( vml, pixbuf, ulm );
892a9205
RN
677
678 // NB Mapnik can apply alpha, but use our own function for now
679 if ( vml->alpha < 255 )
53991e79 680 pixbuf = ui_pixbuf_scale_alpha ( pixbuf, vml->alpha );
19c3c24c 681 a_mapcache_add ( pixbuf, (mapcache_extra_t){ tt }, ulm->x, ulm->y, ulm->z, MAP_ID_MAPNIK_RENDER, ulm->scale, vml->alpha, 0.0, 0.0, vml->filename_xml );
38597a6d 682 g_object_unref(pixbuf);
892a9205
RN
683}
684
685static void render_info_free ( RenderInfo *data )
686{
687 g_free ( data->ul );
688 g_free ( data->br );
689 g_free ( data->ulmc );
690 // NB No need to free the request/key - as this is freed by the hash table destructor
691 g_free ( data );
692}
693
694static void background ( RenderInfo *data, gpointer threaddata )
695{
696 int res = a_background_thread_progress ( threaddata, 0 );
697 if (res == 0) {
698 render ( data->vml, data->ul, data->br, data->ulmc );
699 }
700
701 g_mutex_lock(tp_mutex);
702 g_hash_table_remove (requests, data->request);
703 g_mutex_unlock(tp_mutex);
704
705 if (res == 0)
706 vik_layer_emit_update ( VIK_LAYER(data->vml) ); // NB update display from background
707}
708
709static void render_cancel_cleanup (RenderInfo *data)
710{
711 // Anything?
712}
713
714#define REQUEST_HASHKEY_FORMAT "%d-%d-%d-%d-%d"
715
716/**
717 * Thread
718 */
f93a5a0a 719static void thread_add (VikMapnikLayer *vml, MapCoord *mul, VikCoord *ul, VikCoord *br, gint x, gint y, gint z, gint zoom, const gchar* name )
892a9205
RN
720{
721 // Create request
722 guint nn = name ? g_str_hash ( name ) : 0;
723 gchar *request = g_strdup_printf ( REQUEST_HASHKEY_FORMAT, x, y, z, zoom, nn );
724
725 g_mutex_lock(tp_mutex);
726
727 if ( g_hash_table_lookup_extended (requests, request, NULL, NULL ) ) {
728 g_free ( request );
729 g_mutex_unlock (tp_mutex);
730 return;
731 }
732
733 RenderInfo *ri = g_malloc ( sizeof(RenderInfo) );
734 ri->vml = vml;
735 ri->ul = g_malloc ( sizeof(VikCoord) );
736 ri->br = g_malloc ( sizeof(VikCoord) );
737 ri->ulmc = g_malloc ( sizeof(MapCoord) );
738 memcpy(ri->ul, ul, sizeof(VikCoord));
739 memcpy(ri->br, br, sizeof(VikCoord));
740 memcpy(ri->ulmc, mul, sizeof(MapCoord));
741 ri->request = request;
742
743 g_hash_table_insert ( requests, request, NULL );
744
745 g_mutex_unlock (tp_mutex);
746
747 gchar *basename = g_path_get_basename (name);
748 gchar *description = g_strdup_printf ( _("Mapnik Render %d:%d:%d %s"), zoom, x, y, basename );
749 g_free ( basename );
a8374fcb 750 a_background_thread ( BACKGROUND_POOL_LOCAL_MAPNIK,
892a9205
RN
751 VIK_GTK_WINDOW_FROM_LAYER(vml),
752 description,
753 (vik_thr_func) background,
754 ri,
755 (vik_thr_free_func) render_info_free,
756 (vik_thr_free_func) render_cancel_cleanup,
757 1 );
758 g_free ( description );
759}
760
0a25e232
RN
761/**
762 * load_pixbuf:
38597a6d
SB
763 *
764 * If function returns GdkPixbuf properly, reference counter to this
765 * buffer has to be decreased, when buffer is no longer needed.
0a25e232
RN
766 */
767static GdkPixbuf *load_pixbuf ( VikMapnikLayer *vml, MapCoord *ulm, MapCoord *brm, gboolean *rerender )
768{
769 *rerender = FALSE;
770 GdkPixbuf *pixbuf = NULL;
771 gchar *filename = get_filename ( vml->file_cache_dir, ulm->x, ulm->y, ulm->scale );
772
773 GStatBuf gsb;
774 if ( g_stat ( filename, &gsb ) == 0 ) {
775 // Get from disk
776 GError *error = NULL;
777 pixbuf = gdk_pixbuf_new_from_file ( filename, &error );
778 if ( error ) {
779 g_warning ("%s: %s", __FUNCTION__, error->message );
780 g_error_free ( error );
781 }
782 else {
783 if ( vml->alpha < 255 )
784 pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
19c3c24c 785 a_mapcache_add ( pixbuf, (mapcache_extra_t) { -42.0 }, ulm->x, ulm->y, ulm->z, MAP_ID_MAPNIK_RENDER, ulm->scale, vml->alpha, 0.0, 0.0, vml->filename_xml );
0a25e232
RN
786 }
787 // If file is too old mark for rerendering
788 if ( planet_import_time < gsb.st_mtime ) {
789 *rerender = TRUE;
790 }
791 }
792 g_free ( filename );
793
794 return pixbuf;
795}
796
5fa4fe86 797/**
38597a6d
SB
798 * Caller has to decrease reference counter of returned
799 * GdkPixbuf, when buffer is no longer needed.
5fa4fe86
RN
800 */
801static GdkPixbuf *get_pixbuf ( VikMapnikLayer *vml, MapCoord *ulm, MapCoord *brm )
802{
803 VikCoord ul; VikCoord br;
892a9205 804 GdkPixbuf *pixbuf = NULL;
5fa4fe86
RN
805
806 map_utils_iTMS_to_vikcoord (ulm, &ul);
807 map_utils_iTMS_to_vikcoord (brm, &br);
808
809 pixbuf = a_mapcache_get ( ulm->x, ulm->y, ulm->z, MAP_ID_MAPNIK_RENDER, ulm->scale, vml->alpha, 0.0, 0.0, vml->filename_xml );
810
811 if ( ! pixbuf ) {
0a25e232
RN
812 gboolean rerender = FALSE;
813 if ( vml->use_file_cache && vml->file_cache_dir )
814 pixbuf = load_pixbuf ( vml, ulm, brm, &rerender );
815 if ( ! pixbuf || rerender ) {
892a9205
RN
816 if ( TRUE )
817 thread_add (vml, ulm, &ul, &br, ulm->x, ulm->y, ulm->z, ulm->scale, vml->filename_xml );
818 else {
819 // Run in the foreground
820 render ( vml, &ul, &br, ulm );
821 vik_layer_emit_update ( VIK_LAYER(vml) );
822 }
5fa4fe86
RN
823 }
824 }
825
826 return pixbuf;
827}
828
829/**
830 *
831 */
832static void mapnik_layer_draw ( VikMapnikLayer *vml, VikViewport *vvp )
833{
834 if ( !vml->loaded )
835 return;
836
837 if ( vik_viewport_get_drawmode(vvp) != VIK_VIEWPORT_DRAWMODE_MERCATOR ) {
838 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vml))),
839 VIK_STATUSBAR_INFO, _("Mapnik Rendering must be in Mercator mode") );
840 return;
841 }
842
465559f1
RN
843 if ( vml->mi ) {
844 gchar *copyright = mapnik_interface_get_copyright ( vml->mi );
845 if ( copyright ) {
846 vik_viewport_add_copyright ( vvp, copyright );
847 }
848 }
849
5fa4fe86
RN
850 VikCoord ul, br;
851 ul.mode = VIK_COORD_LATLON;
852 br.mode = VIK_COORD_LATLON;
853 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
854 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
855
856 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
857 gdouble yzoom = vik_viewport_get_ympp ( vvp );
858
859 MapCoord ulm, brm;
860
861 if ( map_utils_vikcoord_to_iTMS ( &ul, xzoom, yzoom, &ulm ) &&
862 map_utils_vikcoord_to_iTMS ( &br, xzoom, yzoom, &brm ) ) {
863 // TODO: Understand if tilesize != 256 does this need to use shrinkfactors??
864 GdkPixbuf *pixbuf;
865 VikCoord coord;
866 gint xx, yy;
867
868 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
869 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
870
871 // Split rendering into a grid for the current viewport
872 // thus each individual 'tile' can then be stored in the map cache
5fa4fe86
RN
873 for (gint x = xmin; x <= xmax; x++ ) {
874 for (gint y = ymin; y <= ymax; y++ ) {
875 ulm.x = x;
876 ulm.y = y;
877 brm.x = x+1;
878 brm.y = y+1;
879
880 pixbuf = get_pixbuf ( vml, &ulm, &brm );
881
882 if ( pixbuf ) {
883 map_utils_iTMS_to_vikcoord ( &ulm, &coord );
884 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
885 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, vml->tile_size_x, vml->tile_size_x );
38597a6d 886 g_object_unref(pixbuf);
5fa4fe86
RN
887 }
888 }
889 }
890
891 // Done after so drawn on top
892 // Just a handy guide to tile blocks.
893 if ( vik_debug && vik_verbose ) {
894 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
895 gint width = vik_viewport_get_width(vvp);
896 gint height = vik_viewport_get_height(vvp);
897 gint xx, yy;
898 ulm.x = xmin; ulm.y = ymin;
899 map_utils_iTMS_to_center_vikcoord ( &ulm, &coord );
900 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
901 xx = xx - (vml->tile_size_x/2);
902 yy = yy - (vml->tile_size_x/2); // Yes use X ATM
903 for (gint x = xmin; x <= xmax; x++ ) {
904 vik_viewport_draw_line ( vvp, black_gc, xx, 0, xx, height );
905 xx += vml->tile_size_x;
906 }
907 for (gint y = ymin; y <= ymax; y++ ) {
908 vik_viewport_draw_line ( vvp, black_gc, 0, yy, width, yy );
909 yy += vml->tile_size_x; // Yes use X ATM
910 }
911 }
912 }
913}
914
915/**
916 *
917 */
918static void mapnik_layer_free ( VikMapnikLayer *vml )
919{
0658c2f1 920 mapnik_interface_free ( vml->mi );
b47af40d
RN
921 if ( vml->filename_css )
922 g_free ( vml->filename_css );
5fa4fe86
RN
923 if ( vml->filename_xml )
924 g_free ( vml->filename_xml );
925}
926
927static VikMapnikLayer *mapnik_layer_create ( VikViewport *vp )
928{
929 return mapnik_layer_new ( vp );
930}
931
932typedef enum {
933 MA_VML = 0,
934 MA_VVP,
935 MA_LAST
936} menu_array_index;
937
938typedef gpointer menu_array_values[MA_LAST];
939
940/**
941 *
942 */
943static void mapnik_layer_flush_memory ( menu_array_values values )
944{
945 a_mapcache_flush_type ( MAP_ID_MAPNIK_RENDER );
946}
947
c2cf0333
RN
948/**
949 *
950 */
951static void mapnik_layer_reload ( menu_array_values values )
952{
953 VikMapnikLayer *vml = values[MA_VML];
954 VikViewport *vvp = values[MA_VVP];
955 mapnik_layer_post_read (VIK_LAYER(vml), vvp, FALSE);
956 mapnik_layer_draw ( vml, vvp );
957}
958
dd5122df
RN
959/**
960 * Force carto run
961 *
962 * Most carto projects will consist of many files
963 * ATM don't have a way of detecting when any of the included files have changed
964 * Thus allow a manual method to force re-running carto
965 */
966static void mapnik_layer_carto ( menu_array_values values )
967{
968 VikMapnikLayer *vml = values[MA_VML];
969 VikViewport *vvp = values[MA_VVP];
970
971 // Don't load the XML config if carto load fails
972 if ( !carto_load ( vml, vvp ) )
973 return;
974
975 gchar* ans = mapnik_interface_load_map_file ( vml->mi, vml->filename_xml, vml->tile_size_x, vml->tile_size_x );
976 if ( ans ) {
977 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vvp),
978 _("Mapnik error loading configuration file:\n%s"),
979 ans );
980 g_free ( ans );
981 }
982 else
983 mapnik_layer_draw ( vml, vvp );
984}
985
581feeec
RN
986/**
987 * Show Mapnik configuration parameters
988 */
989static void mapnik_layer_information ( menu_array_values values )
990{
991 VikMapnikLayer *vml = values[MA_VML];
992 if ( !vml->mi )
993 return;
994 GArray *array = mapnik_interface_get_parameters( vml->mi );
995 if ( array->len ) {
996 a_dialog_list ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Mapnik Information"), array, 1 );
997 // Free the copied strings
998 for ( int i = 0; i < array->len; i++ )
999 g_free ( g_array_index(array,gchar*,i) );
1000 }
1001 g_array_free ( array, FALSE );
1002}
1003
c064c64c
RN
1004/**
1005 *
1006 */
1007static void mapnik_layer_about ( menu_array_values values )
1008{
1009 VikMapnikLayer *vml = values[MA_VML];
1010 gchar *msg = mapnik_interface_about();
1011 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), msg );
1012 g_free ( msg );
1013}
1014
5fa4fe86
RN
1015/**
1016 *
1017 */
1018static void mapnik_layer_add_menu_items ( VikMapnikLayer *vml, GtkMenu *menu, gpointer vlp )
1019{
1020 static menu_array_values values;
1021 values[MA_VML] = vml;
1022 values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
1023
1024 GtkWidget *item = gtk_menu_item_new();
1025 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1026 gtk_widget_show ( item );
1027
1028 // Typical users shouldn't need to use this functionality - so debug only ATM
1029 if ( vik_debug ) {
1030 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flush Memory Cache") );
1031 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
1032 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_flush_memory), values );
1033 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1034 gtk_widget_show ( item );
1035 }
c064c64c 1036
c2cf0333
RN
1037 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_REFRESH, NULL );
1038 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_reload), values );
1039 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1040 gtk_widget_show ( item );
1041
dd5122df
RN
1042 if ( g_strcmp0 ("", vml->filename_css) ) {
1043 item = gtk_image_menu_item_new_with_mnemonic ( _("_Run Carto Command") );
1044 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU) );
1045 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_carto), values );
1046 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1047 gtk_widget_show ( item );
1048 }
1049
581feeec
RN
1050 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_INFO, NULL );
1051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_information), values );
1052 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1053 gtk_widget_show ( item );
1054
c064c64c
RN
1055 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
1056 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_about), values );
1057 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1058 gtk_widget_show ( item );
5fa4fe86 1059}
93eac03d
RN
1060
1061/**
1062 * Rerender a specific tile
1063 */
1064static void mapnik_layer_rerender ( VikMapnikLayer *vml )
1065{
1066 MapCoord ulm;
1067 // Requested position to map coord
1068 map_utils_vikcoord_to_iTMS ( &vml->rerender_ul, vml->rerender_zoom, vml->rerender_zoom, &ulm );
1069 // Reconvert back - thus getting the coordinate at the tile *ul corner*
1070 map_utils_iTMS_to_vikcoord (&ulm, &vml->rerender_ul );
1071 // Bottom right bound is simply +1 in TMS coords
1072 MapCoord brm = ulm;
1073 brm.x = brm.x+1;
1074 brm.y = brm.y+1;
1075 map_utils_iTMS_to_vikcoord (&brm, &vml->rerender_br );
1076 thread_add (vml, &ulm, &vml->rerender_ul, &vml->rerender_br, ulm.x, ulm.y, ulm.z, ulm.scale, vml->filename_xml );
1077}
1078
2d621d52
RN
1079/**
1080 * Info
1081 */
1082static void mapnik_layer_tile_info ( VikMapnikLayer *vml )
1083{
1084 MapCoord ulm;
1085 // Requested position to map coord
1086 map_utils_vikcoord_to_iTMS ( &vml->rerender_ul, vml->rerender_zoom, vml->rerender_zoom, &ulm );
1087
1088 mapcache_extra_t extra = a_mapcache_get_extra ( ulm.x, ulm.y, ulm.z, MAP_ID_MAPNIK_RENDER, ulm.scale, vml->alpha, 0.0, 0.0, vml->filename_xml );
1089
1090 gchar *filename = get_filename ( vml->file_cache_dir, ulm.x, ulm.y, ulm.scale );
1091 gchar *filemsg = NULL;
1092 gchar *timemsg = NULL;
1093
1094 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1095 filemsg = g_strconcat ( "Tile File: ", filename, NULL );
1096 // Get some timestamp information of the tile
2a708810 1097 GStatBuf stat_buf;
2d621d52
RN
1098 if ( g_stat ( filename, &stat_buf ) == 0 ) {
1099 gchar time_buf[64];
1100 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1101 timemsg = g_strdup_printf ( _("Tile File Timestamp: %s"), time_buf );
1102 }
1103 else {
1104 timemsg = g_strdup ( _("Tile File Timestamp: Not Available") );
1105 }
1106 }
1107 else {
1108 filemsg = g_strdup_printf ( "Tile File: %s [Not Available]", filename );
1109 timemsg = g_strdup("");
1110 }
1111
1112 GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
1113 g_array_append_val ( array, filemsg );
1114 g_array_append_val ( array, timemsg );
1115
1116 gchar *rendmsg = NULL;
1117 // Show the info
1118 if ( extra.duration > 0.0 ) {
1119 rendmsg = g_strdup_printf ( _("Rendering time %.2f seconds"), extra.duration );
1120 g_array_append_val ( array, rendmsg );
1121 }
1122
1123 a_dialog_list ( VIK_GTK_WINDOW_FROM_LAYER(vml), _("Tile Information"), array, 5 );
1124 g_array_free ( array, FALSE );
1125
1126 g_free ( rendmsg );
1127 g_free ( timemsg );
1128 g_free ( filemsg );
1129 g_free ( filename );
1130}
1131
93eac03d
RN
1132static gboolean mapnik_feature_release ( VikMapnikLayer *vml, GdkEventButton *event, VikViewport *vvp )
1133{
1134 if ( !vml )
1135 return FALSE;
1136 if ( event->button == 3 ) {
1137 vik_viewport_screen_to_coord ( vvp, MAX(0, event->x), MAX(0, event->y), &vml->rerender_ul );
1138 vml->rerender_zoom = vik_viewport_get_zoom ( vvp );
1139
1140 if ( ! vml->right_click_menu ) {
1141 GtkWidget *item;
1142 vml->right_click_menu = gtk_menu_new ();
1143
1144 item = gtk_image_menu_item_new_with_mnemonic ( _("_Rerender Tile") );
1145 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
1146 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_rerender), vml );
1147 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->right_click_menu), item );
2d621d52
RN
1148
1149 item = gtk_image_menu_item_new_with_mnemonic ( _("_Info") );
1150 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1151 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_tile_info), vml );
1152 gtk_menu_shell_append ( GTK_MENU_SHELL(vml->right_click_menu), item );
93eac03d
RN
1153 }
1154
1155 gtk_menu_popup ( GTK_MENU(vml->right_click_menu), NULL, NULL, NULL, NULL, event->button, event->time );
1156 gtk_widget_show_all ( GTK_WIDGET(vml->right_click_menu) );
1157 }
1158
1159 return FALSE;
1160}