#include "extractor/extractor.hpp" #include "extractor/edge_based_edge.hpp" #include "extractor/extraction_containers.hpp" #include "extractor/extraction_node.hpp" #include "extractor/extraction_relation.hpp" #include "extractor/extraction_way.hpp" #include "extractor/extractor_callbacks.hpp" #include "extractor/files.hpp" #include "extractor/maneuver_override_relation_parser.hpp" #include "extractor/node_based_graph_factory.hpp" #include "extractor/raster_source.hpp" #include "extractor/restriction_filter.hpp" #include "extractor/restriction_parser.hpp" #include "extractor/scripting_environment.hpp" #include "guidance/files.hpp" #include "guidance/guidance_processing.hpp" #include "guidance/segregated_intersection_classification.hpp" #include "guidance/turn_data_container.hpp" #include "storage/io.hpp" #include "util/exception.hpp" #include "util/exception_utils.hpp" #include "util/graph_loader.hpp" #include "util/integer_range.hpp" #include "util/log.hpp" #include "util/name_table.hpp" #include "util/range_table.hpp" #include "util/timing_util.hpp" #include "extractor/compressed_edge_container.hpp" #include "extractor/restriction_index.hpp" #include "extractor/way_restriction_map.hpp" #include "util/static_graph.hpp" #include "util/static_rtree.hpp" // Keep debug include to make sure the debug header is in sync with types. #include "util/debug.hpp" #include "extractor/tarjan_scc.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace osrm { namespace extractor { namespace { // Converts the class name map into a fixed mapping of index to name void SetClassNames(const std::vector &class_names, ExtractorCallbacks::ClassesMap &classes_map, ProfileProperties &profile_properties) { // if we get a list of class names we can validate if we set invalid classes // and add classes that were never reference if (!class_names.empty()) { // add class names that were never used explicitly on a way // this makes sure we can correctly validate unkown class names later for (const auto &name : class_names) { if (!isValidClassName(name)) { throw util::exception("Invalid class name " + name + " only [a-Z0-9] allowed."); } auto iter = classes_map.find(name); if (iter == classes_map.end()) { auto index = classes_map.size(); if (index > MAX_CLASS_INDEX) { throw util::exception("Maximum number of classes if " + std::to_string(MAX_CLASS_INDEX + 1)); } classes_map[name] = getClassData(index); } } // check if class names are only from the list supplied by the user for (const auto &pair : classes_map) { auto iter = std::find(class_names.begin(), class_names.end(), pair.first); if (iter == class_names.end()) { throw util::exception("Profile used unknown class name: " + pair.first); } } } for (const auto &pair : classes_map) { auto range = getClassIndexes(pair.second); BOOST_ASSERT(range.size() == 1); profile_properties.SetClassName(range.front(), pair.first); } } // Converts the class name list to a mask list void SetExcludableClasses(const ExtractorCallbacks::ClassesMap &classes_map, const std::vector> &excludable_classes, ProfileProperties &profile_properties) { if (excludable_classes.size() > MAX_EXCLUDABLE_CLASSES) { throw util::exception("Only " + std::to_string(MAX_EXCLUDABLE_CLASSES) + " excludable combinations allowed."); } // The exclude index 0 is reserve for not excludeing anything profile_properties.SetExcludableClasses(0, 0); std::size_t combination_index = 1; for (const auto &combination : excludable_classes) { ClassData mask = 0; for (const auto &name : combination) { auto iter = classes_map.find(name); if (iter == classes_map.end()) { util::Log(logWARNING) << "Unknown class name " + name + " in excludable combination. Ignoring."; } else { mask |= iter->second; } } if (mask > 0) { profile_properties.SetExcludableClasses(combination_index++, mask); } } } } /** * TODO: Refactor this function into smaller functions for better readability. * * This function is the entry point for the whole extraction process. The goal of the extraction * step is to filter and convert the OSM geometry to something more fitting for routing. * That includes: * - extracting turn restrictions * - splitting ways into (directional!) edge segments * - checking if nodes are barriers or traffic signal * - discarding all tag information: All relevant type information for nodes/ways * is extracted at this point. * * The result of this process are the following files: * .names : Names of all streets, stored as long consecutive string with prefix sum based index * .osrm : Nodes and edges in a intermediate format that easy to digest for osrm-contract * .restrictions : Turn restrictions that are used by osrm-contract to construct the edge-expanded * graph * */ int Extractor::run(ScriptingEnvironment &scripting_environment) { util::LogPolicy::GetInstance().Unmute(); const unsigned recommended_num_threads = tbb::task_scheduler_init::default_num_threads(); const auto number_of_threads = std::min(recommended_num_threads, config.requested_num_threads); tbb::task_scheduler_init init(number_of_threads ? number_of_threads : tbb::task_scheduler_init::automatic); BOOST_ASSERT(init.is_active()); LaneDescriptionMap turn_lane_map; std::vector turn_restrictions; std::vector conditional_turn_restrictions; std::vector unresolved_maneuver_overrides; std::tie(turn_lane_map, turn_restrictions, conditional_turn_restrictions, unresolved_maneuver_overrides) = ParseOSMData(scripting_environment, number_of_threads); // Transform the node-based graph that OSM is based on into an edge-based graph // that is better for routing. Every edge becomes a node, and every valid // movement (e.g. turn from A->B, and B->A) becomes an edge util::Log() << "Generating edge-expanded graph representation"; TIMER_START(expansion); EdgeBasedNodeDataContainer edge_based_nodes_container; std::vector edge_based_node_segments; util::DeallocatingVector edge_based_edge_list; std::vector node_is_startpoint; std::vector edge_based_node_weights; std::uint32_t ebg_connectivity_checksum = 0; // Create a node-based graph from the OSRM file NodeBasedGraphFactory node_based_graph_factory(config.GetPath(".osrm"), scripting_environment, turn_restrictions, conditional_turn_restrictions, unresolved_maneuver_overrides); util::Log() << "Find segregated edges in node-based graph ..." << std::flush; TIMER_START(segregated); util::NameTable names(config.GetPath(".osrm.names").string()); auto segregated_edges = guidance::findSegregatedNodes(node_based_graph_factory, names); TIMER_STOP(segregated); util::Log() << "ok, after " << TIMER_SEC(segregated) << "s"; util::Log() << "Segregated edges count = " << segregated_edges.size(); util::Log() << "Writing nodes for nodes-based and edges-based graphs ..."; auto const &coordinates = node_based_graph_factory.GetCoordinates(); files::writeNodes( config.GetPath(".osrm.nbg_nodes"), coordinates, node_based_graph_factory.GetOsmNodes()); node_based_graph_factory.ReleaseOsmNodes(); auto const &node_based_graph = node_based_graph_factory.GetGraph(); // The osrm-partition tool requires the compressed node based graph with an embedding. // // The `Run` function above re-numbers non-reverse compressed node based graph edges // to a continuous range so that the nodes in the edge based graph are continuous. // // Luckily node based node ids still coincide with the coordinate array. // That's the reason we can only here write out the final compressed node based graph. // Dumps to file asynchronously and makes sure we wait for its completion. std::future compressed_node_based_graph_writing; BOOST_SCOPE_EXIT_ALL(&) { if (compressed_node_based_graph_writing.valid()) compressed_node_based_graph_writing.wait(); }; compressed_node_based_graph_writing = std::async(std::launch::async, [&] { WriteCompressedNodeBasedGraph( config.GetPath(".osrm.cnbg").string(), node_based_graph, coordinates); }); node_based_graph_factory.GetCompressedEdges().PrintStatistics(); const auto &barrier_nodes = node_based_graph_factory.GetBarriers(); const auto &traffic_signals = node_based_graph_factory.GetTrafficSignals(); // stealing the annotation data from the node-based graph edge_based_nodes_container = EdgeBasedNodeDataContainer({}, std::move(node_based_graph_factory.GetAnnotationData())); conditional_turn_restrictions = removeInvalidRestrictions(std::move(conditional_turn_restrictions), node_based_graph); const auto number_of_node_based_nodes = node_based_graph.GetNumberOfNodes(); const util::NameTable name_table(config.GetPath(".osrm.names").string()); const auto number_of_edge_based_nodes = BuildEdgeExpandedGraph(node_based_graph, coordinates, node_based_graph_factory.GetCompressedEdges(), barrier_nodes, traffic_signals, turn_restrictions, conditional_turn_restrictions, segregated_edges, name_table, unresolved_maneuver_overrides, turn_lane_map, scripting_environment, edge_based_nodes_container, edge_based_node_segments, node_is_startpoint, edge_based_node_weights, edge_based_edge_list, ebg_connectivity_checksum); ProcessGuidanceTurns(node_based_graph, edge_based_nodes_container, coordinates, node_based_graph_factory.GetCompressedEdges(), barrier_nodes, turn_restrictions, conditional_turn_restrictions, name_table, std::move(turn_lane_map), scripting_environment); TIMER_STOP(expansion); // output the geometry of the node-based graph, needs to be done after the last usage, since it // destroys internal containers files::writeSegmentData(config.GetPath(".osrm.geometry"), *node_based_graph_factory.GetCompressedEdges().ToSegmentData()); util::Log() << "Saving edge-based node weights to file."; TIMER_START(timer_write_node_weights); { storage::io::FileWriter writer(config.GetPath(".osrm.enw"), storage::io::FileWriter::GenerateFingerprint); storage::serialization::write(writer, edge_based_node_weights); } TIMER_STOP(timer_write_node_weights); util::Log() << "Done writing. (" << TIMER_SEC(timer_write_node_weights) << ")"; util::Log() << "Computing strictly connected components ..."; FindComponents(number_of_edge_based_nodes, edge_based_edge_list, edge_based_node_segments, edge_based_nodes_container); util::Log() << "Building r-tree ..."; TIMER_START(rtree); BuildRTree(std::move(edge_based_node_segments), std::move(node_is_startpoint), coordinates); TIMER_STOP(rtree); files::writeNodeData(config.GetPath(".osrm.ebg_nodes"), edge_based_nodes_container); util::Log() << "Writing edge-based-graph edges ... " << std::flush; TIMER_START(write_edges); files::writeEdgeBasedGraph(config.GetPath(".osrm.ebg"), number_of_edge_based_nodes, edge_based_edge_list, ebg_connectivity_checksum); TIMER_STOP(write_edges); util::Log() << "ok, after " << TIMER_SEC(write_edges) << "s"; util::Log() << "Processed " << edge_based_edge_list.size() << " edges"; const auto nodes_per_second = static_cast(number_of_node_based_nodes / TIMER_SEC(expansion)); const auto edges_per_second = static_cast((number_of_edge_based_nodes) / TIMER_SEC(expansion)); util::Log() << "Expansion: " << nodes_per_second << " nodes/sec and " << edges_per_second << " edges/sec"; util::Log() << "To prepare the data for routing, run: " << "./osrm-contract " << config.GetPath(".osrm"); return 0; } std::tuple, std::vector, std::vector> Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, const unsigned number_of_threads) { TIMER_START(extracting); util::Log() << "Input file: " << config.input_path.filename().string(); if (!config.profile_path.empty()) { util::Log() << "Profile: " << config.profile_path.filename().string(); } util::Log() << "Threads: " << number_of_threads; const osmium::io::File input_file(config.input_path.string()); osmium::thread::Pool pool(number_of_threads); util::Log() << "Parsing in progress.."; TIMER_START(parsing); { // Parse OSM header osmium::io::Reader reader(input_file, pool, osmium::osm_entity_bits::nothing); osmium::io::Header header = reader.header(); std::string generator = header.get("generator"); if (generator.empty()) { generator = "unknown tool"; } util::Log() << "input file generated by " << generator; // write .timestamp data file std::string timestamp = header.get("osmosis_replication_timestamp"); if (timestamp.empty()) { timestamp = "n/a"; } util::Log() << "timestamp: " << timestamp; storage::io::FileWriter timestamp_file(config.GetPath(".osrm.timestamp"), storage::io::FileWriter::GenerateFingerprint); timestamp_file.WriteFrom(timestamp.c_str(), timestamp.length()); } // Extraction containers and restriction parser ExtractionContainers extraction_containers; ExtractorCallbacks::ClassesMap classes_map; LaneDescriptionMap turn_lane_map; auto extractor_callbacks = std::make_unique(extraction_containers, classes_map, turn_lane_map, scripting_environment.GetProfileProperties()); // get list of supported relation types auto relation_types = scripting_environment.GetRelations(); std::sort(relation_types.begin(), relation_types.end()); std::vector restrictions = scripting_environment.GetRestrictions(); // setup restriction parser const RestrictionParser restriction_parser( scripting_environment.GetProfileProperties().use_turn_restrictions, config.parse_conditionals, restrictions); const ManeuverOverrideRelationParser maneuver_override_parser; // OSM data reader using SharedBuffer = std::shared_ptr; struct ParsedBuffer { SharedBuffer buffer; std::vector> resulting_nodes; std::vector> resulting_ways; std::vector> resulting_relations; std::vector resulting_restrictions; std::vector resulting_maneuver_overrides; }; ExtractionRelationContainer relations; const auto buffer_reader = [](osmium::io::Reader &reader) { return tbb::filter_t( tbb::filter::serial_in_order, [&reader](tbb::flow_control &fc) { if (auto buffer = reader.read()) { return std::make_shared(std::move(buffer)); } else { fc.stop(); return SharedBuffer{}; } }); }; // Node locations cache (assumes nodes are placed before ways) using osmium_index_type = osmium::index::map::FlexMem; using osmium_location_handler_type = osmium::handler::NodeLocationsForWays; osmium_index_type location_cache; osmium_location_handler_type location_handler(location_cache); tbb::filter_t location_cacher( tbb::filter::serial_in_order, [&location_handler](SharedBuffer buffer) { osmium::apply(buffer->begin(), buffer->end(), location_handler); return buffer; }); // OSM elements Lua parser tbb::filter_t buffer_transformer( tbb::filter::parallel, [&](const SharedBuffer buffer) { ParsedBuffer parsed_buffer; parsed_buffer.buffer = buffer; scripting_environment.ProcessElements(*buffer, restriction_parser, maneuver_override_parser, relations, parsed_buffer.resulting_nodes, parsed_buffer.resulting_ways, parsed_buffer.resulting_restrictions, parsed_buffer.resulting_maneuver_overrides); return parsed_buffer; }); // Parsed nodes and ways handler unsigned number_of_nodes = 0; unsigned number_of_ways = 0; unsigned number_of_restrictions = 0; unsigned number_of_maneuver_overrides = 0; tbb::filter_t buffer_storage( tbb::filter::serial_in_order, [&](const ParsedBuffer &parsed_buffer) { number_of_nodes += parsed_buffer.resulting_nodes.size(); // put parsed objects thru extractor callbacks for (const auto &result : parsed_buffer.resulting_nodes) { extractor_callbacks->ProcessNode(result.first, result.second); } number_of_ways += parsed_buffer.resulting_ways.size(); for (const auto &result : parsed_buffer.resulting_ways) { extractor_callbacks->ProcessWay(result.first, result.second); } number_of_restrictions += parsed_buffer.resulting_restrictions.size(); for (const auto &result : parsed_buffer.resulting_restrictions) { extractor_callbacks->ProcessRestriction(result); } number_of_maneuver_overrides = parsed_buffer.resulting_maneuver_overrides.size(); for (const auto &result : parsed_buffer.resulting_maneuver_overrides) { extractor_callbacks->ProcessManeuverOverride(result); } }); tbb::filter_t> buffer_relation_cache( tbb::filter::parallel, [&](const SharedBuffer buffer) { if (!buffer) return std::shared_ptr{}; auto relations = std::make_shared(); for (auto entity = buffer->cbegin(), end = buffer->cend(); entity != end; ++entity) { if (entity->type() != osmium::item_type::relation) continue; const auto &rel = static_cast(*entity); const char *rel_type = rel.get_value_by_key("type"); if (!rel_type || !std::binary_search( relation_types.begin(), relation_types.end(), std::string(rel_type))) continue; ExtractionRelation extracted_rel({rel.id(), osmium::item_type::relation}); for (auto const &t : rel.tags()) extracted_rel.attributes.emplace_back(std::make_pair(t.key(), t.value())); for (auto const &m : rel.members()) { ExtractionRelation::OsmIDTyped const mid(m.ref(), m.type()); extracted_rel.AddMember(mid, m.role()); relations->AddRelationMember(extracted_rel.id, mid); } relations->AddRelation(std::move(extracted_rel)); }; return relations; }); unsigned number_of_relations = 0; tbb::filter_t, void> buffer_storage_relation( tbb::filter::serial_in_order, [&](const std::shared_ptr parsed_relations) { number_of_relations += parsed_relations->GetRelationsNum(); relations.Merge(std::move(*parsed_relations)); }); // Parse OSM elements with parallel transformer // Number of pipeline tokens that yielded the best speedup was about 1.5 * num_cores const auto num_threads = tbb::task_scheduler_init::default_num_threads() * 1.5; const auto read_meta = config.use_metadata ? osmium::io::read_meta::yes : osmium::io::read_meta::no; { // Relations reading pipeline util::Log() << "Parse relations ..."; osmium::io::Reader reader(input_file, pool, osmium::osm_entity_bits::relation, read_meta); tbb::parallel_pipeline( num_threads, buffer_reader(reader) & buffer_relation_cache & buffer_storage_relation); } { // Nodes and ways reading pipeline util::Log() << "Parse ways and nodes ..."; osmium::io::Reader reader(input_file, pool, osmium::osm_entity_bits::node | osmium::osm_entity_bits::way | osmium::osm_entity_bits::relation, read_meta); const auto pipeline = scripting_environment.HasLocationDependentData() && config.use_locations_cache ? buffer_reader(reader) & location_cacher & buffer_transformer & buffer_storage : buffer_reader(reader) & buffer_transformer & buffer_storage; tbb::parallel_pipeline(num_threads, pipeline); } TIMER_STOP(parsing); util::Log() << "Parsing finished after " << TIMER_SEC(parsing) << " seconds"; util::Log() << "Raw input contains " << number_of_nodes << " nodes, " << number_of_ways << " ways, and " << number_of_relations << " relations, " << number_of_restrictions << " restrictions"; extractor_callbacks.reset(); if (extraction_containers.all_edges_list.empty()) { throw util::exception(std::string("There are no edges remaining after parsing.") + SOURCE_REF); } extraction_containers.PrepareData(scripting_environment, config.GetPath(".osrm").string(), config.GetPath(".osrm.names").string()); auto profile_properties = scripting_environment.GetProfileProperties(); SetClassNames(scripting_environment.GetClassNames(), classes_map, profile_properties); auto excludable_classes = scripting_environment.GetExcludableClasses(); SetExcludableClasses(classes_map, excludable_classes, profile_properties); files::writeProfileProperties(config.GetPath(".osrm.properties").string(), profile_properties); TIMER_STOP(extracting); util::Log() << "extraction finished after " << TIMER_SEC(extracting) << "s"; return std::make_tuple(std::move(turn_lane_map), std::move(extraction_containers.unconditional_turn_restrictions), std::move(extraction_containers.conditional_turn_restrictions), std::move(extraction_containers.internal_maneuver_overrides)); } void Extractor::FindComponents(unsigned number_of_edge_based_nodes, const util::DeallocatingVector &input_edge_list, const std::vector &input_node_segments, EdgeBasedNodeDataContainer &nodes_container) const { using InputEdge = util::static_graph_details::SortableEdgeWithData; using UncontractedGraph = util::StaticGraph; std::vector edges; edges.reserve(input_edge_list.size() * 2); for (const auto &edge : input_edge_list) { BOOST_ASSERT_MSG(static_cast(std::max(edge.data.weight, 1)) > 0, "edge distance < 1"); BOOST_ASSERT(edge.source < number_of_edge_based_nodes); BOOST_ASSERT(edge.target < number_of_edge_based_nodes); if (edge.data.forward) { edges.push_back({edge.source, edge.target}); } if (edge.data.backward) { edges.push_back({edge.target, edge.source}); } } // Connect forward and backward nodes of each edge to enforce // forward and backward edge-based nodes be in one strongly-connected component for (const auto &segment : input_node_segments) { if (segment.reverse_segment_id.enabled) { BOOST_ASSERT(segment.forward_segment_id.id < number_of_edge_based_nodes); BOOST_ASSERT(segment.reverse_segment_id.id < number_of_edge_based_nodes); edges.push_back({segment.forward_segment_id.id, segment.reverse_segment_id.id}); edges.push_back({segment.reverse_segment_id.id, segment.forward_segment_id.id}); } } tbb::parallel_sort(edges.begin(), edges.end()); edges.erase(std::unique(edges.begin(), edges.end()), edges.end()); auto uncontracted_graph = UncontractedGraph(number_of_edge_based_nodes, edges); TarjanSCC component_search(uncontracted_graph); component_search.Run(); for (NodeID node_id = 0; node_id < number_of_edge_based_nodes; ++node_id) { const auto forward_component = component_search.GetComponentID(node_id); const auto component_size = component_search.GetComponentSize(forward_component); const auto is_tiny = component_size < config.small_component_size; BOOST_ASSERT(node_id < nodes_container.NumberOfNodes()); nodes_container.nodes[node_id].component_id = {1 + forward_component, is_tiny}; } } /** \brief Building an edge-expanded graph from node-based input and turn restrictions */ EdgeID Extractor::BuildEdgeExpandedGraph( // input data const util::NodeBasedDynamicGraph &node_based_graph, const std::vector &coordinates, const CompressedEdgeContainer &compressed_edge_container, const std::unordered_set &barrier_nodes, const std::unordered_set &traffic_signals, const std::vector &turn_restrictions, const std::vector &conditional_turn_restrictions, const std::unordered_set &segregated_edges, const util::NameTable &name_table, const std::vector &maneuver_overrides, const LaneDescriptionMap &turn_lane_map, // for calculating turn penalties ScriptingEnvironment &scripting_environment, // output data EdgeBasedNodeDataContainer &edge_based_nodes_container, std::vector &edge_based_node_segments, std::vector &node_is_startpoint, std::vector &edge_based_node_weights, util::DeallocatingVector &edge_based_edge_list, std::uint32_t &connectivity_checksum) { EdgeBasedGraphFactory edge_based_graph_factory(node_based_graph, edge_based_nodes_container, compressed_edge_container, barrier_nodes, traffic_signals, coordinates, name_table, segregated_edges, turn_lane_map); const auto create_edge_based_edges = [&]() { // scoped to relase intermediate datastructures right after the call std::vector node_restrictions; for (auto const &t : turn_restrictions) if (t.Type() == RestrictionType::NODE_RESTRICTION) node_restrictions.push_back(t); std::vector conditional_node_restrictions; for (auto const &t : conditional_turn_restrictions) if (t.Type() == RestrictionType::NODE_RESTRICTION) conditional_node_restrictions.push_back(t); RestrictionMap via_node_restriction_map(node_restrictions, IndexNodeByFromAndVia()); WayRestrictionMap via_way_restriction_map(conditional_turn_restrictions); ConditionalRestrictionMap conditional_node_restriction_map(conditional_node_restrictions, IndexNodeByFromAndVia()); edge_based_graph_factory.Run(scripting_environment, config.GetPath(".osrm.turn_weight_penalties").string(), config.GetPath(".osrm.turn_duration_penalties").string(), config.GetPath(".osrm.turn_penalties_index").string(), config.GetPath(".osrm.cnbg_to_ebg").string(), config.GetPath(".osrm.restrictions").string(), config.GetPath(".osrm.maneuver_overrides").string(), via_node_restriction_map, conditional_node_restriction_map, via_way_restriction_map, maneuver_overrides); return edge_based_graph_factory.GetNumberOfEdgeBasedNodes(); }; const auto number_of_edge_based_nodes = create_edge_based_edges(); edge_based_graph_factory.GetEdgeBasedEdges(edge_based_edge_list); edge_based_graph_factory.GetEdgeBasedNodeSegments(edge_based_node_segments); edge_based_graph_factory.GetStartPointMarkers(node_is_startpoint); edge_based_graph_factory.GetEdgeBasedNodeWeights(edge_based_node_weights); connectivity_checksum = edge_based_graph_factory.GetConnectivityChecksum(); return number_of_edge_based_nodes; } /** \brief Building rtree-based nearest-neighbor data structure Saves tree into '.ramIndex' and leaves into '.fileIndex'. */ void Extractor::BuildRTree(std::vector edge_based_node_segments, std::vector node_is_startpoint, const std::vector &coordinates) { util::Log() << "Constructing r-tree of " << edge_based_node_segments.size() << " segments build on-top of " << coordinates.size() << " coordinates"; BOOST_ASSERT(node_is_startpoint.size() == edge_based_node_segments.size()); // Filter node based edges based on startpoint auto out_iter = edge_based_node_segments.begin(); auto in_iter = edge_based_node_segments.begin(); for (auto index : util::irange(0UL, node_is_startpoint.size())) { BOOST_ASSERT(in_iter != edge_based_node_segments.end()); if (node_is_startpoint[index]) { *out_iter = *in_iter; out_iter++; } in_iter++; } auto new_size = out_iter - edge_based_node_segments.begin(); if (new_size == 0) { throw util::exception("There are no snappable edges left after processing. Are you " "setting travel modes correctly in the profile? Cannot continue." + SOURCE_REF); } edge_based_node_segments.resize(new_size); TIMER_START(construction); util::StaticRTree rtree(edge_based_node_segments, config.GetPath(".osrm.ramIndex").string(), config.GetPath(".osrm.fileIndex").string(), coordinates); TIMER_STOP(construction); util::Log() << "finished r-tree construction in " << TIMER_SEC(construction) << " seconds"; } void Extractor::WriteCompressedNodeBasedGraph(const std::string &path, const util::NodeBasedDynamicGraph &graph, const std::vector &coordinates) { const auto fingerprint = storage::io::FileWriter::GenerateFingerprint; storage::io::FileWriter writer{path, fingerprint}; // Writes: | Fingerprint | #e | #n | edges | coordinates | // - uint64: number of edges (from, to) pairs // - uint64: number of nodes and therefore also coordinates // - (uint32_t, uint32_t): num_edges * edges // - (int32_t, int32_t: num_nodes * coordinates (lon, lat) const auto num_edges = graph.GetNumberOfEdges(); const auto num_nodes = graph.GetNumberOfNodes(); BOOST_ASSERT_MSG(num_nodes == coordinates.size(), "graph and embedding out of sync"); writer.WriteElementCount64(num_edges); writer.WriteElementCount64(num_nodes); // For all nodes iterate over its edges and dump (from, to) pairs for (const NodeID from_node : util::irange(0u, num_nodes)) { for (const EdgeID edge : graph.GetAdjacentEdgeRange(from_node)) { const auto to_node = graph.GetTarget(edge); writer.WriteOne(from_node); writer.WriteOne(to_node); } } // FIXME this is unneccesary: We have this data for (const auto &qnode : coordinates) { writer.WriteOne(qnode.lon); writer.WriteOne(qnode.lat); } } template auto convertIDMapToVector(const Map &map) { std::vector result(map.size()); for (const auto &pair : map) { BOOST_ASSERT(pair.second < map.size()); result[pair.second] = pair.first; } return result; } void Extractor::ProcessGuidanceTurns( const util::NodeBasedDynamicGraph &node_based_graph, const extractor::EdgeBasedNodeDataContainer &edge_based_node_container, const std::vector &node_coordinates, const CompressedEdgeContainer &compressed_edge_container, const std::unordered_set &barrier_nodes, const std::vector &turn_restrictions, const std::vector &conditional_turn_restrictions, const util::NameTable &name_table, LaneDescriptionMap lane_description_map, ScriptingEnvironment &scripting_environment) { // Output data osrm::guidance::TurnDataExternalContainer turn_data_container; util::guidance::LaneDataIdMap lane_data_map; osrm::guidance::BearingClassesVector bearing_class_by_node_based_node; osrm::guidance::BearingClassesMap bearing_class_hash; osrm::guidance::EntryClassesMap entry_class_hash; std::uint32_t connectivity_checksum = 0; TIMER_START(turn_annotations); { SuffixTable street_name_suffix_table(scripting_environment); const auto &turn_lanes_data = transformTurnLaneMapIntoArrays(lane_description_map); std::vector node_restrictions; for (auto const &t : turn_restrictions) if (t.Type() == RestrictionType::NODE_RESTRICTION) node_restrictions.push_back(t); RestrictionMap node_restriction_map(node_restrictions, IndexNodeByFromAndVia()); WayRestrictionMap way_restriction_map(conditional_turn_restrictions); osrm::guidance::annotateTurns(node_based_graph, edge_based_node_container, node_coordinates, compressed_edge_container, barrier_nodes, node_restriction_map, way_restriction_map, name_table, street_name_suffix_table, turn_lanes_data, lane_description_map, lane_data_map, turn_data_container, bearing_class_by_node_based_node, bearing_class_hash, entry_class_hash, connectivity_checksum); } TIMER_STOP(turn_annotations); util::Log() << "Guidance turn annotations took " << TIMER_SEC(turn_annotations) << "s"; util::Log() << "Writing Intersection Classification Data"; TIMER_START(write_intersections); files::writeIntersections( config.GetPath(".osrm.icd").string(), IntersectionBearingsContainer{bearing_class_by_node_based_node, convertIDMapToVector(bearing_class_hash.data)}, convertIDMapToVector(entry_class_hash.data)); TIMER_STOP(write_intersections); util::Log() << "ok, after " << TIMER_SEC(write_intersections) << "s"; util::Log() << "Writing Turns and Lane Data..."; TIMER_START(write_guidance_data); storage::io::FileWriter writer(config.GetPath(".osrm.tld").string(), storage::io::FileWriter::GenerateFingerprint); storage::serialization::write(writer, convertIDMapToVector(lane_data_map.data)); { // Turn lanes handler modifies lane_description_map, so another transformation is needed std::vector turn_lane_offsets; std::vector turn_lane_masks; std::tie(turn_lane_offsets, turn_lane_masks) = transformTurnLaneMapIntoArrays(lane_description_map); files::writeTurnLaneDescriptions( config.GetPath(".osrm.tls"), turn_lane_offsets, turn_lane_masks); } osrm::guidance::files::writeTurnData( config.GetPath(".osrm.edges").string(), turn_data_container, connectivity_checksum); TIMER_STOP(write_guidance_data); util::Log() << "ok, after " << TIMER_SEC(write_guidance_data) << "s"; } } // namespace extractor } // namespace osrm