osrm-backend/include/extractor/guidance/node_based_graph_walker.hpp
Moritz Kobitzsch 561b7cc58e Rember Intersection Shapes
Changes the processing order in the edge based graph factory.
Instead of iterating over all outgoing edges in order, we compute the edge
expanded graph in the order of intersections.
This allows to remember intersection shapes and re-use them for all possible ingoing edges.

Also: use low accuracry mode for intersections degree 2 intersections

We can use lower accuracy here, since the `bearing`
after the turn is not as relevant for off-route detection.
Getting lost is near impossible here.
2016-12-01 15:24:20 +01:00

200 lines
8.1 KiB
C++

#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 <cstdint>
#include <utility>
namespace osrm
{
namespace extractor
{
namespace guidance
{
/*
* 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 SelectRoadByNameOnlyChoiceAndStraightness 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;
};
// find the next intersection given a hop limit
struct IntersectionFinderAccumulator
{
IntersectionFinderAccumulator(const std::uint8_t hop_limit,
const IntersectionGenerator &intersection_generator);
// true if the path has traversed enough distance
bool terminate();
// update the accumulator
void update(const NodeID from_node, const EdgeID via_edge, const NodeID to_node);
std::uint8_t hops;
const std::uint8_t hop_limit;
// we need to be able to look-up the intersection
const IntersectionGenerator &intersection_generator;
// the result we are looking for
NodeID nid;
EdgeID via_edge_id;
IntersectionView intersection;
};
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 */