]> mj.ucw.cz Git - leo.git/blob - osm.c
Switched to UCW configure and build system
[leo.git] / osm.c
1 /*
2  *      Hic Est Leo -- OSM Data Representation
3  *
4  *      (c) 2014 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <ucw/lib.h>
8 #include <ucw/gary.h>
9 #include <ucw/mempool.h>
10 #include <ucw/stkstring.h>
11 #include <ucw/strtonum.h>
12
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <proj_api.h>
16
17 #include "leo.h"
18 #include "osm.h"
19
20 static struct mempool *osm_pool;
21
22 /*** Generic objects ***/
23
24 struct osm_id_to_obj {
25   osm_id_t id;
26   struct osm_object *o;
27 };
28
29 clist osm_obj_list[OSM_TYPE_MAX];
30
31 #define P(x) [OSM_TYPE_##x] = #x,
32 const char * const osm_obj_type_names[OSM_TYPE_MAX] = {
33   [OSM_TYPE_INVALID] = "Invalid",
34   [OSM_TYPE_NODE] = "Node",
35   [OSM_TYPE_WAY] = "Way",
36   [OSM_TYPE_RELATION] = "Relation",
37   [OSM_TYPE_MULTIPOLYGON] = "Multipolygon",
38 };
39 #undef P
40
41 #define HASH_NODE struct osm_id_to_obj
42 #define HASH_PREFIX(x) osm_id_hash_##x
43 #define HASH_KEY_ATOMIC id
44 #define HASH_ATOMIC_TYPE osm_id_t
45 #define HASH_WANT_FIND
46 #define HASH_WANT_LOOKUP
47 #define HASH_USE_POOL osm_pool
48 #define HASH_ZERO_FILL
49 #define HASH_TABLE_DYNAMIC
50 #include <ucw/hashtable.h>
51
52 static struct osm_id_hash_table osm_id_hash[OSM_TYPE_MAX];
53
54 osm_id_t osm_parse_id(const char *str)
55 {
56   uintmax_t id;
57   const char *err = str_to_uintmax(&id, str, NULL, STN_WHOLE | STN_MINUS | 10);
58   if (err || id != (osm_id_t)id)
59     return OSM_INVALID_ID;
60   else
61     return id;
62 }
63
64 struct osm_object *osm_obj_find_by_id(enum osm_object_type type, osm_id_t id)
65 {
66   ASSERT(type != OSM_TYPE_INVALID && type < OSM_TYPE_MAX);
67   struct osm_id_to_obj *ii = osm_id_hash_find(&osm_id_hash[type], id);
68   return ii ? ii->o : NULL;
69 }
70
71 static void *osm_obj_new(enum osm_object_type type, osm_id_t id, uns size)
72 {
73   ASSERT(type != OSM_TYPE_INVALID && type < OSM_TYPE_MAX);
74   struct osm_id_to_obj *ii = osm_id_hash_lookup(&osm_id_hash[type], id);
75   if (ii->o)
76     die("Id %ju for type %s defined twice", (uintmax_t) id, osm_obj_type_names[type]);
77
78   struct osm_object *o = mp_alloc_zero(osm_pool, size);
79   clist_add_tail(&osm_obj_list[type], &o->n);
80   o->type = type;
81   o->id = id;
82   clist_init(&o->tags);
83   clist_init(&o->backrefs);
84   ii->o = o;
85   return o;
86 }
87
88 void osm_ref_add(struct osm_object *parent, clist *list, struct osm_object *son, osm_val_t role)
89 {
90   ASSERT(parent != son);
91
92   struct osm_ref *ref = mp_alloc(osm_pool, sizeof(*ref));
93   ref->o = son;
94   ref->role = role;
95   clist_add_tail(list, &ref->n);
96
97   struct osm_ref *backref = mp_alloc(osm_pool, sizeof(*ref));
98   backref->o = parent;
99   backref->role = 0;
100   clist_add_tail(&son->backrefs, &backref->n);
101 }
102
103 void osm_obj_dump(struct osm_object *o)
104 {
105   switch (o->type)
106     {
107     case OSM_TYPE_NODE:
108       osm_node_dump((struct osm_node *) o);
109       break;
110     case OSM_TYPE_WAY:
111       osm_way_dump((struct osm_way *) o);
112       break;
113     case OSM_TYPE_RELATION:
114       osm_relation_dump((struct osm_relation *) o);
115       break;
116     case OSM_TYPE_MULTIPOLYGON:
117       osm_multipolygon_dump((struct osm_multipolygon *) o);
118       break;
119     default:
120       ASSERT(0);
121     }
122 }
123
124 void osm_obj_warn(struct osm_object *o, const char *mesg, ...)
125 {
126   va_list args;
127   va_start(args, mesg);
128   msg(L_WARN, "%s: %s", STK_OSM_NAME(o), stk_vprintf(mesg, args));
129   va_end(args);
130 }
131
132 /*** Tags ***/
133
134 struct dict osm_key_dict, osm_value_dict;
135
136 void osm_obj_add_tag_raw(struct osm_object *o, osm_key_t key, osm_val_t val)
137 {
138   struct osm_tag *t = mp_alloc(osm_pool, sizeof(*t));
139   t->key = key;
140   t->val = val;
141   clist_add_tail(&o->tags, &t->n);
142 }
143
144 void osm_obj_add_tag(struct osm_object *o, const char *key, const char *val)
145 {
146   osm_obj_add_tag_raw(o, osm_key_encode(key), osm_val_encode(val));
147 }
148
149 osm_val_t osm_obj_find_tag(struct osm_object *o, osm_key_t key)
150 {
151   CLIST_FOR_EACH(struct osm_tag *, t, o->tags)
152     if (t->key == key)
153       return t->val;
154   return 0;
155 }
156
157 void osm_tag_dump(struct osm_tag *t)
158 {
159   printf("\t%s = %s\n", osm_key_decode(t->key), osm_val_decode(t->val));
160 }
161
162 static const char * const osm_wk_keys[] = {
163   NULL,
164 #define P(x,y) [KEY_##x] = y,
165 #include "dict-keys.h"
166 #undef P
167   NULL
168 };
169
170 static const char * const osm_wk_values[] = {
171   NULL,
172 #define P(x,y) [VALUE_##x] = y,
173 #include "dict-values.h"
174 #undef P
175   NULL
176 };
177
178 static void osm_tag_init(void)
179 {
180   dict_init(&osm_key_dict, osm_wk_keys);
181   dict_init(&osm_value_dict, osm_wk_values);
182 }
183
184 /*** Nodes ***/
185
186 struct osm_node *osm_node_new(osm_id_t id)
187 {
188   struct osm_node *n = osm_obj_new(OSM_TYPE_NODE, id, sizeof(*n));
189   return n;
190 }
191
192 void osm_node_dump(struct osm_node *n)
193 {
194   printf("Node %ju: (%f,%f)\n", (uintmax_t) n->o.id, n->x, n->y);
195   CLIST_FOR_EACH(struct osm_tag *, t, n->o.tags)
196     osm_tag_dump(t);
197 }
198
199 void osm_node_dump_all(void)
200 {
201   printf("Known nodes:\n");
202   CLIST_FOR_EACH(struct osm_node *, n, osm_obj_list[OSM_TYPE_NODE])
203     osm_node_dump(n);
204   putchar('\n');
205 }
206
207 /*** Ways ***/
208
209 struct osm_way *osm_way_new(osm_id_t id)
210 {
211   struct osm_way *w = osm_obj_new(OSM_TYPE_WAY, id, sizeof(*w));
212   clist_init(&w->nodes);
213   return w;
214 }
215
216 void osm_way_add_node(struct osm_way *w, struct osm_node *n)
217 {
218   osm_ref_add(&w->o, &w->nodes, &n->o, 0);
219 }
220
221 void osm_way_dump(struct osm_way *w)
222 {
223   printf("Way %ju%s\n", (uintmax_t) w->o.id, (osm_way_cyclic_p(w) ? " (cyclic)" : ""));
224   CLIST_FOR_EACH(struct osm_tag *, t, w->o.tags)
225     osm_tag_dump(t);
226   OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
227     {
228       printf("\tNode %ju\n", (uintmax_t) n->o.id);
229     }
230   OSM_FOR_EACH_END;
231 }
232
233 void osm_way_dump_all(void)
234 {
235   printf("Known ways:\n");
236   CLIST_FOR_EACH(struct osm_way *, w, osm_obj_list[OSM_TYPE_WAY])
237     osm_way_dump(w);
238   putchar('\n');
239 }
240
241 bool osm_way_cyclic_p(struct osm_way *w)
242 {
243   struct osm_ref *first_ref = clist_head(&w->nodes);
244   struct osm_ref *last_ref = clist_tail(&w->nodes);
245   return (first_ref && last_ref &&
246           first_ref != last_ref &&
247           first_ref->o == last_ref->o);
248 }
249
250 /*** Relations ***/
251
252 struct osm_relation *osm_relation_new(osm_id_t id)
253 {
254   struct osm_relation *r = osm_obj_new(OSM_TYPE_RELATION, id, sizeof(*r));
255   clist_init(&r->members);
256   return r;
257 }
258
259 void osm_relation_add_member(struct osm_relation *r, struct osm_object *o, const char *role)
260 {
261   osm_ref_add(&r->o, &r->members, o, (role && role[0] ? osm_val_encode(role) : 0));
262 }
263
264 void osm_relation_dump(struct osm_relation *r)
265 {
266   printf("Relation %ju\n", (uintmax_t) r->o.id);
267   CLIST_FOR_EACH(struct osm_tag *, t, r->o.tags)
268     osm_tag_dump(t);
269   CLIST_FOR_EACH(struct osm_ref *, f, r->members)
270     printf("\tRole %s: %s %ju\n", (f->role ? osm_val_decode(f->role) : "<none>"), osm_obj_type_names[f->o->type], (uintmax_t) f->o->id);
271 }
272
273 void osm_relation_dump_all(void)
274 {
275   printf("Known relations:\n");
276   CLIST_FOR_EACH(struct osm_relation *, r, osm_obj_list[OSM_TYPE_RELATION])
277     osm_relation_dump(r);
278   putchar('\n');
279 }
280
281 /*** Multipolygons ***/
282
283 void osm_multipolygon_dump(struct osm_multipolygon *m)
284 {
285   printf("Multipolygon %ju\n", (uintmax_t) m->o.id);
286   CLIST_FOR_EACH(struct osm_tag *, t, m->o.tags)
287     osm_tag_dump(t);
288   CLIST_FOR_EACH(struct osm_mpg_boundary *, b, m->boundaries)
289     {
290       printf("\tBoundary inner=%u\n", b->inner);
291       CLIST_FOR_EACH(struct osm_ref *, f, b->nodes)
292         printf("\t\tNode %ju\n", (uintmax_t) f->o->id);
293     }
294 }
295
296 void osm_multipolygon_dump_all(void)
297 {
298   printf("Known multipolygons:\n");
299   CLIST_FOR_EACH(struct osm_multipolygon *, r, osm_obj_list[OSM_TYPE_MULTIPOLYGON])
300     osm_multipolygon_dump(r);
301   putchar('\n');
302 }
303
304 static struct mempool *mpg_pool;
305 static struct osm_multipolygon *mpg_current;
306
307 struct mpg_vertex {
308   osm_id_t id;
309   struct osm_object *o;
310   clist edges;                  // of mpg_edge's
311   bool visited;
312 };
313
314 struct mpg_edge {
315   cnode n;
316   struct mpg_edge *twin;
317   struct mpg_vertex *dest;
318   bool used;
319 };
320
321 #define HASH_NODE struct mpg_vertex
322 #define HASH_PREFIX(x) mpg_vertex_hash_##x
323 #define HASH_KEY_ATOMIC id
324 #define HASH_ATOMIC_TYPE osm_id_t
325 #define HASH_USE_POOL mpg_pool
326 #define HASH_TABLE_ALLOC
327 #define HASH_WANT_LOOKUP
328 #define HASH_LOOKUP_DETECT_NEW
329 #include <ucw/hashtable.h>
330
331 static void mpg_insert_way(struct osm_way *w)
332 {
333   struct mpg_vertex *prev = NULL;
334   OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
335     {
336       int new = 0;
337       struct mpg_vertex *v = mpg_vertex_hash_lookup(n->o.id, &new);
338       if (new)
339         {
340           clist_init(&v->edges);
341           v->visited = 0;
342           v->o = &n->o;
343         }
344       if (prev)
345         {
346           struct mpg_edge *e1 = mp_alloc_zero(mpg_pool, sizeof(*e1));
347           struct mpg_edge *e2 = mp_alloc_zero(mpg_pool, sizeof(*e2));
348           e1->twin = e2;
349           e1->dest = v;
350           clist_add_tail(&prev->edges, &e1->n);
351           e2->twin = e1;
352           e2->dest = prev;
353           clist_add_tail(&v->edges, &e2->n);
354         }
355       prev = v;
356     }
357   OSM_FOR_EACH_END;
358 }
359
360 static void UNUSED mpg_dump(void)
361 {
362   printf("=== Multipolygon graph for relation %ju ===\n", (uintmax_t) mpg_current->o.id);
363   HASH_FOR_ALL(mpg_vertex_hash, v)
364     {
365       printf("Vertex %ju\n", (uintmax_t) v->id);
366       CLIST_FOR_EACH(struct mpg_edge *, e, v->edges)
367         printf("\t-> %ju\n", (uintmax_t) e->dest->id);
368     }
369   HASH_END_FOR;
370   puts("=== End ===");
371 }
372
373 static void mpg_walk_boundary(struct osm_multipolygon *m, bool inner)
374 {
375   // FIXME: Replace by a better algorithm, which respects geometry
376   // and is able to cope with higher degree vertices.
377   HASH_FOR_ALL(mpg_vertex_hash, v)
378     {
379       if (!v->visited)
380         {
381           struct osm_mpg_boundary *b = mp_alloc(osm_pool, sizeof(*b));
382           clist_add_tail(&m->boundaries, &b->n);
383           b->inner = inner;
384           clist_init(&b->nodes);
385
386           struct mpg_vertex *w = v;
387           while (!w->visited)
388             {
389               w->visited = 1;
390               struct osm_ref *f = mp_alloc(osm_pool, sizeof(*f));
391               clist_add_tail(&b->nodes, &f->n);
392               f->o = w->o;
393               f->role = 0;
394
395               struct mpg_vertex *dest = NULL;
396               CLIST_FOR_EACH(struct mpg_edge *, e, w->edges)
397                 {
398                   if (e->used)
399                     continue;
400                   if (dest)
401                     {
402                       if (w != v)
403                         osm_obj_warn(&mpg_current->o, "Suspicious vertex degree of node %ju", (uintmax_t) w->o->id);
404                       break;
405                     }
406                   else
407                     {
408                       dest = e->dest;
409                       e->used = 1;
410                       e->twin->used = 1;
411                     }
412                 }
413
414               w = dest;
415             }
416
417           if (w != v)
418             osm_obj_warn(&mpg_current->o, "Boundary not closed at node %ju", (uintmax_t) w->o->id);
419
420           struct osm_ref *f = mp_alloc(osm_pool, sizeof(*f));
421           clist_add_tail(&b->nodes, &f->n);
422           f->o = v->o;
423           f->role = 0;
424         }
425     }
426   HASH_END_FOR;
427 }
428
429 static void osm_multipolygon_make(struct osm_relation *r)
430 {
431   struct osm_multipolygon *m = osm_obj_new(OSM_TYPE_MULTIPOLYGON, r->o.id, sizeof(*m));
432   mpg_current = m;
433   m->rel = r;
434   CLIST_FOR_EACH(struct osm_tag *, t, r->o.tags)
435     osm_obj_add_tag_raw(&m->o, t->key, t->val);
436   clist_init(&m->boundaries);
437
438   for (int inner=0; inner<2; inner++)
439     {
440       if (!mpg_pool)
441         mpg_pool = mp_new(4096);
442       mpg_vertex_hash_init();
443
444       CLIST_FOR_EACH(struct osm_ref *, f, r->members)
445         {
446           struct osm_object *o = f->o;
447           if (f->role != VALUE_INNER && f->role != VALUE_OUTER)
448             {
449               if (!inner)
450                 osm_obj_warn(o, "Unknown role %s in multipolygon relation", osm_val_decode(f->role));
451               continue;
452             }
453           if ((f->role == VALUE_INNER) != inner)
454             continue;
455           if (o->type == OSM_TYPE_WAY)
456             mpg_insert_way((struct osm_way *) o);
457           else
458             osm_obj_warn(o, "Only ways are supported in multipolygon boundaries");
459         }
460
461       if (debug_dump_multipolygons)
462         mpg_dump();
463
464       mpg_walk_boundary(m, inner);
465       mp_flush(mpg_pool);
466     }
467 }
468
469 void osm_make_multipolygons(void)
470 {
471   uns mpg_cnt = 0;
472
473   CLIST_FOR_EACH(struct osm_relation *, r, osm_obj_list[OSM_TYPE_RELATION])
474     if (osm_obj_find_tag(&r->o, KEY_TYPE) == VALUE_MULTIPOLYGON)
475       {
476         osm_multipolygon_make(r);
477         mpg_cnt++;
478       }
479   msg(L_INFO, "Converted %u relations to multipolygons", mpg_cnt);
480 }
481
482 /*** Projection ***/
483
484 static projPJ osm_pj;
485
486 void osm_project(const char *spec)
487 {
488   osm_pj = pj_init_plus(spec);
489   if (!osm_pj)
490     die("Unable to initialize projection %s", spec);
491
492   CLIST_FOR_EACH(struct osm_node *, n, osm_obj_list[OSM_TYPE_NODE])
493     {
494       projUV p;
495       p.u = n->x * DEG_TO_RAD;
496       p.v = n->y * DEG_TO_RAD;
497       p = pj_fwd(p, osm_pj);
498       n->x = p.u;
499       n->y = p.v;
500     }
501 }
502
503 /*** Object centers ***/
504
505 bool osm_obj_center(struct osm_object *o, double *xp, double *yp)
506 {
507   switch (o->type)
508     {
509     case OSM_TYPE_NODE:
510       {
511         struct osm_node *n = (struct osm_node *) o;
512         *xp = n->x;
513         *yp = n->y;
514         return 1;
515       }
516     case OSM_TYPE_WAY:
517       {
518         struct osm_way *w = (struct osm_way *) o;
519         double sx = 0, sy = 0;
520         uns nn = 0;
521         OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
522           {
523             sx += n->x;
524             sy += n->y;
525             nn++;
526           }
527         OSM_FOR_EACH_END;
528         if (!nn)
529           return 0;
530         *xp = sx / nn;
531         *yp = sy / nn;
532         return 1;
533       }
534     case OSM_TYPE_MULTIPOLYGON:
535       {
536         struct osm_multipolygon *m = (struct osm_multipolygon *) o;
537         double sx = 0, sy = 0;
538         uns nn = 0;
539         CLIST_FOR_EACH(struct osm_mpg_boundary *, b, m->boundaries)
540           {
541             if (b->inner)
542               continue;
543             OSM_FOR_EACH_BEGIN(struct osm_node *, n, b->nodes)
544               {
545                 sx += n->x;
546                 sy += n->y;
547                 nn++;
548               }
549             OSM_FOR_EACH_END;
550           }
551         if (!nn)
552           return 0;
553         *xp = sx / nn;
554         *yp = sy / nn;
555         return 1;
556       }
557     default:
558       return 0;
559     }
560 }
561
562 /*** Globals ***/
563
564 void osm_init(void)
565 {
566   osm_pool = mp_new(65536);
567   for (int i=0; i<OSM_TYPE_MAX; i++)
568     {
569       clist_init(&osm_obj_list[i]);
570       osm_id_hash_init(&osm_id_hash[i]);
571     }
572   osm_tag_init();
573 }
574
575 void osm_dump(void)
576 {
577   osm_node_dump_all();
578   osm_way_dump_all();
579   osm_relation_dump_all();
580   osm_multipolygon_dump_all();
581 }