add node-based-graph traversal capabilities
This commit is contained in:
parent
4ba8ccfcce
commit
faa5185440
@ -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);
|
||||
|
@ -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
|
||||
|
177
include/extractor/guidance/node_based_graph_walker.hpp
Normal file
177
include/extractor/guidance/node_based_graph_walker.hpp
Normal 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 */
|
100
src/extractor/guidance/node_based_graph_walker.cpp
Normal file
100
src/extractor/guidance/node_based_graph_walker.cpp
Normal 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
|
Loading…
Reference in New Issue
Block a user