]> git.street.me.uk Git - andy/viking.git/blobdiff - src/mapnik_interface.cpp
Coverity: Prevent deference after null checks
[andy/viking.git] / src / mapnik_interface.cpp
index bd2fef2fbcc551ca97c6f693f764ab2d9577e34e..abc544ab5ab43496ac7ada34e3a74104bbc603ef 100644 (file)
 #include <mapnik/datasource_cache.hpp>
 #include <mapnik/agg_renderer.hpp>
 #include <mapnik/load_map.hpp>
+#include <mapnik/projection.hpp>
+#if MAPNIK_VERSION < 300000
 #include <mapnik/graphics.hpp>
+#else
+#include <mapnik/value.hpp>
+#endif
 #include <mapnik/image_util.hpp>
 
 #include <exception>
 
 #include <glib.h>
 #include <glib/gstdio.h>
+#include <glib/gi18n.h>
 
 #include "mapnik_interface.h"
 #include "globals.h"
+#include "settings.h"
 
 #if MAPNIK_VERSION < 200000
 #include <mapnik/envelope.hpp>
 #define zoom_to_box zoomToBox
 #else
 #include <mapnik/box2d.hpp>
+#if MAPNIK_VERSION >= 300000
+// In Mapnik3 'image_32' has changed names once again
+#define image_32 image_rgba8
+#define raw_data data
+#endif
 #endif
 
 #define MAPNIK_INTERFACE_TYPE            (mapnik_interface_get_type ())
@@ -75,6 +87,7 @@ static void mapnik_interface_init ( MapnikInterface *mi )
 struct _MapnikInterface {
        GObject obj;
        mapnik::Map *myMap;
+       gchar *copyright; // Cached Mapnik parameter to save looking it up each time
 };
 
 G_DEFINE_TYPE (MapnikInterface, mapnik_interface, G_TYPE_OBJECT)
@@ -86,13 +99,16 @@ MapnikInterface* mapnik_interface_new ()
 {
        MapnikInterface* mi = MAPNIK_INTERFACE ( g_object_new ( MAPNIK_INTERFACE_TYPE, NULL ) );
        mi->myMap = new mapnik::Map;
+       mi->copyright = NULL;
        return mi;
 }
 
 void mapnik_interface_free (MapnikInterface* mi)
 {
-       if ( mi )
+       if ( mi ) {
+               g_free ( mi->copyright );
                delete mi->myMap;
+       }
        g_object_unref ( G_OBJECT(mi) );
 }
 
@@ -106,20 +122,12 @@ void mapnik_interface_initialize (const char *plugins_dir, const char* font_dir,
                if ( plugins_dir )
 #if MAPNIK_VERSION >= 200200
                        mapnik::datasource_cache::instance().register_datasources(plugins_dir);
-                       // FUTURE: Make this an 'about' property
-                       if ( vik_verbose ) {
-                               //for (auto& name in mapnik::datasource_cache::instance().plugin_names()) std::cout << name << '\n';// C++11
-                               std::vector<std::string> plugins = mapnik::datasource_cache::instance().plugin_names();
-                               for (int nn = 0; nn < plugins.size(); nn++ )
-                                       g_printf ("mapnik enabled plugin: %s\n", plugins[nn].c_str());
-                       }
 #else
                        mapnik::datasource_cache::instance()->register_datasources(plugins_dir);
 #endif
                if ( font_dir )
                        if ( ! mapnik::freetype_engine::register_fonts(font_dir, font_dir_recurse ? true : false) )
                                g_warning ("%s: No fonts found", __FUNCTION__);
-               g_debug ("mapnik font faces found: %d", mapnik::freetype_engine::face_names().size());
        } catch (std::exception const& ex) {
                g_warning ("An error occurred while initialising mapnik: %s", ex.what());
        } catch (...) {
@@ -127,17 +135,50 @@ void mapnik_interface_initialize (const char *plugins_dir, const char* font_dir,
        }
 }
 
