[libchamplain] New map and cache loading infrastructure



commit c8508f5e1dcdf44e5353ebd2474034c830ff8b93
Author: JiÅ?í Techet <techet gmail com>
Date:   Mon Jan 18 00:51:10 2010 +0100

    New map and cache loading infrastructure
    
    New infrastructure for mutually cooperating and exchangeable caches
    and map sources.
    
    Signed-off-by: JiÅ?í Techet <techet gmail com>

 champlain/Makefile.am                     |   33 +-
 champlain/champlain-cache.c               |  870 ----------------------------
 champlain/champlain-cache.h               |   94 ---
 champlain/champlain-defines.h             |    4 +-
 champlain/champlain-error-tile-source.c   |  130 +++++
 champlain/champlain-error-tile-source.h   |   57 ++
 champlain/champlain-file-cache.c          |  897 +++++++++++++++++++++++++++++
 champlain/champlain-file-cache.h          |   68 +++
 champlain/champlain-map-data-source.c     |    2 +-
 champlain/champlain-map-data-source.h     |    4 +-
 champlain/champlain-map-source-chain.c    |  288 +++++++++
 champlain/champlain-map-source-chain.h    |   61 ++
 champlain/champlain-map-source-desc.h     |    2 +-
 champlain/champlain-map-source-factory.c  |   46 ++-
 champlain/champlain-map-source.c          |  835 ++++++---------------------
 champlain/champlain-map-source.h          |  107 ++--
 champlain/champlain-memphis-map-source.c  |  651 +++++++++------------
 champlain/champlain-memphis-map-source.h  |   27 +-
 champlain/champlain-network-map-source.c  |  642 ---------------------
 champlain/champlain-network-map-source.h  |   77 ---
 champlain/champlain-network-tile-source.c |  570 ++++++++++++++++++
 champlain/champlain-network-tile-source.h |   78 +++
 champlain/champlain-private.h             |    3 -
 champlain/champlain-tile-cache.c          |  273 +++++++++
 champlain/champlain-tile-cache.h          |   74 +++
 champlain/champlain-tile-source.c         |  510 ++++++++++++++++
 champlain/champlain-tile-source.h         |   86 +++
 champlain/champlain-tile.c                |  132 +-----
 champlain/champlain-tile.h                |    6 -
 champlain/champlain-view.c                |   33 +-
 champlain/champlain.h                     |    8 +-
 demos/launcher-gtk.c                      |   26 +-
 demos/local-rendering.c                   |  171 +++---
 33 files changed, 3836 insertions(+), 3029 deletions(-)
---
diff --git a/champlain/Makefile.am b/champlain/Makefile.am
index 6495b7e..30e049d 100644
--- a/champlain/Makefile.am
+++ b/champlain/Makefile.am
@@ -27,7 +27,12 @@ libchamplain_headers = \
 	champlain-enum-types.h		\
 	champlain-tile.h		\
 	champlain-map-source.h		\
-	champlain-network-map-source.h	\
+	champlain-map-source-chain.h	\
+	champlain-tile-source.h		\
+	champlain-tile-cache.h		\
+	champlain-network-tile-source.h	\
+	champlain-error-tile-source.h	\
+	champlain-file-cache.h		\
 	champlain-map-source-factory.h	\
 	champlain-map-source-desc.h	\
 	champlain-polygon.h		\
@@ -51,11 +56,15 @@ libchamplain_0_5_la_SOURCES = \
 	champlain-zoom-level.c 		\
 	champlain-tile.c		\
 	champlain-map-source.c		\
-	champlain-network-map-source.c	\
+	champlain-map-source-chain.c	\
+	champlain-tile-source.c		\
+	champlain-tile-cache.c		\
+	champlain-network-tile-source.c	\
+	champlain-error-tile-source.c	\
+	champlain-file-cache.c		\
 	champlain-map-source-factory.c	\
 	champlain-map-source-desc.c	\
 	champlain-point.c		\
-	champlain-cache.c		\
 	champlain-polygon.c	\
 	champlain-memphis-map-source.c  \
 	champlain-map-data-source.c \
@@ -75,10 +84,15 @@ noinst_HEADERS = \
 	champlain-tile.h 		\
 	champlain-enum-types.h		\
 	champlain-map-source.h		\
-	champlain-network-map-source.h	\
+	champlain-map-source-chain.h	\
+	champlain-tile-source.h		\
+	champlain-tile-cache.h		\
+	champlain-network-tile-source.h	\
+	champlain-error-tile-source.h	\
+	champlain-file-cache.h		\
+	champlain-enum-types.h		\
 	champlain-map-source-factory.h	\
 	champlain-map-source-desc.h	\
-	champlain-cache.h		\
 	champlain-polygon.h		\
 	champlain-version.h
 
@@ -91,15 +105,20 @@ libchamplain_include_HEADERS = \
 	champlain-layer.h 		\
 	champlain-selection-layer.h 	\
 	champlain-map-source.h		\
-	champlain-network-map-source.h	\
+	champlain-map-source-chain.h	\
+	champlain-tile-source.h		\
+	champlain-tile-cache.h		\
+	champlain-network-tile-source.h	\
+	champlain-error-tile-source.h	\
+	champlain-file-cache.h		\
 	champlain-map-source-factory.h	\
 	champlain-map-source-desc.h	\
 	champlain-tile.h		\
 	champlain-zoom-level.h		\
 	champlain-base-marker.h		\
 	champlain-marker.h		\
-	champlain-cache.h		\
 	champlain-polygon.h		\
+	champlain-version.h
 	champlain-version.h		\
 	champlain-memphis-map-source.h  \
 	champlain-map-data-source.h \
diff --git a/champlain/champlain-defines.h b/champlain/champlain-defines.h
index 8a279a5..90755f9 100644
--- a/champlain/champlain-defines.h
+++ b/champlain/champlain-defines.h
@@ -29,6 +29,8 @@
 typedef struct _ChamplainView ChamplainView;
 typedef struct _ChamplainViewClass ChamplainViewClass;
 
+typedef struct _ChamplainMapSource ChamplainMapSource;
+
 #define CHAMPLAIN_PARAM_READABLE     \
         (G_PARAM_READABLE |     \
          G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
@@ -42,8 +44,6 @@ typedef enum
   CHAMPLAIN_STATE_NONE,
   CHAMPLAIN_STATE_INIT,
   CHAMPLAIN_STATE_LOADING,
-  CHAMPLAIN_STATE_VALIDATING_CACHE, /* Use by tiles when there is cache,
-                                       but it is being validated */
   CHAMPLAIN_STATE_DONE
 } ChamplainState;
 
diff --git a/champlain/champlain-error-tile-source.c b/champlain/champlain-error-tile-source.c
new file mode 100644
index 0000000..918e90a
--- /dev/null
+++ b/champlain/champlain-error-tile-source.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2008-2009 Pierre-Luc Beaudoin <pierre-luc pierlux com>
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "champlain-error-tile-source.h"
+
+G_DEFINE_TYPE (ChamplainErrorTileSource, champlain_error_tile_source, CHAMPLAIN_TYPE_TILE_SOURCE);
+
+#define GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE((obj), CHAMPLAIN_TYPE_ERROR_TILE_SOURCE, ChamplainErrorTileSourcePrivate))
+
+typedef struct _ChamplainErrorTileSourcePrivate ChamplainErrorTileSourcePrivate;
+
+struct _ChamplainErrorTileSourcePrivate
+{
+  ClutterActor *error_actor;
+};
+
+static void fill_tile (ChamplainMapSource *map_source, ChamplainTile *tile);
+
+static void
+champlain_error_tile_source_dispose (GObject *object)
+{
+  G_OBJECT_CLASS (champlain_error_tile_source_parent_class)->dispose (object);
+}
+
+static void
+champlain_error_tile_source_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (champlain_error_tile_source_parent_class)->finalize (object);
+}
+
+static void
+champlain_error_tile_source_class_init (ChamplainErrorTileSourceClass *klass)
+{
+  ChamplainMapSourceClass *map_source_class = CHAMPLAIN_MAP_SOURCE_CLASS (klass);
+  GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (ChamplainErrorTileSourcePrivate));
+
+  object_class->finalize = champlain_error_tile_source_finalize;
+  object_class->dispose = champlain_error_tile_source_dispose;
+
+  map_source_class->fill_tile = fill_tile;
+}
+
+static void
+champlain_error_tile_source_init (ChamplainErrorTileSource *error_source)
+{
+  ChamplainErrorTileSourcePrivate *priv = GET_PRIVATE(error_source);
+  priv->error_actor = NULL;
+}
+
+ChamplainErrorTileSource* champlain_error_tile_source_new_full (guint tile_size)
+{
+  return g_object_new (CHAMPLAIN_TYPE_ERROR_TILE_SOURCE, "tile-size", tile_size, NULL);
+}
+
+static void
+fill_tile (ChamplainMapSource *map_source, ChamplainTile *tile)
+{
+  g_return_if_fail (CHAMPLAIN_IS_ERROR_TILE_SOURCE (map_source));
+  g_return_if_fail (CHAMPLAIN_IS_TILE (tile));
+
+  ChamplainErrorTileSourcePrivate *priv = GET_PRIVATE(map_source);
+  ClutterActor *clone;
+  guint size;
+
+  if (champlain_tile_get_content(tile))
+    /* cache is just validating tile - don't generate error tile in this case */
+    return;
+
+  size = champlain_map_source_get_tile_size (map_source);
+
+  if (!priv->error_actor)
+    {
+      cairo_t *cr;
+      cairo_pattern_t *pat;
+      ClutterActor *stage;
+
+      priv->error_actor = clutter_cairo_texture_new (size, size);
+      cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (priv->error_actor));
+      stage = clutter_stage_get_default ();
+
+      /* draw a linear gray to white pattern */
+      pat = cairo_pattern_create_linear (size / 2.0, 0.0,  size, size / 2.0);
+      cairo_pattern_add_color_stop_rgb (pat, 0, 0.686, 0.686, 0.686);
+      cairo_pattern_add_color_stop_rgb (pat, 1, 0.925, 0.925, 0.925);
+      cairo_set_source (cr, pat);
+      cairo_rectangle (cr, 0, 0, size, size);
+      cairo_fill (cr);
+
+      cairo_pattern_destroy (pat);
+
+      /* draw the red cross */
+      cairo_set_source_rgb (cr, 0.424, 0.078, 0.078);
+      cairo_set_line_width (cr, 14.0);
+      cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+      cairo_move_to (cr, 24, 24);
+      cairo_line_to (cr, 50, 50);
+      cairo_move_to (cr, 50, 24);
+      cairo_line_to (cr, 24, 50);
+      cairo_stroke (cr);
+
+      cairo_destroy (cr);
+
+      clutter_container_add_actor (CLUTTER_CONTAINER (stage), priv->error_actor);
+      clutter_actor_hide (priv->error_actor);
+    }
+
+  clone = clutter_clone_new (priv->error_actor);
+
+  champlain_tile_set_content (tile, clone, TRUE);
+  champlain_tile_set_size (tile, size);
+  champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
+}
diff --git a/champlain/champlain-error-tile-source.h b/champlain/champlain-error-tile-source.h
new file mode 100644
index 0000000..aaea565
--- /dev/null
+++ b/champlain/champlain-error-tile-source.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008-2009 Pierre-Luc Beaudoin <pierre-luc pierlux com>
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#if !defined (__CHAMPLAIN_CHAMPLAIN_H_INSIDE__) && !defined (CHAMPLAIN_COMPILATION)
+#error "Only <champlain/champlain.h> can be included directly."
+#endif
+
+#ifndef _CHAMPLAIN_ERROR_TILE_SOURCE_H_
+#define _CHAMPLAIN_ERROR_TILE_SOURCE_H_
+
+#include <champlain/champlain-tile-source.h>
+
+G_BEGIN_DECLS
+
+#define CHAMPLAIN_TYPE_ERROR_TILE_SOURCE             (champlain_error_tile_source_get_type ())
+#define CHAMPLAIN_ERROR_TILE_SOURCE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHAMPLAIN_TYPE_ERROR_TILE_SOURCE, ChamplainErrorTileSource))
+#define CHAMPLAIN_ERROR_TILE_SOURCE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), CHAMPLAIN_TYPE_ERROR_TILE_SOURCE, ChamplainErrorTileSourceClass))
+#define CHAMPLAIN_IS_ERROR_TILE_SOURCE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CHAMPLAIN_TYPE_ERROR_TILE_SOURCE))
+#define CHAMPLAIN_IS_ERROR_TILE_SOURCE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), CHAMPLAIN_TYPE_ERROR_TILE_SOURCE))
+#define CHAMPLAIN_ERROR_TILE_SOURCE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), CHAMPLAIN_TYPE_ERROR_TILE_SOURCE, ChamplainErrorTileSourceClass))
+
+typedef struct _ChamplainErrorTileSource ChamplainErrorTileSource;
+typedef struct _ChamplainErrorTileSourceClass ChamplainErrorTileSourceClass;
+
+struct _ChamplainErrorTileSource
+{
+  ChamplainTileSource parent_instance;
+};
+
+struct _ChamplainErrorTileSourceClass
+{
+  ChamplainTileSourceClass parent_class;
+};
+
+GType champlain_error_tile_source_get_type (void);
+
+ChamplainErrorTileSource* champlain_error_tile_source_new_full (guint tile_size);
+
+G_END_DECLS
+
+#endif /* _CHAMPLAIN_ERROR_TILE_SOURCE_H_ */
diff --git a/champlain/champlain-file-cache.c b/champlain/champlain-file-cache.c
new file mode 100644
index 0000000..b18c916
--- /dev/null
+++ b/champlain/champlain-file-cache.c
@@ -0,0 +1,897 @@
+/*
+ * Copyright (C) 2009 Pierre-Luc Beaudoin <pierre-luc pierlux com>
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#define DEBUG_FLAG CHAMPLAIN_DEBUG_CACHE
+#include "champlain-debug.h"
+
+#include "champlain-file-cache.h"
+
+#include <sqlite3.h>
+#include <errno.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <string.h>
+#include <stdlib.h>
+
+G_DEFINE_TYPE (ChamplainFileCache, champlain_file_cache, CHAMPLAIN_TYPE_TILE_CACHE);
+
+#define GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE((obj), CHAMPLAIN_TYPE_FILE_CACHE, ChamplainFileCachePrivate))
+
+enum
+{
+  PROP_0,
+  PROP_SIZE_LIMIT,
+  PROP_CACHE_DIR
+};
+
+typedef struct _ChamplainFileCachePrivate ChamplainFileCachePrivate;
+
+struct _ChamplainFileCachePrivate
+{
+  guint size_limit;
+  gchar *cache_dir;
+
+  gchar *real_cache_dir;
+  sqlite3 *db;
+  sqlite3_stmt *stmt_select;
+  sqlite3_stmt *stmt_update;
+};
+
+static void finalize_sql(ChamplainFileCache *file_cache);
+static void delete_temp_cache (ChamplainFileCache *file_cache);
+static void init_cache  (ChamplainFileCache *file_cache);
+static gchar *get_filename (ChamplainFileCache *file_cache, ChamplainTile *tile);
+static gboolean tile_is_expired (ChamplainFileCache *file_cache, ChamplainTile *tile);
+static void delete_tile (ChamplainFileCache *file_cache, const gchar *filename);
+static void delete_dir_recursive (GFile *parent);
+
+static void fill_tile (ChamplainMapSource *map_source,
+                       ChamplainTile *tile);
+
+static void store_tile (ChamplainTileCache *tile_cache,
+                        ChamplainTile *tile,
+                        const gchar *contents,
+                        gsize size);
+static void refresh_tile_time (ChamplainTileCache *tile_cache, ChamplainTile *tile);
+static void on_tile_filled (ChamplainTileCache *tile_cache, ChamplainTile *tile);
+static void clean (ChamplainTileCache *tile_cache);
+
+static void
+champlain_file_cache_get_property (GObject *object,
+                                   guint property_id,
+                                   GValue *value,
+                                   GParamSpec *pspec)
+{
+  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE (object);
+
+  switch (property_id)
+    {
+    case PROP_SIZE_LIMIT:
+      g_value_set_uint (value, champlain_file_cache_get_size_limit (file_cache));
+      break;
+    case PROP_CACHE_DIR:
+      g_value_set_string (value, champlain_file_cache_get_cache_dir (file_cache));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+champlain_file_cache_set_property (GObject *object,
+                                   guint property_id,
+                                   const GValue *value,
+                                   GParamSpec *pspec)
+{
+  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE (object);
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (object);
+
+  switch (property_id)
+    {
+    case PROP_SIZE_LIMIT:
+      champlain_file_cache_set_size_limit (file_cache, g_value_get_uint (value));
+      break;
+    case PROP_CACHE_DIR:
+      priv->cache_dir = g_strdup (g_value_get_string (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+champlain_file_cache_dispose (GObject *object)
+{
+  G_OBJECT_CLASS (champlain_file_cache_parent_class)->dispose (object);
+}
+
+static void
+finalize_sql(ChamplainFileCache *file_cache)
+{
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
+  gint error;
+
+  if (priv->stmt_select)
+    {
+      sqlite3_finalize (priv->stmt_select);
+      priv->stmt_select = NULL;
+    }
+
+  if (priv->stmt_update)
+    {
+      sqlite3_finalize (priv->stmt_update);
+      priv->stmt_update = NULL;
+    }
+
+  if (priv->db)
+    {
+      error = sqlite3_close (priv->db);
+      if (error != SQLITE_OK)
+        DEBUG ("Sqlite returned error %d when closing cache.db", error);
+      priv->db = NULL;
+    }
+}
+
+static void
+delete_temp_cache (ChamplainFileCache *file_cache)
+{
+  ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(file_cache);
+  ChamplainFileCachePrivate *priv = GET_PRIVATE(file_cache);
+
+  if (!champlain_tile_cache_get_persistent(tile_cache) && priv->real_cache_dir)
+    {
+      GFile *file = NULL;
+
+      /* delete the directory contents */
+      file = g_file_new_for_path (priv->real_cache_dir);
+      delete_dir_recursive (file);
+
+      /* delete the directory itself */
+      if (!g_file_delete (file, NULL, NULL))
+        g_warning ("Failed to remove temporary cache main directory");
+
+      g_object_unref (file);
+    }
+}
+
+static void
+champlain_file_cache_finalize (GObject *object)
+{
+  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(object);
+  ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(file_cache);
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
+
+  finalize_sql (file_cache);
+
+  if (!champlain_tile_cache_get_persistent(tile_cache))
+    delete_temp_cache(file_cache);
+
+  g_free(priv->real_cache_dir);
+  g_free(priv->cache_dir);
+
+  G_OBJECT_CLASS (champlain_file_cache_parent_class)->finalize (object);
+}
+
+static void
+init_cache  (ChamplainFileCache *file_cache)
+{
+  ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(file_cache);
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
+  gchar *filename = NULL;
+  gchar *error_msg = NULL;
+  gint error;
+
+  /* If needed, create the cache's dirs */
+  if (priv->cache_dir)
+    {
+      if (g_mkdir_with_parents (priv->cache_dir, 0700) == -1 && errno != EEXIST)
+        {
+          g_warning ("Unable to create the image cache path '%s': %s",
+                     priv->cache_dir, g_strerror (errno));
+          return;
+        }
+    }
+
+  if (champlain_tile_cache_get_persistent(tile_cache))
+    priv->real_cache_dir = g_strdup(priv->cache_dir);
+  else
+    {
+      /* Create temporary directory for non-persistent caches */
+      gchar *tmplate = NULL;
+
+      if (priv->cache_dir)
+        tmplate = g_build_filename (priv->cache_dir, "champlain-XXXXXX", NULL);
+      else
+        tmplate = g_build_filename (g_get_tmp_dir (), "champlain-XXXXXX", NULL);
+
+      priv->real_cache_dir = mkdtemp(tmplate);
+
+      if (!priv->real_cache_dir)
+        {
+          g_warning ("Filed to create filename for temporary cache");
+          g_free(tmplate);
+        }
+    }
+
+  g_return_if_fail (priv->real_cache_dir);
+
+  filename = g_build_filename (priv->real_cache_dir,
+                               "cache.db", NULL);
+  error = sqlite3_open_v2 (filename, &priv->db,
+                           SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
+  g_free (filename);
+
+  if (error == SQLITE_ERROR)
+    {
+      DEBUG ("Sqlite returned error %d when opening cache.db", error);
+      return;
+    }
+
+  sqlite3_exec (priv->db,
+                "PRAGMA synchronous=OFF;"
+                "PRAGMA count_changes=OFF;",
+                NULL, NULL, &error_msg);
+  if (error_msg != NULL)
+    {
+      DEBUG ("Set PRAGMA: %s", error_msg);
+      sqlite3_free (error_msg);
+      return;
+    }
+
+  sqlite3_exec (priv->db,
+                "CREATE TABLE IF NOT EXISTS tiles ("
+                "filename TEXT PRIMARY KEY, "
+                "etag TEXT, "
+                "popularity INT DEFAULT 1, "
+                "size INT DEFAULT 0)",
+                NULL, NULL, &error_msg);
+  if (error_msg != NULL)
+    {
+      DEBUG ("Creating table 'tiles' failed: %s", error_msg);
+      sqlite3_free (error_msg);
+      return;
+    }
+
+  error = sqlite3_prepare_v2 (priv->db,
+                              "SELECT etag FROM tiles WHERE filename = ?", -1,
+                              &priv->stmt_select, NULL);
+  if (error != SQLITE_OK)
+    {
+      priv->stmt_select = NULL;
+      DEBUG ("Failed to prepare the select Etag statement, error:%d: %s",
+             error, sqlite3_errmsg (priv->db));
+      return;
+    }
+
+  error = sqlite3_prepare_v2 (priv->db,
+                              "UPDATE tiles SET popularity = popularity + 1 WHERE filename = ?", -1,
+                              &priv->stmt_update, NULL);
+  if (error != SQLITE_OK)
+    {
+      priv->stmt_update = NULL;
+      DEBUG ("Failed to prepare the update popularity statement, error: %s",
+             sqlite3_errmsg (priv->db));
+      return;
+    }
+
+  g_object_notify (G_OBJECT (file_cache), "cache-dir");
+}
+
+static void
+champlain_file_cache_constructed  (GObject *object)
+{
+  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(object);
+
+  init_cache(file_cache);
+
+  G_OBJECT_CLASS (champlain_file_cache_parent_class)->constructed (object);
+}
+
+static void
+champlain_file_cache_class_init (ChamplainFileCacheClass *klass)
+{
+  ChamplainMapSourceClass *map_source_class = CHAMPLAIN_MAP_SOURCE_CLASS (klass);
+  ChamplainTileCacheClass *tile_cache_class = CHAMPLAIN_TILE_CACHE_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (klass, sizeof (ChamplainFileCachePrivate));
+
+  object_class->finalize = champlain_file_cache_finalize;
+  object_class->dispose = champlain_file_cache_dispose;
+  object_class->get_property = champlain_file_cache_get_property;
+  object_class->set_property = champlain_file_cache_set_property;
+  object_class->constructed = champlain_file_cache_constructed;
+
+  pspec = g_param_spec_uint ("size-limit",
+                             "Size Limit",
+                             "The cache's size limit (Mb)",
+                             1,
+                             G_MAXINT,
+                             100000000,
+                             G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
+  g_object_class_install_property (object_class, PROP_SIZE_LIMIT, pspec);
+
+  pspec = g_param_spec_string ("cache-dir",
+                               "Cache Directory",
+                               "The directory of the cache",
+                               "",
+                               G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+  g_object_class_install_property (object_class, PROP_CACHE_DIR, pspec);
+
+  tile_cache_class->store_tile = store_tile;
+  tile_cache_class->refresh_tile_time = refresh_tile_time;
+  tile_cache_class->on_tile_filled = on_tile_filled;
+  tile_cache_class->clean = clean;
+
+  map_source_class->fill_tile = fill_tile;
+}
+
+static void
+champlain_file_cache_init (ChamplainFileCache *file_cache)
+{
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
+
+  priv->size_limit = 0;
+  priv->cache_dir = NULL;
+
+  priv->real_cache_dir = NULL;
+  priv->db = NULL;
+  priv->stmt_select = NULL;
+  priv->stmt_update = NULL;
+}
+
+ChamplainFileCache* champlain_file_cache_new_full (guint size_limit,
+    const gchar *cache_dir, gboolean persistent)
+{
+  ChamplainFileCache * cache;
+  cache = g_object_new (CHAMPLAIN_TYPE_FILE_CACHE, "size-limit", size_limit,
+                        "cache-dir", cache_dir, "persistent-cache", persistent, NULL);
+  return cache;
+}
+
+guint
+champlain_file_cache_get_size_limit (ChamplainFileCache *file_cache)
+{
+  g_return_val_if_fail(CHAMPLAIN_IS_FILE_CACHE (file_cache), 0);
+
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
+  return priv->size_limit;
+}
+
+const gchar *
+champlain_file_cache_get_cache_dir (ChamplainFileCache *file_cache)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache), NULL);
+
+  ChamplainFileCachePrivate *priv = GET_PRIVATE(file_cache);
+  return priv->cache_dir;
+}
+
+void
+champlain_file_cache_set_size_limit (ChamplainFileCache *file_cache,
+                                     guint size_limit)
+{
+  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache));
+
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
+
+  priv->size_limit = size_limit;
+  g_object_notify (G_OBJECT (file_cache), "size-limit");
+}
+
+static gchar *
+get_filename (ChamplainFileCache *file_cache, ChamplainTile *tile)
+{
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
+
+  g_return_val_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache), NULL);
+  g_return_val_if_fail(CHAMPLAIN_IS_TILE (tile), NULL);
+  g_return_val_if_fail(priv->real_cache_dir, NULL);
+
+  ChamplainMapSource* map_source = CHAMPLAIN_MAP_SOURCE(file_cache);
+
+  gchar *filename = g_strdup_printf ("%s" G_DIR_SEPARATOR_S
+                                     "%s" G_DIR_SEPARATOR_S
+                                     "%d" G_DIR_SEPARATOR_S
+                                     "%d" G_DIR_SEPARATOR_S "%d.png",
+                                     priv->real_cache_dir,
+                                     champlain_map_source_get_id (map_source),
+                                     champlain_tile_get_zoom_level (tile),
+                                     champlain_tile_get_x (tile),
+                                     champlain_tile_get_y (tile));
+  return filename;
+}
+
+static gboolean
+tile_is_expired (ChamplainFileCache *file_cache, ChamplainTile *tile)
+{
+  g_return_val_if_fail(CHAMPLAIN_FILE_CACHE (file_cache), FALSE);
+  g_return_val_if_fail(CHAMPLAIN_TILE (tile), FALSE);
+
+  GTimeVal now = {0, };
+  const GTimeVal *modified_time = champlain_tile_get_modified_time (tile);
+  gboolean validate_cache = TRUE;
+
+  if (modified_time)
+    {
+      g_get_current_time (&now);
+      g_time_val_add (&now, (-24ul * 60ul * 60ul * 1000ul * 1000ul * 7ul)); // Cache expires in 7 days
+      validate_cache = modified_time->tv_sec < now.tv_sec;
+    }
+
+  DEBUG ("%p is %s expired", tile, (validate_cache ? "": "not"));
+
+  return validate_cache;
+}
+
+static void
+fill_tile (ChamplainMapSource *map_source,
+           ChamplainTile *tile)
+{
+  g_return_if_fail(CHAMPLAIN_IS_FILE_CACHE (map_source));
+  g_return_if_fail(CHAMPLAIN_IS_TILE (tile));
+
+  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(map_source);
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
+  GFileInfo *info = NULL;
+  GFile *file = NULL;
+  GError *gerror = NULL;
+  ClutterActor *actor = NULL;
+  gchar *filename = NULL;
+  GTimeVal modified_time = {0,};
+
+  if (champlain_tile_get_content (tile))
+    /* Previous cache in the chain is validating its contents */
+    goto load_next;
+
+  filename = get_filename (file_cache, tile);
+  DEBUG ("fill of %s", filename);
+
+  /* Load the cached version */
+  actor = clutter_texture_new_from_file (filename, &gerror);
+  if (actor)
+    {
+      champlain_tile_set_content (tile, actor, FALSE);
+      champlain_tile_set_size (tile, champlain_map_source_get_tile_size(map_source));
+    }
+  else
+    {
+      DEBUG ("Failed to load tile %s, error: %s",
+             filename, gerror->message);
+      g_error_free (gerror);
+      goto load_next;
+    }
+
+  /* Retrieve modification time */
+  file = g_file_new_for_path (filename);
+  info = g_file_query_info (file,
+                            G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_ETAG_VALUE,
+                            G_FILE_QUERY_INFO_NONE, NULL, NULL);
+  if (info)
+    {
+      g_file_info_get_modification_time (info, &modified_time);
+      champlain_tile_set_modified_time (tile, &modified_time);
+    }
+  g_object_unref (file);
+  g_object_unref (info);
+
+  /* Notify other caches that the tile has been filled */
+  if (CHAMPLAIN_IS_TILE_CACHE(next_source))
+    {
+      on_tile_filled(CHAMPLAIN_TILE_CACHE(next_source), tile);
+    }
+
+  if (tile_is_expired (file_cache, tile))
+    {
+      int sql_rc = SQLITE_OK;
+
+      /* Retrieve etag */
+      sql_rc = sqlite3_bind_text (priv->stmt_select, 1, filename, -1, SQLITE_STATIC);
+      if (sql_rc == SQLITE_ERROR)
+        {
+          DEBUG ("Failed to prepare the SQL query for finding the Etag of '%s', error: %s",
+                 filename, sqlite3_errmsg (priv->db));
+          goto load_next;
+        }
+
+      sql_rc = sqlite3_step (priv->stmt_select);
+      if (sql_rc == SQLITE_ROW)
+        {
+          const gchar *etag = (const gchar *) sqlite3_column_text (priv->stmt_select, 0);
+          champlain_tile_set_etag (CHAMPLAIN_TILE (tile), etag);
+        }
+      else if (sql_rc == SQLITE_DONE)
+        {
+          DEBUG ("'%s' does't have an etag",
+                 filename);
+          goto load_next;
+        }
+      else if (sql_rc == SQLITE_ERROR)
+        {
+          DEBUG ("Failed to finding the Etag of '%s', %d error: %s",
+                 filename, sql_rc, sqlite3_errmsg (priv->db));
+          goto load_next;
+        }
+
+      /* Validate the tile */
+      /* goto load_next; */
+    }
+  else
+    {
+      /* Tile loaded and no validation needed - done */
+      champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
+      goto cleanup;
+    }
+
+load_next:
+  if (CHAMPLAIN_IS_MAP_SOURCE(next_source))
+    {
+      champlain_map_source_fill_tile(next_source, tile);
+    }
+  champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
+
+cleanup:
+  sqlite3_reset (priv->stmt_select);
+  g_free(filename);
+}
+
+static void
+refresh_tile_time (ChamplainTileCache *tile_cache, ChamplainTile *tile)
+{
+  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (tile_cache));
+
+  ChamplainMapSource *map_source = CHAMPLAIN_MAP_SOURCE(tile_cache);
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
+  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(tile_cache);
+  gchar *filename = NULL;
+  GFile *file;
+  GFileInfo *info;
+
+  filename = get_filename (file_cache, tile);
+  file = g_file_new_for_path (filename);
+  g_free (filename);
+
+  info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
+                            G_FILE_QUERY_INFO_NONE, NULL, NULL);
+
+  if (info)
+    {
+      GTimeVal now = {0, };
+
+      g_get_current_time (&now);
+
+      g_file_info_set_modification_time (info, &now);
+      g_file_set_attributes_from_info (file, info, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+    }
+
+  g_object_unref (file);
+  g_object_unref (info);
+
+  if (CHAMPLAIN_IS_TILE_CACHE(next_source))
+    {
+      refresh_tile_time(CHAMPLAIN_TILE_CACHE(next_source), tile);
+    }
+}
+
+static void
+store_tile (ChamplainTileCache *tile_cache,
+            ChamplainTile *tile,
+            const gchar *contents,
+            gsize size)
+{
+  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (tile_cache));
+
+  ChamplainMapSource *map_source = CHAMPLAIN_MAP_SOURCE(tile_cache);
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
+  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(tile_cache);
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
+  gchar *query = NULL;
+  gchar *error = NULL;
+  gchar *path = NULL;
+  gchar *filename = NULL;
+  GError *gerror = NULL;
+  GFile *file;
+  GFileOutputStream *ostream;
+  gsize bytes_written;
+
+  DEBUG ("Update of %p", tile);
+
+  filename = get_filename (file_cache, tile);
+  file = g_file_new_for_path(filename);
+
+  /* If the file exists, delete it */
+  g_file_delete(file, NULL, NULL);
+
+  /* If needed, create the cache's dirs */
+  path = g_path_get_dirname (filename);
+  if (g_mkdir_with_parents (path, 0700) == -1)
+    {
+      if (errno != EEXIST)
+        {
+          g_warning ("Unable to create the image cache path '%s': %s",
+                     path, g_strerror (errno));
+          goto store_next;
+        }
+    }
+
+  ostream = g_file_create(file, G_FILE_CREATE_PRIVATE, NULL, &gerror);
+  if (!ostream)
+    {
+      DEBUG ("GFileOutputStream creation failed: %s", gerror->message);
+      g_error_free (gerror);
+      goto store_next;
+    }
+
+  /* Write the cache */
+  if (!g_output_stream_write_all(G_OUTPUT_STREAM(ostream), contents, size, &bytes_written, NULL, &gerror))
+    {
+      DEBUG ("Writing file contents failed: %s", gerror->message);
+      g_error_free (gerror);
+      g_object_unref(ostream);
+      goto store_next;
+    }
+
+  g_object_unref(ostream);
+
+  query = sqlite3_mprintf ("REPLACE INTO tiles (filename, etag, size) VALUES (%Q, %Q, %d)",
+                           filename,
+                           champlain_tile_get_etag (tile),
+                           size);
+  sqlite3_exec (priv->db, query, NULL, NULL, &error);
+  if (error != NULL)
+    {
+      DEBUG ("Saving Etag and size failed: %s", error);
+      sqlite3_free (error);
+    }
+  sqlite3_free (query);
+
+store_next:
+  if (CHAMPLAIN_IS_TILE_CACHE(next_source))
+    {
+      store_tile(CHAMPLAIN_TILE_CACHE(next_source), tile, contents, size);
+    }
+
+  g_free(filename);
+  g_free(path);
+  g_object_unref(file);
+}
+
+static void
+on_tile_filled (ChamplainTileCache *tile_cache, ChamplainTile *tile)
+{
+  g_return_if_fail(CHAMPLAIN_IS_FILE_CACHE (tile_cache));
+  g_return_if_fail(CHAMPLAIN_IS_TILE (tile));
+
+  ChamplainMapSource *map_source = CHAMPLAIN_MAP_SOURCE(tile_cache);
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
+  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(tile_cache);
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
+  int sql_rc = SQLITE_OK;
+  gchar *filename = NULL;
+
+  filename = get_filename (file_cache, tile);
+
+  DEBUG ("popularity of %s", filename);
+
+  sql_rc = sqlite3_bind_text (priv->stmt_update, 1, filename, -1, SQLITE_STATIC);
+  if (sql_rc != SQLITE_OK)
+    {
+      DEBUG ("Failed to set values to the popularity query of '%s', error: %s",
+             filename, sqlite3_errmsg (priv->db));
+      goto call_next;
+    }
+
+  sql_rc = sqlite3_step (priv->stmt_update);
+  if (sql_rc != SQLITE_DONE)
+    {
+      /* may not be present in this cache */
+      goto call_next;
+    }
+
+call_next:
+  if (CHAMPLAIN_IS_TILE_CACHE(next_source))
+    {
+      on_tile_filled(CHAMPLAIN_TILE_CACHE(next_source), tile);
+    }
+}
+
+static void
+clean (ChamplainTileCache *tile_cache)
+{
+  g_return_if_fail(CHAMPLAIN_IS_FILE_CACHE (tile_cache));
+
+  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(tile_cache);
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
+
+  g_return_if_fail(!champlain_tile_cache_get_persistent(tile_cache));
+
+  finalize_sql (file_cache);
+  delete_temp_cache(file_cache);
+
+  g_free(priv->real_cache_dir);
+  priv->real_cache_dir = NULL;
+
+  init_cache(file_cache);
+}
+
+static void
+delete_tile (ChamplainFileCache *file_cache, const gchar *filename)
+{
+  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache));
+  gchar *query, *error = NULL;
+  GError *gerror = NULL;
+  GFile *file;
+
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
+
+  query = sqlite3_mprintf ("DELETE FROM tiles WHERE filename = %Q", filename);
+  sqlite3_exec (priv->db, query, NULL, NULL, &error);
+  if (error != NULL)
+    {
+      DEBUG ("Deleting tile from db failed: %s", error);
+      sqlite3_free (error);
+    }
+  sqlite3_free (query);
+
+  file = g_file_new_for_path (filename);
+  if (!g_file_delete (file, NULL, &gerror))
+    {
+      DEBUG ("Deleting tile from disk failed: %s", gerror->message);
+      g_error_free (gerror);
+    }
+  g_object_unref (file);
+}
+
+static gboolean
+purge_on_idle (gpointer data)
+{
+  champlain_file_cache_purge (CHAMPLAIN_FILE_CACHE (data));
+  return FALSE;
+}
+
+void
+champlain_file_cache_purge_on_idle (ChamplainFileCache *file_cache)
+{
+  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache));
+  g_idle_add (purge_on_idle, file_cache);
+}
+
+void
+champlain_file_cache_purge (ChamplainFileCache *file_cache)
+{
+  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache));
+
+  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
+  gchar *query;
+  sqlite3_stmt *stmt;
+  int rc = 0;
+  guint current_size = 0;
+  guint highest_popularity = 0;
+  gchar *error;
+
+  query = "SELECT SUM (size) FROM tiles";
+  rc = sqlite3_prepare (priv->db, query, strlen (query), &stmt, NULL);
+  if (rc != SQLITE_OK)
+    {
+      DEBUG ("Can't compute cache size %s", sqlite3_errmsg (priv->db));
+    }
+
+  rc = sqlite3_step (stmt);
+  if (rc != SQLITE_ROW)
+    {
+      DEBUG ("Failed to count the total cache consumption %s",
+             sqlite3_errmsg (priv->db));
+      sqlite3_finalize (stmt);
+      return;
+    }
+
+  current_size = sqlite3_column_int (stmt, 0);
+  if (current_size < priv->size_limit)
+    {
+      DEBUG ("Cache doesn't need to be purged at %d bytes", current_size);
+      sqlite3_finalize (stmt);
+      return;
+    }
+
+  sqlite3_finalize (stmt);
+
+  /* Ok, delete the less popular tiles until size_limit reached */
+  query = "SELECT filename, size, popularity FROM tiles ORDER BY popularity";
+  rc = sqlite3_prepare (priv->db, query, strlen (query), &stmt, NULL);
+  if (rc != SQLITE_OK)
+    {
+      DEBUG ("Can't fetch tiles to delete: %s", sqlite3_errmsg(priv->db));
+    }
+
+  rc = sqlite3_step (stmt);
+  while (rc == SQLITE_ROW && current_size > priv->size_limit)
+    {
+      const char *filename = sqlite3_column_text (stmt, 0);
+      guint size;
+
+      filename = sqlite3_column_text (stmt, 0);
+      size = sqlite3_column_int (stmt, 1);
+      highest_popularity = sqlite3_column_int (stmt, 2);
+      DEBUG ("Deleting %s of size %d", filename, size);
+
+      delete_tile (file_cache, filename);
+
+      current_size -= size;
+
+      rc = sqlite3_step (stmt);
+    }
+  DEBUG ("Cache size is now %d", current_size);
+
+  sqlite3_finalize (stmt);
+
+  query = sqlite3_mprintf ("UPDATE tiles SET popularity = popularity - %d",
+                           highest_popularity);
+  sqlite3_exec (priv->db, query, NULL, NULL, &error);
+  if (error != NULL)
+    {
+      DEBUG ("Updating popularity failed: %s", error);
+      sqlite3_free (error);
+    }
+  sqlite3_free (query);
+}
+
+static void
+delete_dir_recursive (GFile *parent)
+{
+  g_return_if_fail (parent);
+
+  GError *error = NULL;
+  GFileEnumerator *enumerator;
+  GFileInfo *info;
+  GFile *child;
+
+  enumerator = g_file_enumerate_children (parent, "*",
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL);
+
+  if (!enumerator)
+    {
+      DEBUG ("Failed to create file enumerator in delete_dir_recursive: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  info = g_file_enumerator_next_file (enumerator, NULL, NULL);
+  while (info && !error)
+    {
+      child = g_file_get_child (parent, g_file_info_get_name (info));
+
+      if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+        delete_dir_recursive (child);
+
+      error = NULL;
+      if (!g_file_delete (child, NULL, &error))
+        {
+          DEBUG ("Deleting tile from disk failed: %s", error->message);
+          g_error_free (error);
+        }
+
+      g_object_unref (child);
+      g_object_unref (info);
+      info = g_file_enumerator_next_file (enumerator, NULL, NULL);
+    }
+
+  g_file_enumerator_close (enumerator, NULL, NULL);
+}
diff --git a/champlain/champlain-file-cache.h b/champlain/champlain-file-cache.h
new file mode 100644
index 0000000..a6a3d69
--- /dev/null
+++ b/champlain/champlain-file-cache.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009 Pierre-Luc Beaudoin <pierre-luc pierlux com>
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#if !defined (__CHAMPLAIN_CHAMPLAIN_H_INSIDE__) && !defined (CHAMPLAIN_COMPILATION)
+#error "Only <champlain/champlain.h> can be included directly."
+#endif
+
+#ifndef _CHAMPLAIN_FILE_CACHE_H_
+#define _CHAMPLAIN_FILE_CACHE_H_
+
+#include <glib-object.h>
+#include <champlain/champlain-tile-cache.h>
+
+G_BEGIN_DECLS
+
+#define CHAMPLAIN_TYPE_FILE_CACHE             (champlain_file_cache_get_type ())
+#define CHAMPLAIN_FILE_CACHE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHAMPLAIN_TYPE_FILE_CACHE, ChamplainFileCache))
+#define CHAMPLAIN_FILE_CACHE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), CHAMPLAIN_TYPE_FILE_CACHE, ChamplainFileCacheClass))
+#define CHAMPLAIN_IS_FILE_CACHE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CHAMPLAIN_TYPE_FILE_CACHE))
+#define CHAMPLAIN_IS_FILE_CACHE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), CHAMPLAIN_TYPE_FILE_CACHE))
+#define CHAMPLAIN_FILE_CACHE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), CHAMPLAIN_TYPE_FILE_CACHE, ChamplainFileCacheClass))
+
+typedef struct _ChamplainFileCache ChamplainFileCache;
+typedef struct _ChamplainFileCacheClass ChamplainFileCacheClass;
+
+struct _ChamplainFileCache
+{
+  ChamplainTileCache parent_instance;
+};
+
+struct _ChamplainFileCacheClass
+{
+  ChamplainTileCacheClass parent_class;
+};
+
+GType champlain_file_cache_get_type (void);
+
+ChamplainFileCache* champlain_file_cache_new_full (guint size_limit,
+    const gchar *cache_dir, gboolean persistent);
+
+guint champlain_file_cache_get_size_limit (ChamplainFileCache *file_cache);
+void champlain_file_cache_set_size_limit (ChamplainFileCache *file_cache,
+    guint size_limit);
+
+const gchar * champlain_file_cache_get_cache_dir (ChamplainFileCache *file_cache);
+
+void champlain_file_cache_purge (ChamplainFileCache *file_cache);
+void champlain_file_cache_purge_on_idle (ChamplainFileCache *file_cache);
+
+G_END_DECLS
+
+#endif /* _CHAMPLAIN_FILE_CACHE_H_ */
diff --git a/champlain/champlain-map-data-source.c b/champlain/champlain-map-data-source.c
index 0b4fb7d..9e88335 100644
--- a/champlain/champlain-map-data-source.c
+++ b/champlain/champlain-map-data-source.c
@@ -32,7 +32,7 @@
 #include "champlain-bounding-box.h"
 #include "champlain-enum-types.h"
 
-G_DEFINE_TYPE (ChamplainMapDataSource, champlain_map_data_source, G_TYPE_OBJECT)
+G_DEFINE_TYPE (ChamplainMapDataSource, champlain_map_data_source, G_TYPE_INITIALLY_UNOWNED)
 
 #define GET_PRIVATE(o) \
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), CHAMPLAIN_TYPE_MAP_DATA_SOURCE, ChamplainMapDataSourcePrivate))
diff --git a/champlain/champlain-map-data-source.h b/champlain/champlain-map-data-source.h
index 12fd67b..6be4a5b 100644
--- a/champlain/champlain-map-data-source.h
+++ b/champlain/champlain-map-data-source.h
@@ -42,11 +42,11 @@ G_BEGIN_DECLS
   (G_TYPE_INSTANCE_GET_CLASS ((obj), CHAMPLAIN_TYPE_MAP_DATA_SOURCE, ChamplainMapDataSourceClass))
 
 typedef struct {
-  GObject parent;
+  GInitiallyUnowned parent;
 } ChamplainMapDataSource;
 
 typedef struct {
-  GObjectClass parent_class;
+  GInitiallyUnownedClass parent_class;
   MemphisMap* (*get_map_data) (ChamplainMapDataSource *data_source);
 } ChamplainMapDataSourceClass;
 
