add node-based-graph traversal capabilities

This commit is contained in:
Moritz Kobitzsch 2016-10-29 21:38:10 +02:00
parent 4ba8ccfcce
commit faa5185440
4 changed files with 309 additions and 8 deletions

View File

@ -5,6 +5,7 @@
#include <vector>
#include "extractor/guidance/turn_instruction.hpp"
#include "util/guidance/toolkit.hpp"
#include "util/typedefs.hpp" // EdgeID
namespace osrm
@ -59,7 +60,30 @@ struct ConnectedRoad final
// small helper function to print the content of a connected road
std::string toString(const ConnectedRoad &road);
typedef std::vector<ConnectedRoad> Intersection;
struct Intersection final : public std::vector<ConnectedRoad>
{
using Base = std::vector<ConnectedRoad>;
inline Base::iterator findClosestTurn(double angle)
{
return std::min_element(this->begin(),
this->end(),
[angle](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
return util::guidance::angularDeviation(lhs.turn.angle, angle) <
util::guidance::angularDeviation(rhs.turn.angle, angle);
});
}
inline Base::const_iterator findClosestTurn(double angle) const
{
return std::min_element(this->begin(),
this->end(),
[angle](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
return util::guidance::angularDeviation(lhs.turn.angle, angle) <
util::guidance::angularDeviation(rhs.turn.angle, angle);
});
}
};
Intersection::const_iterator findClosestTurn(const Intersection &intersection, const double angle);
Intersection::iterator findClosestTurn(Intersection &intersection, const double angle);

View File

@ -50,13 +50,6 @@ class IntersectionGenerator
// Allow access to the coordinate extractor for all owners
const CoordinateExtractor &GetCoordinateExtractor() const;
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const RestrictionMap &restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const std::vector<QueryNode> &node_info_list;
const CoordinateExtractor coordinate_extractor;
// Check for restrictions/barriers and generate a list of valid and invalid turns present at
// the
// node reached
@ -65,6 +58,13 @@ class IntersectionGenerator
OSRM_ATTR_WARN_UNUSED
Intersection GetConnectedRoads(const NodeID from_node, const EdgeID via_eid) const;
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const RestrictionMap &restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const std::vector<QueryNode> &node_info_list;
const CoordinateExtractor coordinate_extractor;
// check if two indices in an intersection can be seen as a single road in the perceived
// intersection representation. See below for an example. Utility function for
// MergeSegregatedRoads

View File

