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