+/**
+ *  caching this answer instead of looking it up each time
+ */
+static void set_copyright ( MapnikInterface* mi )
+{
+       g_free ( mi->copyright );
+       mi->copyright = NULL;
+
+       mapnik::parameters pmts = mi->myMap->get_extra_parameters();
+#if MAPNIK_VERSION < 300000
+       for (mapnik::parameters::const_iterator ii = pmts.begin(); ii != pmts.end(); ii++) {
+               if ( ii->first == "attribution" || ii->first == "copyright" ) {
+                       std::stringstream ss;
+                       ss << ii->second;
+                       // Copy it
+                       mi->copyright = g_strdup ( (gchar*)ss.str().c_str() );
+                       break;
+               }
+       }
+#else
+       if ( pmts.get<std::string>("attribution") )
+               mi->copyright = g_strdup ( (*pmts.get<std::string>("attribution")).c_str() );
+
+       if ( !mi->copyright )
+               if ( pmts.get<std::string>("copyright") )
+                       mi->copyright = g_strdup ( (*pmts.get<std::string>("copyright")).c_str() );
+#endif
+}
+
+#define VIK_SETTINGS_MAPNIK_BUFFER_SIZE "mapnik_buffer_size"
+
 /**
  * mapnik_interface_load_map_file:
  *
- * Returns 0 on success.
+ * Returns NULL on success otherwise a string about what went wrong.
+ *  This string should be freed once it has been used.
  */
