From ee7912f8822f52722ac5bdea38123f46a88f9ff7 Mon Sep 17 00:00:00 2001 From: vng Date: Sat, 14 Oct 2017 10:54:26 +0000 Subject: [PATCH] Calculating segregated node-based edges. --- include/extractor/extractor.hpp | 3 + .../extractor/node_based_graph_factory.hpp | 11 +- include/util/node_based_graph.hpp | 5 +- src/extractor/extractor.cpp | 226 +++++++++++++++++- src/extractor/node_based_graph_factory.cpp | 9 +- 5 files changed, 240 insertions(+), 14 deletions(-) diff --git a/include/extractor/extractor.hpp b/include/extractor/extractor.hpp index c28b82a3d..23608458c 100644 --- a/include/extractor/extractor.hpp +++ b/include/extractor/extractor.hpp @@ -47,6 +47,7 @@ namespace extractor class ScriptingEnvironment; struct ProfileProperties; +class NodeBasedGraphFactory; class Extractor { @@ -100,6 +101,8 @@ class Extractor void WriteConditionalRestrictions( const std::string &path, std::vector &conditional_turn_restrictions); + + size_t FindSegregatedNodes(NodeBasedGraphFactory &factory); }; } } diff --git a/include/extractor/node_based_graph_factory.hpp b/include/extractor/node_based_graph_factory.hpp index aaadfb728..91d2bc74c 100644 --- a/include/extractor/node_based_graph_factory.hpp +++ b/include/extractor/node_based_graph_factory.hpp @@ -36,12 +36,13 @@ class NodeBasedGraphFactory // node-based graph to represent the OSM network. This includes geometry compression, annotation // data optimisation and many other aspects. After this step, the edge-based graph factory can // turn the graph into the routing graph to be used with the navigation algorithms. - NodeBasedGraphFactory(const boost::filesystem::path &input_file, - ScriptingEnvironment &scripting_environment, - std::vector &turn_restrictions, - std::vector &conditional_turn_restrictions); + NodeBasedGraphFactory(const boost::filesystem::path &input_file); - auto const &GetGraph() const { return compressed_output_graph; } + void CompressAll(ScriptingEnvironment &scripting_environment, + std::vector &turn_restrictions, + std::vector &conditional_turn_restrictions); + + auto &GetGraph() { return compressed_output_graph; } auto const &GetBarriers() const { return barriers; } auto const &GetTrafficSignals() const { return traffic_signals; } auto &GetCompressedEdges() { return compressed_edge_container; } diff --git a/include/util/node_based_graph.hpp b/include/util/node_based_graph.hpp index fa12db3b8..f4d03d25a 100644 --- a/include/util/node_based_graph.hpp +++ b/include/util/node_based_graph.hpp @@ -22,7 +22,7 @@ struct NodeBasedEdgeData { NodeBasedEdgeData() : weight(INVALID_EDGE_WEIGHT), duration(INVALID_EDGE_WEIGHT), geometry_id({0, false}), - reversed(false), annotation_data(-1) + reversed(false), segregated(false), annotation_data(-1) { } @@ -33,7 +33,7 @@ struct NodeBasedEdgeData extractor::NodeBasedEdgeClassification flags, AnnotationID annotation_data) : weight(weight), duration(duration), geometry_id(geometry_id), reversed(reversed), - flags(flags), annotation_data(annotation_data) + segregated(false), flags(flags), annotation_data(annotation_data) { } @@ -41,6 +41,7 @@ struct NodeBasedEdgeData EdgeWeight duration; GeometryID geometry_id; bool reversed : 1; + bool segregated : 1; extractor::NodeBasedEdgeClassification flags; AnnotationID annotation_data; }; diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index 1a9825715..870007273 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -212,10 +212,19 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) std::vector edge_based_node_weights; // Create a node-based graph from the OSRM file - NodeBasedGraphFactory node_based_graph_factory(config.GetPath(".osrm"), - scripting_environment, - turn_restrictions, - conditional_turn_restrictions); + NodeBasedGraphFactory node_based_graph_factory(config.GetPath(".osrm")); + + util::Log() << "Find segregated edges in node-based graph ..." << std::flush; + TIMER_START(segregated); + + const size_t segregated_count = FindSegregatedNodes(node_based_graph_factory); + + TIMER_STOP(segregated); + util::Log() << "ok, after " << TIMER_SEC(segregated) << "s"; + util::Log() << "Segregated edges count = " << segregated_count; + + node_based_graph_factory.CompressAll( + scripting_environment, turn_restrictions, conditional_turn_restrictions); util::Log() << "Writing nodes for nodes-based and edges-based graphs ..."; auto const &coordinates = node_based_graph_factory.GetCoordinates(); @@ -826,5 +835,214 @@ void Extractor::WriteCompressedNodeBasedGraph(const std::string &path, } } +struct EdgeInfo +{ + NodeID node; + + util::StringView name; + + // 0 - outgoing (forward), 1 - incoming (reverse), 2 - both outgoing and incoming + int direction; + + ClassData road_class; + + struct LessName + { + bool operator()(EdgeInfo const &e1, EdgeInfo const &e2) const { return e1.name < e2.name; } + }; +}; + +bool IsSegregated(std::vector v1, std::vector v2, EdgeInfo const ¤t) +{ + if (v1.size() < 2 || v2.size() < 2) + return false; + + auto const sort_by_name_fn = [](std::vector &v) { + std::sort(v.begin(), v.end(), EdgeInfo::LessName()); + }; + + sort_by_name_fn(v1); + sort_by_name_fn(v2); + + // Internal edge with the name should be connected with any other neibour edge with the same + // name, e.g. isolated edge with unique name is not segregated. + // b - 'b' road continues here + // | + // - - a - | + // b - segregated edge + // - - a - | + if (!current.name.empty()) + { + auto const findNameFn = [¤t](std::vector const &v) { + return std::binary_search(v.begin(), v.end(), current, EdgeInfo::LessName()); + }; + + if (!findNameFn(v1) && !findNameFn(v2)) + return false; + } + + std::vector intersect; + std::set_intersection(v1.begin(), + v1.end(), + v2.begin(), + v2.end(), + std::back_inserter(intersect), + EdgeInfo::LessName()); + + intersect.erase(std::remove_if(intersect.begin(), + intersect.end(), + [](EdgeInfo const &info) { return info.name.empty(); }), + intersect.end()); + + return intersect.size() >= 2; + + /* + // set_intersection like routine to count equal name pairs, std function is + // not acceptable because of duplicates {a, a, b} ∩ {a, a, c} == {a, a}. + std::vector> commons; + + auto i1 = v1.begin(); + auto i2 = v2.begin(); + + while (i1 != v1.end() && i2 != v2.end()) + { + if (i1->name_id == i2->name_id) + { + if (i1->name_id != EMPTY_NAMEID) + commons.push_back(std::make_pair(&(*i1), &(*i2))); + + ++i1; + ++i2; + } + else if (i1->name_id < i2->name_id) + ++i1; + else + ++i2; + } + + return (commons.size() >= 2); + */ + + /// @todo Process standalone U-turns. + /* + switch (commons.size()) + { + case 0: + return false; + case 1: + // ingoing + outgoing edges + if (commons.front().first->direction + commons.front().second->direction != 1) + return false; + } + + return true; + */ +} + +size_t Extractor::FindSegregatedNodes(NodeBasedGraphFactory &factory) +{ + util::NameTable names(config.GetPath(".osrm.names").string()); + + auto &graph = factory.GetGraph(); + auto const &annotation = factory.GetAnnotationData(); + auto const &coordinates = factory.GetCoordinates(); + + auto const get_edge_name = [&](auto const &data) { + /// @todo Make string normalization/lowercase/trim for comparison ... + + auto const id = annotation[data.annotation_data].name_id; + BOOST_ASSERT(id != INVALID_NAMEID); + return names.GetNameForID(id); + }; + + auto const get_edge_classes = [&](auto const &data) { + return annotation[data.annotation_data].classes; + }; + + auto const collect_edge_info_fn = [&](auto const &edges1, NodeID node2) { + std::vector info; + + for (auto const &e : edges1) + { + NodeID const target = graph.GetTarget(e); + if (target == node2) + continue; + + auto const &data = graph.GetEdgeData(e); + info.push_back( + {target, get_edge_name(data), data.reversed ? 1 : 0, get_edge_classes(data)}); + } + + if (info.empty()) + return info; + + std::sort(info.begin(), info.end(), [](EdgeInfo const &e1, EdgeInfo const &e2) { + return e1.node < e2.node; + }); + + // Merge equal infos with correct direction. + auto curr = info.begin(); + auto next = curr; + while (++next != info.end()) + { + if (curr->node == next->node) + { + BOOST_ASSERT(curr->name == next->name); + BOOST_ASSERT(curr->road_class == next->road_class); + BOOST_ASSERT(curr->direction != next->direction); + curr->direction = 2; + } + else + curr = next; + } + + info.erase( + std::unique(info.begin(), + info.end(), + [](EdgeInfo const &e1, EdgeInfo const &e2) { return e1.node == e2.node; }), + info.end()); + + return info; + }; + + auto const isSegregatedFn = [&]( + auto const &edgeData, auto const &edges1, NodeID node1, auto const &edges2, NodeID node2) { + return IsSegregated(collect_edge_info_fn(edges1, node2), + collect_edge_info_fn(edges2, node1), + {node1, get_edge_name(edgeData), 0, get_edge_classes(edgeData)}); + }; + + std::unordered_set processed; + size_t segregated_count = 0; + + for (NodeID sourceID = 0; sourceID < graph.GetNumberOfNodes(); ++sourceID) + { + auto const sourceEdges = graph.GetAdjacentEdgeRange(sourceID); + for (EdgeID edgeID : sourceEdges) + { + auto &edgeData = graph.GetEdgeData(edgeID); + + if (edgeData.reversed || edgeData.segregated || !processed.insert(edgeID).second) + continue; + + NodeID const targetID = graph.GetTarget(edgeID); + + if (osrm::util::coordinate_calculation::haversineDistance(coordinates[sourceID], + coordinates[targetID]) > 30.0) + continue; + + auto const targetEdges = graph.GetAdjacentEdgeRange(targetID); + + if (isSegregatedFn(edgeData, sourceEdges, sourceID, targetEdges, targetID)) + { + ++segregated_count; + edgeData.segregated = true; + } + } + } + + return segregated_count; +} + } // namespace extractor } // namespace osrm diff --git a/src/extractor/node_based_graph_factory.cpp b/src/extractor/node_based_graph_factory.cpp index 8fc7b9d24..595aabd58 100644 --- a/src/extractor/node_based_graph_factory.cpp +++ b/src/extractor/node_based_graph_factory.cpp @@ -15,13 +15,16 @@ namespace osrm namespace extractor { -NodeBasedGraphFactory::NodeBasedGraphFactory( - const boost::filesystem::path &input_file, +NodeBasedGraphFactory::NodeBasedGraphFactory(const boost::filesystem::path &input_file) +{ + LoadDataFromFile(input_file); +} + +void NodeBasedGraphFactory::CompressAll( ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, std::vector &conditional_turn_restrictions) { - LoadDataFromFile(input_file); Compress(scripting_environment, turn_restrictions, conditional_turn_restrictions); CompressGeometry(); CompressAnnotationData();