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