diff --git a/champlain/champlain-map-source-chain.c b/champlain/champlain-map-source-chain.c
new file mode 100644
index 0000000..e9df378
--- /dev/null
+++ b/champlain/champlain-map-source-chain.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "champlain-map-source-chain.h"
+#include "champlain-tile-cache.h"
+#include "champlain-tile-source.h"
+
+G_DEFINE_TYPE (ChamplainMapSourceChain, champlain_map_source_chain, CHAMPLAIN_TYPE_MAP_SOURCE);
+
+#define GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE((obj), CHAMPLAIN_TYPE_MAP_SOURCE_CHAIN, ChamplainMapSourceChainPrivate))
+
+typedef struct _ChamplainMapSourceChainPrivate ChamplainMapSourceChainPrivate;
+
+struct _ChamplainMapSourceChainPrivate
+{
+  ChamplainMapSource *stack_top;
+  gulong sig_handler_id;
+};
+
+static const gchar *get_id (ChamplainMapSource *map_source);
+static const gchar *get_name (ChamplainMapSource *map_source);
+static const gchar *get_license (ChamplainMapSource *map_source);
+static const gchar *get_license_uri (ChamplainMapSource *map_source);
+static guint get_min_zoom_level (ChamplainMapSource *map_source);
+static guint get_max_zoom_level (ChamplainMapSource *map_source);
+static guint get_tile_size (ChamplainMapSource *map_source);
+
+static void fill_tile (ChamplainMapSource *map_source, ChamplainTile *tile);
+
+static void
+champlain_map_source_chain_dispose (GObject *object)
+{
+  ChamplainMapSourceChain *source_chain = CHAMPLAIN_MAP_SOURCE_CHAIN(object);
+  ChamplainMapSourceChainPrivate *priv = GET_PRIVATE(object);
+
+  while (priv->stack_top)
+    champlain_map_source_chain_pop_map_source(source_chain);
+
+  G_OBJECT_CLASS (champlain_map_source_chain_parent_class)->dispose (object);
+}
+
+static void
+champlain_map_source_chain_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (champlain_map_source_chain_parent_class)->finalize (object);
+}
+
+static void
+champlain_map_source_chain_class_init (ChamplainMapSourceChainClass *klass)
+{
+  GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (ChamplainMapSourceChainPrivate));
+
+  object_class->finalize = champlain_map_source_chain_finalize;
+  object_class->dispose = champlain_map_source_chain_dispose;
+
+  ChamplainMapSourceClass *map_source_class = CHAMPLAIN_MAP_SOURCE_CLASS (klass);
+
+  map_source_class->get_id = get_id;
+  map_source_class->get_name = get_name;
+  map_source_class->get_license = get_license;
+  map_source_class->get_license_uri = get_license_uri;
+  map_source_class->get_min_zoom_level = get_min_zoom_level;
+  map_source_class->get_max_zoom_level = get_max_zoom_level;
+  map_source_class->get_tile_size = get_tile_size;
+
+  map_source_class->fill_tile = fill_tile;
+}
+
+static void
+champlain_map_source_chain_init (ChamplainMapSourceChain *source_chain)
+{
+  ChamplainMapSourceChainPrivate *priv = GET_PRIVATE(source_chain);
+  priv->stack_top = NULL;
+  priv->sig_handler_id = 0;
+}
+
+ChamplainMapSourceChain* champlain_map_source_chain_new (void)
+{
+  return g_object_new (CHAMPLAIN_TYPE_MAP_SOURCE_CHAIN, NULL);
+}
+
+static const gchar *
+get_id (ChamplainMapSource *map_source)
+{
+  ChamplainMapSourceChain *source_chain = CHAMPLAIN_MAP_SOURCE_CHAIN (map_source);
+  g_return_val_if_fail (source_chain, NULL);
+
+  ChamplainMapSourceChainPrivate *priv = GET_PRIVATE(source_chain);
+  g_return_val_if_fail (priv->stack_top, NULL);
+
+  return champlain_map_source_get_id(priv->stack_top);
+}
+
+static const gchar *
+get_name (ChamplainMapSource *map_source)
+{
+  ChamplainMapSourceChain *source_chain = CHAMPLAIN_MAP_SOURCE_CHAIN (map_source);
+  g_return_val_if_fail (source_chain, NULL);
+
+  ChamplainMapSourceChainPrivate *priv = GET_PRIVATE(source_chain);
+  g_return_val_if_fail (priv->stack_top, NULL);
+
+  return champlain_map_source_get_name(priv->stack_top);
+}
+
+static const gchar *
+get_license (ChamplainMapSource *map_source)
+{
+  ChamplainMapSourceChain *source_chain = CHAMPLAIN_MAP_SOURCE_CHAIN (map_source);
+  g_return_val_if_fail (source_chain, NULL);
+
+  ChamplainMapSourceChainPrivate *priv = GET_PRIVATE(source_chain);
+  g_return_val_if_fail (priv->stack_top, NULL);
+
+  return champlain_map_source_get_license(priv->stack_top);
+}
+
+static const gchar *
+get_license_uri (ChamplainMapSource *map_source)
+{
+  ChamplainMapSourceChain *source_chain = CHAMPLAIN_MAP_SOURCE_CHAIN (map_source);
+  g_return_val_if_fail (source_chain, NULL);
+
+  ChamplainMapSourceChainPrivate *priv = GET_PRIVATE(source_chain);
+  g_return_val_if_fail (priv->stack_top, NULL);
+
+  return champlain_map_source_get_license_uri(priv->stack_top);
+}
+
+static guint
+get_min_zoom_level (ChamplainMapSource *map_source)
+{
+  ChamplainMapSourceChain *source_chain = CHAMPLAIN_MAP_SOURCE_CHAIN (map_source);
+  g_return_val_if_fail (source_chain, 0);
+
+  ChamplainMapSourceChainPrivate *priv = GET_PRIVATE(source_chain);
+  g_return_val_if_fail (priv->stack_top, 0);
+
+  return champlain_map_source_get_min_zoom_level(priv->stack_top);
+}
+
+static guint
+get_max_zoom_level (ChamplainMapSource *map_source)
+{
+  ChamplainMapSourceChain *source_chain = CHAMPLAIN_MAP_SOURCE_CHAIN (map_source);
+  g_return_val_if_fail (source_chain, 0);
+
+  ChamplainMapSourceChainPrivate *priv = GET_PRIVATE(source_chain);
+  g_return_val_if_fail (priv->stack_top, 0);
+
+  return champlain_map_source_get_max_zoom_level(priv->stack_top);
+}
+
+static guint
+get_tile_size (ChamplainMapSource *map_source)
+{
+  ChamplainMapSourceChain *source_chain = CHAMPLAIN_MAP_SOURCE_CHAIN (map_source);
+  g_return_val_if_fail (source_chain, 0);
+
+  ChamplainMapSourceChainPrivate *priv = GET_PRIVATE(source_chain);
+  g_return_val_if_fail (priv->stack_top, 0);
+
+  return champlain_map_source_get_tile_size(priv->stack_top);
+}
+
+static void fill_tile (ChamplainMapSource *map_source,
+                       ChamplainTile *tile)
+{
+  ChamplainMapSourceChain *source_chain = CHAMPLAIN_MAP_SOURCE_CHAIN (map_source);
+  g_return_if_fail (source_chain);
+
+  ChamplainMapSourceChainPrivate *priv = GET_PRIVATE(source_chain);
+  g_return_if_fail (priv->stack_top);
+
+  champlain_map_source_fill_tile(priv->stack_top, tile);
+}
+
+static void assign_cache_of_next_source_sequence(ChamplainMapSource *start_map_source, ChamplainTileCache *tile_cache)
+{
+  ChamplainMapSource *map_source = start_map_source;
+  ChamplainTileSource *tile_source;
+
+  do
+    {
+      map_source = champlain_map_source_get_next_source(map_source);
+    }
+  while (CHAMPLAIN_IS_TILE_CACHE(map_source));
+
+  tile_source = CHAMPLAIN_TILE_SOURCE(map_source);
+  while (tile_source)
+    {
+      champlain_tile_source_set_cache(tile_source, tile_cache);
+      map_source = champlain_map_source_get_next_source(map_source);
+      tile_source = CHAMPLAIN_TILE_SOURCE(map_source);
+    }
+}
+
+static
+void reload_tiles_cb(ChamplainMapSource *map_source, ChamplainMapSourceChain *source_chain)
+{
+  /* propagate the signal from the chain that is inside champlain_map_source_chain */
+  g_signal_emit_by_name (source_chain, "reload-tiles", NULL);
+}
+
+void champlain_map_source_chain_push_map_source(ChamplainMapSourceChain *source_chain, ChamplainMapSource *map_source)
+{
+  ChamplainMapSourceChainPrivate *priv = GET_PRIVATE(source_chain);
+  gboolean is_cache = FALSE;
+
+  if (CHAMPLAIN_IS_TILE_CACHE(map_source))
+    is_cache = TRUE;
+  else
+    g_return_if_fail (CHAMPLAIN_IS_TILE_SOURCE(map_source));
+
+  g_object_ref_sink(map_source);
+
+  if (!priv->stack_top)
+    {
+      /* tile source has to be last */
+      g_return_if_fail(!is_cache);
+      priv->stack_top = map_source;
+    }
+  else
+    {
+      if (g_signal_handler_is_connected (priv->stack_top, priv->sig_handler_id))
+        g_signal_handler_disconnect (priv->stack_top, priv->sig_handler_id);
+
+      champlain_map_source_set_next_source(map_source, priv->stack_top);
+      priv->stack_top = map_source;
+
+      if (is_cache)
+        {
+          ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(map_source);
+          assign_cache_of_next_source_sequence(priv->stack_top, tile_cache);
+        }
+    }
+
+  priv->sig_handler_id = g_signal_connect (priv->stack_top, "reload-tiles",
+                         G_CALLBACK (reload_tiles_cb), source_chain);
+}
+
+void champlain_map_source_chain_pop_map_source(ChamplainMapSourceChain *source_chain)
+{
+  ChamplainMapSourceChainPrivate *priv = GET_PRIVATE(source_chain);
+  ChamplainMapSource *old_stack_top = priv->stack_top;
+
+  g_return_if_fail(priv->stack_top);
+
+  if (g_signal_handler_is_connected (priv->stack_top, priv->sig_handler_id))
+    g_signal_handler_disconnect (priv->stack_top, priv->sig_handler_id);
+
+  if (CHAMPLAIN_IS_TILE_CACHE(priv->stack_top))
+    {
+      ChamplainMapSource *map_source = champlain_map_source_get_next_source(priv->stack_top);
+      ChamplainTileCache *tile_cache = NULL;
+
+      if (CHAMPLAIN_IS_TILE_CACHE(map_source))
+        tile_cache = CHAMPLAIN_TILE_CACHE(map_source);
+
+      assign_cache_of_next_source_sequence(priv->stack_top, tile_cache);
+    }
+
+  priv->stack_top = champlain_map_source_get_next_source(priv->stack_top);
+  if (priv->stack_top)
+    {
+      priv->sig_handler_id = g_signal_connect (priv->stack_top, "reload-tiles",
+                             G_CALLBACK (reload_tiles_cb), source_chain);
+    }
+
+  g_object_unref(old_stack_top);
+}
diff --git a/champlain/champlain-map-source-chain.h b/champlain/champlain-map-source-chain.h
new file mode 100644
index 0000000..e633070
--- /dev/null
+++ b/champlain/champlain-map-source-chain.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#if !defined (__CHAMPLAIN_CHAMPLAIN_H_INSIDE__) && !defined (CHAMPLAIN_COMPILATION)
+#error "Only <champlain/champlain.h> can be included directly."
+#endif
+
+#ifndef _CHAMPLAIN_MAP_SOURCE_CHAIN_H_
+#define _CHAMPLAIN_MAP_SOURCE_CHAIN_H_
+
+#include <glib-object.h>
+
+#include "champlain-map-source.h"
+
+G_BEGIN_DECLS
+
+#define CHAMPLAIN_TYPE_MAP_SOURCE_CHAIN             (champlain_map_source_chain_get_type ())
+#define CHAMPLAIN_MAP_SOURCE_CHAIN(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHAMPLAIN_TYPE_MAP_SOURCE_CHAIN, ChamplainMapSourceChain))
+#define CHAMPLAIN_MAP_SOURCE_CHAIN_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), CHAMPLAIN_TYPE_MAP_SOURCE_CHAIN, ChamplainMapSourceChainClass))
+#define CHAMPLAIN_IS_MAP_SOURCE_CHAIN(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CHAMPLAIN_TYPE_MAP_SOURCE_CHAIN))
+#define CHAMPLAIN_IS_MAP_SOURCE_CHAIN_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), CHAMPLAIN_TYPE_MAP_SOURCE_CHAIN))
+#define CHAMPLAIN_MAP_SOURCE_CHAIN_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), CHAMPLAIN_TYPE_MAP_SOURCE_CHAIN, ChamplainMapSourceChainClass))
+
+typedef struct _ChamplainMapSourceChain ChamplainMapSourceChain;
+typedef struct _ChamplainMapSourceChainClass ChamplainMapSourceChainClass;
+
+struct _ChamplainMapSourceChain
+{
+  ChamplainMapSource parent_instance;
+};
+
+struct _ChamplainMapSourceChainClass
+{
+  ChamplainMapSourceClass parent_class;
+};
+
+GType champlain_map_source_chain_get_type (void);
+
+ChamplainMapSourceChain* champlain_map_source_chain_new (void);
+
+void champlain_map_source_chain_push_map_source(ChamplainMapSourceChain *source_chain, ChamplainMapSource *map_source);
+void champlain_map_source_chain_pop_map_source(ChamplainMapSourceChain *source_chain);
+
+G_END_DECLS
+
+#endif /* _CHAMPLAIN_MAP_SOURCE_CHAIN_H_ */
diff --git a/champlain/champlain-map-source-desc.h b/champlain/champlain-map-source-desc.h
index 6b4d107..6c1fd61 100644
--- a/champlain/champlain-map-source-desc.h
+++ b/champlain/champlain-map-source-desc.h
@@ -24,7 +24,7 @@
 #define CHAMPLAIN_MAP_SOURCE_DESC_H
 
 #include <glib-object.h>
