summaryrefslogtreecommitdiff
path: root/navit/maptool/osm_relations.c
blob: a6084f21ebc5bb7101ec9e9ef39337497a0c79ed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/**
 * Navit, a modular navigation system.
 * Copyright (C) 2005-2011 Navit Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */
#include <stdio.h>
#include <string.h>
#include "maptool.h"
#include "attr.h"

/** Information about all members of a relation type and how to process them. */
struct relations {
    /** Hashes for nodes, ways and relations which are members. */
    GHashTable *member_hash[3];
    /** Default entries for processing items which are not a member of any relation. */
    GList *default_members;
};

struct relations_func {
    void (*func)(void *func_priv, void *relation_priv, struct item_bin *member, void *member_priv);
    void *func_priv;
};

struct relations_member {
    osmid memberid;
    void *relation_priv,*member_priv;
    struct relations_func *func;
};

static guint relations_member_hash(gconstpointer key) {
    const struct relations_member *memb=key;
    return (memb->memberid >> 32)^(memb->memberid & 0xffffffff);
}

static gboolean relations_member_equal(gconstpointer a, gconstpointer b) {
    const struct relations_member *memba=a;
    const struct relations_member *membb=b;
    return (memba->memberid == membb->memberid);
}

struct relations *
relations_new(void) {
    struct relations *ret=g_new0(struct relations, 1);
    int i;

    for (i = 0 ; i < 3 ; i++)
        ret->member_hash[i]=g_hash_table_new(relations_member_hash, relations_member_equal);
    return ret;
}

struct relations_func *
relations_func_new(void (*func)(void *func_priv, void *relation_priv, struct item_bin *member, void *member_priv),
                   void *func_priv) {
    struct relations_func *relations_func=g_new(struct relations_func, 1);
    relations_func->func=func;
    relations_func->func_priv=func_priv;
    return relations_func;
}

static struct relations_member *relations_member_new(struct relations_func *func, void *relation_priv,
        void *member_priv, osmid id) {
    struct relations_member *memb=g_new(struct relations_member, 1);
    memb->memberid=id;
    memb->relation_priv=relation_priv;
    memb->member_priv=member_priv;
    memb->func=func;
    return memb;
}
/*
 * @brief Add an entry for a relation member to the relations collection.
 * This function fills the relations collection, which is then passed to relations_process for
 * processing.
 * @param in rel relations collection to add the new member to
 * @param in func structure defining function to call when this member is read
 * @param in relation_priv parameter describing relation, or NULL. Will be passed to func function.
 * @param in member_priv parameter describing relation member, or NULL. Will be passed to func function.
 * @param in type Type of this member (node, way etc.).
 * @param in id OSM ID of relation member.
 */
void relations_add_relation_member_entry(struct relations *rel, struct relations_func *func, void
        *relation_priv, void *member_priv, enum relation_member_type type, osmid id) {
    struct relations_member *memb=relations_member_new(func, relation_priv, member_priv, id);
    GHashTable *member_hash=rel->member_hash[type-1];
    // The real key is the OSM ID, but we recycle "memb" as key to avoid a second allocating for the key.
    g_hash_table_insert(member_hash, memb, g_list_append(g_hash_table_lookup(member_hash, memb), memb));
}

/*
 * @brief Add a default entry to the relations collection.
 * Put a default entry into the relations collection, which is then passed to
 * relations_process for processing. The default entry is used for map items which are not a
 * member of any relation.
 * @param in rel relations collection to add the new member to
 * @param in func structure defining function to call when this member is read
 */
void relations_add_relation_default_entry(struct relations *rel, struct relations_func *func) {
    struct relations_member *memb=relations_member_new(func, NULL, NULL, 0);
    rel->default_members=g_list_append(rel->default_members, memb);
}


/*
 * @brief The actual relations processing: Loop through raw data and process any relations members.
 * This function reads through all nodes and ways passed in, and looks up each item in the
 * relations collection. For each relation member found, its processing function is called.
 * @param in rel relations collection storing pre-processed relations. Built using relations_add_relation_member_entry.
 * @param in nodes file containing nodes in "coords.tmp" format
 * @param in ways file containing items in item_bin format. This file may contain both nodes, ways, and relations in that format.
 */
void relations_process(struct relations *rel, FILE *nodes, FILE *ways) {
    char buffer[128];
    struct item_bin *ib=(struct item_bin *)buffer;
    osmid *id;
    struct coord *c=(struct coord *)(ib+1),cn= {0,0};
    struct node_item *ni;
    GList *l;

    if (nodes) {
        item_bin_init(ib, type_point_unkn);
        item_bin_add_coord(ib, &cn, 1);
        item_bin_add_attr_longlong(ib, attr_osm_nodeid, 0);
        id=item_bin_get_attr(ib, attr_osm_nodeid, NULL);
        while ((ni=read_node_item(nodes))) {
            *id=ni->nd_id;
            *c=ni->c;
            l=g_hash_table_lookup(rel->member_hash[0], id);
            while (l) {
                struct relations_member *memb=l->data;
                memb->func->func(memb->func->func_priv, memb->relation_priv, ib, memb->member_priv);
                l=g_list_next(l);
            }
        }
    }
    if (ways) {
        while ((ib=read_item(ways))) {
            l=NULL;
            if(NULL!=(id=item_bin_get_attr(ib, attr_osm_nodeid, NULL)))
                l=g_hash_table_lookup(rel->member_hash[0], id);
            else if(NULL!=(id=item_bin_get_attr(ib, attr_osm_wayid, NULL)))
                l=g_hash_table_lookup(rel->member_hash[1], id);
            else if(NULL!=(id=item_bin_get_attr(ib, attr_osm_relationid, NULL)))
                l=g_hash_table_lookup(rel->member_hash[2], id);
            if(!l)
                l=rel->default_members;
            while (l) {
                struct relations_member *memb=l->data;
                memb->func->func(memb->func->func_priv, memb->relation_priv, ib, memb->member_priv);
                l=g_list_next(l);
            }
        }
    }
}

static void relations_destroy_func(void *key, GList *l, void *data) {
    GList *ll=l;
    while (ll) {
        g_free(ll->data);
        ll=g_list_next(ll);
    }
    g_list_free(l);
}

void relations_destroy(struct relations *relations) {
    int i;

    for (i = 0 ; i < 3 ; i++) {
        g_hash_table_foreach(relations->member_hash[i], (GHFunc)relations_destroy_func, NULL);
        g_hash_table_destroy(relations->member_hash[i]);
    }
    g_free(relations);
}