1 #include "extractor/extraction_containers.hpp"
2 #include "extractor/extraction_segment.hpp"
3 #include "extractor/extraction_way.hpp"
4 #include "extractor/files.hpp"
5 #include "extractor/name_table.hpp"
6 #include "extractor/restriction.hpp"
7 #include "extractor/serialization.hpp"
8 
9 #include "util/coordinate_calculation.hpp"
10 
11 #include "util/exception.hpp"
12 #include "util/exception_utils.hpp"
13 #include "util/for_each_indexed.hpp"
14 #include "util/log.hpp"
15 #include "util/timing_util.hpp"
16 
17 #include <boost/assert.hpp>
18 #include <boost/numeric/conversion/cast.hpp>
19 
20 #include <tbb/parallel_sort.h>
21 
22 #include <chrono>
23 #include <limits>
24 #include <mutex>
25 #include <sstream>
26 
27 namespace
28 {
29 namespace oe = osrm::extractor;
30 
31 struct CmpEdgeByOSMStartID
32 {
33     using value_type = oe::InternalExtractorEdge;
operator ()__anon069d93270111::CmpEdgeByOSMStartID34     bool operator()(const value_type &lhs, const value_type &rhs) const
35     {
36         return lhs.result.osm_source_id < rhs.result.osm_source_id;
37     }
38 };
39 
40 struct CmpEdgeByOSMTargetID
41 {
42     using value_type = oe::InternalExtractorEdge;
operator ()__anon069d93270111::CmpEdgeByOSMTargetID43     bool operator()(const value_type &lhs, const value_type &rhs) const
44     {
45         return lhs.result.osm_target_id < rhs.result.osm_target_id;
46     }
47 };
48 
49 struct CmpEdgeByInternalSourceTargetAndName
50 {
51     using value_type = oe::InternalExtractorEdge;
operator ()__anon069d93270111::CmpEdgeByInternalSourceTargetAndName52     bool operator()(const value_type &lhs, const value_type &rhs) const
53     {
54         if (lhs.result.source != rhs.result.source)
55             return lhs.result.source < rhs.result.source;
56 
57         if (lhs.result.source == SPECIAL_NODEID)
58             return false;
59 
60         if (lhs.result.target != rhs.result.target)
61             return lhs.result.target < rhs.result.target;
62 
63         if (lhs.result.target == SPECIAL_NODEID)
64             return false;
65 
66         auto const lhs_name_id = edge_annotation_data[lhs.result.annotation_data].name_id;
67         auto const rhs_name_id = edge_annotation_data[rhs.result.annotation_data].name_id;
68         if (lhs_name_id == rhs_name_id)
69             return false;
70 
71         if (lhs_name_id == EMPTY_NAMEID)
72             return false;
73 
74         if (rhs_name_id == EMPTY_NAMEID)
75             return true;
76 
77         BOOST_ASSERT(!name_offsets.empty() && name_offsets.back() == name_data.size());
78         const oe::ExtractionContainers::NameCharData::const_iterator data = name_data.begin();
79         return std::lexicographical_compare(data + name_offsets[lhs_name_id],
80                                             data + name_offsets[lhs_name_id + 1],
81                                             data + name_offsets[rhs_name_id],
82                                             data + name_offsets[rhs_name_id + 1]);
83     }
84 
85     const oe::ExtractionContainers::AnnotationDataVector &edge_annotation_data;
86     const oe::ExtractionContainers::NameCharData &name_data;
87     const oe::ExtractionContainers::NameOffsets &name_offsets;
88 };
89 
90 template <typename Iter>
mapExternalToInternalNodeID(Iter first,Iter last,const OSMNodeID value)91 inline NodeID mapExternalToInternalNodeID(Iter first, Iter last, const OSMNodeID value)
92 {
93     const auto it = std::lower_bound(first, last, value);
94     return (it == last || value < *it) ? SPECIAL_NODEID
95                                        : static_cast<NodeID>(std::distance(first, it));
96 }
97 
98 /**
99  *   Here's what these properties represent on the node-based-graph
100  *       way "ABCD"                         way "AB"
101  *  -----------------------------------------------------------------
102  *     ⬇   A  first_segment_source_id
103  *     ⬇   |
104  *     ⬇︎   B  first_segment_target_id      A  first_segment_source_id
105  *     ⬇︎   |                            ⬇ |  last_segment_source_id
106  *     ⬇︎   |                            ⬇ |
107  *     ⬇︎   |                               B  first_segment_target_id
108  *     ⬇︎   C  last_segment_source_id          last_segment_target_id
109  *     ⬇︎   |
110  *     ⬇︎   D  last_segment_target_id
111  *
112  * Finds the point where two ways connect at the end, and returns the 3
113  * node-based nodes that describe the turn (the node just before, the
114  * node at the turn, and the next node after the turn)
115  **/
find_turn_nodes(const oe::NodesOfWay & from,const oe::NodesOfWay & via,const OSMNodeID & intersection_node)116 std::tuple<OSMNodeID, OSMNodeID, OSMNodeID> find_turn_nodes(const oe::NodesOfWay &from,
117                                                             const oe::NodesOfWay &via,
118                                                             const OSMNodeID &intersection_node)
119 {
120     // connection node needed to choose orientation if from and via are the same way. E.g. u-turns
121     if (intersection_node == SPECIAL_OSM_NODEID ||
122         intersection_node == from.first_segment_source_id())
123     {
124         if (from.first_segment_source_id() == via.first_segment_source_id())
125         {
126             return std::make_tuple(from.first_segment_target_id(),
127                                    via.first_segment_source_id(),
128                                    via.first_segment_target_id());
129         }
130         if (from.first_segment_source_id() == via.last_segment_target_id())
131         {
132             return std::make_tuple(from.first_segment_target_id(),
133                                    via.last_segment_target_id(),
134                                    via.last_segment_source_id());
135         }
136     }
137     if (intersection_node == SPECIAL_OSM_NODEID ||
138         intersection_node == from.last_segment_target_id())
139     {
140         if (from.last_segment_target_id() == via.first_segment_source_id())
141         {
142             return std::make_tuple(from.last_segment_source_id(),
143                                    via.first_segment_source_id(),
144                                    via.first_segment_target_id());
145         }
146         if (from.last_segment_target_id() == via.last_segment_target_id())
147         {
148             return std::make_tuple(from.last_segment_source_id(),
149                                    via.last_segment_target_id(),
150                                    via.last_segment_source_id());
151         }
152     }
153     return std::make_tuple(SPECIAL_OSM_NODEID, SPECIAL_OSM_NODEID, SPECIAL_OSM_NODEID);
154 }
155 } // namespace
156 
157 namespace osrm
158 {
159 namespace extractor
160 {
161 
ExtractionContainers()162 ExtractionContainers::ExtractionContainers()
163 {
164     // Insert four empty strings offsets for name, ref, destination, pronunciation, and exits
165     name_offsets.push_back(0);
166     name_offsets.push_back(0);
167     name_offsets.push_back(0);
168     name_offsets.push_back(0);
169     name_offsets.push_back(0);
170     // Insert the total length sentinel (corresponds to the next name string offset)
171     name_offsets.push_back(0);
172     // Sentinel for offset into used_nodes
173     way_node_id_offsets.push_back(0);
174 }
175 
176 /**
177  * Processes the collected data and serializes it.
178  * At this point nodes are still referenced by their OSM id.
179  *
180  * - map start-end nodes of ways to ways used in restrictions to compute compressed
181  *   trippe representation
182  * - filter nodes list to nodes that are referenced by ways
183  * - merge edges with nodes to include location of start/end points and serialize
184  *
185  */
PrepareData(ScriptingEnvironment & scripting_environment,const std::string & osrm_path,const std::string & name_file_name)186 void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environment,
187                                        const std::string &osrm_path,
188                                        const std::string &name_file_name)
189 {
190     storage::tar::FileWriter writer(osrm_path, storage::tar::FileWriter::GenerateFingerprint);
191 
192     const auto restriction_ways = IdentifyRestrictionWays();
193     const auto maneuver_override_ways = IdentifyManeuverOverrideWays();
194 
195     PrepareNodes();
196     WriteNodes(writer);
197     PrepareEdges(scripting_environment);
198     all_nodes_list.clear(); // free all_nodes_list before allocation of normal_edges
199     all_nodes_list.shrink_to_fit();
200     WriteEdges(writer);
201     WriteMetadata(writer);
202 
203     PrepareManeuverOverrides(maneuver_override_ways);
204     PrepareRestrictions(restriction_ways);
205     WriteCharData(name_file_name);
206 }
207 
WriteCharData(const std::string & file_name)208 void ExtractionContainers::WriteCharData(const std::string &file_name)
209 {
210     util::UnbufferedLog log;
211     log << "writing street name index ... ";
212     TIMER_START(write_index);
213 
214     files::writeNames(file_name,
215                       NameTable{NameTable::IndexedData(
216                           name_offsets.begin(), name_offsets.end(), name_char_data.begin())});
217 
218     TIMER_STOP(write_index);
219     log << "ok, after " << TIMER_SEC(write_index) << "s";
220 }
221 
PrepareNodes()222 void ExtractionContainers::PrepareNodes()
223 {
224     {
225         util::UnbufferedLog log;
226         log << "Sorting used nodes        ... " << std::flush;
227         TIMER_START(sorting_used_nodes);
228         tbb::parallel_sort(used_node_id_list.begin(), used_node_id_list.end());
229         TIMER_STOP(sorting_used_nodes);
230         log << "ok, after " << TIMER_SEC(sorting_used_nodes) << "s";
231     }
232 
233     {
234         util::UnbufferedLog log;
235         log << "Erasing duplicate nodes   ... " << std::flush;
236         TIMER_START(erasing_dups);
237         auto new_end = std::unique(used_node_id_list.begin(), used_node_id_list.end());
238         used_node_id_list.resize(new_end - used_node_id_list.begin());
239         TIMER_STOP(erasing_dups);
240         log << "ok, after " << TIMER_SEC(erasing_dups) << "s";
241     }
242 
243     {
244         util::UnbufferedLog log;
245         log << "Sorting all nodes         ... " << std::flush;
246         TIMER_START(sorting_nodes);
247         tbb::parallel_sort(
248             all_nodes_list.begin(), all_nodes_list.end(), [](const auto &left, const auto &right) {
249                 return left.node_id < right.node_id;
250             });
251         TIMER_STOP(sorting_nodes);
252         log << "ok, after " << TIMER_SEC(sorting_nodes) << "s";
253     }
254 
255     {
256         util::UnbufferedLog log;
257         log << "Building node id map      ... " << std::flush;
258         TIMER_START(id_map);
259         auto node_iter = all_nodes_list.begin();
260         auto ref_iter = used_node_id_list.begin();
261         auto used_nodes_iter = used_node_id_list.begin();
262         const auto all_nodes_list_end = all_nodes_list.end();
263         const auto used_node_id_list_end = used_node_id_list.end();
264 
265         // compute the intersection of nodes that were referenced and nodes we actually have
266         while (node_iter != all_nodes_list_end && ref_iter != used_node_id_list_end)
267         {
268             if (node_iter->node_id < *ref_iter)
269             {
270                 node_iter++;
271                 continue;
272             }
273             if (node_iter->node_id > *ref_iter)
274             {
275                 ref_iter++;
276                 continue;
277             }
278             BOOST_ASSERT(node_iter->node_id == *ref_iter);
279             *used_nodes_iter = std::move(*ref_iter);
280             used_nodes_iter++;
281             node_iter++;
282             ref_iter++;
283         }
284 
285         // Remove unused nodes and check maximal internal node id
286         used_node_id_list.resize(std::distance(used_node_id_list.begin(), used_nodes_iter));
287         if (used_node_id_list.size() > std::numeric_limits<NodeID>::max())
288         {
289             throw util::exception("There are too many nodes remaining after filtering, OSRM only "
290                                   "supports 2^32 unique nodes, but there were " +
291                                   std::to_string(used_node_id_list.size()) + SOURCE_REF);
292         }
293         max_internal_node_id = boost::numeric_cast<std::uint64_t>(used_node_id_list.size());
294         TIMER_STOP(id_map);
295         log << "ok, after " << TIMER_SEC(id_map) << "s";
296     }
297 }
298 
PrepareEdges(ScriptingEnvironment & scripting_environment)299 void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environment)
300 {
301     // Sort edges by start.
302     {
303         util::UnbufferedLog log;
304         log << "Sorting edges by start    ... " << std::flush;
305         TIMER_START(sort_edges_by_start);
306         tbb::parallel_sort(all_edges_list.begin(), all_edges_list.end(), CmpEdgeByOSMStartID());
307         TIMER_STOP(sort_edges_by_start);
308         log << "ok, after " << TIMER_SEC(sort_edges_by_start) << "s";
309     }
310 
311     {
312         util::UnbufferedLog log;
313         log << "Setting start coords      ... " << std::flush;
314         TIMER_START(set_start_coords);
315         // Traverse list of edges and nodes in parallel and set start coord
316         auto node_iterator = all_nodes_list.begin();
317         auto edge_iterator = all_edges_list.begin();
318 
319         const auto all_edges_list_end = all_edges_list.end();
320         const auto all_nodes_list_end = all_nodes_list.end();
321 
322         while (edge_iterator != all_edges_list_end && node_iterator != all_nodes_list_end)
323         {
324             if (edge_iterator->result.osm_source_id < node_iterator->node_id)
325             {
326                 util::Log(logDEBUG)
327                     << "Found invalid node reference " << edge_iterator->result.source;
328                 edge_iterator->result.source = SPECIAL_NODEID;
329                 ++edge_iterator;
330                 continue;
331             }
332             if (edge_iterator->result.osm_source_id > node_iterator->node_id)
333             {
334                 node_iterator++;
335                 continue;
336             }
337 
338             // remove loops
339             if (edge_iterator->result.osm_source_id == edge_iterator->result.osm_target_id)
340             {
341                 edge_iterator->result.source = SPECIAL_NODEID;
342                 edge_iterator->result.target = SPECIAL_NODEID;
343                 ++edge_iterator;
344                 continue;
345             }
346 
347             BOOST_ASSERT(edge_iterator->result.osm_source_id == node_iterator->node_id);
348 
349             // assign new node id
350             const auto node_id = mapExternalToInternalNodeID(
351                 used_node_id_list.begin(), used_node_id_list.end(), node_iterator->node_id);
352             BOOST_ASSERT(node_id != SPECIAL_NODEID);
353             edge_iterator->result.source = node_id;
354 
355             edge_iterator->source_coordinate.lat = node_iterator->lat;
356             edge_iterator->source_coordinate.lon = node_iterator->lon;
357             ++edge_iterator;
358         }
359 
360         // Remove all remaining edges. They are invalid because there are no corresponding nodes for
361         // them. This happens when using osmosis with bbox or polygon to extract smaller areas.
362         auto markSourcesInvalid = [](InternalExtractorEdge &edge) {
363             util::Log(logDEBUG) << "Found invalid node reference " << edge.result.source;
364             edge.result.source = SPECIAL_NODEID;
365             edge.result.osm_source_id = SPECIAL_OSM_NODEID;
366         };
367         std::for_each(edge_iterator, all_edges_list_end, markSourcesInvalid);
368         TIMER_STOP(set_start_coords);
369         log << "ok, after " << TIMER_SEC(set_start_coords) << "s";
370     }
371 
372     {
373         // Sort Edges by target
374         util::UnbufferedLog log;
375         log << "Sorting edges by target   ... " << std::flush;
376         TIMER_START(sort_edges_by_target);
377         tbb::parallel_sort(all_edges_list.begin(), all_edges_list.end(), CmpEdgeByOSMTargetID());
378         TIMER_STOP(sort_edges_by_target);
379         log << "ok, after " << TIMER_SEC(sort_edges_by_target) << "s";
380     }
381 
382     {
383         // Compute edge weights
384         util::UnbufferedLog log;
385         log << "Computing edge weights    ... " << std::flush;
386         TIMER_START(compute_weights);
387         auto node_iterator = all_nodes_list.begin();
388         auto edge_iterator = all_edges_list.begin();
389         const auto all_edges_list_end_ = all_edges_list.end();
390         const auto all_nodes_list_end_ = all_nodes_list.end();
391 
392         const auto weight_multiplier =
393             scripting_environment.GetProfileProperties().GetWeightMultiplier();
394 
395         while (edge_iterator != all_edges_list_end_ && node_iterator != all_nodes_list_end_)
396         {
397             // skip all invalid edges
398             if (edge_iterator->result.source == SPECIAL_NODEID)
399             {
400                 ++edge_iterator;
401                 continue;
402             }
403 
404             if (edge_iterator->result.osm_target_id < node_iterator->node_id)
405             {
406                 util::Log(logDEBUG) << "Found invalid node reference "
407                                     << static_cast<uint64_t>(edge_iterator->result.osm_target_id);
408                 edge_iterator->result.target = SPECIAL_NODEID;
409                 ++edge_iterator;
410                 continue;
411             }
412             if (edge_iterator->result.osm_target_id > node_iterator->node_id)
413             {
414                 ++node_iterator;
415                 continue;
416             }
417 
418             BOOST_ASSERT(edge_iterator->result.osm_target_id == node_iterator->node_id);
419             BOOST_ASSERT(edge_iterator->source_coordinate.lat !=
420                          util::FixedLatitude{std::numeric_limits<std::int32_t>::min()});
421             BOOST_ASSERT(edge_iterator->source_coordinate.lon !=
422                          util::FixedLongitude{std::numeric_limits<std::int32_t>::min()});
423 
424             util::Coordinate source_coord(edge_iterator->source_coordinate);
425             util::Coordinate target_coord{node_iterator->lon, node_iterator->lat};
426 
427             // flip source and target coordinates if segment is in backward direction only
428             if (!edge_iterator->result.flags.forward && edge_iterator->result.flags.backward)
429                 std::swap(source_coord, target_coord);
430 
431             const auto distance =
432                 util::coordinate_calculation::greatCircleDistance(source_coord, target_coord);
433             const auto weight = edge_iterator->weight_data(distance);
434             const auto duration = edge_iterator->duration_data(distance);
435 
436             const auto accurate_distance =
437                 util::coordinate_calculation::fccApproximateDistance(source_coord, target_coord);
438 
439             ExtractionSegment segment(source_coord, target_coord, distance, weight, duration);
440             scripting_environment.ProcessSegment(segment);
441 
442             auto &edge = edge_iterator->result;
443             edge.weight = std::max<EdgeWeight>(1, std::round(segment.weight * weight_multiplier));
444             edge.duration = std::max<EdgeWeight>(1, std::round(segment.duration * 10.));
445             edge.distance = accurate_distance;
446 
447             // assign new node id
448             const auto node_id = mapExternalToInternalNodeID(
449                 used_node_id_list.begin(), used_node_id_list.end(), node_iterator->node_id);
450             BOOST_ASSERT(node_id != SPECIAL_NODEID);
451             edge.target = node_id;
452 
453             // orient edges consistently: source id < target id
454             // important for multi-edge removal
455             if (edge.source > edge.target)
456             {
457                 std::swap(edge.source, edge.target);
458 
459                 // std::swap does not work with bit-fields
460                 bool temp = edge.flags.forward;
461                 edge.flags.forward = edge.flags.backward;
462                 edge.flags.backward = temp;
463             }
464             ++edge_iterator;
465         }
466 
467         // Remove all remaining edges. They are invalid because there are no corresponding nodes for
468         // them. This happens when using osmosis with bbox or polygon to extract smaller areas.
469         auto markTargetsInvalid = [](InternalExtractorEdge &edge) {
470             util::Log(logDEBUG) << "Found invalid node reference " << edge.result.target;
471             edge.result.target = SPECIAL_NODEID;
472         };
473         std::for_each(edge_iterator, all_edges_list_end_, markTargetsInvalid);
474         TIMER_STOP(compute_weights);
475         log << "ok, after " << TIMER_SEC(compute_weights) << "s";
476     }
477 
478     // Sort edges by start.
479     {
480         util::UnbufferedLog log;
481         log << "Sorting edges by renumbered start ... ";
482         TIMER_START(sort_edges_by_renumbered_start);
483         tbb::parallel_sort(all_edges_list.begin(),
484                            all_edges_list.end(),
485                            CmpEdgeByInternalSourceTargetAndName{
486                                all_edges_annotation_data_list, name_char_data, name_offsets});
487         TIMER_STOP(sort_edges_by_renumbered_start);
488         log << "ok, after " << TIMER_SEC(sort_edges_by_renumbered_start) << "s";
489     }
490 
491     BOOST_ASSERT(all_edges_list.size() > 0);
492     for (std::size_t i = 0; i < all_edges_list.size();)
493     {
494         // only invalid edges left
495         if (all_edges_list[i].result.source == SPECIAL_NODEID)
496         {
497             break;
498         }
499         // skip invalid edges
500         if (all_edges_list[i].result.target == SPECIAL_NODEID)
501         {
502             ++i;
503             continue;
504         }
505 
506         std::size_t start_idx = i;
507         NodeID source = all_edges_list[i].result.source;
508         NodeID target = all_edges_list[i].result.target;
509 
510         auto min_forward = std::make_pair(std::numeric_limits<EdgeWeight>::max(),
511                                           std::numeric_limits<EdgeWeight>::max());
512         auto min_backward = std::make_pair(std::numeric_limits<EdgeWeight>::max(),
513                                            std::numeric_limits<EdgeWeight>::max());
514         std::size_t min_forward_idx = std::numeric_limits<std::size_t>::max();
515         std::size_t min_backward_idx = std::numeric_limits<std::size_t>::max();
516 
517         // find minimal edge in both directions
518         while (i < all_edges_list.size() && all_edges_list[i].result.source == source &&
519                all_edges_list[i].result.target == target)
520         {
521             const auto &result = all_edges_list[i].result;
522             const auto value = std::make_pair(result.weight, result.duration);
523             if (result.flags.forward && value < min_forward)
524             {
525                 min_forward_idx = i;
526                 min_forward = value;
527             }
528             if (result.flags.backward && value < min_backward)
529             {
530                 min_backward_idx = i;
531                 min_backward = value;
532             }
533 
534             // this also increments the outer loop counter!
535             i++;
536         }
537 
538         BOOST_ASSERT(min_forward_idx == std::numeric_limits<std::size_t>::max() ||
539                      min_forward_idx < i);
540         BOOST_ASSERT(min_backward_idx == std::numeric_limits<std::size_t>::max() ||
541                      min_backward_idx < i);
542         BOOST_ASSERT(min_backward_idx != std::numeric_limits<std::size_t>::max() ||
543                      min_forward_idx != std::numeric_limits<std::size_t>::max());
544 
545         if (min_backward_idx == min_forward_idx)
546         {
547             all_edges_list[min_forward_idx].result.flags.is_split = false;
548             all_edges_list[min_forward_idx].result.flags.forward = true;
549             all_edges_list[min_forward_idx].result.flags.backward = true;
550         }
551         else
552         {
553             bool has_forward = min_forward_idx != std::numeric_limits<std::size_t>::max();
554             bool has_backward = min_backward_idx != std::numeric_limits<std::size_t>::max();
555             if (has_forward)
556             {
557                 all_edges_list[min_forward_idx].result.flags.forward = true;
558                 all_edges_list[min_forward_idx].result.flags.backward = false;
559                 all_edges_list[min_forward_idx].result.flags.is_split = has_backward;
560             }
561             if (has_backward)
562             {
563                 std::swap(all_edges_list[min_backward_idx].result.source,
564                           all_edges_list[min_backward_idx].result.target);
565                 all_edges_list[min_backward_idx].result.flags.forward = true;
566                 all_edges_list[min_backward_idx].result.flags.backward = false;
567                 all_edges_list[min_backward_idx].result.flags.is_split = has_forward;
568             }
569         }
570 
571         // invalidate all unused edges
572         for (std::size_t j = start_idx; j < i; j++)
573         {
574             if (j == min_forward_idx || j == min_backward_idx)
575             {
576                 continue;
577             }
578             all_edges_list[j].result.source = SPECIAL_NODEID;
579             all_edges_list[j].result.target = SPECIAL_NODEID;
580         }
581     }
582 }
583 
WriteEdges(storage::tar::FileWriter & writer) const584 void ExtractionContainers::WriteEdges(storage::tar::FileWriter &writer) const
585 {
586     std::vector<NodeBasedEdge> normal_edges;
587     normal_edges.reserve(all_edges_list.size());
588     {
589         util::UnbufferedLog log;
590         log << "Writing used edges       ... " << std::flush;
591         TIMER_START(write_edges);
592         // Traverse list of edges and nodes in parallel and set target coord
593 
594         for (const auto &edge : all_edges_list)
595         {
596             if (edge.result.source == SPECIAL_NODEID || edge.result.target == SPECIAL_NODEID)
597             {
598                 continue;
599             }
600 
601             // IMPORTANT: here, we're using slicing to only write the data from the base
602             // class of NodeBasedEdgeWithOSM
603             normal_edges.push_back(edge.result);
604         }
605 
606         if (normal_edges.size() > std::numeric_limits<uint32_t>::max())
607         {
608             throw util::exception("There are too many edges, OSRM only supports 2^32" + SOURCE_REF);
609         }
610 
611         storage::serialization::write(writer, "/extractor/edges", normal_edges);
612 
613         TIMER_STOP(write_edges);
614         log << "ok, after " << TIMER_SEC(write_edges) << "s";
615         log << " -- Processed " << normal_edges.size() << " edges";
616     }
617 }
618 
WriteMetadata(storage::tar::FileWriter & writer) const619 void ExtractionContainers::WriteMetadata(storage::tar::FileWriter &writer) const
620 {
621     util::UnbufferedLog log;
622     log << "Writing way meta-data     ... " << std::flush;
623     TIMER_START(write_meta_data);
624 
625     storage::serialization::write(writer, "/extractor/annotations", all_edges_annotation_data_list);
626 
627     TIMER_STOP(write_meta_data);
628     log << "ok, after " << TIMER_SEC(write_meta_data) << "s";
629     log << " -- Metadata contains << " << all_edges_annotation_data_list.size() << " entries.";
630 }
631 
WriteNodes(storage::tar::FileWriter & writer) const632 void ExtractionContainers::WriteNodes(storage::tar::FileWriter &writer) const
633 {
634     {
635         util::UnbufferedLog log;
636         log << "Confirming/Writing used nodes     ... ";
637         TIMER_START(write_nodes);
638         // identify all used nodes by a merging step of two sorted lists
639         auto node_iterator = all_nodes_list.begin();
640         auto node_id_iterator = used_node_id_list.begin();
641         const auto all_nodes_list_end = all_nodes_list.end();
642 
643         const std::function<QueryNode()> encode_function = [&]() -> QueryNode {
644             BOOST_ASSERT(node_id_iterator != used_node_id_list.end());
645             BOOST_ASSERT(node_iterator != all_nodes_list_end);
646             BOOST_ASSERT(*node_id_iterator >= node_iterator->node_id);
647             while (*node_id_iterator > node_iterator->node_id &&
648                    node_iterator != all_nodes_list_end)
649             {
650                 ++node_iterator;
651             }
652             if (node_iterator == all_nodes_list_end || *node_id_iterator < node_iterator->node_id)
653             {
654                 throw util::exception(
655                     "Invalid OSM data: Referenced non-existing node with ID " +
656                     std::to_string(static_cast<std::uint64_t>(*node_id_iterator)));
657             }
658             BOOST_ASSERT(*node_id_iterator == node_iterator->node_id);
659 
660             ++node_id_iterator;
661             return *node_iterator++;
662         };
663 
664         writer.WriteElementCount64("/extractor/nodes", used_node_id_list.size());
665         writer.WriteStreaming<QueryNode>(
666             "/extractor/nodes",
667             boost::make_function_input_iterator(encode_function, boost::infinite()),
668             used_node_id_list.size());
669 
670         TIMER_STOP(write_nodes);
671         log << "ok, after " << TIMER_SEC(write_nodes) << "s";
672     }
673 
674     {
675         util::UnbufferedLog log;
676         log << "Writing barrier nodes     ... ";
677         TIMER_START(write_nodes);
678         std::vector<NodeID> internal_barrier_nodes;
679         for (const auto osm_id : barrier_nodes)
680         {
681             const auto node_id = mapExternalToInternalNodeID(
682                 used_node_id_list.begin(), used_node_id_list.end(), osm_id);
683             if (node_id != SPECIAL_NODEID)
684             {
685                 internal_barrier_nodes.push_back(node_id);
686             }
687         }
688         storage::serialization::write(writer, "/extractor/barriers", internal_barrier_nodes);
689         log << "ok, after " << TIMER_SEC(write_nodes) << "s";
690     }
691 
692     {
693         util::UnbufferedLog log;
694         log << "Writing traffic light nodes     ... ";
695         TIMER_START(write_nodes);
696         std::vector<NodeID> internal_traffic_signals;
697         for (const auto osm_id : traffic_signals)
698         {
699             const auto node_id = mapExternalToInternalNodeID(
700                 used_node_id_list.begin(), used_node_id_list.end(), osm_id);
701             if (node_id != SPECIAL_NODEID)
702             {
703                 internal_traffic_signals.push_back(node_id);
704             }
705         }
706         storage::serialization::write(
707             writer, "/extractor/traffic_lights", internal_traffic_signals);
708         log << "ok, after " << TIMER_SEC(write_nodes) << "s";
709     }
710 
711     util::Log() << "Processed " << max_internal_node_id << " nodes";
712 }
713 
IdentifyManeuverOverrideWays()714 ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyManeuverOverrideWays()
715 {
716     ReferencedWays maneuver_override_ways;
717 
718     // prepare for extracting source/destination nodes for all maneuvers
719     util::UnbufferedLog log;
720     log << "Collecting way information on " << external_maneuver_overrides_list.size()
721         << " maneuver overrides...";
722     TIMER_START(identify_maneuver_override_ways);
723 
724     const auto mark_ids = [&](auto const &external_maneuver_override) {
725         NodesOfWay dummy_segment{MAX_OSM_WAYID, {MAX_OSM_NODEID, MAX_OSM_NODEID}};
726         std::for_each(external_maneuver_override.via_ways.begin(),
727                       external_maneuver_override.via_ways.end(),
728                       [&maneuver_override_ways, dummy_segment](const auto &element) {
729                           maneuver_override_ways[element] = dummy_segment;
730                       });
731     };
732 
733     // First, make an empty hashtable keyed by the ways referenced
734     // by the maneuver overrides
735     std::for_each(
736         external_maneuver_overrides_list.begin(), external_maneuver_overrides_list.end(), mark_ids);
737 
738     const auto set_ids = [&](size_t way_list_idx, auto const &way_id) {
739         auto itr = maneuver_override_ways.find(way_id);
740         if (itr != maneuver_override_ways.end())
741         {
742             auto node_start_itr = used_node_id_list.begin() + way_node_id_offsets[way_list_idx];
743             auto node_end_itr = used_node_id_list.begin() + way_node_id_offsets[way_list_idx + 1];
744             itr->second = NodesOfWay(way_id, std::vector<OSMNodeID>(node_start_itr, node_end_itr));
745         }
746     };
747 
748     // Then, populate the values in that hashtable for only the ways
749     // referenced
750     util::for_each_indexed(ways_list.cbegin(), ways_list.cend(), set_ids);
751 
752     TIMER_STOP(identify_maneuver_override_ways);
753     log << "ok, after " << TIMER_SEC(identify_maneuver_override_ways) << "s";
754 
755     return maneuver_override_ways;
756 }
757 
PrepareManeuverOverrides(const ReferencedWays & maneuver_override_ways)758 void ExtractionContainers::PrepareManeuverOverrides(const ReferencedWays &maneuver_override_ways)
759 {
760     auto const osm_node_to_internal_nbn = [&](auto const osm_node) {
761         auto internal = mapExternalToInternalNodeID(
762             used_node_id_list.begin(), used_node_id_list.end(), osm_node);
763         if (internal == SPECIAL_NODEID)
764         {
765             util::Log(logDEBUG) << "Maneuver override references invalid node: " << osm_node;
766         }
767         return internal;
768     };
769 
770     auto const get_turn_from_way_pair = [&](const OSMWayID &from_id, const OSMWayID &to_id) {
771         auto const from_segment_itr = maneuver_override_ways.find(from_id);
772         if (from_segment_itr->second.way_id != from_id)
773         {
774             util::Log(logDEBUG) << "Override references invalid way: " << from_id;
775             return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
776         }
777 
778         auto const to_segment_itr = maneuver_override_ways.find(to_id);
779         if (to_segment_itr->second.way_id != to_id)
780         {
781             util::Log(logDEBUG) << "Override references invalid way: " << to_id;
782             return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
783         }
784 
785         OSMNodeID from, via, to;
786         std::tie(from, via, to) =
787             find_turn_nodes(from_segment_itr->second, to_segment_itr->second, SPECIAL_OSM_NODEID);
788         if (via == SPECIAL_OSM_NODEID)
789         {
790             // unconnected
791             util::Log(logDEBUG) << "Maneuver override ways " << from_segment_itr->second.way_id
792                                 << " and " << to_segment_itr->second.way_id << " are not connected";
793             return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
794         }
795         return NodeBasedTurn{osm_node_to_internal_nbn(from),
796                              osm_node_to_internal_nbn(via),
797                              osm_node_to_internal_nbn(to)};
798     };
799 
800     const auto strings_to_turn_type_and_direction = [](const std::string &turn_string,
801                                                        const std::string &direction_string) {
802         auto result = std::make_pair(guidance::TurnType::MaxTurnType,
803                                      guidance::DirectionModifier::MaxDirectionModifier);
804 
805         if (turn_string == "uturn")
806         {
807             result.first = guidance::TurnType::Turn;
808             result.second = guidance::DirectionModifier::UTurn;
809         }
810         else if (turn_string == "continue")
811         {
812             result.first = guidance::TurnType::Continue;
813         }
814         else if (turn_string == "turn")
815         {
816             result.first = guidance::TurnType::Turn;
817         }
818         else if (turn_string == "fork")
819         {
820             result.first = guidance::TurnType::Fork;
821         }
822         else if (turn_string == "suppress")
823         {
824             result.first = guidance::TurnType::Suppressed;
825         }
826 
827         // Directions
828         if (direction_string == "left")
829         {
830             result.second = guidance::DirectionModifier::Left;
831         }
832         else if (direction_string == "slight_left")
833         {
834             result.second = guidance::DirectionModifier::SlightLeft;
835         }
836         else if (direction_string == "sharp_left")
837         {
838             result.second = guidance::DirectionModifier::SharpLeft;
839         }
840         else if (direction_string == "sharp_right")
841         {
842             result.second = guidance::DirectionModifier::SharpRight;
843         }
844         else if (direction_string == "slight_right")
845         {
846             result.second = guidance::DirectionModifier::SlightRight;
847         }
848         else if (direction_string == "right")
849         {
850             result.second = guidance::DirectionModifier::Right;
851         }
852         else if (direction_string == "straight")
853         {
854             result.second = guidance::DirectionModifier::Straight;
855         }
856 
857         return result;
858     };
859 
860     // Transform an InternalManeuverOverride (based on WayIDs) into an OSRM override (base on
861     // NodeIDs).
862     // Returns true on successful transformation, false in case of invalid references.
863     // Later, the UnresolvedManeuverOverride will be converted into a final ManeuverOverride
864     // once the edge-based-node IDs are generated by the edge-based-graph-factory
865     const auto transform = [&](const auto &external, auto &internal) {
866         // Create a stub override
867         auto maneuver_override =
868             UnresolvedManeuverOverride{{},
869                                        osm_node_to_internal_nbn(external.via_node),
870                                        guidance::TurnType::Invalid,
871                                        guidance::DirectionModifier::MaxDirectionModifier};
872 
873         // Convert Way IDs into node-based-node IDs
874         // We iterate from back to front here because the first node in the node_sequence
875         // must eventually be a source node, but all the others must be targets.
876         // the get_internal_pairs_from_ways returns (source,target), so if we
877         // iterate backwards, we will end up with source,target,target,target,target
878         // in a sequence, which is what we want
879         for (auto i = 0ul; i < external.via_ways.size() - 1; ++i)
880         {
881             // returns the two far ends of the referenced ways
882             auto turn = get_turn_from_way_pair(external.via_ways[i], external.via_ways[i + 1]);
883 
884             maneuver_override.turn_sequence.push_back(turn);
885         }
886 
887         // check if we were able to resolve all the involved ways
888         // auto maneuver_override =
889         //    get_maneuver_override_from_OSM_ids(external.from, external.to,
890         //    external.via_node);
891 
892         std::tie(maneuver_override.override_type, maneuver_override.direction) =
893             strings_to_turn_type_and_direction(external.maneuver, external.direction);
894 
895         if (!maneuver_override.Valid())
896         {
897             util::Log(logDEBUG) << "Override is invalid";
898             return false;
899         }
900 
901         internal = std::move(maneuver_override);
902         return true;
903     };
904 
905     const auto transform_into_internal_types =
906         [&](const InputManeuverOverride &external_maneuver_override) {
907             UnresolvedManeuverOverride internal_maneuver_override;
908             if (transform(external_maneuver_override, internal_maneuver_override))
909                 internal_maneuver_overrides.push_back(std::move(internal_maneuver_override));
910         };
911 
912     // Transforming the overrides into the dedicated internal types
913     {
914         util::UnbufferedLog log;
915         log << "Collecting node information on " << external_maneuver_overrides_list.size()
916             << " maneuver overrides...";
917         TIMER_START(transform);
918         std::for_each(external_maneuver_overrides_list.begin(),
919                       external_maneuver_overrides_list.end(),
920                       transform_into_internal_types);
921         TIMER_STOP(transform);
922         log << "ok, after " << TIMER_SEC(transform) << "s";
923     }
924 }
925 
IdentifyRestrictionWays()926 ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyRestrictionWays()
927 {
928     // Contains the nodes of each way that is part of an restriction
929     ReferencedWays restriction_ways;
930 
931     // Prepare for extracting nodes for all restrictions
932     util::UnbufferedLog log;
933     log << "Collecting way information on " << restrictions_list.size() << " restrictions...";
934     TIMER_START(identify_restriction_ways);
935 
936     // Enter invalid IDs into the map to indicate that we want to find out about
937     // nodes of these ways.
938     const auto mark_ids = [&](auto const &turn_restriction) {
939         NodesOfWay dummy_segment{MAX_OSM_WAYID, {MAX_OSM_NODEID, MAX_OSM_NODEID}};
940         if (turn_restriction.Type() == RestrictionType::WAY_RESTRICTION)
941         {
942             const auto &way = turn_restriction.AsWayRestriction();
943             restriction_ways[way.from] = dummy_segment;
944             restriction_ways[way.to] = dummy_segment;
945             for (const auto &v : way.via)
946             {
947                 restriction_ways[v] = dummy_segment;
948             }
949         }
950         else
951         {
952             BOOST_ASSERT(turn_restriction.Type() == RestrictionType::NODE_RESTRICTION);
953             const auto &node = turn_restriction.AsNodeRestriction();
954             restriction_ways[node.from] = dummy_segment;
955             restriction_ways[node.to] = dummy_segment;
956         }
957     };
958 
959     std::for_each(restrictions_list.begin(), restrictions_list.end(), mark_ids);
960 
961     // Update the values for all ways already sporting SPECIAL_NODEID
962     const auto set_ids = [&](const size_t way_list_idx, auto const &way_id) {
963         auto itr = restriction_ways.find(way_id);
964         if (itr != restriction_ways.end())
965         {
966             const auto node_start_offset =
967                 used_node_id_list.begin() + way_node_id_offsets[way_list_idx];
968             const auto node_end_offset =
969                 used_node_id_list.begin() + way_node_id_offsets[way_list_idx + 1];
970             itr->second =
971                 NodesOfWay(way_id, std::vector<OSMNodeID>(node_start_offset, node_end_offset));
972         }
973     };
974 
975     util::for_each_indexed(ways_list.cbegin(), ways_list.cend(), set_ids);
976     TIMER_STOP(identify_restriction_ways);
977     log << "ok, after " << TIMER_SEC(identify_restriction_ways) << "s";
978 
979     return restriction_ways;
980 }
981 
PrepareRestrictions(const ReferencedWays & restriction_ways)982 void ExtractionContainers::PrepareRestrictions(const ReferencedWays &restriction_ways)
983 {
984 
985     auto const to_internal = [&](auto const osm_node) {
986         auto internal = mapExternalToInternalNodeID(
987             used_node_id_list.begin(), used_node_id_list.end(), osm_node);
988         if (internal == SPECIAL_NODEID)
989         {
990             util::Log(logDEBUG) << "Restriction references invalid node: " << osm_node;
991         }
992         return internal;
993     };
994 
995     // Way restrictions are comprised of:
996     // 1. The segment in the from way that intersects with the via path
997     // 2. All segments that make up the via path
998     // 3. The segment in the to way that intersects with the via path.
999     //
1000     // from: [a, b, c, d, e]
1001     // via: [[f, g, h, i, j], [k, l], [m, n, o]]
1002     // to: [p, q, r, s]
1003     //
1004     // First establish the orientation of the from/via intersection by finding which end
1005     // nodes both ways share. From this we can select the from segment.
1006     //
1007     // intersect | from segment | next_connection
1008     //    a=f    |      b,a     |        f
1009     //    a=j    |      b,a     |        j
1010     //    e=f    |      e,d     |        f
1011     //    e=j    |      e,d     |        j
1012     //
1013     // Use the next connection to inform the orientation of the first via
1014     // way and the intersection between first and second via ways.
1015     //
1016     // next_connection | intersect | via result  | next_next_connection
1017     //       f         |    j=k    | [f,g,h,i,j] |      k
1018     //       f         |    j=l    | [f,g,h,i,j] |      l
1019     //       j         |    f=k    | [j,i,h,g,f] |      k
1020     //       j         |    f=l    | [j,i,h,g,f] |      l
1021     //
1022     // This is continued for the remaining via ways, appending to the via result
1023     //
1024     // The final via/to intersection also uses the next_connection information in a similar fashion.
1025     //
1026     // next_connection | intersect | to_segment
1027     //       m         |    o=p    |    p,q
1028     //       m         |    o=s    |    s,r
1029     //       o         |    m=p    |    p,q
1030     //       o         |    m=s    |    s,r
1031     //
1032     // The final result is a list of nodes that represent a valid from->via->to path through the
1033     // ways.
1034     //
1035     // E.g. if intersection nodes are a=j, f=l, k=o, m=s
1036     // the result will be {e [d,c,b,a,i,h,g,f,k,n,m] r}
1037     auto const find_way_restriction = [&](const NodesOfWay &from_way,
1038                                           const std::vector<NodesOfWay> &via_ways,
1039                                           const NodesOfWay &to_way) {
1040         BOOST_ASSERT(!via_ways.empty());
1041 
1042         WayRestriction restriction;
1043 
1044         // Find the orientation of the connected ways starting with the from-via intersection.
1045         OSMNodeID from, via;
1046         std::tie(from, via, std::ignore) =
1047             find_turn_nodes(from_way, via_ways.front(), SPECIAL_OSM_NODEID);
1048         if (via == SPECIAL_OSM_NODEID)
1049         {
1050             util::Log(logDEBUG) << "Restriction has unconnected from and via ways: "
1051                                 << from_way.way_id << ", " << via_ways.front().way_id;
1052             return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID};
1053         }
1054         restriction.from = to_internal(from);
1055         restriction.via.push_back(to_internal(via));
1056 
1057         // Use the connection node from the previous intersection to inform our conversion of
1058         // via ways into internal nodes.
1059         OSMNodeID next_connection = via;
1060         for (const auto &via_way : via_ways)
1061         {
1062             if (next_connection == via_way.first_segment_source_id())
1063             {
1064                 std::transform(std::next(via_way.node_ids.begin()),
1065                                via_way.node_ids.end(),
1066                                std::back_inserter(restriction.via),
1067                                to_internal);
1068                 next_connection = via_way.last_segment_target_id();
1069             }
1070             else if (next_connection == via_way.last_segment_target_id())
1071             {
1072                 std::transform(std::next(via_way.node_ids.rbegin()),
1073                                via_way.node_ids.rend(),
1074                                std::back_inserter(restriction.via),
1075                                to_internal);
1076                 next_connection = via_way.first_segment_source_id();
1077             }
1078             else
1079             {
1080                 util::Log(logDEBUG) << "Restriction has unconnected via way: " << via_way.way_id
1081                                     << " to node " << next_connection;
1082                 return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID};
1083             }
1084         }
1085 
1086         // Add the final to node after the via-to intersection.
1087         if (next_connection == to_way.first_segment_source_id())
1088         {
1089             restriction.to = to_internal(to_way.first_segment_target_id());
1090         }
1091         else if (next_connection == to_way.last_segment_target_id())
1092         {
1093             restriction.to = to_internal(to_way.last_segment_source_id());
1094         }
1095         else
1096         {
1097             util::Log(logDEBUG) << "Restriction has unconnected via and to ways: "
1098                                 << via_ways.back().way_id << ", " << to_way.way_id;
1099             return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID};
1100         }
1101         return restriction;
1102     };
1103 
1104     // Check if we were able to resolve all the involved OSM elements before translating to an
1105     // internal restriction
1106     auto const get_way_restriction_from_OSM_ids =
1107         [&](auto const from_id, auto const to_id, const std::vector<OSMWayID> &via_ids) {
1108             auto const from_way_itr = restriction_ways.find(from_id);
1109             if (from_way_itr->second.way_id != from_id)
1110             {
1111                 util::Log(logDEBUG) << "Restriction references invalid from way: " << from_id;
1112                 return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID};
1113             }
1114 
1115             std::vector<NodesOfWay> via_ways;
1116             for (const auto &via_id : via_ids)
1117             {
1118                 auto const via_segment_itr = restriction_ways.find(via_id);
1119                 if (via_segment_itr->second.way_id != via_id)
1120                 {
1121                     util::Log(logDEBUG) << "Restriction references invalid via way: " << via_id;
1122                     return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID};
1123                 }
1124                 via_ways.push_back(via_segment_itr->second);
1125             }
1126 
1127             auto const to_way_itr = restriction_ways.find(to_id);
1128             if (to_way_itr->second.way_id != to_id)
1129             {
1130                 util::Log(logDEBUG) << "Restriction references invalid to way: " << to_id;
1131                 return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID};
1132             }
1133 
1134             return find_way_restriction(from_way_itr->second, via_ways, to_way_itr->second);
1135         };
1136 
1137     // Node restrictions are described as a restriction between the two segments closest
1138     // to the shared via-node on the from and to ways.
1139     // from: [a, b, c, d, e]
1140     // to: [f, g, h, i, j]
1141     //
1142     // The via node establishes the orientation of the from/to intersection when choosing the
1143     // segments.
1144     //   via    |  node restriction
1145     //   a=f    |     b,a,g
1146     //   a=j    |     b,a,i
1147     //   e=f    |     d,e,g
1148     //   e=j    |     d,e,i
1149     auto const find_node_restriction =
1150         [&](auto const &from_segment, auto const &to_segment, auto const via_node) {
1151             OSMNodeID from, via, to;
1152             std::tie(from, via, to) = find_turn_nodes(from_segment, to_segment, via_node);
1153             if (via == SPECIAL_OSM_NODEID)
1154             {
1155                 // unconnected
1156                 util::Log(logDEBUG)
1157                     << "Restriction references unconnected way: " << from_segment.way_id;
1158                 return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
1159             }
1160             return NodeRestriction{to_internal(from), to_internal(via), to_internal(to)};
1161         };
1162 
1163     // Check if we were able to resolve all the involved OSM elements before translating to an
1164     // internal restriction
1165     auto const get_node_restriction_from_OSM_ids = [&](auto const from_id,
1166                                                        auto const to_id,
1167                                                        const OSMNodeID via_node) {
1168         auto const from_segment_itr = restriction_ways.find(from_id);
1169 
1170         if (from_segment_itr->second.way_id != from_id)
1171         {
1172             util::Log(logDEBUG) << "Restriction references invalid way: " << from_id;
1173             return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
1174         }
1175 
1176         auto const to_segment_itr = restriction_ways.find(to_id);
1177         if (to_segment_itr->second.way_id != to_id)
1178         {
1179             util::Log(logDEBUG) << "Restriction references invalid way: " << to_id;
1180             return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
1181         }
1182 
1183         return find_node_restriction(from_segment_itr->second, to_segment_itr->second, via_node);
1184     };
1185 
1186     // Transform an OSMRestriction (based on WayIDs) into an OSRM restriction (base on NodeIDs).
1187     // Returns true on successful transformation, false in case of invalid references.
1188     const auto transform = [&](const auto &external_type, auto &internal_type) {
1189         if (external_type.Type() == RestrictionType::WAY_RESTRICTION)
1190         {
1191             auto const &external = external_type.AsWayRestriction();
1192             auto const restriction =
1193                 get_way_restriction_from_OSM_ids(external.from, external.to, external.via);
1194 
1195             if (!restriction.Valid())
1196                 return false;
1197 
1198             internal_type.node_or_way = restriction;
1199             return true;
1200         }
1201         else
1202         {
1203             BOOST_ASSERT(external_type.Type() == RestrictionType::NODE_RESTRICTION);
1204             auto const &external = external_type.AsNodeRestriction();
1205 
1206             auto restriction =
1207                 get_node_restriction_from_OSM_ids(external.from, external.to, external.via);
1208 
1209             if (!restriction.Valid())
1210                 return false;
1211 
1212             internal_type.node_or_way = restriction;
1213             return true;
1214         }
1215     };
1216 
1217     const auto transform_into_internal_types = [&](InputTurnRestriction &external_restriction) {
1218         TurnRestriction restriction;
1219         if (transform(external_restriction, restriction))
1220         {
1221             restriction.is_only = external_restriction.is_only;
1222             restriction.condition = std::move(external_restriction.condition);
1223             turn_restrictions.push_back(std::move(restriction));
1224         }
1225     };
1226 
1227     // Transforming the restrictions into the dedicated internal types
1228     {
1229         util::UnbufferedLog log;
1230         log << "Collecting node information on " << restrictions_list.size() << " restrictions...";
1231         TIMER_START(transform);
1232         std::for_each(
1233             restrictions_list.begin(), restrictions_list.end(), transform_into_internal_types);
1234         TIMER_STOP(transform);
1235         log << "ok, after " << TIMER_SEC(transform) << "s";
1236     }
1237 }
1238 
1239 } // namespace extractor
1240 } // namespace osrm
1241