]> mj.ucw.cz Git - leo.git/blob - osm.c
Průvodce: Rendering of boundary etc.
[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
16 #define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H
17 #include <proj_api.h>
18
19 #include "leo.h"
20 #include "osm.h"
21
22 struct osm *osm_this;
23
24 /*** Generic objects ***/
25
26 struct osm_id_to_obj {
27   osm_id_t id;
28   struct osm_object *o;
29 };
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_TABLE_VARS struct mempool *pool;
48 #define HASH_USE_POOL table->pool
49 #define HASH_ZERO_FILL
50 #define HASH_TABLE_DYNAMIC
51 #include <ucw/hashtable.h>
52
53 osm_id_t osm_parse_id(const char *str)
54 {
55   uintmax_t id;
56   const char *err = str_to_uintmax(&id, str, NULL, STN_WHOLE | STN_MINUS | 10);
57   if (err || id != (osm_id_t)id)
58     return OSM_INVALID_ID;
59   else
60     return id;
61 }
62
63 struct osm_object *osm_obj_find_by_id(enum osm_object_type type, osm_id_t id)
64 {
65   ASSERT(type != OSM_TYPE_INVALID && type < OSM_TYPE_MAX);
66   struct osm_id_to_obj *ii = osm_id_hash_find(osm_this->id_hash[type], id);
67   return ii ? ii->o : NULL;
68 }
69
70 static void *osm_obj_new(enum osm_object_type type, osm_id_t id, uns size)
71 {
72   ASSERT(type != OSM_TYPE_INVALID && type < OSM_TYPE_MAX);
73   struct osm_id_to_obj *ii = osm_id_hash_lookup(osm_this->id_hash[type], id);
74   if (ii->o)
75     die("Id %ju for type %s defined twice", (uintmax_t) id, osm_obj_type_names[type]);
76
77   struct osm_object *o = mp_alloc_zero(osm_this->pool, size);
78   clist_add_tail(&osm_this->obj_list[type], &o->n);
79   osm_this->obj_cnt[type]++;
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_this->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_this->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_this->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_this->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_this->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_this->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_this->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_this->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 && !w->visited)
388             {
389               w->visited = 1;
390               struct osm_ref *f = mp_alloc(osm_this->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)
418             osm_obj_warn(&mpg_current->o, "Boundary not closed");
419           else if (w != v)
420             osm_obj_warn(&mpg_current->o, "Boundary not closed at node %ju", (uintmax_t) w->o->id);
421
422           struct osm_ref *f = mp_alloc(osm_this->pool, sizeof(*f));
423           clist_add_tail(&b->nodes, &f->n);
424           f->o = v->o;
425           f->role = 0;
426         }
427     }
428   HASH_END_FOR;
429 }
430
431 static void osm_multipolygon_make(struct osm_relation *r)
432 {
433   struct osm_multipolygon *m = osm_obj_new(OSM_TYPE_MULTIPOLYGON, r->o.id, sizeof(*m));
434   mpg_current = m;
435   m->rel = r;
436   CLIST_FOR_EACH(struct osm_tag *, t, r->o.tags)
437     osm_obj_add_tag_raw(&m->o, t->key, t->val);
438   clist_init(&m->boundaries);
439
440   for (int inner=0; inner<2; inner++)
441     {
442       if (!mpg_pool)
443         mpg_pool = mp_new(4096);
444       mpg_vertex_hash_init();
445
446       CLIST_FOR_EACH(struct osm_ref *, f, r->members)
447         {
448           struct osm_object *o = f->o;
449           if (f->role != VALUE_INNER && f->role != VALUE_OUTER)
450             {
451               if (!inner)
452                 osm_obj_warn(o, "Unknown role %s in multipolygon relation", osm_val_decode(f->role));
453               continue;
454             }
455           if ((f->role == VALUE_INNER) != inner)
456             continue;
457           if (o->type == OSM_TYPE_WAY)
458             mpg_insert_way((struct osm_way *) o);
459           else
460             osm_obj_warn(o, "Only ways are supported in multipolygon boundaries");
461         }
462
463       if (debug_dump_multipolygons)
464         mpg_dump();
465
466       mpg_walk_boundary(m, inner);
467       mp_flush(mpg_pool);
468     }
469 }
470
471 void osm_make_multipolygons(void)
472 {
473   uns mpg_cnt = 0;
474
475   CLIST_FOR_EACH(struct osm_relation *, r, osm_this->obj_list[OSM_TYPE_RELATION])
476     if (osm_obj_find_tag(&r->o, KEY_TYPE) == VALUE_MULTIPOLYGON)
477       {
478         osm_multipolygon_make(r);
479         mpg_cnt++;
480       }
481   msg(L_INFO, "Converted %u relations to multipolygons", mpg_cnt);
482 }
483
484 /*** Projection ***/
485
486 static projPJ osm_pj;
487
488 void osm_project(const char *spec)
489 {
490   osm_pj = pj_init_plus(spec);
491   if (!osm_pj)
492     die("Unable to initialize projection %s", spec);
493
494   CLIST_FOR_EACH(struct osm_node *, n, osm_this->obj_list[OSM_TYPE_NODE])
495     {
496       projUV p;
497       p.u = n->x * DEG_TO_RAD;
498       p.v = n->y * DEG_TO_RAD;
499       p = pj_fwd(p, osm_pj);
500       n->x = p.u;
501       n->y = p.v;
502     }
503 }
504
505 /*** Object centers ***/
506
507 bool osm_obj_center(struct osm_object *o, double *xp, double *yp)
508 {
509   switch (o->type)
510     {
511     case OSM_TYPE_NODE:
512       {
513         struct osm_node *n = (struct osm_node *) o;
514         *xp = n->x;
515         *yp = n->y;
516         return 1;
517       }
518     case OSM_TYPE_WAY:
519       {
520         struct osm_way *w = (struct osm_way *) o;
521         double sx = 0, sy = 0;
522         uns nn = 0;
523         OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
524           {
525             sx += n->x;
526             sy += n->y;
527             nn++;
528           }
529         OSM_FOR_EACH_END;
530         if (!nn)
531           return 0;
532         *xp = sx / nn;
533         *yp = sy / nn;
534         return 1;
535       }
536     case OSM_TYPE_MULTIPOLYGON:
537       {
538         struct osm_multipolygon *m = (struct osm_multipolygon *) o;
539         double sx = 0, sy = 0;
540         uns nn = 0;
541         CLIST_FOR_EACH(struct osm_mpg_boundary *, b, m->boundaries)
542           {
543             if (b->inner)
544               continue;
545             OSM_FOR_EACH_BEGIN(struct osm_node *, n, b->nodes)
546               {
547                 sx += n->x;
548                 sy += n->y;
549                 nn++;
550               }
551             OSM_FOR_EACH_END;
552           }
553         if (!nn)
554           return 0;
555         *xp = sx / nn;
556         *yp = sy / nn;
557         return 1;
558       }
559     default:
560       return 0;
561     }
562 }
563
564 /*** Globals ***/
565
566 struct osm *osm_init(void)
567 {
568   struct mempool *mp = mp_new(65536);
569   struct osm *osm = mp_alloc_zero(mp, sizeof(*osm));
570   osm->pool = mp;
571
572   for (int i=0; i<OSM_TYPE_MAX; i++)
573     {
574       clist_init(&osm->obj_list[i]);
575       osm->id_hash[i] = mp_alloc(mp, sizeof(struct osm_id_hash_table));
576       osm->id_hash[i]->pool = mp;
577       osm_id_hash_init(osm->id_hash[i]);
578     }
579
580   if (!osm_this)
581     osm_tag_init();
582   osm_this = osm;
583   return osm;
584 }
585
586 void osm_dump(void)
587 {
588   osm_node_dump_all();
589   osm_way_dump_all();
590   osm_relation_dump_all();
591   osm_multipolygon_dump_all();
592 }
593
594 void osm_stats(void)
595 {
596   msg(L_INFO, "Loaded %u nodes, %u ways, %u relations",
597     osm_this->obj_cnt[OSM_TYPE_NODE],
598     osm_this->obj_cnt[OSM_TYPE_WAY],
599     osm_this->obj_cnt[OSM_TYPE_RELATION]);
600 }