From 1c1bfd75411cf7ffb95f6f19fcf4bef9d2ae1ae7 Mon Sep 17 00:00:00 2001 From: Moritz Kobitzsch Date: Thu, 7 Jan 2016 10:33:47 +0100 Subject: [PATCH] Fix routing when start and target are on the same segment Fixes issue #1864. Given the simple set-up: a --> b --> c ^-----------| This would translate into an edge based graph (ab) -> (bc), (bc) -> (ca), (ca) -> (ab). Starting at the end of the one-way street (ab) and going to the beginning, the query has to find a self-loop within the graph (ab) -> (bc) -> (ca) -> (ab), as both nodes map to the same segment (ab). --- CMakeLists.txt | 5 +- features/support/data.rb | 2 +- features/testbot/distance_matrix.feature | 28 ++- features/testbot/via.feature | 45 +++- include/contractor/contractor.hpp | 188 +++++++++++----- include/contractor/processing_chain.hpp | 3 + .../routing_algorithms/alternative_path.hpp | 61 ++++- .../direct_shortest_path.hpp | 8 +- .../routing_algorithms/many_to_many.hpp | 17 +- .../routing_algorithms/routing_base.hpp | 208 ++++++++++++++---- .../routing_algorithms/shortest_path.hpp | 158 +++---------- .../extractor/edge_based_graph_factory.hpp | 8 +- include/extractor/extractor.hpp | 12 +- include/extractor/extractor_options.hpp | 5 + include/util/io.hpp | 127 +++++++++++ src/contractor/processing_chain.cpp | 42 +++- src/extractor/edge_based_graph_factory.cpp | 16 ++ src/extractor/extractor.cpp | 42 ++-- src/extractor/extractor_options.cpp | 4 + src/tools/extract.cpp | 2 +- unit_tests/io/io.cpp | 43 ++++ unit_tests/io_tests.cpp | 7 + 22 files changed, 744 insertions(+), 287 deletions(-) create mode 100644 include/util/io.hpp create mode 100644 unit_tests/io/io.cpp create mode 100644 unit_tests/io_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a1cf4e87..763842cf8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ add_custom_target(FingerPrintConfigure ALL ${CMAKE_COMMAND} COMMENT "Configuring revision fingerprint" VERBATIM) -add_custom_target(tests DEPENDS engine-tests extractor-tests util-tests) +add_custom_target(tests DEPENDS engine-tests extractor-tests util-tests io-tests) add_custom_target(benchmarks DEPENDS rtree-bench) set(BOOST_COMPONENTS date_time filesystem iostreams program_options regex system thread unit_test_framework) @@ -61,6 +61,7 @@ file(GLOB EngineGlob src/engine/*.cpp src/engine/**/*.cpp) file(GLOB ExtractorTestsGlob unit_tests/extractor/*.cpp) file(GLOB EngineTestsGlob unit_tests/engine/*.cpp) file(GLOB UtilTestsGlob unit_tests/util/*.cpp) +file(GLOB IOTestsGlob unit_tests/io/*.cpp) add_library(UTIL OBJECT ${UtilGlob}) add_library(EXTRACTOR OBJECT ${ExtractorGlob}) @@ -85,6 +86,7 @@ target_link_libraries(osrm-routed OSRM) add_executable(engine-tests EXCLUDE_FROM_ALL unit_tests/engine_tests.cpp ${EngineTestsGlob} $ $ $) add_executable(extractor-tests EXCLUDE_FROM_ALL unit_tests/extractor_tests.cpp ${ExtractorTestsGlob} $ $) add_executable(util-tests EXCLUDE_FROM_ALL unit_tests/util_tests.cpp ${UtilTestsGlob} $ $) +add_executable(io-tests EXCLUDE_FROM_ALL unit_tests/io_tests.cpp ${IOTestsGlob} $) # Benchmarks add_executable(rtree-bench EXCLUDE_FROM_ALL src/benchmarks/static_rtree.cpp $ $) @@ -248,6 +250,7 @@ target_link_libraries(engine-tests ${Boost_LIBRARIES}) target_link_libraries(extractor-tests ${Boost_LIBRARIES}) target_link_libraries(util-tests ${Boost_LIBRARIES}) target_link_libraries(rtree-bench ${Boost_LIBRARIES}) +target_link_libraries(io-tests ${Boost_LIBRARIES}) find_package(Threads REQUIRED) target_link_libraries(osrm-extract ${CMAKE_THREAD_LIBS_INIT}) diff --git a/features/support/data.rb b/features/support/data.rb index eabd934bd..adc3d118c 100644 --- a/features/support/data.rb +++ b/features/support/data.rb @@ -263,7 +263,7 @@ def extract_data raise ExtractError.new $?.exitstatus, "osrm-extract exited with code #{$?.exitstatus}." end begin - ["osrm","osrm.names","osrm.restrictions","osrm.ebg","osrm.edges","osrm.fileIndex","osrm.geometry","osrm.nodes","osrm.ramIndex"].each do |file| + ["osrm","osrm.names","osrm.restrictions","osrm.ebg","osrm.enw","osrm.edges","osrm.fileIndex","osrm.geometry","osrm.nodes","osrm.ramIndex"].each do |file| log "Renaming #{osm_file}.#{file} to #{extracted_file}.#{file}", :preprocess File.rename "#{osm_file}.#{file}", "#{extracted_file}.#{file}" end diff --git a/features/testbot/distance_matrix.feature b/features/testbot/distance_matrix.feature index 491012623..d02136ef8 100644 --- a/features/testbot/distance_matrix.feature +++ b/features/testbot/distance_matrix.feature @@ -29,7 +29,7 @@ Feature: Basic Distance Matrix | ab | primary | | bc | secondary | | cd | tertiary | - + When I request a travel time matrix I should get | | a | b | c | d | | a | 0 | 100 | 300 | 600 | @@ -49,7 +49,7 @@ Feature: Basic Distance Matrix | | a | b | | a | 0 | 95 +- 10 | | b | 95 ~10% | 0 | - + Scenario: Testbot - Travel time matrix of small grid Given the node map | a | b | c | @@ -82,7 +82,7 @@ Feature: Basic Distance Matrix | | a | b | | a | 0 | 100 | | b | | 0 | - + Scenario: Testbot - Travel time matrix of network with oneways Given the node map | x | a | b | y | @@ -136,7 +136,7 @@ Feature: Basic Distance Matrix | a | 100 | 200 | 300 | | b | 0 | 100 | 200 | - Scenario: Testbog - All coordinates are from same small component + Scenario: Testbot - All coordinates are from same small component Given a grid size of 300 meters Given the extract extra arguments "--small-component-size 4" Given the node map @@ -156,7 +156,7 @@ Feature: Basic Distance Matrix | f | 0 | 300 | | g | 300 | 0 | - Scenario: Testbog - Coordinates are from different small component and snap to big CC + Scenario: Testbot - Coordinates are from different small component and snap to big CC Given a grid size of 300 meters Given the extract extra arguments "--small-component-size 4" Given the node map @@ -179,3 +179,21 @@ Feature: Basic Distance Matrix | h | 0 | 300 | 0 | 300 | | i | 300 | 0 | 300 | 0 | + Scenario: Testbot - Travel time matrix with loops + Given the node map + | a | 1 | 2 | b | + | d | 4 | 3 | c | + + And the ways + | nodes | oneway | + | ab | yes | + | bc | yes | + | cd | yes | + | da | yes | + + When I request a travel time matrix I should get + | | 1 | 2 | 3 | 4 | + | 1 | 0 | 100 +-1 | 400 +-1 | 500 +-1 | + | 2 | 700 +-1 | 0 | 300 +-1 | 400 +-1 | + | 3 | 400 +-1 | 500 +-1 | 0 | 100 +-1 | + | 4 | 300 +-1 | 400 +-1 | 700 +-1 | 0 | diff --git a/features/testbot/via.feature b/features/testbot/via.feature index 2168c741f..3af38a9d6 100644 --- a/features/testbot/via.feature +++ b/features/testbot/via.feature @@ -93,8 +93,6 @@ Feature: Via points | 1,3,2 | ab,bc,cd,cd,de,ef,fa,ab,bc | 1600m +-1 | head,straight,straight,via,right,right,right,right,straight,destination | | 3,2,1 | cd,de,ef,fa,ab,bc,bc,cd,de,ef,fa,ab | 2400m +-1 | head,right,right,right,right,straight,via,straight,right,right,right,right,destination | - # TODO: Remove this ignore when https://github.com/Project-OSRM/osrm-backend/issues/1863 gets fixed - @ignore-platform-mac Scenario: Via points on ring on the same oneway # xa it to avoid only having a single ring, which cna trigger edge cases Given the node map @@ -118,7 +116,6 @@ Feature: Via points | 1,3,2 | ab,ab,bc,cd,da,ab | 1100m +-1 | head,via,right,right,right,right,destination | | 3,2,1 | ab,bc,cd,da,ab,ab,bc,cd,da,ab | 1800m | head,right,right,right,right,via,right,right,right,right,destination | - # See issue #1896 Scenario: Via point at a dead end with oneway Given the node map @@ -162,3 +159,45 @@ Feature: Via points | waypoints | route | | a,1,c | abc,bd,bd,bd,abc | | c,1,a | abc,bd,bd,bd,abc | + + Scenario: Via points on ring on the same oneway, forces one of the vertices to be top node + Given the node map + | a | 1 | 2 | b | + | 8 | | | 3 | + | 7 | | | 4 | + | d | 6 | 5 | c | + + And the ways + | nodes | oneway | + | ab | yes | + | bc | yes | + | cd | yes | + | da | yes | + + When I route I should get + | waypoints | route | distance | turns | + | 2,1 | ab,bc,cd,da,ab | 1100m +-1 | head,right,right,right,right,destination | + | 4,3 | bc,cd,da,ab,bc | 1100m +-1 | head,right,right,right,right,destination | + | 6,5 | cd,da,ab,bc,cd | 1100m +-1 | head,right,right,right,right,destination | + | 8,7 | da,ab,bc,cd,da | 1100m +-1 | head,right,right,right,right,destination | + + Scenario: Multiple Via points on ring on the same oneway, forces one of the vertices to be top node + Given the node map + | a | 1 | 2 | 3 | b | + | | | | | 4 | + | | | | | 5 | + | | | | | 6 | + | d | 9 | 8 | 7 | c | + + And the ways + | nodes | oneway | + | ab | yes | + | bc | yes | + | cd | yes | + | da | yes | + + When I route I should get + | waypoints | route | distance | turns | + | 3,2,1 | ab,bc,cd,da,ab,ab,bc,cd,da,ab | 3000m +-1 | head,right,right,right,right,via,right,right,right,right,destination | + | 6,5,4 | bc,cd,da,ab,bc,bc,cd,da,ab,bc | 3000m +-1 | head,right,right,right,right,via,right,right,right,right,destination | + | 9,8,7 | cd,da,ab,bc,cd,cd,da,ab,bc,cd | 3000m +-1 | head,right,right,right,right,via,right,right,right,right,destination | diff --git a/include/contractor/contractor.hpp b/include/contractor/contractor.hpp index 633a67d41..6e4f8688b 100644 --- a/include/contractor/contractor.hpp +++ b/include/contractor/contractor.hpp @@ -145,8 +145,11 @@ class Contractor } template - Contractor(int nodes, ContainerT &input_edge_list, std::vector &&node_levels_) - : node_levels(std::move(node_levels_)) + Contractor(int nodes, + ContainerT &input_edge_list, + std::vector &&node_levels_, + std::vector &&node_weights_) + : node_levels(std::move(node_levels_)), node_weights(std::move(node_weights_)) { std::vector edges; edges.reserve(input_edge_list.size() * 2); @@ -203,8 +206,7 @@ class Contractor forward_edge.data.shortcut = reverse_edge.data.shortcut = false; forward_edge.data.id = reverse_edge.data.id = id; forward_edge.data.originalEdges = reverse_edge.data.originalEdges = 1; - forward_edge.data.distance = reverse_edge.data.distance = - std::numeric_limits::max(); + forward_edge.data.distance = reverse_edge.data.distance = INVALID_EDGE_WEIGHT; // remove parallel edges while (i < edges.size() && edges[i].source == source && edges[i].target == target) { @@ -223,7 +225,7 @@ class Contractor // merge edges (s,t) and (t,s) into bidirectional edge if (forward_edge.data.distance == reverse_edge.data.distance) { - if ((int)forward_edge.data.distance != std::numeric_limits::max()) + if ((int)forward_edge.data.distance != INVALID_EDGE_WEIGHT) { forward_edge.data.backward = true; edges[edge++] = forward_edge; @@ -231,11 +233,11 @@ class Contractor } else { // insert seperate edges - if (((int)forward_edge.data.distance) != std::numeric_limits::max()) + if (((int)forward_edge.data.distance) != INVALID_EDGE_WEIGHT) { edges[edge++] = forward_edge; } - if ((int)reverse_edge.data.distance != std::numeric_limits::max()) + if ((int)reverse_edge.data.distance != INVALID_EDGE_WEIGHT) { edges[edge++] = reverse_edge; } @@ -360,18 +362,21 @@ class Contractor // Create new priority array std::vector new_node_priority(remaining_nodes.size()); + std::vector new_node_weights(remaining_nodes.size()); // this map gives the old IDs from the new ones, necessary to get a consistent graph // at the end of contraction orig_node_id_from_new_node_id_map.resize(remaining_nodes.size()); // this map gives the new IDs from the old ones, necessary to remap targets from the // remaining graph - std::vector new_node_id_from_orig_id_map(number_of_nodes, UINT_MAX); + std::vector new_node_id_from_orig_id_map(number_of_nodes, SPECIAL_NODEID); for (const auto new_node_id : util::irange(0, remaining_nodes.size())) { auto &node = remaining_nodes[new_node_id]; BOOST_ASSERT(node_priorities.size() > node.id); new_node_priority[new_node_id] = node_priorities[node.id]; + BOOST_ASSERT(node_weights.size() > node.id); + new_node_weights[new_node_id] = node_weights[node.id]; } // build forward and backward renumbering map and remap ids in remaining_nodes @@ -404,9 +409,9 @@ class Contractor new_node_id_from_orig_id_map[target], data}; new_edge.data.is_original_via_node_ID = true; - BOOST_ASSERT_MSG(UINT_MAX != new_node_id_from_orig_id_map[source], + BOOST_ASSERT_MSG(SPECIAL_NODEID != new_node_id_from_orig_id_map[source], "new source id not resolveable"); - BOOST_ASSERT_MSG(UINT_MAX != new_node_id_from_orig_id_map[target], + BOOST_ASSERT_MSG(SPECIAL_NODEID != new_node_id_from_orig_id_map[target], "new target id not resolveable"); new_edge_set.push_back(new_edge); } @@ -420,8 +425,12 @@ class Contractor // Replace old priorities array by new one node_priorities.swap(new_node_priority); // Delete old node_priorities vector + // Due to the scope, these should get cleared automatically? @daniel-j-h do you + // agree? new_node_priority.clear(); new_node_priority.shrink_to_fit(); + + node_weights.swap(new_node_weights); // old Graph is removed contractor_graph.reset(); @@ -581,7 +590,7 @@ class Contractor remaining_nodes.resize(begin_independent_nodes_idx); // unsigned maxdegree = 0; // unsigned avgdegree = 0; - // unsigned mindegree = UINT_MAX; + // unsigned mindegree = SPECIAL_NODEID; // unsigned quaddegree = 0; // // for(unsigned i = 0; i < remaining_nodes.size(); ++i) { @@ -686,8 +695,8 @@ class Contractor new_edge.source = node; new_edge.target = target; } - BOOST_ASSERT_MSG(UINT_MAX != new_edge.source, "Source id invalid"); - BOOST_ASSERT_MSG(UINT_MAX != new_edge.target, "Target id invalid"); + BOOST_ASSERT_MSG(SPECIAL_NODEID != new_edge.source, "Source id invalid"); + BOOST_ASSERT_MSG(SPECIAL_NODEID != new_edge.target, "Target id invalid"); new_edge.data.distance = data.distance; new_edge.data.shortcut = data.shortcut; if (!data.is_original_via_node_ID && !orig_node_id_from_new_node_id_map.empty()) @@ -718,23 +727,55 @@ class Contractor } private: + inline void RelaxNode(const NodeID node, + const NodeID forbidden_node, + const int distance, + ContractorHeap &heap) + { + const short current_hop = heap.GetData(node).hop + 1; + for (auto edge : contractor_graph->GetAdjacentEdgeRange(node)) + { + const ContractorEdgeData &data = contractor_graph->GetEdgeData(edge); + if (!data.forward) + { + continue; + } + const NodeID to = contractor_graph->GetTarget(edge); + if (forbidden_node == to) + { + continue; + } + const int to_distance = distance + data.distance; + + // New Node discovered -> Add to Heap + Node Info Storage + if (!heap.WasInserted(to)) + { + heap.Insert(to, to_distance, ContractorHeapData(current_hop, false)); + } + // Found a shorter Path -> Update distance + else if (to_distance < heap.GetKey(to)) + { + heap.DecreaseKey(to, to_distance); + heap.GetData(to).hop = current_hop; + } + } + } + inline void Dijkstra(const int max_distance, const unsigned number_of_targets, const int maxNodes, - ContractorThreadData *const data, + ContractorThreadData &data, const NodeID middleNode) { - ContractorHeap &heap = data->heap; + ContractorHeap &heap = data.heap; int nodes = 0; unsigned number_of_targets_found = 0; while (!heap.Empty()) { const NodeID node = heap.DeleteMin(); - const int distance = heap.GetKey(node); - const short current_hop = heap.GetData(node).hop + 1; - + const auto distance = heap.GetKey(node); if (++nodes > maxNodes) { return; @@ -754,33 +795,7 @@ class Contractor } } - // iterate over all edges of node - for (auto edge : contractor_graph->GetAdjacentEdgeRange(node)) - { - const ContractorEdgeData &data = contractor_graph->GetEdgeData(edge); - if (!data.forward) - { - continue; - } - const NodeID to = contractor_graph->GetTarget(edge); - if (middleNode == to) - { - continue; - } - const int to_distance = distance + data.distance; - - // New Node discovered -> Add to Heap + Node Info Storage - if (!heap.WasInserted(to)) - { - heap.Insert(to, to_distance, ContractorHeapData(current_hop, false)); - } - // Found a shorter Path -> Update distance - else if (to_distance < heap.GetKey(to)) - { - heap.DecreaseKey(to, to_distance); - heap.GetData(to).hop = current_hop; - } - } + RelaxNode(node, middleNode, distance, heap); } } @@ -815,13 +830,21 @@ class Contractor ContractNode(ContractorThreadData *data, const NodeID node, ContractionStats *stats = nullptr) { ContractorHeap &heap = data->heap; - int inserted_edges_size = data->inserted_edges.size(); + std::size_t inserted_edges_size = data->inserted_edges.size(); std::vector &inserted_edges = data->inserted_edges; + const constexpr bool SHORTCUT_ARC = true; + const constexpr bool FORWARD_DIRECTION_ENABLED = true; + const constexpr bool FORWARD_DIRECTION_DISABLED = false; + const constexpr bool REVERSE_DIRECTION_ENABLED = true; + const constexpr bool REVERSE_DIRECTION_DISABLED = false; for (auto in_edge : contractor_graph->GetAdjacentEdgeRange(node)) { const ContractorEdgeData &in_data = contractor_graph->GetEdgeData(in_edge); const NodeID source = contractor_graph->GetTarget(in_edge); + if (source == node) + continue; + if (RUNSIMULATION) { BOOST_ASSERT(stats != nullptr); @@ -846,22 +869,64 @@ class Contractor continue; } const NodeID target = contractor_graph->GetTarget(out_edge); - const int path_distance = in_data.distance + out_data.distance; + if (node == target) + continue; + + const EdgeWeight path_distance = in_data.distance + out_data.distance; + if (target == source) + { + if (path_distance < node_weights[node]) + { + if (RUNSIMULATION) + { + // make sure to prune better, but keep inserting this loop if it should + // still be the best + // CAREFUL: This only works due to the independent node-setting. This + // guarantees that source is not connected to another node that is + // contracted + node_weights[source] = path_distance + 1; + BOOST_ASSERT(stats != nullptr); + stats->edges_added_count += 2; + stats->original_edges_added_count += + 2 * (out_data.originalEdges + in_data.originalEdges); + } + else + { + // CAREFUL: This only works due to the independent node-setting. This + // guarantees that source is not connected to another node that is + // contracted + node_weights[source] = path_distance; // make sure to prune better + inserted_edges.emplace_back( + source, target, path_distance, + out_data.originalEdges + in_data.originalEdges, node, SHORTCUT_ARC, + FORWARD_DIRECTION_ENABLED, REVERSE_DIRECTION_DISABLED); + + inserted_edges.emplace_back( + target, source, path_distance, + out_data.originalEdges + in_data.originalEdges, node, SHORTCUT_ARC, + FORWARD_DIRECTION_DISABLED, REVERSE_DIRECTION_ENABLED); + } + } + continue; + } max_distance = std::max(max_distance, path_distance); if (!heap.WasInserted(target)) { - heap.Insert(target, INT_MAX, ContractorHeapData(0, true)); + heap.Insert(target, INVALID_EDGE_WEIGHT, ContractorHeapData(0, true)); ++number_of_targets; } } if (RUNSIMULATION) { - Dijkstra(max_distance, number_of_targets, 1000, data, node); + const int constexpr SIMULATION_SEARCH_SPACE_SIZE = 1000; + Dijkstra(max_distance, number_of_targets, SIMULATION_SEARCH_SPACE_SIZE, *data, + node); } else { - Dijkstra(max_distance, number_of_targets, 2000, data, node); + const int constexpr FULL_SEARCH_SPACE_SIZE = 2000; + Dijkstra(max_distance, number_of_targets, FULL_SEARCH_SPACE_SIZE, *data, node); } for (auto out_edge : contractor_graph->GetAdjacentEdgeRange(node)) { @@ -871,6 +936,8 @@ class Contractor continue; } const NodeID target = contractor_graph->GetTarget(out_edge); + if (target == node) + continue; const int path_distance = in_data.distance + out_data.distance; const int distance = heap.GetKey(target); if (path_distance < distance) @@ -886,22 +953,26 @@ class Contractor { inserted_edges.emplace_back(source, target, path_distance, out_data.originalEdges + in_data.originalEdges, - node, true, true, false); + node, SHORTCUT_ARC, FORWARD_DIRECTION_ENABLED, + REVERSE_DIRECTION_DISABLED); inserted_edges.emplace_back(target, source, path_distance, out_data.originalEdges + in_data.originalEdges, - node, true, false, true); + node, SHORTCUT_ARC, FORWARD_DIRECTION_DISABLED, + REVERSE_DIRECTION_ENABLED); } } } } + // Check For One-Way Streets to decide on the creation of self-loops + if (!RUNSIMULATION) { - int iend = inserted_edges.size(); - for (int i = inserted_edges_size; i < iend; ++i) + std::size_t iend = inserted_edges.size(); + for (std::size_t i = inserted_edges_size; i < iend; ++i) { bool found = false; - for (int other = i + 1; other < iend; ++other) + for (std::size_t other = i + 1; other < iend; ++other) { if (inserted_edges[other].source != inserted_edges[i].source) { @@ -1071,6 +1142,13 @@ class Contractor stxxl::vector external_edge_list; std::vector orig_node_id_from_new_node_id_map; std::vector node_levels; + + // A list of weights for every node in the graph. + // The weight represents the cost for a u-turn on the segment in the base-graph in addition to + // its traversal. + // During contraction, self-loops are checked against this node weight to ensure that necessary + // self-loops are added. + std::vector node_weights; std::vector is_core_node; util::XORFastHash fast_hash; }; diff --git a/include/contractor/processing_chain.hpp b/include/contractor/processing_chain.hpp index 42034eb57..e75af29e0 100644 --- a/include/contractor/processing_chain.hpp +++ b/include/contractor/processing_chain.hpp @@ -11,6 +11,8 @@ #include +#include + #include struct lua_State; @@ -43,6 +45,7 @@ class Prepare void ContractGraph(const unsigned max_edge_id, util::DeallocatingVector &edge_based_edge_list, util::DeallocatingVector &contracted_edge_list, + std::vector &&node_weights, std::vector &is_core_node, std::vector &node_levels) const; void WriteCoreNodeMarker(std::vector &&is_core_node) const; diff --git a/include/engine/routing_algorithms/alternative_path.hpp b/include/engine/routing_algorithms/alternative_path.hpp index 70d08b870..7d7f64832 100644 --- a/include/engine/routing_algorithms/alternative_path.hpp +++ b/include/engine/routing_algorithms/alternative_path.hpp @@ -156,8 +156,23 @@ class AlternativeRouting final std::vector packed_forward_path; std::vector packed_reverse_path; - super::RetrievePackedPathFromSingleHeap(forward_heap1, middle_node, packed_forward_path); - super::RetrievePackedPathFromSingleHeap(reverse_heap1, middle_node, packed_reverse_path); + if (upper_bound_to_shortest_path_distance != + forward_heap1.GetKey(middle_node) + reverse_heap1.GetKey(middle_node)) + { + // Self Loop + BOOST_ASSERT(forward_heap1.GetData(middle_node).parent == middle_node && + reverse_heap1.GetData(middle_node).parent == middle_node); + packed_forward_path.push_back(middle_node); + packed_forward_path.push_back(middle_node); + } + else + { + + super::RetrievePackedPathFromSingleHeap(forward_heap1, middle_node, + packed_forward_path); + super::RetrievePackedPathFromSingleHeap(reverse_heap1, middle_node, + packed_reverse_path); + } // this set is is used as an indicator if a node is on the shortest path std::unordered_set nodes_in_path(packed_forward_path.size() + @@ -382,10 +397,13 @@ class AlternativeRouting final int upper_bound_s_v_path_length = INVALID_EDGE_WEIGHT; new_reverse_heap.Insert(via_node, 0, via_node); // compute path by reusing forward search from s + const bool constexpr STALLING_ENABLED = true; + const bool constexpr DO_NOT_FORCE_LOOPS = false; while (!new_reverse_heap.Empty()) { super::RoutingStep(new_reverse_heap, existing_forward_heap, s_v_middle, - upper_bound_s_v_path_length, min_edge_offset, false); + upper_bound_s_v_path_length, min_edge_offset, false, + STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS); } // compute path by reusing backward search from node t NodeID v_t_middle = SPECIAL_NODEID; @@ -394,7 +412,8 @@ class AlternativeRouting final while (!new_forward_heap.Empty()) { super::RoutingStep(new_forward_heap, existing_reverse_heap, v_t_middle, - upper_bound_of_v_t_path_length, min_edge_offset, true); + upper_bound_of_v_t_path_length, min_edge_offset, true, + STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS); } *real_length_of_via_path = upper_bound_s_v_path_length + upper_bound_of_v_t_path_length; @@ -442,10 +461,10 @@ class AlternativeRouting final partially_unpacked_shortest_path.size())) - 1; for (int64_t current_node = 0; (current_node < packed_path_length) && - (partially_unpacked_via_path[current_node] == - partially_unpacked_shortest_path[current_node] && - partially_unpacked_via_path[current_node + 1] == - partially_unpacked_shortest_path[current_node + 1]); + (partially_unpacked_via_path[current_node] == + partially_unpacked_shortest_path[current_node] && + partially_unpacked_via_path[current_node + 1] == + partially_unpacked_shortest_path[current_node + 1]); ++current_node) { EdgeID selected_edge = @@ -600,6 +619,18 @@ class AlternativeRouting final // << " // at distance " << new_distance; } + else + { + // check whether there is a loop present at the node + const auto loop_distance = super::GetLoopWeight(node); + const int new_distance_with_loop = new_distance + loop_distance; + if (loop_distance != INVALID_EDGE_WEIGHT && + new_distance_with_loop <= *upper_bound_to_shortest_path_distance) + { + *middle_node = node; + *upper_bound_to_shortest_path_distance = loop_distance; + } + } } } @@ -655,10 +686,13 @@ class AlternativeRouting final int upper_bound_s_v_path_length = INVALID_EDGE_WEIGHT; // compute path by reusing forward search from s new_reverse_heap.Insert(candidate.node, 0, candidate.node); + const bool constexpr STALLING_ENABLED = true; + const bool constexpr DO_NOT_FORCE_LOOPS = false; while (new_reverse_heap.Size() > 0) { super::RoutingStep(new_reverse_heap, existing_forward_heap, *s_v_middle, - upper_bound_s_v_path_length, min_edge_offset, false); + upper_bound_s_v_path_length, min_edge_offset, false, + STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS); } if (INVALID_EDGE_WEIGHT == upper_bound_s_v_path_length) @@ -673,7 +707,8 @@ class AlternativeRouting final while (new_forward_heap.Size() > 0) { super::RoutingStep(new_forward_heap, existing_reverse_heap, *v_t_middle, - upper_bound_of_v_t_path_length, min_edge_offset, true); + upper_bound_of_v_t_path_length, min_edge_offset, true, + STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS); } if (INVALID_EDGE_WEIGHT == upper_bound_of_v_t_path_length) @@ -841,12 +876,14 @@ class AlternativeRouting final if (!forward_heap3.Empty()) { super::RoutingStep(forward_heap3, reverse_heap3, middle, upper_bound, - min_edge_offset, true); + min_edge_offset, true, STALLING_ENABLED, DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS); } if (!reverse_heap3.Empty()) { super::RoutingStep(reverse_heap3, forward_heap3, middle, upper_bound, - min_edge_offset, false); + min_edge_offset, false, STALLING_ENABLED, DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS); } } return (upper_bound <= t_test_path_length); diff --git a/include/engine/routing_algorithms/direct_shortest_path.hpp b/include/engine/routing_algorithms/direct_shortest_path.hpp index 437d88d27..931b6a5da 100644 --- a/include/engine/routing_algorithms/direct_shortest_path.hpp +++ b/include/engine/routing_algorithms/direct_shortest_path.hpp @@ -93,6 +93,9 @@ class DirectShortestPathRouting final int distance = INVALID_EDGE_WEIGHT; std::vector packed_leg; + const bool constexpr DO_NOT_FORCE_LOOPS = + false; // prevents forcing of loops, since offsets are set correctly + if (super::facade->GetCoreSize() > 0) { engine_working_data.InitializeOrClearSecondThreadLocalStorage( @@ -103,11 +106,12 @@ class DirectShortestPathRouting final reverse_core_heap.Clear(); super::SearchWithCore(forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, - distance, packed_leg); + distance, packed_leg, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS); } else { - super::Search(forward_heap, reverse_heap, distance, packed_leg); + super::Search(forward_heap, reverse_heap, distance, packed_leg, DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS); } // No path found for both target nodes? diff --git a/include/engine/routing_algorithms/many_to_many.hpp b/include/engine/routing_algorithms/many_to_many.hpp index c8311783f..016b5e94b 100644 --- a/include/engine/routing_algorithms/many_to_many.hpp +++ b/include/engine/routing_algorithms/many_to_many.hpp @@ -139,14 +139,21 @@ class ManyToManyRouting final // get target id from bucket entry const unsigned target_id = current_bucket.target_id; const int target_distance = current_bucket.distance; - const EdgeWeight current_distance = - (*result_table)[source_id * number_of_targets + target_id]; + auto ¤t_distance = (*result_table)[source_id * number_of_targets + target_id]; // check if new distance is better const EdgeWeight new_distance = source_distance + target_distance; - if (new_distance >= 0 && new_distance < current_distance) + if (new_distance < 0) { - (*result_table)[source_id * number_of_targets + target_id] = - (source_distance + target_distance); + const EdgeWeight loop_weight = super::GetLoopWeight(node); + const int new_distance_with_loop = new_distance + loop_weight; + if (loop_weight != INVALID_EDGE_WEIGHT && new_distance_with_loop >= 0) + { + current_distance = std::min(current_distance, new_distance_with_loop); + } + } + else if (new_distance < current_distance) + { + (*result_table)[source_id * number_of_targets + target_id] = new_distance; } } } diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp index 19865b494..6f9315d1e 100644 --- a/include/engine/routing_algorithms/routing_base.hpp +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -5,9 +5,17 @@ #include "engine/internal_route_result.hpp" #include "engine/search_engine_data.hpp" #include "extractor/turn_instructions.hpp" +#include "util/typedefs.hpp" #include +#include +#include + +#include +#include +#include +#include #include namespace osrm @@ -71,24 +79,54 @@ template class BasicRoutingInterface void RoutingStep(SearchEngineData::QueryHeap &forward_heap, SearchEngineData::QueryHeap &reverse_heap, NodeID &middle_node_id, - int &upper_bound, - int min_edge_offset, + std::int32_t &upper_bound, + std::int32_t min_edge_offset, const bool forward_direction, - const bool stalling = true) const + const bool stalling, + const bool force_loop_forward, + const bool force_loop_reverse) const { const NodeID node = forward_heap.DeleteMin(); - const int distance = forward_heap.GetKey(node); + const std::int32_t distance = forward_heap.GetKey(node); if (reverse_heap.WasInserted(node)) { - const int new_distance = reverse_heap.GetKey(node) + distance; + const std::int32_t new_distance = reverse_heap.GetKey(node) + distance; if (new_distance < upper_bound) { - if (new_distance >= 0) + if (new_distance >= 0 && + (!force_loop_forward || + forward_heap.GetData(node).parent != + node) // if loops are forced, they are so at the source + && (!force_loop_reverse || reverse_heap.GetData(node).parent != node)) { middle_node_id = node; upper_bound = new_distance; } + else + { + // check whether there is a loop present at the node + for (const auto edge : facade->GetAdjacentEdgeRange(node)) + { + const EdgeData &data = facade->GetEdgeData(edge); + bool forward_directionFlag = + (forward_direction ? data.forward : data.backward); + if (forward_directionFlag) + { + const NodeID to = facade->GetTarget(edge); + if (to == node) + { + const EdgeWeight edge_weight = data.distance; + const std::int32_t loop_distance = new_distance + edge_weight; + if (loop_distance >= 0 && loop_distance < upper_bound) + { + middle_node_id = node; + upper_bound = loop_distance; + } + } + } + } + } } } @@ -111,7 +149,7 @@ template class BasicRoutingInterface if (reverse_flag) { const NodeID to = facade->GetTarget(edge); - const int edge_weight = data.distance; + const EdgeWeight edge_weight = data.distance; BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); @@ -134,7 +172,7 @@ template class BasicRoutingInterface { const NodeID to = facade->GetTarget(edge); - const int edge_weight = data.distance; + const EdgeWeight edge_weight = data.distance; BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); const int to_distance = distance + edge_weight; @@ -155,6 +193,24 @@ template class BasicRoutingInterface } } + inline EdgeWeight GetLoopWeight(NodeID node) const + { + EdgeWeight loop_weight = INVALID_EDGE_WEIGHT; + for (auto edge : facade->GetAdjacentEdgeRange(node)) + { + const auto &data = facade->GetEdgeData(edge); + if (data.forward) + { + const NodeID to = facade->GetTarget(edge); + if (to == node) + { + loop_weight = std::min(loop_weight, data.distance); + } + } + } + return loop_weight; + } + template void UnpackPath(RandomIter packed_path_begin, RandomIter packed_path_end, @@ -188,10 +244,10 @@ template class BasicRoutingInterface // facade->FindEdge does not suffice here in case of shortcuts. // The above explanation unclear? Think! EdgeID smaller_edge_id = SPECIAL_EDGEID; - int edge_weight = std::numeric_limits::max(); + EdgeWeight edge_weight = std::numeric_limits::max(); for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first)) { - const int weight = facade->GetEdgeData(edge_id).distance; + const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; if ((facade->GetTarget(edge_id) == edge.second) && (weight < edge_weight) && facade->GetEdgeData(edge_id).forward) { @@ -207,7 +263,7 @@ template class BasicRoutingInterface { for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second)) { - const int weight = facade->GetEdgeData(edge_id).distance; + const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; if ((facade->GetTarget(edge_id) == edge.first) && (weight < edge_weight) && facade->GetEdgeData(edge_id).backward) { @@ -345,10 +401,10 @@ template class BasicRoutingInterface recursion_stack.pop(); EdgeID smaller_edge_id = SPECIAL_EDGEID; - int edge_weight = std::numeric_limits::max(); + EdgeWeight edge_weight = std::numeric_limits::max(); for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first)) { - const int weight = facade->GetEdgeData(edge_id).distance; + const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; if ((facade->GetTarget(edge_id) == edge.second) && (weight < edge_weight) && facade->GetEdgeData(edge_id).forward) { @@ -361,7 +417,7 @@ template class BasicRoutingInterface { for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second)) { - const int weight = facade->GetEdgeData(edge_id).distance; + const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; if ((facade->GetTarget(edge_id) == edge.first) && (weight < edge_weight) && facade->GetEdgeData(edge_id).backward) { @@ -414,10 +470,23 @@ template class BasicRoutingInterface } // assumes that heaps are already setup correctly. + // ATTENTION: This only works if no additional offset is supplied next to the Phantom Node + // Offsets. + // In case additional offsets are supplied, you might have to force a loop first. + // A forced loop might be necessary, if source and target are on the same segment. + // If this is the case and the offsets of the respective direction are larger for the source + // than the target + // then a force loop is required (e.g. source_phantom.forward_node_id == + // target_phantom.forward_node_id + // && source_phantom.GetForwardWeightPlusOffset() > target_phantom.GetForwardWeightPlusOffset()) + // requires + // a force loop, if the heaps have been initialized with positive offsets. void Search(SearchEngineData::QueryHeap &forward_heap, SearchEngineData::QueryHeap &reverse_heap, - int &distance, - std::vector &packed_leg) const + std::int32_t &distance, + std::vector &packed_leg, + const bool force_loop_forward, + const bool force_loop_reverse) const { NodeID middle = SPECIAL_NODEID; @@ -428,15 +497,18 @@ template class BasicRoutingInterface BOOST_ASSERT(reverse_heap.MinKey() >= 0); // run two-Target Dijkstra routing step. + const constexpr bool STALLING_ENABLED = true; while (0 < (forward_heap.Size() + reverse_heap.Size())) { if (!forward_heap.Empty()) { - RoutingStep(forward_heap, reverse_heap, middle, distance, min_edge_offset, true); + RoutingStep(forward_heap, reverse_heap, middle, distance, min_edge_offset, true, + STALLING_ENABLED, force_loop_forward, force_loop_reverse); } if (!reverse_heap.Empty()) { - RoutingStep(reverse_heap, forward_heap, middle, distance, min_edge_offset, false); + RoutingStep(reverse_heap, forward_heap, middle, distance, min_edge_offset, false, + STALLING_ENABLED, force_loop_reverse, force_loop_forward); } } @@ -450,16 +522,38 @@ template class BasicRoutingInterface BOOST_ASSERT_MSG((SPECIAL_NODEID != middle && INVALID_EDGE_WEIGHT != distance), "no path found"); - RetrievePackedPathFromHeap(forward_heap, reverse_heap, middle, packed_leg); + // make sure to correctly unpack loops + if (distance != forward_heap.GetKey(middle) + reverse_heap.GetKey(middle)) + { + // self loop + BOOST_ASSERT(forward_heap.GetData(middle).parent == middle && + reverse_heap.GetData(middle).parent == middle); + packed_leg.push_back(middle); + packed_leg.push_back(middle); + } + else + { + RetrievePackedPathFromHeap(forward_heap, reverse_heap, middle, packed_leg); + } } // assumes that heaps are already setup correctly. + // A forced loop might be necessary, if source and target are on the same segment. + // If this is the case and the offsets of the respective direction are larger for the source + // than the target + // then a force loop is required (e.g. source_phantom.forward_node_id == + // target_phantom.forward_node_id + // && source_phantom.GetForwardWeightPlusOffset() > target_phantom.GetForwardWeightPlusOffset()) + // requires + // a force loop, if the heaps have been initialized with positive offsets. void SearchWithCore(SearchEngineData::QueryHeap &forward_heap, SearchEngineData::QueryHeap &reverse_heap, SearchEngineData::QueryHeap &forward_core_heap, SearchEngineData::QueryHeap &reverse_core_heap, int &distance, - std::vector &packed_leg) const + std::vector &packed_leg, + const bool force_loop_forward, + const bool force_loop_reverse) const { NodeID middle = SPECIAL_NODEID; @@ -471,6 +565,7 @@ template class BasicRoutingInterface // we only every insert negative offsets for nodes in the forward heap BOOST_ASSERT(reverse_heap.MinKey() >= 0); + const constexpr bool STALLING_ENABLED = true; // run two-Target Dijkstra routing step. while (0 < (forward_heap.Size() + reverse_heap.Size())) { @@ -484,8 +579,8 @@ template class BasicRoutingInterface } else { - RoutingStep(forward_heap, reverse_heap, middle, distance, min_edge_offset, - true); + RoutingStep(forward_heap, reverse_heap, middle, distance, min_edge_offset, true, + STALLING_ENABLED, force_loop_forward, force_loop_reverse); } } if (!reverse_heap.Empty()) @@ -499,7 +594,7 @@ template class BasicRoutingInterface else { RoutingStep(reverse_heap, forward_heap, middle, distance, min_edge_offset, - false); + false, STALLING_ENABLED, force_loop_reverse, force_loop_forward); } } } @@ -548,18 +643,21 @@ template class BasicRoutingInterface BOOST_ASSERT(min_core_edge_offset <= 0); // run two-target Dijkstra routing step on core with termination criterion + const constexpr bool STALLING_DISABLED = false; while (0 < (forward_core_heap.Size() + reverse_core_heap.Size()) && distance > (forward_core_heap.MinKey() + reverse_core_heap.MinKey())) { if (!forward_core_heap.Empty()) { RoutingStep(forward_core_heap, reverse_core_heap, middle, distance, - min_core_edge_offset, true, false); + min_core_edge_offset, true, STALLING_DISABLED, force_loop_forward, + force_loop_reverse); } if (!reverse_core_heap.Empty()) { RoutingStep(reverse_core_heap, forward_core_heap, middle, distance, - min_core_edge_offset, false, false); + min_core_edge_offset, false, STALLING_DISABLED, force_loop_reverse, + force_loop_forward); } } @@ -573,29 +671,45 @@ template class BasicRoutingInterface BOOST_ASSERT_MSG((SPECIAL_NODEID != middle && INVALID_EDGE_WEIGHT != distance), "no path found"); - // we need to unpack sub path from core heaps - if (facade->IsCoreNode(middle)) + if (distance != forward_heap.GetKey(middle) + reverse_heap.GetKey(middle)) { - std::vector packed_core_leg; - RetrievePackedPathFromHeap(forward_core_heap, reverse_core_heap, middle, - packed_core_leg); - BOOST_ASSERT(packed_core_leg.size() > 0); - RetrievePackedPathFromSingleHeap(forward_heap, packed_core_leg.front(), packed_leg); - std::reverse(packed_leg.begin(), packed_leg.end()); - packed_leg.insert(packed_leg.end(), packed_core_leg.begin(), packed_core_leg.end()); - RetrievePackedPathFromSingleHeap(reverse_heap, packed_core_leg.back(), packed_leg); + // self loop + BOOST_ASSERT(forward_heap.GetData(middle).parent == middle && + reverse_heap.GetData(middle).parent == middle); + packed_leg.push_back(middle); + packed_leg.push_back(middle); } else { - RetrievePackedPathFromHeap(forward_heap, reverse_heap, middle, packed_leg); + // we need to unpack sub path from core heaps + if (facade->IsCoreNode(middle)) + { + std::vector packed_core_leg; + RetrievePackedPathFromHeap(forward_core_heap, reverse_core_heap, middle, + packed_core_leg); + BOOST_ASSERT(packed_core_leg.size() > 0); + RetrievePackedPathFromSingleHeap(forward_heap, packed_core_leg.front(), packed_leg); + std::reverse(packed_leg.begin(), packed_leg.end()); + packed_leg.insert(packed_leg.end(), packed_core_leg.begin(), packed_core_leg.end()); + RetrievePackedPathFromSingleHeap(reverse_heap, packed_core_leg.back(), packed_leg); + } + else + { + RetrievePackedPathFromHeap(forward_heap, reverse_heap, middle, packed_leg); + } } } + // Requires the heaps for be empty + // If heaps should be adjusted to be initialized outside of this function, + // the addition of force_loop parameters might be required double get_network_distance(SearchEngineData::QueryHeap &forward_heap, SearchEngineData::QueryHeap &reverse_heap, const PhantomNode &source_phantom, const PhantomNode &target_phantom) const { + BOOST_ASSERT(forward_heap.Empty()); + BOOST_ASSERT(reverse_heap.Empty()); EdgeWeight upper_bound = INVALID_EDGE_WEIGHT; NodeID middle_node = SPECIAL_NODEID; EdgeWeight edge_offset = std::min(0, -source_phantom.GetForwardWeightPlusOffset()); @@ -628,17 +742,19 @@ template class BasicRoutingInterface } // search from s and t till new_min/(1+epsilon) > length_of_shortest_path + const constexpr bool STALLING_ENABLED = true; + const constexpr bool DO_NOT_FORCE_LOOPS = false; while (0 < (forward_heap.Size() + reverse_heap.Size())) { if (0 < forward_heap.Size()) { - RoutingStep(forward_heap, reverse_heap, middle_node, upper_bound, edge_offset, - true); + RoutingStep(forward_heap, reverse_heap, middle_node, upper_bound, edge_offset, true, + STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS); } if (0 < reverse_heap.Size()) { RoutingStep(reverse_heap, forward_heap, middle_node, upper_bound, edge_offset, - false); + false, STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS); } } @@ -646,7 +762,19 @@ template class BasicRoutingInterface if (upper_bound != INVALID_EDGE_WEIGHT) { std::vector packed_leg; - RetrievePackedPathFromHeap(forward_heap, reverse_heap, middle_node, packed_leg); + if (upper_bound != forward_heap.GetKey(middle_node) + reverse_heap.GetKey(middle_node)) + { + // self loop + BOOST_ASSERT(forward_heap.GetData(middle_node).parent == middle_node && + reverse_heap.GetData(middle_node).parent == middle_node); + packed_leg.push_back(middle_node); + packed_leg.push_back(middle_node); + } + else + { + RetrievePackedPathFromHeap(forward_heap, reverse_heap, middle_node, packed_leg); + } + std::vector unpacked_path; PhantomNodes nodes; nodes.source_phantom = source_phantom; diff --git a/include/engine/routing_algorithms/shortest_path.hpp b/include/engine/routing_algorithms/shortest_path.hpp index 2613c3227..d03780fbb 100644 --- a/include/engine/routing_algorithms/shortest_path.hpp +++ b/include/engine/routing_algorithms/shortest_path.hpp @@ -24,6 +24,9 @@ class ShortestPathRouting final using super = BasicRoutingInterface>; using QueryHeap = SearchEngineData::QueryHeap; SearchEngineData &engine_working_data; + const static constexpr bool FORWARD_DIRECTION = true; + const static constexpr bool REVERSE_DIRECTION = false; + const static constexpr bool DO_NOT_FORCE_LOOP = false; public: ShortestPathRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) @@ -33,6 +36,19 @@ class ShortestPathRouting final ~ShortestPathRouting() {} + inline bool + forceLoop(bool forward, const PhantomNode &source_phantom, const PhantomNode &target_phantom) const + { + if (forward) + return source_phantom.forward_node_id == target_phantom.forward_node_id && + source_phantom.GetForwardWeightPlusOffset() > + target_phantom.GetForwardWeightPlusOffset(); + else + return source_phantom.reverse_node_id == target_phantom.reverse_node_id && + source_phantom.GetReverseWeightPlusOffset() > + target_phantom.GetReverseWeightPlusOffset(); + }; + // allows a uturn at the target_phantom // searches source forward/reverse -> target forward/reverse void SearchWithUTurn(QueryHeap &forward_heap, @@ -76,113 +92,12 @@ class ShortestPathRouting final target_phantom.GetReverseWeightPlusOffset(), target_phantom.reverse_node_id); } + BOOST_ASSERT(forward_heap.Size() > 0); BOOST_ASSERT(reverse_heap.Size() > 0); - super::Search(forward_heap, reverse_heap, new_total_distance, leg_packed_path); - } - - // If source and target are reverse on a oneway we need to find a path - // that connects the two. This is _not_ the shortest path in our model, - // as source and target are on the same edge based node. - // We force a detour by inserting "virtaul vias", which means we search a path - // from all nodes that are connected by outgoing edges to all nodes that are connected by - // incoming edges. - // ------^ - // | ^source - // | ^ - // | ^target - // ------^ - void SearchLoop(QueryHeap &forward_heap, - QueryHeap &reverse_heap, - const bool search_forward_node, - const bool search_reverse_node, - const PhantomNode &source_phantom, - const PhantomNode &target_phantom, - const int total_distance_to_forward, - const int total_distance_to_reverse, - int &new_total_distance_to_forward, - int &new_total_distance_to_reverse, - std::vector &leg_packed_path_forward, - std::vector &leg_packed_path_reverse) const - { - BOOST_ASSERT(source_phantom.forward_node_id == target_phantom.forward_node_id); - BOOST_ASSERT(source_phantom.reverse_node_id == target_phantom.reverse_node_id); - - if (search_forward_node) - { - forward_heap.Clear(); - reverse_heap.Clear(); - - auto node_id = source_phantom.forward_node_id; - - for (const auto edge : super::facade->GetAdjacentEdgeRange(node_id)) - { - const auto &data = super::facade->GetEdgeData(edge); - if (data.forward) - { - auto target = super::facade->GetTarget(edge); - auto offset = total_distance_to_forward + data.distance - - source_phantom.GetForwardWeightPlusOffset(); - forward_heap.Insert(target, offset, target); - } - - if (data.backward) - { - auto target = super::facade->GetTarget(edge); - auto offset = data.distance + target_phantom.GetForwardWeightPlusOffset(); - reverse_heap.Insert(target, offset, target); - } - } - - BOOST_ASSERT(forward_heap.Size() > 0); - BOOST_ASSERT(reverse_heap.Size() > 0); - super::Search(forward_heap, reverse_heap, new_total_distance_to_forward, - leg_packed_path_forward); - - // insert node to both endpoints to close the leg - leg_packed_path_forward.push_back(node_id); - std::reverse(leg_packed_path_forward.begin(), leg_packed_path_forward.end()); - leg_packed_path_forward.push_back(node_id); - std::reverse(leg_packed_path_forward.begin(), leg_packed_path_forward.end()); - } - - if (search_reverse_node) - { - forward_heap.Clear(); - reverse_heap.Clear(); - - auto node_id = source_phantom.reverse_node_id; - - for (const auto edge : super::facade->GetAdjacentEdgeRange(node_id)) - { - const auto &data = super::facade->GetEdgeData(edge); - if (data.forward) - { - auto target = super::facade->GetTarget(edge); - auto offset = total_distance_to_reverse + data.distance - - source_phantom.GetReverseWeightPlusOffset(); - forward_heap.Insert(target, offset, target); - } - - if (data.backward) - { - auto target = super::facade->GetTarget(edge); - auto offset = data.distance + target_phantom.GetReverseWeightPlusOffset(); - reverse_heap.Insert(target, offset, target); - } - } - - BOOST_ASSERT(forward_heap.Size() > 0); - BOOST_ASSERT(reverse_heap.Size() > 0); - super::Search(forward_heap, reverse_heap, new_total_distance_to_reverse, - leg_packed_path_reverse); - - // insert node to both endpoints to close the leg - leg_packed_path_reverse.push_back(node_id); - std::reverse(leg_packed_path_reverse.begin(), leg_packed_path_reverse.end()); - leg_packed_path_reverse.push_back(node_id); - std::reverse(leg_packed_path_reverse.begin(), leg_packed_path_reverse.end()); - } + super::Search(forward_heap, reverse_heap, new_total_distance, leg_packed_path, + forceLoop(FORWARD_DIRECTION, source_phantom, target_phantom), + forceLoop(REVERSE_DIRECTION, source_phantom, target_phantom)); } // searches shortest path between: @@ -227,8 +142,9 @@ class ShortestPathRouting final } BOOST_ASSERT(forward_heap.Size() > 0); BOOST_ASSERT(reverse_heap.Size() > 0); - super::Search(forward_heap, reverse_heap, new_total_distance_to_forward, - leg_packed_path_forward); + super::Search( + forward_heap, reverse_heap, new_total_distance_to_forward, leg_packed_path_forward, + forceLoop(FORWARD_DIRECTION, source_phantom, target_phantom), DO_NOT_FORCE_LOOP); } if (search_to_reverse_node) @@ -255,7 +171,8 @@ class ShortestPathRouting final BOOST_ASSERT(forward_heap.Size() > 0); BOOST_ASSERT(reverse_heap.Size() > 0); super::Search(forward_heap, reverse_heap, new_total_distance_to_reverse, - leg_packed_path_reverse); + leg_packed_path_reverse, DO_NOT_FORCE_LOOP, + forceLoop(REVERSE_DIRECTION, source_phantom, target_phantom)); } } @@ -336,19 +253,6 @@ class ShortestPathRouting final BOOST_ASSERT(!search_from_reverse_node || source_phantom.reverse_node_id != SPECIAL_NODEID); - if (source_phantom.forward_node_id == target_phantom.forward_node_id && - source_phantom.GetForwardWeightPlusOffset() > - target_phantom.GetForwardWeightPlusOffset()) - { - search_to_forward_node = search_from_reverse_node; - } - if (source_phantom.reverse_node_id == target_phantom.reverse_node_id && - source_phantom.GetReverseWeightPlusOffset() > - target_phantom.GetReverseWeightPlusOffset()) - { - search_to_reverse_node = search_from_forward_node; - } - BOOST_ASSERT(search_from_forward_node || search_from_reverse_node); if (search_to_reverse_node || search_to_forward_node) @@ -385,18 +289,6 @@ class ShortestPathRouting final packed_leg_to_reverse); } } - else - { - search_to_forward_node = target_phantom.forward_node_id != SPECIAL_NODEID; - search_to_reverse_node = target_phantom.reverse_node_id != SPECIAL_NODEID; - BOOST_ASSERT(search_from_reverse_node == search_to_reverse_node); - BOOST_ASSERT(search_from_forward_node == search_to_forward_node); - SearchLoop(forward_heap, reverse_heap, search_from_forward_node, - search_from_reverse_node, source_phantom, target_phantom, - total_distance_to_forward, total_distance_to_reverse, - new_total_distance_to_forward, new_total_distance_to_reverse, - packed_leg_to_forward, packed_leg_to_reverse); - } // No path found for both target nodes? if ((INVALID_EDGE_WEIGHT == new_total_distance_to_forward) && diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp index 94772a3dd..ef9bacf99 100644 --- a/include/extractor/edge_based_graph_factory.hpp +++ b/include/extractor/edge_based_graph_factory.hpp @@ -62,10 +62,11 @@ class EdgeBasedGraphFactory const bool generate_edge_lookup); #endif + //The following get access functions destroy the content in the factory void GetEdgeBasedEdges(util::DeallocatingVector &edges); - void GetEdgeBasedNodes(std::vector &nodes); void GetStartPointMarkers(std::vector &node_is_startpoint); + void GetEdgeBasedNodeWeights(std::vector &output_node_weights); unsigned GetHighestEdgeID(); @@ -80,6 +81,11 @@ class EdgeBasedGraphFactory //! maps index from m_edge_based_node_list to ture/false if the node is an entry point to the //! graph std::vector m_edge_based_node_is_startpoint; + + //! node weights that indicate the length of the segment (node based) represented by the + //! edge-based node + std::vector m_edge_based_node_weights; + //! list of edge based nodes (compressed segments) std::vector m_edge_based_node_list; util::DeallocatingVector m_edge_based_edge_list; diff --git a/include/extractor/extractor.hpp b/include/extractor/extractor.hpp index 68feaf624..72e5be8b7 100644 --- a/include/extractor/extractor.hpp +++ b/include/extractor/extractor.hpp @@ -6,24 +6,28 @@ #include "extractor/edge_based_graph_factory.hpp" #include "extractor/graph_compressor.hpp" +#include "util/typedefs.hpp" + namespace osrm { namespace extractor { -class extractor +class Extractor { public: - extractor(ExtractorConfig extractor_config) : config(std::move(extractor_config)) {} + Extractor(ExtractorConfig extractor_config) : config(std::move(extractor_config)) {} int run(); private: ExtractorConfig config; + void SetupScriptingEnvironment(lua_State *myLuaState, SpeedProfileProperties &speed_profile); std::pair BuildEdgeExpandedGraph(std::vector &internal_to_external_node_map, std::vector &node_based_edge_list, std::vector &node_is_startpoint, + std::vector &edge_based_node_weights, util::DeallocatingVector &edge_based_edge_list); void WriteNodeMapping(const std::vector &internal_to_external_node_map); void FindComponents(unsigned max_edge_id, @@ -38,8 +42,8 @@ class extractor std::unordered_set &traffic_lights, std::vector &internal_to_external_node_map); - void WriteEdgeBasedGraph(std::string const &output_file_filename, - size_t const max_edge_id, + void WriteEdgeBasedGraph(const std::string &output_file_filename, + const size_t max_edge_id, util::DeallocatingVector const &edge_based_edge_list); }; } diff --git a/include/extractor/extractor_options.hpp b/include/extractor/extractor_options.hpp index 287d62f06..57f79c1ec 100644 --- a/include/extractor/extractor_options.hpp +++ b/include/extractor/extractor_options.hpp @@ -35,6 +35,11 @@ struct ExtractorConfig std::string rtree_nodes_output_path; std::string rtree_leafs_output_path; + // every edge based node represents a segment in the original graph. During contraciton we need + // to know about this segment length, as we might have to add self-loops in cases of shorter + // parts than the segment represents itself + std::string edge_based_node_weights_output_path; + unsigned requested_num_threads; unsigned small_component_size; diff --git a/include/util/io.hpp b/include/util/io.hpp new file mode 100644 index 000000000..b7951032c --- /dev/null +++ b/include/util/io.hpp @@ -0,0 +1,127 @@ +#ifndef OSRM_INCLUDE_UTIL_IO_HPP_ +#define OSRM_INCLUDE_UTIL_IO_HPP_ + +#include "util/simple_logger.hpp" + +#include + +#include +#include + +#include +#include +#include + +#include "util/fingerprint.hpp" + +namespace osrm +{ +namespace util +{ + +inline bool writeFingerprint(std::ostream &stream) +{ + const auto fingerprint = FingerPrint::GetValid(); + stream.write(reinterpret_cast(&fingerprint), sizeof(fingerprint)); + return static_cast(stream); +} + +inline bool readAndCheckFingerprint(std::istream &stream) +{ + FingerPrint fingerprint; + const auto valid = FingerPrint::GetValid(); + stream.read(reinterpret_cast(&fingerprint), sizeof(fingerprint)); + // compare the compilation state stored in the fingerprint + return static_cast(stream) && valid.IsMagicNumberOK(fingerprint) && + valid.TestPrepare(fingerprint) && valid.TestGraphUtil(fingerprint) && + valid.TestRTree(fingerprint) && valid.TestQueryObjects(fingerprint); +} + +template +bool serializeVector(const std::string &filename, const std::vector &data) +{ + std::ofstream stream(filename, std::ios::binary); + + writeFingerprint(stream); + + std::uint64_t count = data.size(); + stream.write(reinterpret_cast(&count), sizeof(count)); + if (!data.empty()) + stream.write(reinterpret_cast(&data[0]), sizeof(simple_type) * count); + return static_cast(stream); +} + +template +bool deserializeVector(const std::string &filename, std::vector &data) +{ + std::ifstream stream(filename, std::ios::binary); + + if (!readAndCheckFingerprint(stream)) + return false; + + std::uint64_t count = 0; + stream.read(reinterpret_cast(&count), sizeof(count)); + data.resize(count); + if (count) + stream.read(reinterpret_cast(&data[0]), sizeof(simple_type) * count); + return static_cast(stream); +} + +inline bool serializeFlags(const boost::filesystem::path &path, const std::vector &flags) +{ + // TODO this should be replaced with a FILE-based write using error checking + std::ofstream flag_stream(path.string(), std::ios::binary); + + writeFingerprint(flag_stream); + + std::uint32_t number_of_bits = flags.size(); + flag_stream.write(reinterpret_cast(&number_of_bits), sizeof(number_of_bits)); + // putting bits in ints + std::uint32_t chunk = 0; + std::size_t chunk_count = 0; + for (std::size_t bit_nr = 0; bit_nr < number_of_bits;) + { + std::bitset<32> chunk_bitset; + for (std::size_t chunk_bit = 0; chunk_bit < 32 && bit_nr < number_of_bits; + ++chunk_bit, ++bit_nr) + chunk_bitset[chunk_bit] = flags[bit_nr]; + + chunk = chunk_bitset.to_ulong(); + ++chunk_count; + flag_stream.write(reinterpret_cast(&chunk), sizeof(chunk)); + } + SimpleLogger().Write() << "Wrote " << number_of_bits << " bits in " << chunk_count + << " chunks (Flags)."; + return static_cast(flag_stream); +} + +inline bool deserializeFlags(const boost::filesystem::path &path, std::vector &flags) +{ + SimpleLogger().Write() << "Reading flags from " << path; + std::ifstream flag_stream(path.string(), std::ios::binary); + + if (!readAndCheckFingerprint(flag_stream)) + return false; + + std::uint32_t number_of_bits; + flag_stream.read(reinterpret_cast(&number_of_bits), sizeof(number_of_bits)); + flags.resize(number_of_bits); + // putting bits in ints + std::uint32_t chunks = (number_of_bits + 31) / 32; + std::size_t bit_position = 0; + std::uint32_t chunk; + for (std::size_t chunk_id = 0; chunk_id < chunks; ++chunk_id) + { + flag_stream.read(reinterpret_cast(&chunk), sizeof(chunk)); + std::bitset<32> chunk_bits(chunk); + for (std::size_t bit = 0; bit < 32 && bit_position < number_of_bits; ++bit, ++bit_position) + flags[bit_position] = chunk_bits[bit]; + } + SimpleLogger().Write() << "Read " << number_of_bits << " bits in " << chunks + << " Chunks from disk."; + return static_cast(flag_stream); +} +} // namespace util +} // namespace osrm + +#endif // OSRM_INCLUDE_UTIL_IO_HPP_ diff --git a/src/contractor/processing_chain.cpp b/src/contractor/processing_chain.cpp index 30402bfa2..a28f347a1 100644 --- a/src/contractor/processing_chain.cpp +++ b/src/contractor/processing_chain.cpp @@ -7,6 +7,7 @@ #include "contractor/crc32_processor.hpp" #include "util/graph_loader.hpp" +#include "util/io.hpp" #include "util/integer_range.hpp" #include "util/lua_util.hpp" #include "util/osrm_exception.hpp" @@ -22,6 +23,9 @@ #include +#include +#include +#include #include #include #include @@ -73,7 +77,7 @@ int Prepare::Run() util::DeallocatingVector edge_based_edge_list; - size_t max_edge_id = LoadEdgeExpandedGraph( + std::size_t max_edge_id = LoadEdgeExpandedGraph( config.edge_based_graph_path, edge_based_edge_list, config.edge_segment_lookup_path, config.edge_penalty_path, config.segment_speed_lookup_path); @@ -86,9 +90,18 @@ int Prepare::Run() { ReadNodeLevels(node_levels); } + + util::SimpleLogger().Write() << "Reading node weights."; + std::vector node_weights; + std::string node_file_name = config.osrm_input_path.string() + ".enw"; + if (util::deserializeVector(node_file_name, node_weights)) + util::SimpleLogger().Write() << "Done reading node weights."; + else + util::SimpleLogger().Write() << "Failed reading node weights."; + util::DeallocatingVector contracted_edge_list; - ContractGraph(max_edge_id, edge_based_edge_list, contracted_edge_list, is_core_node, - node_levels); + ContractGraph(max_edge_id, edge_based_edge_list, contracted_edge_list, std::move(node_weights), + is_core_node, node_levels); TIMER_STOP(contraction); util::SimpleLogger().Write() << "Contraction took " << TIMER_SEC(contraction) << " sec"; @@ -143,10 +156,12 @@ std::size_t Prepare::LoadEdgeExpandedGraph( input_stream.read((char *)&fingerprint_loaded, sizeof(util::FingerPrint)); fingerprint_loaded.TestPrepare(fingerprint_valid); - size_t number_of_edges = 0; - size_t max_edge_id = SPECIAL_EDGEID; - input_stream.read((char *)&number_of_edges, sizeof(size_t)); - input_stream.read((char *)&max_edge_id, sizeof(size_t)); + // TODO std::size_t can vary on systems. Our files are not transferable, but we might want to + // consider using a fixed size type for I/O + std::size_t number_of_edges = 0; + std::size_t max_edge_id = SPECIAL_EDGEID; + input_stream.read((char *)&number_of_edges, sizeof(std::size_t)); + input_stream.read((char *)&max_edge_id, sizeof(std::size_t)); edge_based_edge_list.resize(number_of_edges); util::SimpleLogger().Write() << "Reading " << number_of_edges @@ -179,7 +194,6 @@ std::size_t Prepare::LoadEdgeExpandedGraph( { extractor::EdgeBasedEdge inbuffer; input_stream.read((char *)&inbuffer, sizeof(extractor::EdgeBasedEdge)); - if (update_edge_weights) { // Processing-time edge updates @@ -370,8 +384,11 @@ Prepare::WriteContractedGraph(unsigned max_node_id, util::StaticGraph::EdgeArrayEntry current_edge; for (const auto edge : util::irange(0, contracted_edge_list.size())) { + // some self-loops are required for oneway handling. Need to assertthat we only keep these + // (TODO) // no eigen loops - BOOST_ASSERT(contracted_edge_list[edge].source != contracted_edge_list[edge].target); + // BOOST_ASSERT(contracted_edge_list[edge].source != contracted_edge_list[edge].target || + // node_represents_oneway[contracted_edge_list[edge].source]); current_edge.target = contracted_edge_list[edge].target; current_edge.data = contracted_edge_list[edge].data; @@ -407,17 +424,22 @@ void Prepare::ContractGraph( const unsigned max_edge_id, util::DeallocatingVector &edge_based_edge_list, util::DeallocatingVector &contracted_edge_list, + std::vector &&node_weights, std::vector &is_core_node, std::vector &inout_node_levels) const { std::vector node_levels; node_levels.swap(inout_node_levels); - Contractor contractor(max_edge_id + 1, edge_based_edge_list, std::move(node_levels)); + Contractor contractor(max_edge_id + 1, edge_based_edge_list, std::move(node_levels), + std::move(node_weights)); contractor.Run(config.core_factor); contractor.GetEdges(contracted_edge_list); contractor.GetCoreMarker(is_core_node); contractor.GetNodeLevels(inout_node_levels); + + std::cout << "Levels: " << inout_node_levels.size() << " Core: " << is_core_node.size() + << " MEID: " << max_edge_id << std::endl; } } } diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index f71544b17..a6585b0f9 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -67,6 +67,12 @@ void EdgeBasedGraphFactory::GetStartPointMarkers(std::vector &node_is_star swap(m_edge_based_node_is_startpoint, node_is_startpoint); } +void EdgeBasedGraphFactory::GetEdgeBasedNodeWeights(std::vector &output_node_weights) +{ + using std::swap; // Koenig swap + swap(m_edge_based_node_weights, output_node_weights); +} + unsigned EdgeBasedGraphFactory::GetHighestEdgeID() { return m_max_edge_id; } void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeID node_v) @@ -92,6 +98,9 @@ void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeI return; } + if (forward_data.edge_id != SPECIAL_NODEID && reverse_data.edge_id == SPECIAL_NODEID) + m_edge_based_node_weights[forward_data.edge_id] = INVALID_EDGE_WEIGHT; + BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_id_1) == m_compressed_edge_container.HasEntryForID(edge_id_2)); if (m_compressed_edge_container.HasEntryForID(edge_id_1)) @@ -231,6 +240,7 @@ void EdgeBasedGraphFactory::Run(const std::string &original_edge_data_filename, TIMER_STOP(renumber); TIMER_START(generate_nodes); + m_edge_based_node_weights.reserve(m_max_edge_id + 1); GenerateEdgeExpandedNodes(); TIMER_STOP(generate_nodes); @@ -270,6 +280,11 @@ unsigned EdgeBasedGraphFactory::RenumberEdges() continue; } + // oneway streets always require this self-loop. Other streets only if a u-turn plus + // traversal + // of the street takes longer than the loop + m_edge_based_node_weights.push_back(edge_data.distance + speed_profile.u_turn_penalty); + BOOST_ASSERT(numbered_edges_count < m_node_based_graph->GetNumberOfEdges()); edge_data.edge_id = numbered_edges_count; ++numbered_edges_count; @@ -321,6 +336,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedNodes() } BOOST_ASSERT(m_edge_based_node_list.size() == m_edge_based_node_is_startpoint.size()); + BOOST_ASSERT(m_max_edge_id+1 == m_edge_based_node_weights.size()); util::SimpleLogger().Write() << "Generated " << m_edge_based_node_list.size() << " nodes in edge-expanded graph"; diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index ddf787c35..cdd7ced41 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -9,6 +9,7 @@ #include "extractor/scripting_environment.hpp" #include "extractor/raster_source.hpp" +#include "util/io.hpp" #include "util/make_unique.hpp" #include "util/simple_logger.hpp" #include "util/timing_util.hpp" @@ -46,6 +47,7 @@ #include #include #include +#include namespace osrm { @@ -71,7 +73,7 @@ namespace extractor * graph * */ -int extractor::run() +int Extractor::run() { try { @@ -265,16 +267,25 @@ int extractor::run() std::vector node_based_edge_list; util::DeallocatingVector edge_based_edge_list; std::vector node_is_startpoint; + std::vector edge_based_node_weights; std::vector internal_to_external_node_map; - auto graph_size = - BuildEdgeExpandedGraph(internal_to_external_node_map, node_based_edge_list, - node_is_startpoint, edge_based_edge_list); + auto graph_size = BuildEdgeExpandedGraph(internal_to_external_node_map, + node_based_edge_list, node_is_startpoint, + edge_based_node_weights, edge_based_edge_list); auto number_of_node_based_nodes = graph_size.first; auto max_edge_id = graph_size.second; TIMER_STOP(expansion); + util::SimpleLogger().Write() << "Saving edge-based node weights to file."; + TIMER_START(timer_write_node_weights); + util::serializeVector(config.edge_based_node_weights_output_path, + edge_based_node_weights); + TIMER_STOP(timer_write_node_weights); + util::SimpleLogger().Write() << "Done writing. (" << TIMER_SEC(timer_write_node_weights) + << ")"; + util::SimpleLogger().Write() << "building r-tree ..."; TIMER_START(rtree); @@ -309,7 +320,7 @@ int extractor::run() \brief Setups scripting environment (lua-scripting) Also initializes speed profile. */ -void extractor::SetupScriptingEnvironment(lua_State *lua_state, +void Extractor::SetupScriptingEnvironment(lua_State *lua_state, SpeedProfileProperties &speed_profile) { // open utility libraries string library; @@ -347,7 +358,7 @@ void extractor::SetupScriptingEnvironment(lua_State *lua_state, speed_profile.has_turn_penalty_function = util::lua_function_exists(lua_state, "turn_function"); } -void extractor::FindComponents(unsigned max_edge_id, +void Extractor::FindComponents(unsigned max_edge_id, const util::DeallocatingVector &input_edge_list, std::vector &input_nodes) const { @@ -425,7 +436,7 @@ void extractor::FindComponents(unsigned max_edge_id, /** \brief Build load restrictions from .restriction file */ -std::shared_ptr extractor::LoadRestrictionMap() +std::shared_ptr Extractor::LoadRestrictionMap() { boost::filesystem::ifstream input_stream(config.restriction_file_name, std::ios::in | std::ios::binary); @@ -442,7 +453,7 @@ std::shared_ptr extractor::LoadRestrictionMap() \brief Load node based graph from .osrm file */ std::shared_ptr -extractor::LoadNodeBasedGraph(std::unordered_set &barrier_nodes, +Extractor::LoadNodeBasedGraph(std::unordered_set &barrier_nodes, std::unordered_set &traffic_lights, std::vector &internal_to_external_node_map) { @@ -483,9 +494,10 @@ extractor::LoadNodeBasedGraph(std::unordered_set &barrier_nodes, \brief Building an edge-expanded graph from node-based input and turn restrictions */ std::pair -extractor::BuildEdgeExpandedGraph(std::vector &internal_to_external_node_map, +Extractor::BuildEdgeExpandedGraph(std::vector &internal_to_external_node_map, std::vector &node_based_edge_list, std::vector &node_is_startpoint, + std::vector &edge_based_node_weights, util::DeallocatingVector &edge_based_edge_list) { lua_State *lua_state = luaL_newstate(); @@ -526,6 +538,7 @@ extractor::BuildEdgeExpandedGraph(std::vector &internal_to_external_n edge_based_graph_factory.GetEdgeBasedEdges(edge_based_edge_list); edge_based_graph_factory.GetEdgeBasedNodes(node_based_edge_list); edge_based_graph_factory.GetStartPointMarkers(node_is_startpoint); + edge_based_graph_factory.GetEdgeBasedNodeWeights(edge_based_node_weights); auto max_edge_id = edge_based_graph_factory.GetHighestEdgeID(); const std::size_t number_of_node_based_nodes = node_based_graph->GetNumberOfNodes(); @@ -535,7 +548,7 @@ extractor::BuildEdgeExpandedGraph(std::vector &internal_to_external_n /** \brief Writing info on original (node-based) nodes */ -void extractor::WriteNodeMapping(const std::vector &internal_to_external_node_map) +void Extractor::WriteNodeMapping(const std::vector &internal_to_external_node_map) { boost::filesystem::ofstream node_stream(config.node_output_path, std::ios::binary); const unsigned size_of_mapping = internal_to_external_node_map.size(); @@ -553,7 +566,7 @@ void extractor::WriteNodeMapping(const std::vector &internal_to_exter Saves tree into '.ramIndex' and leaves into '.fileIndex'. */ -void extractor::BuildRTree(std::vector node_based_edge_list, +void Extractor::BuildRTree(std::vector node_based_edge_list, std::vector node_is_startpoint, const std::vector &internal_to_external_node_map) { @@ -589,7 +602,7 @@ void extractor::BuildRTree(std::vector node_based_edge_list, << " seconds"; } -void extractor::WriteEdgeBasedGraph( +void Extractor::WriteEdgeBasedGraph( std::string const &output_file_filename, size_t const max_edge_id, util::DeallocatingVector const &edge_based_edge_list) @@ -600,7 +613,8 @@ void extractor::WriteEdgeBasedGraph( const util::FingerPrint fingerprint = util::FingerPrint::GetValid(); file_out_stream.write((char *)&fingerprint, sizeof(util::FingerPrint)); - std::cout << "[extractor] Writing edge-based-graph egdes ... " << std::flush; + util::SimpleLogger().Write() << "[extractor] Writing edge-based-graph egdes ... " + << std::flush; TIMER_START(write_edges); size_t number_of_used_edges = edge_based_edge_list.size(); @@ -613,7 +627,7 @@ void extractor::WriteEdgeBasedGraph( } TIMER_STOP(write_edges); - std::cout << "ok, after " << TIMER_SEC(write_edges) << "s" << std::endl; + util::SimpleLogger().Write() << "ok, after " << TIMER_SEC(write_edges) << "s" << std::endl; util::SimpleLogger().Write() << "Processed " << number_of_used_edges << " edges"; file_out_stream.close(); diff --git a/src/extractor/extractor_options.cpp b/src/extractor/extractor_options.cpp index 12081266c..7c985714f 100644 --- a/src/extractor/extractor_options.cpp +++ b/src/extractor/extractor_options.cpp @@ -142,6 +142,7 @@ void ExtractorOptions::GenerateOutputFilesNames(ExtractorConfig &extractor_confi extractor_config.geometry_output_path = input_path.string(); extractor_config.edge_output_path = input_path.string(); extractor_config.edge_graph_output_path = input_path.string(); + extractor_config.edge_based_node_weights_output_path = input_path.string(); extractor_config.node_output_path = input_path.string(); extractor_config.rtree_nodes_output_path = input_path.string(); extractor_config.rtree_leafs_output_path = input_path.string(); @@ -173,6 +174,7 @@ void ExtractorOptions::GenerateOutputFilesNames(ExtractorConfig &extractor_confi extractor_config.node_output_path.append(".osrm.nodes"); extractor_config.edge_output_path.append(".osrm.edges"); extractor_config.edge_graph_output_path.append(".osrm.ebg"); + extractor_config.edge_based_node_weights_output_path.append(".osrm.enw"); extractor_config.rtree_nodes_output_path.append(".osrm.ramIndex"); extractor_config.rtree_leafs_output_path.append(".osrm.fileIndex"); extractor_config.edge_segment_lookup_path.append(".osrm.edge_segment_lookup"); @@ -188,6 +190,7 @@ void ExtractorOptions::GenerateOutputFilesNames(ExtractorConfig &extractor_confi extractor_config.node_output_path.replace(pos, 5, ".osrm.nodes"); extractor_config.edge_output_path.replace(pos, 5, ".osrm.edges"); extractor_config.edge_graph_output_path.replace(pos, 5, ".osrm.ebg"); + extractor_config.edge_based_node_weights_output_path.replace(pos, 5, ".osrm.enw"); extractor_config.rtree_nodes_output_path.replace(pos, 5, ".osrm.ramIndex"); extractor_config.rtree_leafs_output_path.replace(pos, 5, ".osrm.fileIndex"); extractor_config.edge_segment_lookup_path.replace(pos, 5, ".osrm.edge_segment_lookup"); @@ -204,6 +207,7 @@ void ExtractorOptions::GenerateOutputFilesNames(ExtractorConfig &extractor_confi extractor_config.node_output_path.replace(pos, 8, ".osrm.nodes"); extractor_config.edge_output_path.replace(pos, 8, ".osrm.edges"); extractor_config.edge_graph_output_path.replace(pos, 8, ".osrm.ebg"); + extractor_config.edge_based_node_weights_output_path.replace(pos, 8, ".osrm.enw"); extractor_config.rtree_nodes_output_path.replace(pos, 8, ".osrm.ramIndex"); extractor_config.rtree_leafs_output_path.replace(pos, 8, ".osrm.fileIndex"); extractor_config.edge_segment_lookup_path.replace(pos, 8, ".osrm.edge_segment_lookup"); diff --git a/src/tools/extract.cpp b/src/tools/extract.cpp index a4edf8ba0..7f27fbc8d 100644 --- a/src/tools/extract.cpp +++ b/src/tools/extract.cpp @@ -49,7 +49,7 @@ int main(int argc, char *argv[]) try << "Profile " << extractor_config.profile_path.string() << " not found!"; return EXIT_FAILURE; } - return extractor::extractor(extractor_config).run(); + return extractor::Extractor(extractor_config).run(); } catch (const std::bad_alloc &e) { diff --git a/unit_tests/io/io.cpp b/unit_tests/io/io.cpp new file mode 100644 index 000000000..75aed5bc1 --- /dev/null +++ b/unit_tests/io/io.cpp @@ -0,0 +1,43 @@ +#include "util/io.hpp" +#include "util/typedefs.hpp" + +#include +#include + +#include + +const static std::string IO_TMP_FILE = "test_io.tmp"; + +BOOST_AUTO_TEST_SUITE(osrm_io) + +BOOST_AUTO_TEST_CASE(io_flags) +{ + std::vector flags_in, flags_out; + flags_in.resize(53); + for (std::size_t i = 0; i < flags_in.size(); ++i) + flags_in[i] = ((i % 2) == 1); + + osrm::util::serializeFlags(IO_TMP_FILE, flags_in); + osrm::util::deserializeFlags(IO_TMP_FILE, flags_out); + + BOOST_REQUIRE_EQUAL(flags_in.size(), flags_out.size()); + BOOST_CHECK_EQUAL_COLLECTIONS(flags_out.begin(), flags_out.end(), flags_in.begin(), + flags_in.end()); +} + +BOOST_AUTO_TEST_CASE(io_data) +{ + std::vector data_in, data_out; + data_in.resize(53); + for (std::size_t i = 0; i < data_in.size(); ++i) + data_in[i] = i; + + osrm::util::serializeVector(IO_TMP_FILE, data_in); + osrm::util::deserializeVector(IO_TMP_FILE, data_out); + + BOOST_REQUIRE_EQUAL(data_in.size(), data_out.size()); + BOOST_CHECK_EQUAL_COLLECTIONS(data_out.begin(), data_out.end(), data_in.begin(), + data_in.end()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/io_tests.cpp b/unit_tests/io_tests.cpp new file mode 100644 index 000000000..4b0d83b56 --- /dev/null +++ b/unit_tests/io_tests.cpp @@ -0,0 +1,7 @@ +#define BOOST_TEST_MODULE io tests + +#include + +/* + * This file will contain an automatically generated main function. + */