@ -0,0 +1,177 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_NODE_BASED_GRAPH_WALKER
#define OSRM_EXTRACTOR_GUIDANCE_NODE_BASED_GRAPH_WALKER
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/toolkit.hpp"
#include "extractor/guidance/toolkit.hpp"
#include "util/coordinate.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
#include <boost/assert.hpp>
#include <boost/optional.hpp>
#include <utility>
namespace osrm
{
namespace extractor
{
namespace guidance
{
// forward declaration to allow interaction between the intersection generator and the graph hopper
class IntersectionGenerator;
/*
* The graph hopper is a utility that lets you find certain intersections with a node based graph,
* accumulating information along the way
*/
class NodeBasedGraphWalker
{
public:
NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &node_based_graph,
const IntersectionGenerator &intersection_generator);
/*
* the returned node-id, edge-id are either the last ones used, just prior accumulator
* terminating or empty if the traversal ran into a dead end. For examples of the
* selector/accumulator look below. You can find an example for both (and the required interface
* description). The function returns the last used `NodeID` and `EdgeID` (node just prior to
* the last intersection and the edge it was reached by), if it wasn't stopped early (e.g. the
* selector not provinding any further edge to traverse)
*/
template <class accumulator_type, class selector_type>
boost::optional<std::pair<NodeID, EdgeID>> TraverseRoad(NodeID starting_at_node_id,
EdgeID following_edge_id,
accumulator_type &accumulator,
const selector_type &selector);
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const IntersectionGenerator &intersection_generator;
};
/*
* Accumulate all coordinates following a road until we
* Example of a possible accumulator for walking a node-based graph
*/
struct LengthLimitedCoordinateAccumulator
{
LengthLimitedCoordinateAccumulator(
const extractor::guidance::CoordinateExtractor &coordinate_extractor,
const util::NodeBasedDynamicGraph &node_based_graph,
const double max_length);
/*
* !! REQUIRED - Function for the use of TraverseRoad in the graph walker.
* Terminate should return true if the last intersection given to accumulator is supposed to
* stop the search. A typical example would be to find the next intersection with degree larger
* than 2 (an actual intersection). Here you should return true if the last intersection you
* looked at was of degree larger than 2.
*/
bool terminate(); // true if the path has traversed enough distance
/*
* !! REQUIRED - Function for the use of TraverseRoad in the graph walker.
* starting with the very first provided node and edge, the graph walker will call `update` on
* your accumulator. Here you can choose to accumulate any data that you might want to collect /
* update your termination criteria. The accumulator described here will extract the coordinates
* that we see traversing `via_edge` and store them for later usage.
*/
void update(const NodeID from_node, const EdgeID via_edge, const NodeID to_node);
const extractor::guidance::CoordinateExtractor &coordinate_extractor;
const util::NodeBasedDynamicGraph &node_based_graph;
const double max_length;
double accumulated_length;
std::vector<util::Coordinate> coordinates;
};
/*
* The FollowRoadNameSelector tries to follow a given name along a route. We offer methods to skip
* over bridges/similar situations if desired, following narrow turns
* This struct offers an example implementation of a possible road selector for traversing the
* node-based graph using the NodeBasedGraphWalker
*/
struct SelectRoadByNameOnlyChoiceAndStraightness
{
SelectRoadByNameOnlyChoiceAndStraightness(const NameID desired_name_id,
const bool requires_entry);
/*
* !! REQUIRED - Function for the use of TraverseRoad in the graph walker.
* The operator() needs to return (if any is found) the next road to continue in the graph
* traversal. If no such edge is found, return {} is allowed. Usually you want to choose some
* form of obious turn to follow.
*/
boost::optional<EdgeID> operator()(const NodeID nid,
const EdgeID via_edge_id,
const Intersection &intersection,
const util::NodeBasedDynamicGraph &node_based_graph) const;
const NameID desired_name_id;
const bool requires_entry;
};
template <class accumulator_type, class selector_type>
boost::optional<std::pair<NodeID, EdgeID>>
NodeBasedGraphWalker::TraverseRoad(NodeID current_node_id,
EdgeID current_edge_id,
accumulator_type &accumulator,
const selector_type &selector)
{
/*
* since graph hopping is used in many ways, we don't generate an adjusted intersection
* (otherwise we could end up in infinite recursion if we call the graph hopper during the
* adjustment itself). Relying only on `GetConnectedRoads` (which itself does no graph hopping),
* we prevent this from happening.
*/
const auto stop_node_id = current_node_id;
/* we wan't to put out the last valid entries. To do so, we need to update within the following
* loop. We use a for loop since traversal of the node-based-graph is expensive and we don't
* want to look at many coordinates. If you require more than 2/3 intersections down the road,
* you are doing something wrong/unsupported by OSRM. To not fail hard in cases that offer
* strange loop contractions, we restrict ourselves to an extremely large number of possible
* steps and simply warn in cases were extraction runs into these limits.
*/
for (std::size_t safety_hop_limit = 0; safety_hop_limit < 1000; ++safety_hop_limit)
{
accumulator.update(
current_node_id, current_edge_id, node_based_graph.GetTarget(current_edge_id));
// we have looped back to our initial intersection
if (node_based_graph.GetTarget(current_edge_id) == stop_node_id)
return {};
// look at the next intersection
const auto next_intersection =
intersection_generator.GetConnectedRoads(current_node_id, current_edge_id);
// don't follow u-turns or go past our initial intersection
if (next_intersection.size() <= 1)
return {};
auto next_edge_id =
selector(current_node_id, current_edge_id, next_intersection, node_based_graph);
if (!next_edge_id)
return {};
if (accumulator.terminate())
return {std::make_pair(current_node_id, current_edge_id)};
current_node_id = node_based_graph.GetTarget(current_edge_id);
current_edge_id = *next_edge_id;
}
BOOST_ASSERT(
"Reached safety hop limit. Graph hopper seems to have been caught in an endless loop");
return {};
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /* OSRM_EXTRACTOR_GUIDANCE_NODE_BASED_GRAPH_WALKER */

View File

@ -0,0 +1,100 @@
#include "extractor/guidance/node_based_graph_walker.hpp"
#include "util/coordinate_calculation.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
// ---------------------------------------------------------------------------------
NodeBasedGraphWalker::NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &node_based_graph,
const IntersectionGenerator &intersection_generator)
: node_based_graph(node_based_graph), intersection_generator(intersection_generator)
{
}
LengthLimitedCoordinateAccumulator::LengthLimitedCoordinateAccumulator(
const extractor::guidance::CoordinateExtractor &coordinate_extractor,
const util::NodeBasedDynamicGraph &node_based_graph,
const double max_length)
: coordinate_extractor(coordinate_extractor), node_based_graph(node_based_graph),
max_length(max_length), accumulated_length(0)
{
}
bool LengthLimitedCoordinateAccumulator::terminate() { return accumulated_length >= max_length; }
// update the accumulator
void LengthLimitedCoordinateAccumulator::update(const NodeID from_node,
const EdgeID via_edge,
const NodeID to_node)
{
const util::NodeBasedEdgeData &edge_data = node_based_graph.GetEdgeData(via_edge);
const auto current_coordinates = coordinate_extractor.GetForwardCoordinatesAlongRoad(
from_node, via_edge);
const auto length = util::coordinate_calculation::getLength(
coordinates, util::coordinate_calculation::haversineDistance);
// in case we get too many coordinates, we limit them to our desired length
if (length + accumulated_length > max_length)
coordinate_extractor.TrimCoordinatesToLength(current_coordinates,
max_length - accumulated_length);
coordinates.insert(coordinates.end(), current_coordinates.begin(), current_coordinates.end());
accumulated_length += length;
}
// ---------------------------------------------------------------------------------
SelectRoadByNameOnlyChoiceAndStraightness::SelectRoadByNameOnlyChoiceAndStraightness(
const NameID desired_name_id, const bool requires_entry)
: desired_name_id(desired_name_id), requires_entry(requires_entry)
{
}
boost::optional<EdgeID> SelectRoadByNameOnlyChoiceAndStraightness::
operator()(const NodeID /*nid*/,
const EdgeID /*via_edge_id*/,
const Intersection &intersection,
const util::NodeBasedDynamicGraph &node_based_graph) const
{
BOOST_ASSERT(!intersection.empty());
const auto comparator = [this, &node_based_graph](const ConnectedRoad &lhs,
const ConnectedRoad &rhs) {
// the score of an elemnt results in an ranking preferring valid entries, if required over
// invalid
// requested name_ids over non-requested
// narrow deviations over non-narrow
const auto score = [this, &node_based_graph](const ConnectedRoad &road) {
double result_score = 0;
// since angular deviation is limited by 0-180, we add 360 for invalid
if (requires_entry && !road.entry_allowed)
result_score += 360.;
// 180 for undesired name-ids
if (desired_name_id != node_based_graph.GetEdgeData(road.turn.eid).name_id)
result_score += 180;
return result_score + angularDeviation(road.turn.angle, STRAIGHT_ANGLE);
};
return score(lhs) < score(rhs);
};
const auto min_element =
std::min_element(std::next(std::begin(intersection)), std::end(intersection), comparator);
if (min_element == intersection.end() || (requires_entry && !min_element->entry_allowed))
return {};
else
return min_element->turn.eid;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm