From 25ee26de3ba94ec61f7046f49dad3cebbd304610 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Mon, 4 Dec 2017 18:03:55 +0000 Subject: [PATCH] Refactor segregated intersection classification to right module --- include/extractor/extractor.hpp | 8 - ...segregated_intersection_classification.hpp | 27 ++ src/extractor/extractor.cpp | 230 +---------------- ...segregated_intersection_classification.cpp | 244 ++++++++++++++++++ 4 files changed, 275 insertions(+), 234 deletions(-) create mode 100644 include/extractor/guidance/segregated_intersection_classification.hpp create mode 100644 src/extractor/guidance/segregated_intersection_classification.cpp diff --git a/include/extractor/extractor.hpp b/include/extractor/extractor.hpp index ebb4ef633..9dcd93916 100644 --- a/include/extractor/extractor.hpp +++ b/include/extractor/extractor.hpp @@ -47,7 +47,6 @@ namespace extractor class ScriptingEnvironment; struct ProfileProperties; -class NodeBasedGraphFactory; class Extractor { @@ -102,13 +101,6 @@ class Extractor void WriteConditionalRestrictions( const std::string &path, std::vector &conditional_turn_restrictions); - - // Find all "segregated" edges, e.g. edges that can be skipped in turn instructions. - // The main cases are: - // - middle edges between two osm ways in one logic road (U-turn) - // - staggered intersections (X-cross) - // - square/circle intersections - std::unordered_set FindSegregatedNodes(NodeBasedGraphFactory &factory); }; } } diff --git a/include/extractor/guidance/segregated_intersection_classification.hpp b/include/extractor/guidance/segregated_intersection_classification.hpp new file mode 100644 index 000000000..ac9fab69f --- /dev/null +++ b/include/extractor/guidance/segregated_intersection_classification.hpp @@ -0,0 +1,27 @@ +#include "util/typedefs.hpp" + +#include + +namespace osrm +{ +namespace util +{ +class NameTable; +} + +namespace extractor +{ +class NodeBasedGraphFactory; + +namespace guidance +{ +// Find all "segregated" edges, e.g. edges that can be skipped in turn instructions. +// The main cases are: +// - middle edges between two osm ways in one logic road (U-turn) +// - staggered intersections (X-cross) +// - square/circle intersections +std::unordered_set findSegregatedNodes(NodeBasedGraphFactory &factory, const util::NameTable& names); +} + +} +} diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index 54d6c27b3..0ed977c53 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -13,6 +13,8 @@ #include "extractor/restriction_parser.hpp" #include "extractor/scripting_environment.hpp" +#include "extractor/guidance/segregated_intersection_classification.hpp" + #include "storage/io.hpp" #include "util/exception.hpp" @@ -220,7 +222,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) util::Log() << "Find segregated edges in node-based graph ..." << std::flush; TIMER_START(segregated); - auto segregated_edges = FindSegregatedNodes(node_based_graph_factory); + 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"; @@ -838,232 +841,7 @@ 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; - - guidance::RoadPriorityClass::Enum road_priority_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, - double edgeLength) -{ - 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; - } - - // set_intersection like routine to get equal result pairs - std::vector> commons; - - auto i1 = v1.begin(); - auto i2 = v2.begin(); - - while (i1 != v1.end() && i2 != v2.end()) - { - if (i1->name == i2->name) - { - if (!i1->name.empty()) - commons.push_back(std::make_pair(&(*i1), &(*i2))); - - ++i1; - ++i2; - } - else if (i1->name < i2->name) - ++i1; - else - ++i2; - } - - if (commons.size() < 2) - return false; - - auto const check_equal_class = [](std::pair const &e) { - // Or (e.first->road_class & e.second->road_class != 0) - return e.first->road_class == e.second->road_class; - }; - - size_t equal_class_count = 0; - for (auto const &e : commons) - if (check_equal_class(e)) - ++equal_class_count; - - if (equal_class_count < 2) - return false; - - auto const get_length_threshold = [](EdgeInfo const *e) { - switch (e->road_priority_class) - { - case guidance::RoadPriorityClass::MOTORWAY: - case guidance::RoadPriorityClass::TRUNK: - return 30.0; - case guidance::RoadPriorityClass::PRIMARY: - return 20.0; - case guidance::RoadPriorityClass::SECONDARY: - case guidance::RoadPriorityClass::TERTIARY: - return 10.0; - default: - return 5.0; - } - }; - - double threshold = std::numeric_limits::max(); - for (auto const &e : commons) - threshold = - std::min(threshold, get_length_threshold(e.first) + get_length_threshold(e.second)); - - return edgeLength <= threshold; -} - -std::unordered_set Extractor::FindSegregatedNodes(NodeBasedGraphFactory &factory) -{ - util::NameTable names(config.GetPath(".osrm.names").string()); - - auto const &graph = factory.GetGraph(); - auto const &annotation = factory.GetAnnotationData(); - - guidance::CoordinateExtractor coordExtractor( - graph, factory.GetCompressedEdges(), factory.GetCoordinates()); - - auto const get_edge_length = [&](NodeID from_node, EdgeID edgeID, NodeID to_node) { - auto const geom = coordExtractor.GetCoordinatesAlongRoad(from_node, edgeID, false, to_node); - double length = 0.0; - for (size_t i = 1; i < geom.size(); ++i) - { - length += osrm::util::coordinate_calculation::haversineDistance(geom[i - 1], geom[i]); - } - return length; - }; - - auto const get_edge_info = [&](NodeID node, auto const &edgeData) -> EdgeInfo { - /// @todo Make string normalization/lowercase/trim for comparison ... - - auto const id = annotation[edgeData.annotation_data].name_id; - BOOST_ASSERT(id != INVALID_NAMEID); - auto const name = names.GetNameForID(id); - - return {node, - name, - edgeData.reversed ? 1 : 0, - annotation[edgeData.annotation_data].classes, - edgeData.flags.road_classification.GetClass()}; - }; - - 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; - - info.push_back(get_edge_info(target, graph.GetEdgeData(e))); - } - - 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, - double edgeLength) { - return IsSegregated(collect_edge_info_fn(edges1, node2), - collect_edge_info_fn(edges2, node1), - get_edge_info(node1, edgeData), - edgeLength); - }; - - std::unordered_set segregated_edges; - - for (NodeID sourceID = 0; sourceID < graph.GetNumberOfNodes(); ++sourceID) - { - auto const sourceEdges = graph.GetAdjacentEdgeRange(sourceID); - for (EdgeID edgeID : sourceEdges) - { - auto const &edgeData = graph.GetEdgeData(edgeID); - - if (edgeData.reversed) - continue; - - NodeID const targetID = graph.GetTarget(edgeID); - auto const targetEdges = graph.GetAdjacentEdgeRange(targetID); - - double const length = get_edge_length(sourceID, edgeID, targetID); - if (isSegregatedFn(edgeData, sourceEdges, sourceID, targetEdges, targetID, length)) - segregated_edges.insert(edgeID); - } - } - - return segregated_edges; -} } // namespace extractor } // namespace osrm diff --git a/src/extractor/guidance/segregated_intersection_classification.cpp b/src/extractor/guidance/segregated_intersection_classification.cpp new file mode 100644 index 000000000..aac67b76f --- /dev/null +++ b/src/extractor/guidance/segregated_intersection_classification.cpp @@ -0,0 +1,244 @@ +#include "extractor/guidance/segregated_intersection_classification.hpp" +#include "extractor/guidance/coordinate_extractor.hpp" +#include "extractor/node_based_graph_factory.hpp" + +#include "util/coordinate_calculation.hpp" +#include "util/name_table.hpp" + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +struct EdgeInfo +{ + NodeID node; + + util::StringView name; + + // 0 - outgoing (forward), 1 - incoming (reverse), 2 - both outgoing and incoming + int direction; + + ClassData road_class; + + RoadPriorityClass::Enum road_priority_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, + double edgeLength) +{ + 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; + } + + // set_intersection like routine to get equal result pairs + std::vector> commons; + + auto i1 = v1.begin(); + auto i2 = v2.begin(); + + while (i1 != v1.end() && i2 != v2.end()) + { + if (i1->name == i2->name) + { + if (!i1->name.empty()) + commons.push_back(std::make_pair(&(*i1), &(*i2))); + + ++i1; + ++i2; + } + else if (i1->name < i2->name) + ++i1; + else + ++i2; + } + + if (commons.size() < 2) + return false; + + auto const check_equal_class = [](std::pair const &e) { + // Or (e.first->road_class & e.second->road_class != 0) + return e.first->road_class == e.second->road_class; + }; + + size_t equal_class_count = 0; + for (auto const &e : commons) + if (check_equal_class(e)) + ++equal_class_count; + + if (equal_class_count < 2) + return false; + + auto const get_length_threshold = [](EdgeInfo const *e) { + switch (e->road_priority_class) + { + case RoadPriorityClass::MOTORWAY: + case RoadPriorityClass::TRUNK: + return 30.0; + case RoadPriorityClass::PRIMARY: + return 20.0; + case RoadPriorityClass::SECONDARY: + case RoadPriorityClass::TERTIARY: + return 10.0; + default: + return 5.0; + } + }; + + double threshold = std::numeric_limits::max(); + for (auto const &e : commons) + threshold = + std::min(threshold, get_length_threshold(e.first) + get_length_threshold(e.second)); + + return edgeLength <= threshold; +} + +std::unordered_set findSegregatedNodes(NodeBasedGraphFactory &factory, + const util::NameTable &names) +{ + + auto const &graph = factory.GetGraph(); + auto const &annotation = factory.GetAnnotationData(); + + CoordinateExtractor coordExtractor( + graph, factory.GetCompressedEdges(), factory.GetCoordinates()); + + auto const get_edge_length = [&](NodeID from_node, EdgeID edgeID, NodeID to_node) { + auto const geom = coordExtractor.GetCoordinatesAlongRoad(from_node, edgeID, false, to_node); + double length = 0.0; + for (size_t i = 1; i < geom.size(); ++i) + { + length += util::coordinate_calculation::haversineDistance(geom[i - 1], geom[i]); + } + return length; + }; + + auto const get_edge_info = [&](NodeID node, auto const &edgeData) -> EdgeInfo { + /// @todo Make string normalization/lowercase/trim for comparison ... + + auto const id = annotation[edgeData.annotation_data].name_id; + BOOST_ASSERT(id != INVALID_NAMEID); + auto const name = names.GetNameForID(id); + + return {node, + name, + edgeData.reversed ? 1 : 0, + annotation[edgeData.annotation_data].classes, + edgeData.flags.road_classification.GetClass()}; + }; + + 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; + + info.push_back(get_edge_info(target, graph.GetEdgeData(e))); + } + + 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, + double edgeLength) { + return IsSegregated(collect_edge_info_fn(edges1, node2), + collect_edge_info_fn(edges2, node1), + get_edge_info(node1, edgeData), + edgeLength); + }; + + std::unordered_set segregated_edges; + + for (NodeID sourceID = 0; sourceID < graph.GetNumberOfNodes(); ++sourceID) + { + auto const sourceEdges = graph.GetAdjacentEdgeRange(sourceID); + for (EdgeID edgeID : sourceEdges) + { + auto const &edgeData = graph.GetEdgeData(edgeID); + + if (edgeData.reversed) + continue; + + NodeID const targetID = graph.GetTarget(edgeID); + auto const targetEdges = graph.GetAdjacentEdgeRange(targetID); + + double const length = get_edge_length(sourceID, edgeID, targetID); + if (isSegregatedFn(edgeData, sourceEdges, sourceID, targetEdges, targetID, length)) + segregated_edges.insert(edgeID); + } + } + + return segregated_edges; +} + +} +} +}