1 #include "catch.hpp"
2 
3 #include "utils.hpp"
4 
5 #include <osmium/io/xml_input.hpp>
6 #include <osmium/osm/relation.hpp>
7 #include <osmium/relations/relations_manager.hpp>
8 
9 #include <iterator>
10 
11 struct EmptyRM : public osmium::relations::RelationsManager<EmptyRM, true, true, true> {
12 };
13 
14 struct TestRM : public osmium::relations::RelationsManager<TestRM, true, true, true> {
15 
16     std::size_t count_new_rels      = 0;
17     std::size_t count_new_members   = 0;
18     std::size_t count_complete_rels = 0;
19     std::size_t count_before        = 0;
20     std::size_t count_not_in_any    = 0;
21     std::size_t count_after         = 0;
22 
new_relationTestRM23     bool new_relation(const osmium::Relation& /*relation*/) noexcept {
24         ++count_new_rels;
25         return true;
26     }
27 
new_memberTestRM28     bool new_member(const osmium::Relation& /*relation*/, const osmium::RelationMember& /*member*/, std::size_t /*n*/) noexcept {
29         ++count_new_members;
30         return true;
31     }
32 
complete_relationTestRM33     void complete_relation(const osmium::Relation& /*relation*/) noexcept {
34         ++count_complete_rels;
35     }
36 
before_nodeTestRM37     void before_node(const osmium::Node& /*node*/) noexcept {
38         ++count_before;
39     }
40 
node_not_in_any_relationTestRM41     void node_not_in_any_relation(const osmium::Node& /*node*/) noexcept {
42         ++count_not_in_any;
43     }
44 
after_nodeTestRM45     void after_node(const osmium::Node& /*node*/) noexcept {
46         ++count_after;
47     }
48 
before_wayTestRM49     void before_way(const osmium::Way& /*way*/) noexcept {
50         ++count_before;
51     }
52 
way_not_in_any_relationTestRM53     void way_not_in_any_relation(const osmium::Way& /*way*/) noexcept {
54         ++count_not_in_any;
55     }
56 
after_wayTestRM57     void after_way(const osmium::Way& /*way*/) noexcept {
58         ++count_after;
59     }
60 
before_relationTestRM61     void before_relation(const osmium::Relation& /*relation*/) noexcept {
62         ++count_before;
63     }
64 
relation_not_in_any_relationTestRM65     void relation_not_in_any_relation(const osmium::Relation& /*relation*/) noexcept {
66         ++count_not_in_any;
67     }
68 
after_relationTestRM69     void after_relation(const osmium::Relation& /*relation*/) noexcept {
70         ++count_after;
71     }
72 
73 };
74 
75 struct CallbackRM : public osmium::relations::RelationsManager<CallbackRM, true, false, false> {
76 
77     std::size_t count_nodes = 0;
78 
new_relationCallbackRM79     static bool new_relation(const osmium::Relation& /*relation*/) noexcept {
80         return true;
81     }
82 
new_memberCallbackRM83     static bool new_member(const osmium::Relation& /*relation*/, const osmium::RelationMember& member, std::size_t /*n*/) noexcept {
84         return member.type() == osmium::item_type::node;
85     }
86 
complete_relationCallbackRM87     void complete_relation(const osmium::Relation& relation) {
88         for (const auto& member : relation.members()) {
89             if (member.type() == osmium::item_type::node) {
90                 ++count_nodes;
91                 const auto* node = get_member_node(member.ref());
92                 REQUIRE(node);
93                 buffer().add_item(*node);
94                 buffer().commit();
95             }
96         }
97     }
98 
99 };
100 
101 struct AnyRM : public osmium::relations::RelationsManager<AnyRM, true, true, true> {
new_relationAnyRM102     static bool new_relation(const osmium::Relation& /*relation*/) noexcept {
103         return true;
104     }
105 
new_memberAnyRM106     static bool new_member(const osmium::Relation& /*relation*/, const osmium::RelationMember& /*member*/, std::size_t /*n*/) noexcept {
107         return true;
108     }
109 };
110 
111 TEST_CASE("Use RelationsManager without any overloaded functions in derived class") {
112     osmium::io::File file{with_data_dir("t/relations/data.osm")};
113 
114     EmptyRM manager;
115 
116     osmium::relations::read_relations(file, manager);
117 
118     REQUIRE(manager.member_nodes_database().size()     == 2);
119     REQUIRE(manager.member_ways_database().size()      == 2);
120     REQUIRE(manager.member_relations_database().size() == 1);
121 
122     REQUIRE(manager.member_database(osmium::item_type::node).size()     == 2);
123     REQUIRE(manager.member_database(osmium::item_type::way).size()      == 2);
124     REQUIRE(manager.member_database(osmium::item_type::relation).size() == 1);
125 
126     const auto& m = manager;
127     REQUIRE(m.member_database(osmium::item_type::node).size()     == 2);
128     REQUIRE(m.member_database(osmium::item_type::way).size()      == 2);
129     REQUIRE(m.member_database(osmium::item_type::relation).size() == 1);
130 
131     osmium::io::Reader reader{file};
132     osmium::apply(reader, manager.handler());
133     reader.close();
134 }
135 
136 TEST_CASE("Relations manager derived class") {
137     osmium::io::File file{with_data_dir("t/relations/data.osm")};
138 
139     TestRM manager;
140 
141     osmium::relations::read_relations(file, manager);
142 
143     REQUIRE(manager.member_nodes_database().size()     == 2);
144     REQUIRE(manager.member_ways_database().size()      == 2);
145     REQUIRE(manager.member_relations_database().size() == 1);
146 
147     bool callback_called = false;
148     osmium::io::Reader reader{file};
__anon978e35a40102(osmium::memory::Buffer&& ) 149     osmium::apply(reader, manager.handler([&](osmium::memory::Buffer&& /*unused*/) {
150         callback_called = true;
151     }));
152     reader.close();
153     REQUIRE_FALSE(callback_called);
154 
155     REQUIRE(manager.count_new_rels      ==  3);
156     REQUIRE(manager.count_new_members   ==  5);
157     REQUIRE(manager.count_complete_rels ==  2);
158     REQUIRE(manager.count_before        == 10);
159     REQUIRE(manager.count_not_in_any    ==  6);
160     REQUIRE(manager.count_after         == 10);
161 
162     int n = 0;
__anon978e35a40202(const osmium::relations::RelationHandle& handle)163     manager.for_each_incomplete_relation([&](const osmium::relations::RelationHandle& handle){
164         ++n;
165         REQUIRE(handle->id() == 31);
166         for (const auto& member : handle->members()) {
167             const auto* obj = manager.get_member_object(member);
168             if (member.ref() == 22) {
169                 REQUIRE_FALSE(obj);
170             } else {
171                 REQUIRE(obj);
172             }
173         }
174     });
175     REQUIRE(n == 1);
176 }
177 
178 TEST_CASE("Relations manager with callback") {
179     osmium::io::File file{with_data_dir("t/relations/data.osm")};
180 
181     CallbackRM manager;
182 
183     osmium::relations::read_relations(file, manager);
184 
185     REQUIRE(manager.member_nodes_database().size()     == 2);
186     REQUIRE(manager.member_ways_database().size()      == 0);
187     REQUIRE(manager.member_relations_database().size() == 0);
188 
189     bool callback_called = false;
190     osmium::io::Reader reader{file};
__anon978e35a40302(osmium::memory::Buffer&& buffer) 191     osmium::apply(reader, manager.handler([&](osmium::memory::Buffer&& buffer) {
192         callback_called = true;
193         REQUIRE(std::distance(buffer.begin(), buffer.end()) == 2);
194     }));
195     reader.close();
196     REQUIRE(manager.count_nodes == 2);
197     REQUIRE(callback_called);
198 }
199 
200 TEST_CASE("Relations manager reading buffer without callback") {
201     osmium::io::File file{with_data_dir("t/relations/data.osm")};
202 
203     CallbackRM manager;
204 
205     osmium::relations::read_relations(file, manager);
206 
207     REQUIRE(manager.member_nodes_database().size()     == 2);
208     REQUIRE(manager.member_ways_database().size()      == 0);
209     REQUIRE(manager.member_relations_database().size() == 0);
210 
211     osmium::io::Reader reader{file};
212     osmium::apply(reader, manager.handler());
213     reader.close();
214 
215     auto buffer = manager.read();
216     REQUIRE(std::distance(buffer.begin(), buffer.end()) == 2);
217 
218     REQUIRE(manager.count_nodes == 2);
219 }
220 
221 TEST_CASE("Access members via RelationsManager") {
222     EmptyRM manager;
223 
224     manager.prepare_for_lookup();
225 
226     REQUIRE(nullptr == manager.get_member_node(0));
227     REQUIRE(nullptr == manager.get_member_way(0));
228     REQUIRE(nullptr == manager.get_member_relation(0));
229 
230     REQUIRE(nullptr == manager.get_member_node(17));
231     REQUIRE(nullptr == manager.get_member_way(17));
232     REQUIRE(nullptr == manager.get_member_relation(17));
233 }
234 
235 TEST_CASE("Handle duplicate members correctly") {
236     osmium::io::File file{with_data_dir("t/relations/dupl_member.osm")};
237 
238     TestRM manager;
239 
240     osmium::relations::read_relations(file, manager);
241 
242     auto c = manager.member_nodes_database().count();
243     REQUIRE(c.tracked   == 5);
244     REQUIRE(c.available == 0);
245     REQUIRE(c.removed   == 0);
246 
247     osmium::io::Reader reader{file};
248     osmium::apply(reader, manager.handler());
249     reader.close();
250 
251     c = manager.member_nodes_database().count();
252     REQUIRE(c.tracked   == 0);
253     REQUIRE(c.available == 0);
254     REQUIRE(c.removed   == 5);
255 
256     REQUIRE(manager.count_new_rels      == 2);
257     REQUIRE(manager.count_new_members   == 5);
258     REQUIRE(manager.count_complete_rels == 2);
259     REQUIRE(manager.count_not_in_any    == 2); // 2 relations
260 }
261 
262 TEST_CASE("Check handling of missing members") {
263     osmium::io::File file{with_data_dir("t/relations/missing_members.osm")};
264 
265     AnyRM manager;
266 
267     osmium::relations::read_relations(file, manager);
268 
269     osmium::io::Reader reader{file};
270     osmium::apply(reader, manager.handler());
271     reader.close();
272 
273 
274     size_t nodes = 0;
275     size_t ways = 0;
276     size_t relations = 0;
277     size_t missing_nodes = 0;
278     size_t missing_ways = 0;
279     size_t missing_relations = 0;
280 
__anon978e35a40402(const osmium::relations::RelationHandle& handle)281     manager.for_each_incomplete_relation([&](const osmium::relations::RelationHandle& handle){
282         if (handle->id() != 31) {
283             // count relation 31 only
284             return;
285         }
286         for (const auto& member : handle->members()) {
287             // RelationMember::ref() is supposed to returns 0 if we are interested in the member.
288             // RelationsManagerBase::get_member_object() is supposed to return a nullptr if the
289             // member is not available (missing in the input file).
290             const osmium::OSMObject* object = manager.get_member_object(member);
291             switch (member.type()) {
292             case osmium::item_type::node :
293                 ++nodes;
294                 if (member.ref() != 0 && !object) {
295                     ++missing_nodes;
296                 }
297                 break;
298             case osmium::item_type::way :
299                 ++ways;
300                 if (member.ref() != 0 && !object) {
301                     ++missing_ways;
302                 }
303                 break;
304             case osmium::item_type::relation :
305                 ++relations;
306                 if (member.ref() != 0 && !object) {
307                     ++missing_relations;
308                 }
309                 break;
310             default:
311                 break;
312             }
313         }
314     });
315     REQUIRE(nodes == 2);
316     REQUIRE(ways == 3);
317     REQUIRE(relations == 3);
318     REQUIRE(missing_nodes == 1);
319     REQUIRE(missing_ways == 1);
320     REQUIRE(missing_relations == 2);
321 }
322 
323