summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMarcus Lundblad <ml@update.uu.se>2015-10-19 21:38:57 +0200
committerJonas Danielsson <jonas@threetimestwo.org>2015-12-12 17:07:08 +0100
commit60471b8eccc501019714c4020a59adf247469b21 (patch)
tree5474369634038cb5d4f4c7283852e53237f8ac85 /lib
parente25c9940773f8ab10ac66012e8d59ef24e45d8d7 (diff)
downloadgnome-maps-60471b8eccc501019714c4020a59adf247469b21.tar.gz
osmEdit: Add OSM shim library
The following GObject classes are defined: MapsOSMObject: An abstract base class representing objects in the OpenStreetMap database. Has a function to serialize objects to their XML representation and abstract functions that implementation classes define to add object-specific XML tags. MapsOSMNode: Represents an object of type ”node” in OSM. Inherits from MapsOSMObject. MapsOSMWay: Represents an object of type ”way” in OSM. Inherits from MapsOSMObject. MapsOSMRelation: Represents an object of type ”relation” in OSM. Inherits from MapsOSMObject. MapsOSMChangeset: Represents a changeset in OSM. Has a function to serialize the changeset (with comment and client identifier) to the XML representation. maps-osm.[c|h]: Contains parsing functions to read OSM objects from the raw XML input as downloaded from the OSM database. Also contains utility functions to initialize and destruct the libxml2 parser. https://bugzilla.gnome.org/show_bug.cgi?id=726628
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am16
-rw-r--r--lib/maps-osm-changeset.c190
-rw-r--r--lib/maps-osm-changeset.h53
-rw-r--r--lib/maps-osm-node.c169
-rw-r--r--lib/maps-osm-node.h46
-rw-r--r--lib/maps-osm-object.c331
-rw-r--r--lib/maps-osm-object.h56
-rw-r--r--lib/maps-osm-relation.c156
-rw-r--r--lib/maps-osm-relation.h57
-rw-r--r--lib/maps-osm-way.c116
-rw-r--r--lib/maps-osm-way.h48
-rw-r--r--lib/maps-osm.c473
-rw-r--r--lib/maps-osm.h35
13 files changed, 1744 insertions, 2 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index cc4252c0..74818e2d 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -9,12 +9,24 @@ libgnome_maps_headers_private = \
maps-contact.h \
mapsintl.h \
maps.h \
- maps-file-tile-source.h
+ maps-file-tile-source.h \
+ maps-osm.h \
+ maps-osm-changeset.h \
+ maps-osm-node.h \
+ maps-osm-object.h \
+ maps-osm-relation.h \
+ maps-osm-way.h
libgnome_maps_sources = \
maps-contact-store.c \
maps-contact.c \
- maps-file-tile-source.c
+ maps-file-tile-source.c \
+ maps-osm.c \
+ maps-osm-changeset.c \
+ maps-osm-node.c \
+ maps-osm-object.c \
+ maps-osm-way.c \
+ maps-osm-relation.c
libgnome_maps_la_SOURCES = \
$(libgnome_maps_sources) \
diff --git a/lib/maps-osm-changeset.c b/lib/maps-osm-changeset.c
new file mode 100644
index 00000000..00edd415
--- /dev/null
+++ b/lib/maps-osm-changeset.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2015 Marcus Lundblad
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Author: Marcus Lundblad <ml@update.uu.se>
+ */
+
+#include "maps-osm-changeset.h"
+
+#include <libxml/xpath.h>
+
+struct _MapsOSMChangesetPrivate
+{
+ char *comment;
+ char *created_by;
+};
+
+enum {
+ PROP_0,
+
+ PROP_COMMENT,
+ PROP_CREATED_BY
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (MapsOSMChangeset, maps_osm_changeset, G_TYPE_OBJECT)
+
+static void
+maps_osm_changeset_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MapsOSMChangeset *changeset = MAPS_OSMCHANGESET (object);
+
+ switch (property_id)
+ {
+ case PROP_COMMENT:
+ changeset->priv->comment = g_value_dup_string (value);
+ break;
+
+ case PROP_CREATED_BY:
+ changeset->priv->created_by = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+maps_osm_changeset_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MapsOSMChangeset *changeset = MAPS_OSMCHANGESET (object);
+
+ switch (property_id)
+ {
+ case PROP_COMMENT:
+ g_value_set_string (value, changeset->priv->comment);
+ break;
+
+ case PROP_CREATED_BY:
+ g_value_set_string (value, changeset->priv->created_by);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+maps_osm_changeset_dispose (GObject *object)
+{
+ MapsOSMChangeset *changeset = MAPS_OSMCHANGESET (object);
+
+ g_free (changeset->priv->comment);
+ g_free (changeset->priv->created_by);
+
+ G_OBJECT_CLASS (maps_osm_changeset_parent_class)->dispose (object);
+}
+
+static void
+maps_osm_changeset_class_init (MapsOSMChangesetClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GParamSpec *pspec;
+
+ object_class->dispose = maps_osm_changeset_dispose;
+ object_class->get_property = maps_osm_changeset_get_property;
+ object_class->set_property = maps_osm_changeset_set_property;
+
+ /**
+ * MapsOSMChangeset:comment:
+ *
+ * The comment of the changes.
+ */
+ pspec = g_param_spec_string ("comment",
+ "Comment",
+ "Comment",
+ NULL,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_COMMENT, pspec);
+
+ /**
+ * MapsOSMChangeset:created_by:
+ *
+ * The identifier of the client making the changeset.
+ */
+ pspec = g_param_spec_string ("created_by",
+ "Created by",
+ "Created by",
+ NULL,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_CREATED_BY, pspec);
+}
+
+static void
+maps_osm_changeset_init (MapsOSMChangeset *changeset)
+{
+ changeset->priv = maps_osm_changeset_get_instance_private (changeset);
+
+ changeset->priv->comment = NULL;
+ changeset->priv->created_by = NULL;
+}
+
+MapsOSMChangeset *
+maps_osm_changeset_new (const char *comment, const char *created_by)
+{
+ return g_object_new (MAPS_TYPE_OSMCHANGESET,
+ "comment", comment,
+ "created_by", created_by, NULL);
+}
+
+xmlNodePtr
+maps_osm_changeset_create_tag_node (const char *key, const char * value)
+{
+ xmlNodePtr node;
+
+ node = xmlNewNode (NULL, "tag");
+ xmlNewProp (node, "k", key);
+ xmlNewProp (node, "v", value);
+
+ return node;
+}
+
+char *
+maps_osm_changeset_serialize (const MapsOSMChangeset *changeset)
+{
+ xmlDocPtr doc;
+ xmlNodePtr osm_node;
+ xmlNodePtr changeset_node;
+ xmlNodePtr comment_node;
+ xmlNodePtr created_by_node;
+ xmlChar *result;
+ int size;
+
+ doc = xmlNewDoc ("1.0");
+ osm_node = xmlNewNode (NULL, "osm");
+ changeset_node = xmlNewNode (NULL, "changeset");
+ comment_node =
+ maps_osm_changeset_create_tag_node ("comment", changeset->priv->comment);
+ created_by_node =
+ maps_osm_changeset_create_tag_node ("created_by",
+ changeset->priv->created_by);
+ xmlAddChild (osm_node, changeset_node);
+ xmlAddChild (changeset_node, comment_node);
+ xmlAddChild (changeset_node, created_by_node);
+ xmlDocSetRootElement (doc, osm_node);
+
+ xmlDocDumpMemory (doc, &result, &size);
+ xmlFreeDoc (doc);
+
+ return result;
+}
diff --git a/lib/maps-osm-changeset.h b/lib/maps-osm-changeset.h
new file mode 100644
index 00000000..01963366
--- /dev/null
+++ b/lib/maps-osm-changeset.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015 Marcus Lundblad
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Author: Marcus Lundblad <ml@update.uu.se>
+ */
+
+#ifndef __MAPS_OSM_CHANGESET_H__
+#define __MAPS_OSM_CHANGESET_H__
+
+#include <glib-object.h>
+
+#define MAPS_TYPE_OSMCHANGESET maps_osm_changeset_get_type ()
+G_DECLARE_FINAL_TYPE(MapsOSMChangeset, maps_osm_changeset, MAPS, OSMCHANGESET,
+ GObject)
+
+typedef struct _MapsOSMChangesetPrivate MapsOSMChangesetPrivate;
+
+struct _MapsOSMChangeset
+{
+ GObject parent_instance;
+ MapsOSMChangesetPrivate *priv;
+};
+
+struct _MapsOSMChangesetClass
+{
+ GObjectClass parent_class;
+};
+
+/**
+ * maps_osm_changeset_new:
+ * @comment: (nullable): A comment about the OSM change, optional
+ * @created_by: (nullable): The client identifier of the OSM change, optional
+ */
+MapsOSMChangeset *maps_osm_changeset_new (const char *comment,
+ const char *created_by);
+
+char *maps_osm_changeset_serialize (const MapsOSMChangeset *changeset);
+
+#endif /* __MAPS_OSM_CHANGESET_H__ */
+
diff --git a/lib/maps-osm-node.c b/lib/maps-osm-node.c
new file mode 100644
index 00000000..e0562530
--- /dev/null
+++ b/lib/maps-osm-node.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2015 Marcus Lundblad
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Author: Marcus Lundblad <ml@update.uu.se>
+ */
+
+#include "maps-osm-node.h"
+
+struct _MapsOSMNodePrivate
+{
+ double lon;
+ double lat;
+};
+
+enum {
+ PROP_0,
+
+ PROP_LONGITUDE,
+ PROP_LATITUDE
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (MapsOSMNode, maps_osm_node, MAPS_TYPE_OSMOBJECT)
+
+
+static void
+maps_osm_node_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MapsOSMNode *node = MAPS_OSMNODE (object);
+
+ switch (property_id)
+ {
+ case PROP_LONGITUDE:
+ node->priv->lon = g_value_get_double (value);
+ break;
+
+ case PROP_LATITUDE:
+ node->priv->lat = g_value_get_double (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+maps_osm_node_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MapsOSMNode *node = MAPS_OSMNODE (object);
+
+ switch (property_id)
+ {
+ case PROP_LONGITUDE:
+ g_value_set_double (value, node->priv->lon);
+ break;
+
+ case PROP_LATITUDE:
+ g_value_set_double (value, node->priv->lat);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static const char *
+maps_osm_node_get_xml_tag_name (void)
+{
+ return "node";
+}
+
+static GHashTable *
+maps_osm_node_get_xml_attributes (const MapsOSMObject *object)
+{
+ const MapsOSMNode *node = MAPS_OSMNODE (object);
+ GHashTable *attributes;
+ char buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+ attributes = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
+
+ g_ascii_dtostr (buf, sizeof (buf), node->priv->lon);
+ g_hash_table_insert (attributes, "lon", g_strdup (buf));
+ g_ascii_dtostr (buf, sizeof (buf), node->priv->lat);
+ g_hash_table_insert (attributes, "lat", g_strdup (buf));
+
+ return attributes;
+}
+
+static void
+maps_osm_node_class_init (MapsOSMNodeClass *klass)
+{
+ GObjectClass *node_class = G_OBJECT_CLASS (klass);
+ MapsOSMObjectClass *object_class = MAPS_OSMOBJECT_CLASS (klass);
+ GParamSpec *pspec;
+
+ node_class->get_property = maps_osm_node_get_property;
+ node_class->set_property = maps_osm_node_set_property;
+ object_class->get_xml_tag_name = maps_osm_node_get_xml_tag_name;
+ object_class->get_xml_attributes = maps_osm_node_get_xml_attributes;
+
+ /**
+ * MapsOSMNode:longitude:
+ *
+ * The longitude of the node.
+ */
+ pspec = g_param_spec_double ("longitude",
+ "Longitude",
+ "Longitude",
+ -180.0,
+ 180.0,
+ 0.0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (node_class, PROP_LONGITUDE, pspec);
+
+ /**
+ * MapsOSMNode:latitude:
+ *
+ * The latitude of the node.
+ */
+ pspec = g_param_spec_double ("latitude",
+ "Latitude",
+ "Latitude",
+ -90.0,
+ 90.0,
+ 0.0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (node_class, PROP_LATITUDE, pspec);
+}
+
+static void
+maps_osm_node_init (MapsOSMNode *node)
+{
+ node->priv = maps_osm_node_get_instance_private (node);
+
+ node->priv->lon = 0.0;
+ node->priv->lat = 0.0;
+}
+
+MapsOSMNode *
+maps_osm_node_new (guint64 id, guint version, guint64 changeset,
+ double longitude, double latitude)
+{
+ return g_object_new (MAPS_TYPE_OSMNODE,
+ "id", id,
+ "version", version,
+ "changeset", changeset,
+ "longitude", longitude,
+ "latitude", latitude, NULL);
+}
diff --git a/lib/maps-osm-node.h b/lib/maps-osm-node.h
new file mode 100644
index 00000000..ba12d3bc
--- /dev/null
+++ b/lib/maps-osm-node.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 Marcus Lundblad
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Author: Marcus Lundblad <ml@update.uu.se>
+ */
+
+#ifndef __MAPS_OSM_NODE_H__
+#define __MAPS_OSM_NODE_H__
+
+#include "maps-osm-object.h"
+
+#include <glib-object.h>
+
+#define MAPS_TYPE_OSMNODE maps_osm_node_get_type ()
+G_DECLARE_FINAL_TYPE(MapsOSMNode, maps_osm_node, MAPS, OSMNODE, MapsOSMObject)
+
+typedef struct _MapsOSMNodePrivate MapsOSMNodePrivate;
+
+struct _MapsOSMNode
+{
+ MapsOSMObject parent_instance;
+ MapsOSMNodePrivate *priv;
+};
+
+struct _MapsOSMNodeClass
+{
+ MapsOSMObjectClass parent_class;
+};
+
+MapsOSMNode *maps_osm_node_new (guint64 id, guint version, guint64 changeset,
+ double longitude, double latitude);
+
+#endif //__MAPS_OSM_NODE_H__
diff --git a/lib/maps-osm-object.c b/lib/maps-osm-object.c
new file mode 100644
index 00000000..270e5931
--- /dev/null
+++ b/lib/maps-osm-object.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2015 Marcus Lundblad
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Author: Marcus Lundblad <ml@update.uu.se>
+ */
+
+#include "maps-osm-object.h"
+
+struct _MapsOSMObjectPrivate
+{
+ guint64 id;
+ guint version;
+ guint64 changeset;
+
+ GHashTable *tags;
+};
+
+enum {
+ PROP_0,
+
+ PROP_ID,
+ PROP_VERSION,
+ PROP_CHANGESET
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MapsOSMObject, maps_osm_object,
+ G_TYPE_OBJECT)
+
+static void
+maps_osm_object_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MapsOSMObject *osm_object = MAPS_OSMOBJECT (object);
+ MapsOSMObjectPrivate *priv =
+ maps_osm_object_get_instance_private (osm_object);
+
+ switch (property_id)
+ {
+ case PROP_ID:
+ priv->id = g_value_get_uint64 (value);
+ break;
+
+ case PROP_VERSION:
+ priv->version = g_value_get_uint (value);
+ break;
+
+ case PROP_CHANGESET:
+ priv->changeset = g_value_get_uint64 (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+maps_osm_object_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MapsOSMObject *osm_object = MAPS_OSMOBJECT (object);
+ MapsOSMObjectPrivate *priv =
+ maps_osm_object_get_instance_private (osm_object);
+
+ switch (property_id)
+ {
+ case PROP_ID:
+ g_value_set_uint64 (value, priv->id);
+ break;
+
+ case PROP_VERSION:
+ g_value_set_uint (value, priv->version);
+ break;
+
+ case PROP_CHANGESET:
+ g_value_set_uint64 (value, priv->changeset);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+maps_osm_object_dispose (GObject *object)
+{
+ MapsOSMObject *osm_object = MAPS_OSMOBJECT (object);
+ MapsOSMObjectPrivate *priv =
+ maps_osm_object_get_instance_private (osm_object);
+
+ g_hash_table_destroy (priv->tags);
+ priv->tags = NULL;
+
+ G_OBJECT_CLASS (maps_osm_object_parent_class)->dispose (object);
+}
+
+/* base implementation returning no object-specific XML attributes */
+static GHashTable *
+maps_osm_object_get_xml_attributes (const MapsOSMObject *object)
+{
+ return NULL;
+}
+
+/* base implementation return no object-specific child XML nodes */
+static xmlNodePtr
+maps_osm_object_get_xml_child_nodes (const MapsOSMObject *object)
+{
+ return NULL;
+}
+
+static void
+maps_osm_object_class_init (MapsOSMObjectClass *klass)
+{
+ GObjectClass *maps_class = G_OBJECT_CLASS (klass);
+ MapsOSMObjectClass *object_class = MAPS_OSMOBJECT_CLASS (klass);
+ GParamSpec *pspec;
+
+ maps_class->dispose = maps_osm_object_dispose;
+ maps_class->get_property = maps_osm_object_get_property;
+ maps_class->set_property = maps_osm_object_set_property;
+ object_class->get_xml_attributes = maps_osm_object_get_xml_attributes;
+ object_class->get_xml_child_nodes = maps_osm_object_get_xml_child_nodes;
+
+ /**
+ * MapsOSMObject:id:
+ *
+ * The OSM id of the object.
+ */
+ pspec = g_param_spec_uint64 ("id",
+ "ID",
+ "ID",
+ 0,
+ G_MAXUINT64,
+ 0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (maps_class, PROP_ID, pspec);
+
+ /**
+ * MapsOSMObject:version:
+ *
+ * The latest OSM version of the object.
+ */
+ pspec = g_param_spec_uint("version",
+ "Version",
+ "Version",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (maps_class, PROP_VERSION, pspec);
+
+ /**
+ * MapsOSMObject:changeset:
+ *
+ * The OSM changeset for the current upload of the object.
+ */
+ pspec = g_param_spec_uint64 ("changeset",
+ "Changeset",
+ "Changeset",
+ 0,
+ G_MAXUINT64,
+ 0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (maps_class, PROP_CHANGESET, pspec);
+}
+
+static void
+maps_osm_object_init (MapsOSMObject *object)
+{
+ MapsOSMObjectPrivate *priv = maps_osm_object_get_instance_private (object);
+
+ priv->tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+}
+
+const char *
+maps_osm_object_get_tag (const MapsOSMObject *object, const char *key)
+{
+ MapsOSMObjectPrivate *priv = maps_osm_object_get_instance_private (object);
+
+ g_return_val_if_fail (key != NULL, NULL);
+
+ return g_hash_table_lookup (priv->tags, key);
+}
+
+void
+maps_osm_object_set_tag (MapsOSMObject *object, const char *key,
+ const char *value)
+{
+ MapsOSMObjectPrivate *priv = maps_osm_object_get_instance_private (object);
+
+ g_return_if_fail (key != NULL);
+
+ g_hash_table_insert (priv->tags, g_strdup (key), g_strdup (value));
+}
+
+void
+maps_osm_object_delete_tag (MapsOSMObject *object, const char *key)
+{
+ MapsOSMObjectPrivate *priv = maps_osm_object_get_instance_private (object);
+
+ g_return_if_fail (key != NULL);
+
+ g_hash_table_remove (priv->tags, key);
+}
+
+static void
+maps_osm_object_foreach_tag (gpointer key, gpointer value, gpointer user_data)
+{
+ const char *name = (const char *) key;
+ const char *val = (const char *) value;
+ xmlNodePtr object_node = (xmlNodePtr) user_data;
+
+ /* skip tag if it has an empty placeholder value */
+ if (val)
+ {
+ xmlNodePtr tag_node;
+
+ tag_node = xmlNewNode (NULL, "tag");
+ xmlNewProp (tag_node, "k", key);
+ xmlNewProp (tag_node, "v", val);
+ xmlAddChild (object_node, tag_node);
+ }
+}
+
+static void
+maps_osm_object_foreach_type_attr (gpointer key, gpointer value,
+ gpointer user_data)
+{
+ const char *name = (const char *) key;
+ const char *val = (const char *) value;
+ xmlNodePtr object_node = (xmlNodePtr) user_data;
+
+ xmlNewProp (object_node, name, val);
+}
+
+xmlDocPtr
+maps_osm_object_to_xml (const MapsOSMObject *object)
+{
+ MapsOSMObjectPrivate *priv;
+ xmlDocPtr doc;
+ xmlNodePtr osm_node;
+ xmlNodePtr object_node;
+ const char *type;
+ GHashTable *type_attrs;
+ xmlNodePtr type_sub_nodes;
+
+ doc = xmlNewDoc ("1.0");
+ osm_node = xmlNewNode (NULL, "osm");
+ priv = (MapsOSMObjectPrivate *) maps_osm_object_get_instance_private (object);
+ type = MAPS_OSMOBJECT_GET_CLASS (object)->get_xml_tag_name ();
+ object_node = xmlNewNode (NULL, type);
+
+ /* add common OSM attributes */
+ if (priv->id != 0)
+ {
+ char buf[32];
+ g_snprintf (buf, 32, "%" G_GUINT64_FORMAT, priv->id);
+ xmlNewProp (object_node, "id", buf);
+ }
+
+ if (priv->version != 0)
+ {
+ char buf[16];
+ g_snprintf (buf, 16, "%d", priv->version);
+ xmlNewProp (object_node, "version", buf);
+ }
+
+ if (priv->changeset != 0)
+ {
+ char buf[32];
+ g_snprintf (buf, 32, "%" G_GUINT64_FORMAT, priv->changeset);
+ xmlNewProp (object_node, "changeset", buf);
+ }
+
+ /* add OSM tags */
+ g_hash_table_foreach (priv->tags, maps_osm_object_foreach_tag, object_node);
+
+ /* add type-specific attributes */
+ type_attrs = MAPS_OSMOBJECT_GET_CLASS (object)->get_xml_attributes (object);
+ if (type_attrs)
+ {
+ g_hash_table_foreach (type_attrs, maps_osm_object_foreach_type_attr,
+ object_node);
+ g_hash_table_destroy (type_attrs);
+ }
+
+ /* add type-specific sub-nodes */
+ type_sub_nodes =
+ MAPS_OSMOBJECT_GET_CLASS (object)->get_xml_child_nodes (object);
+ if (type_sub_nodes)
+ xmlAddChildList (object_node, type_sub_nodes);
+
+ /* add type node to top node */
+ xmlAddChild (osm_node, object_node);
+ xmlDocSetRootElement (doc, osm_node);
+
+ return doc;
+}
+
+
+char *
+maps_osm_object_serialize (const MapsOSMObject *object)
+{
+ xmlDocPtr doc;
+ xmlChar *result;
+ int size;
+
+ doc = maps_osm_object_to_xml (object);
+ xmlDocDumpMemory (doc, &result, &size);
+ xmlFreeDoc (doc);
+
+ return result;
+}
diff --git a/lib/maps-osm-object.h b/lib/maps-osm-object.h
new file mode 100644
index 00000000..6e8f2e3b
--- /dev/null
+++ b/lib/maps-osm-object.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015 Marcus Lundblad
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Author: Marcus Lundblad <ml@update.uu.se>
+ */
+
+#ifndef __MAPS_OSM_OBJECT_H__
+#define __MAPS_OSM_OBJECT_H__
+
+#include <glib-object.h>
+#include <libxml/xpath.h>
+
+#define MAPS_TYPE_OSMOBJECT maps_osm_object_get_type ()
+G_DECLARE_DERIVABLE_TYPE(MapsOSMObject, maps_osm_object, MAPS, OSMOBJECT,
+ GObject)
+
+typedef struct _MapsOSMObjectPrivate MapsOSMObjectPrivate;
+
+struct _MapsOSMObjectClass
+{
+ GObjectClass parent_class;
+
+ /* return the name of the distinguishing OSM XML tag (beneath <osm/>) */
+ const char * (* get_xml_tag_name) (void);
+
+ /* return hash table with XML attributes (key/values) specific for
+ the object (on the XML tag beneath <osm/>) */
+ GHashTable * (* get_xml_attributes) (const MapsOSMObject *object);
+
+ /* return a list of custom object-specific XML tags to attach,
+ can return NULL if there's no object-specific nodes */
+ xmlNodePtr (* get_xml_child_nodes) (const MapsOSMObject *object);
+};
+
+const char *maps_osm_object_get_tag (const MapsOSMObject *object,
+ const char *key);
+void maps_osm_object_set_tag (MapsOSMObject *object, const char *key,
+ const char *value);
+void maps_osm_object_delete_tag (MapsOSMObject *object, const char *key);
+
+char *maps_osm_object_serialize (const MapsOSMObject *object);
+
+#endif //__MAPS_OSM_OBJECT_H__
diff --git a/lib/maps-osm-relation.c b/lib/maps-osm-relation.c
new file mode 100644
index 00000000..bb3f424b
--- /dev/null
+++ b/lib/maps-osm-relation.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2015 Marcus Lundblad
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Author: Marcus Lundblad <ml@update.uu.se>
+ */
+
+#include "maps-osm-relation.h"
+
+struct _MapsOSMRelationPrivate
+{
+ GList *members;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (MapsOSMRelation, maps_osm_relation,
+ MAPS_TYPE_OSMOBJECT);
+
+typedef struct
+{
+ char *role;
+ guint type;
+ guint64 ref;
+} MapsOSMRelationMember;
+
+static void
+maps_osm_relation_member_free (gpointer data)
+{
+ MapsOSMRelationMember *member = (MapsOSMRelationMember *) data;
+
+ g_free (member->role);
+}
+
+static void
+maps_osm_relation_dispose (GObject *object)
+{
+ MapsOSMRelation *relation = MAPS_OSMRELATION (object);
+
+ g_list_free_full (relation->priv->members, maps_osm_relation_member_free);
+ relation->priv->members = NULL;
+
+ G_OBJECT_CLASS (maps_osm_relation_parent_class)->dispose (object);
+}
+
+static const char *
+maps_osm_relation_get_xml_tag_name (void)
+{
+ return "relation";
+}
+
+static const char *
+maps_osm_relation_member_type_to_string (guint type)
+{
+ switch (type) {
+ case MEMBER_TYPE_NODE:
+ return "node";
+ case MEMBER_TYPE_WAY:
+ return "way";
+ case MEMBER_TYPE_RELATION:
+ return "relation";
+ default:
+ g_warning ("Unknown relation member type: %d\n", type);
+ return NULL;
+ }
+}
+
+static xmlNodePtr
+maps_osm_relation_get_member_node (const MapsOSMRelationMember *member)
+{
+ xmlNodePtr node = xmlNewNode (NULL, "member");
+ char buf[16];
+
+ if (member->role)
+ xmlNewProp (node, "role", g_strdup (member->role));
+
+ xmlNewProp (node, "type",
+ maps_osm_relation_member_type_to_string (member->type));
+ g_snprintf (buf, 16, "%" G_GUINT64_FORMAT, member->ref);
+ xmlNewProp (node, "ref", buf);
+
+ return node;
+}
+
+static xmlNodePtr
+maps_osm_relation_get_xml_child_nodes (const MapsOSMObject *object)
+{
+ MapsOSMRelation *relation = MAPS_OSMRELATION (object);
+ xmlNodePtr nodes = NULL;
+ const GList *members = relation->priv->members;
+
+ if (members)
+ {
+ const GList *iter;
+ nodes = maps_osm_relation_get_member_node ((MapsOSMRelationMember *)
+ members->data);
+
+ for (iter = members->next; iter; iter = iter->next)
+ {
+ xmlAddSibling (nodes, maps_osm_relation_get_member_node (
+ (MapsOSMRelationMember *) iter->data));
+ }
+ }
+
+ return nodes;
+}
+
+static void
+maps_osm_relation_class_init (MapsOSMRelationClass *klass)
+{
+ GObjectClass *relation_class = G_OBJECT_CLASS (klass);
+ MapsOSMObjectClass *object_class = MAPS_OSMOBJECT_CLASS (klass);
+
+ relation_class->dispose = maps_osm_relation_dispose;
+ object_class->get_xml_tag_name = maps_osm_relation_get_xml_tag_name;
+ object_class->get_xml_child_nodes = maps_osm_relation_get_xml_child_nodes;
+}
+
+static void
+maps_osm_relation_init (MapsOSMRelation *relation)
+{
+ relation->priv = maps_osm_relation_get_instance_private (relation);
+}
+
+MapsOSMRelation *
+maps_osm_relation_new (guint64 id, guint version, guint64 changeset)
+{
+ return g_object_new (MAPS_TYPE_OSMRELATION,
+ "id", id,
+ "version", version,
+ "changeset", changeset, NULL);
+}
+
+void
+maps_osm_relation_add_member (MapsOSMRelation *relation, const gchar *role,
+ guint type, guint64 ref)
+{
+ MapsOSMRelationMember *member = g_new (MapsOSMRelationMember, 1);
+
+ member->role = g_strdup (role);
+ member->type = type;
+ member->ref = ref;
+
+ relation->priv->members = g_list_append (relation->priv->members, member);
+}
+
diff --git a/lib/maps-osm-relation.h b/lib/maps-osm-relation.h
new file mode 100644
index 00000000..ffcae785
--- /dev/null
+++ b/lib/maps-osm-relation.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015 Marcus Lundblad
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Author: Marcus Lundblad <ml@update.uu.se>
+ */
+
+#ifndef __MAPS_OSM_RELATION_H__
+#define __MAPS_OSM_RELATION_H__
+
+#include "maps-osm-object.h"
+
+#include <glib-object.h>
+
+#define MAPS_TYPE_OSMRELATION maps_osm_relation_get_type ()
+G_DECLARE_FINAL_TYPE(MapsOSMRelation, maps_osm_relation, MAPS, OSMRELATION,
+ MapsOSMObject)
+
+typedef struct _MapsOSMRelationPrivate MapsOSMRelationPrivate;
+
+struct _MapsOSMRelation
+{
+ MapsOSMObject parent_instance;
+ MapsOSMRelationPrivate *priv;
+};
+
+struct _MapsOSMRelationClass
+{
+ MapsOSMObjectClass parent_class;
+};
+
+enum {
+ MEMBER_TYPE_NODE,
+ MEMBER_TYPE_WAY,
+ MEMBER_TYPE_RELATION
+};
+
+MapsOSMRelation *maps_osm_relation_new (guint64 id, guint version,
+ guint64 changeset);
+
+void maps_osm_relation_add_member (MapsOSMRelation *relation, const char *role,
+ guint type, guint64 ref);
+
+#endif /* __MAPS_OSM_RELATION_H__ */
+
diff --git a/lib/maps-osm-way.c b/lib/maps-osm-way.c
new file mode 100644
index 00000000..71b35e26
--- /dev/null
+++ b/lib/maps-osm-way.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015 Marcus Lundblad
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Author: Marcus Lundblad <ml@update.uu.se>
+ */
+
+#include "maps-osm-way.h"
+
+struct _MapsOSMWayPrivate
+{
+ GArray *node_ids;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (MapsOSMWay, maps_osm_way, MAPS_TYPE_OSMOBJECT);
+
+static void
+maps_osm_way_dispose (GObject *object)
+{
+ MapsOSMWay *way = MAPS_OSMWAY (object);
+
+ g_array_free (way->priv->node_ids, TRUE);
+ way->priv->node_ids = NULL;
+
+ G_OBJECT_CLASS (maps_osm_way_parent_class)->dispose (object);
+}
+
+static const char *
+maps_osm_way_get_xml_tag_name (void)
+{
+ return "way";
+}
+
+static xmlNodePtr
+maps_osm_way_create_node_xml_node (guint64 ref)
+{
+ xmlNodePtr nd;
+ char buf[16];
+
+ g_snprintf (buf, 16, "%" G_GUINT64_FORMAT, ref);
+ nd = xmlNewNode (NULL, "nd");
+ xmlNewProp (nd, "ref", buf);
+
+ return nd;
+}
+
+static xmlNodePtr
+maps_osm_way_get_xml_child_nodes(const MapsOSMObject *object)
+{
+ const MapsOSMWay *way = MAPS_OSMWAY (object);
+ int i;
+ xmlNodePtr result;
+ xmlNodePtr next;
+
+ g_return_val_if_fail (way->priv->node_ids->len > 0, NULL);
+
+ result = maps_osm_way_create_node_xml_node (g_array_index (way->priv->node_ids,
+ guint64, 0));
+ next = result;
+
+ for (i = 1; i < way->priv->node_ids->len; i++)
+ {
+ xmlNodePtr new_node;
+ new_node =
+ maps_osm_way_create_node_xml_node (g_array_index (way->priv->node_ids,
+ guint64, i));
+ next = xmlAddNextSibling (next, new_node);
+ }
+
+ return result;
+}
+
+static void
+maps_osm_way_class_init (MapsOSMWayClass *klass)
+{
+ GObjectClass *way_class = G_OBJECT_CLASS (klass);
+ MapsOSMObjectClass *object_class = MAPS_OSMOBJECT_CLASS (klass);
+
+ way_class->dispose = maps_osm_way_dispose;
+ object_class->get_xml_tag_name = maps_osm_way_get_xml_tag_name;
+ object_class->get_xml_child_nodes = maps_osm_way_get_xml_child_nodes;
+}
+
+static void
+maps_osm_way_init (MapsOSMWay *way)
+{
+ way->priv = maps_osm_way_get_instance_private (way);
+ way->priv->node_ids = g_array_new (FALSE, FALSE, sizeof (guint64));
+}
+
+MapsOSMWay *
+maps_osm_way_new (guint64 id, guint version, guint64 changeset)
+{
+ return g_object_new (MAPS_TYPE_OSMWAY,
+ "id", id,
+ "version", version,
+ "changeset", changeset, NULL);
+}
+
+void
+maps_osm_way_add_node_id (MapsOSMWay *way, guint64 id)
+{
+ g_array_append_val (way->priv->node_ids, id);
+}
diff --git a/lib/maps-osm-way.h b/lib/maps-osm-way.h
new file mode 100644
index 00000000..0dee1eec
--- /dev/null
+++ b/lib/maps-osm-way.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015 Marcus Lundblad
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Author: Marcus Lundblad <ml@update.uu.se>
+ */
+
+#ifndef __MAPS_OSM_WAY_H__
+#define __MAPS_OSM_WAY_H__
+
+#include "maps-osm-object.h"
+
+#include <glib-object.h>
+
+#define MAPS_TYPE_OSMWAY maps_osm_way_get_type ()
+G_DECLARE_FINAL_TYPE(MapsOSMWay, maps_osm_way, MAPS, OSMWAY, MapsOSMObject)
+
+typedef struct _MapsOSMWayPrivate MapsOSMWayPrivate;
+
+struct _MapsOSMWay
+{
+ MapsOSMObject parent_instance;
+ MapsOSMWayPrivate *priv;
+};
+
+struct _MapsOSMWayClass
+{
+ MapsOSMObjectClass parent_class;
+};
+
+MapsOSMWay *maps_osm_way_new (guint64 id, guint version, guint64 changeset);
+
+void maps_osm_way_add_node_id (MapsOSMWay *way, guint64 id);
+
+#endif /* __MAPS_OSM_WAY_H__ */
+
diff --git a/lib/maps-osm.c b/lib/maps-osm.c
new file mode 100644
index 00000000..ec13320e
--- /dev/null
+++ b/lib/maps-osm.c
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2015 Marcus Lundblad
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Author: Marcus Lundblad <ml@update.uu.se>
+ */
+
+#include "maps-osm.h"
+#include "mapsintl.h"
+
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+
+#define MAPS_OSM_ERROR maps_osm_error_quark ()
+
+GQuark
+maps_osm_error_quark (void)
+{
+ return g_quark_from_static_string ("maps-osm-error");
+}
+
+void
+maps_osm_init (void)
+{
+ LIBXML_TEST_VERSION;
+}
+
+void
+maps_osm_finalize (void)
+{
+ xmlCleanupParser ();
+}
+
+static xmlDocPtr
+read_xml_doc (const char *content, guint length, GError **error)
+{
+ xmlDoc *doc;
+
+ doc = xmlReadMemory (content, length, "noname.xml", NULL, 0);
+
+ if (!doc)
+ {
+ *error = g_error_new_literal (MAPS_OSM_ERROR, 0,
+ _("Failed to parse XML document"));
+ return NULL;
+ }
+
+ return doc;
+}
+
+static void
+parse_tag (const xmlAttr *attrs, GHashTable *tags)
+{
+ const xmlAttr *cur_attr;
+ char *key;
+ char *value;
+ char *result;
+
+ key = NULL;
+ value = NULL;
+
+ for (cur_attr = attrs; cur_attr; cur_attr = cur_attr->next)
+ {
+ if (g_str_equal (cur_attr->name, "k"))
+ key = cur_attr->children->content, "";
+ else if (g_str_equal (cur_attr->name, "v"))
+ value = cur_attr->children->content, "";
+ else
+ g_warning ("Unexpected tag property: %s\n", cur_attr->name);
+ }
+
+ g_hash_table_insert (tags, key, value);
+}
+
+static GHashTable *
+parse_attributes (const xmlNode *node)
+{
+ GHashTable *attributes;
+ const xmlAttr *cur_attr;
+
+ attributes = g_hash_table_new (g_str_hash, g_str_equal);
+
+ for (cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next)
+ {
+ g_hash_table_insert (attributes,
+ (gpointer) cur_attr->name,
+ (gpointer) cur_attr->children->content);
+ }
+
+ return attributes;
+}
+
+static GHashTable *
+parse_tags (const xmlNode *tag_child)
+{
+ GHashTable *tags;
+ const xmlNode *cur_node;
+
+ tags = g_hash_table_new (g_str_hash, g_str_equal);
+
+ for (cur_node = tag_child; cur_node; cur_node = cur_node->next)
+ {
+ /* skip non-element nodes */
+ if (cur_node->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (g_str_equal (cur_node->name, "tag"))
+ parse_tag (cur_node->properties, tags);
+ }
+
+ return tags;
+}
+
+static GArray *
+parse_node_refs (const xmlNode *node_ref_child)
+{
+ GArray *node_refs;
+ const xmlNode *cur_node;
+
+ node_refs = g_array_new (FALSE, FALSE, sizeof (guint64));
+
+ for (cur_node = node_ref_child; cur_node; cur_node = cur_node->next)
+ {
+ /* skip non-element nodes */
+ if (cur_node->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (g_str_equal (cur_node->name, "nd"))
+ {
+ char *ref;
+ GHashTable *attributes;
+
+ attributes = parse_attributes (cur_node);
+ ref = g_hash_table_lookup (attributes, "ref");
+
+ if (ref)
+ {
+ guint64 id = g_ascii_strtoull (ref, NULL, 10);
+
+ if (id == 0)
+ g_warning ("Invalid node ref: %s", ref);
+ else
+ g_array_append_val (node_refs, id);
+ }
+
+ g_hash_table_destroy (attributes);
+ }
+ }
+
+ return node_refs;
+}
+
+static xmlNode *
+get_sub_node (xmlDoc *doc)
+{
+ xmlNode *node;
+ xmlXPathContext *xpath_ctx;
+ xmlXPathObject * xpath_obj;
+
+ xpath_ctx = xmlXPathNewContext (doc);
+ xpath_obj = xmlXPathEvalExpression ("/osm/node|/osm/way|/osm/relation",
+ xpath_ctx);
+
+ if (xpath_obj && xpath_obj->nodesetval && xpath_obj->nodesetval->nodeNr > 0)
+ {
+ node = xmlCopyNode (xpath_obj->nodesetval->nodeTab[0], 1);
+ }
+ else
+ {
+ g_warning ("Couldn't find element");
+ node = NULL;
+ }
+
+ xmlXPathFreeObject (xpath_obj);
+ xmlXPathFreeContext (xpath_ctx);
+
+ return node;
+}
+
+static void
+for_each_tag (gpointer key, gpointer value, gpointer user_data)
+{
+ const char *k = (const char *) key;
+ const char *v = (const char *) value;
+ MapsOSMObject *object = MAPS_OSMOBJECT (user_data);
+
+ maps_osm_object_set_tag (object, k, v);
+}
+
+static void
+fill_tags (MapsOSMObject *object, GHashTable *tags)
+{
+ g_hash_table_foreach (tags, for_each_tag, object);
+}
+
+static void
+fill_node_ref_list (MapsOSMWay *way, const GArray *node_refs)
+{
+ int i;
+
+ for (i = 0; i < node_refs->len; i++)
+ {
+ maps_osm_way_add_node_id (way, g_array_index (node_refs, guint64, i));
+ }
+}
+
+static MapsOSMNode *
+parse_node (const xmlNodePtr node, GError **error)
+{
+ const char *id_string;
+ guint64 id;
+ const char *changeset_string;
+ guint64 changeset;
+ const char *version_string;
+ guint version;
+ const char *lat_string;
+ double lat;
+ const char *lon_string;
+ double lon;
+
+ const xmlAttr *cur_attr;
+
+ GHashTable *tags;
+ GHashTable *attributes;
+
+ MapsOSMNode *result;
+
+ attributes = parse_attributes (node);
+
+ id_string = g_hash_table_lookup (attributes, "id");
+ changeset_string = g_hash_table_lookup (attributes, "changeset");
+ version_string = g_hash_table_lookup (attributes, "version");
+ lat_string = g_hash_table_lookup (attributes, "lat");
+ lon_string = g_hash_table_lookup (attributes, "lon");
+
+ if (!id_string || !changeset_string || !version_string
+ || !lat_string || !lon_string)
+ {
+ *error = g_error_new_literal (MAPS_OSM_ERROR, 0,
+ _("Missing required attributes"));
+ g_hash_table_destroy (attributes);
+ return NULL;
+ }
+
+ id = g_ascii_strtoull (id_string, NULL, 10);
+ changeset = g_ascii_strtoull (changeset_string, NULL, 10);
+ version = g_ascii_strtoull (version_string, NULL, 10);
+ lon = g_ascii_strtod (lon_string, NULL);
+ lat = g_ascii_strtod (lat_string, NULL);
+
+ g_hash_table_destroy (attributes);
+
+ result = maps_osm_node_new (id, version, changeset, lon, lat);
+
+ tags = parse_tags (node->children);
+ fill_tags (MAPS_OSMOBJECT (result), tags);
+
+ g_hash_table_destroy (tags);
+
+ return result;
+}
+
+static MapsOSMWay *
+parse_way (const xmlNodePtr way, GError **error)
+{
+ GHashTable *attributes;
+ GHashTable *tags;
+ GArray *node_refs;
+ MapsOSMWay *result;
+
+ const char *id_string;
+ guint64 id;
+ const char *changeset_string;
+ guint64 changeset;
+ const char *version_string;
+ guint version;
+
+ attributes = parse_attributes (way);
+
+ id_string = g_hash_table_lookup (attributes, "id");
+ changeset_string = g_hash_table_lookup (attributes, "changeset");
+ version_string = g_hash_table_lookup (attributes, "version");
+
+ if (!id_string || !changeset_string || !version_string)
+ {
+ g_warning ("Missing required attributes\n");
+ g_hash_table_destroy (attributes);
+ return NULL;
+ }
+
+ g_hash_table_destroy (attributes);
+
+ id = g_ascii_strtoull (id_string, NULL, 10);
+ changeset = g_ascii_strtoull (changeset_string, NULL, 10);
+ version = g_ascii_strtoull (version_string, NULL, 10);
+
+ result = maps_osm_way_new (id, version, changeset);
+
+ tags = parse_tags (way->children);
+ fill_tags (MAPS_OSMOBJECT (result), tags);
+ g_hash_table_destroy (tags);
+
+ node_refs = parse_node_refs (way->children);
+ fill_node_ref_list (result, node_refs);
+ g_array_free (node_refs, TRUE);
+
+ return result;
+}
+
+
+static GList *
+parse_members (const xmlNode *member_child)
+{
+ const xmlNode *cur_node;
+ GList *members;
+
+ members = NULL;
+
+ for (cur_node = member_child; cur_node; cur_node = cur_node->next)
+ {
+ /* skip non-element nodes */
+ if (cur_node->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (g_str_equal (cur_node->name, "member"))
+ {
+ GHashTable *attributes;
+
+ attributes = parse_attributes (cur_node);
+ members = g_list_append (members, attributes);
+ }
+ }
+
+ return members;
+}
+
+static void
+fill_members (MapsOSMRelation *relation, const GList *members)
+{
+ const GList *cur;
+
+ for (cur = members; cur; cur = g_list_next (cur)) {
+ GHashTable *attributes = (GHashTable *) cur->data;
+ const char *type_string = g_hash_table_lookup (attributes, "type");
+ guint type;
+ const char *role = g_hash_table_lookup (attributes, "role");
+ const char *ref_string = g_hash_table_lookup (attributes, "ref");
+ guint64 ref;
+
+ if (ref_string)
+ ref = g_ascii_strtoull (ref_string, NULL, 10);
+
+ if (g_strcmp0 (type_string, "node") == 0)
+ type = MEMBER_TYPE_NODE;
+ else if (g_strcmp0 (type_string, "way") == 0)
+ type = MEMBER_TYPE_WAY;
+ else if (g_strcmp0 (type_string, "relation") == 0)
+ type = MEMBER_TYPE_RELATION;
+ else
+ {
+ g_warning ("Unknown relation type: %s\n", type_string);
+ continue;
+ }
+
+ maps_osm_relation_add_member (relation, role, type, ref);
+ }
+}
+
+static MapsOSMRelation *
+parse_relation (const xmlNodePtr relation, GError **error)
+{
+ GHashTable *attributes;
+ GHashTable *tags;
+ GList *member_list;
+
+ const char *id_string;
+ guint64 id;
+ const char *changeset_string;
+ guint64 changeset;
+ const char *version_string;
+ guint version;
+
+ MapsOSMRelation *result;
+
+ attributes = parse_attributes (relation);
+ id_string = g_hash_table_lookup (attributes, "id");
+ changeset_string = g_hash_table_lookup (attributes, "changeset");
+ version_string = g_hash_table_lookup (attributes, "version");
+
+ if (!id_string || !changeset_string || !version_string)
+ {
+ *error = g_error_new_literal (MAPS_OSM_ERROR, 0,
+ _("Missing required attributes"));
+ g_hash_table_destroy (attributes);
+ return NULL;
+ }
+
+ g_hash_table_destroy (attributes);
+
+ id = g_ascii_strtoull (id_string, NULL, 10);
+ changeset = g_ascii_strtoull (changeset_string, NULL, 10);
+ version = g_ascii_strtoull (version_string, NULL, 10);
+
+ result = maps_osm_relation_new (id, version, changeset);
+
+ tags = parse_tags (relation->children);
+ fill_tags (MAPS_OSMOBJECT (result), tags);
+ g_hash_table_destroy (tags);
+
+ member_list = parse_members (relation->children);
+ fill_members (result, member_list);
+ g_list_free_full (member_list, (GDestroyNotify) g_hash_table_destroy);
+
+ return result;
+}
+
+/**
+ * maps_osm_parse:
+ * @content: XML data
+ * @length: Length of data
+ * @error: Error handle
+ * Returns: (transfer full): A MapsOSMObject
+ */
+MapsOSMObject *
+maps_osm_parse (const char *content, guint length, GError **error)
+{
+ xmlDocPtr doc;
+ xmlNodePtr sub_node;
+ MapsOSMObject *object;
+
+ doc = read_xml_doc (content, length, error);
+
+ if (!doc)
+ return NULL;
+
+ sub_node = get_sub_node (doc);
+
+ if (!sub_node)
+ {
+ *error = g_error_new_literal (MAPS_OSM_ERROR, 0,
+ _("Could not find OSM element"));
+ return NULL;
+ }
+
+ if (g_str_equal (sub_node->name, "node"))
+ {
+ object = MAPS_OSMOBJECT (parse_node (sub_node, error));
+ }
+ else if (g_str_equal (sub_node->name, "way"))
+ {
+ object = MAPS_OSMOBJECT (parse_way (sub_node, error));
+ }
+ else if (g_str_equal (sub_node->name, "relation"))
+ {
+ object = MAPS_OSMOBJECT (parse_relation (sub_node, error));
+ }
+
+ xmlFreeNode (sub_node);
+
+ return object;
+}
diff --git a/lib/maps-osm.h b/lib/maps-osm.h
new file mode 100644
index 00000000..faca7404
--- /dev/null
+++ b/lib/maps-osm.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 Marcus Lundblad
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Author: Marcus Lundblad <ml@update.uu.se>
+ */
+
+#ifndef __MAPS_OSM_H__
+#define __MAPS_OSM_H__
+
+#include <glib.h>
+
+#include "maps-osm-node.h"
+#include "maps-osm-way.h"
+#include "maps-osm-relation.h"
+
+void maps_osm_init (void);
+void maps_osm_finalize (void);
+
+MapsOSMObject *maps_osm_parse (const char *content, guint length,
+ GError **error);
+
+#endif