]> git.street.me.uk Git - andy/viking.git/blobdiff - src/vikmapniklayer.c
[DOC] Mention XML configuration can override internal defaults.
[andy/viking.git] / src / vikmapniklayer.c
index e9c12ec585bd62e06512c1a8ca4b778883d3abce..dc8c555897034d0d3046695f925eb19a1114abcd 100644 (file)
@@ -24,7 +24,6 @@
 #endif
 
 #include "viking.h"
-#include "vikutils.h"
 #include <glib.h>
 #include <glib/gstdio.h>
 #include <glib/gi18n.h>
 
 #include "vikmapslayer.h"
 
-#if !GLIB_CHECK_VERSION(2,26,0)
-typedef struct stat GStatBuf;
-#endif
-
 struct _VikMapnikLayerClass
 {
        VikLayerClass object_class;
@@ -123,7 +118,8 @@ static gboolean mapnik_feature_release ( VikMapnikLayer *vml, GdkEventButton *ev
 static VikToolInterface mapnik_tools[] = {
        // Layer Info
        // Zoom All?
-  { { "MapnikFeatures", GTK_STOCK_INFO, N_("_Mapnik Features"), NULL, N_("Mapnik Features"), 0 },
+  { NULL,
+    { "MapnikFeatures", GTK_STOCK_INFO, N_("_Mapnik Features"), NULL, N_("Mapnik Features"), 0 },
     (VikToolConstructorFunc) mapnik_feature_create,
     NULL,
     NULL,
@@ -163,6 +159,8 @@ VikLayerInterface vik_mapnik_layer_interface = {
        (VikLayerFuncDraw)                    mapnik_layer_draw,
        (VikLayerFuncChangeCoordMode)         NULL,
 
+       (VikLayerFuncGetTimestamp)            NULL,
+
        (VikLayerFuncSetMenuItemsSelection)   NULL,
        (VikLayerFuncGetMenuItemsSelection)   NULL,
 
@@ -229,8 +227,10 @@ static VikLayerParamData plugins_default ( void )
 #else
        if ( g_file_test ( "/usr/lib/mapnik/input", G_FILE_TEST_EXISTS ) )
                data.s = g_strdup ( "/usr/lib/mapnik/input" );
+               // Current Debian locations
+       else if ( g_file_test ( "/usr/lib/mapnik/3.0/input", G_FILE_TEST_EXISTS ) )
+               data.s = g_strdup ( "/usr/lib/mapnik/3.0/input" );
        else if ( g_file_test ( "/usr/lib/mapnik/2.2/input", G_FILE_TEST_EXISTS ) )
-               // Current Debian location
                data.s = g_strdup ( "/usr/lib/mapnik/2.2/input" );
        else
                data.s = g_strdup ( "" );
@@ -270,11 +270,11 @@ static GHashTable *requests = NULL;
 /**
  * vik_mapnik_layer_init:
  *
- * Mostly to initialize preferences
+ * Just initialize preferences
  */
 void vik_mapnik_layer_init (void)
 {
-       a_preferences_register_group ( MAPNIK_PREFS_GROUP_KEY, "Mapnik" );
+       a_preferences_register_group ( MAPNIK_PREFS_GROUP_KEY, _("Mapnik") );
 
        guint i = 0;
        VikLayerParamData tmp = plugins_default();
@@ -291,7 +291,15 @@ void vik_mapnik_layer_init (void)
 
        tmp.s = "carto";
        a_preferences_register(&prefs[i++], tmp, MAPNIK_PREFS_GROUP_KEY);
+}
 
+/**
+ * vik_mapnik_layer_post_init:
+ *
+ * Initialize data structures - now that reading preferences is OK to perform
+ */
+void vik_mapnik_layer_post_init (void)
+{
        tp_mutex = vik_mutex_new();
 
        // Just storing keys only
@@ -323,9 +331,14 @@ void vik_mapnik_layer_uninit ()
 // NB Only performed once per program run
 static void mapnik_layer_class_init ( VikMapnikLayerClass *klass )
 {
-       mapnik_interface_initialize ( a_preferences_get (MAPNIK_PREFS_NAMESPACE"plugins_directory")->s,
-                                     a_preferences_get (MAPNIK_PREFS_NAMESPACE"fonts_directory")->s,
-                                     a_preferences_get (MAPNIK_PREFS_NAMESPACE"recurse_fonts_directory")->b );
+       VikLayerParamData *pd = a_preferences_get (MAPNIK_PREFS_NAMESPACE"plugins_directory");
+       VikLayerParamData *fd = a_preferences_get (MAPNIK_PREFS_NAMESPACE"fonts_directory");
+       VikLayerParamData *rfd = a_preferences_get (MAPNIK_PREFS_NAMESPACE"recurse_fonts_directory");
+
+       if ( pd && fd && rfd )
+               mapnik_interface_initialize ( pd->s, fd->s, rfd->b );
+       else
+               g_critical ( "Unable to initialize mapnik interface from preferences" );
 }
 
 GType vik_mapnik_layer_get_type ()
@@ -359,7 +372,11 @@ static void mapnik_layer_set_file_xml ( VikMapnikLayer *vml, const gchar *name )
 {
        if ( vml->filename_xml )
                g_free (vml->filename_xml);
-       vml->filename_xml = g_strdup (name);
+       // Mapnik doesn't seem to cope with relative filenames
+       if ( g_strcmp0 (name, "" ) )
+               vml->filename_xml = vu_get_canonical_filename ( VIK_LAYER(vml), name);
+       else
+               vml->filename_xml = g_strdup (name);
 }
 
 static void mapnik_layer_set_file_css ( VikMapnikLayer *vml, const gchar *name )
@@ -488,6 +505,7 @@ gboolean carto_load ( VikMapnikLayer *vml, VikViewport *vvp )
                gchar *msg = g_strdup_printf ( "%s: %s", _("Running"), command );
                vik_window_statusbar_update ( vw, msg, VIK_STATUSBAR_INFO );
                vik_window_set_busy_cursor ( vw );
+               g_free ( msg );
        }
 
        gint64 tt1 = 0;
@@ -522,6 +540,7 @@ gboolean carto_load ( VikMapnikLayer *vml, VikViewport *vvp )
                                if ( !g_strcmp0 ( vml->filename_xml, vml->filename_css ) ) {
                                        vml->filename_xml = g_strconcat ( vml->filename_css, ".xml", NULL );
                                }
+                               g_regex_unref ( regex );
                        }
                        if ( !g_file_set_contents (vml->filename_xml, mystdout, -1, &error)  ) {
                                g_warning ("%s: %s", __FUNCTION__, error->message );
@@ -616,7 +635,8 @@ static void possibly_save_pixbuf ( VikMapnikLayer *vml, GdkPixbuf *pixbuf, MapCo
 
                        gchar *dir = g_path_get_dirname ( filename );
                        if ( !g_file_test ( filename, G_FILE_TEST_EXISTS ) )
-                               g_mkdir_with_parents ( dir , 0777 );
+                               if ( g_mkdir_with_parents ( dir , 0777 ) != 0 )
+                                       g_warning ("%s: Failed to mkdir %s", __FUNCTION__, dir );
                        g_free ( dir );
 
                        if ( !gdk_pixbuf_save (pixbuf, filename, "png", &error, NULL ) ) {
@@ -647,7 +667,8 @@ static void render ( VikMapnikLayer *vml, VikCoord *ul, VikCoord *br, MapCoord *
        gint64 tt1 = g_get_real_time ();
        GdkPixbuf *pixbuf = mapnik_interface_render ( vml->mi, ul->north_south, ul->east_west, br->north_south, br->east_west );
        gint64 tt2 = g_get_real_time ();
-       g_debug ( "Mapnik rendering completed in %.3f seconds", (gdouble)(tt2-tt1)/1000000 );
+       gdouble tt = (gdouble)(tt2-tt1)/1000000;
+       g_debug ( "Mapnik rendering completed in %.3f seconds", tt );
        if ( !pixbuf ) {
                // A pixbuf to stick into cache incase of an unrenderable area - otherwise will get continually re-requested
                pixbuf = gdk_pixbuf_scale_simple ( gdk_pixbuf_from_pixdata(&vikmapniklayer_pixbuf, FALSE, NULL), vml->tile_size_x, vml->tile_size_x, GDK_INTERP_BILINEAR );
@@ -656,8 +677,9 @@ static void render ( VikMapnikLayer *vml, VikCoord *ul, VikCoord *br, MapCoord *
 
        // NB Mapnik can apply alpha, but use our own function for now
        if ( vml->alpha < 255 )
-               pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
-       a_mapcache_add ( pixbuf, ulm->x, ulm->y, ulm->z, MAP_ID_MAPNIK_RENDER, ulm->scale, vml->alpha, 0.0, 0.0, vml->filename_xml );
+               pixbuf = ui_pixbuf_scale_alpha ( pixbuf, vml->alpha );
+       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 );
+       g_object_unref(pixbuf);
 }
 
 static void render_info_free ( RenderInfo *data )
@@ -694,7 +716,7 @@ static void render_cancel_cleanup (RenderInfo *data)
 /**
  * Thread
  */
-void thread_add (VikMapnikLayer *vml, MapCoord *mul, VikCoord *ul, VikCoord *br, gint x, gint y, gint z, gint zoom, const gchar* name )
+static void thread_add (VikMapnikLayer *vml, MapCoord *mul, VikCoord *ul, VikCoord *br, gint x, gint y, gint z, gint zoom, const gchar* name )
 {
        // Create request
        guint nn = name ? g_str_hash ( name ) : 0;
@@ -725,7 +747,7 @@ void thread_add (VikMapnikLayer *vml, MapCoord *mul, VikCoord *ul, VikCoord *br,
        gchar *basename = g_path_get_basename (name);
        gchar *description = g_strdup_printf ( _("Mapnik Render %d:%d:%d %s"), zoom, x, y, basename );
        g_free ( basename );
-       a_background_thread ( BACKGROUND_POOL_LOCAL,
+       a_background_thread ( BACKGROUND_POOL_LOCAL_MAPNIK,
                              VIK_GTK_WINDOW_FROM_LAYER(vml),
                              description,
                              (vik_thr_func) background,
@@ -738,6 +760,9 @@ void thread_add (VikMapnikLayer *vml, MapCoord *mul, VikCoord *ul, VikCoord *br,
 
 /**
  * load_pixbuf:
+ *
+ * If function returns GdkPixbuf properly, reference counter to this
+ * buffer has to be decreased, when buffer is no longer needed.
  */
 static GdkPixbuf *load_pixbuf ( VikMapnikLayer *vml, MapCoord *ulm, MapCoord *brm, gboolean *rerender )
 {
@@ -757,7 +782,7 @@ static GdkPixbuf *load_pixbuf ( VikMapnikLayer *vml, MapCoord *ulm, MapCoord *br
                else {
                        if ( vml->alpha < 255 )
                                pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
-                       a_mapcache_add ( pixbuf, ulm->x, ulm->y, ulm->z, MAP_ID_MAPNIK_RENDER, ulm->scale, vml->alpha, 0.0, 0.0, vml->filename_xml );
+                       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 );
                }
                // If file is too old mark for rerendering
                if ( planet_import_time < gsb.st_mtime ) {
@@ -770,7 +795,8 @@ static GdkPixbuf *load_pixbuf ( VikMapnikLayer *vml, MapCoord *ulm, MapCoord *br
 }
 
 /**
- *
+ * Caller has to decrease reference counter of returned
+ * GdkPixbuf, when buffer is no longer needed.
  */
 static GdkPixbuf *get_pixbuf ( VikMapnikLayer *vml, MapCoord *ulm, MapCoord *brm )
 {
@@ -814,6 +840,13 @@ static void mapnik_layer_draw ( VikMapnikLayer *vml, VikViewport *vvp )
                return;
        }
 
+       if ( vml->mi ) {
+               gchar *copyright = mapnik_interface_get_copyright ( vml->mi );
+               if ( copyright ) {
+                       vik_viewport_add_copyright ( vvp, copyright );
+               }
+       }
+
        VikCoord ul, br;
        ul.mode = VIK_COORD_LATLON;
        br.mode = VIK_COORD_LATLON;
@@ -850,6 +883,7 @@ static void mapnik_layer_draw ( VikMapnikLayer *vml, VikViewport *vvp )
                                        map_utils_iTMS_to_vikcoord ( &ulm, &coord );
                                        vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy );
                                        vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, vml->tile_size_x, vml->tile_size_x );
+                                       g_object_unref(pixbuf);
                                }
                        }
                }
@@ -922,6 +956,51 @@ static void mapnik_layer_reload ( menu_array_values values )
        mapnik_layer_draw ( vml, vvp );
 }
 
+/**
+ * Force carto run
+ *
+ * Most carto projects will consist of many files
+ * ATM don't have a way of detecting when any of the included files have changed
+ * Thus allow a manual method to force re-running carto
+ */
+static void mapnik_layer_carto ( menu_array_values values )
+{
+       VikMapnikLayer *vml = values[MA_VML];
+       VikViewport *vvp = values[MA_VVP];
+
+       // Don't load the XML config if carto load fails
+       if ( !carto_load ( vml, vvp ) )
+               return;
+
+       gchar* ans = mapnik_interface_load_map_file ( vml->mi, vml->filename_xml, vml->tile_size_x, vml->tile_size_x );
+       if ( ans ) {
+               a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vvp),
+                                          _("Mapnik error loading configuration file:\n%s"),
+                                          ans );
+               g_free ( ans );
+       }
+       else
+               mapnik_layer_draw ( vml, vvp );
+}
+
+/**
+ * Show Mapnik configuration parameters
+ */
+static void mapnik_layer_information ( menu_array_values values )
+{
+       VikMapnikLayer *vml = values[MA_VML];
+       if ( !vml->mi )
+               return;
+       GArray *array = mapnik_interface_get_parameters( vml->mi );
+       if ( array->len ) {
+               a_dialog_list (  VIK_GTK_WINDOW_FROM_LAYER(vml), _("Mapnik Information"), array, 1 );
+               // Free the copied strings
+               for ( int i = 0; i < array->len; i++ )
+                       g_free ( g_array_index(array,gchar*,i) );
+       }
+       g_array_free ( array, FALSE );
+}
+
 /**
  *
  */
@@ -960,6 +1039,19 @@ static void mapnik_layer_add_menu_items ( VikMapnikLayer *vml, GtkMenu *menu, gp
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
        gtk_widget_show ( item );
 
+       if ( g_strcmp0 ("", vml->filename_css) ) {
+               item = gtk_image_menu_item_new_with_mnemonic ( _("_Run Carto Command") );
+               gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU) );
+               g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_carto), values );
+               gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+               gtk_widget_show ( item );
+       }
+
+       item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_INFO, NULL );
+       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_information), values );
+       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+       gtk_widget_show ( item );
+
        item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_ABOUT, NULL );
        g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_about), values );
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
@@ -984,6 +1076,59 @@ static void mapnik_layer_rerender ( VikMapnikLayer *vml )
        thread_add (vml, &ulm, &vml->rerender_ul, &vml->rerender_br, ulm.x, ulm.y, ulm.z, ulm.scale, vml->filename_xml );
 }
 
