1 #include "extractor/maneuver_override_relation_parser.hpp"
2 #include "extractor/maneuver_override.hpp"
3 
4 #include <boost/optional/optional.hpp>
5 #include <boost/ref.hpp>
6 
7 #include <osmium/osm.hpp>
8 #include <osmium/tags/filter.hpp>
9 #include <osmium/tags/taglist.hpp>
10 
11 #include <algorithm>
12 #include <iterator>
13 
14 namespace osrm
15 {
16 namespace extractor
17 {
18 
ManeuverOverrideRelationParser()19 ManeuverOverrideRelationParser::ManeuverOverrideRelationParser() {}
20 
21 /**
22  * Parses the `type=maneuver` relation.  Reads the fields, and puts data
23  * into an InputManeuverOverride object, if the relation is considered
24  * valid (i.e. has the minimum tags we expect).
25  */
26 boost::optional<InputManeuverOverride>
TryParse(const osmium::Relation & relation) const27 ManeuverOverrideRelationParser::TryParse(const osmium::Relation &relation) const
28 {
29 
30     // Support both American and British spellings of maneuver/manoeuvre
31     osmium::tags::KeyValueFilter filter{false};
32     filter.add(true, "type", "maneuver");
33     filter.add(true, "type", "manoeuvre");
34 
35     const osmium::TagList &tag_list = relation.tags();
36 
37     if (osmium::tags::match_none_of(tag_list, filter))
38     // if it's not a maneuver, continue;
39     {
40         return boost::none;
41     }
42 
43     // we pretend every restriction is a conditional restriction. If we do not find any restriction,
44     // we can trim away the vector after parsing
45     InputManeuverOverride maneuver_override;
46 
47     // Handle both spellings
48     if (relation.tags().has_key("manoeuvre"))
49     {
50         maneuver_override.maneuver = relation.tags().get_value_by_key("manoeuvre", "");
51     }
52     else
53     {
54         maneuver_override.maneuver = relation.tags().get_value_by_key("maneuver", "");
55     }
56 
57     maneuver_override.direction = relation.tags().get_value_by_key("direction", "");
58 
59     bool valid_relation = true;
60     OSMNodeID via_node = SPECIAL_OSM_NODEID;
61     OSMWayID from = SPECIAL_OSM_WAYID, to = SPECIAL_OSM_WAYID;
62     std::vector<OSMWayID> via_ways;
63 
64     for (const auto &member : relation.members())
65     {
66         const char *role = member.role();
67         if (strcmp("from", role) != 0 && strcmp("to", role) != 0 && strcmp("via", role) != 0)
68         {
69             continue;
70         }
71 
72         switch (member.type())
73         {
74         case osmium::item_type::node:
75         {
76 
77             // Make sure nodes appear only in the role if a via node
78             if (0 == strcmp("from", role) || 0 == strcmp("to", role))
79             {
80                 continue;
81             }
82             BOOST_ASSERT(0 == strcmp("via", role));
83             // set via node id
84             valid_relation &= via_node == SPECIAL_OSM_NODEID;
85             via_node = OSMNodeID{static_cast<std::uint64_t>(member.ref())};
86             break;
87         }
88         case osmium::item_type::way:
89             BOOST_ASSERT(0 == strcmp("from", role) || 0 == strcmp("to", role) ||
90                          0 == strcmp("via", role));
91             if (0 == strcmp("from", role))
92             {
93                 valid_relation &= from == SPECIAL_OSM_WAYID;
94                 from = OSMWayID{static_cast<std::uint64_t>(member.ref())};
95             }
96             else if (0 == strcmp("to", role))
97             {
98                 valid_relation &= to == SPECIAL_OSM_WAYID;
99                 to = OSMWayID{static_cast<std::uint64_t>(member.ref())};
100             }
101             else if (0 == strcmp("via", role))
102             {
103                 via_ways.push_back(OSMWayID{static_cast<std::uint64_t>(member.ref())});
104             }
105             break;
106         case osmium::item_type::relation:
107             // not yet supported, but who knows what the future holds...
108             break;
109         default:
110             // shouldn't ever happen
111             break;
112         }
113     }
114 
115     // Check required roles
116     valid_relation &= from != SPECIAL_OSM_WAYID;
117     valid_relation &= to != SPECIAL_OSM_WAYID;
118     valid_relation &= via_node != SPECIAL_OSM_NODEID;
119 
120     if (valid_relation)
121     {
122         maneuver_override.via_ways.push_back(from);
123         std::copy(via_ways.begin(), via_ways.end(), std::back_inserter(maneuver_override.via_ways));
124         maneuver_override.via_ways.push_back(to);
125         maneuver_override.via_node = via_node;
126     }
127     else
128     {
129         return boost::none;
130     }
131     return maneuver_override;
132 }
133 } // namespace extractor
134 } // namespace osrm
135