]> mj.ucw.cz Git - leo.git/blob - xml.c
Labelling: Comparison of individuals
[leo.git] / xml.c
1 /*
2  *      Hic Est Leo -- OSM XML Parser
3  *
4  *      (c) 2014--2015 Martin Mares <mj@ucw.cz>
5  */
6
7 #undef LOCAL_DEBUG
8
9 #include <ucw/lib.h>
10 #include <ucw/fastbuf.h>
11 #include <ucw-xml/xml.h>
12
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16
17 #include "leo.h"
18 #include "osm.h"
19
20 static void parse_tag(struct xml_context *ctx, struct osm_object *o, struct xml_node *t)
21 {
22   char *tag_k = xml_attr_value(ctx, t, "k");
23   char *tag_v = xml_attr_value(ctx, t, "v");
24   if (!tag_k || !tag_v)
25     die("Object %ju has malformed tag", (uintmax_t) o->id);
26   osm_obj_add_tag(o, tag_k, tag_v);
27 }
28
29 static void parse_node(struct xml_context *ctx, struct xml_node *e)
30 {
31   char *attr_id = xml_attr_value(ctx, e, "id");
32   if (!attr_id)
33     die("Node with no id");
34
35   char *attr_vis = xml_attr_value(ctx, e, "visible");
36   if (attr_vis && strcmp(attr_vis, "true"))
37     {
38       msg(L_DEBUG, "Node %s invisible", attr_id);
39       return;
40     }
41
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);
46
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);
50
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);
54 }
55
56 static void parse_way(struct xml_context *ctx, struct xml_node *e)
57 {
58   char *attr_id = xml_attr_value(ctx, e, "id");
59   if (!attr_id)
60     die("Way with no id");
61
62   char *attr_vis = xml_attr_value(ctx, e, "visible");
63   if (attr_vis && strcmp(attr_vis, "true"))
64     {
65       msg(L_DEBUG, "Way %s invisible", attr_id);
66       return;
67     }
68
69   struct osm_way *w = osm_way_new(osm_parse_id(attr_id));
70
71   XML_NODE_FOR_EACH(t, e)
72     if (t->type == XML_NODE_ELEM)
73       {
74         if (!strcmp(t->name, "tag"))
75           parse_tag(ctx, &w->o, t);
76         else if (!strcmp(t->name, "nd"))
77           {
78             char *nd_ref = xml_attr_value(ctx, t, "ref");
79             if (!nd_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));
82             if (!o)
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);
85           }
86       }
87 }
88
89 static void parse_relation(struct xml_context *ctx, struct xml_node *e)
90 {
91   char *attr_id = xml_attr_value(ctx, e, "id");
92   if (!attr_id)
93     die("Relation with no id");
94
95   char *attr_vis = xml_attr_value(ctx, e, "visible");
96   if (attr_vis && strcmp(attr_vis, "true"))
97     {
98       msg(L_DEBUG, "Relation %s invisible", attr_id);
99       return;
100     }
101
102   struct osm_relation *r = osm_relation_new(osm_parse_id(attr_id));
103
104   XML_NODE_FOR_EACH(t, e)
105     if (t->type == XML_NODE_ELEM)
106       {
107         if (!strcmp(t->name, "tag"))
108           parse_tag(ctx, &r->o, t);
109         else if (!strcmp(t->name, "member"))
110           {
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);
117
118             enum osm_object_type type;
119             if (!strcmp(m_type, "node"))
120               type = OSM_TYPE_NODE;
121             else if (!strcmp(m_type, "way"))
122               type = OSM_TYPE_WAY;
123             else if (!strcmp(m_type, "relation"))
124               {
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);
128               }
129             else
130               {
131                 msg(L_WARN, "Relation %ju refers to member %ju of unknown type %s", (uintmax_t) r->o.id, (uintmax_t) ref, m_type);
132                 continue;
133               }
134
135             struct osm_object *o = osm_obj_find_by_id(type, ref);
136             if (!o)
137               {
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);
140                 continue;
141               }
142             osm_relation_add_member(r, o, m_role);
143           }
144       }
145 }
146
147 static void h_error(struct xml_context *ctx)
148 {
149   fprintf(stderr, "%s at %u: %s\n", (ctx->err_code < XML_ERR_ERROR) ? "warn" : "error", xml_row(ctx), ctx->err_msg);
150 }
151
152 static void parse_element(struct xml_context *ctx, struct xml_node *e)
153 {
154   if (!strcmp(e->name, "node"))
155     parse_node(ctx, e);
156   else if (!strcmp(e->name, "way"))
157     parse_way(ctx, e);
158   else if (!strcmp(e->name, "relation"))
159     parse_relation(ctx, e);
160 }
161
162 void osm_xml_parse(const char *name)
163 {
164   struct xml_context xml_ctx, *ctx = &xml_ctx;
165   xml_init(ctx);
166   ctx->h_warn = ctx->h_error = ctx->h_fatal = h_error;
167   xml_push_fastbuf(ctx, bopen_file(name, O_RDONLY, NULL));
168
169   uint state;
170   while (state = xml_next_state(ctx, XML_PULL_STAG))
171     if (state == XML_STATE_STAG)
172       {
173         DBG("Level 1 tag <%s>", ctx->node->name);
174         if (!strcmp(ctx->node->name, "osm"))
175           {
176             DBG("Switched to level 2");
177             while ((state = xml_next_state(ctx, XML_PULL_STAG | XML_PULL_ETAG)) == XML_STATE_STAG)
178               {
179                 ctx->flags |= XML_ALLOC_CHARS | XML_ALLOC_TAGS;
180                 if (xml_skip_element(ctx) == XML_STATE_ETAG)
181                   {
182                     DBG("Level 2 tag <%s>", ctx->node->name);
183                     parse_element(ctx, ctx->node);
184                   }
185               }
186             DBG("Exited level 2 in state %u", state);
187           }
188         else
189           xml_skip_element(ctx);
190       }
191
192   if (ctx->err_code)
193     die("Fatal error in XML parser");
194
195   xml_cleanup(ctx);
196 }