1 #ifndef OSMIUM_RELATIONS_RELATIONS_MANAGER_HPP
2 #define OSMIUM_RELATIONS_RELATIONS_MANAGER_HPP
3 
4 /*
5 
6 This file is part of Osmium (https://osmcode.org/libosmium).
7 
8 Copyright 2013-2021 Jochen Topf <jochen@topf.org> and others (see README).
9 
10 Boost Software License - Version 1.0 - August 17th, 2003
11 
12 Permission is hereby granted, free of charge, to any person or organization
13 obtaining a copy of the software and accompanying documentation covered by
14 this license (the "Software") to use, reproduce, display, distribute,
15 execute, and transmit the Software, and to prepare derivative works of the
16 Software, and to permit third-parties to whom the Software is furnished to
17 do so, all subject to the following:
18 
19 The copyright notices in the Software and this entire statement, including
20 the above license grant, this restriction and the following disclaimer,
21 must be included in all copies of the Software, in whole or in part, and
22 all derivative works of the Software, unless such copies or derivative
23 works are solely in the form of machine-executable object code generated by
24 a source language processor.
25 
26 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
29 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
30 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
31 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32 DEALINGS IN THE SOFTWARE.
33 
34 */
35 
36 #include <osmium/handler.hpp>
37 #include <osmium/handler/check_order.hpp>
38 #include <osmium/memory/buffer.hpp>
39 #include <osmium/memory/callback_buffer.hpp>
40 #include <osmium/osm/item_type.hpp>
41 #include <osmium/osm/relation.hpp>
42 #include <osmium/osm/tag.hpp>
43 #include <osmium/osm/way.hpp>
44 #include <osmium/relations/manager_util.hpp>
45 #include <osmium/relations/members_database.hpp>
46 #include <osmium/relations/relations_database.hpp>
47 #include <osmium/storage/item_stash.hpp>
48 #include <osmium/tags/taglist.hpp>
49 #include <osmium/tags/tags_filter.hpp>
50 
51 #include <algorithm>
52 #include <cassert>
53 #include <cstddef>
54 #include <cstdint>
55 #include <cstring>
56 #include <stdexcept>
57 #include <type_traits>
58 #include <vector>
59 
60 namespace osmium {
61 
62     namespace relations {
63 
64         /**
65          * This is a base class of the RelationsManager class template. It
66          * contains databases for the relations and the members that we need
67          * to keep track of and handles the ouput buffer. Unlike the
68          * RelationsManager class template this is a plain class.
69          *
70          * Usually it is better to use the RelationsManager class template
71          * as a basis for your code, but you can also use this class if you
72          * have special needs.
73          */
74         class RelationsManagerBase : public osmium::handler::Handler {
75 
76             // All relations and members we are interested in will be kept
77             // in here.
78             osmium::ItemStash m_stash{};
79 
80             /// Database of all relations we are interested in.
81             relations::RelationsDatabase m_relations_db;
82 
83             /// Databases of all members we are interested in.
84             relations::MembersDatabase<osmium::Node>     m_member_nodes_db;
85             relations::MembersDatabase<osmium::Way>      m_member_ways_db;
86             relations::MembersDatabase<osmium::Relation> m_member_relations_db;
87 
88             /// Output buffer.
89             osmium::memory::CallbackBuffer m_output{};
90 
91         public:
92 
RelationsManagerBase()93             RelationsManagerBase() :
94                 m_relations_db(m_stash),
95                 m_member_nodes_db(m_stash, m_relations_db),
96                 m_member_ways_db(m_stash, m_relations_db),
97                 m_member_relations_db(m_stash, m_relations_db) {
98             }
99 
100             /// Access the internal RelationsDatabase.
relations_database()101             osmium::relations::RelationsDatabase& relations_database() noexcept {
102                 return m_relations_db;
103             }
104 
105             /// Access the internal database containing member nodes.
member_nodes_database()106             osmium::relations::MembersDatabase<osmium::Node>& member_nodes_database() noexcept {
107                 return m_member_nodes_db;
108             }
109 
110             /// Access the internal database containing member nodes.
member_nodes_database() const111             const osmium::relations::MembersDatabase<osmium::Node>& member_nodes_database() const noexcept {
112                 return m_member_nodes_db;
113             }
114 
115             /// Access the internal database containing member ways.
member_ways_database()116             osmium::relations::MembersDatabase<osmium::Way>& member_ways_database() noexcept {
117                 return m_member_ways_db;
118             }
119 
120             /// Access the internal database containing member ways.
member_ways_database() const121             const osmium::relations::MembersDatabase<osmium::Way>& member_ways_database() const noexcept {
122                 return m_member_ways_db;
123             }
124 
125             /// Access the internal database containing member relations.
member_relations_database()126             osmium::relations::MembersDatabase<osmium::Relation>& member_relations_database() noexcept {
127                 return m_member_relations_db;
128             }
129 
130             /// Access the internal database containing member relations.
member_relations_database() const131             const osmium::relations::MembersDatabase<osmium::Relation>& member_relations_database() const noexcept {
132                 return m_member_relations_db;
133             }
134 
135             /**
136              * Access the internal database containing members of the
137              * specified type (non-const version of this function).
138              *
139              * @param type osmium::item_type::node, way, or relation.
140              */
member_database(osmium::item_type type)141             relations::MembersDatabaseCommon& member_database(osmium::item_type type) noexcept {
142                 switch (type) {
143                     case osmium::item_type::node:
144                         return m_member_nodes_db;
145                     case osmium::item_type::way:
146                         return m_member_ways_db;
147                     case osmium::item_type::relation:
148                         return m_member_relations_db;
149                     default:
150                         break;
151                 }
152 
153                 assert(false && "Should not be here");
154                 return m_member_nodes_db;
155             }
156 
157             /**
158              * Access the internal database containing members of the
159              * specified type (const version of this function).
160              *
161              * @param type osmium::item_type::node, way, or relation.
162              */
member_database(osmium::item_type type) const163             const relations::MembersDatabaseCommon& member_database(osmium::item_type type) const noexcept {
164                 switch (type) {
165                     case osmium::item_type::node:
166                         return m_member_nodes_db;
167                     case osmium::item_type::way:
168                         return m_member_ways_db;
169                     case osmium::item_type::relation:
170                         return m_member_relations_db;
171                     default:
172                         break;
173                 }
174 
175                 assert(false && "Should not be here");
176                 return m_member_nodes_db;
177             }
178 
179             /**
180              * Get member object from relation member.
181              *
182              * @returns A pointer to the member object if it is available.
183              *          Returns nullptr otherwise.
184              */
get_member_object(const osmium::RelationMember & member) const185             const osmium::OSMObject* get_member_object(const osmium::RelationMember& member) const noexcept {
186                 if (member.ref() == 0) {
187                     return nullptr;
188                 }
189                 return member_database(member.type()).get_object(member.ref());
190             }
191 
192             /**
193              * Get node with specified ID from members database.
194              *
195              * @param id The node ID we are looking for.
196              * @returns A pointer to the member node if it is available.
197              *          Returns nullptr otherwise.
198              */
get_member_node(osmium::object_id_type id) const199             const osmium::Node* get_member_node(osmium::object_id_type id) const noexcept {
200                 if (id == 0) {
201                     return nullptr;
202                 }
203                 return member_nodes_database().get(id);
204             }
205 
206             /**
207              * Get way with specified ID from members database.
208              *
209              * @param id The way ID we are looking for.
210              * @returns A pointer to the member way if it is available.
211              *          Returns nullptr otherwise.
212              */
get_member_way(osmium::object_id_type id) const213             const osmium::Way* get_member_way(osmium::object_id_type id) const noexcept {
214                 if (id == 0) {
215                     return nullptr;
216                 }
217                 return member_ways_database().get(id);
218             }
219 
220             /**
221              * Get relation with specified ID from members database.
222              *
223              * @param id The relation ID we are looking for.
224              * @returns A pointer to the member relation if it is available.
225              *          Returns nullptr otherwise.
226              */
get_member_relation(osmium::object_id_type id) const227             const osmium::Relation* get_member_relation(osmium::object_id_type id) const noexcept {
228                 if (id == 0) {
229                     return nullptr;
230                 }
231                 return member_relations_database().get(id);
232             }
233 
234             /**
235              * Sort the members databases to prepare them for reading. Usually
236              * this is called between the first and second pass reading through
237              * an OSM data file.
238              */
prepare_for_lookup()239             void prepare_for_lookup() {
240                 m_member_nodes_db.prepare_for_lookup();
241                 m_member_ways_db.prepare_for_lookup();
242                 m_member_relations_db.prepare_for_lookup();
243             }
244 
245             /**
246              * Return the memory used by different components of the manager.
247              */
used_memory() const248             relations_manager_memory_usage used_memory() const noexcept {
249                 return {
250                     m_relations_db.used_memory(),
251                       m_member_nodes_db.used_memory()
252                     + m_member_ways_db.used_memory()
253                     + m_member_relations_db.used_memory(),
254                     m_stash.used_memory()
255                 };
256             }
257 
258             /// Access the output buffer.
buffer()259             osmium::memory::Buffer& buffer() noexcept {
260                 return m_output.buffer();
261             }
262 
263             /// Set the callback called when the output buffer is full.
set_callback(const std::function<void (osmium::memory::Buffer &&)> & callback)264             void set_callback(const std::function<void(osmium::memory::Buffer&&)>& callback) {
265                 m_output.set_callback(callback);
266             }
267 
268             /// Flush the output buffer.
flush_output()269             void flush_output() {
270                 m_output.flush();
271             }
272 
273             /// Flush the output buffer if it is full.
possibly_flush()274             void possibly_flush() {
275                 m_output.possibly_flush();
276             }
277 
278             /// Return the contents of the output buffer.
read()279             osmium::memory::Buffer read() {
280                 return m_output.read();
281             }
282 
283         }; // class RelationsManagerBase
284 
285         /**
286          * This is a base class for RelationManager classes. It keeps track of
287          * all interesting relations and all interesting members of those
288          * relations. When all members are available it calls code to handle
289          * the completed relation.
290          *
291          * This class is intended as a base class for classes that handle
292          * specific types of relations. Derive from this class, implement
293          * the complete_relation() function and overwrite certain other
294          * functions as needed.
295          *
296          * @tparam TManager The derived class (Uses CRTP).
297          * @tparam TNodes Are we interested in member nodes?
298          * @tparam TWays Are we interested in member ways?
299          * @tparam TRelations Are we interested in member relations?
300          * @tparam TCheckOrder Should the order of the input data be checked?
301          *
302          * @pre The Ids of all objects must be unique in the input data.
303          */
304         template <typename TManager, bool TNodes, bool TWays, bool TRelations, bool TCheckOrder = true>
305         class RelationsManager : public RelationsManagerBase {
306 
307             using check_order_handler = typename std::conditional<TCheckOrder, osmium::handler::CheckOrder, osmium::handler::Handler>::type;
308 
309             check_order_handler m_check_order_handler;
310 
311             SecondPassHandler<RelationsManager> m_handler_pass2;
312 
wanted_type(osmium::item_type type)313             static bool wanted_type(osmium::item_type type) noexcept {
314                 return (TNodes     && type == osmium::item_type::node) ||
315                        (TWays      && type == osmium::item_type::way) ||
316                        (TRelations && type == osmium::item_type::relation);
317             }
318 
319             /**
320              * This method is called from the first pass handler for every
321              * relation in the input, to check whether it should be kept.
322              *
323              * Overwrite this method in a derived class to only add relations
324              * you are interested in, for instance depending on the type tag.
325              * Storing relations takes a lot of memory, so it makes sense to
326              * filter this as much as possible.
327              */
new_relation(const osmium::Relation &) const328             bool new_relation(const osmium::Relation& /*relation*/) const noexcept {
329                 return true;
330             }
331 
332             /**
333              * This method is called for every member of every relation that
334              * will be kept. It should decide if the member is interesting or
335              * not and return true or false to signal that. Only interesting
336              * members are later added to the relation.
337              *
338              * Overwrite this method in a derived class. In the
339              * MultipolygonManager class this is used for instance to only
340              * keep members of type way and ignore all others.
341              */
new_member(const osmium::Relation &,const osmium::RelationMember &,std::size_t) const342             bool new_member(const osmium::Relation& /*relation*/, const osmium::RelationMember& /*member*/, std::size_t /*n*/) const noexcept {
343                 return true;
344             }
345 
346             /**
347              * This method is called for each complete relation, ie when
348              * all members you have expressed interest in are available.
349              *
350              * You have to overwrite this in a derived class.
351              */
complete_relation(const osmium::Relation &) const352             void complete_relation(const osmium::Relation& /*relation*/) const noexcept {
353             }
354 
355             /**
356              * This method is called for all nodes during the second pass
357              * before the relation member handling.
358              *
359              * Overwrite this method in a derived class if you are interested
360              * in this.
361              */
before_node(const osmium::Node &) const362             void before_node(const osmium::Node& /*node*/) const noexcept {
363             }
364 
365             /**
366              * This method is called for all nodes that are not a member of
367              * any relation.
368              *
369              * Overwrite this method in a derived class if you are interested
370              * in this.
371              */
node_not_in_any_relation(const osmium::Node &) const372             void node_not_in_any_relation(const osmium::Node& /*node*/) const noexcept {
373             }
374 
375             /**
376              * This method is called for all nodes during the second pass
377              * after the relation member handling.
378              *
379              * Overwrite this method in a derived class if you are interested
380              * in this.
381              */
after_node(const osmium::Node &) const382             void after_node(const osmium::Node& /*node*/) const noexcept {
383             }
384 
385             /**
386              * This method is called for all ways during the second pass
387              * before the relation member handling.
388              *
389              * Overwrite this method in a derived class if you are interested
390              * in this.
391              */
before_way(const osmium::Way &) const392             void before_way(const osmium::Way& /*way*/) const noexcept {
393             }
394 
395             /**
396              * This method is called for all ways that are not a member of
397              * any relation.
398              *
399              * Overwrite this method in a derived class if you are interested
400              * in this.
401              */
way_not_in_any_relation(const osmium::Way &) const402             void way_not_in_any_relation(const osmium::Way& /*way*/) const noexcept {
403             }
404 
405             /**
406              * This method is called for all ways during the second pass
407              * after the relation member handling.
408              *
409              * Overwrite this method in a derived class if you are interested
410              * in this.
411              */
after_way(const osmium::Way &) const412             void after_way(const osmium::Way& /*way*/) const noexcept {
413             }
414 
415             /**
416              * This method is called for all relations during the second pass
417              * before the relation member handling.
418              *
419              * Overwrite this method in a derived class if you are interested
420              * in this.
421              */
before_relation(const osmium::Relation &) const422             void before_relation(const osmium::Relation& /*relation*/) const noexcept {
423             }
424 
425             /**
426              * This method is called for all relations that are not a member of
427              * any relation.
428              *
429              * Overwrite this method in a derived class if you are interested
430              * in this.
431              */
relation_not_in_any_relation(const osmium::Relation &) const432             void relation_not_in_any_relation(const osmium::Relation& /*relation*/) const noexcept {
433             }
434 
435             /**
436              * This method is called for all relations during the second pass
437              * after the relation member handling.
438              *
439              * Overwrite this method in a derived class if you are interested
440              * in this.
441              */
after_relation(const osmium::Relation &) const442             void after_relation(const osmium::Relation& /*relation*/) const noexcept {
443             }
444 
derived()445             TManager& derived() noexcept {
446                 return *static_cast<TManager*>(this);
447             }
448 
handle_complete_relation(RelationHandle & rel_handle)449             void handle_complete_relation(RelationHandle& rel_handle) {
450                 derived().complete_relation(*rel_handle);
451                 possibly_flush();
452 
453                 for (const auto& member : rel_handle->members()) {
454                     if (member.ref() != 0) {
455                         member_database(member.type()).remove(member.ref(), rel_handle->id());
456                     }
457                 }
458 
459                 rel_handle.remove();
460             }
461 
462         public:
463 
RelationsManager()464             RelationsManager() :
465                 RelationsManagerBase(),
466                 m_check_order_handler(),
467                 m_handler_pass2(*this) {
468             }
469 
470             /**
471              * Return reference to second pass handler.
472              */
handler(const std::function<void (osmium::memory::Buffer &&)> & callback=nullptr)473             SecondPassHandler<RelationsManager>& handler(const std::function<void(osmium::memory::Buffer&&)>& callback = nullptr) {
474                 set_callback(callback);
475                 return m_handler_pass2;
476             }
477 
478             /**
479              * Add the specified relation to the list of relations we want to
480              * build. This calls the new_relation() and new_member()
481              * functions to actually decide what to keep.
482              *
483              * This member function is named relation() so the manager can
484              * be used as a handler for the first pass through a data file.
485              *
486              * @param relation Relation we might want to build.
487              */
relation(const osmium::Relation & relation)488             void relation(const osmium::Relation& relation) {
489                 if (derived().new_relation(relation)) {
490                     auto rel_handle = relations_database().add(relation);
491 
492                     std::size_t n = 0;
493                     for (auto& member : rel_handle->members()) {
494                         if (wanted_type(member.type()) &&
495                             derived().new_member(relation, member, n)) {
496                             member_database(member.type()).track(rel_handle, member.ref(), n);
497                         } else {
498                             member.set_ref(0); // set member id to zero to indicate we are not interested
499                         }
500                         ++n;
501                     }
502                 }
503             }
504 
handle_node(const osmium::Node & node)505             void handle_node(const osmium::Node& node) {
506                 if (TNodes) {
507                     m_check_order_handler.node(node);
508                     derived().before_node(node);
509                     const bool added = member_nodes_database().add(node, [this](RelationHandle& rel_handle) {
510                         handle_complete_relation(rel_handle);
511                     });
512                     if (!added) {
513                         derived().node_not_in_any_relation(node);
514                     }
515                     derived().after_node(node);
516                     possibly_flush();
517                 }
518             }
519 
handle_way(const osmium::Way & way)520             void handle_way(const osmium::Way& way) {
521                 if (TWays) {
522                     m_check_order_handler.way(way);
523                     derived().before_way(way);
524                     const bool added = member_ways_database().add(way, [this](RelationHandle& rel_handle) {
525                         handle_complete_relation(rel_handle);
526                     });
527                     if (!added) {
528                         derived().way_not_in_any_relation(way);
529                     }
530                     derived().after_way(way);
531                     possibly_flush();
532                 }
533             }
534 
handle_relation(const osmium::Relation & relation)535             void handle_relation(const osmium::Relation& relation) {
536                 if (TRelations) {
537                     m_check_order_handler.relation(relation);
538                     derived().before_relation(relation);
539                     const bool added = member_relations_database().add(relation, [this](RelationHandle& rel_handle) {
540                         handle_complete_relation(rel_handle);
541                     });
542                     if (!added) {
543                         derived().relation_not_in_any_relation(relation);
544                     }
545                     derived().after_relation(relation);
546                     possibly_flush();
547                 }
548             }
549 
550             /**
551              * Call this function it will call your function back for every
552              * incomplete relation, that is all relations that have missing
553              * members in the input data. Usually you call this only after
554              * your second pass through the data if you are interested in
555              * any relations that have all or some of their members missing
556              * in the input data.
557              */
558             template <typename TFunc>
for_each_incomplete_relation(TFunc && func)559             void for_each_incomplete_relation(TFunc&& func) {
560                 relations_database().for_each_relation(std::forward<TFunc>(func));
561             }
562 
563         }; // class RelationsManager
564 
565     } // namespace relations
566 
567 } // namespace osmium
568 
569 #endif // OSMIUM_RELATIONS_RELATIONS_MANAGER_HPP
570