-#include "champlain-map-source.h"
+#include "champlain-tile-source.h"
 
 G_BEGIN_DECLS
 
diff --git a/champlain/champlain-map-source-factory.c b/champlain/champlain-map-source-factory.c
index da8f828..90a7de3 100644
--- a/champlain/champlain-map-source-factory.c
+++ b/champlain/champlain-map-source-factory.c
@@ -39,13 +39,16 @@
 #include "champlain-debug.h"
 
 #include "champlain.h"
-#include "champlain-cache.h"
+#include "champlain-file-cache.h"
 #include "champlain-defines.h"
 #include "champlain-enum-types.h"
 #include "champlain-map-source.h"
 #include "champlain-marshal.h"
 #include "champlain-private.h"
 #include "champlain-zoom-level.h"
+#include "champlain-network-tile-source.h"
+#include "champlain-map-source-chain.h"
+#include "champlain-error-tile-source.h"
 
 #include <glib.h>
 #include <string.h>
@@ -354,7 +357,12 @@ ChamplainMapSource *
 champlain_map_source_factory_create (ChamplainMapSourceFactory *factory,
     const gchar *id)
 {
+//  ChamplainMapSource *map_source = NULL;
+//  ChamplainMapSourceChain *source_chain;
+//  ChamplainMapSource *source;
   GSList *item;
+//  guint tile_size;
+//  gchar *cache_path;
 
   item = factory->priv->registered_sources;
 
@@ -362,10 +370,28 @@ champlain_map_source_factory_create (ChamplainMapSourceFactory *factory,
     {
       ChamplainMapSourceDesc *desc = CHAMPLAIN_MAP_SOURCE_DESC (item->data);
       if (strcmp (desc->id, id) == 0)
-        return desc->constructor (desc, desc->data);
+        return desc->constructor (desc, desc->data); //map_source = desc->constructor (desc, desc->data);
       item = g_slist_next (item);
     }
-  return NULL;
+
+//  if (!map_source)
+     return NULL;
+
+/*  source_chain = champlain_map_source_chain_new ();
+
+  tile_size = champlain_map_source_get_tile_size(map_source);
+  source = CHAMPLAIN_MAP_SOURCE(champlain_error_tile_source_new_full (tile_size));
+
+  champlain_map_source_chain_push_map_source(source_chain, source);
+  champlain_map_source_chain_push_map_source(source_chain, map_source);
+
+  cache_path = g_build_path (G_DIR_SEPARATOR_S, g_get_user_cache_dir (), "champlain", NULL);
+  source = CHAMPLAIN_MAP_SOURCE(champlain_file_cache_new_full (100000000, cache_path, TRUE));
+  g_free(cache_path);
+
+  champlain_map_source_chain_push_map_source(source_chain, source);
+
+  return CHAMPLAIN_MAP_SOURCE(source_chain);*/
 }
 
 /**
@@ -402,7 +428,7 @@ static ChamplainMapSource *
 champlain_map_source_new_generic (
      ChamplainMapSourceDesc *desc, gpointer user_data)
 {
-  return CHAMPLAIN_MAP_SOURCE (champlain_network_map_source_new_full (
+  return CHAMPLAIN_MAP_SOURCE (champlain_network_tile_source_new_full (
       desc->id,
       desc->name,
       desc->license,
@@ -431,12 +457,18 @@ champlain_map_source_new_memphis (ChamplainMapSourceDesc *desc,
             desc->uri_format);
     }
   else if (g_strcmp0 (desc->id, CHAMPLAIN_MAP_SOURCE_MEMPHIS_NETWORK) == 0)
-    {
       map_data_source = CHAMPLAIN_MAP_DATA_SOURCE (champlain_network_map_data_source_new ());
-    }
   else
     return NULL;
 
   return CHAMPLAIN_MAP_SOURCE (champlain_memphis_map_source_new_full (
-      desc, map_data_source));
+      desc->id,
+      desc->name,
+      desc->license,
+      desc->license_uri,
+      desc->min_zoom_level,
+      desc->max_zoom_level,
+      256,
+      desc->projection,
+      map_data_source));
 }
diff --git a/champlain/champlain-map-source.c b/champlain/champlain-map-source.c
index a42c33b..42e5689 100644
--- a/champlain/champlain-map-source.c
+++ b/champlain/champlain-map-source.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2009 Pierre-Luc Beaudoin <pierre-luc pierlux com>
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -16,40 +16,13 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-/**
- * SECTION:champlain-map-source
- * @short_description: A base class for map sources
- *
- * #ChamplainTiles come from map sources which are represented by
- * #ChamplainMapSource.  This is should be considered an abstract
- * type as it does nothing of interest.
- *
- * When loading new tiles, #ChamplainView calls #champlain_map_source_get_tile
- * on the current #ChamplainMapSource passing it a #ChamplainTile to be filled
- * with the image.  #ChamplainMapSources should check with #ChamplainCache
- * if the image is already locally available with #champlain_cache_fill_tile.
- */
-
-#include "config.h"
-
-#define DEBUG_FLAG CHAMPLAIN_DEBUG_LOADING
-#include "champlain-debug.h"
-
-#include "champlain.h"
-#include "champlain-defines.h"
-#include "champlain-enum-types.h"
 #include "champlain-map-source.h"
-#include "champlain-marshal.h"
-#include "champlain-private.h"
-#include "champlain-zoom-level.h"
 
-#include <glib.h>
-#include <glib-object.h>
 #include <math.h>
-#include <string.h>
 
-void champlain_map_source_real_fill_tile (ChamplainMapSource *map_source,
-    ChamplainTile *tile);
+G_DEFINE_TYPE (ChamplainMapSource, champlain_map_source, G_TYPE_INITIALLY_UNOWNED);
+
+#define GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE((obj), CHAMPLAIN_TYPE_MAP_SOURCE, ChamplainMapSourcePrivate))
 
 enum
 {
@@ -61,267 +34,117 @@ enum
 enum
 {
   PROP_0,
-  PROP_ID,
-  PROP_NAME,
-  PROP_LICENSE,
-  PROP_LICENSE_URI,
-  PROP_MAX_ZOOM_LEVEL,
-  PROP_MIN_ZOOM_LEVEL,
-  PROP_TILE_SIZE,
-  PROP_MAP_PROJECTION,
+  PROP_NEXT_SOURCE,
 };
 
 static guint champlain_map_source_signals[LAST_SIGNAL] = { 0, };
 
-G_DEFINE_TYPE (ChamplainMapSource, champlain_map_source, G_TYPE_INITIALLY_UNOWNED);
-
-#define GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE((obj), CHAMPLAIN_TYPE_MAP_SOURCE, ChamplainMapSourcePrivate))
+typedef struct _ChamplainMapSourcePrivate ChamplainMapSourcePrivate;
 
 struct _ChamplainMapSourcePrivate
 {
-  gchar *id;
-  gchar *name;
-  gchar *license;
-  gchar *license_uri;
-  guint max_zoom_level;
-  guint min_zoom_level;
-  guint tile_size;
-  ChamplainMapProjection map_projection;
+  ChamplainMapSource *next_source;
+
+  gulong sig_handler_id;
 };
 
-static void
-real_fill_tile (ChamplainMapSource *map_source,
-    ChamplainTile *tile)
-{
-  g_error ("Should not be reached");
-}
+static void reload_tiles_cb(ChamplainMapSource *orig, ChamplainMapSource *self);
 
 static void
 champlain_map_source_get_property (GObject *object,
-    guint prop_id,
-    GValue *value,
-    GParamSpec *pspec)
+                                   guint prop_id,
+                                   GValue *value,
+                                   GParamSpec *pspec)
 {
-  ChamplainMapSource *map_source = CHAMPLAIN_MAP_SOURCE(object);
-  ChamplainMapSourcePrivate *priv = map_source->priv;
+  ChamplainMapSourcePrivate *priv = GET_PRIVATE(object);
 
   switch(prop_id)
     {
-      case PROP_ID:
-        g_value_set_string (value, priv->id);
-        break;
-      case PROP_NAME:
-        g_value_set_string (value, priv->name);
-        break;
-      case PROP_LICENSE:
-        g_value_set_string (value, priv->license);
-        break;
-      case PROP_LICENSE_URI:
-        g_value_set_string (value, priv->license_uri);
-        break;
-      case PROP_MAX_ZOOM_LEVEL:
-        g_value_set_uint (value, priv->max_zoom_level);
-        break;
-      case PROP_MIN_ZOOM_LEVEL:
-        g_value_set_uint (value, priv->min_zoom_level);
-        break;
-      case PROP_TILE_SIZE:
-        g_value_set_uint (value, priv->tile_size);
-        break;
-      case PROP_MAP_PROJECTION:
-        g_value_set_enum (value, priv->map_projection);
-        break;
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    case PROP_NEXT_SOURCE:
+      g_value_set_object (value, priv->next_source);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     }
 }
 
 static void
 champlain_map_source_set_property (GObject *object,
-    guint prop_id,
-    const GValue *value,
-    GParamSpec *pspec)
+                                   guint prop_id,
+                                   const GValue *value,
+                                   GParamSpec *pspec)
 {
   ChamplainMapSource *map_source = CHAMPLAIN_MAP_SOURCE(object);
-  ChamplainMapSourcePrivate *priv = map_source->priv;
 
   switch(prop_id)
     {
-      case PROP_ID:
-        champlain_map_source_set_id (map_source,
-            g_value_get_string (value));
-      case PROP_NAME:
-        champlain_map_source_set_name (map_source,
-            g_value_get_string (value));
-        break;
-      case PROP_LICENSE:
-        priv->license = g_value_dup_string (value);
-        break;
-      case PROP_LICENSE_URI:
-        priv->license_uri = g_value_dup_string (value);
-        break;
-      case PROP_MAX_ZOOM_LEVEL:
-        priv->max_zoom_level = g_value_get_uint (value);
-        break;
-      case PROP_MIN_ZOOM_LEVEL:
-        priv->min_zoom_level = g_value_get_uint (value);
-        break;
-      case PROP_TILE_SIZE:
-        priv->tile_size = g_value_get_uint (value);
-        break;
-      case PROP_MAP_PROJECTION:
-        priv->map_projection = g_value_get_enum (value);
-        break;
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    case PROP_NEXT_SOURCE:
+      champlain_map_source_set_next_source (map_source,
+                                            g_value_get_object (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     }
 }
 
 static void
-champlain_map_source_finalize (GObject *object)
+champlain_map_source_dispose (GObject *object)
 {
-  ChamplainMapSource *map_source = CHAMPLAIN_MAP_SOURCE (object);
-  ChamplainMapSourcePrivate *priv = map_source->priv;
+  ChamplainMapSourcePrivate *priv = GET_PRIVATE(object);
 
-  g_free (priv->id);
-  g_free (priv->name);
-  g_free (priv->license);
-  g_free (priv->license_uri);
+  if (priv->next_source)
+    {
+      g_object_unref (priv->next_source);
+
+      priv->next_source = NULL;
+    }
+
+  G_OBJECT_CLASS (champlain_map_source_parent_class)->dispose (object);
+}
+
+static void
+champlain_map_source_finalize (GObject *object)
+{
   G_OBJECT_CLASS (champlain_map_source_parent_class)->finalize (object);
 }
 
 static void
+champlain_map_source_constructed  (GObject *object)
+{
+  if (G_OBJECT_CLASS (champlain_map_source_parent_class)->constructed)
+    G_OBJECT_CLASS (champlain_map_source_parent_class)->constructed (object);
+}
+
+static void
 champlain_map_source_class_init (ChamplainMapSourceClass *klass)
 {
+  GObjectClass* object_class = G_OBJECT_CLASS (klass);
   GParamSpec *pspec;
 
   g_type_class_add_private (klass, sizeof (ChamplainMapSourcePrivate));
 
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
   object_class->finalize = champlain_map_source_finalize;
+  object_class->dispose = champlain_map_source_dispose;
   object_class->get_property = champlain_map_source_get_property;
   object_class->set_property = champlain_map_source_set_property;
+  object_class->constructed = champlain_map_source_constructed;
 
-  klass->fill_tile = real_fill_tile;
-
-  /**
-  * ChamplainMapSource:id:
-  *
-  * The name of the map source
-  *
-  * Since: 0.4
-  */
-  pspec = g_param_spec_string ("id",
-                               "Id",
-                               "The id of the map source",
-                               "",
-                               (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-  g_object_class_install_property (object_class, PROP_ID, pspec);
-
-  /**
-  * ChamplainMapSource:name:
-  *
-  * The name of the map source
-  *
-  * Since: 0.4
-  */
-  pspec = g_param_spec_string ("name",
-                               "Name",
-                               "The name of the map source",
-                               "",
-                               (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-  g_object_class_install_property (object_class, PROP_NAME, pspec);
+  klass->get_id = NULL;
+  klass->get_name = NULL;
+  klass->get_license = NULL;
+  klass->get_license_uri = NULL;
+  klass->get_min_zoom_level = NULL;
+  klass->get_max_zoom_level = NULL;
+  klass->get_tile_size = NULL;
 
-  /**
-  * ChamplainMapSource:license:
-  *
-  * The usage license of the map source
-  *
-  * Since: 0.4
-  */
-  pspec = g_param_spec_string ("license",
-                               "License",
-                               "The usage license of the map source",
-                               "",
-                               (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-  g_object_class_install_property (object_class, PROP_LICENSE, pspec);
+  klass->fill_tile = NULL;
 
-  /**
-  * ChamplainMapSource:license-uri:
-  *
-  * The usage license's uri for more information 
-  *
-  * Since: 0.4
-  */
-  pspec = g_param_spec_string ("license-uri",
-                               "License-uri",
-                               "The usage license's uri for more information",
-                               "",
-                               (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-  g_object_class_install_property (object_class, PROP_LICENSE_URI, pspec);
-
-  /**
-  * ChamplainMapSource:max-zoom-level:
-  *
-  * The maximum zoom level
-  *
-  * Since: 0.4
-  */
-  pspec = g_param_spec_uint ("max-zoom-level",
-                             "Maximum Zoom Level",
-                             "The maximum zoom level",
-                             0,
-                             50,
-                             18,
-                             (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-  g_object_class_install_property (object_class, PROP_MAX_ZOOM_LEVEL, pspec);
-
-  /**
-  * ChamplainMapSource:min-zoom-level:
-  *
-  * The minimum zoom level
-  *
-  * Since: 0.4
-  */
-  pspec = g_param_spec_uint ("min-zoom-level",
-                             "Minimum Zoom Level",
-                             "The minimum zoom level",
-                             0,
-                             50,
-                             0,
-                             (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-  g_object_class_install_property (object_class, PROP_MIN_ZOOM_LEVEL, pspec);
-
-  /**
-  * ChamplainMapSource:tile-size:
-  *
-  * The tile size of the map source
-  *
-  * Since: 0.4
-  */
-  pspec = g_param_spec_uint ("tile-size",
-                             "Tile Size",
-                             "The tile size",
-                             0,
-                             2048,
-                             256,
-                             (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-  g_object_class_install_property (object_class, PROP_TILE_SIZE, pspec);
-
-  /**
-  * ChamplainMapSource:projection
-  *
-  * The map projection of the map source
-  *
-  * Since: 0.4
-  */
-  pspec = g_param_spec_enum ("projection",
-                             "Projection",
-                             "The map projection",
-                             CHAMPLAIN_TYPE_MAP_PROJECTION,
-                             CHAMPLAIN_MAP_PROJECTION_MERCATOR,
-                             (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-  g_object_class_install_property (object_class, PROP_MAP_PROJECTION, pspec);
+  pspec = g_param_spec_object ("next-source",
+                               "Next Source",
+                               "Next source in the loading chain",
+                               CHAMPLAIN_TYPE_MAP_SOURCE,
+                               G_PARAM_READWRITE);
+  g_object_class_install_property (object_class, PROP_NEXT_SOURCE, pspec);
 
   /**
   * ChamplainMapSource::reload-tiles:
@@ -333,439 +156,203 @@ champlain_map_source_class_init (ChamplainMapSourceClass *klass)
   * Since: 0.6
   */
   champlain_map_source_signals[RELOAD_TILES] =
-      g_signal_new ("reload-tiles", G_OBJECT_CLASS_TYPE (object_class),
-          G_SIGNAL_RUN_LAST, 0, NULL, NULL,
-          g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
-          0, NULL);
+    g_signal_new ("reload-tiles", G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
+                  0, NULL);
 }
 
 static void
-champlain_map_source_init (ChamplainMapSource *champlainMapSource)
+champlain_map_source_init (ChamplainMapSource *map_source)
 {
-  ChamplainMapSourcePrivate *priv = GET_PRIVATE (champlainMapSource);
-  champlainMapSource->priv = priv;
+  ChamplainMapSourcePrivate *priv = GET_PRIVATE(map_source);
+  priv->next_source = NULL;
+  priv->sig_handler_id = 0;
 }
 
-/**
- * champlain_map_source_get_max_zoom_level:
- * @map_source: a #ChamplainMapSource
- *
- * Returns: the maximum zoom level this map source supports
- *
- * Since: 0.4
- */
-gint
-champlain_map_source_get_max_zoom_level (ChamplainMapSource *map_source)
+ChamplainMapSource *
+champlain_map_source_get_next_source (ChamplainMapSource *map_source)
 {
-  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), NULL);
 
-  ChamplainMapSourcePrivate *priv = map_source->priv;
-  return priv->max_zoom_level;
+  ChamplainMapSourcePrivate *priv = GET_PRIVATE(map_source);
+  return priv->next_source;
 }
 
-/**
- * champlain_map_source_get_min_zoom_level:
- * @map_source: a #ChamplainMapSource
- *
- * Returns: the miminum zoom level this map source supports
- *
- * Since: 0.4
- */
-gint
-champlain_map_source_get_min_zoom_level (ChamplainMapSource *map_source)
+static
+void reload_tiles_cb(ChamplainMapSource *orig, ChamplainMapSource *self)
 {
-  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
-
-  ChamplainMapSourcePrivate *priv = map_source->priv;
-  return priv->min_zoom_level;
+  /* propagate the signal up the chain */
+  g_signal_emit_by_name (self, "reload-tiles", NULL);
 }
 
-/**
- * champlain_map_source_get_tile_size:
- * @map_source: a #ChamplainMapSource
- *
- * Returns: the tile's size (width and height) in pixels for this map source
- *
- * Since: 0.4
- */
-guint
-champlain_map_source_get_tile_size (ChamplainMapSource *map_source)
+void
+champlain_map_source_set_next_source (ChamplainMapSource *map_source,
+                                      ChamplainMapSource *next_source)
 {
-  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
+  g_return_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source));
 
-  ChamplainMapSourcePrivate *priv = map_source->priv;
-  return priv->tile_size;
-}
+  ChamplainMapSourcePrivate *priv = GET_PRIVATE(map_source);
 
-/**
- * champlain_map_source_get_x:
- * @map_source: a #ChamplainMapSource
- * @zoom_level: the zoom level
- * @longitude: a longitude
- *
- * Returns: the x position on the map using this map source's projection.
- * (0, 0) is located at the top left.
- *
- * Since: 0.4
- */
-guint
-champlain_map_source_get_x (ChamplainMapSource *map_source,
-    gint zoom_level,
-    gdouble longitude)
-{
-  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
+  if (priv->next_source != NULL)
+    {
+      if (g_signal_handler_is_connected (priv->next_source, priv->sig_handler_id))
+        g_signal_handler_disconnect (priv->next_source, priv->sig_handler_id);
 
-  ChamplainMapSourcePrivate *priv = map_source->priv;
-  // FIXME: support other projections
-  return ((longitude + 180.0) / 360.0 * pow(2.0, zoom_level)) * priv->tile_size;
-}
+      g_object_unref(priv->next_source);
+    }
 
-/**
- * champlain_map_source_get_y:
- * @map_source: a #ChamplainMapSource
- * @zoom_level: the zoom level
- * @latitude: a latitude
- *
- * Returns: the y position on the map using this map source's projection.
- * (0, 0) is located at the top left.
- *
- * Since: 0.4
- */
-guint
-champlain_map_source_get_y (ChamplainMapSource *map_source,
-    gint zoom_level,
-    gdouble latitude)
-{
-  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
+  if (next_source)
+    {
+      g_return_if_fail (CHAMPLAIN_IS_MAP_SOURCE (next_source));
 
-  ChamplainMapSourcePrivate *priv = map_source->priv;
-  // FIXME: support other projections
-  return ((1.0 - log (tan (latitude * M_PI / 180.0) + 1.0 /
-          cos (latitude * M_PI / 180.0)) /
-        M_PI) / 2.0 * pow (2.0, zoom_level)) * priv->tile_size;
-}
+      g_object_ref_sink(next_source);
 
-/**
- * champlain_map_source_get_row_count:
- * @map_source: a #ChamplainMapSource
- * @zoom_level: the zoom level
- *
- * Returns: the number of tiles in a row at this zoom level for this map source.
- *
- * Since: 0.4
- */
-guint
-champlain_map_source_get_row_count (ChamplainMapSource *map_source,
-    gint zoom_level)
-{
-  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
+      priv->sig_handler_id = g_signal_connect (next_source, "reload-tiles",
+                             G_CALLBACK (reload_tiles_cb), map_source);
+    }
 
-  //ChamplainMapSourcePrivate *priv = map_source->priv;
-  // FIXME: support other projections
-  return pow (2, zoom_level);
+  priv->next_source = next_source;
+
+  g_object_notify (G_OBJECT (map_source), "next-source");
 }
 
-/**
- * champlain_map_source_get_column_count:
- * @map_source: a #ChamplainMapSource
- * @zoom_level: the zoom level
- *
- * Returns: the number of tiles in a column at this zoom level for this map
- * source.
- *
- * Since: 0.4
- */
-guint
-champlain_map_source_get_column_count (ChamplainMapSource *map_source,
-    gint zoom_level)
+const gchar *
+champlain_map_source_get_id (ChamplainMapSource *map_source)
 {
-  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), NULL);
 
-  //ChamplainMapSourcePrivate *priv = map_source->priv;
-  // FIXME: support other projections
-  return pow (2, zoom_level);
+  return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_id (map_source);
 }
 
-/**
- * champlain_map_source_fill_tile:
- * @map_source: a #ChamplainMapSource
- * @tile: A #ChamplainTile
- *
- * Fills the tile with image data (either from cache, network or rendered
- * locally).
- *
- * Since: 0.4
- */
-void
-champlain_map_source_fill_tile (ChamplainMapSource *map_source,
-    ChamplainTile *tile)
+const gchar *
+champlain_map_source_get_name (ChamplainMapSource *map_source)
 {
-  g_return_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source));
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), NULL);
 
-  CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->fill_tile (map_source, tile);
+  return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_name (map_source);
 }
 
-/**
- * champlain_map_source_get_longitude:
- * @map_source: a #ChamplainMapSource
- * @zoom_level: the zoom level
- * @x: a x position
- *
- * Returns: the longitude corresponding to this x position in the map source's
- * projection.
- *
- * Since: 0.4
- */
-gdouble
-champlain_map_source_get_longitude (ChamplainMapSource *map_source,
-    gint zoom_level,
-    guint x)
+const gchar *
+champlain_map_source_get_license (ChamplainMapSource *map_source)
 {
-  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
-  //ChamplainMapSourcePrivate *priv = map_source->priv;
-  // FIXME: support other projections
-  gdouble dx = (float)x / champlain_map_source_get_tile_size (map_source);
-  return dx / pow (2.0, zoom_level) * 360.0 - 180;
-}
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), NULL);
 
-/**
- * champlain_map_source_get_latitude:
- * @map_source: a #ChamplainMapSource
- * @zoom_level: the zoom level
- * @y: a y position
- *
- * Returns: the latitude corresponding to this y position in the map source's
- * projection.
- *
- * Since: 0.4
- */
-gdouble
-champlain_map_source_get_latitude (ChamplainMapSource *map_source,
-    gint zoom_level,
-    guint y)
-{
-  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
-  //ChamplainMapSourcePrivate *priv = map_source->priv;
-  // FIXME: support other projections
-  gdouble dy = (float)y / champlain_map_source_get_tile_size (map_source);
-  double n = M_PI - 2.0 * M_PI * dy / pow (2.0, zoom_level);
-  return 180.0 / M_PI * atan (0.5 * (exp (n) - exp (-n)));
+  return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_license (map_source);
 }
 
-/**
- * champlain_map_source_get_name:
- * @map_source: a #ChamplainMapSource
- *
- * Returns: the map source's name.
- *
- * Since: 0.4
- */
 const gchar *
-champlain_map_source_get_name (ChamplainMapSource *map_source)
+champlain_map_source_get_license_uri (ChamplainMapSource *map_source)
 {
-  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), NULL);
 
-  ChamplainMapSourcePrivate *priv = map_source->priv;
-  return priv->name;
+  return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_license_uri (map_source);
 }
 
-/**
- * champlain_map_source_set_name:
- * @map_source: a #ChamplainMapSource
- * @name: a name
- *
- * Sets the map source's name.
- *
- * Since: 0.4
- */
-void
-champlain_map_source_set_name (ChamplainMapSource *map_source,
-    const gchar *name)
+guint
+champlain_map_source_get_min_zoom_level (ChamplainMapSource *map_source)
 {
-  g_return_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source));
-
-  ChamplainMapSourcePrivate *priv = map_source->priv;
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
 
-  g_free (priv->name);
-  priv->name = g_strdup (name);
-  g_object_notify (G_OBJECT (map_source), "name");
+  return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_min_zoom_level (map_source);
 }
 
-/**
- * champlain_map_source_get_license:
- * @map_source: a #ChamplainMapSource
- *
- * Returns: the map source's license.
- *
- * Since: 0.4
- */
-const gchar *
-champlain_map_source_get_license (ChamplainMapSource *map_source)
+guint
+champlain_map_source_get_max_zoom_level (ChamplainMapSource *map_source)
 {
   g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
 
-  ChamplainMapSourcePrivate *priv = map_source->priv;
-  return priv->license;
+  return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_max_zoom_level (map_source);
 }
 
-/**
- * champlain_map_source_set_license:
- * @map_source: a #ChamplainMapSource
- * @license: the licence
- *
- * Sets the map source's license.
- *
- * Since: 0.4
- */
-void
-champlain_map_source_set_license (ChamplainMapSource *map_source,
-    const gchar *license)
+guint
+champlain_map_source_get_tile_size (ChamplainMapSource *map_source)
 {
-  g_return_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source));
-
-  ChamplainMapSourcePrivate *priv = map_source->priv;
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
 
-  g_free (priv->license);
-  priv->license = g_strdup (license);
-  g_object_notify (G_OBJECT (map_source), "license");
+  return CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->get_tile_size (map_source);
 }
 
