]> git.street.me.uk Git - andy/viking.git/blame - src/mapnik_interface.cpp
Fix spelling in gpsd retry warning print out
[andy/viking.git] / src / mapnik_interface.cpp
CommitLineData
5fa4fe86
RN
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2/*
3 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 *
5 * Copyright (C) 2015, Rob Norris <rw_norris@hotmail.com>
6 *
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.
11 *
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.
16 *
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
20 *
21 */
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>
616812b7
RN
27#include <mapnik/projection.hpp>
28#if MAPNIK_VERSION < 300000
5fa4fe86 29#include <mapnik/graphics.hpp>
616812b7
RN
30#else
31#include <mapnik/value.hpp>
32#endif
5fa4fe86
RN
33#include <mapnik/image_util.hpp>
34
35#include <exception>
36#include <iostream>
37#include <memory>
38#include <string>
39#include <stdlib.h>
40
41#include <glib.h>
42#include <glib/gstdio.h>
c064c64c 43#include <glib/gi18n.h>
5fa4fe86
RN
44
45#include "mapnik_interface.h"
46#include "globals.h"
54a451e9 47#include "settings.h"
5fa4fe86
RN
48
49#if MAPNIK_VERSION < 200000
50#include <mapnik/envelope.hpp>
51#define image_32 Image32
52#define image_data_32 ImageData32
53#define box2d Envelope
54#define zoom_to_box zoomToBox
55#else
56#include <mapnik/box2d.hpp>
616812b7
RN
57#if MAPNIK_VERSION >= 300000
58// In Mapnik3 'image_32' has changed names once again
59#define image_32 image_rgba8
60#define raw_data data
61#endif
5fa4fe86
RN
62#endif
63
0658c2f1
RN
64#define MAPNIK_INTERFACE_TYPE (mapnik_interface_get_type ())
65#define MAPNIK_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAPNIK_INTERFACE_TYPE, MapnikInterface))
66#define MAPNIK_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAPNIK_INTERFACE_TYPE, MapnikInterfaceClass))
67#define IS_MAPNIK_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAPNIK_INTERFACE_TYPE))
68#define IS_MAPNIK_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAPNIK_INTERFACE_TYPE))
69
70typedef struct _MapnikInterfaceClass MapnikInterfaceClass;
71typedef struct _MapnikInterface MapnikInterface;
72
73GType mapnik_interface_get_type ();
74struct _MapnikInterfaceClass
75{
76 GObjectClass object_class;
77};
78
79static void mapnik_interface_class_init ( MapnikInterfaceClass *mic )
80{
81}
82
83static void mapnik_interface_init ( MapnikInterface *mi )
84{
85}
86
87struct _MapnikInterface {
88 GObject obj;
89 mapnik::Map *myMap;
465559f1 90 gchar *copyright; // Cached Mapnik parameter to save looking it up each time
0658c2f1
RN
91};
92
93G_DEFINE_TYPE (MapnikInterface, mapnik_interface, G_TYPE_OBJECT)
5fa4fe86
RN
94
95// Can't change prj after init - but ATM only support drawing in Spherical Mercator
96static mapnik::projection prj( mapnik::MAPNIK_GMERC_PROJ );
97
0658c2f1
RN
98MapnikInterface* mapnik_interface_new ()
99{
100 MapnikInterface* mi = MAPNIK_INTERFACE ( g_object_new ( MAPNIK_INTERFACE_TYPE, NULL ) );
101 mi->myMap = new mapnik::Map;
465559f1 102 mi->copyright = NULL;
0658c2f1
RN
103 return mi;
104}
105
106void mapnik_interface_free (MapnikInterface* mi)
107{
465559f1
RN
108 if ( mi ) {
109 g_free ( mi->copyright );
0658c2f1 110 delete mi->myMap;
465559f1 111 }
0658c2f1
RN
112 g_object_unref ( G_OBJECT(mi) );
113}
114
115/**
116 * mapnik_interface_initialize:
117 */
118void mapnik_interface_initialize (const char *plugins_dir, const char* font_dir, int font_dir_recurse)
5fa4fe86
RN
119{
120 g_debug ("using mapnik version %s", MAPNIK_VERSION_STRING );
121 try {
122 if ( plugins_dir )
123#if MAPNIK_VERSION >= 200200
124 mapnik::datasource_cache::instance().register_datasources(plugins_dir);
5fa4fe86
RN
125#else
126 mapnik::datasource_cache::instance()->register_datasources(plugins_dir);
127#endif
128 if ( font_dir )
129 if ( ! mapnik::freetype_engine::register_fonts(font_dir, font_dir_recurse ? true : false) )
130 g_warning ("%s: No fonts found", __FUNCTION__);
5fa4fe86
RN
131 } catch (std::exception const& ex) {
132 g_warning ("An error occurred while initialising mapnik: %s", ex.what());
133 } catch (...) {
134 g_warning ("An unknown error occurred while initialising mapnik");
135 }
136}
137
465559f1
RN
138/**
139 * caching this answer instead of looking it up each time
140 */
141static void set_copyright ( MapnikInterface* mi )
142{
616812b7
RN
143 g_free ( mi->copyright );
144 mi->copyright = NULL;
465559f1
RN
145
146 mapnik::parameters pmts = mi->myMap->get_extra_parameters();
616812b7 147#if MAPNIK_VERSION < 300000
465559f1
RN
148 for (mapnik::parameters::const_iterator ii = pmts.begin(); ii != pmts.end(); ii++) {
149 if ( ii->first == "attribution" || ii->first == "copyright" ) {
150 std::stringstream ss;
151 ss << ii->second;
152 // Copy it
153 mi->copyright = g_strdup ( (gchar*)ss.str().c_str() );
616812b7 154 break;
465559f1
RN
155 }
156 }
616812b7
RN
157#else
158 if ( pmts.get<std::string>("attribution") )
159 mi->copyright = g_strdup ( (*pmts.get<std::string>("attribution")).c_str() );
160
161 if ( !mi->copyright )
162 if ( pmts.get<std::string>("copyright") )
163 mi->copyright = g_strdup ( (*pmts.get<std::string>("copyright")).c_str() );
164#endif
465559f1
RN
165}
166
54a451e9
RN
167#define VIK_SETTINGS_MAPNIK_BUFFER_SIZE "mapnik_buffer_size"
168
5fa4fe86
RN
169/**
170 * mapnik_interface_load_map_file:
171 *
76bb97a4
RN
172 * Returns NULL on success otherwise a string about what went wrong.
173 * This string should be freed once it has been used.
5fa4fe86 174 */
76bb97a4
RN
175gchar* mapnik_interface_load_map_file ( MapnikInterface* mi,
176 const gchar *filename,
177 guint width,
178 guint height )
5fa4fe86 179{
76bb97a4
RN
180 gchar *msg = NULL;
181 if ( !mi ) return g_strdup ("Internal Error");
5fa4fe86 182 try {
0658c2f1
RN
183 mi->myMap->remove_all(); // Support reloading
184 mapnik::load_map(*mi->myMap, filename);
5fa4fe86 185
0658c2f1
RN
186 mi->myMap->resize(width,height);
187 mi->myMap->set_srs ( mapnik::MAPNIK_GMERC_PROJ ); // ONLY WEB MERCATOR output supported ATM
5fa4fe86
RN
188
189 // IIRC This size is the number of pixels outside the tile to be considered so stuff is shown (i.e. particularly labels)
190 // Only set buffer size if the buffer size isn't explicitly set in the mapnik stylesheet.
191 // Alternatively render a bigger 'virtual' tile and then only use the appropriate subset
0658c2f1 192 if (mi->myMap->buffer_size() == 0) {
54a451e9
RN
193 gint buffer_size = (width+height/4); // e.g. 128 for a 256x256 image.
194 gint tmp;
195 if ( a_settings_get_integer ( VIK_SETTINGS_MAPNIK_BUFFER_SIZE, &tmp ) )
196 buffer_size = tmp;
197
198 mi->myMap->set_buffer_size(buffer_size);
5fa4fe86
RN
199 }
200
465559f1
RN
201 set_copyright ( mi );
202
304c1942 203 g_debug ("%s layers: %d", __FUNCTION__, (guint)mi->myMap->layer_count() );
5fa4fe86 204 } catch (std::exception const& ex) {
76bb97a4 205 msg = g_strdup ( ex.what() );
5fa4fe86 206 } catch (...) {
76bb97a4 207 msg = g_strdup ("unknown error");
5fa4fe86 208 }
76bb97a4 209 return msg;
5fa4fe86
RN
210}
211
5fa4fe86
RN
212
213/**
214 * mapnik_interface_render:
215 *
216 * Returns a #GdkPixbuf of the specified area. #GdkPixbuf may be NULL
217 */
0658c2f1 218GdkPixbuf* mapnik_interface_render ( MapnikInterface* mi, double lat_tl, double lon_tl, double lat_br, double lon_br )
5fa4fe86 219{
0658c2f1
RN
220 if ( !mi ) return NULL;
221
892a9205
RN
222 // Copy main object to local map variable
223 // This enables rendering to work when this function is called from different threads
224 mapnik::Map myMap(*mi->myMap);
225
5fa4fe86
RN
226 // Note prj & bbox want stuff in lon,lat order!
227 double p0x = lon_tl;
228 double p0y = lat_tl;
229 double p1x = lon_br;
230 double p1y = lat_br;
231
232 // Convert into projection coordinates for bbox
233 prj.forward(p0x, p0y);
234 prj.forward(p1x, p1y);
235
236 GdkPixbuf *pixbuf = NULL;
237 try {
892a9205
RN
238 unsigned width = myMap.width();
239 unsigned height = myMap.height();
5fa4fe86
RN
240 mapnik::image_32 image(width,height);
241 mapnik::box2d<double> bbox(p0x, p0y, p1x, p1y);
892a9205 242 myMap.zoom_to_box(bbox);
5fa4fe86 243 // FUTURE: option to use cairo / grid renderers?
892a9205 244 mapnik::agg_renderer<mapnik::image_32> render(myMap,image);
5fa4fe86
RN
245 render.apply();
246
247 if ( image.painted() ) {
53991e79 248 unsigned char *ImageRawDataPtr = (unsigned char *) g_malloc(width * 4 * height);
5fa4fe86
RN
249 if (!ImageRawDataPtr)
250 return NULL;
53991e79
MH
251 memcpy(ImageRawDataPtr, image.raw_data(), width * height * 4);
252 pixbuf = gdk_pixbuf_new_from_data(ImageRawDataPtr, GDK_COLORSPACE_RGB, TRUE, 8, width, height, width * 4, NULL, NULL);
5fa4fe86
RN
253 }
254 else
255 g_warning ("%s not rendered", __FUNCTION__ );
256 }
257 catch (const std::exception & ex) {
258 g_warning ("An error occurred while rendering: %s", ex.what());
259 } catch (...) {
260 g_warning ("An unknown error occurred while rendering");
261 }
262
263 return pixbuf;
264}
c064c64c 265
465559f1
RN
266/**
267 * Copyright/Attribution information about the Map - string maybe NULL
268 *
269 * Free returned string after use
270 */
271gchar* mapnik_interface_get_copyright ( MapnikInterface* mi )
272{
273 if ( !mi ) return NULL;
274 return mi->copyright;
275}
276
581feeec
RN
277/**
278 * 'Parameter' information about the Map configuration
279 *
280 * Free every string element and the returned GArray itself after use
281 */
282GArray* mapnik_interface_get_parameters ( MapnikInterface* mi )
283{
284 GArray *array = g_array_new (FALSE, TRUE, sizeof(gchar*));
285
286 mapnik::parameters pmts = mi->myMap->get_extra_parameters();
616812b7
RN
287 // Simply want the strings of each parameter so we can display something...
288#if MAPNIK_VERSION < 300000
289 for (mapnik::parameters::const_iterator pmt = pmts.begin(); pmt != pmts.end(); pmt++) {
581feeec 290 std::stringstream ss;
616812b7
RN
291 ss << pmt->first << ": " << pmt->second;
292#else
293 for (auto const& pmt : pmts) {
294 std::stringstream ss;
295 ss << pmt.first << ": " << *(pmts.get<std::string>(pmt.first,"empty"));
296#endif
581feeec
RN
297 // Copy - otherwise ss goes output scope and junk memory would be referenced.
298 gchar *str2 = g_strdup ( (gchar*)ss.str().c_str() );
299 g_array_append_val ( array, str2 );
300 }
301
302 return array;
303}
304
c064c64c
RN
305/**
306 * General information about Mapnik
307 *
308 * Free the returned string after use
309 */
310gchar * mapnik_interface_about ( void )
311{
312 // Normally about 10 plugins so list them all
313#if MAPNIK_VERSION >= 200200
314 std::vector<std::string> plugins = mapnik::datasource_cache::instance().plugin_names();
315#else
316 std::vector<std::string> plugins = mapnik::datasource_cache::instance()->plugin_names();
317#endif
318 std::string str;
ce7af119 319 for (uint nn = 0; nn < plugins.size(); nn++ )
c064c64c
RN
320 str += plugins[nn] + ',';
321 str += '\n';
322 // NB Can have a couple hundred fonts loaded when using system directories
323 // So ATM don't list them all - otherwise need better GUI feedback display.
324 gchar *msg = g_strdup_printf ( _("%s %s\nPlugins=%sFonts loaded=%d"),
325 _("Mapnik"),
326 MAPNIK_VERSION_STRING,
327 str.c_str(),
304c1942 328 (guint)mapnik::freetype_engine::face_names().size() );
c064c64c
RN
329 return msg;
330}