1 #ifndef EXTRACTION_RELATION_HPP
2 #define EXTRACTION_RELATION_HPP
3 
4 #include "util/exception.hpp"
5 
6 #include <osmium/osm/relation.hpp>
7 
8 #include <boost/assert.hpp>
9 
10 #include <string>
11 #include <unordered_map>
12 #include <vector>
13 
14 namespace osrm
15 {
16 namespace extractor
17 {
18 
19 struct ExtractionRelation
20 {
21     class OsmIDTyped
22     {
23       public:
OsmIDTyped(osmium::object_id_type _id,osmium::item_type _type)24         OsmIDTyped(osmium::object_id_type _id, osmium::item_type _type) : id(_id), type(_type) {}
25 
GetID() const26         std::uint64_t GetID() const { return std::uint64_t(id); }
GetType() const27         osmium::item_type GetType() const { return type; }
28 
Hash() const29         std::uint64_t Hash() const { return id ^ (static_cast<std::uint64_t>(type) << 56); }
30 
31       private:
32         osmium::object_id_type id;
33         osmium::item_type type;
34     };
35 
36     using AttributesList = std::vector<std::pair<std::string, std::string>>;
37     using MembersRolesList = std::vector<std::pair<std::uint64_t, std::string>>;
38 
ExtractionRelationosrm::extractor::ExtractionRelation39     explicit ExtractionRelation(const OsmIDTyped &_id) : id(_id) {}
40 
Clearosrm::extractor::ExtractionRelation41     void Clear()
42     {
43         attributes.clear();
44         members_role.clear();
45     }
46 
GetAttrosrm::extractor::ExtractionRelation47     const char *GetAttr(const std::string &attr) const
48     {
49         auto it = std::lower_bound(
50             attributes.begin(), attributes.end(), std::make_pair(attr, std::string()));
51 
52         if (it != attributes.end() && (*it).first == attr)
53             return (*it).second.c_str();
54 
55         return nullptr;
56     }
57 
Prepareosrm::extractor::ExtractionRelation58     void Prepare()
59     {
60         std::sort(attributes.begin(), attributes.end());
61         std::sort(members_role.begin(), members_role.end());
62     }
63 
AddMemberosrm::extractor::ExtractionRelation64     void AddMember(const OsmIDTyped &member_id, const char *role)
65     {
66         members_role.emplace_back(std::make_pair(member_id.Hash(), std::string(role)));
67     }
68 
GetRoleosrm::extractor::ExtractionRelation69     const char *GetRole(const OsmIDTyped &member_id) const
70     {
71         const auto hash = member_id.Hash();
72         auto it = std::lower_bound(
73             members_role.begin(), members_role.end(), std::make_pair(hash, std::string()));
74 
75         if (it != members_role.end() && (*it).first == hash)
76             return (*it).second.c_str();
77 
78         return nullptr;
79     }
80 
81     OsmIDTyped id;
82     AttributesList attributes;
83     MembersRolesList members_role;
84 };
85 
86 // It contains data of all parsed relations for each node/way element
87 class ExtractionRelationContainer
88 {
89   public:
90     using AttributesMap = ExtractionRelation::AttributesList;
91     using OsmIDTyped = ExtractionRelation::OsmIDTyped;
92     using RelationList = std::vector<AttributesMap>;
93     using RelationIDList = std::vector<ExtractionRelation::OsmIDTyped>;
94     using RelationRefMap = std::unordered_map<std::uint64_t, RelationIDList>;
95 
AddRelation(ExtractionRelation && rel)96     void AddRelation(ExtractionRelation &&rel)
97     {
98         rel.Prepare();
99 
100         BOOST_ASSERT(relations_data.find(rel.id.GetID()) == relations_data.end());
101         relations_data.insert(std::make_pair(rel.id.GetID(), std::move(rel)));
102     }
103 
AddRelationMember(const OsmIDTyped & relation_id,const OsmIDTyped & member_id)104     void AddRelationMember(const OsmIDTyped &relation_id, const OsmIDTyped &member_id)
105     {
106         switch (member_id.GetType())
107         {
108         case osmium::item_type::node:
109             node_refs[member_id.GetID()].push_back(relation_id);
110             break;
111 
112         case osmium::item_type::way:
113             way_refs[member_id.GetID()].push_back(relation_id);
114             break;
115 
116         case osmium::item_type::relation:
117             rel_refs[member_id.GetID()].push_back(relation_id);
118             break;
119 
120         default:
121             break;
122         };
123     }
124 
Merge(ExtractionRelationContainer && other)125     void Merge(ExtractionRelationContainer &&other)
126     {
127         for (auto it : other.relations_data)
128         {
129             const auto res = relations_data.insert(std::make_pair(it.first, std::move(it.second)));
130             BOOST_ASSERT(res.second);
131             (void)res; // prevent unused warning in release
132         }
133 
134         auto MergeRefMap = [&](RelationRefMap &source, RelationRefMap &target) {
135             for (auto it : source)
136             {
137                 auto &v = target[it.first];
138                 v.insert(v.end(), it.second.begin(), it.second.end());
139             }
140         };
141 
142         MergeRefMap(other.way_refs, way_refs);
143         MergeRefMap(other.node_refs, node_refs);
144         MergeRefMap(other.rel_refs, rel_refs);
145     }
146 
GetRelationsNum() const147     std::size_t GetRelationsNum() const { return relations_data.size(); }
148 
GetRelations(const OsmIDTyped & member_id) const149     const RelationIDList &GetRelations(const OsmIDTyped &member_id) const
150     {
151         auto getFromMap = [this](std::uint64_t id,
152                                  const RelationRefMap &map) -> const RelationIDList & {
153             auto it = map.find(id);
154             if (it != map.end())
155                 return it->second;
156 
157             return empty_rel_list;
158         };
159 
160         switch (member_id.GetType())
161         {
162         case osmium::item_type::node:
163             return getFromMap(member_id.GetID(), node_refs);
164 
165         case osmium::item_type::way:
166             return getFromMap(member_id.GetID(), way_refs);
167 
168         case osmium::item_type::relation:
169             return getFromMap(member_id.GetID(), rel_refs);
170 
171         default:
172             break;
173         }
174 
175         return empty_rel_list;
176     }
177 
GetRelationData(const ExtractionRelation::OsmIDTyped & rel_id) const178     const ExtractionRelation &GetRelationData(const ExtractionRelation::OsmIDTyped &rel_id) const
179     {
180         auto it = relations_data.find(rel_id.GetID());
181         if (it == relations_data.end())
182             throw osrm::util::exception("Can't find relation data for " +
183                                         std::to_string(rel_id.GetID()));
184 
185         return it->second;
186     }
187 
188   private:
189     RelationIDList empty_rel_list;
190     std::unordered_map<std::uint64_t, ExtractionRelation> relations_data;
191 
192     // each map contains list of relation id's, that has keyed id as a member
193     RelationRefMap way_refs;
194     RelationRefMap node_refs;
195     RelationRefMap rel_refs;
196 };
197 
198 } // namespace extractor
199 } // namespace osrm
200 
201 #endif // EXTRACTION_RELATION_HPP
202