-/**
- * champlain_map_source_get_license_uri:
- * @map_source: a #ChamplainMapSource
- *
- * Returns: the map source's license URI.
- *
- * Since: 0.4
- */
-const gchar *
-champlain_map_source_get_license_uri (ChamplainMapSource *map_source)
+guint
+champlain_map_source_get_x (ChamplainMapSource *map_source,
+                            guint zoom_level,
+                            gdouble longitude)
 {
   g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
 
-  ChamplainMapSourcePrivate *priv = map_source->priv;
-
-  return priv->license_uri;
+  // FIXME: support other projections
+  return ((longitude + 180.0) / 360.0 * pow(2.0, zoom_level)) * champlain_map_source_get_tile_size (map_source);
 }
 
-/**
- * champlain_map_source_set_license_uri:
- * @map_source: a #ChamplainMapSource
- * @license_uri: the licence URI
- *
- * Sets the map source's license URI.
- *
- * Since: 0.4
- */
-void
-champlain_map_source_set_license_uri (ChamplainMapSource *map_source,
-    const gchar *license_uri)
+guint
+champlain_map_source_get_y (ChamplainMapSource *map_source,
+                            guint zoom_level,
+                            gdouble latitude)
 {
-  g_return_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source));
-
-  ChamplainMapSourcePrivate *priv = map_source->priv;
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
 
-  g_free (priv->license_uri);
-  priv->license = g_strdup (license_uri);
-  g_object_notify (G_OBJECT (map_source), "license-uri");
+  // FIXME: support other projections
+  return ((1.0 - log (tan (latitude * M_PI / 180.0) + 1.0 /
+                      cos (latitude * M_PI / 180.0)) /
+           M_PI) / 2.0 * pow (2.0, zoom_level)) * champlain_map_source_get_tile_size (map_source);
 }
 
-/**
- * champlain_map_source_get_projection:
- * @map_source: a #ChamplainMapSource
- *
- * Returns: the map source's projection.
- *
- * Since: 0.4
- */
-ChamplainMapProjection
-champlain_map_source_get_projection (ChamplainMapSource *map_source)
+gdouble
+champlain_map_source_get_longitude (ChamplainMapSource *map_source,
+                                    guint zoom_level,
+                                    guint x)
 {
-  ChamplainMapSourcePrivate *priv = map_source->priv;
-
-  return priv->map_projection;
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
+  //ChamplainMapSourcePrivate *priv = map_source->priv;
+  // FIXME: support other projections
+  gdouble dx = (float)x / champlain_map_source_get_tile_size (map_source);
+  return dx / pow (2.0, zoom_level) * 360.0 - 180;
 }
 
-/**
- * champlain_map_source_set_projection:
- * @map_source: a #ChamplainMapSource
- * @projection: a #ChamplainMapProjection
- *
- * Sets the map source's projection.
- *
- * Since: 0.4
- */
-void
-champlain_map_source_set_projection (ChamplainMapSource *map_source,
-    ChamplainMapProjection projection)
+gdouble
+champlain_map_source_get_latitude (ChamplainMapSource *map_source,
+                                   guint zoom_level,
+                                   guint y)
 {
-  g_return_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source));
-
-  ChamplainMapSourcePrivate *priv = map_source->priv;
-
-  priv->map_projection = projection;
-  g_object_notify (G_OBJECT (map_source), "projection");
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
+  //ChamplainMapSourcePrivate *priv = map_source->priv;
+  // FIXME: support other projections
+  gdouble dy = (float)y / champlain_map_source_get_tile_size (map_source);
+  gdouble n = M_PI - 2.0 * M_PI * dy / pow (2.0, zoom_level);
+  return 180.0 / M_PI * atan (0.5 * (exp (n) - exp (-n)));
 }
 
-/**
- * champlain_map_source_get_id:
- * @map_source: a #ChamplainMapSource
- *
- * Returns: the map source's id.
- *
- * Since: 0.4
- */
-const gchar *
-champlain_map_source_get_id (ChamplainMapSource *map_source)
+guint
+champlain_map_source_get_row_count (ChamplainMapSource *map_source,
+                                    guint zoom_level)
 {
   g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
 
-  ChamplainMapSourcePrivate *priv = map_source->priv;
-  return priv->id;
+  //ChamplainMapSourcePrivate *priv = map_source->priv;
+  // FIXME: support other projections
+  return pow (2, zoom_level);
 }
 
-/**
- * champlain_map_source_set_id:
- * @map_source: a #ChamplainMapSource
- * @id: a id
- *
- * Sets the map source's id.
- *
- * Since: 0.4
- */
-void
-champlain_map_source_set_id (ChamplainMapSource *map_source,
-    const gchar *id)
+guint
+champlain_map_source_get_column_count (ChamplainMapSource *map_source,
+                                       guint zoom_level)
 {
-  g_return_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source));
-
-  ChamplainMapSourcePrivate *priv = map_source->priv;
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
 
-  g_free (priv->id);
-  priv->id = g_strdup (id);
-  g_object_notify (G_OBJECT (map_source), "id");
+  //ChamplainMapSourcePrivate *priv = map_source->priv;
+  // FIXME: support other projections
+  return pow (2, zoom_level);
 }
 
-#define EARTH_RADIUS 6378137 /* meters, Equatorial radius */
+#define EARTH_RADIUS 6378137.0 /* meters, Equatorial radius */
 
-/**
- * champlain_map_source_get_meters_per_pixel:
- * @map_source: a #ChamplainMapSource
- * @zoom_level: the zoom level
- * @latitude: a latitude
- * @longitude: a longitude
- *
- * Returns: the meters per pixel at the position on the map using this map source's projection.
- *
- * Since: 0.4.3
- */
-gfloat
+gdouble
 champlain_map_source_get_meters_per_pixel (ChamplainMapSource *map_source,
-    gint zoom_level,
+    guint zoom_level,
     gdouble latitude,
     gdouble longitude)
 {
-  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0.0);
 
   /* Width is in pixels. (1 px)
      m/px = radius_at_latitude / width_in_pixels
@@ -773,61 +360,17 @@ champlain_map_source_get_meters_per_pixel (ChamplainMapSource *map_source,
      radius_at_latitude = 2Ï? * k * sin (Ï?/2-θ)
   */
 
-  ChamplainMapSourcePrivate *priv = map_source->priv;
+  gdouble tile_size = champlain_map_source_get_tile_size (map_source);
   // FIXME: support other projections
-  return 2 * M_PI * EARTH_RADIUS * sin (M_PI/2 - M_PI / 180 * latitude) /
-    (priv->tile_size * champlain_map_source_get_row_count (map_source, zoom_level));
+  return 2.0 * M_PI * EARTH_RADIUS * sin (M_PI/2.0 - M_PI / 180.0 * latitude) /
+         (tile_size * champlain_map_source_get_row_count (map_source, zoom_level));
 }
 
-/* protected function */
-static ClutterActor *error_actor = NULL;
-
 void
-create_error_tile (ChamplainTile* tile)
+champlain_map_source_fill_tile (ChamplainMapSource *map_source,
+                                ChamplainTile *tile)
 {
-  ClutterActor *clone;
-
-  if (!error_actor)
-    {
-      guint size;
-      ClutterActor *actor;
-      cairo_t *cr;
-      cairo_pattern_t *pat;
-      ClutterActor *stage;
-
-      size = champlain_tile_get_size (tile);
-      actor = clutter_cairo_texture_new (size, size);
-      cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (actor));
-      stage = clutter_stage_get_default ();
-
-      /* draw a linear gray to white pattern */
-      pat = cairo_pattern_create_linear (size / 2.0, 0.0,  size, size / 2.0);
-      cairo_pattern_add_color_stop_rgb (pat, 0, 0.686, 0.686, 0.686);
-      cairo_pattern_add_color_stop_rgb (pat, 1, 0.925, 0.925, 0.925);
-      cairo_set_source (cr, pat);
-      cairo_rectangle (cr, 0, 0, size, size);
-      cairo_fill (cr);
-
-      cairo_pattern_destroy (pat);
-
-      /* draw the red cross */
-      cairo_set_source_rgb (cr, 0.424, 0.078, 0.078);
-      cairo_set_line_width (cr, 14.0);
-      cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
-      cairo_move_to (cr, 24, 24);
-      cairo_line_to (cr, 50, 50);
-      cairo_move_to (cr, 50, 24);
-      cairo_line_to (cr, 24, 50);
-      cairo_stroke (cr);
-
-      cairo_destroy (cr);
-
-      clutter_actor_show (actor);
-      clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);
-      error_actor = actor;
-    }
+  g_return_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source));
 
-  clone = clutter_clone_new (error_actor);
-  champlain_tile_set_content (tile, clone, TRUE);
-  champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
+  CHAMPLAIN_MAP_SOURCE_GET_CLASS (map_source)->fill_tile (map_source, tile);
 }
diff --git a/champlain/champlain-map-source.h b/champlain/champlain-map-source.h
index a543b5c..7436b33 100644
--- a/champlain/champlain-map-source.h
+++ b/champlain/champlain-map-source.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2009 Pierre-Luc Beaudoin <pierre-luc pierlux com>
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -20,105 +20,84 @@
 #error "Only <champlain/champlain.h> can be included directly."
 #endif
 
-#ifndef CHAMPLAIN_MAP_SOURCE_H
-#define CHAMPLAIN_MAP_SOURCE_H
+#ifndef _CHAMPLAIN_MAP_SOURCE_H_
+#define _CHAMPLAIN_MAP_SOURCE_H_
 
 #include <champlain/champlain-defines.h>
 #include <champlain/champlain-tile.h>
 #include <champlain/champlain-zoom-level.h>
 
-#include <glib-object.h>
-
 G_BEGIN_DECLS
 
-#define CHAMPLAIN_TYPE_MAP_SOURCE     (champlain_map_source_get_type())
-#define CHAMPLAIN_MAP_SOURCE(obj)     (G_TYPE_CHECK_INSTANCE_CAST((obj), CHAMPLAIN_TYPE_MAP_SOURCE, ChamplainMapSource))
-#define CHAMPLAIN_MAP_SOURCE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  CHAMPLAIN_TYPE_MAP_SOURCE, ChamplainMapSourceClass))
-#define CHAMPLAIN_IS_MAP_SOURCE(obj)  (G_TYPE_CHECK_INSTANCE_TYPE((obj), CHAMPLAIN_TYPE_MAP_SOURCE))
-#define CHAMPLAIN_IS_MAP_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  CHAMPLAIN_TYPE_MAP_SOURCE))
-#define CHAMPLAIN_MAP_SOURCE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  CHAMPLAIN_TYPE_MAP_SOURCE, ChamplainMapSourceClass))
+#define CHAMPLAIN_TYPE_MAP_SOURCE             (champlain_map_source_get_type ())
+#define CHAMPLAIN_MAP_SOURCE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHAMPLAIN_TYPE_MAP_SOURCE, ChamplainMapSource))
+#define CHAMPLAIN_MAP_SOURCE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), CHAMPLAIN_TYPE_MAP_SOURCE, ChamplainMapSourceClass))
+#define CHAMPLAIN_IS_MAP_SOURCE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CHAMPLAIN_TYPE_MAP_SOURCE))
+#define CHAMPLAIN_IS_MAP_SOURCE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), CHAMPLAIN_TYPE_MAP_SOURCE))
+#define CHAMPLAIN_MAP_SOURCE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), CHAMPLAIN_TYPE_MAP_SOURCE, ChamplainMapSourceClass))
 
-typedef struct _ChamplainMapSource ChamplainMapSource;
 typedef struct _ChamplainMapSourceClass ChamplainMapSourceClass;
-typedef struct _ChamplainMapSourcePrivate ChamplainMapSourcePrivate;
-
-typedef enum
-{
-  CHAMPLAIN_MAP_PROJECTION_MERCATOR
-} ChamplainMapProjection;
 
 struct _ChamplainMapSource
 {
-  GInitiallyUnowned parent;
-  ChamplainMapSourcePrivate *priv;
+  GInitiallyUnowned parent_instance;
 };
 
 struct _ChamplainMapSourceClass
 {
   GInitiallyUnownedClass parent_class;
 
+  const gchar * (*get_id) (ChamplainMapSource *map_source);
+  const gchar * (*get_name) (ChamplainMapSource *map_source);
+  const gchar * (*get_license) (ChamplainMapSource *map_source);
+  const gchar * (*get_license_uri) (ChamplainMapSource *map_source);
+  guint (*get_min_zoom_level) (ChamplainMapSource *map_source);
+  guint (*get_max_zoom_level) (ChamplainMapSource *map_source);
+  guint (*get_tile_size) (ChamplainMapSource *map_source);
+
   void (*fill_tile) (ChamplainMapSource *map_source,
-                    ChamplainTile *tile);
+                     ChamplainTile *tile);
 };
 
 GType champlain_map_source_get_type (void);
 
-gint champlain_map_source_get_min_zoom_level (ChamplainMapSource *map_source);
-
-gint champlain_map_source_get_max_zoom_level (ChamplainMapSource *map_source);
+ChamplainMapSource *champlain_map_source_get_next_source (ChamplainMapSource *map_source);
+void champlain_map_source_set_next_source (ChamplainMapSource *map_source,
+    ChamplainMapSource *next_source);
 
+const gchar * champlain_map_source_get_id (ChamplainMapSource *map_source);
+const gchar * champlain_map_source_get_name (ChamplainMapSource *map_source);
+const gchar * champlain_map_source_get_license (ChamplainMapSource *map_source);
+const gchar * champlain_map_source_get_license_uri (ChamplainMapSource *map_source);
+guint champlain_map_source_get_min_zoom_level (ChamplainMapSource *map_source);
+guint champlain_map_source_get_max_zoom_level (ChamplainMapSource *map_source);
 guint champlain_map_source_get_tile_size (ChamplainMapSource *map_source);
 
 guint champlain_map_source_get_x (ChamplainMapSource *map_source,
-    gint zoom_level,
-    gdouble longitude);
-
+                                  guint zoom_level,
+                                  gdouble longitude);
 guint champlain_map_source_get_y (ChamplainMapSource *map_source,
-    gint zoom_level,
-    gdouble latitude);
-
+                                  guint zoom_level,
+                                  gdouble latitude);
 gdouble champlain_map_source_get_longitude (ChamplainMapSource *map_source,
-    gint zoom_level,
+    guint zoom_level,
     guint x);
-
 gdouble champlain_map_source_get_latitude (ChamplainMapSource *map_source,
-    gint zoom_level,
+    guint zoom_level,
     guint y);
-
 guint champlain_map_source_get_row_count (ChamplainMapSource *map_source,
-    gint zoom_level);
-
+    guint zoom_level);
 guint champlain_map_source_get_column_count (ChamplainMapSource *map_source,
-    gint zoom_level);
-
-void champlain_map_source_fill_tile (ChamplainMapSource *map_source,
-    ChamplainTile *tile);
-
-void champlain_map_source_set_id (ChamplainMapSource *map_source,
-    const gchar *id);
-const gchar * champlain_map_source_get_id (ChamplainMapSource *map_source);
-
-void champlain_map_source_set_name (ChamplainMapSource *map_source,
-    const gchar *name);
-const gchar * champlain_map_source_get_name (ChamplainMapSource *map_source);
-
-void champlain_map_source_set_license (ChamplainMapSource *map_source,
-    const gchar *license);
-const gchar * champlain_map_source_get_license (ChamplainMapSource *map_source);
-
-void champlain_map_source_set_license_uri (ChamplainMapSource *map_source,
-    const gchar *license_uri);
-const gchar * champlain_map_source_get_license_uri (ChamplainMapSource *map_source);
-
-void champlain_map_source_set_projection (ChamplainMapSource *map_source,
-    ChamplainMapProjection projection);
-ChamplainMapProjection champlain_map_source_get_projection (ChamplainMapSource *map_source);
-
-gfloat champlain_map_source_get_meters_per_pixel (ChamplainMapSource *map_source,
-    gint zoom_level,
+    guint zoom_level);
+gdouble champlain_map_source_get_meters_per_pixel (ChamplainMapSource *map_source,
+    guint zoom_level,
     gdouble latitude,
     gdouble longitude);
 
+void champlain_map_source_fill_tile (ChamplainMapSource *map_source,
+                                     ChamplainTile *tile);
+
 G_END_DECLS
 
-#endif
+#endif /* _CHAMPLAIN_MAP_SOURCE_H_ */
+
diff --git a/champlain/champlain-memphis-map-source.c b/champlain/champlain-memphis-map-source.c
index 73673a7..a72a669 100644
--- a/champlain/champlain-memphis-map-source.c
+++ b/champlain/champlain-memphis-map-source.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009 Simon Wenner <simon wenner ch>
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -45,18 +46,18 @@
 
 #define DEBUG_FLAG CHAMPLAIN_DEBUG_MEMPHIS
 #include "champlain-debug.h"
-#include "champlain-cache.h"
+#include "champlain-tile-cache.h"
 #include "champlain-defines.h"
 #include "champlain-enum-types.h"
 #include "champlain-private.h"
 
+#include <gdk/gdk.h>
+
 #include <errno.h>
 #include <string.h>
 
 /* Tuning parameters */
 #define MAX_THREADS 4
-#define DEFAULT_TILE_SIZE 256 // FIXME: switching between map sources
-                              // with different tile size crashes champlain.
 
 const gchar default_rules[] =
   "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
@@ -69,39 +70,46 @@ const gchar default_rules[] =
 enum
 {
   PROP_0,
-  PROP_MAP_DATA_SOURCE,
-  PROP_SESSION_ID,
-  PROP_PERSISTENT_CACHE
+  PROP_MAP_DATA_SOURCE
 };
 
-G_DEFINE_TYPE (ChamplainMemphisMapSource, champlain_memphis_map_source, CHAMPLAIN_TYPE_MAP_SOURCE)
+G_DEFINE_TYPE (ChamplainMemphisMapSource, champlain_memphis_map_source, CHAMPLAIN_TYPE_TILE_SOURCE)
 
 #define GET_PRIVATE(o) \
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), CHAMPLAIN_TYPE_MEMPHIS_MAP_SOURCE, ChamplainMemphisMapSourcePrivate))
 
 typedef struct _ChamplainMemphisMapSourcePrivate ChamplainMemphisMapSourcePrivate;
 
