2 * Hic Est Leo -- OSM Data Representation
4 * (c) 2014 Martin Mares <mj@ucw.cz>
10 #include <ucw/mempool.h>
11 #include <ucw/stkstring.h>
12 #include <ucw/strtonum.h>
17 #define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H
24 /*** Generic objects ***/
26 struct osm_id_to_obj {
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",
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>
53 osm_id_t osm_parse_id(const char *str)
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;
63 struct osm_object *osm_obj_find_by_id(enum osm_object_type type, osm_id_t id)
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;
70 static void *osm_obj_new(enum osm_object_type type, osm_id_t id, uns size)
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);
75 die("Id %ju for type %s defined twice", (uintmax_t) id, osm_obj_type_names[type]);
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]++;
83 clist_init(&o->backrefs);
88 void osm_ref_add(struct osm_object *parent, clist *list, struct osm_object *son, osm_val_t role)
90 ASSERT(parent != son);
92 struct osm_ref *ref = mp_alloc(osm_this->pool, sizeof(*ref));
95 clist_add_tail(list, &ref->n);
97 struct osm_ref *backref = mp_alloc(osm_this->pool, sizeof(*ref));
100 clist_add_tail(&son->backrefs, &backref->n);
103 void osm_obj_dump(struct osm_object *o)
108 osm_node_dump((struct osm_node *) o);
111 osm_way_dump((struct osm_way *) o);
113 case OSM_TYPE_RELATION:
114 osm_relation_dump((struct osm_relation *) o);
116 case OSM_TYPE_MULTIPOLYGON:
117 osm_multipolygon_dump((struct osm_multipolygon *) o);
124 void osm_obj_warn(struct osm_object *o, const char *mesg, ...)
127 va_start(args, mesg);
128 msg(L_WARN, "%s: %s", STK_OSM_NAME(o), stk_vprintf(mesg, args));
134 struct dict osm_key_dict, osm_value_dict;
136 void osm_obj_add_tag_raw(struct osm_object *o, osm_key_t key, osm_val_t val)
138 struct osm_tag *t = mp_alloc(osm_this->pool, sizeof(*t));
141 clist_add_tail(&o->tags, &t->n);
144 void osm_obj_add_tag(struct osm_object *o, const char *key, const char *val)
146 osm_obj_add_tag_raw(o, osm_key_encode(key), osm_val_encode(val));
149 void osm_obj_set_tag_raw(struct osm_object *o, osm_key_t key, osm_val_t val)
151 CLIST_FOR_EACH(struct osm_tag *, t, o->tags)
158 osm_obj_add_tag_raw(o, key, val);
161 void osm_obj_set_tag(struct osm_object *o, const char *key, const char *val)
163 osm_obj_set_tag_raw(o, osm_key_encode(key), osm_val_encode(val));
166 osm_val_t osm_obj_find_tag(struct osm_object *o, osm_key_t key)
168 CLIST_FOR_EACH(struct osm_tag *, t, o->tags)
174 void osm_tag_dump(struct osm_tag *t)
176 printf("\t%s = %s\n", osm_key_decode(t->key), osm_val_decode(t->val));
179 static const char * const osm_wk_keys[] = {
181 #define P(x,y) [KEY_##x] = y,
182 #include "dict-keys.h"
187 static const char * const osm_wk_values[] = {
189 #define P(x,y) [VALUE_##x] = y,
190 #include "dict-values.h"
195 static void osm_tag_init(void)
197 dict_init(&osm_key_dict, osm_wk_keys);
198 dict_init(&osm_value_dict, osm_wk_values);
203 struct osm_node *osm_node_new(osm_id_t id)
205 struct osm_node *n = osm_obj_new(OSM_TYPE_NODE, id, sizeof(*n));
209 void osm_node_dump(struct osm_node *n)
211 printf("Node %ju: (%f,%f)\n", (uintmax_t) n->o.id, n->x, n->y);
212 CLIST_FOR_EACH(struct osm_tag *, t, n->o.tags)
216 void osm_node_dump_all(void)
218 printf("Known nodes:\n");
219 CLIST_FOR_EACH(struct osm_node *, n, osm_this->obj_list[OSM_TYPE_NODE])
226 struct osm_way *osm_way_new(osm_id_t id)
228 struct osm_way *w = osm_obj_new(OSM_TYPE_WAY, id, sizeof(*w));
229 clist_init(&w->nodes);
233 void osm_way_add_node(struct osm_way *w, struct osm_node *n)
235 osm_ref_add(&w->o, &w->nodes, &n->o, 0);
238 void osm_way_dump(struct osm_way *w)
240 printf("Way %ju%s\n", (uintmax_t) w->o.id, (osm_way_cyclic_p(w) ? " (cyclic)" : ""));
241 CLIST_FOR_EACH(struct osm_tag *, t, w->o.tags)
243 OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
245 printf("\tNode %ju\n", (uintmax_t) n->o.id);
250 void osm_way_dump_all(void)
252 printf("Known ways:\n");
253 CLIST_FOR_EACH(struct osm_way *, w, osm_this->obj_list[OSM_TYPE_WAY])
258 bool osm_way_cyclic_p(struct osm_way *w)
260 struct osm_ref *first_ref = clist_head(&w->nodes);
261 struct osm_ref *last_ref = clist_tail(&w->nodes);
262 return (first_ref && last_ref &&
263 first_ref != last_ref &&
264 first_ref->o == last_ref->o);
269 struct osm_relation *osm_relation_new(osm_id_t id)
271 struct osm_relation *r = osm_obj_new(OSM_TYPE_RELATION, id, sizeof(*r));
272 clist_init(&r->members);
276 void osm_relation_add_member(struct osm_relation *r, struct osm_object *o, const char *role)
278 osm_ref_add(&r->o, &r->members, o, (role && role[0] ? osm_val_encode(role) : 0));
281 void osm_relation_dump(struct osm_relation *r)
283 printf("Relation %ju\n", (uintmax_t) r->o.id);
284 CLIST_FOR_EACH(struct osm_tag *, t, r->o.tags)
286 CLIST_FOR_EACH(struct osm_ref *, f, r->members)
287 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);
290 void osm_relation_dump_all(void)
292 printf("Known relations:\n");
293 CLIST_FOR_EACH(struct osm_relation *, r, osm_this->obj_list[OSM_TYPE_RELATION])
294 osm_relation_dump(r);
298 /*** Multipolygons ***/
300 void osm_multipolygon_dump(struct osm_multipolygon *m)
302 printf("Multipolygon %ju\n", (uintmax_t) m->o.id);
303 CLIST_FOR_EACH(struct osm_tag *, t, m->o.tags)
305 CLIST_FOR_EACH(struct osm_mpg_boundary *, b, m->boundaries)
307 printf("\tBoundary inner=%u\n", b->inner);
308 CLIST_FOR_EACH(struct osm_ref *, f, b->nodes)
309 printf("\t\tNode %ju\n", (uintmax_t) f->o->id);
313 void osm_multipolygon_dump_all(void)
315 printf("Known multipolygons:\n");
316 CLIST_FOR_EACH(struct osm_multipolygon *, r, osm_this->obj_list[OSM_TYPE_MULTIPOLYGON])
317 osm_multipolygon_dump(r);
321 static struct mempool *mpg_pool;
322 static struct osm_multipolygon *mpg_current;
326 struct osm_object *o;
327 clist edges; // of mpg_edge's
333 struct mpg_edge *twin;
334 struct mpg_vertex *dest;
338 #define HASH_NODE struct mpg_vertex
339 #define HASH_PREFIX(x) mpg_vertex_hash_##x
340 #define HASH_KEY_ATOMIC id
341 #define HASH_ATOMIC_TYPE osm_id_t
342 #define HASH_USE_POOL mpg_pool
343 #define HASH_TABLE_ALLOC
344 #define HASH_WANT_LOOKUP
345 #define HASH_LOOKUP_DETECT_NEW
346 #include <ucw/hashtable.h>
348 static void mpg_insert_way(struct osm_way *w)
350 struct mpg_vertex *prev = NULL;
351 OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
354 struct mpg_vertex *v = mpg_vertex_hash_lookup(n->o.id, &new);
357 clist_init(&v->edges);
363 struct mpg_edge *e1 = mp_alloc_zero(mpg_pool, sizeof(*e1));
364 struct mpg_edge *e2 = mp_alloc_zero(mpg_pool, sizeof(*e2));
367 clist_add_tail(&prev->edges, &e1->n);
370 clist_add_tail(&v->edges, &e2->n);
377 static void UNUSED mpg_dump(void)
379 printf("=== Multipolygon graph for relation %ju ===\n", (uintmax_t) mpg_current->o.id);
380 HASH_FOR_ALL(mpg_vertex_hash, v)
382 printf("Vertex %ju\n", (uintmax_t) v->id);
383 CLIST_FOR_EACH(struct mpg_edge *, e, v->edges)
384 printf("\t-> %ju\n", (uintmax_t) e->dest->id);
390 static void mpg_walk_boundary(struct osm_multipolygon *m, bool inner)
392 // FIXME: Replace by a better algorithm, which respects geometry
393 // and is able to cope with higher degree vertices.
394 HASH_FOR_ALL(mpg_vertex_hash, v)
398 struct osm_mpg_boundary *b = mp_alloc(osm_this->pool, sizeof(*b));
399 clist_add_tail(&m->boundaries, &b->n);
401 clist_init(&b->nodes);
403 struct mpg_vertex *w = v;
404 while (w && !w->visited)
407 struct osm_ref *f = mp_alloc(osm_this->pool, sizeof(*f));
408 clist_add_tail(&b->nodes, &f->n);
412 struct mpg_vertex *dest = NULL;
413 CLIST_FOR_EACH(struct mpg_edge *, e, w->edges)
420 osm_obj_warn(&mpg_current->o, "Suspicious vertex degree of node %ju", (uintmax_t) w->o->id);
435 osm_obj_warn(&mpg_current->o, "Boundary not closed");
437 osm_obj_warn(&mpg_current->o, "Boundary not closed at node %ju", (uintmax_t) w->o->id);
439 struct osm_ref *f = mp_alloc(osm_this->pool, sizeof(*f));
440 clist_add_tail(&b->nodes, &f->n);
448 static void osm_multipolygon_make(struct osm_relation *r)
450 struct osm_multipolygon *m = osm_obj_new(OSM_TYPE_MULTIPOLYGON, r->o.id, sizeof(*m));
453 CLIST_FOR_EACH(struct osm_tag *, t, r->o.tags)
454 osm_obj_add_tag_raw(&m->o, t->key, t->val);
455 clist_init(&m->boundaries);
457 for (int inner=0; inner<2; inner++)
460 mpg_pool = mp_new(4096);
461 mpg_vertex_hash_init();
463 CLIST_FOR_EACH(struct osm_ref *, f, r->members)
465 struct osm_object *o = f->o;
466 if (f->role != VALUE_INNER && f->role != VALUE_OUTER)
469 osm_obj_warn(o, "Unknown role %s in multipolygon relation", f->role ? osm_val_decode(f->role) : "<none>");
472 if ((f->role == VALUE_INNER) != inner)
474 if (o->type == OSM_TYPE_WAY)
475 mpg_insert_way((struct osm_way *) o);
477 osm_obj_warn(o, "Only ways are supported in multipolygon boundaries");
480 if (debug_dump_multipolygons)
483 mpg_walk_boundary(m, inner);
488 void osm_make_multipolygons(void)
492 CLIST_FOR_EACH(struct osm_relation *, r, osm_this->obj_list[OSM_TYPE_RELATION])
493 if (osm_obj_find_tag(&r->o, KEY_TYPE) == VALUE_MULTIPOLYGON)
495 osm_multipolygon_make(r);
498 msg(L_INFO, "Converted %u relations to multipolygons", mpg_cnt);
503 static projPJ osm_pj;
505 void osm_project(const char *spec)
507 osm_pj = pj_init_plus(spec);
509 die("Unable to initialize projection %s", spec);
511 CLIST_FOR_EACH(struct osm_node *, n, osm_this->obj_list[OSM_TYPE_NODE])
514 p.u = n->x * DEG_TO_RAD;
515 p.v = n->y * DEG_TO_RAD;
516 p = pj_fwd(p, osm_pj);
522 /*** Object centers ***/
524 bool osm_obj_center(struct osm_object *o, double *xp, double *yp)
530 struct osm_node *n = (struct osm_node *) o;
537 struct osm_way *w = (struct osm_way *) o;
538 double sx = 0, sy = 0;
540 OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
553 case OSM_TYPE_MULTIPOLYGON:
555 struct osm_multipolygon *m = (struct osm_multipolygon *) o;
556 double sx = 0, sy = 0;
558 CLIST_FOR_EACH(struct osm_mpg_boundary *, b, m->boundaries)
562 OSM_FOR_EACH_BEGIN(struct osm_node *, n, b->nodes)
583 struct osm *osm_init(void)
585 struct mempool *mp = mp_new(65536);
586 struct osm *osm = mp_alloc_zero(mp, sizeof(*osm));
589 for (int i=0; i<OSM_TYPE_MAX; i++)
591 clist_init(&osm->obj_list[i]);
592 osm->id_hash[i] = mp_alloc(mp, sizeof(struct osm_id_hash_table));
593 osm->id_hash[i]->pool = mp;
594 osm_id_hash_init(osm->id_hash[i]);
607 osm_relation_dump_all();
608 osm_multipolygon_dump_all();
613 msg(L_INFO, "Loaded %u nodes, %u ways, %u relations",
614 osm_this->obj_cnt[OSM_TYPE_NODE],
615 osm_this->obj_cnt[OSM_TYPE_WAY],
616 osm_this->obj_cnt[OSM_TYPE_RELATION]);