1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
3 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
5 * Copyright (C) 2015, Rob Norris <rw_norris@hotmail.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <mapnik/version.hpp>
23 #include <mapnik/map.hpp>
24 #include <mapnik/datasource_cache.hpp>
25 #include <mapnik/agg_renderer.hpp>
26 #include <mapnik/load_map.hpp>
27 #include <mapnik/graphics.hpp>
28 #include <mapnik/image_util.hpp>
37 #include <glib/gstdio.h>
38 #include <glib/gi18n.h>
40 #include "mapnik_interface.h"
44 #if MAPNIK_VERSION < 200000
45 #include <mapnik/envelope.hpp>
46 #define image_32 Image32
47 #define image_data_32 ImageData32
48 #define box2d Envelope
49 #define zoom_to_box zoomToBox
51 #include <mapnik/box2d.hpp>
54 #define MAPNIK_INTERFACE_TYPE (mapnik_interface_get_type ())
55 #define MAPNIK_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAPNIK_INTERFACE_TYPE, MapnikInterface))
56 #define MAPNIK_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAPNIK_INTERFACE_TYPE, MapnikInterfaceClass))
57 #define IS_MAPNIK_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAPNIK_INTERFACE_TYPE))
58 #define IS_MAPNIK_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAPNIK_INTERFACE_TYPE))
60 typedef struct _MapnikInterfaceClass MapnikInterfaceClass;
61 typedef struct _MapnikInterface MapnikInterface;
63 GType mapnik_interface_get_type ();
64 struct _MapnikInterfaceClass
66 GObjectClass object_class;
69 static void mapnik_interface_class_init ( MapnikInterfaceClass *mic )
73 static void mapnik_interface_init ( MapnikInterface *mi )
77 struct _MapnikInterface {
80 gchar *copyright; // Cached Mapnik parameter to save looking it up each time
83 G_DEFINE_TYPE (MapnikInterface, mapnik_interface, G_TYPE_OBJECT)
85 // Can't change prj after init - but ATM only support drawing in Spherical Mercator
86 static mapnik::projection prj( mapnik::MAPNIK_GMERC_PROJ );
88 MapnikInterface* mapnik_interface_new ()
90 MapnikInterface* mi = MAPNIK_INTERFACE ( g_object_new ( MAPNIK_INTERFACE_TYPE, NULL ) );
91 mi->myMap = new mapnik::Map;
96 void mapnik_interface_free (MapnikInterface* mi)
99 g_free ( mi->copyright );
102 g_object_unref ( G_OBJECT(mi) );
106 * mapnik_interface_initialize:
108 void mapnik_interface_initialize (const char *plugins_dir, const char* font_dir, int font_dir_recurse)
110 g_debug ("using mapnik version %s", MAPNIK_VERSION_STRING );
113 #if MAPNIK_VERSION >= 200200
114 mapnik::datasource_cache::instance().register_datasources(plugins_dir);
116 mapnik::datasource_cache::instance()->register_datasources(plugins_dir);
119 if ( ! mapnik::freetype_engine::register_fonts(font_dir, font_dir_recurse ? true : false) )
120 g_warning ("%s: No fonts found", __FUNCTION__);
121 } catch (std::exception const& ex) {
122 g_warning ("An error occurred while initialising mapnik: %s", ex.what());
124 g_warning ("An unknown error occurred while initialising mapnik");
129 * caching this answer instead of looking it up each time
131 static void set_copyright ( MapnikInterface* mi )
134 g_free ( mi->copyright );
136 mapnik::parameters pmts = mi->myMap->get_extra_parameters();
137 for (mapnik::parameters::const_iterator ii = pmts.begin(); ii != pmts.end(); ii++) {
138 if ( ii->first == "attribution" || ii->first == "copyright" ) {
139 std::stringstream ss;
142 mi->copyright = g_strdup ( (gchar*)ss.str().c_str() );
147 #define VIK_SETTINGS_MAPNIK_BUFFER_SIZE "mapnik_buffer_size"
150 * mapnik_interface_load_map_file:
152 * Returns NULL on success otherwise a string about what went wrong.
153 * This string should be freed once it has been used.
155 gchar* mapnik_interface_load_map_file ( MapnikInterface* mi,
156 const gchar *filename,
161 if ( !mi ) return g_strdup ("Internal Error");
163 mi->myMap->remove_all(); // Support reloading
164 mapnik::load_map(*mi->myMap, filename);
166 mi->myMap->resize(width,height);
167 mi->myMap->set_srs ( mapnik::MAPNIK_GMERC_PROJ ); // ONLY WEB MERCATOR output supported ATM
169 // IIRC This size is the number of pixels outside the tile to be considered so stuff is shown (i.e. particularly labels)
170 // Only set buffer size if the buffer size isn't explicitly set in the mapnik stylesheet.
171 // Alternatively render a bigger 'virtual' tile and then only use the appropriate subset
172 if (mi->myMap->buffer_size() == 0) {
173 gint buffer_size = (width+height/4); // e.g. 128 for a 256x256 image.
175 if ( a_settings_get_integer ( VIK_SETTINGS_MAPNIK_BUFFER_SIZE, &tmp ) )
178 mi->myMap->set_buffer_size(buffer_size);
181 set_copyright ( mi );
183 g_debug ("%s layers: %d", __FUNCTION__, mi->myMap->layer_count() );
184 } catch (std::exception const& ex) {
185 msg = g_strdup ( ex.what() );
187 msg = g_strdup ("unknown error");
194 * mapnik_interface_render:
196 * Returns a #GdkPixbuf of the specified area. #GdkPixbuf may be NULL
198 GdkPixbuf* mapnik_interface_render ( MapnikInterface* mi, double lat_tl, double lon_tl, double lat_br, double lon_br )
200 if ( !mi ) return NULL;
202 // Copy main object to local map variable
203 // This enables rendering to work when this function is called from different threads
204 mapnik::Map myMap(*mi->myMap);
206 // Note prj & bbox want stuff in lon,lat order!
212 // Convert into projection coordinates for bbox
213 prj.forward(p0x, p0y);
214 prj.forward(p1x, p1y);
216 GdkPixbuf *pixbuf = NULL;
218 unsigned width = myMap.width();
219 unsigned height = myMap.height();
220 mapnik::image_32 image(width,height);
221 mapnik::box2d<double> bbox(p0x, p0y, p1x, p1y);
222 myMap.zoom_to_box(bbox);
223 // FUTURE: option to use cairo / grid renderers?
224 mapnik::agg_renderer<mapnik::image_32> render(myMap,image);
227 if ( image.painted() ) {
228 unsigned char *ImageRawDataPtr = (unsigned char *) g_malloc(width * 4 * height);
229 if (!ImageRawDataPtr)
231 memcpy(ImageRawDataPtr, image.raw_data(), width * height * 4);
232 pixbuf = gdk_pixbuf_new_from_data(ImageRawDataPtr, GDK_COLORSPACE_RGB, TRUE, 8, width, height, width * 4, NULL, NULL);
235 g_warning ("%s not rendered", __FUNCTION__ );
237 catch (const std::exception & ex) {
238 g_warning ("An error occurred while rendering: %s", ex.what());
240 g_warning ("An unknown error occurred while rendering");
247 * Copyright/Attribution information about the Map - string maybe NULL
249 * Free returned string after use
251 gchar* mapnik_interface_get_copyright ( MapnikInterface* mi )
253 if ( !mi ) return NULL;
254 return mi->copyright;
258 * 'Parameter' information about the Map configuration
260 * Free every string element and the returned GArray itself after use
262 GArray* mapnik_interface_get_parameters ( MapnikInterface* mi )
264 GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
266 mapnik::parameters pmts = mi->myMap->get_extra_parameters();
267 for (mapnik::parameters::const_iterator ii = pmts.begin(); ii != pmts.end(); ii++) {
268 // Dodgy hacking to avoid using boost or mapnik::utils visitor stuff (not available in mapnik 2.2)
269 // Simply want the strings of each parameter so we can display something...
270 std::stringstream ss;
271 ss << ii->first << ": " << ii->second;
272 // Copy - otherwise ss goes output scope and junk memory would be referenced.
273 gchar *str2 = g_strdup ( (gchar*)ss.str().c_str() );
274 g_array_append_val ( array, str2 );
281 * General information about Mapnik
283 * Free the returned string after use
285 gchar * mapnik_interface_about ( void )
287 // Normally about 10 plugins so list them all
288 #if MAPNIK_VERSION >= 200200
289 std::vector<std::string> plugins = mapnik::datasource_cache::instance().plugin_names();
291 std::vector<std::string> plugins = mapnik::datasource_cache::instance()->plugin_names();
294 for (int nn = 0; nn < plugins.size(); nn++ )
295 str += plugins[nn] + ',';
297 // NB Can have a couple hundred fonts loaded when using system directories
298 // So ATM don't list them all - otherwise need better GUI feedback display.
299 gchar *msg = g_strdup_printf ( _("%s %s\nPlugins=%sFonts loaded=%d"),
301 MAPNIK_VERSION_STRING,
303 mapnik::freetype_engine::face_names().size() );