diff --git a/configure.ac b/configure.ac
index 8952c34..5524617 100644
--- a/configure.ac
+++ b/configure.ac
@@ -35,6 +35,10 @@ ENCHANT_REQUIRED=1.2.0
 ISO_CODES_REQUIRED=0.35
 LIBNOTIFY_REQUIRED=0.4.4
 LIBCANBERRA_GTK_REQUIRED=0.4
+GEOCLUE_REQUIRED=0.11
+LIBCHAMPLAIN_REQUIRED=0.3.0
+LIBCHAMPLAIN_GTK_REQUIRED=0.3.0
+CLUTTER_GTK_REQUIRED=0.8.2
 
 # Use --enable-maintainer-mode to disabled deprecated symbols
 GNOME_MAINTAINER_MODE_DEFINES
@@ -190,6 +194,68 @@ fi
 AM_CONDITIONAL(HAVE_ENCHANT, test "x$have_enchant" = "xyes")
 
 # -----------------------------------------------------------
+# Map view checks: libchamplain
+# -----------------------------------------------------------
+AC_ARG_ENABLE(map,
+              AS_HELP_STRING([--enable-map=@<:@no/yes/auto@:>@],
+                             [Enable map view]), ,
+                             enable_map=auto)
+
+if test "x$enable_map" != "xno"; then
+   PKG_CHECK_MODULES(LIBCHAMPLAIN,
+   [
+      champlain-0.3 >= $LIBCHAMPLAIN_REQUIRED,
+      champlain-gtk-0.3 >= $LIBCHAMPLAIN_GTK_REQUIRED
+      clutter-gtk-0.8 >= $CLUTTER_GTK_REQUIRED
+   ], have_libchamplain="yes", have_libchamplain="no")
+
+   if test "x$have_libchamplain" = "xyes"; then
+      AC_DEFINE(HAVE_LIBCHAMPLAIN, 1, [Define if you have libchamplain])
+   else
+       have_libchamplain="no"
+   fi
+else
+   have_libchamplain=no
+fi
+
+if test "x$enable_map" = "xyes" -a "x$have_libchamplain" != "xyes"; then
+   AC_MSG_ERROR([Couldn't find map view dependencies.])
+fi
+
+AM_CONDITIONAL(HAVE_LIBCHAMPLAIN, test "x$have_libchamplain" = "xyes")
+
+# -----------------------------------------------------------
+# location checks: geoclue
+# -----------------------------------------------------------
+AC_ARG_ENABLE(location,
+              AS_HELP_STRING([--enable-location=@<:@no/yes/auto@:>@],
+                             [Enable location awareness]), ,
+                             enable_location=auto)
+
+if test "x$enable_location" != "xno"; then
+   PKG_CHECK_MODULES(GEOCLUE,
+   [
+      geoclue >= $GEOCLUE_REQUIRED
+   ], have_geoclue="yes", have_geoclue="no")
+
+   if test "x$have_geoclue" = "xyes"; then
+      AC_DEFINE(HAVE_GEOCLUE, 1, [Define if you have geoclue])
+   else
+       have_geoclue="no"
+   fi
+else
+   have_geoclue=no
+fi
+
+if test "x$enable_map" = "xyes" -a "x$have_geoclue" != "xyes"; then
+   AC_MSG_ERROR([Couldn't find location dependencies.])
+fi
+
+AM_CONDITIONAL(HAVE_GEOCLUE, test "x$have_geoclue" = "xyes")
+AC_SUBST(GEOCLUE_CFLAGS)
+AC_SUBST(GEOCLUE_LIBS)
+
+# -----------------------------------------------------------
 # Megaphone
 # -----------------------------------------------------------
 AC_ARG_ENABLE(megaphone,
@@ -362,6 +428,8 @@ Configure summary:
 	Documentation...............:  ${enable_gtk_doc}
 	Python bindings.............:  ${have_python}
 	Spell checking..............:  ${have_enchant}
+	Use libchamplain............:  ${have_libchamplain}
+	Use Geoclue.................:  ${have_geoclue}
 	Megaphone applet............:  ${have_megaphone}
 	Nothere applet..............:  ${have_nothere}
 "
diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am
index cf6a276..2f1eaaa 100644
--- a/libempathy-gtk/Makefile.am
+++ b/libempathy-gtk/Makefile.am
@@ -7,6 +7,8 @@ AM_CPPFLAGS =                                           \
 	-DPKGDATADIR=\""$(pkgdatadir)"\"		\
 	$(LIBEMPATHYGTK_CFLAGS)				\
 	$(ENCHANT_CFLAGS)				\
+	$(GEOCLUE_CFLAGS)				\
+	$(LIBCHAMPLAIN_CFLAGS)				\
 	$(WARN_CFLAGS)					\
 	$(DISABLE_DEPRECATED)
 
@@ -68,6 +70,8 @@ nodist_libempathy_gtk_la_SOURCES =\
 libempathy_gtk_la_LIBADD =			\
 	$(LIBEMPATHYGTK_LIBS)			\
 	$(ENCHANT_LIBS)				\
+	$(GEOCLUE_LIBS)				\
+	$(LIBCHAMPLAIN_LIBS)			\
 	$(top_builddir)/libempathy/libempathy.la
 
 libempathy_gtk_la_LDFLAGS =                \
diff --git a/libempathy-gtk/empathy-contact-dialogs.c b/libempathy-gtk/empathy-contact-dialogs.c
index 7de7931..307b0fb 100644
--- a/libempathy-gtk/empathy-contact-dialogs.c
+++ b/libempathy-gtk/empathy-contact-dialogs.c
@@ -190,6 +190,7 @@ empathy_contact_information_dialog_show (EmpathyContact *contact,
 
 	/* Contact info widget */
 	contact_widget = empathy_contact_widget_new (contact,
+		EMPATHY_CONTACT_WIDGET_SHOW_LOCATION |
 		EMPATHY_CONTACT_WIDGET_EDIT_NONE);
 	gtk_container_set_border_width (GTK_CONTAINER (contact_widget), 8);
 	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
diff --git a/libempathy-gtk/empathy-contact-widget.c b/libempathy-gtk/empathy-contact-widget.c
index a32ac65..5ccc4b0 100644
--- a/libempathy-gtk/empathy-contact-widget.c
+++ b/libempathy-gtk/empathy-contact-widget.c
@@ -21,18 +21,26 @@
 
 #include <config.h>
 
+#define _GNU_SOURCE
 #include <string.h>
 #include <stdlib.h>
+#include <time.h>
 
 #include <gtk/gtk.h>
 #include <glib/gi18n-lib.h>
 
+#if HAVE_LIBCHAMPLAIN
+#include <champlain/champlain.h>
+#include <champlain-gtk/champlain-gtk.h>
+#endif
+
 #include <libmissioncontrol/mc-account.h>
 #include <telepathy-glib/util.h>
 
 #include <libempathy/empathy-tp-contact-factory.h>
 #include <libempathy/empathy-contact-manager.h>
 #include <libempathy/empathy-contact-list.h>
+#include <libempathy/empathy-location.h>
 #include <libempathy/empathy-utils.h>
 
 #include "empathy-contact-widget.h"
@@ -90,6 +98,17 @@ typedef struct
   GtkWidget *table_contact;
   GtkWidget *vbox_avatar;
 
+  /* Location */
+  GtkWidget *vbox_location;
+  GtkWidget *subvbox_location;
+  GtkWidget *table_location;
+  GtkWidget *label_location;
+#if HAVE_LIBCHAMPLAIN
+  GtkWidget *viewport_map;
+  GtkWidget *map_view_embed;
+  ClutterActor *map_view;
+#endif
+
   /* Groups */
   GtkWidget *vbox_groups;
   GtkWidget *entry_group;
@@ -161,6 +180,7 @@ static void contact_widget_details_setup (EmpathyContactWidget *information);
 static void contact_widget_details_update (EmpathyContactWidget *information);
 static void contact_widget_client_setup (EmpathyContactWidget *information);
 static void contact_widget_client_update (EmpathyContactWidget *information);
+static void contact_widget_location_update (EmpathyContactWidget *information);
 
 enum
 {
@@ -203,6 +223,12 @@ empathy_contact_widget_new (EmpathyContact *contact,
        "label_status", &information->label_status,
        "table_contact", &information->table_contact,
        "vbox_avatar", &information->vbox_avatar,
+       "vbox_location", &information->vbox_location,
+       "subvbox_location", &information->subvbox_location,
+       "label_location", &information->label_location,
+#if HAVE_LIBCHAMPLAIN
+       "viewport_map", &information->viewport_map,
+#endif
        "vbox_groups", &information->vbox_groups,
        "entry_group", &information->entry_group,
        "button_group", &information->button_group,
@@ -222,6 +248,7 @@ empathy_contact_widget_new (EmpathyContact *contact,
       "entry_group", "activate", contact_widget_entry_group_activate_cb,
       "button_group", "clicked", contact_widget_button_group_clicked_cb,
       NULL);
+  information->table_location = NULL;
 
   g_object_set_data (G_OBJECT (information->vbox_contact_widget),
       "EmpathyContactWidget",
@@ -379,6 +406,7 @@ contact_widget_set_contact (EmpathyContactWidget *information,
   contact_widget_groups_update (information);
   contact_widget_details_update (information);
   contact_widget_client_update (information);
+  contact_widget_location_update (information);
 }
 
 static gboolean
@@ -1203,3 +1231,215 @@ contact_widget_client_update (EmpathyContactWidget *information)
 {
   /* FIXME: Needs new telepathy spec */
 }
+
+/* Converts the Location's GHashTable's key to a user readable string */
+static const gchar *
+location_key_to_label (const gchar *key)
+{
+  if (tp_strdiff (key, EMPATHY_LOCATION_COUNTRY_CODE) == FALSE)
+    return _("Country ISO Code:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_COUNTRY) == FALSE)
+    return _("Country:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_REGION) == FALSE)
+    return _("State:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_LOCALITY) == FALSE)
+    return _("City:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_AREA) == FALSE)
+    return _("Area:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_POSTAL_CODE) == FALSE)
+    return _("Postal Code:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_STREET) == FALSE)
+    return _("Street:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_BUILDING) == FALSE)
+    return _("Building:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_FLOOR) == FALSE)
+    return _("Floor:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_ROOM) == FALSE)
+    return _("Room:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_TEXT) == FALSE)
+    return _("Text:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_DESCRIPTION) == FALSE)
+    return _("Description:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_URI) == FALSE)
+    return _("URI:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_ACCURACY_LEVEL) == FALSE)
+    return _("Accuracy Level:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_ERROR) == FALSE)
+    return _("Error:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_VERTICAL_ERROR_M) == FALSE)
+    return _("Vertical Error (meters):");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_HORIZONTAL_ERROR_M) == FALSE)
+    return _("Horizontal Error (meters):");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_SPEED) == FALSE)
+    return _("Speed:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_BEARING) == FALSE)
+    return _("Bearing:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_CLIMB) == FALSE)
+    return _("Climb Speed:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_TIMESTAMP) == FALSE)
+    return _("Taken at:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_LON) == FALSE)
+    return _("Longitude:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_LAT) == FALSE)
+    return _("Latitude:");
+  else if (tp_strdiff (key, EMPATHY_LOCATION_ALT) == FALSE)
+    return _("Altitude:");
+  else
+  {
+    DEBUG ("Unexpected Location key: %s", key);
+    return key;
+  }
+}
+
+static void
+contact_widget_location_update (EmpathyContactWidget *information)
+{
+  GHashTable *location;
+  GValue *value;
+  gdouble lat, lon;
+  gboolean has_position = TRUE;
+
+  location = empathy_contact_get_location (information->contact);
+  if (location == NULL || g_hash_table_size (location) == 0)
+    {
+      gtk_widget_hide (information->vbox_location);
+      return;
+    }
+
+  value = g_hash_table_lookup (location, EMPATHY_LOCATION_LAT);
+  if (value == NULL)
+      has_position = FALSE;
+  else
+      lat = g_value_get_double (value);
+
+  value = g_hash_table_lookup (location, EMPATHY_LOCATION_LON);
+  if (value == NULL)
+      has_position = FALSE;
+  else
+      lon = g_value_get_double (value);
+
+  value = g_hash_table_lookup (location, EMPATHY_LOCATION_TIMESTAMP);
+  if (value == NULL)
+    gtk_label_set_markup (GTK_LABEL (information->label_location), _("<b>Location</b>"));
+  else
+    {
+      gchar user_date [100];
+      gint64 stamp;
+      struct tm *ptm;
+      time_t time;
+
+      stamp = g_value_get_int64 (value);
+      time = stamp;
+      ptm = gmtime (&time);
+
+      if (strftime (user_date, 100, _("%B %e, %Y at %R UTC"), ptm) > 0)
+        {
+          gchar *text;
+          text = g_strconcat ( _("<b>Location</b> on "), user_date, NULL);
+          gtk_label_set_markup (GTK_LABEL (information->label_location), text);
+          g_free (text);
+        }
+      else
+        gtk_label_set_markup (GTK_LABEL (information->label_location), _("<b>Location</b>"));
+    }
+
+  if (information->flags & EMPATHY_CONTACT_WIDGET_FOR_TOOLTIP ||
+      information->flags & EMPATHY_CONTACT_WIDGET_SHOW_LOCATION)
+    {
+      GtkWidget *label;
+      guint row = 0;
+      GHashTableIter iter;
+      gpointer key, value;
+
+      if (information->table_location != NULL)
+        {
+          gtk_widget_destroy (information->table_location);
+        }
+
+      information->table_location = gtk_table_new (1, 2, FALSE);
+      gtk_box_pack_start (GTK_BOX (information->subvbox_location),
+          information->table_location, FALSE, FALSE, 5);
+
+      g_hash_table_iter_init (&iter, location);
+      while (g_hash_table_iter_next (&iter, &key, &value))
+        {
+          const gchar *skey;
+          const gchar* user_label;
+          GValue *gvalue;
+          char *svalue = NULL;
+
+          skey = (const gchar *) key;
+          if (tp_strdiff (skey, EMPATHY_LOCATION_TIMESTAMP) == FALSE)
+            continue;
+
+          user_label = location_key_to_label (skey);
+          gvalue = (GValue *) value;
+
+          label = gtk_label_new (user_label);
+          gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+          gtk_table_attach (GTK_TABLE (information->table_location),
+              label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 10, 0);
+          gtk_widget_show (label);
+
+          if (G_VALUE_TYPE (gvalue) == G_TYPE_DOUBLE)
+            {
+              gdouble dvalue;
+              dvalue = g_value_get_double (gvalue);
+              svalue = g_strdup_printf ("%f", dvalue);
+            }
+          else if (G_VALUE_TYPE (gvalue) == G_TYPE_STRING)
+            {
+              svalue = g_value_dup_string (gvalue);
+            }
+
+          if (svalue != NULL)
+            {
+              label = gtk_label_new (svalue);
+              gtk_table_attach_defaults (GTK_TABLE (information->table_location),
+                  label, 1, 2, row, row + 1);
+              gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+              gtk_widget_show (label);
+            }
+
+          g_free (svalue);
+          row++;
+        }
+
+      gtk_widget_show (information->table_location);
+    }
+
+#if HAVE_LIBCHAMPLAIN
+  /* Cannot be displayed in tooltips until Clutter-Gtk can deal with such
+   * windows
+   */
+  if (has_position &&
+      information->flags & EMPATHY_CONTACT_WIDGET_SHOW_LOCATION)
+    {
+      ClutterActor *marker;
+      ChamplainLayer *layer;
+
+      information->map_view = champlain_view_new ();
+      information->map_view_embed = champlain_view_embed_new (
+          CHAMPLAIN_VIEW (information->map_view));
+
+      gtk_container_add (GTK_CONTAINER (information->viewport_map),
+          information->map_view_embed);
+      g_object_set (G_OBJECT (information->map_view), "show-license", FALSE,
+          "scroll-mode", CHAMPLAIN_SCROLL_MODE_KINETIC,
+          NULL);
+
+      layer = champlain_layer_new ();
+      champlain_view_add_layer (CHAMPLAIN_VIEW (information->map_view), layer);
+
+      marker = champlain_marker_new_with_text (
+          empathy_contact_get_name (information->contact), NULL, NULL, NULL);
+      champlain_base_marker_set_position (CHAMPLAIN_BASE_MARKER (marker), lat, lon);
+      clutter_container_add (CLUTTER_CONTAINER (layer), marker, NULL);
+
+      champlain_view_center_on (CHAMPLAIN_VIEW(information->map_view), lat, lon);
+      gtk_widget_show_all (information->viewport_map);
+    }
+#endif
+
+    gtk_widget_show (information->vbox_location);
+}
diff --git a/libempathy-gtk/empathy-contact-widget.h b/libempathy-gtk/empathy-contact-widget.h
index 0da5580..b010578 100644
--- a/libempathy-gtk/empathy-contact-widget.h
+++ b/libempathy-gtk/empathy-contact-widget.h
@@ -59,6 +59,7 @@ typedef enum
   EMPATHY_CONTACT_WIDGET_EDIT_ID      = 1 << 3,
   EMPATHY_CONTACT_WIDGET_EDIT_GROUPS  = 1 << 4,
   EMPATHY_CONTACT_WIDGET_FOR_TOOLTIP  = 1 << 5,
+  EMPATHY_CONTACT_WIDGET_SHOW_LOCATION  = 1 << 6,
 } EmpathyContactWidgetFlags;
 
 GtkWidget * empathy_contact_widget_new (EmpathyContact *contact,
diff --git a/libempathy-gtk/empathy-contact-widget.ui b/libempathy-gtk/empathy-contact-widget.ui
index 1f53f76..a46b2c6 100644
--- a/libempathy-gtk/empathy-contact-widget.ui
+++ b/libempathy-gtk/empathy-contact-widget.ui
@@ -162,6 +162,62 @@
       </packing>
     </child>
     <child>
+      <object class="GtkVBox" id="vbox_location">
+        <property name="orientation">vertical</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkLabel" id="label_location">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">&lt;b&gt;Location&lt;/b&gt; at (date)	</property>
+            <property name="use_markup">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkAlignment" id="alignment1">
+            <property name="visible">True</property>
+            <property name="left_padding">12</property>
+            <child>
+              <object class="GtkVBox" id="subvbox_location">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">5</property>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <object class="GtkViewport" id="viewport_map">
+                    <property name="height_request">150</property>
+                    <property name="resize_mode">queue</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+    <child>
       <object class="GtkVBox" id="vbox_groups">
         <property name="orientation">vertical</property>
         <property name="spacing">6</property>
@@ -267,7 +323,7 @@
         </child>
       </object>
       <packing>
-        <property name="position">1</property>
+        <property name="position">2</property>
       </packing>
     </child>
     <child>
@@ -411,7 +467,7 @@
       </object>
       <packing>
         <property name="expand">False</property>
-        <property name="position">2</property>
+        <property name="position">3</property>
       </packing>
     </child>
     <child>
@@ -578,7 +634,7 @@
       </object>
       <packing>
         <property name="expand">False</property>
-        <property name="position">3</property>
+        <property name="position">4</property>
       </packing>
     </child>
   </object>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index ac7fe06..8952537 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -79,3 +79,5 @@ src/empathy-status-icon.c
 [type: gettext/glade]src/empathy-status-icon.ui
 src/empathy-tube-dispatch.c
 [type: gettext/glade]src/empathy-call-window-fullscreen.ui
+src/empathy-map-view.c
+[type: gettext/glade]src/empathy-map-view.ui
diff --git a/src/Makefile.am b/src/Makefile.am
index 58f30ad..ef6311f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,15 +4,19 @@ AM_CPPFLAGS =						\
 	-I$(top_srcdir)                          	\
 	$(EMPATHY_CFLAGS)				\
 	$(LIBNOTIFY_CFLAGS)				\
-	$(WARN_CFLAGS)					\
+	$(GEOCLUE_CFLAGS)				\
+	$(LIBCHAMPLAIN_CFLAGS)				\
 	$(DISABLE_DEPRECATED)
+	$(WARN_CFLAGS)
 
 LDADD =								\
 	$(top_builddir)/libempathy-gtk/libempathy-gtk.la	\
 	$(top_builddir)/libempathy/libempathy.la		\
 	$(top_builddir)/extensions/libemp-extensions.la		\
 	$(LIBNOTIFY_LIBS)					\
-	$(EMPATHY_LIBS)
+	$(EMPATHY_LIBS)						\
+	$(LIBCHAMPLAIN_LIBS)					\
+	$(GEOCLUE_LIBS)
 
 bin_PROGRAMS =			\
 	empathy			\
@@ -71,6 +75,14 @@ ui_DATA = 					\
 	empathy-preferences.ui			\
 	empathy-status-icon.ui
 
+if HAVE_LIBCHAMPLAIN
+empathy_SOURCES += 				\
+	empathy-map-view.c empathy-map-view.h
+
+ui_DATA += 					\
+	empathy-map-view.ui
+endif
+
 dist_man_MANS =			\
 	empathy.1
 
diff --git a/src/empathy-main-window.c b/src/empathy-main-window.c
index d453fed..9d26137 100644
--- a/src/empathy-main-window.c
+++ b/src/empathy-main-window.c
@@ -56,6 +56,7 @@
 #include "empathy-preferences.h"
 #include "empathy-about-dialog.h"
 #include "empathy-new-chatroom-dialog.h"
+#include "empathy-map-view.h"
 #include "empathy-chatrooms-window.h"
 #include "empathy-event-manager.h"
 #include "empathy-ft-manager.h"
@@ -714,6 +715,15 @@ main_window_view_show_offline_cb (GtkToggleAction   *action,
 	//empathy_sound_set_enabled (TRUE);
 }
 
+#if HAVE_LIBCHAMPLAIN
+static void
+main_window_view_show_map_cb (GtkCheckMenuItem  *item,
+			      EmpathyMainWindow *window)
+{
+	empathy_map_view_show ();
+}
+#endif
+
 static void
 main_window_favorite_chatroom_join (EmpathyChatroom *chatroom)
 {
@@ -1097,6 +1107,7 @@ empathy_main_window_show (void)
 	GtkWidget                *sw;
 	GtkToggleAction          *show_offline_widget;
 	GtkWidget                *ebox;
+	GtkAction                *show_map_widget;
 	GtkToolItem              *item;
 	gboolean                  show_offline;
 	gboolean                  show_avatars;
@@ -1121,6 +1132,7 @@ empathy_main_window_show (void)
 				       "ui_manager", &window->ui_manager,
 				       "view_show_offline", &show_offline_widget,
 				       "view_history", &window->view_history,
+				       "view_show_map", &show_map_widget,
 				       "room_join_favorites", &window->room_join_favorites,
 				       "presence_toolbar", &window->presence_toolbar,
 				       "roster_scrolledwindow", &sw,
@@ -1139,6 +1151,9 @@ empathy_main_window_show (void)
 			      "chat_add_contact", "activate", main_window_chat_add_contact_cb,
 			      "view_show_ft_manager", "activate", main_window_view_show_ft_manager,
 			      "view_show_offline", "toggled", main_window_view_show_offline_cb,
+#if HAVE_LIBCHAMPLAIN
+			      "view_show_map", "activate", main_window_view_show_map_cb,
+#endif
 			      "edit", "activate", main_window_edit_cb,
 			      "edit_accounts", "activate", main_window_edit_accounts_cb,
 			      "edit_personal_information", "activate", main_window_edit_personal_information_cb,
@@ -1153,6 +1168,10 @@ empathy_main_window_show (void)
 	g_object_ref (window->ui_manager);
 	g_object_unref (gui);
 
+#if !HAVE_LIBCHAMPLAIN
+	gtk_action_set_visible (show_map_widget, FALSE);
+#endif
+
 	window->mc = empathy_mission_control_dup_singleton ();
 	window->account_manager = empathy_account_manager_dup_singleton ();
 
diff --git a/src/empathy-main-window.ui b/src/empathy-main-window.ui
index 14660b7..bbf8f19 100644
--- a/src/empathy-main-window.ui
+++ b/src/empathy-main-window.ui
@@ -48,6 +48,12 @@
           <accelerator key="H" modifiers="GDK_CONTROL_MASK"/>
         </child>
         <child>
+          <object class="GtkAction" id="view_show_map">
+            <property name="name">view_show_map</property>
+            <property name="label" translatable="yes">Show Contacts on a Map</property>
+          </object>
+        </child>
+        <child>
           <object class="GtkAction" id="chat_quit">
             <property name="stock_id">gtk-quit</property>
             <property name="name">chat_quit</property>
@@ -162,6 +168,7 @@
           <separator/>
           <menuitem action="view_history"/>
           <menuitem action="view_show_ft_manager"/>
+          <menuitem action="view_show_map"/>
         </menu>
         <menu action="room">
           <menuitem action="room_join_new"/>
diff --git a/src/empathy-map-view.c b/src/empathy-map-view.c
new file mode 100644
index 0000000..da5837e
--- /dev/null
+++ b/src/empathy-map-view.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2008, 2009 Collabora Ltd.
+ *
+ * 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
+ *
+ * Authors: Pierre-Luc Beaudoin <pierre-luc.beaudoin@collabora.co.uk>
+ */
+
+#include <config.h>
+
+#include <sys/stat.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include <champlain/champlain.h>
+#include <champlain-gtk/champlain-gtk.h>
+#include <clutter-gtk/gtk-clutter-embed.h>
+#if HAVE_GEOCLUE
+#include <geoclue/geoclue-geocode.h>
+#endif
+#include <telepathy-glib/util.h>
+
+#include <libempathy/empathy-contact.h>
+#include <libempathy/empathy-contact-manager.h>
+#include <libempathy/empathy-utils.h>
+#include <libempathy/empathy-location.h>
+
+#include <libempathy-gtk/empathy-contact-list-store.h>
+#include <libempathy-gtk/empathy-contact-list-view.h>
+#include <libempathy-gtk/empathy-presence-chooser.h>
+#include <libempathy-gtk/empathy-ui-utils.h>
+
+#include "empathy-map-view.h"
+
+#define DEBUG_FLAG EMPATHY_DEBUG_LOCATION
+#include <libempathy/empathy-debug.h>
+
+typedef struct {
+  EmpathyContactListStore *list_store;
+
+  GtkWidget *window;
+  GtkWidget *zoom_in;
+  GtkWidget *zoom_out;
+  ChamplainView *map_view;
+  ChamplainLayer *layer;
+#if HAVE_GEOCLUE
+  GeoclueGeocode *geocode;
+#endif
+} EmpathyMapView;
+
+static void map_view_destroy_cb (GtkWidget *widget,
+    EmpathyMapView *window);
+static gboolean map_view_contacts_foreach (GtkTreeModel *model,
+    GtkTreePath *path,
+    GtkTreeIter *iter,
+    gpointer user_data);
+static void map_view_zoom_in_cb (GtkWidget *widget,
+    EmpathyMapView *window);
+static void map_view_zoom_out_cb (GtkWidget *widget,
+    EmpathyMapView *window);
+static void map_view_contact_location_notify (GObject *gobject,
+    GParamSpec *arg1,
+    gpointer user_data);
+static gchar * get_dup_string (GHashTable *location,
+    gchar *key);
+
+GtkWidget *
+empathy_map_view_show ()
+{
+  static EmpathyMapView *window = NULL;
+  GtkBuilder *gui;
+  GtkWidget *sw;
+  GtkWidget *embed;
+  gchar *filename;
+  GtkTreeModel *model;
+  EmpathyContactList *list_iface;
+  EmpathyContactListStore *list_store;
+
+  if (window)
+    {
+      empathy_window_present (GTK_WINDOW (window->window), TRUE);
+      return window->window;
+    }
+
+  window = g_slice_new0 (EmpathyMapView);
+
+  /* Set up interface */
+  filename = empathy_file_lookup ("empathy-map-view.ui", "src");
+  gui = empathy_builder_get_file (filename,
+     "map_view", &window->window,
+     "zoom_in", &window->zoom_in,
+     "zoom_out", &window->zoom_out,
+     "map_scrolledwindow", &sw,
+     NULL);
+  g_free (filename);
+
+  empathy_builder_connect (gui, window,
+      "map_view", "destroy", map_view_destroy_cb,
+      "zoom_in", "clicked", map_view_zoom_in_cb,
+      "zoom_out", "clicked", map_view_zoom_out_cb,
+      NULL);
+
+  g_object_unref (gui);
+
+  /* Clear the static pointer to window if the dialog is destroyed */
+  g_object_add_weak_pointer (G_OBJECT (window->window), (gpointer *) &window);
+
+  list_iface = EMPATHY_CONTACT_LIST (empathy_contact_manager_dup_singleton ());
+  list_store = empathy_contact_list_store_new (list_iface);
+  empathy_contact_list_store_set_show_groups (list_store, FALSE);
+  empathy_contact_list_store_set_show_avatars (list_store, TRUE);
+  g_object_unref (list_iface);
+
+  window->list_store = list_store;
+
+  /* Set up map view */
+  window->map_view = CHAMPLAIN_VIEW (champlain_view_new ());
+  g_object_set (G_OBJECT (window->map_view), "zoom-level", 1,
+     "scroll-mode", CHAMPLAIN_SCROLL_MODE_KINETIC, NULL);
+  champlain_view_center_on (window->map_view, 36, 0);
+
+  embed = champlain_view_embed_new (window->map_view);
+  gtk_container_add (GTK_CONTAINER (sw),
+     GTK_WIDGET (embed));
+  gtk_widget_show_all (embed);
+
+  window->layer = g_object_ref (champlain_layer_new ());
+  champlain_view_add_layer (window->map_view, window->layer);
+
+  /* Set up contact list. */
+  model = GTK_TREE_MODEL (window->list_store);
+  gtk_tree_model_foreach (model, map_view_contacts_foreach, window);
+
+  empathy_window_present (GTK_WINDOW (window->window), TRUE);
+  return window->window;
+}
+
+static void
+map_view_destroy_cb (GtkWidget *widget,
+    EmpathyMapView *window)
+{
+  GtkTreeModel *model;
+
+  g_object_unref (window->list_store);
+  g_object_unref (window->layer);
+#if HAVE_GEOCLUE
+  if (window->geocode != NULL)
+    g_object_unref (window->geocode);
+#endif
+  g_slice_free (EmpathyMapView, window);
+}
+
+#if HAVE_GEOCLUE
+#define GEOCODE_SERVICE "org.freedesktop.Geoclue.Providers.Yahoo"
+#define GEOCODE_PATH "/org/freedesktop/Geoclue/Providers/Yahoo"
+
+/* This callback is called by geoclue when it found a position
+ * for the given address.  A position is necessary for a contact
+ * to show up on the map
+ */
+static void
+map_view_geocode_cb (GeoclueGeocode *geocode,
+    GeocluePositionFields fields,
+    double latitude,
+    double longitude,
+    double altitude,
+    GeoclueAccuracy *accuracy,
+    GError *error,
+    gpointer userdata)
+{
+  GValue *new_value;
+  GHashTable *location;
+
+  location = empathy_contact_get_location (EMPATHY_CONTACT (userdata));
+
+  GHashTable *address = g_object_get_data (userdata, "geoclue-address");
+  g_hash_table_destroy (address);
+  g_object_set_data (userdata, "geoclue-address", NULL);
+
+  if (error != NULL)
+    {
+      DEBUG ("Error geocoding location : %s", error->message);
+      return;
+    }
+
+  if (fields & GEOCLUE_POSITION_FIELDS_LONGITUDE)
+    {
+      new_value = tp_g_value_slice_new_double (longitude);
+      g_hash_table_replace (location, EMPATHY_LOCATION_LON, new_value);
+      DEBUG ("\t - Longitude: %f", longitude);
+    }
+  if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE)
+    {
+      new_value = tp_g_value_slice_new_double (latitude);
+      g_hash_table_replace (location, EMPATHY_LOCATION_LAT, new_value);
+      DEBUG ("\t - Latitude: %f", latitude);
+    }
+  if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE)
+    {
+      new_value = tp_g_value_slice_new_double (altitude);
+      g_hash_table_replace (location, EMPATHY_LOCATION_ALT, new_value);
+      DEBUG ("\t - Altitude: %f", altitude);
+    }
+
+  /* Don't change the accuracy as we used an address to get this position */
+
+  /* Ref the location hash table as it will be unref'd in set_location, 
+   * and we are only updating it */
+  g_hash_table_ref (location);
+  empathy_contact_set_location (EMPATHY_CONTACT (userdata), location);
+  g_hash_table_unref (location);
+}
+#endif
+
+static gchar *
+get_dup_string (GHashTable *location,
+    gchar *key)
+{
+  GValue *value;
+
+  value = g_hash_table_lookup (location, key);
+  if (value != NULL)
+    return g_value_dup_string (value);
+
+  return NULL;
+}
+
+static void
+map_view_marker_update_position (ChamplainMarker *marker,
+    EmpathyContact *contact)
+{
+  gdouble lon, lat;
+  GValue *value;
+  GHashTable *location;
+
+  location = empathy_contact_get_location (contact);
+  if (location == NULL ||
+      g_hash_table_size (location) == 0)
+  {
+    clutter_actor_hide (CLUTTER_ACTOR (marker));
+    return;
+  }
+
+  value = g_hash_table_lookup (location, EMPATHY_LOCATION_LAT);
+  if (value == NULL)
+    {
+      clutter_actor_hide (CLUTTER_ACTOR (marker));
+      return;
+    }
+  lat = g_value_get_double (value);
+
+  value = g_hash_table_lookup (location, EMPATHY_LOCATION_LON);
+  if (value == NULL)
+    {
+      clutter_actor_hide (CLUTTER_ACTOR (marker));
+      return;
+    }
+  lon = g_value_get_double (value);
+
+  clutter_actor_show (CLUTTER_ACTOR (marker));
+  champlain_base_marker_set_position (CHAMPLAIN_BASE_MARKER (marker), lat, lon);
+}
+
+static void
+map_view_contact_location_notify (GObject *gobject,
+    GParamSpec *arg1,
+    gpointer user_data)
+{
+  ChamplainMarker *marker = CHAMPLAIN_MARKER (user_data);
+  EmpathyContact *contact = EMPATHY_CONTACT (gobject);
+  map_view_marker_update_position (marker, contact);
+}
+
+static gboolean
+map_view_contacts_foreach (GtkTreeModel *model,
+    GtkTreePath *path,
+    GtkTreeIter *iter,
+    gpointer user_data)
+{
+  EmpathyMapView *window = (EmpathyMapView*) user_data;
+  EmpathyContact *contact;
+  ClutterActor *marker;
+  ClutterActor *texture;
+  GHashTable *location;
+  GdkPixbuf *avatar;
+  const gchar *name;
+
+  gtk_tree_model_get (model, iter, EMPATHY_CONTACT_LIST_STORE_COL_CONTACT,
+     &contact, -1);
+
+  if (contact == NULL)
+    return FALSE;
+
+  location = empathy_contact_get_location (contact);
+
+  if (location == NULL)
+    return FALSE;
+
+  marker = champlain_marker_new ();
+
+  avatar = empathy_pixbuf_avatar_from_contact_scaled (contact, 32, 32);
+  if (avatar != NULL)
+    {
+      texture = clutter_texture_new ();
+      gtk_clutter_texture_set_from_pixbuf (CLUTTER_TEXTURE (texture), avatar);
+      champlain_marker_set_image (CHAMPLAIN_MARKER (marker), texture);
+    }
+  else
+    champlain_marker_set_image (CHAMPLAIN_MARKER (marker), NULL);
+
+  name = empathy_contact_get_name (contact);
+  champlain_marker_set_text (CHAMPLAIN_MARKER (marker), name);
+
+  clutter_container_add (CLUTTER_CONTAINER (window->layer), marker, NULL);
+
+  g_signal_connect (contact, "notify::location",
+      G_CALLBACK (map_view_contact_location_notify), marker);
+
+
+#if HAVE_GEOCLUE
+  gchar *str;
+  GHashTable *address;
+  GValue *value;
+
+  value = g_hash_table_lookup (location, EMPATHY_LOCATION_LON);
+  if (value == NULL)
+      {
+        if (window->geocode == NULL)
+          window->geocode = geoclue_geocode_new (GEOCODE_SERVICE, GEOCODE_PATH);
+
+        address = geoclue_address_details_new();
+
+        str = get_dup_string (location, EMPATHY_LOCATION_COUNTRY);
+        if (str != NULL)
+          g_hash_table_insert (address, g_strdup ("country"), str);
+
+        str = get_dup_string (location, EMPATHY_LOCATION_POSTAL_CODE);
+        if (str != NULL)
+          g_hash_table_insert (address, g_strdup ("postalcode"), str);
+
+        str = get_dup_string (location, EMPATHY_LOCATION_LOCALITY);
+        if (str != NULL)
+          g_hash_table_insert (address, g_strdup ("locality"), str);
+
+        str = get_dup_string (location, EMPATHY_LOCATION_STREET);
+        if (str != NULL)
+          g_hash_table_insert (address, g_strdup ("street"), str);
+
+        g_object_set_data (G_OBJECT (contact), "geoclue-address", address);
+
+        geoclue_geocode_address_to_position_async (window->geocode, address,
+            map_view_geocode_cb, contact);
+      }
+#endif
+
+  map_view_marker_update_position (CHAMPLAIN_MARKER (marker), contact);
+
+  g_object_unref (contact);
+  return FALSE;
+}
+
+static void
+map_view_zoom_in_cb (GtkWidget *widget,
+    EmpathyMapView *window)
+{
+  champlain_view_zoom_in (window->map_view);
+}
+
+static void
+map_view_zoom_out_cb (GtkWidget *widget,
+    EmpathyMapView *window)
+{
+  champlain_view_zoom_out (window->map_view);
+}
diff --git a/src/empathy-map-view.h b/src/empathy-map-view.h
new file mode 100644
index 0000000..80a05a1
--- /dev/null
+++ b/src/empathy-map-view.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 Collabora Ltd.
+ *
+ * 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
+ *
+ * Authors: Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
+ */
+
+#ifndef __EMPATHY_MAP_VIEW_H__
+#define __EMPATHY_MAP_VIEW_H__
+
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+GtkWidget *empathy_map_view_show (void);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_MAP_VIEW_H__ */
diff --git a/src/empathy-map-view.ui b/src/empathy-map-view.ui
new file mode 100644
index 0000000..f114039
--- /dev/null
+++ b/src/empathy-map-view.ui
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy toplevel-contextual -->
+  <object class="GtkWindow" id="map_view">
+    <property name="title" translatable="yes">Contact Map View</property>
+    <property name="role">map_view</property>
+    <property name="window_position">center</property>
+    <property name="default_width">512</property>
+    <property name="default_height">384</property>
+    <child>
+      <object class="GtkVBox" id="main_vbox">
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkToolbar" id="toolbar">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkToolButton" id="zoom_in">
+                <property name="visible">True</property>
+                <property name="stock_id">gtk-zoom-in</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolButton" id="zoom_out">
+                <property name="visible">True</property>
+                <property name="stock_id">gtk-zoom-out</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkViewport" id="map_scrolledwindow">
+            <property name="visible">True</property>
+            <property name="resize_mode">queue</property>
+            <child>
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/src/empathy.c b/src/empathy.c
index fe122e0..91d77a5 100644
--- a/src/empathy.c
+++ b/src/empathy.c
@@ -31,6 +31,10 @@
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
 
+#if HAVE_LIBCHAMPLAIN
+#include <clutter-gtk/gtk-clutter-embed.h>
+#endif
+
 #include <libebook/e-book.h>
 #include <libnotify/notify.h>
 
@@ -466,6 +470,10 @@ main (int argc, char *argv[])
 
 	gst_init (&argc, &argv);
 
+#if HAVE_LIBCHAMPLAIN
+	gtk_clutter_init (&argc, &argv);
+#endif
+
 	gtk_window_set_default_icon_name ("empathy");
 	textdomain (GETTEXT_PACKAGE);
 