-struct _ChamplainMemphisMapSourcePrivate {
+struct _ChamplainMemphisMapSourcePrivate
+{
   ChamplainMapDataSource *map_data_source;
-  gchar *session_id;
   MemphisRuleSet *rules;
   MemphisRenderer *renderer;
   GThreadPool *thpool;
   gboolean no_map_data;
-  gboolean persistent_cache;
 };
 
-typedef struct _TileData TileData;
+typedef struct _TileLoadedData TileLoadedData;
 
-struct _TileData {
+struct _TileLoadedData
+{
+  ChamplainMapSource *map_source;
   ChamplainTile *tile;
   cairo_surface_t *cst;
-  gchar *session_id;
 };
 
 /* lock to protect the renderer state while rendering */
 GStaticRWLock MemphisLock = G_STATIC_RW_LOCK_INIT;
 
+static void fill_tile (ChamplainMapSource *map_source, ChamplainTile *tile);
+
+static void reload_tiles (ChamplainMemphisMapSource *self);
+static void memphis_worker_thread (gpointer data, gpointer user_data);
+static void map_data_changed_cb (ChamplainMapDataSource *map_data_source,
+                                 GParamSpec *gobject,
+                                 ChamplainMemphisMapSource *map_source);
+void argb_to_rgba(guchar *data, guint size);
+
 static void
 champlain_memphis_map_source_get_property (GObject *object,
     guint property_id,
@@ -113,17 +121,11 @@ champlain_memphis_map_source_get_property (GObject *object,
 
   switch (property_id)
     {
-      case PROP_MAP_DATA_SOURCE:
-        g_value_set_object (value, priv->map_data_source);
-        break;
-      case PROP_SESSION_ID:
-        g_value_set_string (value, priv->session_id);
-        break;
-      case PROP_PERSISTENT_CACHE:
-        g_value_set_boolean (value, priv->persistent_cache);
-        break;
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    case PROP_MAP_DATA_SOURCE:
+      g_value_set_object (value, priv->map_data_source);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
 }
 
@@ -134,30 +136,22 @@ champlain_memphis_map_source_set_property (GObject *object,
     GParamSpec *pspec)
 {
   ChamplainMemphisMapSource *self = CHAMPLAIN_MEMPHIS_MAP_SOURCE (object);
-  ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE (self);
 
   switch (property_id)
     {
-      case PROP_MAP_DATA_SOURCE:
-        champlain_memphis_map_source_set_map_data_source (self,
-            g_value_get_object (value));
-        break;
-      case PROP_SESSION_ID:
-        champlain_memphis_map_source_set_session_id (self,
-            g_value_get_string (value));
-        break;
-      case PROP_PERSISTENT_CACHE:
-        priv->persistent_cache = g_value_get_boolean (value);
-        break;
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    case PROP_MAP_DATA_SOURCE:
+      champlain_memphis_map_source_set_map_data_source (self,
+          g_value_get_object (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
 }
 
 static void
 champlain_memphis_map_source_dispose (GObject *object)
 {
-  ChamplainMemphisMapSource *self = (ChamplainMemphisMapSource *) object;
+  ChamplainMemphisMapSource *self = CHAMPLAIN_MEMPHIS_MAP_SOURCE(object);
   ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE(self);
 
   if (priv->thpool)
@@ -187,21 +181,120 @@ champlain_memphis_map_source_dispose (GObject *object)
 static void
 champlain_memphis_map_source_finalize (GObject *object)
 {
-  ChamplainMemphisMapSource *self = (ChamplainMemphisMapSource *) object;
+  G_OBJECT_CLASS (champlain_memphis_map_source_parent_class)->finalize (object);
+}
+
+static void
+champlain_memphis_map_source_constructed (GObject *object)
+{
+  ChamplainMapSource *map_source = CHAMPLAIN_MAP_SOURCE(object);
+  ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE(object);
+
+  memphis_renderer_set_resolution (priv->renderer, champlain_map_source_get_tile_size(map_source));
+
+  G_OBJECT_CLASS (champlain_memphis_map_source_parent_class)->constructed (object);
+}
+
+static void
+champlain_memphis_map_source_class_init (ChamplainMemphisMapSourceClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ChamplainMapSourceClass *map_source_class = CHAMPLAIN_MAP_SOURCE_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (ChamplainMemphisMapSourcePrivate));
+
+  object_class->get_property = champlain_memphis_map_source_get_property;
+  object_class->set_property = champlain_memphis_map_source_set_property;
+  object_class->dispose = champlain_memphis_map_source_dispose;
+  object_class->finalize = champlain_memphis_map_source_finalize;
+  object_class->constructed = champlain_memphis_map_source_constructed;
+
+  map_source_class->fill_tile = fill_tile;
+
+  /**
+  * ChamplainMemphisMapSource:map-data-source:
+  *
+  * The data source of the renderer
+  *
+  * Since: 0.6
+  */
+  g_object_class_install_property (object_class,
+                                   PROP_MAP_DATA_SOURCE,
+                                   g_param_spec_object ("map-data-source",
+                                       "Map data source",
+                                       "The data source of the renderer",
+                                       CHAMPLAIN_TYPE_MAP_DATA_SOURCE,
+                                       G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
+}
+
+static void
+champlain_memphis_map_source_init (ChamplainMemphisMapSource *self)
+{
   ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE(self);
 
-  g_free (priv->session_id);
+  priv->map_data_source = NULL;
+  priv->rules = NULL;
+  priv->renderer = NULL;
+  priv->no_map_data = TRUE;
 
-  G_OBJECT_CLASS (champlain_memphis_map_source_parent_class)->finalize (object);
+  priv->rules = memphis_rule_set_new ();
+  memphis_rule_set_load_from_data (priv->rules, default_rules,
+                                   strlen (default_rules));
+
+  priv->renderer = memphis_renderer_new_full (priv->rules, memphis_map_new ());
+
+  priv->thpool = g_thread_pool_new (memphis_worker_thread, self,
+                                    MAX_THREADS, FALSE, NULL);
+}
+
+/**
+ * champlain_memphis_map_source_new_full:
+ * @id: the map source's id
+ * @name: the map source's name
+ * @license: the map source's license
+ * @license_uri: the map source's license URI
+ * @min_zoom: the map source's minimum zoom level
+ * @max_zoom: the map source's maximum zoom level
+ * @tile_size: the map source's tile size (in pixels)
+ * @projection: the map source's projection
+ * @map_data_source: a #ChamplainMapDataSource
+ *
+ * Returns: a new ChamplainMemphisMapSource.
+ *
+ * Since: 0.6
+ */
+ChamplainMemphisMapSource* champlain_memphis_map_source_new_full (const gchar *id,
+    const gchar *name,
+    const gchar *license,
+    const gchar *license_uri,
+    guint min_zoom_level,
+    guint max_zoom_level,
+    guint tile_size,
+    ChamplainMapProjection projection,
+    ChamplainMapDataSource *map_data_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_DATA_SOURCE (map_data_source), NULL);
+
+  return g_object_new (CHAMPLAIN_TYPE_MEMPHIS_MAP_SOURCE,
+                       "id", id,
+                       "name", name,
+                       "license", license,
+                       "license-uri", license_uri,
+                       "min-zoom-level", min_zoom_level,
+                       "max-zoom-level", max_zoom_level,
+                       "tile-size", tile_size,
+                       "projection", projection,
+                       "map-data-source", map_data_source,
+                       NULL);
 }
 
 static void
 map_data_changed_cb (ChamplainMapDataSource *map_data_source,
-    GParamSpec *gobject,
-    ChamplainMemphisMapSource *map_source)
+                     GParamSpec *gobject,
+                     ChamplainMemphisMapSource *map_source)
 {
   g_assert (CHAMPLAIN_IS_MAP_DATA_SOURCE (map_data_source) &&
-      CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (map_source));
+            CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (map_source));
 
   MemphisMap *map;
   ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE(map_source);
@@ -218,9 +311,7 @@ map_data_changed_cb (ChamplainMapDataSource *map_data_source,
       priv->no_map_data = TRUE;
     }
   else
-    {
-      priv->no_map_data = FALSE;
-    }
+    priv->no_map_data = FALSE;
 
   DEBUG ("DataSource has been changed!");
 
@@ -228,118 +319,88 @@ map_data_changed_cb (ChamplainMapDataSource *map_data_source,
   memphis_renderer_set_map (priv->renderer, map);
   g_static_rw_lock_writer_unlock (&MemphisLock);
 
-  if (!priv->persistent_cache)
-    champlain_memphis_map_source_delete_session_cache (map_source);
+  reload_tiles (map_source);
 }
 
-static void
-fill_tile (ChamplainMapSource *map_source, ChamplainTile *tile)
+/*
+Transform ARGB (Cairo) to RGBA (GdkPixbuf). RGBA is actualy reversed in
+memory, so the transformation is ARGB -> ABGR (i.e. swapping B and R)
+*/
+void argb_to_rgba(guchar *data, guint size)
 {
-  ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE(map_source);
-  ChamplainCache* cache = champlain_cache_dup_default ();
-  guint size;
-  GError *error = NULL;
-  gchar *filename;
-  gboolean in_cache = FALSE;
-
-  size = champlain_map_source_get_tile_size (map_source);
-  champlain_tile_set_size (tile, size);
-
-  filename = champlain_cache_get_filename (cache, map_source, tile,
-      priv->session_id);
-  champlain_tile_set_filename (tile, filename);
-
-  in_cache = champlain_cache_fill_tile (cache, tile);
-
-  /* check for cached version */
-  if (in_cache == TRUE)
-    {
-      DEBUG ("Tile was cached (%u, %u, %u)", champlain_tile_get_x (tile),
-          champlain_tile_get_y (tile),
-          champlain_tile_get_zoom_level (tile));
-      champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
-    }
-  else if (priv->no_map_data)
-    {
-      DEBUG ("No tile data (%u, %u, %u)",
-          champlain_tile_get_x (tile),
-          champlain_tile_get_y (tile),
-          champlain_tile_get_zoom_level (tile));
-
-      create_error_tile (tile);
-    }
-  else
-    {
-      DEBUG ("Render tile (%u, %u, %u)", champlain_tile_get_x (tile),
-          champlain_tile_get_y (tile),
-          champlain_tile_get_zoom_level (tile));
-      champlain_tile_set_state (tile, CHAMPLAIN_STATE_LOADING);
-
-      /* So we don't loose a tile if it is in the thread pool queue for a long time */
-      tile = g_object_ref (tile);
-
-      g_thread_pool_push (priv->thpool, tile, &error);
-      if (error)
-        {
-          g_error ("Thread pool error: %s", error->message);
-          g_error_free (error);
-        }
-    }
-  g_object_unref (cache);
+  guint32 *ptr;
+  guint32 *endptr = (guint32 *)data + size / 4;
+  for (ptr = (guint32 *)data; ptr < endptr; ptr++)
+    *ptr = (*ptr & 0xFF00FF00) ^ ((*ptr & 0xFF0000) >> 16) ^ ((*ptr & 0xFF) << 16);
 }
 
 static gboolean
-set_tile_content (gpointer data)
+tile_loaded_cb (gpointer data)
 {
-  TileData *tdata = (TileData *) data;
+  TileLoadedData *tdata = (TileLoadedData *) data;
+  ChamplainMapSource *map_source = tdata->map_source;
   ChamplainTile *tile = tdata->tile;
   cairo_surface_t *cst = tdata->cst;
-  gchar *session = tdata->session_id;
+  ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE(map_source);
+  ChamplainTileCache *tile_cache = champlain_tile_source_get_cache(tile_source);
   cairo_t *cr_clutter;
   ClutterActor *actor;
   guint size;
-  ChamplainCache *cache = champlain_cache_dup_default ();
+  GError *error = NULL;
 
-  /* update the cache */
-  champlain_cache_update_tile_with_session (cache, tile, 20, session);
+  g_free (tdata);
 
-  g_object_unref (cache);
+  // FIXME - once memphis detects when tile cannot be rendered, call fill_tile
+  // on next_source here and return
 
-  /* draw the clutter texture */
   size = champlain_tile_get_size (tile);
+
+  /* draw the clutter texture */
   actor = clutter_cairo_texture_new (size, size);
 
   cr_clutter = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (actor));
   cairo_set_source_surface (cr_clutter, cst, 0, 0);
   cairo_paint (cr_clutter);
   cairo_destroy (cr_clutter);
-  cairo_surface_destroy (cst);
 
-  champlain_tile_set_content (tile, actor, TRUE);
-
-  champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
-  g_object_unref (tile);
-
-  g_free (tdata);
-  return FALSE;
-}
-
-static gboolean
-delete_session_cache (gpointer data)
-{
-  ChamplainMemphisMapSource *self = CHAMPLAIN_MEMPHIS_MAP_SOURCE (data);
-  ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE(self);
-  ChamplainCache* cache = champlain_cache_dup_default ();
+  /* update the cache */
+  if (tile_cache)
+    {
+      GdkPixbuf * pixbuf;
+      gchar *buffer;
+      gsize buffer_size;
+
+      /* modify directly the buffer of cairo surface - we don't use it any more
+         and we close the surface anyway */
+      argb_to_rgba(cairo_image_surface_get_data(cst),
+                   cairo_image_surface_get_stride(cst) * cairo_image_surface_get_height(cst));
+
+      pixbuf = gdk_pixbuf_new_from_data(cairo_image_surface_get_data(cst),
+                                        GDK_COLORSPACE_RGB,
+                                        TRUE,
+                                        8,
+                                        size,
+                                        size,
+                                        cairo_image_surface_get_stride(cst),
+                                        NULL,
+                                        NULL);
+
+      if (gdk_pixbuf_save_to_buffer(pixbuf, &buffer, &buffer_size, "png", &error, NULL))
+        {
+          champlain_tile_cache_store_tile(tile_cache, tile, buffer, buffer_size);
+        }
 
-  champlain_cache_delete_session (cache, CHAMPLAIN_MAP_SOURCE (self),
-      priv->session_id);
+      g_free(buffer);
+      g_object_unref(pixbuf);
+    }
 
-  DEBUG ("Delete '%s' session cache", priv->session_id);
+  cairo_surface_destroy (cst);
 
-  g_object_unref (cache);
+  champlain_tile_set_content (tile, actor, TRUE);
+  champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
 
-  g_signal_emit_by_name (CHAMPLAIN_MAP_SOURCE (self),
-      "reload-tiles", NULL);
+  g_object_unref (tile);
+  g_object_unref (map_source);
 
   return FALSE;
 }
@@ -347,13 +408,12 @@ delete_session_cache (gpointer data)
 static void
 memphis_worker_thread (gpointer data, gpointer user_data)
 {
-  ChamplainTile *tile = CHAMPLAIN_TILE (data);
-  ChamplainMemphisMapSource *map_source = CHAMPLAIN_MEMPHIS_MAP_SOURCE (user_data);
-  ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE(map_source);
+  ChamplainTile *tile = (ChamplainTile *)data;
+  ChamplainMapSource *map_source = (ChamplainMapSource *)user_data;
   cairo_t *cr;
   cairo_surface_t *cst;
   guint x, y, z, size;
-  TileData *tdata;
+  TileLoadedData *loaded_data;
 
   x = champlain_tile_get_x (tile);
   y = champlain_tile_get_y (tile);
@@ -366,177 +426,82 @@ memphis_worker_thread (gpointer data, gpointer user_data)
 
   DEBUG ("Draw Tile (%d, %d, %d)", x, y, z);
 
+  // FIXME - memphis should support multithreaded access to utilize multiple CPUs
   g_static_rw_lock_reader_lock (&MemphisLock);
-  memphis_renderer_draw_tile (priv->renderer, cr, x, y, z);
+  // FIXME - memphis needs to indicate if it cannot render the tile so we can
+  // load the tile from the next map source
+  memphis_renderer_draw_tile (GET_PRIVATE(map_source)->renderer, cr, x, y, z);
   g_static_rw_lock_reader_unlock (&MemphisLock);
-  cairo_destroy (cr);
-
-  const gchar *filename = champlain_tile_get_filename (tile);
-  /* Create, if needed, the cache's dirs */
-  char *path = g_path_get_dirname (filename);
 
-  if (g_mkdir_with_parents (path, 0700) == -1)
-    {
-      if (errno != EEXIST)
-        {
-          g_warning ("Unable to create the image cache path '%s': %s",
-                     path, g_strerror (errno));
-        }
-    }
-  g_free (path);
-
-  /* Write png image for caching */
-  if (cairo_surface_write_to_png (cst, filename) != CAIRO_STATUS_SUCCESS)
-    {
-      g_warning ("Unable to write image '%s' to the cache", filename);
-    }
+  cairo_destroy (cr);
 
   /* Write the tile content and cache entry */
-  tdata = g_new (TileData, 1);
-  tdata->tile = tile;
-  tdata->cst = cst;
-  tdata->session_id = priv->session_id;
+  loaded_data = g_new (TileLoadedData, 1);
+  loaded_data->map_source = map_source;
+  loaded_data->tile = tile;
+  loaded_data->cst = cst;
 
-  clutter_threads_add_idle_full (G_PRIORITY_DEFAULT, set_tile_content,
-      tdata, NULL);
+  clutter_threads_add_idle_full (G_PRIORITY_DEFAULT, tile_loaded_cb,
+                                 loaded_data, NULL);
 }
 
 static void
-champlain_memphis_map_source_class_init (ChamplainMemphisMapSourceClass *klass)
+fill_tile (ChamplainMapSource *map_source, ChamplainTile *tile)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-  g_type_class_add_private (klass, sizeof (ChamplainMemphisMapSourcePrivate));
+  g_return_if_fail (CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (map_source));
 
-  object_class->get_property = champlain_memphis_map_source_get_property;
-  object_class->set_property = champlain_memphis_map_source_set_property;
-  object_class->dispose = champlain_memphis_map_source_dispose;
-  object_class->finalize = champlain_memphis_map_source_finalize;
+  ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE(map_source);
 
-  ChamplainMapSourceClass *map_source_class = CHAMPLAIN_MAP_SOURCE_CLASS (klass);
-  map_source_class->fill_tile = fill_tile;
+  DEBUG ("Render tile (%u, %u, %u)", champlain_tile_get_x (tile),
+         champlain_tile_get_y (tile),
+         champlain_tile_get_zoom_level (tile));
 
-  /**
-  * ChamplainMemphisMapSource:map-data-source:
-  *
-  * The data source of the renderer
-  *
-  * Since: 0.6
-  */
-  g_object_class_install_property (object_class,
-      PROP_MAP_DATA_SOURCE,
-      g_param_spec_object ("map-data-source",
-        "Map data source",
-        "The data source of the renderer",
-        CHAMPLAIN_TYPE_MAP_DATA_SOURCE,
-        G_PARAM_READWRITE));
+  if (priv->no_map_data)
+    {
+      ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
 
-  /**
-  * ChamplainMemphisMapSource:session:
-  *
-  * The session id of the tile cache
-  *
-  * Since: 0.6
-  */
-  g_object_class_install_property (object_class,
-      PROP_SESSION_ID,
-      g_param_spec_string ("session-id",
-        "Cache session id",
-        "The session id of the cache",
-        "default",
-        G_PARAM_READWRITE));
+      if (CHAMPLAIN_IS_MAP_SOURCE(next_source))
+        champlain_map_source_fill_tile(next_source, tile);
+    }
+  else
+    {
+      GError *error = NULL;
+      guint size;
 
-  /**
-  * ChamplainMemphisMapSource:persistent-cache:
-  *
-  * If the session cache should be deleted if data or rules are changed.
-  * If enabled the client has to manage the cache explicitly with
-  * champlain_memphis_map_source_delete_session_cache().
-  *
-  * Since: 0.6
-  */
-  g_object_class_install_property (object_class,
-      PROP_PERSISTENT_CACHE,
-      g_param_spec_boolean ("persistent-cache",
-        "Persistent cache",
-        "If the cache is persistent",
-        FALSE,
-        G_PARAM_READWRITE));
-}
+      size = champlain_map_source_get_tile_size (map_source);
+      champlain_tile_set_size (tile, size);
 
-static void
-champlain_memphis_map_source_init (ChamplainMemphisMapSource *self)
-{
-  ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE(self);
+      g_object_ref (tile);
+      g_object_ref (map_source);
 
-  priv->map_data_source = NULL;
-  priv->rules = NULL;
-  priv->renderer = NULL;
-  priv->thpool = NULL;
-  priv->session_id = g_strdup ("default");
-  priv->no_map_data = TRUE;
-  priv->persistent_cache = FALSE;
+      g_thread_pool_push (priv->thpool, tile, &error);
+      if (error)
+        {
+          g_error ("Thread pool error: %s", error->message);
+          g_error_free (error);
+          g_object_unref(map_source);
+          g_object_unref(tile);
+        }
+    }
 }
 
-/**
- * champlain_memphis_map_source_new_full:
- * @desc: a #ChamplainMapSourceDesc
- * @map_data_source: a #ChamplainMapDataSource
- *
- * Returns: a new ChamplainMemphisMapSource.
- *
- * Since: 0.6
- */
-ChamplainMemphisMapSource *
-champlain_memphis_map_source_new_full (ChamplainMapSourceDesc *desc,
-    ChamplainMapDataSource *map_data_source)
+static void
+reload_tiles (ChamplainMemphisMapSource *self)
 {
-  g_return_val_if_fail (CHAMPLAIN_IS_MAP_DATA_SOURCE (map_data_source) &&
-      CHAMPLAIN_MAP_SOURCE_DESC (desc), NULL);
-
-  ChamplainMemphisMapSource *source;
-  ChamplainMemphisMapSourcePrivate *priv;
-  MemphisMap *map;
-
-  source = g_object_new (CHAMPLAIN_TYPE_MEMPHIS_MAP_SOURCE,
-      "id", desc->id,
-      "name", desc->name,
-      "license", desc->license,
-      "license-uri", desc->license_uri,
-      "projection", desc->projection,
-      "min-zoom-level", desc->min_zoom_level,
-      "max-zoom-level", desc->max_zoom_level,
-      "tile-size", DEFAULT_TILE_SIZE,
-      NULL);
-
-  priv = GET_PRIVATE(source);
-  priv->map_data_source = g_object_ref (map_data_source);
+  g_return_if_fail (CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (self));
 
-  g_signal_connect (priv->map_data_source, "notify::state",
-      G_CALLBACK (map_data_changed_cb), source);
+  ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE(self);
+  ChamplainTileCache *tile_cache = champlain_tile_source_get_cache(tile_source);
 
-  priv->rules = memphis_rule_set_new ();
-  map = champlain_map_data_source_get_map_data (priv->map_data_source);
-  if (map == NULL)
+  if (tile_cache && !champlain_tile_cache_get_persistent(tile_cache))
     {
-      map = memphis_map_new ();
-      priv->no_map_data = TRUE;
-    }
-  else
-    {
-      priv->no_map_data = FALSE;
-    }
-
-  priv->renderer = memphis_renderer_new_full (priv->rules, map);
-  memphis_renderer_set_resolution (priv->renderer, DEFAULT_TILE_SIZE);
-
-  memphis_rule_set_load_from_data (priv->rules, default_rules,
-      strlen (default_rules));
+      DEBUG ("Clean temporary cache");
 
-  priv->thpool = g_thread_pool_new (memphis_worker_thread, source,
-      MAX_THREADS, FALSE, NULL);
+      champlain_tile_cache_clean (tile_cache);
+    }
 
-  return source;
+  g_signal_emit_by_name (CHAMPLAIN_MAP_SOURCE (self),
+                         "reload-tiles", NULL);
 }
 
 /**
@@ -550,8 +515,8 @@ champlain_memphis_map_source_new_full (ChamplainMapSourceDesc *desc,
  */
 void
 champlain_memphis_map_source_load_rules (
-    ChamplainMemphisMapSource *self,
-    const gchar *rules_path)
+  ChamplainMemphisMapSource *self,
+  const gchar *rules_path)
 {
   g_return_if_fail (CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (self));
 
@@ -569,11 +534,10 @@ champlain_memphis_map_source_load_rules (
     memphis_rule_set_load_from_file (priv->rules, rules_path);
   else
     memphis_rule_set_load_from_data (priv->rules, default_rules,
-        strlen (default_rules));
+                                     strlen (default_rules));
   g_static_rw_lock_writer_unlock (&MemphisLock);
 
-  if (!priv->persistent_cache)
-    champlain_memphis_map_source_delete_session_cache (self);
+  reload_tiles (self);
 }
 
 /**
@@ -587,17 +551,31 @@ champlain_memphis_map_source_load_rules (
  */
 void
 champlain_memphis_map_source_set_map_data_source (
-    ChamplainMemphisMapSource *self,
-    ChamplainMapDataSource *map_data_source)
+  ChamplainMemphisMapSource *self,
+  ChamplainMapDataSource *map_data_source)
 {
   g_return_if_fail (CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (self) &&
-      CHAMPLAIN_IS_MAP_DATA_SOURCE (map_data_source));
+                    CHAMPLAIN_IS_MAP_DATA_SOURCE (map_data_source));
 
   ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE (self);
   MemphisMap *map;
 
-  priv->map_data_source = map_data_source;
+  if (priv->map_data_source)
+    g_object_unref (priv->map_data_source);
+
+  priv->map_data_source = g_object_ref_sink(map_data_source);
+
+  g_signal_connect (priv->map_data_source, "notify::state",
+                    G_CALLBACK (map_data_changed_cb), self);
+
   map = champlain_map_data_source_get_map_data (priv->map_data_source);
+  if (map == NULL)
+    {
+      map = memphis_map_new ();
+      priv->no_map_data = TRUE;
+    }
+  else
+    priv->no_map_data = FALSE;
 
   g_static_rw_lock_writer_lock (&MemphisLock);
   memphis_renderer_set_map (priv->renderer, map);
@@ -614,7 +592,7 @@ champlain_memphis_map_source_set_map_data_source (
  */
 ChamplainMapDataSource *
 champlain_memphis_map_source_get_map_data_source (
-    ChamplainMemphisMapSource *self)
+  ChamplainMemphisMapSource *self)
 {
   g_return_val_if_fail (CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (self), NULL);
 
@@ -623,64 +601,6 @@ champlain_memphis_map_source_get_map_data_source (
 }
 
 /**
- * champlain_memphis_map_source_delete_session_cache:
- * @map_source: a #ChamplainMemphisMapSource
- *
- * Deletes all cached tiles of the current session.
- *
- * Since: 0.6
- */
-void
-champlain_memphis_map_source_delete_session_cache (ChamplainMemphisMapSource *self)
-{
-  g_return_if_fail (CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (self));
-
-  clutter_threads_add_idle_full (G_PRIORITY_DEFAULT, delete_session_cache,
-      self, NULL);
-}
-
-/**
- * champlain_memphis_map_source_set_session_id:
- * @map_source: a #ChamplainMemphisMapSource
- * @session_id: a session id string
- *
- * Sets the session id of the cache.
- *
- * Since: 0.6
- */
-void
-champlain_memphis_map_source_set_session_id (ChamplainMemphisMapSource *self,
-    const gchar *session_id)
-{
-  g_return_if_fail (CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (self)
-      && session_id != NULL);
-
-  ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE (self);
-
-  if (priv->session_id)
-    g_free (priv->session_id);
-
-  priv->session_id = g_strdup (session_id);
-}
-
-/**
- * champlain_memphis_map_source_get_session_id:
- * @map_source: a #ChamplainMemphisMapSource
- *
- * Returns the session id string.
- *
- * Since: 0.6
- */
-const gchar *
-champlain_memphis_map_source_get_session_id (ChamplainMemphisMapSource *self)
-{
-  g_return_val_if_fail (CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (self), NULL);
-
-  ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE (self);
-  return priv->session_id;
-}
-
-/**
  * champlain_memphis_map_source_get_background_color:
  * @map_source: a #ChamplainMemphisMapSource
  *
@@ -690,7 +610,7 @@ champlain_memphis_map_source_get_session_id (ChamplainMemphisMapSource *self)
  * Since: 0.6
  */
 ClutterColor * champlain_memphis_map_source_get_background_color (
-    ChamplainMemphisMapSource *self)
+  ChamplainMemphisMapSource *self)
 {
   g_return_val_if_fail (CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (self), NULL);
 
@@ -720,8 +640,8 @@ ClutterColor * champlain_memphis_map_source_get_background_color (
  */
 void
 champlain_memphis_map_source_set_background_color (
-    ChamplainMemphisMapSource *self,
-    const ClutterColor *color)
+  ChamplainMemphisMapSource *self,
+  const ClutterColor *color)
 {
   g_return_if_fail (CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (self));
 
@@ -729,11 +649,10 @@ champlain_memphis_map_source_set_background_color (
 
   g_static_rw_lock_writer_lock (&MemphisLock);
   memphis_rule_set_set_bg_color (priv->rules, color->red,
-      color->green, color->blue, color->alpha);
+                                 color->green, color->blue, color->alpha);
   g_static_rw_lock_writer_unlock (&MemphisLock);
 
-  if (!priv->persistent_cache)
-    champlain_memphis_map_source_delete_session_cache (self);
+  reload_tiles (self);
 }
 
 /**
@@ -748,10 +667,10 @@ champlain_memphis_map_source_set_background_color (
  */
 void
 champlain_memphis_map_source_set_rule (ChamplainMemphisMapSource *self,
-    MemphisRule *rule)
+                                       MemphisRule *rule)
 {
   g_return_if_fail (CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (self) &&
-      MEMPHIS_RULE (rule));
+                    MEMPHIS_RULE (rule));
 
   ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE (self);
 
@@ -759,8 +678,7 @@ champlain_memphis_map_source_set_rule (ChamplainMemphisMapSource *self,
   memphis_rule_set_set_rule (priv->rules, rule);
   g_static_rw_lock_writer_unlock (&MemphisLock);
 
-  if (!priv->persistent_cache)
-    champlain_memphis_map_source_delete_session_cache (self);
+  reload_tiles (self);
 }
 
 /**
@@ -774,10 +692,10 @@ champlain_memphis_map_source_set_rule (ChamplainMemphisMapSource *self,
  */
 MemphisRule *
 champlain_memphis_map_source_get_rule (ChamplainMemphisMapSource *self,
-    const gchar *id)
+                                       const gchar *id)
 {
   g_return_val_if_fail (CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (self) &&
-      id != NULL, NULL);
+                        id != NULL, NULL);
 
   ChamplainMemphisMapSourcePrivate *priv = GET_PRIVATE (self);
   MemphisRule *rule;
@@ -825,8 +743,8 @@ champlain_memphis_map_source_get_rule_ids (ChamplainMemphisMapSource *self)
  * Since: 0.6
  */
 void champlain_memphis_map_source_remove_rule (
-    ChamplainMemphisMapSource *self,
-    const gchar *id)
+  ChamplainMemphisMapSource *self,
+  const gchar *id)
 {
   g_return_if_fail (CHAMPLAIN_IS_MEMPHIS_MAP_SOURCE (self));
 
@@ -836,6 +754,5 @@ void champlain_memphis_map_source_remove_rule (
   memphis_rule_set_remove_rule (priv->rules, id);
   g_static_rw_lock_writer_unlock (&MemphisLock);
 
-  if (!priv->persistent_cache)
-    champlain_memphis_map_source_delete_session_cache (self);
+  reload_tiles (self);
 }
diff --git a/champlain/champlain-memphis-map-source.h b/champlain/champlain-memphis-map-source.h
index e49e45c..403bfa6 100644
--- a/champlain/champlain-memphis-map-source.h
+++ b/champlain/champlain-memphis-map-source.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009 Simon Wenner <simon wenner ch>
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -19,9 +20,8 @@
 #ifndef _CHAMPLAIN_MEMPHIS_MAP_SOURCE
 #define _CHAMPLAIN_MEMPHIS_MAP_SOURCE
 
+#include <champlain/champlain-tile-source.h>
 #include <champlain/champlain-map-data-source.h>
-#include <champlain/champlain-map-source.h>
-#include <champlain/champlain-map-source-desc.h>
 #include <memphis/memphis.h>
 
 #include <glib-object.h>
@@ -46,17 +46,23 @@ G_BEGIN_DECLS
   (G_TYPE_INSTANCE_GET_CLASS ((obj), CHAMPLAIN_TYPE_MEMPHIS_MAP_SOURCE, ChamplainMemphisMapSourceClass))
 
 typedef struct {
-  ChamplainMapSource parent;
+  ChamplainTileSource parent;
 } ChamplainMemphisMapSource;
 
 typedef struct {
-  ChamplainMapSourceClass parent_class;
+  ChamplainTileSourceClass parent_class;
 } ChamplainMemphisMapSourceClass;
 
 GType champlain_memphis_map_source_get_type (void);
 
-ChamplainMemphisMapSource * champlain_memphis_map_source_new_full (
-    ChamplainMapSourceDesc *desc,
+ChamplainMemphisMapSource* champlain_memphis_map_source_new_full (const gchar *id,
+    const gchar *name,
+    const gchar *license,
+    const gchar *license_uri,
+    guint min_zoom,
+    guint max_zoom,
+    guint tile_size,
+    ChamplainMapProjection projection,
     ChamplainMapDataSource *map_data_source);
 
 void champlain_memphis_map_source_load_rules (
@@ -70,15 +76,6 @@ void champlain_memphis_map_source_set_map_data_source (
 ChamplainMapDataSource * champlain_memphis_map_source_get_map_data_source (
     ChamplainMemphisMapSource *map_source);
 
-void champlain_memphis_map_source_delete_session_cache (
-    ChamplainMemphisMapSource *map_source);
-
-void champlain_memphis_map_source_set_session_id (
-    ChamplainMemphisMapSource *map_source,
-    const gchar *session_id);
-const gchar * champlain_memphis_map_source_get_session_id (
-    ChamplainMemphisMapSource *map_source);
-
 ClutterColor * champlain_memphis_map_source_get_background_color (
     ChamplainMemphisMapSource *map_source);
 
diff --git a/champlain/champlain-network-tile-source.c b/champlain/champlain-network-tile-source.c
new file mode 100644
index 0000000..f26a854
--- /dev/null
+++ b/champlain/champlain-network-tile-source.c
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2008-2009 Pierre-Luc Beaudoin <pierre-luc pierlux com>
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+
+#include "champlain-network-tile-source.h"
+
+#define DEBUG_FLAG CHAMPLAIN_DEBUG_LOADING
+#include "champlain-debug.h"
+
+#include "champlain.h"
+#include "champlain-defines.h"
+#include "champlain-enum-types.h"
+#include "champlain-map-source.h"
+#include "champlain-marshal.h"
+#include "champlain-private.h"
+#include "champlain-zoom-level.h"
+
+#include <errno.h>
+#include <gdk/gdk.h>
+#include <gio/gio.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#ifdef HAVE_LIBSOUP_GNOME
+#include <libsoup/soup-gnome.h>
+#else
+#include <libsoup/soup.h>
+#endif
+#include <math.h>
+#include <sys/stat.h>
+#include <string.h>
+
+enum
+{
+  PROP_0,
+  PROP_URI_FORMAT,
+  PROP_OFFLINE,
+  PROP_PROXY_URI
+};
+
+G_DEFINE_TYPE (ChamplainNetworkTileSource, champlain_network_tile_source, CHAMPLAIN_TYPE_TILE_SOURCE);
+
+#define GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE((obj), CHAMPLAIN_TYPE_NETWORK_TILE_SOURCE, ChamplainNetworkTileSourcePrivate))
+
+typedef struct _ChamplainNetworkTileSourcePrivate ChamplainNetworkTileSourcePrivate;
+
+struct _ChamplainNetworkTileSourcePrivate
+{
+  gboolean offline;
+  gchar *uri_format;
+  gchar *proxy_uri;
+  SoupSession * soup_session;
+};
+
+typedef struct
+{
+  ChamplainMapSource *map_source;
+  ChamplainTile *tile;
+} TileLoadedCallbackData;
+
+static void fill_tile (ChamplainMapSource *map_source,
+                       ChamplainTile *tile);
+
+static gchar *
+get_tile_uri (ChamplainNetworkTileSource *source,
+              gint x,
+              gint y,
+              gint z);
+
+static void
+champlain_network_tile_source_get_property (GObject *object,
+    guint prop_id,
+    GValue *value,
+    GParamSpec *pspec)
+{
+  ChamplainNetworkTileSourcePrivate *priv = GET_PRIVATE(object);
+
+  switch(prop_id)
+    {
+    case PROP_URI_FORMAT:
+      g_value_set_string (value, priv->uri_format);
+      break;
+    case PROP_OFFLINE:
+      g_value_set_boolean (value, priv->offline);
+      break;
+    case PROP_PROXY_URI:
+      g_value_set_string (value, priv->proxy_uri);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    }
+}
+
+static void
+champlain_network_tile_source_set_property (GObject *object,
+    guint prop_id,
+    const GValue *value,
+    GParamSpec *pspec)
+{
+  ChamplainNetworkTileSource *tile_source = CHAMPLAIN_NETWORK_TILE_SOURCE(object);
+
+  switch(prop_id)
+    {
+    case PROP_URI_FORMAT:
+      champlain_network_tile_source_set_uri_format(tile_source, g_value_get_string (value));
+      break;
+    case PROP_OFFLINE:
+      champlain_network_tile_source_set_offline(tile_source, g_value_get_boolean (value));
+      break;
+    case PROP_PROXY_URI:
+      champlain_network_tile_source_set_proxy_uri(tile_source, g_value_get_string (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    }
+}
+
+static void
+champlain_network_tile_source_dispose (GObject *object)
+{
+  ChamplainNetworkTileSourcePrivate *priv = GET_PRIVATE(object);
+
+  if (priv->soup_session)
+    soup_session_abort (priv->soup_session);
+
+  G_OBJECT_CLASS (champlain_network_tile_source_parent_class)->dispose (object);
+}
+
+static void
+champlain_network_tile_source_finalize (GObject *object)
+{
+  ChamplainNetworkTileSourcePrivate *priv = GET_PRIVATE(object);
+
+  g_free (priv->uri_format);
+  g_free (priv->proxy_uri);
+
+  G_OBJECT_CLASS (champlain_network_tile_source_parent_class)->finalize (object);
+}
+
+static void
+champlain_network_tile_source_constructed  (GObject *object)
+{
+  G_OBJECT_CLASS (champlain_network_tile_source_parent_class)->constructed (object);
+}
+
+static void
+champlain_network_tile_source_class_init (ChamplainNetworkTileSourceClass *klass)
+{
+  ChamplainMapSourceClass *map_source_class = CHAMPLAIN_MAP_SOURCE_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (klass, sizeof (ChamplainNetworkTileSourcePrivate));
+
+  object_class->finalize = champlain_network_tile_source_finalize;
+  object_class->dispose = champlain_network_tile_source_dispose;
+  object_class->get_property = champlain_network_tile_source_get_property;
+  object_class->set_property = champlain_network_tile_source_set_property;
+  object_class->constructed = champlain_network_tile_source_constructed;
+
+  map_source_class->fill_tile = fill_tile;
+
+  pspec = g_param_spec_string ("uri-format",
+                               "URI Format",
+                               "The URI format",
+                               "",
+                               (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class, PROP_URI_FORMAT, pspec);
+
+  pspec = g_param_spec_boolean ("offline",
+                                "Offline",
+                                "Offline",
+                                FALSE,
+                                G_PARAM_READWRITE);
+  g_object_class_install_property (object_class, PROP_OFFLINE, pspec);
+
+  pspec = g_param_spec_string ("proxy-uri",
+                               "Proxy URI",
+                               "The proxy URI to use to access network",
+                               "",
+                               G_PARAM_READWRITE);
+  g_object_class_install_property (object_class, PROP_PROXY_URI, pspec);
+}
+
+static void
+champlain_network_tile_source_init (ChamplainNetworkTileSource *tile_source)
+{
+  ChamplainNetworkTileSourcePrivate *priv = GET_PRIVATE (tile_source);
+
+  priv->proxy_uri = NULL;
+  priv->uri_format = NULL;
+  priv->offline = FALSE;
+
+  priv->soup_session = soup_session_async_new_with_options (
+                         "proxy-uri", NULL,
+#ifdef HAVE_LIBSOUP_GNOME
+                         SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_GNOME,
+#endif
+                         NULL);
+  g_object_set (G_OBJECT (priv->soup_session),
+                "user-agent", "libchamplain/" CHAMPLAIN_VERSION_S,
+                "max-conns-per-host", 2, NULL); // This is as required by OSM
+
+}
+
+ChamplainNetworkTileSource*
+champlain_network_tile_source_new_full (const gchar *id,
+                                        const gchar *name,
+                                        const gchar *license,
+                                        const gchar *license_uri,
+                                        guint min_zoom,
+                                        guint max_zoom,
+                                        guint tile_size,
+                                        ChamplainMapProjection projection,
+                                        const gchar *uri_format)
+{
+  ChamplainNetworkTileSource * source;
+  source = g_object_new (CHAMPLAIN_TYPE_NETWORK_TILE_SOURCE, "id", id,
+                         "name", name, "license", license, "license-uri", license_uri,
+                         "min-zoom-level", min_zoom, "max-zoom-level", max_zoom,
+                         "tile-size", tile_size, "projection", projection,
+                         "uri-format", uri_format, NULL);
+  return source;
+}
+
+const gchar *
+champlain_network_tile_source_get_uri_format (ChamplainNetworkTileSource *tile_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_NETWORK_TILE_SOURCE (tile_source), NULL);
+
+  ChamplainNetworkTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+  return priv->uri_format;
+}
+
+void
+champlain_network_tile_source_set_uri_format (ChamplainNetworkTileSource *tile_source,
+    const gchar *uri_format)
+{
+  g_return_if_fail (CHAMPLAIN_IS_NETWORK_TILE_SOURCE (tile_source));
+
+  ChamplainNetworkTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+
+  g_free (priv->uri_format);
+  priv->uri_format = g_strdup (uri_format);
+
+  g_object_notify (G_OBJECT (tile_source), "uri-format");
+}
+
+const gchar *
+champlain_network_tile_source_get_proxy_uri (ChamplainNetworkTileSource *tile_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_NETWORK_TILE_SOURCE (tile_source), NULL);
+
+  ChamplainNetworkTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+  return priv->proxy_uri;
+}
+
+void
+champlain_network_tile_source_set_proxy_uri (ChamplainNetworkTileSource *tile_source,
+    const gchar *proxy_uri)
+{
+  g_return_if_fail (CHAMPLAIN_IS_NETWORK_TILE_SOURCE (tile_source));
+
+  ChamplainNetworkTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+  SoupURI *uri = NULL;
+
+  g_free (priv->proxy_uri);
+  priv->proxy_uri = g_strdup (proxy_uri);
+
+  if (priv->proxy_uri)
+    uri = soup_uri_new (priv->proxy_uri);
+
+  if (priv->soup_session)
+    g_object_set (G_OBJECT (priv->soup_session),
+                  "proxy-uri", uri,
+                  NULL);
+
+  if (uri)
+    g_object_unref (uri);
+
+  g_object_notify (G_OBJECT (tile_source), "proxy-uri");
+}
+
+gboolean
+champlain_network_tile_source_get_offline (ChamplainNetworkTileSource *tile_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_NETWORK_TILE_SOURCE (tile_source), FALSE);
+
+  ChamplainNetworkTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+  return priv->offline;
+}
+
+void
+champlain_network_tile_source_set_offline (ChamplainNetworkTileSource *tile_source,
+    gboolean offline)
+{
+  g_return_if_fail (CHAMPLAIN_IS_NETWORK_TILE_SOURCE (tile_source));
+
+  ChamplainNetworkTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+
+  priv->offline = offline;
+
+  g_object_notify (G_OBJECT (tile_source), "offline");
+}
+
+#define SIZE 8
+static gchar *
+get_tile_uri (ChamplainNetworkTileSource *source,
+              gint x,
+              gint y,
+              gint z)
+{
+  ChamplainNetworkTileSourcePrivate *priv = GET_PRIVATE(source);
+
+  gchar **tokens;
+  gchar *token;
+  GString *ret;
+  gint i = 0;
+
+  tokens = g_strsplit (priv->uri_format, "#", 20);
+  token = tokens[i];
+  ret = g_string_sized_new (strlen (priv->uri_format));
+
+  while (token != NULL)
+    {
+      gint number = G_MAXINT;
+      gchar value[SIZE];
+
+      if (strcmp (token, "X") == 0)
+        number = x;
+      if (strcmp (token, "Y") == 0)
+        number = y;
+      if (strcmp (token, "Z") == 0)
+        number = z;
+
+      if (number != G_MAXINT)
+        {
+          g_snprintf (value, SIZE, "%d", number);
+          g_string_append (ret, value);
+        }
+      else
+        g_string_append (ret, token);
+
+      token = tokens[++i];
+    }
+
+  token = ret->str;
+  g_string_free (ret, FALSE);
+  g_strfreev (tokens);
+
+  return token;
+}
+
+static void
+tile_loaded_cb (SoupSession *session,
+                SoupMessage *msg,
+                gpointer user_data)
+{
+  TileLoadedCallbackData *callback_data = (TileLoadedCallbackData *)user_data;
+  ChamplainMapSource *map_source = callback_data->map_source;
+  ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE(map_source);
+  ChamplainTileCache *tile_cache = champlain_tile_source_get_cache(tile_source);
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
+  ChamplainTile *tile = callback_data->tile;
+  GdkPixbufLoader* loader;
+  GError *error = NULL;
+  ClutterActor *actor;
+  const gchar *etag;
+
+  g_free(user_data);
+
+  DEBUG ("Got reply %d", msg->status_code);
+
+  if (msg->status_code == SOUP_STATUS_CANCELLED)
+    {
+      DEBUG ("Download of tile %d, %d got cancelled",
+             champlain_tile_get_x (tile), champlain_tile_get_y (tile));
+      //champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
+
+      g_object_unref (tile);
+      g_object_unref (map_source);
+      return;
+    }
+
+  if (msg->status_code == SOUP_STATUS_NOT_MODIFIED)
+    {
+      if (tile_cache)
+        champlain_tile_cache_refresh_tile_time(tile_cache, tile);
+      goto finish;
+    }
+
+  if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+    {
+      DEBUG ("Unable to download tile %d, %d: %s",
+             champlain_tile_get_x (tile),
+             champlain_tile_get_y (tile),
+             soup_status_get_phrase (msg->status_code));
+
+      goto load_next;
+    }
+
+  /* Load the data from the http response */
+  loader = gdk_pixbuf_loader_new();
+  if (!gdk_pixbuf_loader_write (loader,
+                                (const guchar *) msg->response_body->data,
+                                msg->response_body->length,
+                                &error))
+    {
+      if (error)
+        {
+          g_warning ("Unable to load the pixbuf: %s", error->message);
+          g_error_free (error);
+        }
+
+      goto load_next_cleanup;
+    }
+
+  gdk_pixbuf_loader_close (loader, &error);
+  if (error)
+    {
+      g_warning ("Unable to close the pixbuf loader: %s", error->message);
+      g_error_free (error);
+      goto load_next_cleanup;
+    }
+
+  /* Verify if the server sent an etag and save it */
+  etag = soup_message_headers_get (msg->response_headers, "ETag");
+  DEBUG ("Received ETag %s", etag);
+
+  if (etag != NULL)
+    champlain_tile_set_etag (tile, etag);
+
+  if (tile_cache)
+    champlain_tile_cache_store_tile (tile_cache, tile, msg->response_body->data, msg->response_body->length);
+
+  /* Load the image into clutter */
+  GdkPixbuf* pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+  actor = clutter_texture_new ();
+  if (!clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (actor),
+                                          gdk_pixbuf_get_pixels (pixbuf),
+                                          gdk_pixbuf_get_has_alpha (pixbuf),
+                                          gdk_pixbuf_get_width (pixbuf),
+                                          gdk_pixbuf_get_height (pixbuf),
+                                          gdk_pixbuf_get_rowstride (pixbuf),
+                                          gdk_pixbuf_get_bits_per_sample (pixbuf) *
+                                          gdk_pixbuf_get_n_channels (pixbuf) / 8,
+                                          0, &error))
+    {
+      if (error)
+        {
+          g_warning ("Unable to transfer to clutter: %s", error->message);
+          g_error_free (error);
+        }
+
+      g_object_unref (actor);
+      goto load_next_cleanup;
+    }
+
+  champlain_tile_set_content (tile, actor, TRUE);
+  champlain_tile_set_size (tile, champlain_map_source_get_tile_size (map_source));
+
+  goto finish;
+
+load_next_cleanup:
+  g_object_unref (loader);
+
+load_next:
+  if (next_source)
+    {
+      champlain_map_source_fill_tile(next_source, tile);
+    }
+  g_object_unref (tile);
+  g_object_unref (map_source);
+  return;
+
+finish:
+  champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
+  g_object_unref (tile);
+  g_object_unref (map_source);
+}
+
+static void
+fill_tile (ChamplainMapSource *map_source,
+           ChamplainTile *tile)
+{
+  g_return_if_fail (CHAMPLAIN_IS_NETWORK_TILE_SOURCE (map_source));
+  g_return_if_fail (CHAMPLAIN_IS_TILE (tile));
+
+  ChamplainNetworkTileSource *tile_source = CHAMPLAIN_NETWORK_TILE_SOURCE (map_source);
+  ChamplainNetworkTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+
+  if (!priv->offline)
+    {
+      TileLoadedCallbackData *callback_data;
+      SoupMessage *msg;
+      gchar *uri;
+
+      uri = get_tile_uri (tile_source,
+                          champlain_tile_get_x (tile),
+                          champlain_tile_get_y (tile),
+                          champlain_tile_get_zoom_level (tile));
+
+      msg = soup_message_new (SOUP_METHOD_GET, uri);
+
+      if (champlain_tile_get_content (tile))
+        {
+          /* validate tile */
+
+          const gchar *etag = champlain_tile_get_etag (tile);
+          gchar *date = champlain_tile_get_modified_time_string (tile);
+
+          /* If an etag is available, only use it.
+           * OSM servers seems to send now as the modified time for all tiles
+           * Omarender servers set the modified time correctly
+           */
+          if (etag)
+            {
+              DEBUG("If-None-Match: %s", etag);
+              soup_message_headers_append (msg->request_headers,
+                                           "If-None-Match", etag);
+            }
+          else if (date)
+            {
+              DEBUG("If-Modified-Since %s", date);
+              soup_message_headers_append (msg->request_headers,
+                                           "If-Modified-Since", date);
+            }
+
+          g_free (date);
+        }
+
+      callback_data = g_new(TileLoadedCallbackData, 1);
+      callback_data->tile = tile;
+      callback_data->map_source = map_source;
+
+      /* Ref the tile as it may be freeing during the loading
+       * Unref when the loading is done.
+       */
+      g_object_ref (tile);
+      g_object_ref (map_source);
+
+      soup_session_queue_message (priv->soup_session, msg,
+                                  tile_loaded_cb,
+                                  callback_data);
+    }
+  else
+    {
+      ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
+
+      if (CHAMPLAIN_IS_MAP_SOURCE(next_source))
+          champlain_map_source_fill_tile(next_source, tile);
+    }
+}
diff --git a/champlain/champlain-network-tile-source.h b/champlain/champlain-network-tile-source.h
new file mode 100644
index 0000000..bd7ba63
--- /dev/null
+++ b/champlain/champlain-network-tile-source.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008-2009 Pierre-Luc Beaudoin <pierre-luc pierlux com>
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#if !defined (__CHAMPLAIN_CHAMPLAIN_H_INSIDE__) && !defined (CHAMPLAIN_COMPILATION)
+#error "Only <champlain/champlain.h> can be included directly."
+#endif
+
+#ifndef _CHAMPLAIN_NETWORK_TILE_SOURCE_H_
+#define _CHAMPLAIN_NETWORK_TILE_SOURCE_H_
+
+#include <champlain/champlain-defines.h>
+#include <champlain/champlain-tile-source.h>
+
+G_BEGIN_DECLS
+
+#define CHAMPLAIN_TYPE_NETWORK_TILE_SOURCE             (champlain_network_tile_source_get_type ())
+#define CHAMPLAIN_NETWORK_TILE_SOURCE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHAMPLAIN_TYPE_NETWORK_TILE_SOURCE, ChamplainNetworkTileSource))
+#define CHAMPLAIN_NETWORK_TILE_SOURCE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), CHAMPLAIN_TYPE_NETWORK_TILE_SOURCE, ChamplainNetworkTileSourceClass))
+#define CHAMPLAIN_IS_NETWORK_TILE_SOURCE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CHAMPLAIN_TYPE_NETWORK_TILE_SOURCE))
+#define CHAMPLAIN_IS_NETWORK_TILE_SOURCE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), CHAMPLAIN_TYPE_NETWORK_TILE_SOURCE))
+#define CHAMPLAIN_NETWORK_TILE_SOURCE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), CHAMPLAIN_TYPE_NETWORK_TILE_SOURCE, ChamplainNetworkTileSourceClass))
+
+typedef struct _ChamplainNetworkTileSource ChamplainNetworkTileSource;
+typedef struct _ChamplainNetworkTileSourceClass ChamplainNetworkTileSourceClass;
+
+struct _ChamplainNetworkTileSource
+{
+  ChamplainTileSource parent_instance;
+};
+
+struct _ChamplainNetworkTileSourceClass
+{
+  ChamplainTileSourceClass parent_class;
+};
+
+GType champlain_network_tile_source_get_type (void);
+
+ChamplainNetworkTileSource* champlain_network_tile_source_new_full (const gchar *id,
+    const gchar *name,
+    const gchar *license,
+    const gchar *license_uri,
+    guint min_zoom,
+    guint max_zoom,
+    guint tile_size,
+    ChamplainMapProjection projection,
+    const gchar *uri_format);
+
+const gchar * champlain_network_tile_source_get_uri_format (ChamplainNetworkTileSource *source);
+void champlain_network_tile_source_set_uri_format (ChamplainNetworkTileSource *source,
+    const gchar *uri_format);
+
+gboolean champlain_network_tile_source_get_offline (ChamplainNetworkTileSource *source);
+void champlain_network_tile_source_set_offline (ChamplainNetworkTileSource *source,
+    gboolean offline);
+
+const gchar * champlain_network_tile_source_get_proxy_uri (ChamplainNetworkTileSource *source);
+void champlain_network_tile_source_set_proxy_uri (ChamplainNetworkTileSource *source,
+    const gchar *proxy_uri);
+
+G_END_DECLS
+
+#endif /* _CHAMPLAIN_NETWORK_TILE_SOURCE_H_ */
diff --git a/champlain/champlain-private.h b/champlain/champlain-private.h
index e42993e..7455fe7 100644
--- a/champlain/champlain-private.h
+++ b/champlain/champlain-private.h
@@ -21,7 +21,6 @@
 
 #include <glib.h>
 #include <clutter/clutter.h>
-#include "champlain-tile.h"
 
 typedef struct _Map Map;
 
@@ -60,6 +59,4 @@ typedef struct
   gint height;
 } ChamplainRectangle;
 
-void create_error_tile (ChamplainTile* tile);
-
 #endif
diff --git a/champlain/champlain-tile-cache.c b/champlain/champlain-tile-cache.c
new file mode 100644
index 0000000..5d5e43b
--- /dev/null
+++ b/champlain/champlain-tile-cache.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "champlain-tile-cache.h"
+
+G_DEFINE_TYPE (ChamplainTileCache, champlain_tile_cache, CHAMPLAIN_TYPE_MAP_SOURCE);
+
+#define GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE((obj), CHAMPLAIN_TYPE_TILE_CACHE, ChamplainTileCachePrivate))
+
+enum
+{
+  PROP_0,
+  PROP_PERSISTENT_CACHE
+};
+
+typedef struct _ChamplainTileCachePrivate ChamplainTileCachePrivate;
+
+struct _ChamplainTileCachePrivate
+{
+  gboolean persistent;
+};
+
+static const gchar *get_id (ChamplainMapSource *map_source);
+static const gchar *get_name (ChamplainMapSource *map_source);
+static const gchar *get_license (ChamplainMapSource *map_source);
+static const gchar *get_license_uri (ChamplainMapSource *map_source);
+static guint get_min_zoom_level (ChamplainMapSource *map_source);
+static guint get_max_zoom_level (ChamplainMapSource *map_source);
+static guint get_tile_size (ChamplainMapSource *map_source);
+
+static void
+champlain_tile_cache_get_property (GObject *object,
+                                   guint property_id,
+                                   GValue *value,
+                                   GParamSpec *pspec)
+{
+  ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE (object);
+
+  switch (property_id)
+    {
+    case PROP_PERSISTENT_CACHE:
+      g_value_set_boolean (value, champlain_tile_cache_get_persistent (tile_cache));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+champlain_tile_cache_set_property (GObject *object,
+                                   guint property_id,
+                                   const GValue *value,
+                                   GParamSpec *pspec)
+{
+  ChamplainTileCachePrivate *priv = GET_PRIVATE (object);
+
+  switch (property_id)
+    {
+    case PROP_PERSISTENT_CACHE:
+      priv->persistent = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+champlain_tile_cache_dispose (GObject *object)
+{
+  G_OBJECT_CLASS (champlain_tile_cache_parent_class)->dispose (object);
+}
+
+static void
+champlain_tile_cache_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (champlain_tile_cache_parent_class)->finalize (object);
+}
+
+static void
+champlain_tile_cache_constructed  (GObject *object)
+{
+  G_OBJECT_CLASS (champlain_tile_cache_parent_class)->constructed (object);
+}
+
+static void
+champlain_tile_cache_class_init (ChamplainTileCacheClass *klass)
+{
+  GObjectClass* object_class = G_OBJECT_CLASS (klass);
+  ChamplainMapSourceClass *map_source_class = CHAMPLAIN_MAP_SOURCE_CLASS (klass);
+  ChamplainTileCacheClass *tile_cache_class = CHAMPLAIN_TILE_CACHE_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (klass, sizeof (ChamplainTileCachePrivate));
+
+  object_class->finalize = champlain_tile_cache_finalize;
+  object_class->dispose = champlain_tile_cache_dispose;
+  object_class->get_property = champlain_tile_cache_get_property;
+  object_class->set_property = champlain_tile_cache_set_property;
+  object_class->constructed = champlain_tile_cache_constructed;
+
+  map_source_class->get_id = get_id;
+  map_source_class->get_name = get_name;
+  map_source_class->get_license = get_license;
+  map_source_class->get_license_uri = get_license_uri;
+  map_source_class->get_min_zoom_level = get_min_zoom_level;
+  map_source_class->get_max_zoom_level = get_max_zoom_level;
+  map_source_class->get_tile_size = get_tile_size;
+
+  map_source_class->fill_tile = NULL;
+
+  tile_cache_class->refresh_tile_time = NULL;
+  tile_cache_class->on_tile_filled = NULL;
+  tile_cache_class->store_tile = NULL;
+  tile_cache_class->clean = NULL;
+
+  pspec = g_param_spec_boolean ("persistent-cache",
+                                "Persistent Cache",
+                                "Specifies whether the cache is persistent",
+                                FALSE,
+                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+  g_object_class_install_property (object_class, PROP_PERSISTENT_CACHE, pspec);
+}
+
+static void
+champlain_tile_cache_init (ChamplainTileCache *tile_cache)
+{
+  ChamplainTileCachePrivate *priv = GET_PRIVATE (tile_cache);
+
+  priv->persistent = FALSE;
+}
+
+gboolean
+champlain_tile_cache_get_persistent (ChamplainTileCache *tile_cache)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_CACHE (tile_cache), FALSE);
+
+  ChamplainTileCachePrivate *priv = GET_PRIVATE(tile_cache);
+  return priv->persistent;
+}
+
+void
+champlain_tile_cache_store_tile (ChamplainTileCache *tile_cache,
+                                 ChamplainTile *tile,
+                                 const gchar *contents,
+                                 gsize size)
+{
+  g_return_if_fail (CHAMPLAIN_IS_TILE_CACHE (tile_cache));
+
+  return CHAMPLAIN_TILE_CACHE_GET_CLASS (tile_cache)->store_tile (tile_cache, tile, contents, size);
+}
+
+void
+champlain_tile_cache_refresh_tile_time (ChamplainTileCache *tile_cache, ChamplainTile *tile)
+{
+  g_return_if_fail (CHAMPLAIN_IS_TILE_CACHE (tile_cache));
+
+  return CHAMPLAIN_TILE_CACHE_GET_CLASS (tile_cache)->refresh_tile_time (tile_cache, tile);
+}
+
+void
+champlain_tile_cache_on_tile_filled (ChamplainTileCache *tile_cache, ChamplainTile *tile)
+{
+  g_return_if_fail (CHAMPLAIN_IS_TILE_CACHE (tile_cache));
+
+  return CHAMPLAIN_TILE_CACHE_GET_CLASS (tile_cache)->on_tile_filled (tile_cache, tile);
+}
+
+void
+champlain_tile_cache_clean (ChamplainTileCache *tile_cache)
+{
+  g_return_if_fail (CHAMPLAIN_IS_TILE_CACHE (tile_cache));
+
+  return CHAMPLAIN_TILE_CACHE_GET_CLASS (tile_cache)->clean (tile_cache);
+}
+
+static const gchar *
+get_id (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_CACHE (map_source), NULL);
+
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
+
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (next_source), NULL);
+
+  return champlain_map_source_get_id(next_source);
+}
+
+static const gchar *
+get_name (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_CACHE (map_source), NULL);
+
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
+
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (next_source), NULL);
+
+  return champlain_map_source_get_name(next_source);
+}
+
+static const gchar *
+get_license (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_CACHE (map_source), NULL);
+
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
+
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (next_source), NULL);
+
+  return champlain_map_source_get_license(next_source);
+}
+
+static const gchar *
+get_license_uri (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_CACHE (map_source), NULL);
+
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
+
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (next_source), NULL);
+
+  return champlain_map_source_get_license_uri(next_source);
+}
+
+static guint
+get_min_zoom_level (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_CACHE (map_source), 0);
+
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
+
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (next_source), 0);
+
+  return champlain_map_source_get_min_zoom_level(next_source);
+}
+
+static guint
+get_max_zoom_level (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_CACHE (map_source), 0);
+
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
+
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (next_source), 0);
+
+  return champlain_map_source_get_max_zoom_level(next_source);
+}
+
+static guint
+get_tile_size (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_CACHE (map_source), 0);
+
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source(map_source);
+
+  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (next_source), 0);
+
+  return champlain_map_source_get_tile_size(next_source);
+}
diff --git a/champlain/champlain-tile-cache.h b/champlain/champlain-tile-cache.h
new file mode 100644
index 0000000..de2530c
--- /dev/null
+++ b/champlain/champlain-tile-cache.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#if !defined (__CHAMPLAIN_CHAMPLAIN_H_INSIDE__) && !defined (CHAMPLAIN_COMPILATION)
+#error "Only <champlain/champlain.h> can be included directly."
+#endif
+
+#ifndef _CHAMPLAIN_TILE_CACHE_H_
+#define _CHAMPLAIN_TILE_CACHE_H_
+
+#include <champlain/champlain-defines.h>
+#include <champlain/champlain-map-source.h>
+
+G_BEGIN_DECLS
+
+#define CHAMPLAIN_TYPE_TILE_CACHE             (champlain_tile_cache_get_type ())
+#define CHAMPLAIN_TILE_CACHE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHAMPLAIN_TYPE_TILE_CACHE, ChamplainTileCache))
+#define CHAMPLAIN_TILE_CACHE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), CHAMPLAIN_TYPE_TILE_CACHE, ChamplainTileCacheClass))
+#define CHAMPLAIN_IS_TILE_CACHE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CHAMPLAIN_TYPE_TILE_CACHE))
+#define CHAMPLAIN_IS_TILE_CACHE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), CHAMPLAIN_TYPE_TILE_CACHE))
+#define CHAMPLAIN_TILE_CACHE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), CHAMPLAIN_TYPE_TILE_CACHE, ChamplainTileCacheClass))
+
+typedef struct _ChamplainTileCache ChamplainTileCache;
+typedef struct _ChamplainTileCacheClass ChamplainTileCacheClass;
+
+struct _ChamplainTileCache
+{
+  ChamplainMapSource parent_instance;
+};
+
+struct _ChamplainTileCacheClass
+{
+  ChamplainMapSourceClass parent_class;
+
+  void (*store_tile) (ChamplainTileCache *tile_cache,
+                      ChamplainTile *tile,
+                      const gchar *contents,
+                      gsize size);
+  void (*refresh_tile_time) (ChamplainTileCache *tile_cache, ChamplainTile *tile);
+  void (*on_tile_filled) (ChamplainTileCache *tile_cache, ChamplainTile *tile);
+  void (*clean) (ChamplainTileCache *tile_cache);
+};
+
+GType champlain_tile_cache_get_type (void);
+
+gboolean champlain_tile_cache_get_persistent (ChamplainTileCache *tile_cache);
+
+void champlain_tile_cache_store_tile (ChamplainTileCache *tile_cache,
+                                      ChamplainTile *tile,
+                                      const gchar *contents,
+                                      gsize size);
+void champlain_tile_cache_refresh_tile_time (ChamplainTileCache *tile_cache, ChamplainTile *tile);
+void champlain_tile_cache_on_tile_filled (ChamplainTileCache *tile_cache, ChamplainTile *tile);
+void champlain_tile_cache_clean (ChamplainTileCache *tile_cache);
+
+G_END_DECLS
+
+#endif /* _CHAMPLAIN_TILE_CACHE_H_ */
+
diff --git a/champlain/champlain-tile-source.c b/champlain/champlain-tile-source.c
new file mode 100644
index 0000000..7a04dce
--- /dev/null
+++ b/champlain/champlain-tile-source.c
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2008-2009 Pierre-Luc Beaudoin <pierre-luc pierlux com>
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "champlain-tile-source.h"
+#include "champlain-enum-types.h"
+
+G_DEFINE_TYPE (ChamplainTileSource, champlain_tile_source, CHAMPLAIN_TYPE_MAP_SOURCE);
+
+#define GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE((obj), CHAMPLAIN_TYPE_TILE_SOURCE, ChamplainTileSourcePrivate))
+
+enum
+{
+  PROP_0,
+  PROP_ID,
+  PROP_NAME,
+  PROP_LICENSE,
+  PROP_LICENSE_URI,
+  PROP_MIN_ZOOM_LEVEL,
+  PROP_MAX_ZOOM_LEVEL,
+  PROP_TILE_SIZE,
+  PROP_MAP_PROJECTION,
+  PROP_CACHE
+};
+
+typedef struct _ChamplainTileSourcePrivate ChamplainTileSourcePrivate;
+
+struct _ChamplainTileSourcePrivate
+{
+  gchar *id;
+  gchar *name;
+  gchar *license;
+  gchar *license_uri;
+  guint min_zoom_level;
+  guint max_zoom_level;
+  guint tile_size;
+  ChamplainMapProjection map_projection;
+  ChamplainTileCache *cache;
+};
+
+static const gchar *get_id (ChamplainMapSource *map_source);
+static const gchar *get_name (ChamplainMapSource *map_source);
+static const gchar *get_license (ChamplainMapSource *map_source);
+static const gchar *get_license_uri (ChamplainMapSource *map_source);
+static guint get_min_zoom_level (ChamplainMapSource *map_source);
+static guint get_max_zoom_level (ChamplainMapSource *map_source);
+static guint get_tile_size (ChamplainMapSource *map_source);
+
+static void
+champlain_tile_source_get_property (GObject *object,
+                                    guint prop_id,
+                                    GValue *value,
+                                    GParamSpec *pspec)
+{
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(object);
+
+  switch(prop_id)
+    {
+    case PROP_ID:
+      g_value_set_string (value, priv->id);
+      break;
+    case PROP_NAME:
+      g_value_set_string (value, priv->name);
+      break;
+    case PROP_LICENSE:
+      g_value_set_string (value, priv->license);
+      break;
+    case PROP_LICENSE_URI:
+      g_value_set_string (value, priv->license_uri);
+      break;
+    case PROP_MIN_ZOOM_LEVEL:
+      g_value_set_uint (value, priv->min_zoom_level);
+      break;
+    case PROP_MAX_ZOOM_LEVEL:
+      g_value_set_uint (value, priv->max_zoom_level);
+      break;
+    case PROP_TILE_SIZE:
+      g_value_set_uint (value, priv->tile_size);
+      break;
+    case PROP_MAP_PROJECTION:
+      g_value_set_enum (value, priv->map_projection);
+      break;
+    case PROP_CACHE:
+      g_value_set_object (value, priv->cache);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    }
+}
+
+static void
+champlain_tile_source_set_property (GObject *object,
+                                    guint prop_id,
+                                    const GValue *value,
+                                    GParamSpec *pspec)
+{
+  ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE(object);
+
+  switch(prop_id)
+    {
+    case PROP_ID:
+      champlain_tile_source_set_id (tile_source,
+                                    g_value_get_string (value));
+    case PROP_NAME:
+      champlain_tile_source_set_name (tile_source,
+                                      g_value_get_string (value));
+      break;
+    case PROP_LICENSE:
+      champlain_tile_source_set_license (tile_source,
+                                         g_value_get_string (value));
+      break;
+    case PROP_LICENSE_URI:
+      champlain_tile_source_set_license_uri (tile_source,
+                                             g_value_get_string (value));
+      break;
+    case PROP_MIN_ZOOM_LEVEL:
+      champlain_tile_source_set_min_zoom_level (tile_source,
+          g_value_get_uint (value));
+      break;
+    case PROP_MAX_ZOOM_LEVEL:
+      champlain_tile_source_set_max_zoom_level (tile_source,
+          g_value_get_uint (value));
+      break;
+    case PROP_TILE_SIZE:
+      champlain_tile_source_set_tile_size (tile_source,
+                                           g_value_get_uint (value));
+      break;
+    case PROP_MAP_PROJECTION:
+      champlain_tile_source_set_projection (tile_source,
+                                            g_value_get_enum (value));
+      break;
+    case PROP_CACHE:
+      champlain_tile_source_set_cache (tile_source,
+                                       g_value_get_object (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    }
+}
+
+static void
+champlain_tile_source_dispose (GObject *object)
+{
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(object);
+
+  if (priv->cache)
+    {
+      g_object_unref (priv->cache);
+
+      priv->cache = NULL;
+    }
+
+  G_OBJECT_CLASS (champlain_tile_source_parent_class)->dispose (object);
+}
+
+static void
+champlain_tile_source_finalize (GObject *object)
+{
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(object);
+
+  g_free (priv->id);
+  g_free (priv->name);
+  g_free (priv->license);
+  g_free (priv->license_uri);
+
+  G_OBJECT_CLASS (champlain_tile_source_parent_class)->finalize (object);
+}
+
+static void
+champlain_tile_source_constructed  (GObject *object)
+{
+  G_OBJECT_CLASS (champlain_tile_source_parent_class)->constructed (object);
+}
+
+static void
+champlain_tile_source_class_init (ChamplainTileSourceClass *klass)
+{
+  ChamplainMapSourceClass *map_source_class = CHAMPLAIN_MAP_SOURCE_CLASS (klass);
+  GObjectClass* object_class = G_OBJECT_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (klass, sizeof (ChamplainTileSourcePrivate));
+
+  object_class->finalize = champlain_tile_source_finalize;
+  object_class->dispose = champlain_tile_source_dispose;
+  object_class->get_property = champlain_tile_source_get_property;
+  object_class->set_property = champlain_tile_source_set_property;
+  object_class->constructed = champlain_tile_source_constructed;
+
+  map_source_class->get_id = get_id;
+  map_source_class->get_name = get_name;
+  map_source_class->get_license = get_license;
+  map_source_class->get_license_uri = get_license_uri;
+  map_source_class->get_min_zoom_level = get_min_zoom_level;
+  map_source_class->get_max_zoom_level = get_max_zoom_level;
+  map_source_class->get_tile_size = get_tile_size;
+
+  map_source_class->fill_tile = NULL;
+
+  pspec = g_param_spec_string ("id",
+                               "Id",
+                               "The id of the tile source",
+                               "",
+                               (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class, PROP_ID, pspec);
+
+  pspec = g_param_spec_string ("name",
+                               "Name",
+                               "The name of the tile source",
+                               "",
+                               (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class, PROP_NAME, pspec);
+
+  pspec = g_param_spec_string ("license",
+                               "License",
+                               "The usage license of the tile source",
+                               "",
+                               (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class, PROP_LICENSE, pspec);
+
+  pspec = g_param_spec_string ("license-uri",
+                               "License-uri",
+                               "The usage license's uri for more information",
+                               "",
+                               (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class, PROP_LICENSE_URI, pspec);
+
+  pspec = g_param_spec_uint ("min-zoom-level",
+                             "Minimum Zoom Level",
+                             "The minimum zoom level",
+                             0,
+                             50,
+                             0,
+                             (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class, PROP_MIN_ZOOM_LEVEL, pspec);
+
+  pspec = g_param_spec_uint ("max-zoom-level",
+                             "Maximum Zoom Level",
+                             "The maximum zoom level",
+                             0,
+                             50,
+                             18,
+                             (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class, PROP_MAX_ZOOM_LEVEL, pspec);
+
+  pspec = g_param_spec_uint ("tile-size",
+                             "Tile Size",
+                             "The tile size",
+                             0,
+                             2048,
+                             256,
+                             (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class, PROP_TILE_SIZE, pspec);
+
+  pspec = g_param_spec_enum ("projection",
+                             "Projection",
+                             "The map projection",
+                             CHAMPLAIN_TYPE_MAP_PROJECTION,
+                             CHAMPLAIN_MAP_PROJECTION_MERCATOR,
+                             (G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class, PROP_MAP_PROJECTION, pspec);
+
+  pspec = g_param_spec_object ("cache",
+                               "Cache",
+                               "Cache used for tile sorage",
+                               CHAMPLAIN_TYPE_TILE_CACHE,
+                               G_PARAM_READWRITE);
+  g_object_class_install_property (object_class, PROP_CACHE, pspec);
+}
+
+static void
+champlain_tile_source_init (ChamplainTileSource *tile_source)
+{
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+  priv->cache = NULL;
+  priv->id = NULL;
+  priv->name = NULL;
+  priv->license = NULL;
+  priv->license_uri = NULL;
+  priv->min_zoom_level = 0;
+  priv->max_zoom_level = 0;
+  priv->tile_size = 0;
+  priv->map_projection = CHAMPLAIN_MAP_PROJECTION_MERCATOR;
+}
+
+ChamplainMapProjection
+champlain_tile_source_get_projection (ChamplainTileSource *tile_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_SOURCE (tile_source), 0);
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+  return priv->map_projection;
+}
+
+ChamplainTileCache *
+champlain_tile_source_get_cache (ChamplainTileSource *tile_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_SOURCE (tile_source), NULL);
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+  return priv->cache;
+}
+
+void
+champlain_tile_source_set_projection (ChamplainTileSource *tile_source,
+                                      ChamplainMapProjection projection)
+{
+  g_return_if_fail (CHAMPLAIN_IS_TILE_SOURCE (tile_source));
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+
+  priv->map_projection = projection;
+  g_object_notify (G_OBJECT (tile_source), "projection");
+}
+
+void
+champlain_tile_source_set_cache (ChamplainTileSource *tile_source,
+                                 ChamplainTileCache *cache)
+{
+  g_return_if_fail (CHAMPLAIN_IS_TILE_SOURCE (tile_source));
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+
+  if (priv->cache != NULL)
+    g_object_unref(priv->cache);
+
+  if (cache)
+    {
+      g_return_if_fail (CHAMPLAIN_IS_TILE_CACHE (cache));
+
+      g_object_ref_sink(cache);
+    }
+
+  priv->cache = cache;
+
+  g_object_notify (G_OBJECT (tile_source), "cache");
+}
+
+static const gchar *
+get_id (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_SOURCE (map_source), NULL);
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(map_source);
+  return priv->id;
+}
+
+static const gchar *
+get_name (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_SOURCE (map_source), NULL);
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(map_source);
+  return priv->name;
+}
+
+static const gchar *
+get_license (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_SOURCE (map_source), NULL);
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(map_source);
+  return priv->license;
+}
+
+static const gchar *
+get_license_uri (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_SOURCE (map_source), NULL);
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(map_source);
+  return priv->license_uri;
+}
+
+static guint
+get_min_zoom_level (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_SOURCE (map_source), 0);
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(map_source);
+  return priv->min_zoom_level;
+}
+
+static guint
+get_max_zoom_level (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_SOURCE (map_source), 0);
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(map_source);
+  return priv->max_zoom_level;
+}
+
+static guint
+get_tile_size (ChamplainMapSource *map_source)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE_SOURCE (map_source), 0);
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(map_source);
+  return priv->tile_size;
+}
+
+void
+champlain_tile_source_set_id (ChamplainTileSource *tile_source,
+                              const gchar *id)
+{
+  g_return_if_fail (CHAMPLAIN_IS_TILE_SOURCE (tile_source));
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+
+  g_free (priv->id);
+  priv->id = g_strdup (id);
+
+  g_object_notify (G_OBJECT (tile_source), "id");
+}
+
+void
+champlain_tile_source_set_name (ChamplainTileSource *tile_source,
+                                const gchar *name)
+{
+  g_return_if_fail (CHAMPLAIN_IS_TILE_SOURCE (tile_source));
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+
+  g_free (priv->name);
+  priv->name = g_strdup (name);
+
+  g_object_notify (G_OBJECT (tile_source), "name");
+}
+
+void
+champlain_tile_source_set_license (ChamplainTileSource *tile_source,
+                                   const gchar *license)
+{
+  g_return_if_fail (CHAMPLAIN_IS_TILE_SOURCE (tile_source));
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+
+  g_free (priv->license);
+  priv->license = g_strdup (license);
+
+  g_object_notify (G_OBJECT (tile_source), "license");
+}
+
+void
+champlain_tile_source_set_license_uri (ChamplainTileSource *tile_source,
+                                       const gchar *license_uri)
+{
+  g_return_if_fail (CHAMPLAIN_IS_TILE_SOURCE (tile_source));
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+
+  g_free (priv->license_uri);
+  priv->license_uri = g_strdup (license_uri);
+
+  g_object_notify (G_OBJECT (tile_source), "license-uri");
+}
+
+void
+champlain_tile_source_set_min_zoom_level (ChamplainTileSource *tile_source,
+    guint zoom_level)
+{
+  g_return_if_fail (CHAMPLAIN_IS_TILE_SOURCE (tile_source));
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+
+  priv->min_zoom_level = zoom_level;
+
+  g_object_notify (G_OBJECT (tile_source), "min-zoom-level");
+}
+
+void
+champlain_tile_source_set_max_zoom_level (ChamplainTileSource *tile_source,
+    guint zoom_level)
+{
+  g_return_if_fail (CHAMPLAIN_IS_TILE_SOURCE (tile_source));
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+
+  priv->max_zoom_level = zoom_level;
+
+  g_object_notify (G_OBJECT (tile_source), "max-zoom-level");
+}
+
+void
+champlain_tile_source_set_tile_size (ChamplainTileSource *tile_source,
+                                     guint tile_size)
+{
+  g_return_if_fail (CHAMPLAIN_IS_TILE_SOURCE (tile_source));
+
+  ChamplainTileSourcePrivate *priv = GET_PRIVATE(tile_source);
+
+  priv->tile_size = tile_size;
+
+  g_object_notify (G_OBJECT (tile_source), "tile-size");
+}
diff --git a/champlain/champlain-tile-source.h b/champlain/champlain-tile-source.h
new file mode 100644
index 0000000..7e6fcc2
--- /dev/null
+++ b/champlain/champlain-tile-source.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2008-2009 Pierre-Luc Beaudoin <pierre-luc pierlux com>
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#if !defined (__CHAMPLAIN_CHAMPLAIN_H_INSIDE__) && !defined (CHAMPLAIN_COMPILATION)
+#error "Only <champlain/champlain.h> can be included directly."
+#endif
+
+#ifndef _CHAMPLAIN_TILE_SOURCE_H_
+#define _CHAMPLAIN_TILE_SOURCE_H_
+
+#include <champlain/champlain-defines.h>
+#include <champlain/champlain-map-source.h>
+#include <champlain/champlain-tile-cache.h>
+
+G_BEGIN_DECLS
+
+#define CHAMPLAIN_TYPE_TILE_SOURCE             (champlain_tile_source_get_type ())
+#define CHAMPLAIN_TILE_SOURCE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHAMPLAIN_TYPE_TILE_SOURCE, ChamplainTileSource))
+#define CHAMPLAIN_TILE_SOURCE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), CHAMPLAIN_TYPE_TILE_SOURCE, ChamplainTileSourceClass))
+#define CHAMPLAIN_IS_TILE_SOURCE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CHAMPLAIN_TYPE_TILE_SOURCE))
+#define CHAMPLAIN_IS_TILE_SOURCE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), CHAMPLAIN_TYPE_TILE_SOURCE))
+#define CHAMPLAIN_TILE_SOURCE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), CHAMPLAIN_TYPE_TILE_SOURCE, ChamplainTileSourceClass))
+
+typedef struct _ChamplainTileSource ChamplainTileSource;
+typedef struct _ChamplainTileSourceClass ChamplainTileSourceClass;
+
+typedef enum
+{
+  CHAMPLAIN_MAP_PROJECTION_MERCATOR
+} ChamplainMapProjection;
+
+struct _ChamplainTileSource
+{
+  ChamplainMapSource parent_instance;
+};
+
+struct _ChamplainTileSourceClass
+{
+  ChamplainMapSourceClass parent_class;
+};
+
+GType champlain_tile_source_get_type (void);
+
+ChamplainMapProjection champlain_tile_source_get_projection (ChamplainTileSource *tile_source);
+void champlain_tile_source_set_projection (ChamplainTileSource *tile_source,
+    ChamplainMapProjection projection);
+
+ChamplainTileCache *champlain_tile_source_get_cache (ChamplainTileSource *tile_source);
+void champlain_tile_source_set_cache (ChamplainTileSource *tile_source,
+                                      ChamplainTileCache *cache);
+
+void champlain_tile_source_set_id (ChamplainTileSource *tile_source,
+                                   const gchar *id);
+void champlain_tile_source_set_name (ChamplainTileSource *tile_source,
+                                     const gchar *name);
+void champlain_tile_source_set_license (ChamplainTileSource *tile_source,
+                                        const gchar *license);
+void champlain_tile_source_set_license_uri (ChamplainTileSource *tile_source,
+    const gchar *license_uri);
+
+void champlain_tile_source_set_min_zoom_level (ChamplainTileSource *tile_source,
+    guint zoom_level);
+void champlain_tile_source_set_max_zoom_level (ChamplainTileSource *tile_source,
+    guint zoom_level);
+void champlain_tile_source_set_tile_size (ChamplainTileSource *tile_source,
+    guint zoom_level);
+
+G_END_DECLS
+
+#endif /* _CHAMPLAIN_TILE_SOURCE_H_ */
diff --git a/champlain/champlain-tile.c b/champlain/champlain-tile.c
index faad92d..4227373 100644
--- a/champlain/champlain-tile.c
+++ b/champlain/champlain-tile.c
@@ -47,8 +47,6 @@ enum
   PROP_Y,
   PROP_ZOOM_LEVEL,
   PROP_SIZE,
