+#define MAPNIK_LAYER_FILE_CACHE_LAYOUT "%s"G_DIR_SEPARATOR_S"%d"G_DIR_SEPARATOR_S"%d"G_DIR_SEPARATOR_S"%d.png"
+
+// Free returned string after use
+static gchar *get_filename ( gchar *dir, guint x, guint y, guint z)
+{
+ return g_strdup_printf ( MAPNIK_LAYER_FILE_CACHE_LAYOUT, dir, (17-z), x, y );
+}
+
+static void possibly_save_pixbuf ( VikMapnikLayer *vml, GdkPixbuf *pixbuf, MapCoord *ulm )
+{
+ if ( vml->use_file_cache ) {
+ if ( vml->file_cache_dir ) {
+ GError *error = NULL;
+ gchar *filename = get_filename ( vml->file_cache_dir, ulm->x, ulm->y, ulm->scale );
+
+ gchar *dir = g_path_get_dirname ( filename );
+ if ( !g_file_test ( filename, G_FILE_TEST_EXISTS ) )
+ g_mkdir_with_parents ( dir , 0777 );
+ g_free ( dir );
+
+ if ( !gdk_pixbuf_save (pixbuf, filename, "png", &error, NULL ) ) {
+ g_warning ("%s: %s", __FUNCTION__, error->message );
+ g_error_free (error);
+ }
+ g_free (filename);
+ }
+ }
+}
+
+typedef struct
+{
+ VikMapnikLayer *vml;
+ VikCoord *ul;
+ VikCoord *br;
+ MapCoord *ulmc;
+ const gchar* request;
+} RenderInfo;
+
+/**
+ * render:
+ *
+ * Common render function which can run in separate thread
+ */
+static void render ( VikMapnikLayer *vml, VikCoord *ul, VikCoord *br, MapCoord *ulm )
+{
+ 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 ();
+ 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 );
+ }
+ possibly_save_pixbuf ( vml, pixbuf, ulm );
+
+ // 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, (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 );
+}
+
+static void render_info_free ( RenderInfo *data )
+{
+ g_free ( data->ul );
+ g_free ( data->br );
+ g_free ( data->ulmc );
+ // NB No need to free the request/key - as this is freed by the hash table destructor
+ g_free ( data );
+}
+
+static void background ( RenderInfo *data, gpointer threaddata )
+{
+ int res = a_background_thread_progress ( threaddata, 0 );
+ if (res == 0) {
+ render ( data->vml, data->ul, data->br, data->ulmc );
+ }
+
+ g_mutex_lock(tp_mutex);
+ g_hash_table_remove (requests, data->request);
+ g_mutex_unlock(tp_mutex);
+
+ if (res == 0)
+ vik_layer_emit_update ( VIK_LAYER(data->vml) ); // NB update display from background
+}
+
+static void render_cancel_cleanup (RenderInfo *data)
+{
+ // Anything?
+}
+
+#define REQUEST_HASHKEY_FORMAT "%d-%d-%d-%d-%d"
+
+/**
+ * Thread
+ */
+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;
+ gchar *request = g_strdup_printf ( REQUEST_HASHKEY_FORMAT, x, y, z, zoom, nn );
+
+ g_mutex_lock(tp_mutex);
+
+ if ( g_hash_table_lookup_extended (requests, request, NULL, NULL ) ) {
+ g_free ( request );
+ g_mutex_unlock (tp_mutex);
+ return;
+ }
+
+ RenderInfo *ri = g_malloc ( sizeof(RenderInfo) );
+ ri->vml = vml;
+ ri->ul = g_malloc ( sizeof(VikCoord) );
+ ri->br = g_malloc ( sizeof(VikCoord) );
+ ri->ulmc = g_malloc ( sizeof(MapCoord) );
+ memcpy(ri->ul, ul, sizeof(VikCoord));
+ memcpy(ri->br, br, sizeof(VikCoord));
+ memcpy(ri->ulmc, mul, sizeof(MapCoord));
+ ri->request = request;
+
+ g_hash_table_insert ( requests, request, NULL );
+
+ g_mutex_unlock (tp_mutex);
+
+ 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_MAPNIK,
+ VIK_GTK_WINDOW_FROM_LAYER(vml),
+ description,
+ (vik_thr_func) background,
+ ri,
+ (vik_thr_free_func) render_info_free,
+ (vik_thr_free_func) render_cancel_cleanup,
+ 1 );
+ g_free ( description );
+}
+
+/**
+ * load_pixbuf:
+ */
+static GdkPixbuf *load_pixbuf ( VikMapnikLayer *vml, MapCoord *ulm, MapCoord *brm, gboolean *rerender )
+{
+ *rerender = FALSE;
+ GdkPixbuf *pixbuf = NULL;
+ gchar *filename = get_filename ( vml->file_cache_dir, ulm->x, ulm->y, ulm->scale );
+
+ GStatBuf gsb;
+ if ( g_stat ( filename, &gsb ) == 0 ) {
+ // Get from disk
+ GError *error = NULL;
+ pixbuf = gdk_pixbuf_new_from_file ( filename, &error );
+ if ( error ) {
+ g_warning ("%s: %s", __FUNCTION__, error->message );
+ g_error_free ( error );
+ }
+ else {
+ if ( vml->alpha < 255 )
+ pixbuf = ui_pixbuf_set_alpha ( pixbuf, vml->alpha );
+ 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 ) {
+ *rerender = TRUE;
+ }
+ }
+ g_free ( filename );
+
+ return pixbuf;
+}
+