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