-  PROP_URI,
-  PROP_FILENAME,
   PROP_STATE,
   PROP_ACTOR,
   PROP_CONTENT,
@@ -61,9 +59,7 @@ struct _ChamplainTilePrivate {
   gint size; /* The tile's width and height (only support square tiles */
   gint zoom_level; /* The tile's zoom level */
 
-  gchar *uri; /* The URI where to find the tile */
   ChamplainState state; /* The tile state: loading, validation, done */
-  gchar *filename; /* The tile's cache filename */
   ClutterActor *actor; /* An actor grouping all content actors */
   ClutterActor *content_actor; /* The actual tile actor */
 
@@ -95,12 +91,6 @@ champlain_tile_get_property (GObject *object,
       case PROP_STATE:
         g_value_set_enum (value, champlain_tile_get_state (self));
         break;
-      case PROP_URI:
-        g_value_set_string (value, champlain_tile_get_uri (self));
-        break;
-      case PROP_FILENAME:
-        g_value_set_string (value, champlain_tile_get_filename (self));
-        break;
       case PROP_ACTOR:
         g_value_set_object (value, champlain_tile_get_actor (self));
         break;
@@ -139,12 +129,6 @@ champlain_tile_set_property (GObject *object,
       case PROP_STATE:
         champlain_tile_set_state (self, g_value_get_enum (value));
         break;
-      case PROP_URI:
-        champlain_tile_set_uri (self, g_value_get_string (value));
-        break;
-      case PROP_FILENAME:
-        champlain_tile_set_filename (self, g_value_get_string (value));
-        break;
       case PROP_CONTENT:
         champlain_tile_set_content (self, g_value_get_object (value), FALSE);
         break;
@@ -176,13 +160,13 @@ champlain_tile_dispose (GObject *object)
   G_OBJECT_CLASS (champlain_tile_parent_class)->dispose (object);
 }
 
+
+
 static void
 champlain_tile_finalize (GObject *object)
 {
   ChamplainTilePrivate *priv = CHAMPLAIN_TILE (object)->priv;
 
-  g_free (priv->uri);
-  g_free (priv->filename);
   g_free (priv->modified_time);
   g_free (priv->etag);
 
@@ -286,36 +270,6 @@ champlain_tile_class_init (ChamplainTileClass *klass)
           G_PARAM_READWRITE));
 
   /**
-  * ChamplainTile:uri:
-  *
-  * The remote uri of the tile
-  *
-  * Since: 0.4
-  */
-  g_object_class_install_property (object_class,
-      PROP_URI,
-      g_param_spec_string ("uri",
-          "URI",
-          "The URI of the tile",
-          "",
-          G_PARAM_READWRITE));
-
-  /**
-  * ChamplainTile:filename:
-  *
-  * The local path of the cached tile
-  *
-  * Since: 0.4
-  */
-  g_object_class_install_property (object_class,
-      PROP_FILENAME,
-      g_param_spec_string ("filename",
-          "Filename",
-          "The filename of the tile",
-          "",
-          G_PARAM_READWRITE));
-
-  /**
   * ChamplainTile:actor:
   *
   * The #ClutterActor where the tile content is rendered.  Should never change
@@ -377,8 +331,6 @@ champlain_tile_init (ChamplainTile *self)
   priv->y = 0;
   priv->zoom_level = 0;
   priv->size = 0;
-  priv->uri = NULL;
-  priv->filename = NULL;
   priv->modified_time = NULL;
   priv->etag = NULL;
 
@@ -482,38 +434,6 @@ champlain_tile_get_state (ChamplainTile *self)
 }
 
 /**
- * champlain_tile_get_uri:
- * @self: the #ChamplainTile
- *
- * Returns: the tile's remote uri
- *
- * Since: 0.4
- */
-G_CONST_RETURN gchar *
-champlain_tile_get_uri (ChamplainTile *self)
-{
-  g_return_val_if_fail (CHAMPLAIN_TILE (self), NULL);
-
-  return self->priv->uri;
-}
-
-/**
- * champlain_tile_get_filename:
- * @self: the #ChamplainTile
- *
- * Returns: the tile's local filename
- *
- * Since: 0.4
- */
-G_CONST_RETURN gchar *
-champlain_tile_get_filename (ChamplainTile *self)
-{
-  g_return_val_if_fail (CHAMPLAIN_TILE (self), NULL);
-
-  return self->priv->filename;
-}
-
-/**
  * champlain_tile_get_actor:
  * @self: the #ChamplainTile
  *
@@ -652,54 +572,6 @@ champlain_tile_new_full (gint x,
 }
 
 /**
- * champlain_tile_set_uri:
- * @self: the #ChamplainTile
- * @uri: the uri
- *
- * Sets the tile's uri
- *
- * Since: 0.4
- */
-void
-champlain_tile_set_uri (ChamplainTile *self,
-    const gchar *uri)
-{
-  g_return_if_fail (CHAMPLAIN_TILE (self));
-  g_return_if_fail (uri != NULL);
-
-  ChamplainTilePrivate *priv = self->priv;
-
-  g_free (priv->uri);
-  priv->uri = g_strdup (uri);
-
-  g_object_notify (G_OBJECT (self), "uri");
-}
-
-/**
- * champlain_tile_set_filename:
- * @self: the #ChamplainTile
- * @filename: a local path to an image
- *
- * Sets the tile's filename
- *
- * Since: 0.4
- */
-void
-champlain_tile_set_filename (ChamplainTile *self,
-    const gchar *filename)
-{
-  g_return_if_fail (CHAMPLAIN_TILE (self));
-  g_return_if_fail (filename != NULL);
-
-  ChamplainTilePrivate *priv = self->priv;
-
-  g_free (priv->filename);
-  priv->filename = g_strdup (filename);
-
-  g_object_notify (G_OBJECT (self), "filename");
-}
-
-/**
  * champlain_tile_get_modified_time:
  * @self: the #ChamplainTile
  *
diff --git a/champlain/champlain-tile.h b/champlain/champlain-tile.h
index 0ed5941..8451f64 100644
--- a/champlain/champlain-tile.h
+++ b/champlain/champlain-tile.h
@@ -71,8 +71,6 @@ gint champlain_tile_get_y (ChamplainTile *self);
 gint champlain_tile_get_zoom_level (ChamplainTile *self);
 guint champlain_tile_get_size (ChamplainTile *self);
 ChamplainState champlain_tile_get_state (ChamplainTile *self);
-G_CONST_RETURN gchar * champlain_tile_get_uri (ChamplainTile *self);
-G_CONST_RETURN gchar * champlain_tile_get_filename (ChamplainTile *self);
 ClutterActor * champlain_tile_get_actor (ChamplainTile *self);
 ClutterActor * champlain_tile_get_content (ChamplainTile *self);
 const GTimeVal * champlain_tile_get_modified_time (ChamplainTile *self);
@@ -88,10 +86,6 @@ void champlain_tile_set_size (ChamplainTile *self,
     guint size);
 void champlain_tile_set_state (ChamplainTile *self,
     ChamplainState state);
-void champlain_tile_set_uri (ChamplainTile *self,
-    const gchar *uri);
-void champlain_tile_set_filename (ChamplainTile *self,
-    const gchar *filename);
 void champlain_tile_set_content (ChamplainTile *self,
     ClutterActor* actor,
     gboolean fade_in);
diff --git a/champlain/champlain-view.c b/champlain/champlain-view.c
index 7b949dc..29f7b10 100644
--- a/champlain/champlain-view.c
+++ b/champlain/champlain-view.c
@@ -610,7 +610,7 @@ champlain_view_get_property (GObject *object,
             CLAMP (priv->longitude, CHAMPLAIN_MIN_LONG, CHAMPLAIN_MAX_LONG));
         break;
       case PROP_LATITUDE:
-        g_value_set_double (value, 
+        g_value_set_double (value,
             CLAMP (priv->latitude, CHAMPLAIN_MIN_LAT, CHAMPLAIN_MAX_LAT));
         break;
       case PROP_ZOOM_LEVEL:
@@ -1286,13 +1286,35 @@ static void
 champlain_view_init (ChamplainView *view)
 {
   ChamplainViewPrivate *priv = GET_PRIVATE (view);
+  ChamplainMapSourceChain *source_chain;
+  ChamplainMapSource *source;
+  ChamplainMapSource *src;
+  guint tile_size;
+  gchar *cache_path;
 
   champlain_debug_set_flags (g_getenv ("CHAMPLAIN_DEBUG"));
 
   view->priv = priv;
 
   priv->factory = champlain_map_source_factory_dup_default ();
-  priv->map_source = champlain_map_source_factory_create (priv->factory, CHAMPLAIN_MAP_SOURCE_OSM_MAPNIK);
+  source = champlain_map_source_factory_create (priv->factory, CHAMPLAIN_MAP_SOURCE_OSM_MAPNIK);
+
+  source_chain = champlain_map_source_chain_new ();
+
+  tile_size = champlain_map_source_get_tile_size(source);
+  src = CHAMPLAIN_MAP_SOURCE(champlain_error_tile_source_new_full (tile_size));
+
+  champlain_map_source_chain_push_map_source(source_chain, src);
+  champlain_map_source_chain_push_map_source(source_chain, source);
+
+  cache_path = g_build_path (G_DIR_SEPARATOR_S, g_get_user_cache_dir (), "champlain", NULL);
+  src = CHAMPLAIN_MAP_SOURCE(champlain_file_cache_new_full (100000000, cache_path, TRUE));
+  g_free(cache_path);
+
+  champlain_map_source_chain_push_map_source(source_chain, src);
+
+  priv->map_source = CHAMPLAIN_MAP_SOURCE(source_chain);
+
   priv->zoom_level = 0;
   priv->min_zoom_level = champlain_map_source_get_min_zoom_level (priv->map_source);
   priv->max_zoom_level = champlain_map_source_get_max_zoom_level (priv->map_source);
@@ -2304,6 +2326,7 @@ view_load_visible_tiles (ChamplainView *view)
                   champlain_tile_get_actor (tile), NULL);
 
               champlain_zoom_level_add_tile (level, tile);
+              champlain_tile_set_state (tile, CHAMPLAIN_STATE_LOADING);
               champlain_map_source_fill_tile (priv->map_source, tile);
 
               g_object_unref (tile);
@@ -2405,8 +2428,7 @@ view_reload_tiles_cb (ChamplainMapSource *map_source,
       if (tile == NULL)
         continue;
 
-      if (champlain_tile_get_state (tile) != CHAMPLAIN_STATE_LOADING &&
-          champlain_tile_get_state (tile) != CHAMPLAIN_STATE_VALIDATING_CACHE)
+      if (champlain_tile_get_state (tile) != CHAMPLAIN_STATE_LOADING)
         champlain_map_source_fill_tile (priv->map_source, tile);
     }
   view_update_state (view);
@@ -2435,8 +2457,7 @@ view_update_state (ChamplainView *view)
       if (tile == NULL)
         continue;
 
-      if (champlain_tile_get_state (tile) == CHAMPLAIN_STATE_LOADING ||
-          champlain_tile_get_state (tile) == CHAMPLAIN_STATE_VALIDATING_CACHE)
+      if (champlain_tile_get_state (tile) == CHAMPLAIN_STATE_LOADING)
         new_state = CHAMPLAIN_STATE_LOADING;
     }
 
diff --git a/champlain/champlain.h b/champlain/champlain.h
index 9ae37e1..43fd5ba 100644
--- a/champlain/champlain.h
+++ b/champlain/champlain.h
@@ -29,7 +29,6 @@
 #include <glib.h>
 
 #include "champlain/champlain-defines.h"
-#include "champlain/champlain-cache.h"
 #include "champlain/champlain-layer.h"
 #include "champlain/champlain-selection-layer.h"
 #include "champlain/champlain-base-marker.h"
@@ -38,8 +37,13 @@
 #include "champlain/champlain-enum-types.h"
 #include "champlain/champlain-point.h"
 #include "champlain/champlain-map-source.h"
+#include "champlain/champlain-map-source-chain.h"
+#include "champlain/champlain-tile-source.h"
+#include "champlain/champlain-tile-cache.h"
+#include "champlain/champlain-network-tile-source.h"
+#include "champlain/champlain-file-cache.h"
+#include "champlain/champlain-error-tile-source.h"
 #include "champlain/champlain-map-source-factory.h"
-#include "champlain/champlain-network-map-source.h"
 #include "champlain/champlain-version.h"
 #include "champlain/champlain-memphis-map-source.h"
 #include "champlain/champlain-map-data-source.h"
diff --git a/demos/launcher-gtk.c b/demos/launcher-gtk.c
index 23cdf1f..20b561e 100644
--- a/demos/launcher-gtk.c
+++ b/demos/launcher-gtk.c
@@ -75,6 +75,11 @@ map_source_changed (GtkWidget *widget,
   ChamplainMapSource *source;
   GtkTreeIter iter;
   GtkTreeModel *model;
+  ChamplainMapSourceChain *source_chain;
+  ChamplainMapSource *src;
+  guint tile_size;
+  gchar *cache_path;
+
 
   if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter))
     return;
@@ -86,13 +91,22 @@ map_source_changed (GtkWidget *widget,
   ChamplainMapSourceFactory *factory = champlain_map_source_factory_dup_default ();
   source = champlain_map_source_factory_create (factory, id);
 
-  if (source != NULL)
-    {
-      g_object_set (G_OBJECT (view), "map-source", source, NULL);
-      g_object_unref (factory);
-    }
+  source_chain = champlain_map_source_chain_new ();
+
+  tile_size = champlain_map_source_get_tile_size(source);
+  src = CHAMPLAIN_MAP_SOURCE(champlain_error_tile_source_new_full (tile_size));
 
-  g_object_unref (source);
+  champlain_map_source_chain_push_map_source(source_chain, src);
+  champlain_map_source_chain_push_map_source(source_chain, source);
+
+  cache_path = g_build_path (G_DIR_SEPARATOR_S, g_get_user_cache_dir (), "champlain", NULL);
+  src = CHAMPLAIN_MAP_SOURCE(champlain_file_cache_new_full (100000000, cache_path, TRUE));
+  g_free(cache_path);
+
+  champlain_map_source_chain_push_map_source(source_chain, src);
+
+  g_object_set (G_OBJECT (view), "map-source", source_chain, NULL);
+  g_object_unref (factory);
 }
 
 static void
diff --git a/demos/local-rendering.c b/demos/local-rendering.c
index e4333cc..c2d4034 100644
--- a/demos/local-rendering.c
+++ b/demos/local-rendering.c
@@ -42,6 +42,8 @@ static GtkWidget *bordercolor, *bordersize, *borderminz, *bordermaxz;
 static GtkWidget *textcolor, *textsize, *textminz, *textmaxz;
 static MemphisRule *current_rule = NULL;
 
+static ChamplainMapSource *tile_source = NULL;
+
 /*
  * Terminate the main loop.
  */
@@ -52,7 +54,7 @@ on_destroy (GtkWidget *widget, gpointer data)
 }
 
 static void color_gdk_to_clutter (const GdkColor *gdk_color,
-    ClutterColor *clutter_color)
+                                  ClutterColor *clutter_color)
 {
   clutter_color->red = CLAMP (((gdk_color->red / 65535.0) * 255), 0, 255);
   clutter_color->green = CLAMP (((gdk_color->green / 65535.0) * 255), 0, 255);
@@ -61,7 +63,7 @@ static void color_gdk_to_clutter (const GdkColor *gdk_color,
 }
 
 static void color_clutter_to_gdk (const ClutterColor *clutter_color,
-    GdkColor *gdk_color)
+                                  GdkColor *gdk_color)
 {
   gdk_color->red = ((guint16) clutter_color->red) << 8;
   gdk_color->green = ((guint16) clutter_color->green) << 8;
@@ -70,8 +72,8 @@ static void color_clutter_to_gdk (const ClutterColor *clutter_color,
 
 static void
 data_source_state_changed (ChamplainView *view,
-                    GParamSpec *gobject,
-                    GtkImage *image)
+                           GParamSpec *gobject,
+                           GtkImage *image)
 {
   ChamplainState state;
 
@@ -94,8 +96,8 @@ load_local_map_data (ChamplainMapSource *source)
   ChamplainLocalMapDataSource *map_data_source;
 
   map_data_source = CHAMPLAIN_LOCAL_MAP_DATA_SOURCE (
-    champlain_memphis_map_source_get_map_data_source (
-    CHAMPLAIN_MEMPHIS_MAP_SOURCE (source)));
+                      champlain_memphis_map_source_get_map_data_source (
+                        CHAMPLAIN_MEMPHIS_MAP_SOURCE (source)));
 
   champlain_local_map_data_source_load_map_data (map_data_source,
       maps[map_index]);
@@ -108,11 +110,11 @@ load_network_map_data (ChamplainMapSource *source, ChamplainView *view)
   gdouble lat, lon;
 
   map_data_source = CHAMPLAIN_NETWORK_MAP_DATA_SOURCE (
-      champlain_memphis_map_source_get_map_data_source (
-      CHAMPLAIN_MEMPHIS_MAP_SOURCE (source)));
+                      champlain_memphis_map_source_get_map_data_source (
+                        CHAMPLAIN_MEMPHIS_MAP_SOURCE (source)));
 
   g_signal_connect (map_data_source, "notify::state", G_CALLBACK (data_source_state_changed),
-      map_data_state_img);
+                    map_data_state_img);
 
   g_object_get (G_OBJECT (view), "latitude", &lat, "longitude", &lon, NULL);
 