-int mapnik_interface_load_map_file ( MapnikInterface* mi,
-                                     const gchar *filename,
-                                     guint width,
-                                     guint height )
+gchar* mapnik_interface_load_map_file ( MapnikInterface* mi,
+                                        const gchar *filename,
+                                        guint width,
+                                        guint height )
 {
-       if ( !mi ) return 1;
+       gchar *msg = NULL;
+       if ( !mi ) return g_strdup ("Internal Error");
        try {
                mi->myMap->remove_all(); // Support reloading
                mapnik::load_map(*mi->myMap, filename);
@@ -149,48 +190,25 @@ int mapnik_interface_load_map_file ( MapnikInterface* mi,
                // Only set buffer size if the buffer size isn't explicitly set in the mapnik stylesheet.
                // Alternatively render a bigger 'virtual' tile and then only use the appropriate subset
                if (mi->myMap->buffer_size() == 0) {
-                       mi->myMap->set_buffer_size((width+height/4)); // e.g. 128 for a 256x256 image.
+                       gint buffer_size = (width+height/4); // e.g. 128 for a 256x256 image.
+                       gint tmp;
+                       if ( a_settings_get_integer ( VIK_SETTINGS_MAPNIK_BUFFER_SIZE, &tmp ) )
+                               buffer_size = tmp;
+
+                       mi->myMap->set_buffer_size(buffer_size);
                }
 
-               g_debug ("%s layers: %d", __FUNCTION__, mi->myMap->layer_count() );
+               set_copyright ( mi );
+
+               g_debug ("%s layers: %d", __FUNCTION__, (guint)mi->myMap->layer_count() );
        } catch (std::exception const& ex) {
-               g_debug ("An error occurred while loading the mapnik config '%s': %s", filename, ex.what());
-               return 2;
+               msg = g_strdup ( ex.what() );
        } catch (...) {
-               g_debug ("An unknown error occurred while loading the mapnik config '%s': %s", filename );
-               return 3;
+               msg = g_strdup ("unknown error");
        }
-       return 0;
-}
-
-// These two functions copied from gpsdrive 2.11
-/**
- * convert the color channel
- */
-inline unsigned char
-convert_color_channel (unsigned char Source, unsigned char Alpha)
-{
-       return Alpha ? ((Source << 8) - Source) / Alpha : 0;
+       return msg;
 }
 
-/**
- * converting argb32 to gdkpixbuf
- */
-static void
-convert_argb32_to_gdkpixbuf_data (unsigned char const *Source, unsigned int width, unsigned int height, unsigned char *Dest)
-{
-       unsigned char const *SourcePixel = Source;
-       unsigned char *DestPixel = Dest;
-       for (int y = 0; y < height; y++) {
-               for (int x = 0; x < width; x++) {
-                       DestPixel[0] = convert_color_channel(SourcePixel[0], SourcePixel[3]);
-                       DestPixel[1] = convert_color_channel(SourcePixel[1], SourcePixel[3]);
-                       DestPixel[2] = convert_color_channel(SourcePixel[2], SourcePixel[3]);
-                       DestPixel += 3;
-                       SourcePixel += 4;
-               }
-       }
-}
 
 /**
  * mapnik_interface_render:
@@ -201,6 +219,10 @@ GdkPixbuf* mapnik_interface_render ( MapnikInterface* mi, double lat_tl, double
 {
        if ( !mi ) return NULL;
 
+       // Copy main object to local map variable
+       //  This enables rendering to work when this function is called from different threads
+       mapnik::Map myMap(*mi->myMap);
+
        // Note prj & bbox want stuff in lon,lat order!
        double p0x = lon_tl;
        double p0y = lat_tl;
@@ -213,21 +235,21 @@ GdkPixbuf* mapnik_interface_render ( MapnikInterface* mi, double lat_tl, double
 
        GdkPixbuf *pixbuf = NULL;
        try {
-               unsigned width  = mi->myMap->width();
-               unsigned height = mi->myMap->height();
+               unsigned width  = myMap.width();
+               unsigned height = myMap.height();
                mapnik::image_32 image(width,height);
                mapnik::box2d<double> bbox(p0x, p0y, p1x, p1y);
-               mi->myMap->zoom_to_box(bbox);
+               myMap.zoom_to_box(bbox);
                // FUTURE: option to use cairo / grid renderers?
-               mapnik::agg_renderer<mapnik::image_32> render(*mi->myMap,image);
+               mapnik::agg_renderer<mapnik::image_32> render(myMap,image);
                render.apply();
 
                if ( image.painted() ) {
-                       unsigned char *ImageRawDataPtr = (unsigned char *) g_malloc(width * 3 * height);
+                       unsigned char *ImageRawDataPtr = (unsigned char *) g_malloc(width * 4 * height);
                        if (!ImageRawDataPtr)
                                return NULL;
-                       convert_argb32_to_gdkpixbuf_data(image.raw_data(), width, height, ImageRawDataPtr);
-                       pixbuf = gdk_pixbuf_new_from_data(ImageRawDataPtr, GDK_COLORSPACE_RGB, FALSE, 8, width, height, width * 3, NULL, NULL);
+                       memcpy(ImageRawDataPtr, image.raw_data(), width * height * 4);
+                       pixbuf = gdk_pixbuf_new_from_data(ImageRawDataPtr, GDK_COLORSPACE_RGB, TRUE, 8, width, height, width * 4, NULL, NULL);
                }
                else
                        g_warning ("%s not rendered", __FUNCTION__ );
@@ -240,3 +262,69 @@ GdkPixbuf* mapnik_interface_render ( MapnikInterface* mi, double lat_tl, double
 
        return pixbuf;
 }
+
+/**
+ * Copyright/Attribution information about the Map - string maybe NULL
+ *
+ * Free returned string  after use
+ */
+gchar* mapnik_interface_get_copyright ( MapnikInterface* mi )
+{
+       if ( !mi ) return NULL;
+       return mi->copyright;
+}
+
+/**
+ * 'Parameter' information about the Map configuration
+ *
+ * Free every string element and the returned GArray itself after use
+ */
+GArray* mapnik_interface_get_parameters ( MapnikInterface* mi )
+{
+       GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
+
+       mapnik::parameters pmts = mi->myMap->get_extra_parameters();
+       // Simply want the strings of each parameter so we can display something...
+#if MAPNIK_VERSION < 300000
+       for (mapnik::parameters::const_iterator pmt = pmts.begin(); pmt != pmts.end(); pmt++) {
+               std::stringstream ss;
+               ss << pmt->first << ": " << pmt->second;
+#else
+       for (auto const& pmt : pmts) {
+               std::stringstream ss;
+               ss << pmt.first << ": " << *(pmts.get<std::string>(pmt.first,"empty"));
+#endif
+               // Copy - otherwise ss goes output scope and junk memory would be referenced.
+               gchar *str2 = g_strdup ( (gchar*)ss.str().c_str() );
+               g_array_append_val ( array, str2 );
+       }
+
+       return array;
+}
+
+/**
+ * General information about Mapnik
+ *
+ * Free the returned string after use
+ */
+gchar * mapnik_interface_about ( void )
+{
+       // Normally about 10 plugins so list them all
+#if MAPNIK_VERSION >= 200200
+       std::vector<std::string> plugins = mapnik::datasource_cache::instance().plugin_names();
+#else
+       std::vector<std::string> plugins = mapnik::datasource_cache::instance()->plugin_names();
+#endif
+       std::string str;
+       for (int nn = 0; nn < plugins.size(); nn++ )
+               str += plugins[nn] + ',';
+       str += '\n';
+       // NB Can have a couple hundred fonts loaded when using system directories
+       //  So ATM don't list them all - otherwise need better GUI feedback display.
+       gchar *msg = g_strdup_printf ( _("%s %s\nPlugins=%sFonts loaded=%d"),
+                                      _("Mapnik"),
+                                      MAPNIK_VERSION_STRING,
+                                      str.c_str(),
+                                      (guint)mapnik::freetype_engine::face_names().size() );
+       return msg;
+}