+/**
+ * Info
+ */
+static void mapnik_layer_tile_info ( VikMapnikLayer *vml )
+{
+       MapCoord ulm;
+       // Requested position to map coord
+       map_utils_vikcoord_to_iTMS ( &vml->rerender_ul, vml->rerender_zoom, vml->rerender_zoom, &ulm );
+
+       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 );
+
+       gchar *filename = get_filename ( vml->file_cache_dir, ulm.x, ulm.y, ulm.scale );
+       gchar *filemsg = NULL;
+       gchar *timemsg = NULL;
+
+       if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
+               filemsg = g_strconcat ( "Tile File: ", filename, NULL );
+               // Get some timestamp information of the tile
+               GStatBuf stat_buf;
+               if ( g_stat ( filename, &stat_buf ) == 0 ) {
+                       gchar time_buf[64];
+                       strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
+                       timemsg = g_strdup_printf ( _("Tile File Timestamp: %s"), time_buf );
+               }
+               else {
+                       timemsg = g_strdup ( _("Tile File Timestamp: Not Available") );
+               }
+       }
+       else {
+               filemsg = g_strdup_printf ( "Tile File: %s [Not Available]", filename );
+               timemsg = g_strdup("");
+       }
+
+       GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
+       g_array_append_val ( array, filemsg );
+       g_array_append_val ( array, timemsg );
+
+       gchar *rendmsg = NULL;
+       // Show the info
+       if ( extra.duration > 0.0 ) {
+               rendmsg = g_strdup_printf ( _("Rendering time %.2f seconds"), extra.duration );
+               g_array_append_val ( array, rendmsg );
+       }
+
+       a_dialog_list (  VIK_GTK_WINDOW_FROM_LAYER(vml), _("Tile Information"), array, 5 );
+       g_array_free ( array, FALSE );
+
+       g_free ( rendmsg );
+       g_free ( timemsg );
+       g_free ( filemsg );
+       g_free ( filename );
+}
+
 static gboolean mapnik_feature_release ( VikMapnikLayer *vml, GdkEventButton *event, VikViewport *vvp )
 {
        if ( !vml )
@@ -1000,6 +1145,11 @@ static gboolean mapnik_feature_release ( VikMapnikLayer *vml, GdkEventButton *ev
                        gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
                        g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_rerender), vml );
                        gtk_menu_shell_append ( GTK_MENU_SHELL(vml->right_click_menu), item );
+
+                       item = gtk_image_menu_item_new_with_mnemonic ( _("_Info") );
+                       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
+                       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(mapnik_layer_tile_info), vml );
+                       gtk_menu_shell_append ( GTK_MENU_SHELL(vml->right_click_menu), item );
                }
 
                gtk_menu_popup ( GTK_MENU(vml->right_click_menu), NULL, NULL, NULL, NULL, event->button, event->time );