]> mj.ucw.cz Git - leo.git/blob - xml.c
Lua bindings for object attributes and basic style properties
[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 "leo.h"
10 #include "osm.h"
11
12 #include <ucw/fastbuf.h>
13 #include <ucw-xml/xml.h>
14
15 #include <fcntl.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18
19 static void parse_tag(struct xml_context *ctx, struct osm_object *o, struct xml_node *t)
20 {
21   char *tag_k = xml_attr_value(ctx, t, "k");
22   char *tag_v = xml_attr_value(ctx, t, "v");
23   if (!tag_k || !tag_v)
24     die("Object %ju has malformed tag", (uintmax_t) o->id);
25   osm_obj_add_tag(o, tag_k, tag_v);
26 }
27
28 static void parse_node(struct xml_context *ctx, struct xml_node *e)
29 {
30   char *attr_id = xml_attr_value(ctx, e, "id");
31   if (!attr_id)
32     die("Node with no id");
33
34   char *attr_vis = xml_attr_value(ctx, e, "visible");
35   if (attr_vis && strcmp(attr_vis, "true"))
36     {
37       msg(L_DEBUG, "Node %s invisible", attr_id);
38       return;
39     }
40
41   char *attr_lat = xml_attr_value(ctx, e, "lat");
42   char *attr_lon = xml_attr_value(ctx, e, "lon");
43   if (!attr_lat || !attr_lon)
44     die("Node %s has mandatory attributes missing", attr_id);
45
46   struct osm_node *n = osm_node_new(osm_parse_id(attr_id));
47   n->x = atof(attr_lon);
48   n->y = atof(attr_lat);
49
50   XML_NODE_FOR_EACH(t, e)
51     if (t->type == XML_NODE_ELEM && !strcmp(t->name, "tag"))
52       parse_tag(ctx, &n->o, t);
53 }
54
55 static void parse_way(struct xml_context *ctx, struct xml_node *e)
56 {
57   char *attr_id = xml_attr_value(ctx, e, "id");
58   if (!attr_id)
59     die("Way with no id");
60
61   char *attr_vis = xml_attr_value(ctx, e, "visible");
62   if (attr_vis && strcmp(attr_vis, "true"))
63     {
64       msg(L_DEBUG, "Way %s invisible", attr_id);
65       return;
66     }
67
68   struct osm_way *w = osm_way_new(osm_parse_id(attr_id));
69
70   XML_NODE_FOR_EACH(t, e)
71     if (t->type == XML_NODE_ELEM)
72       {
73         if (!strcmp(t->name, "tag"))
74           parse_tag(ctx, &w->o, t);
75         else if (!strcmp(t->name, "nd"))
76           {
77             char *nd_ref = xml_attr_value(ctx, t, "ref");
78             if (!nd_ref)
79               die("Way %s has malformed ref", attr_id);
80             struct osm_object *o = osm_obj_find_by_id(OSM_TYPE_NODE, osm_parse_id(nd_ref));
81             if (!o)
82               die("Way %ju refers to unknown node %s", (uintmax_t) w->o.id, nd_ref);
83             osm_way_add_node(w, (struct osm_node *) o);
84           }
85       }
86 }
87
88 static void parse_relation(struct xml_context *ctx, struct xml_node *e)
89 {
90   char *attr_id = xml_attr_value(ctx, e, "id");
91   if (!attr_id)
92     die("Relation with no id");
93
94   char *attr_vis = xml_attr_value(ctx, e, "visible");
95   if (attr_vis && strcmp(attr_vis, "true"))
96     {
97       msg(L_DEBUG, "Relation %s invisible", attr_id);
98       return;
99     }
100
101   struct osm_relation *r = osm_relation_new(osm_parse_id(attr_id));
102
103   XML_NODE_FOR_EACH(t, e)
104     if (t->type == XML_NODE_ELEM)
105       {
106         if (!strcmp(t->name, "tag"))
107           parse_tag(ctx, &r->o, t);
108         else if (!strcmp(t->name, "member"))
109           {
110             char *m_role = xml_attr_value(ctx, t, "role");
111             char *m_ref = xml_attr_value(ctx, t, "ref");
112             char *m_type = xml_attr_value(ctx, t, "type");
113             if (!m_role || !m_ref || !m_type)
114               die("Relation %ju has malformed member", (uintmax_t) r->o.id);
115             osm_id_t ref = osm_parse_id(m_ref);
116
117             enum osm_object_type type;
118             if (!strcmp(m_type, "node"))
119               type = OSM_TYPE_NODE;
120             else if (!strcmp(m_type, "way"))
121               type = OSM_TYPE_WAY;
122             else if (!strcmp(m_type, "relation"))
123               {
124                 type = OSM_TYPE_RELATION;
125                 // Since the order of objects is topological, we need not worry about cycles
126                 // msg(L_DEBUG, "Relation inside relation (%ju inside %ju)", (uintmax_t) o->id, (uintmax_t) r->o.id);
127               }
128             else
129               {
130                 msg(L_WARN, "Relation %ju refers to member %ju of unknown type %s", (uintmax_t) r->o.id, (uintmax_t) ref, m_type);
131                 continue;
132               }
133
134             struct osm_object *o = osm_obj_find_by_id(type, ref);
135             if (!o)
136               {
137                 // This is a standard situation, so warn only when debugging
138                 // msg(L_WARN, "Relation %ju refers to unknown member node %s", (uintmax_t) ref, m_ref);
139                 continue;
140               }
141             osm_relation_add_member(r, o, m_role);
142           }
143       }
144 }
145
146 static void h_error(struct xml_context *ctx)
147 {
148   fprintf(stderr, "%s at %u: %s\n", (ctx->err_code < XML_ERR_ERROR) ? "warn" : "error", xml_row(ctx), ctx->err_msg);
149 }
150
151 static void parse_element(struct xml_context *ctx, struct xml_node *e)
152 {
153   // JOSM marks deleted, but not uploaded, objects
154   char *action = xml_attr_value(ctx, e, "action");
155   if (action && !strcmp(action, "delete"))
156     return;
157
158   if (!strcmp(e->name, "node"))
159     parse_node(ctx, e);
160   else if (!strcmp(e->name, "way"))
161     parse_way(ctx, e);
162   else if (!strcmp(e->name, "relation"))
163     parse_relation(ctx, e);
164 }
165
166 void osm_xml_parse(const char *name)
167 {
168   struct xml_context xml_ctx, *ctx = &xml_ctx;
169   xml_init(ctx);
170   ctx->h_warn = ctx->h_error = ctx->h_fatal = h_error;
171   xml_push_fastbuf(ctx, bopen_file(name, O_RDONLY, NULL));
172
173   uint state;
174   while (state = xml_next_state(ctx, XML_PULL_STAG))
175     if (state == XML_STATE_STAG)
176       {
177         DBG("Level 1 tag <%s>", ctx->node->name);
178         if (!strcmp(ctx->node->name, "osm"))
179           {
180             DBG("Switched to level 2");
181             while ((state = xml_next_state(ctx, XML_PULL_STAG | XML_PULL_ETAG)) == XML_STATE_STAG)
182               {
183                 ctx->flags |= XML_ALLOC_CHARS | XML_ALLOC_TAGS;
184                 if (xml_skip_element(ctx) == XML_STATE_ETAG)
185                   {
186                     DBG("Level 2 tag <%s>", ctx->node->name);
187                     parse_element(ctx, ctx->node);
188                   }
189               }
190             DBG("Exited level 2 in state %u", state);
191           }
192         else
193           xml_skip_element(ctx);
194       }
195
196   if (ctx->err_code)
197     die("Fatal error in XML parser");
198
199   xml_cleanup(ctx);
200 }