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