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