diff options
author | Marcus Lundblad <ml@update.uu.se> | 2015-10-19 21:38:57 +0200 |
---|---|---|
committer | Jonas Danielsson <jonas@threetimestwo.org> | 2015-12-12 17:07:08 +0100 |
commit | 60471b8eccc501019714c4020a59adf247469b21 (patch) | |
tree | 5474369634038cb5d4f4c7283852e53237f8ac85 /lib | |
parent | e25c9940773f8ab10ac66012e8d59ef24e45d8d7 (diff) | |
download | gnome-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.am | 16 | ||||
-rw-r--r-- | lib/maps-osm-changeset.c | 190 | ||||
-rw-r--r-- | lib/maps-osm-changeset.h | 53 | ||||
-rw-r--r-- | lib/maps-osm-node.c | 169 | ||||
-rw-r--r-- | lib/maps-osm-node.h | 46 | ||||
-rw-r--r-- | lib/maps-osm-object.c | 331 | ||||
-rw-r--r-- | lib/maps-osm-object.h | 56 | ||||
-rw-r--r-- | lib/maps-osm-relation.c | 156 | ||||
-rw-r--r-- | lib/maps-osm-relation.h | 57 | ||||
-rw-r--r-- | lib/maps-osm-way.c | 116 | ||||
-rw-r--r-- | lib/maps-osm-way.h | 48 | ||||
-rw-r--r-- | lib/maps-osm.c | 473 | ||||
-rw-r--r-- | lib/maps-osm.h | 35 |
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 |