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