@@ -123,19 +125,17 @@ load_network_map_data (ChamplainMapSource *source, ChamplainView *view)
 static void
 load_rules_into_gui (ChamplainView *view)
 {
-  ChamplainMapSource *source;
   GList* ids, *ptr;
   GtkTreeModel *store;
   GtkTreeIter iter;
   GdkColor gdk_color;
   ClutterColor *clutter_color;
 
-  g_object_get (G_OBJECT (view), "map-source", &source, NULL);
   ids = champlain_memphis_map_source_get_rule_ids (
-      CHAMPLAIN_MEMPHIS_MAP_SOURCE (source));
+          CHAMPLAIN_MEMPHIS_MAP_SOURCE(tile_source));
 
   clutter_color = champlain_memphis_map_source_get_background_color (
-      CHAMPLAIN_MEMPHIS_MAP_SOURCE (source));
+                    CHAMPLAIN_MEMPHIS_MAP_SOURCE(tile_source));
 
   color_clutter_to_gdk (clutter_color, &gdk_color);
   clutter_color_free (clutter_color);
@@ -274,7 +274,7 @@ gtk_memphis_prop_new (gint type, MemphisRuleAttr *attr)
 
 static void
 create_rule_edit_window (MemphisRule *rule, gchar* id,
-    ChamplainMemphisMapSource *source)
+                         ChamplainMemphisMapSource *source)
 {
   GtkWidget *label, *table, *props, *button;
 
@@ -284,11 +284,11 @@ create_rule_edit_window (MemphisRule *rule, gchar* id,
   gtk_container_set_border_width (GTK_CONTAINER (rule_edit_window), 10);
   gtk_window_set_title (GTK_WINDOW (rule_edit_window), id);
   gtk_window_set_position (GTK_WINDOW (rule_edit_window),
-      GTK_WIN_POS_CENTER_ON_PARENT);
+                           GTK_WIN_POS_CENTER_ON_PARENT);
   gtk_window_set_transient_for (GTK_WINDOW (rule_edit_window),
-      GTK_WINDOW (window));
+                                GTK_WINDOW (window));
   g_signal_connect (G_OBJECT (rule_edit_window), "destroy",
-      G_CALLBACK (rule_window_close_cb), NULL);
+                    G_CALLBACK (rule_window_close_cb), NULL);
 
   table = gtk_table_new (6, 2, FALSE);
   gtk_table_set_col_spacings (GTK_TABLE (table), 8);
