2 * Hic Est Leo -- OSM XML Parser
4 * (c) 2014--2015 Martin Mares <mj@ucw.cz>
11 #include <ucw/fastbuf.h>
12 #include <ucw-xml/xml.h>
20 static void parse_tag(struct xml_context *ctx, struct osm_object *o, struct xml_node *t)
22 char *tag_k = xml_attr_value(ctx, t, "k");
23 char *tag_v = xml_attr_value(ctx, t, "v");
25 die("Object %ju has malformed tag", (uintmax_t) o->id);
26 osm_obj_add_tag(o, tag_k, tag_v);
29 static void parse_node(struct xml_context *ctx, struct xml_node *e)
31 char *attr_id = xml_attr_value(ctx, e, "id");
33 die("Node with no id");
35 char *attr_vis = xml_attr_value(ctx, e, "visible");
36 if (attr_vis && strcmp(attr_vis, "true"))
38 msg(L_DEBUG, "Node %s invisible", attr_id);
42 char *attr_lat = xml_attr_value(ctx, e, "lat");
43 char *attr_lon = xml_attr_value(ctx, e, "lon");
44 if (!attr_lat || !attr_lon)
45 die("Node %s has mandatory attributes missing", attr_id);
47 struct osm_node *n = osm_node_new(osm_parse_id(attr_id));
48 n->x = atof(attr_lon);
49 n->y = atof(attr_lat);
51 XML_NODE_FOR_EACH(t, e)
52 if (t->type == XML_NODE_ELEM && !strcmp(t->name, "tag"))
53 parse_tag(ctx, &n->o, t);
56 static void parse_way(struct xml_context *ctx, struct xml_node *e)
58 char *attr_id = xml_attr_value(ctx, e, "id");
60 die("Way with no id");
62 char *attr_vis = xml_attr_value(ctx, e, "visible");
63 if (attr_vis && strcmp(attr_vis, "true"))
65 msg(L_DEBUG, "Way %s invisible", attr_id);
69 struct osm_way *w = osm_way_new(osm_parse_id(attr_id));
71 XML_NODE_FOR_EACH(t, e)
72 if (t->type == XML_NODE_ELEM)
74 if (!strcmp(t->name, "tag"))
75 parse_tag(ctx, &w->o, t);
76 else if (!strcmp(t->name, "nd"))
78 char *nd_ref = xml_attr_value(ctx, t, "ref");
80 die("Way %s has malformed ref", attr_id);
81 struct osm_object *o = osm_obj_find_by_id(OSM_TYPE_NODE, osm_parse_id(nd_ref));
83 die("Way %ju refers to unknown node %s", (uintmax_t) w->o.id, nd_ref);
84 osm_way_add_node(w, (struct osm_node *) o);
89 static void parse_relation(struct xml_context *ctx, struct xml_node *e)
91 char *attr_id = xml_attr_value(ctx, e, "id");
93 die("Relation with no id");
95 char *attr_vis = xml_attr_value(ctx, e, "visible");
96 if (attr_vis && strcmp(attr_vis, "true"))
98 msg(L_DEBUG, "Relation %s invisible", attr_id);
102 struct osm_relation *r = osm_relation_new(osm_parse_id(attr_id));
104 XML_NODE_FOR_EACH(t, e)
105 if (t->type == XML_NODE_ELEM)
107 if (!strcmp(t->name, "tag"))
108 parse_tag(ctx, &r->o, t);
109 else if (!strcmp(t->name, "member"))
111 char *m_role = xml_attr_value(ctx, t, "role");
112 char *m_ref = xml_attr_value(ctx, t, "ref");
113 char *m_type = xml_attr_value(ctx, t, "type");
114 if (!m_role || !m_ref || !m_type)
115 die("Relation %ju has malformed member", (uintmax_t) r->o.id);
116 osm_id_t ref = osm_parse_id(m_ref);
118 enum osm_object_type type;
119 if (!strcmp(m_type, "node"))
120 type = OSM_TYPE_NODE;
121 else if (!strcmp(m_type, "way"))
123 else if (!strcmp(m_type, "relation"))
125 type = OSM_TYPE_RELATION;
126 // Since the order of objects is topological, we need not worry about cycles
127 // msg(L_DEBUG, "Relation inside relation (%ju inside %ju)", (uintmax_t) o->id, (uintmax_t) r->o.id);
131 msg(L_WARN, "Relation %ju refers to member %ju of unknown type %s", (uintmax_t) r->o.id, (uintmax_t) ref, m_type);
135 struct osm_object *o = osm_obj_find_by_id(type, ref);
138 // This is a standard situation, so warn only when debugging
139 // msg(L_WARN, "Relation %ju refers to unknown member node %s", (uintmax_t) ref, m_ref);
142 osm_relation_add_member(r, o, m_role);
147 static void h_error(struct xml_context *ctx)
149 fprintf(stderr, "%s at %u: %s\n", (ctx->err_code < XML_ERR_ERROR) ? "warn" : "error", xml_row(ctx), ctx->err_msg);
152 static void parse_element(struct xml_context *ctx, struct xml_node *e)
154 if (!strcmp(e->name, "node"))
156 else if (!strcmp(e->name, "way"))
158 else if (!strcmp(e->name, "relation"))
159 parse_relation(ctx, e);
162 void osm_xml_parse(const char *name)
164 struct xml_context xml_ctx, *ctx = &xml_ctx;
166 ctx->h_warn = ctx->h_error = ctx->h_fatal = h_error;
167 xml_push_fastbuf(ctx, bopen_file(name, O_RDONLY, NULL));
170 while (state = xml_next_state(ctx, XML_PULL_STAG))
171 if (state == XML_STATE_STAG)
173 DBG("Level 1 tag <%s>", ctx->node->name);
174 if (!strcmp(ctx->node->name, "osm"))
176 DBG("Switched to level 2");
177 while ((state = xml_next_state(ctx, XML_PULL_STAG | XML_PULL_ETAG)) == XML_STATE_STAG)
179 ctx->flags |= XML_ALLOC_CHARS | XML_ALLOC_TAGS;
180 if (xml_skip_element(ctx) == XML_STATE_ETAG)
182 DBG("Level 2 tag <%s>", ctx->node->name);
183 parse_element(ctx, ctx->node);
186 DBG("Exited level 2 in state %u", state);
189 xml_skip_element(ctx);
193 die("Fatal error in XML parser");