2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (c) 2015, Rob Norris <rw_norris@hotmail.com>
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.
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.
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
29 #include <glib/gstdio.h>
30 #include <glib/gi18n.h>
42 #include "preferences.h"
43 #include "icons/icons.h"
44 #include "mapnik_interface.h"
46 struct _VikMapnikLayerClass
48 VikLayerClass object_class;
51 static VikLayerParamData file_default ( void )
53 VikLayerParamData data;
58 static VikLayerParamData size_default ( void ) { return VIK_LPD_UINT ( 256 ); }
59 static VikLayerParamData alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
61 static VikLayerParamScale scales[] = {
62 { 0, 255, 5, 0 }, // Alpha
63 { 64, 1024, 8, 0 }, // Tile size
66 VikLayerParam mapnik_layer_params[] = {
67 { 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,
68 N_("Mapnik XML configuration file"), file_default, NULL, NULL },
69 { VIK_LAYER_MAPNIK, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Alpha:"), VIK_LAYER_WIDGET_HSCALE, &scales[0], NULL,
70 NULL, alpha_default, NULL, NULL },
78 static const gchar* mapnik_layer_tooltip ( VikMapnikLayer *vml );
79 static void mapnik_layer_marshall( VikMapnikLayer *vml, guint8 **data, gint *len );
80 static VikMapnikLayer *mapnik_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
81 static gboolean mapnik_layer_set_param ( VikMapnikLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
82 static VikLayerParamData mapnik_layer_get_param ( VikMapnikLayer *vml, guint16 id, gboolean is_file_operation );
83 static VikMapnikLayer *mapnik_layer_new ( VikViewport *vvp );
84 static VikMapnikLayer *mapnik_layer_create ( VikViewport *vp );
85 static void mapnik_layer_free ( VikMapnikLayer *vml );
86 static void mapnik_layer_draw ( VikMapnikLayer *vml, VikViewport *vp );
87 static void mapnik_layer_add_menu_items ( VikMapnikLayer *vml, GtkMenu *menu, gpointer vlp );
89 // See comment in viktrwlayer.c for advice on values used
91 static VikToolInterface mapnik_tools[] = {
96 static void mapnik_layer_post_read (VikLayer *vl, VikViewport *vvp, gboolean from_file);
98 VikLayerInterface vik_mapnik_layer_interface = {
100 N_("Mapnik Rendering"),
102 &vikmapniklayer_pixbuf, // icon
105 sizeof(mapnik_tools) / sizeof(VikToolInterface),
114 (VikLayerFuncCreate) mapnik_layer_create,
115 (VikLayerFuncRealize) NULL,
116 (VikLayerFuncPostRead) mapnik_layer_post_read,
117 (VikLayerFuncFree) mapnik_layer_free,
119 (VikLayerFuncProperties) NULL,
120 (VikLayerFuncDraw) mapnik_layer_draw,
121 (VikLayerFuncChangeCoordMode) NULL,
123 (VikLayerFuncSetMenuItemsSelection) NULL,
124 (VikLayerFuncGetMenuItemsSelection) NULL,
126 (VikLayerFuncAddMenuItems) mapnik_layer_add_menu_items,
127 (VikLayerFuncSublayerAddMenuItems) NULL,
129 (VikLayerFuncSublayerRenameRequest) NULL,
130 (VikLayerFuncSublayerToggleVisible) NULL,
131 (VikLayerFuncSublayerTooltip) NULL,
132 (VikLayerFuncLayerTooltip) mapnik_layer_tooltip,
133 (VikLayerFuncLayerSelected) NULL,
135 (VikLayerFuncMarshall) mapnik_layer_marshall,
136 (VikLayerFuncUnmarshall) mapnik_layer_unmarshall,
138 (VikLayerFuncSetParam) mapnik_layer_set_param,
139 (VikLayerFuncGetParam) mapnik_layer_get_param,
140 (VikLayerFuncChangeParam) NULL,
142 (VikLayerFuncReadFileData) NULL,
143 (VikLayerFuncWriteFileData) NULL,
145 (VikLayerFuncDeleteItem) NULL,
146 (VikLayerFuncCutItem) NULL,
147 (VikLayerFuncCopyItem) NULL,
148 (VikLayerFuncPasteItem) NULL,
149 (VikLayerFuncFreeCopiedItem) NULL,
150 (VikLayerFuncDragDropRequest) NULL,
152 (VikLayerFuncSelectClick) NULL,
153 (VikLayerFuncSelectMove) NULL,
154 (VikLayerFuncSelectRelease) NULL,
155 (VikLayerFuncSelectedViewportMenu) NULL,
158 struct _VikMapnikLayer {
163 guint tile_size_x; // Y is the same as X ATM
167 #define MAPNIK_PREFS_GROUP_KEY "mapnik"
168 #define MAPNIK_PREFS_NAMESPACE "mapnik."
170 static VikLayerParamData plugins_default ( void )
172 VikLayerParamData data;
174 data.s = g_strdup ( "input" );
176 if ( g_file_test ( "/usr/lib/mapnik/input", G_FILE_TEST_EXISTS ) )
177 data.s = g_strdup ( "/usr/lib/mapnik/input" );
178 else if ( g_file_test ( "/usr/lib/mapnik/2.2/input", G_FILE_TEST_EXISTS ) )
179 // Current Debian location
180 data.s = g_strdup ( "/usr/lib/mapnik/2.2/input" );
182 data.s = g_strdup ( "" );
187 static VikLayerParamData fonts_default ( void )
189 // Possibly should be string list to allow loading from multiple directories
190 VikLayerParamData data;
192 data.s = g_strdup ( "C:\\Windows\\Fonts" );
193 #elif defined __APPLE__
194 data.s = g_strdup ( "/Library/Fonts" );
196 data.s = g_strdup ( "/usr/share/fonts" );
201 static VikLayerParam prefs[] = {
202 // Changing these values only applies before first mapnik layer is 'created'
203 { 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 },
204 { 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 },
205 { 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 },
208 // NB Only performed once per program run
209 static void mapnik_layer_class_init ( VikMapnikLayerClass *klass )
211 mapnik_interface_init ( a_preferences_get (MAPNIK_PREFS_NAMESPACE"plugins_directory")->s,
212 a_preferences_get (MAPNIK_PREFS_NAMESPACE"fonts_directory")->s,
213 a_preferences_get (MAPNIK_PREFS_NAMESPACE"recurse_fonts_directory")->b );
216 void vik_mapnik_layer_init (void)
218 a_preferences_register_group ( MAPNIK_PREFS_GROUP_KEY, "Mapnik" );
221 VikLayerParamData tmp = plugins_default();
222 a_preferences_register(&prefs[i++], tmp, MAPNIK_PREFS_GROUP_KEY);
224 tmp = fonts_default();
225 a_preferences_register(&prefs[i++], tmp, MAPNIK_PREFS_GROUP_KEY);
228 a_preferences_register(&prefs[i++], tmp, MAPNIK_PREFS_GROUP_KEY);
231 GType vik_mapnik_layer_get_type ()
233 static GType vml_type = 0;
236 static const GTypeInfo vml_info = {
237 sizeof (VikMapnikLayerClass),
238 NULL, /* base_init */
239 NULL, /* base_finalize */
240 (GClassInitFunc) mapnik_layer_class_init, /* class init */
241 NULL, /* class_finalize */
242 NULL, /* class_data */
243 sizeof (VikMapnikLayer),
245 NULL /* instance init */
247 vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapnikLayer", &vml_info, 0 );
253 static const gchar* mapnik_layer_tooltip ( VikMapnikLayer *vml )
255 return vml->filename_xml;
258 static void mapnik_layer_set_file_xml ( VikMapnikLayer *vml, const gchar *name )
260 if ( vml->filename_xml )
261 g_free (vml->filename_xml);
262 vml->filename_xml = g_strdup (name);
265 static void mapnik_layer_marshall( VikMapnikLayer *vml, guint8 **data, gint *len )
267 vik_layer_marshall_params ( VIK_LAYER(vml), data, len );
270 static VikMapnikLayer *mapnik_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
272 VikMapnikLayer *rv = mapnik_layer_new ( vvp );
273 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
277 static gboolean mapnik_layer_set_param ( VikMapnikLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
280 case PARAM_CONFIG_XML: mapnik_layer_set_file_xml (vml, data.s); break;
281 case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break;
287 static VikLayerParamData mapnik_layer_get_param ( VikMapnikLayer *vml, guint16 id, gboolean is_file_operation )
289 VikLayerParamData data;
291 case PARAM_CONFIG_XML: {
292 data.s = vml->filename_xml;
293 gboolean set = FALSE;
294 if ( is_file_operation ) {
295 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
296 gchar *cwd = g_get_current_dir();
298 data.s = file_GetRelativeFilename ( cwd, vml->filename_xml );
299 if ( !data.s ) data.s = "";
305 data.s = vml->filename_xml ? vml->filename_xml : "";
308 case PARAM_ALPHA: data.u = vml->alpha; break;
309 //case PARAM_TILE_X: data.u = vml->tile_size_x; break;
318 static VikMapnikLayer *mapnik_layer_new ( VikViewport *vvp )
320 VikMapnikLayer *vml = VIK_MAPNIK_LAYER ( g_object_new ( VIK_MAPNIK_LAYER_TYPE, NULL ) );
321 vik_layer_set_type ( VIK_LAYER(vml), VIK_LAYER_MAPNIK );
322 vik_layer_set_defaults ( VIK_LAYER(vml), vvp );
323 vml->tile_size_x = size_default().u; // FUTURE: Is there any use in this being configurable?
331 static void mapnik_layer_post_read (VikLayer *vl, VikViewport *vvp, gboolean from_file)
333 VikMapnikLayer *vml = VIK_MAPNIK_LAYER(vl);
335 if ( mapnik_interface_load_map_file ( vml->filename_xml, vml->tile_size_x, vml->tile_size_x ) ) {
337 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vvp),
338 _("Mapnik error loading configuration file: %s"),
341 g_warning ( _("Mapnik error loading configuration file: %s"), vml->filename_xml );
346 ui_add_recent_file ( vml->filename_xml );
353 static GdkPixbuf *get_pixbuf ( VikMapnikLayer *vml, MapCoord *ulm, MapCoord *brm )
355 VikCoord ul; VikCoord br;
358 map_utils_iTMS_to_vikcoord (ulm, &ul);
359 map_utils_iTMS_to_vikcoord (brm, &br);
361 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 );
364 pixbuf = mapnik_interface_render ( vml->mi, ul.north_south, ul.east_west, br.north_south, br.east_west );
366 // NB Mapnik can apply alpha, but use our own function for now
367 if ( vml->alpha < 255 )
368 pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
369 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 );
379 static void mapnik_layer_draw ( VikMapnikLayer *vml, VikViewport *vvp )
384 if ( vik_viewport_get_drawmode(vvp) != VIK_VIEWPORT_DRAWMODE_MERCATOR ) {
385 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vml))),
386 VIK_STATUSBAR_INFO, _("Mapnik Rendering must be in Mercator mode") );
391 ul.mode = VIK_COORD_LATLON;
392 br.mode = VIK_COORD_LATLON;
393 vik_viewport_screen_to_coord ( vvp, 0, 0, &ul );
394 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br );
396 gdouble xzoom = vik_viewport_get_xmpp ( vvp );
397 gdouble yzoom = vik_viewport_get_ympp ( vvp );
401 if ( map_utils_vikcoord_to_iTMS ( &ul, xzoom, yzoom, &ulm ) &&
402 map_utils_vikcoord_to_iTMS ( &br, xzoom, yzoom, &brm ) ) {
403 // TODO: Understand if tilesize != 256 does this need to use shrinkfactors??
408 gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x);
409 gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y);
411 // Split rendering into a grid for the current viewport
412 // thus each individual 'tile' can then be stored in the map cache
413 // TODO: Also potentially allows using multi threads for each individual tile
414 // this is more important for complicated stylesheets/datasources as each rendering may take some time
415 for (gint x = xmin; x <= xmax; x++ ) {
416 for (gint y = ymin; y <= ymax; y++ ) {
422 pixbuf = get_pixbuf ( vml, &ulm, &brm );
425 map_utils_iTMS_to_vikcoord ( &ulm, &coord );
426 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
427 vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, vml->tile_size_x, vml->tile_size_x );
432 // Done after so drawn on top
433 // Just a handy guide to tile blocks.
434 if ( vik_debug && vik_verbose ) {
435 GdkGC *black_gc = GTK_WIDGET(vvp)->style->black_gc;
436 gint width = vik_viewport_get_width(vvp);
437 gint height = vik_viewport_get_height(vvp);
439 ulm.x = xmin; ulm.y = ymin;
440 map_utils_iTMS_to_center_vikcoord ( &ulm, &coord );
441 vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
442 xx = xx - (vml->tile_size_x/2);
443 yy = yy - (vml->tile_size_x/2); // Yes use X ATM
444 for (gint x = xmin; x <= xmax; x++ ) {
445 vik_viewport_draw_line ( vvp, black_gc, xx, 0, xx, height );
446 xx += vml->tile_size_x;
448 for (gint y = ymin; y <= ymax; y++ ) {
449 vik_viewport_draw_line ( vvp, black_gc, 0, yy, width, yy );
450 yy += vml->tile_size_x; // Yes use X ATM
459 static void mapnik_layer_free ( VikMapnikLayer *vml )
461 if ( vml->filename_xml )
462 g_free ( vml->filename_xml );
465 static VikMapnikLayer *mapnik_layer_create ( VikViewport *vp )
467 return mapnik_layer_new ( vp );
476 typedef gpointer menu_array_values[MA_LAST];
481 static void mapnik_layer_flush_memory ( menu_array_values values )
483 a_mapcache_flush_type ( MAP_ID_MAPNIK_RENDER );
489 static void mapnik_layer_add_menu_items ( VikMapnikLayer *vml, GtkMenu *menu, gpointer vlp )
491 static menu_array_values values;
492 values[MA_VML] = vml;
493 values[MA_VVP] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) );
495 GtkWidget *item = gtk_menu_item_new();
496 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
497 gtk_widget_show ( item );
499 // Typical users shouldn't need to use this functionality - so debug only ATM
501 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flush Memory Cache") );
502 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
503 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_flush_memory), values );
504 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
505 gtk_widget_show ( item );