@@ -340,10 +340,10 @@ create_rule_edit_window (MemphisRule *rule, gchar* id,
   button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
   g_signal_connect (G_OBJECT (button), "clicked",
-      G_CALLBACK (rule_window_close_cb), NULL);
+                    G_CALLBACK (rule_window_close_cb), NULL);
   button = gtk_button_new_from_stock (GTK_STOCK_APPLY);
   g_signal_connect (G_OBJECT (button), "clicked",
-      G_CALLBACK (rule_apply_cb), source);
+                    G_CALLBACK (rule_apply_cb), source);
   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
 
   GtkWidget *mainbox = gtk_vbox_new (FALSE, 0);
@@ -357,13 +357,11 @@ create_rule_edit_window (MemphisRule *rule, gchar* id,
 static void
 zoom_to_map_data (GtkWidget *widget, ChamplainView *view)
 {
-  ChamplainMemphisMapSource *source;
   ChamplainMapDataSource *data_source;
   ChamplainBoundingBox *bbox;
   gdouble lat, lon;
 
-  g_object_get (G_OBJECT (view), "map-source", &source, NULL);
-  data_source = champlain_memphis_map_source_get_map_data_source (source);
+  data_source = champlain_memphis_map_source_get_map_data_source (CHAMPLAIN_MEMPHIS_MAP_SOURCE(tile_source));
   g_object_get (G_OBJECT (data_source), "bounding-box", &bbox, NULL);
   champlain_bounding_box_get_center (bbox, &lat, &lon);
 
@@ -375,16 +373,14 @@ static void
 request_osm_data_cb (GtkWidget *widget, ChamplainView *view)
 {
   gdouble lat, lon;
-  ChamplainMapSource *source;
   g_object_get (G_OBJECT (view), "latitude", &lat, "longitude", &lon, NULL);
-  g_object_get (G_OBJECT (view), "map-source", &source, NULL);
 
-  if (g_strcmp0 (champlain_map_source_get_id (source), "memphis-network") == 0)
+  if (g_strcmp0 (champlain_map_source_get_id (tile_source), "memphis-network") == 0)
     {
       ChamplainNetworkMapDataSource *data_source =
-          CHAMPLAIN_NETWORK_MAP_DATA_SOURCE (
+        CHAMPLAIN_NETWORK_MAP_DATA_SOURCE (
           champlain_memphis_map_source_get_map_data_source (
-          CHAMPLAIN_MEMPHIS_MAP_SOURCE(source)));
+            CHAMPLAIN_MEMPHIS_MAP_SOURCE(tile_source)));
 
       champlain_network_map_data_source_load_map_data (data_source,
           lon - 0.008, lat - 0.008, lon + 0.008, lat + 0.008);
@@ -395,18 +391,16 @@ void
 bg_color_set_cb (GtkColorButton *widget, ChamplainView *view)
 {
   GdkColor gdk_color;
-  ChamplainMapSource *source;
 
   gtk_color_button_get_color (widget, &gdk_color);
 
-  g_object_get (G_OBJECT (view), "map-source", &source, NULL);
-  if (strncmp (champlain_map_source_get_id (source), "memphis", 7) == 0)
+  if (strncmp (champlain_map_source_get_id (tile_source), "memphis", 7) == 0)
     {
       ClutterColor clutter_color;
       color_gdk_to_clutter (&gdk_color, &clutter_color);
 
       champlain_memphis_map_source_set_background_color (
-          CHAMPLAIN_MEMPHIS_MAP_SOURCE (source), &clutter_color);
+        CHAMPLAIN_MEMPHIS_MAP_SOURCE (tile_source), &clutter_color);
     }
 }
 
@@ -431,10 +425,15 @@ map_source_changed (GtkWidget *widget, ChamplainView *view)
 
   if (source != NULL)
     {
+      ChamplainMapSourceChain *source_chain;
+      ChamplainMapSource *src;
+      guint tile_size;
+      gchar *cache_path;
+
       if (g_strcmp0 (id, "memphis-local") == 0)
         {
           champlain_memphis_map_source_load_rules (
-              CHAMPLAIN_MEMPHIS_MAP_SOURCE (source), rules[rules_index]);
+            CHAMPLAIN_MEMPHIS_MAP_SOURCE (source), rules[rules_index]);
           load_local_map_data (source);
           gtk_widget_hide_all (memphis_box);
           gtk_widget_set_no_show_all (memphis_box, FALSE);
@@ -445,7 +444,7 @@ map_source_changed (GtkWidget *widget, ChamplainView *view)
       else if (g_strcmp0 (id, "memphis-network") == 0)
         {
           champlain_memphis_map_source_load_rules (
-              CHAMPLAIN_MEMPHIS_MAP_SOURCE (source), rules[rules_index]);
+            CHAMPLAIN_MEMPHIS_MAP_SOURCE (source), rules[rules_index]);
           load_network_map_data (source, view);
           gtk_widget_hide_all (memphis_box);
           gtk_widget_set_no_show_all (memphis_box, FALSE);
@@ -459,8 +458,23 @@ map_source_changed (GtkWidget *widget, ChamplainView *view)
           gtk_widget_set_no_show_all (memphis_box, TRUE);
         }
 
-      g_object_set (G_OBJECT (view), "map-source", source, NULL);
-      g_object_unref (source);
+      tile_source = CHAMPLAIN_MAP_SOURCE(source);
+
+      source_chain = champlain_map_source_chain_new ();
+
+      tile_size = champlain_map_source_get_tile_size(tile_source);
+      src = CHAMPLAIN_MAP_SOURCE(champlain_error_tile_source_new_full (tile_size));
+
+      champlain_map_source_chain_push_map_source(source_chain, src);
+      champlain_map_source_chain_push_map_source(source_chain, tile_source);
+
+      cache_path = g_build_path (G_DIR_SEPARATOR_S, g_get_user_cache_dir (), "champlain", NULL);
+      src = CHAMPLAIN_MAP_SOURCE(champlain_file_cache_new_full (100000000, cache_path, FALSE));
+      g_free(cache_path);
+
+      champlain_map_source_chain_push_map_source(source_chain, src);
+
+      g_object_set (G_OBJECT (view), "map-source", source_chain, NULL);
       if (strncmp (id, "memphis", 7) == 0)
         load_rules_into_gui (view);
     }
@@ -472,7 +486,6 @@ static void
 map_data_changed (GtkWidget *widget, ChamplainView *view)
 {
   gint index;
-  ChamplainMapSource *source;
   GtkTreeIter iter;
   GtkTreeModel *model;
 
@@ -481,19 +494,17 @@ map_data_changed (GtkWidget *widget, ChamplainView *view)
 
   model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
   gtk_tree_model_get (model, &iter, 1, &index, -1);
-  
+
   map_index = index;
 
-  g_object_get (G_OBJECT (view), "map-source", &source, NULL);
-  if (g_strcmp0 (champlain_map_source_get_id (source), "memphis-local") == 0)
-    load_local_map_data (source);
+  if (g_strcmp0 (champlain_map_source_get_id (tile_source), "memphis-local") == 0)
+    load_local_map_data (tile_source);
 }
 
 static void
 rules_changed (GtkWidget *widget, ChamplainView *view)
 {
   gchar* file;
-  ChamplainMapSource *source;
   GtkTreeIter iter;
   GtkTreeModel *model;
 
@@ -503,12 +514,11 @@ rules_changed (GtkWidget *widget, ChamplainView *view)
   model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
   gtk_tree_model_get (model, &iter, 0, &file, -1);
 
-  g_object_get (G_OBJECT (view), "map-source", &source, NULL);
-  if (strncmp (champlain_map_source_get_id (source), "memphis", 7) == 0)
+  if (strncmp (champlain_map_source_get_id (tile_source), "memphis", 7) == 0)
     {
       champlain_memphis_map_source_load_rules (
-          CHAMPLAIN_MEMPHIS_MAP_SOURCE (source),
-          file);
+        CHAMPLAIN_MEMPHIS_MAP_SOURCE (tile_source),
+        file);
       load_rules_into_gui (view);
     }
 }
@@ -556,25 +566,25 @@ build_source_combo_box (GtkComboBox *box)
   GtkCellRenderer *cell;
 
   store = gtk_tree_store_new (N_COLS, G_TYPE_STRING, /* id */
-      G_TYPE_STRING, /* name */
-      -1);
+                              G_TYPE_STRING, /* name */
+                              -1);
 
   factory = champlain_map_source_factory_dup_default ();
   sources = champlain_map_source_factory_dup_list (factory);
 
   iter = sources;
   while (iter != NULL)
-  {
-    ChamplainMapSourceDesc *desc;
+    {
+      ChamplainMapSourceDesc *desc;
 
-    desc = (ChamplainMapSourceDesc*) iter->data;
+      desc = (ChamplainMapSourceDesc*) iter->data;
 
-    gtk_tree_store_append (store, &parent, NULL);
-    gtk_tree_store_set (store, &parent, COL_ID, desc->id,
-        COL_NAME, desc->name, -1);
+      gtk_tree_store_append (store, &parent, NULL);
+      gtk_tree_store_set (store, &parent, COL_ID, desc->id,
+                          COL_NAME, desc->name, -1);
 
-    iter = g_slist_next (iter);
-  }
+      iter = g_slist_next (iter);
+    }
 
   g_slist_free (sources);
   g_object_unref (factory);
@@ -584,7 +594,7 @@ build_source_combo_box (GtkComboBox *box)
   cell = gtk_cell_renderer_text_new ();
   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box), cell, FALSE);
   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box), cell,
-      "text", COL_NAME, NULL);
+                                  "text", COL_NAME, NULL);
 }
 
 static void
@@ -595,22 +605,22 @@ build_data_combo_box (GtkComboBox *box)
   GtkCellRenderer *cell;
 
   store = gtk_tree_store_new (2, G_TYPE_STRING, /* file name */
-      G_TYPE_INT, /* index */ -1);
+                              G_TYPE_INT, /* index */ -1);
 
   gtk_tree_store_append (store, &parent, NULL);
   gtk_tree_store_set (store, &parent, 0, maps[0],
-      1, 0, -1);
+                      1, 0, -1);
 
   gtk_tree_store_append (store, &parent, NULL);
   gtk_tree_store_set (store, &parent, 0, maps[1],
-      1, 1, -1);
+                      1, 1, -1);
 
   gtk_combo_box_set_model (box, GTK_TREE_MODEL (store));
 
   cell = gtk_cell_renderer_text_new ();
   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box), cell, FALSE);
   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box), cell,
-      "text", 0, NULL);
+                                  "text", 0, NULL);
 }
 
 static void
@@ -632,41 +642,40 @@ build_rules_combo_box (GtkComboBox *box)
   cell = gtk_cell_renderer_text_new ();
   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box), cell, FALSE);
   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box), cell,
-      "text", 0, NULL);
+                                  "text", 0, NULL);
 }
 
 void list_item_selected_cb (GtkTreeView *tree_view,
-    GtkTreePath *path,
-    GtkTreeViewColumn *column,
-    ChamplainView *view)
+                            GtkTreePath *path,
+                            GtkTreeViewColumn *column,
+                            ChamplainView *view)
 {
   GtkTreeIter iter;
   GtkTreeModel *model;
   char *id;
   GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
-  ChamplainMemphisMapSource *source;
   MemphisRule *rule;
 
   if (rule_edit_window != NULL)
     return;
 
-  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
-    gtk_tree_model_get (model, &iter, 0, &id, -1);
+  if (gtk_tree_selection_get_selected (selection, &model, &iter))
+    {
+      gtk_tree_model_get (model, &iter, 0, &id, -1);
 
-    g_object_get (G_OBJECT (view), "map-source", &source, NULL);
-    rule = champlain_memphis_map_source_get_rule (source, id);
+      rule = champlain_memphis_map_source_get_rule (CHAMPLAIN_MEMPHIS_MAP_SOURCE(tile_source), id);
 
-    if (rule != NULL)
-      create_rule_edit_window (rule, id, source);
+      if (rule != NULL)
+        create_rule_edit_window (rule, id, CHAMPLAIN_MEMPHIS_MAP_SOURCE(tile_source));
 
-    g_free (id);
-  }
+      g_free (id);
+    }
 }
 
 static gboolean
 delete_window (GtkWidget *widget,
-    GdkEvent  *event,
-    gpointer   user_data)
+               GdkEvent  *event,
+               gpointer   user_data)
 {
   gtk_main_quit ();
 }
@@ -695,7 +704,7 @@ main (int argc,
    * stop the main GTK loop
    */
   g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (on_destroy),
-      NULL);
+                    NULL);
 
   hbox = gtk_hbox_new (FALSE, 10);
   menubox = gtk_vbox_new (FALSE, 10);
@@ -710,7 +719,7 @@ main (int argc,
   view = gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (widget));
 
   g_object_set (G_OBJECT (view), "scroll-mode", CHAMPLAIN_SCROLL_MODE_KINETIC,
-      "zoom-level", 9, NULL);
+                "zoom-level", 9, NULL);
 
   gtk_widget_set_size_request (widget, 640, 480);
 
@@ -726,10 +735,10 @@ main (int argc,
 
   button = gtk_spin_button_new_with_range(0, 20, 1);
   gtk_spin_button_set_value (GTK_SPIN_BUTTON (button),
-      champlain_view_get_zoom_level (view));
+                             champlain_view_get_zoom_level (view));
   g_signal_connect (button, "changed", G_CALLBACK (zoom_changed), view);
   g_signal_connect (view, "notify::zoom-level", G_CALLBACK (map_zoom_changed),
-      button);
+                    button);
   gtk_container_add (GTK_CONTAINER (bbox), button);
 
   gtk_box_pack_start (GTK_BOX (menubox), bbox, FALSE, FALSE, 0);
@@ -782,7 +791,7 @@ main (int argc,
   label = gtk_label_new (NULL);
   gtk_label_set_markup (GTK_LABEL (label), "Background color");
   gtk_box_pack_start (GTK_BOX (bbox), label, FALSE, FALSE, 0);
-  
+
   bg_button = gtk_color_button_new ();
   gtk_color_button_set_title (GTK_COLOR_BUTTON (bg_button), "Background");
   g_signal_connect (bg_button, "color-set", G_CALLBACK (bg_color_set_cb), view);
@@ -818,7 +827,7 @@ main (int argc,
 
   scrolled = gtk_scrolled_window_new (NULL, NULL);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
-      GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(scrolled), tree_view);
 
   gtk_box_pack_start (GTK_BOX (memphis_box), scrolled, TRUE, TRUE, 0);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]