]> git.street.me.uk Git - andy/viking.git/blob - src/vikmapniklayer.c
Pass Mapnik loading errors around and display in the GUI.
[andy/viking.git] / src / vikmapniklayer.c
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"
40 #include "util.h"
41 #include "ui_util.h"
42 #include "preferences.h"
43 #include "icons/icons.h"
44 #include "mapnik_interface.h"
45
46 struct _VikMapnikLayerClass
47 {
48         VikLayerClass object_class;
49 };
50
51 static VikLayerParamData file_default ( void )
52 {
53         VikLayerParamData data;
54         data.s = "";
55         return data;
56 }
57
58 static VikLayerParamData size_default ( void ) { return VIK_LPD_UINT ( 256 ); }
59 static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
60
61 static VikLayerParamScale scales[] = {
62         { 0, 255, 5, 0 }, // Alpha
63         { 64, 1024, 8, 0 }, // Tile size
64 };
65
66 VikLayerParam mapnik_layer_params[] = {
67   { 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,
68     N_("CartoCSS configuration file"), file_default, NULL, NULL },
69   { 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,
70     N_("Mapnik XML configuration file"), file_default, NULL, NULL },
71   { VIK_LAYER_MAPNIK, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, &scales[0], NULL,
72     NULL, alpha_default, NULL, NULL },
73 };
74
75 enum {
76   PARAM_CONFIG_CSS = 0,
77   PARAM_CONFIG_XML,
78   PARAM_ALPHA,
79   NUM_PARAMS };
80
81 static const gchar* mapnik_layer_tooltip ( VikMapnikLayer *vml );
82 static void mapnik_layer_marshall( VikMapnikLayer *vml, guint8 **data, gint *len );
83 static VikMapnikLayer *mapnik_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
84 static gboolean mapnik_layer_set_param ( VikMapnikLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
85 static VikLayerParamData mapnik_layer_get_param ( VikMapnikLayer *vml, guint16 id, gboolean is_file_operation );
86 static VikMapnikLayer *mapnik_layer_new ( VikViewport *vvp );
87 static VikMapnikLayer *mapnik_layer_create ( VikViewport *vp );
88 static void mapnik_layer_free ( VikMapnikLayer *vml );
89 static void mapnik_layer_draw ( VikMapnikLayer *vml, VikViewport *vp );
90 static void mapnik_layer_add_menu_items ( VikMapnikLayer *vml, GtkMenu *menu, gpointer vlp );
91
92 // See comment in viktrwlayer.c for advice on values used
93 // FUTURE:
94 static VikToolInterface mapnik_tools[] = {
95         // Layer Info
96         // Zoom All?
97 };
98
99 static void mapnik_layer_post_read (VikLayer *vl, VikViewport *vvp, gboolean from_file);
100
101 VikLayerInterface vik_mapnik_layer_interface = {
102         "Mapnik Rendering",
103         N_("Mapnik Rendering"),
104         NULL,
105         &vikmapniklayer_pixbuf, // icon
106
107         mapnik_tools,
108         sizeof(mapnik_tools) / sizeof(VikToolInterface),
109
110         mapnik_layer_params,
111         NUM_PARAMS,
112         NULL,
113         0,
114
115         VIK_MENU_ITEM_ALL,
116
117         (VikLayerFuncCreate)                  mapnik_layer_create,
118         (VikLayerFuncRealize)                 NULL,
119         (VikLayerFuncPostRead)                mapnik_layer_post_read,
120         (VikLayerFuncFree)                    mapnik_layer_free,
121
122         (VikLayerFuncProperties)              NULL,
123         (VikLayerFuncDraw)                    mapnik_layer_draw,
124         (VikLayerFuncChangeCoordMode)         NULL,
125
126         (VikLayerFuncSetMenuItemsSelection)   NULL,
127         (VikLayerFuncGetMenuItemsSelection)   NULL,
128
129         (VikLayerFuncAddMenuItems)            mapnik_layer_add_menu_items,
130         (VikLayerFuncSublayerAddMenuItems)    NULL,
131
132         (VikLayerFuncSublayerRenameRequest)   NULL,
133         (VikLayerFuncSublayerToggleVisible)   NULL,
134         (VikLayerFuncSublayerTooltip)         NULL,
135         (VikLayerFuncLayerTooltip)            mapnik_layer_tooltip,
136         (VikLayerFuncLayerSelected)           NULL,
137
138         (VikLayerFuncMarshall)                mapnik_layer_marshall,
139         (VikLayerFuncUnmarshall)              mapnik_layer_unmarshall,
140
141         (VikLayerFuncSetParam)                mapnik_layer_set_param,
142         (VikLayerFuncGetParam)                mapnik_layer_get_param,
143         (VikLayerFuncChangeParam)             NULL,
144
145         (VikLayerFuncReadFileData)            NULL,
146         (VikLayerFuncWriteFileData)           NULL,
147
148         (VikLayerFuncDeleteItem)              NULL,
149         (VikLayerFuncCutItem)                 NULL,
150         (VikLayerFuncCopyItem)                NULL,
151         (VikLayerFuncPasteItem)               NULL,
152         (VikLayerFuncFreeCopiedItem)          NULL,
153         (VikLayerFuncDragDropRequest)         NULL,
154
155         (VikLayerFuncSelectClick)             NULL,
156         (VikLayerFuncSelectMove)              NULL,
157         (VikLayerFuncSelectRelease)           NULL,
158         (VikLayerFuncSelectedViewportMenu)    NULL,
159 };
160
161 struct _VikMapnikLayer {
162         VikLayer vl;
163         gchar *filename_css; // CartoCSS MML File - use 'carto' to convert into xml
164         gchar *filename_xml;
165         guint8 alpha;
166
167         guint tile_size_x; // Y is the same as X ATM
168         gboolean loaded;
169         MapnikInterface* mi;
170 };
171
172 #define MAPNIK_PREFS_GROUP_KEY "mapnik"
173 #define MAPNIK_PREFS_NAMESPACE "mapnik."
174
175 static VikLayerParamData plugins_default ( void )
176 {
177         VikLayerParamData data;
178 #ifdef WINDOWS
179         data.s = g_strdup ( "input" );
180 #else
181         if ( g_file_test ( "/usr/lib/mapnik/input", G_FILE_TEST_EXISTS ) )
182                 data.s = g_strdup ( "/usr/lib/mapnik/input" );
183         else if ( g_file_test ( "/usr/lib/mapnik/2.2/input", G_FILE_TEST_EXISTS ) )
184                 // Current Debian location
185                 data.s = g_strdup ( "/usr/lib/mapnik/2.2/input" );
186         else
187                 data.s = g_strdup ( "" );
188 #endif
189         return data;
190 }
191
192 static VikLayerParamData fonts_default ( void )
193 {
194         // Possibly should be string list to allow loading from multiple directories
195         VikLayerParamData data;
196 #ifdef WINDOWS
197         data.s = g_strdup ( "C:\\Windows\\Fonts" );
198 #elif defined __APPLE__
199         data.s = g_strdup ( "/Library/Fonts" );
200 #else
201         data.s = g_strdup ( "/usr/share/fonts" );
202 #endif
203         return data;
204 }
205
206 static VikLayerParam prefs[] = {
207         // Changing these values only applies before first mapnik layer is 'created'
208         { 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 },
209         { 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 },
210         { 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 },
211         // Changeable any time
212         { 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 },
213 };
214
215 // NB Only performed once per program run
216 static void mapnik_layer_class_init ( VikMapnikLayerClass *klass )
217 {
218         mapnik_interface_initialize ( a_preferences_get (MAPNIK_PREFS_NAMESPACE"plugins_directory")->s,
219                                       a_preferences_get (MAPNIK_PREFS_NAMESPACE"fonts_directory")->s,
220                                       a_preferences_get (MAPNIK_PREFS_NAMESPACE"recurse_fonts_directory")->b );
221 }
222
223 void vik_mapnik_layer_init (void)
224 {
225         a_preferences_register_group ( MAPNIK_PREFS_GROUP_KEY, "Mapnik" );
226
227         guint i = 0;
228         VikLayerParamData tmp = plugins_default();
229         a_preferences_register(&prefs[i++], tmp, MAPNIK_PREFS_GROUP_KEY);
230
231         tmp = fonts_default();
232         a_preferences_register(&prefs[i++], tmp, MAPNIK_PREFS_GROUP_KEY);
233
234         tmp.b = TRUE;
235         a_preferences_register(&prefs[i++], tmp, MAPNIK_PREFS_GROUP_KEY);
236
237         tmp.s = "carto";
238         a_preferences_register(&prefs[i++], tmp, MAPNIK_PREFS_GROUP_KEY);
239 }
240
241 GType vik_mapnik_layer_get_type ()
242 {
243         static GType vml_type = 0;
244
245         if (!vml_type) {
246                 static const GTypeInfo vml_info = {
247                         sizeof (VikMapnikLayerClass),
248                         NULL, /* base_init */
249                         NULL, /* base_finalize */
250                         (GClassInitFunc) mapnik_layer_class_init, /* class init */
251                         NULL, /* class_finalize */
252                         NULL, /* class_data */
253                         sizeof (VikMapnikLayer),
254                         0,
255                         NULL /* instance init */
256                 };
257                 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapnikLayer", &vml_info, 0 );
258         }
259
260         return vml_type;
261 }
262
263 static const gchar* mapnik_layer_tooltip ( VikMapnikLayer *vml )
264 {
265         return vml->filename_xml;
266 }
267
268 static void mapnik_layer_set_file_xml ( VikMapnikLayer *vml, const gchar *name )
269 {
270         if ( vml->filename_xml )
271                 g_free (vml->filename_xml);
272         vml->filename_xml = g_strdup (name);
273 }
274
275 static void mapnik_layer_set_file_css ( VikMapnikLayer *vml, const gchar *name )
276 {
277         if ( vml->filename_css )
278                 g_free (vml->filename_css);
279         vml->filename_css = g_strdup (name);
280 }
281
282 static void mapnik_layer_marshall( VikMapnikLayer *vml, guint8 **data, gint *len )
283 {
284         vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
285 }
286
287 static VikMapnikLayer *mapnik_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
288 {
289         VikMapnikLayer *rv = mapnik_layer_new ( vvp );
290         vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
291         return rv;
292 }
293
294 static gboolean mapnik_layer_set_param ( VikMapnikLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
295 {
296         switch ( id ) {
297                 case PARAM_CONFIG_CSS: mapnik_layer_set_file_css (vml, data.s); break;
298                 case PARAM_CONFIG_XML: mapnik_layer_set_file_xml (vml, data.s); break;
299                 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
300                 default: break;
301         }
302         return TRUE;
303 }
304
305 static VikLayerParamData mapnik_layer_get_param ( VikMapnikLayer *vml, guint16 id, gboolean is_file_operation )
306 {
307         VikLayerParamData data;
308         switch ( id ) {
309                 case PARAM_CONFIG_CSS: {
310                         data.s = vml->filename_css;
311                         gboolean set = FALSE;
312                         if ( is_file_operation ) {
313                                 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
314                                         gchar *cwd = g_get_current_dir();
315                                         if ( cwd ) {
316                                                 data.s = file_GetRelativeFilename ( cwd, vml->filename_css );
317                                                 if ( !data.s ) data.s = "";
318                                                 set = TRUE;
319                                         }
320                                 }
321                         }
322                         if ( !set )
323                                 data.s = vml->filename_css ? vml->filename_css : "";
324                         break;
325                 }
326                 case PARAM_CONFIG_XML: {
327                         data.s = vml->filename_xml;
328                         gboolean set = FALSE;
329                         if ( is_file_operation ) {
330                                 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
331                                         gchar *cwd = g_get_current_dir();
332                                         if ( cwd ) {
333                                                 data.s = file_GetRelativeFilename ( cwd, vml->filename_xml );
334                                                 if ( !data.s ) data.s = "";
335                                                 set = TRUE;
336                                         }
337                                 }
338                         }
339                         if ( !set )
340                                 data.s = vml->filename_xml ? vml->filename_xml : "";
341                         break;
342                 }
343                 case PARAM_ALPHA: data.u = vml->alpha; break;
344                 //case PARAM_TILE_X: data.u = vml->tile_size_x; break;
345                 default: break;
346         }
347         return data;
348 }
349
350 /**
351  *
352  */
353 static VikMapnikLayer *mapnik_layer_new ( VikViewport *vvp )
354 {
355         VikMapnikLayer *vml = VIK_MAPNIK_LAYER ( g_object_new ( VIK_MAPNIK_LAYER_TYPE, NULL ) );
356         vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPNIK );
357         vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
358         vml->tile_size_x = size_default().u; // FUTURE: Is there any use in this being configurable?
359         vml->loaded = FALSE;
360         vml->mi = mapnik_interface_new();
361         return vml;
362 }
363
364 /**
365  * Run carto command
366  * ATM don't have any version issues AFAIK
367  * Tested with carto 0.14.0
368  */
369 gboolean carto_load ( VikMapnikLayer *vml, VikViewport *vvp )
370 {
371         gchar *mystdout = NULL;
372         gchar *mystderr = NULL;
373         GError *error = NULL;
374
375         VikLayerParamData *vlpd = a_preferences_get(MAPNIK_PREFS_NAMESPACE"carto");
376         gchar *command = g_strdup_printf ( "%s %s", vlpd->s, vml->filename_css );
377
378         gboolean answer = TRUE;
379         //gchar *args[2]; args[0] = vlpd->s; args[1] = vml->filename_css;
380         //GPid pid;
381         //if ( g_spawn_async_with_pipes ( NULL, args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, NULL, &carto_stdout, &carto_error, &error ) ) {
382         // cf code in babel.c to handle stdout
383
384         // NB Running carto may take several seconds
385         //  especially for large style sheets like the default OSM Mapnik style (~6 seconds on my system)
386         VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_WIDGET(vvp));
387         if ( vw ) {
388                 gchar *msg = g_strdup_printf ( "%s: %s", _("Running"), command );
389                 vik_window_statusbar_update ( vw, msg, VIK_STATUSBAR_INFO );
390                 vik_window_set_busy_cursor ( vw );
391         }
392
393         gint64 tt1 = 0;
394         gint64 tt2 = 0;
395         // You won't get a sensible timing measurement if running too old a GLIB
396 #if GLIB_CHECK_VERSION (2, 28, 0)
397         tt1 = g_get_real_time ();
398 #endif
399
400         if ( g_spawn_command_line_sync ( command, &mystdout, &mystderr, NULL, &error ) ) {
401 #if GLIB_CHECK_VERSION (2, 28, 0)
402                 tt2 = g_get_real_time ();
403 #endif
404                 if ( mystderr )
405                         if ( strlen(mystderr) > 1 ) {
406                                 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vvp), _("Error running carto command:\n%s"), mystderr );
407                                 answer = FALSE;
408                         }
409                 if ( mystdout ) {
410                         // NB This will overwrite the specified XML file
411                         if ( ! ( vml->filename_xml && strlen (vml->filename_xml) > 1 ) ) {
412                                 // XML Not specified so try to create based on CSS file name
413                                 GRegex *regex = g_regex_new ( "\\.mml$|\\.mss|\\.css$", G_REGEX_CASELESS, 0, &error );
414                                 if ( error )
415                                         g_critical ("%s: %s", __FUNCTION__, error->message );
416                                 if ( vml->filename_xml )
417                                         g_free (vml->filename_xml);
418                                 vml->filename_xml = g_regex_replace_literal ( regex, vml->filename_css, -1, 0, ".xml", 0, &error );
419                                 if ( error )
420                                         g_warning ("%s: %s", __FUNCTION__, error->message );
421                                 // Prevent overwriting self
422                                 if ( !g_strcmp0 ( vml->filename_xml, vml->filename_css ) ) {
423                                         vml->filename_xml = g_strconcat ( vml->filename_css, ".xml", NULL );
424                                 }
425                         }
426                         if ( !g_file_set_contents (vml->filename_xml, mystdout, -1, &error)  ) {
427                                 g_warning ("%s: %s", __FUNCTION__, error->message );
428                                 g_error_free (error);
429                         }
430                 }
431                 g_free ( mystdout );
432                 g_free ( mystderr );
433         }
434         else {
435                 g_warning ("%s: %s", __FUNCTION__, error->message );
436                 g_error_free (error);
437         }
438         g_free ( command );
439
440         if ( vw ) {
441                 gchar *msg = g_strdup_printf ( "%s %s %.1f %s",  vlpd->s, _(" completed in "), (gdouble)(tt2-tt1)/G_USEC_PER_SEC, _("seconds") );
442                 vik_window_statusbar_update ( vw, msg, VIK_STATUSBAR_INFO );
443                 g_free ( msg );
444                 vik_window_clear_busy_cursor ( vw );
445         }
446         return answer;
447 }
448
449 #if !GLIB_CHECK_VERSION(2,26,0)
450 typedef struct stat GStatBuf;
451 #endif
452
453 /**
454  *
455  */
456 static void mapnik_layer_post_read (VikLayer *vl, VikViewport *vvp, gboolean from_file)
457 {
458         VikMapnikLayer *vml = VIK_MAPNIK_LAYER(vl);
459
460         // Determine if carto needs to be run
461         gboolean do_carto = FALSE;
462         if ( vml->filename_css && strlen(vml->filename_css) > 1 ) {
463                 if ( vml->filename_xml && strlen(vml->filename_xml) > 1) {
464                         // Compare timestamps
465                         GStatBuf gsb1;
466                         if ( g_stat ( vml->filename_xml, &gsb1 ) == 0 ) {
467                                 GStatBuf gsb2;
468                                 if ( g_stat ( vml->filename_css, &gsb2 ) == 0 ) {
469                                         // Is CSS file newer than the XML file
470                                         if ( gsb2.st_mtime > gsb1.st_mtime )
471                                                 do_carto = TRUE;
472                                         else
473                                                 g_debug ( "No need to run carto" );
474                                 }
475                         }
476                         else {
477                                 // XML file doesn't exist
478                                 do_carto = TRUE;
479                         }
480                 }
481                 else {
482                         // No XML specified thus need to generate
483                         do_carto = TRUE;
484                 }
485         }
486
487         if ( do_carto )
488                 // Don't load the XML config if carto load fails
489                 if ( !carto_load ( vml, vvp ) )
490                         return;
491
492         gchar* ans = mapnik_interface_load_map_file ( vml->mi, vml->filename_xml, vml->tile_size_x, vml->tile_size_x );
493         if ( ans ) {
494                 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vvp),
495                                            _("Mapnik error loading configuration file:\n%s"),
496                                            ans );
497                 g_free ( ans );
498         }
499         else {
500                 vml->loaded = TRUE;
501                 if ( !from_file )
502                         ui_add_recent_file ( vml->filename_xml );
503         }
504 }
505
506 /**
507  *
508  */
509 static GdkPixbuf *get_pixbuf ( VikMapnikLayer *vml, MapCoord *ulm, MapCoord *brm )
510 {
511         VikCoord ul; VikCoord br;
512         GdkPixbuf *pixbuf;
513
514         map_utils_iTMS_to_vikcoord (ulm, &ul);
515         map_utils_iTMS_to_vikcoord (brm, &br);
516
517         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 );
518
519         if ( ! pixbuf ) {
520                 pixbuf = mapnik_interface_render ( vml->mi, ul.north_south, ul.east_west, br.north_south, br.east_west );
521                 if ( pixbuf ) {
522                         // NB Mapnik can apply alpha, but use our own function for now
523                         if ( vml->alpha < 255 )
524                                 pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
525                         a_mapcache_add ( pixbuf, ulm->x, ulm->y, ulm->z, MAPNIK_LAYER_MAP_TYPE, ulm->scale, vml->alpha, 0.0, 0.0, vml->filename_xml );
526                 }
527         }
528
529         return pixbuf;
530 }
531
532 /**
533  *
534  */
535 static void mapnik_layer_draw ( VikMapnikLayer *vml, VikViewport *vvp )
536 {
537         if ( !vml->loaded )
538                 return;
539
540         if ( vik_viewport_get_drawmode(vvp) != VIK_VIEWPORT_DRAWMODE_MERCATOR ) {
541                 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vml))),
542                                             VIK_STATUSBAR_INFO, _("Mapnik Rendering must be in Mercator mode") );
543                 return;
544         }
545
546         VikCoord ul, br;
547         ul.mode = VIK_COORD_LATLON;
548         br.mode = VIK_COORD_LATLON;
549         vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
550         vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
551
552         gdouble xzoom = vik_viewport_get_xmpp ( vvp );
553         gdouble yzoom = vik_viewport_get_ympp ( vvp );
554
555         MapCoord ulm, brm;
556
557         if ( map_utils_vikcoord_to_iTMS ( &ul, xzoom, yzoom, &ulm ) &&
558              map_utils_vikcoord_to_iTMS ( &br, xzoom, yzoom, &brm ) ) {
559                 // TODO: Understand if tilesize != 256 does this need to use shrinkfactors??
560                 GdkPixbuf *pixbuf;
561                 VikCoord coord;
562                 gint xx, yy;
563
564                 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
565                 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
566
567                 // Split rendering into a grid for the current viewport
568                 //  thus each individual 'tile' can then be stored in the map cache
569                 // TODO: Also potentially allows using multi threads for each individual tile
570                 //   this is more important for complicated stylesheets/datasources as each rendering may take some time
571                 for (gint x = xmin; x <= xmax; x++ ) {
572                         for (gint y = ymin; y <= ymax; y++ ) {
573                                 ulm.x = x;
574                                 ulm.y = y;
575                                 brm.x = x+1;
576                                 brm.y = y+1;
577
578                                 pixbuf = get_pixbuf ( vml, &ulm, &brm );
579
580                                 if ( pixbuf ) {
581                                         map_utils_iTMS_to_vikcoord ( &ulm, &coord );
582                                         vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
583                                         vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, vml->tile_size_x, vml->tile_size_x );
584                                 }
585                         }
586                 }
587
588                 // Done after so drawn on top
589                 // Just a handy guide to tile blocks.
590                 if ( vik_debug && vik_verbose ) {
591                         GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
592                         gint width = vik_viewport_get_width(vvp);
593                         gint height = vik_viewport_get_height(vvp);
594                         gint xx, yy;
595                         ulm.x = xmin; ulm.y = ymin;
596                         map_utils_iTMS_to_center_vikcoord ( &ulm, &coord );
597                         vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
598                         xx = xx - (vml->tile_size_x/2);
599                         yy = yy - (vml->tile_size_x/2); // Yes use X ATM
600                         for (gint x = xmin; x <= xmax; x++ ) {
601                                 vik_viewport_draw_line ( vvp, black_gc, xx, 0, xx, height );
602                                 xx += vml->tile_size_x;
603                         }
604                         for (gint y = ymin; y <= ymax; y++ ) {
605                                 vik_viewport_draw_line ( vvp, black_gc, 0, yy, width, yy );
606                                 yy += vml->tile_size_x; // Yes use X ATM
607                         }
608                 }
609         }
610 }
611
612 /**
613  *
614  */
615 static void mapnik_layer_free ( VikMapnikLayer *vml )
616 {
617         mapnik_interface_free ( vml->mi );
618         if ( vml->filename_css )
619                 g_free ( vml->filename_css );
620         if ( vml->filename_xml )
621                 g_free ( vml->filename_xml );
622 }
623
624 static VikMapnikLayer *mapnik_layer_create ( VikViewport *vp )
625 {
626         return mapnik_layer_new ( vp );
627 }
628
629 typedef enum {
630         MA_VML = 0,
631         MA_VVP,
632         MA_LAST
633 } menu_array_index;
634
635 typedef gpointer menu_array_values[MA_LAST];
636
637 /**
638  *
639  */
640 static void mapnik_layer_flush_memory ( menu_array_values values )
641 {
642         a_mapcache_flush_type ( MAP_ID_MAPNIK_RENDER );
643 }
644
645 /**
646  *
647  */
648 static void mapnik_layer_about ( menu_array_values values )
649 {
650         VikMapnikLayer *vml = values[MA_VML];
651         gchar *msg = mapnik_interface_about();
652         a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml),  msg );
653         g_free ( msg );
654 }
655
656 /**
657  *
658  */
659 static void mapnik_layer_add_menu_items ( VikMapnikLayer *vml, GtkMenu *menu, gpointer vlp )
660 {
661         static menu_array_values values;
662         values[MA_VML] = vml;
663         values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
664
665         GtkWidget *item = gtk_menu_item_new();
666         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
667         gtk_widget_show ( item );
668
669         // Typical users shouldn't need to use this functionality - so debug only ATM
670         if ( vik_debug ) {
671                 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flush Memory Cache") );
672                 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
673                 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_flush_memory), values );
674                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
675                 gtk_widget_show ( item );
676         }
677
678         item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
679         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_about), values );
680         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
681         gtk_widget_show ( item );
682 }