refactor of turn analysis into turn handlers
This commit is contained in:
parent
dfd180a292
commit
fcbf527ba5
66
include/extractor/guidance/intersection.hpp
Normal file
66
include/extractor/guidance/intersection.hpp
Normal file
@ -0,0 +1,66 @@
|
||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "util/typedefs.hpp" // EdgeID
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
// Every Turn Operation describes a way of switching onto a segment, indicated by an EdgeID. The
|
||||
// associated turn is described by an angle and an instruction that is used to announce it.
|
||||
// The Turn Operation indicates what is exposed to the outside of the turn analysis.
|
||||
struct TurnOperation final
|
||||
{
|
||||
EdgeID eid;
|
||||
double angle;
|
||||
TurnInstruction instruction;
|
||||
};
|
||||
|
||||
// A Connected Road is the internal representation of a potential turn. Internally, we require
|
||||
// full list of all connected roads to determine the outcome.
|
||||
// The reasoning behind is that even invalid turns can influence the perceived angles, or even
|
||||
// instructions themselves. An pososible example can be described like this:
|
||||
//
|
||||
// aaa(2)aa
|
||||
// a - bbbbb
|
||||
// aaa(1)aa
|
||||
//
|
||||
// will not be perceived as a turn from (1) -> b, and as a U-turn from (1) -> (2).
|
||||
// In addition, they can influence whether a turn is obvious or not. b->(2) would also be no
|
||||
// turn-operation,
|
||||
// but rather a name change.
|
||||
//
|
||||
// If this were a normal intersection with
|
||||
//
|
||||
// cccccccc
|
||||
// o bbbbb
|
||||
// aaaaaaaa
|
||||
//
|
||||
// We would perceive a->c as a sharp turn, a->b as a slight turn, and b->c as a slight turn.
|
||||
struct ConnectedRoad final
|
||||
{
|
||||
ConnectedRoad(const TurnOperation turn, const bool entry_allowed = false);
|
||||
|
||||
// a turn may be relevant to good instructions, even if we cannot enter the road
|
||||
bool entry_allowed;
|
||||
TurnOperation turn;
|
||||
};
|
||||
|
||||
// small helper function to print the content of a connected road
|
||||
std::string toString(const ConnectedRoad &road);
|
||||
|
||||
typedef std::vector<ConnectedRoad> Intersection;
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_*/
|
68
include/extractor/guidance/intersection_generator.hpp
Normal file
68
include/extractor/guidance/intersection_generator.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_
|
||||
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "extractor/restriction_map.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
// The Intersection Generator is given a turn location and generates an intersection representation
|
||||
// from it. For this all turn possibilities are analysed.
|
||||
// We consider turn restrictions to indicate possible turns. U-turns are generated based on profile
|
||||
// decisions.
|
||||
|
||||
class IntersectionGenerator
|
||||
{
|
||||
public:
|
||||
IntersectionGenerator(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 CompressedEdgeContainer &compressed_edge_container);
|
||||
|
||||
Intersection operator()(const NodeID nid, 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 CompressedEdgeContainer &compressed_edge_container;
|
||||
|
||||
// Check for restrictions/barriers and generate a list of valid and invalid turns present at
|
||||
// the
|
||||
// node reached
|
||||
// from `from_node` via `via_eid`
|
||||
// The resulting candidates have to be analysed for their actual instructions later on.
|
||||
Intersection getConnectedRoads(const NodeID from_node, const EdgeID via_eid) const;
|
||||
|
||||
// Merge segregated roads to omit invalid turns in favor of treating segregated roads as
|
||||
// one.
|
||||
// This function combines roads the following way:
|
||||
//
|
||||
// * *
|
||||
// * is converted to *
|
||||
// v ^ +
|
||||
// v ^ +
|
||||
//
|
||||
// The treatment results in a straight turn angle of 180º rather than a turn angle of approx
|
||||
// 160
|
||||
Intersection mergeSegregatedRoads(Intersection intersection) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif /* OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_ */
|
71
include/extractor/guidance/intersection_handler.hpp
Normal file
71
include/extractor/guidance/intersection_handler.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_
|
||||
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
|
||||
#include "util/name_table.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
// Intersection handlers deal with all issues related to intersections.
|
||||
// They assign appropriate turn operations to the TurnOperations.
|
||||
// This base class provides both the interface and implementations for
|
||||
// common functions.
|
||||
class IntersectionHandler
|
||||
{
|
||||
public:
|
||||
IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const util::NameTable &name_table);
|
||||
virtual ~IntersectionHandler();
|
||||
|
||||
// check whether the handler can actually handle the intersection
|
||||
virtual bool
|
||||
canProcess(const NodeID nid, const EdgeID via_eid, const Intersection &intersection) const = 0;
|
||||
|
||||
// process the intersection
|
||||
virtual Intersection
|
||||
operator()(const NodeID nid, const EdgeID via_eid, Intersection intersection) const = 0;
|
||||
|
||||
protected:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
const std::vector<QueryNode> &node_info_list;
|
||||
const util::NameTable &name_table;
|
||||
|
||||
// counts the number on allowed entry roads
|
||||
std::size_t countValid(const Intersection &intersection) const;
|
||||
|
||||
// Decide on a basic turn types
|
||||
TurnType findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
|
||||
|
||||
// Get the Instruction for an obvious turn
|
||||
TurnInstruction getInstructionForObvious(const std::size_t number_of_candidates,
|
||||
const EdgeID via_edge,
|
||||
const bool through_street,
|
||||
const ConnectedRoad &candidate) const;
|
||||
|
||||
// Treating potential forks
|
||||
void assignFork(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
|
||||
void assignFork(const EdgeID via_edge,
|
||||
ConnectedRoad &left,
|
||||
ConnectedRoad ¢er,
|
||||
ConnectedRoad &right) const;
|
||||
|
||||
bool isThroughStreet(const std::size_t index, const Intersection &intersection) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_*/
|
51
include/extractor/guidance/motorway_handler.hpp
Normal file
51
include/extractor/guidance/motorway_handler.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
|
||||
|
||||
#include "extractor/guidance/intersection_handler.hpp"
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
|
||||
#include "util/name_table.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
// Intersection handlers deal with all issues related to intersections.
|
||||
// They assign appropriate turn operations to the TurnOperations.
|
||||
class MotorwayHandler : public IntersectionHandler
|
||||
{
|
||||
public:
|
||||
MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const util::NameTable &name_table);
|
||||
~MotorwayHandler() override final;
|
||||
|
||||
// check whether the handler can actually handle the intersection
|
||||
bool canProcess(const NodeID nid,
|
||||
const EdgeID via_eid,
|
||||
const Intersection &intersection) const override final;
|
||||
|
||||
// process the intersection
|
||||
Intersection operator()(const NodeID nid,
|
||||
const EdgeID via_eid,
|
||||
Intersection intersection) const override final;
|
||||
|
||||
private:
|
||||
Intersection fromMotorway(const EdgeID via_edge, Intersection intersection) const;
|
||||
Intersection fromRamp(const EdgeID via_edge, Intersection intersection) const;
|
||||
|
||||
Intersection fallback(Intersection intersection) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif /*OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_*/
|
71
include/extractor/guidance/roundabout_handler.hpp
Normal file
71
include/extractor/guidance/roundabout_handler.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_
|
||||
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/intersection_handler.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
|
||||
#include "util/name_table.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct RoundaboutFlags
|
||||
{
|
||||
bool on_roundabout;
|
||||
bool can_enter;
|
||||
bool can_exit_separately;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
// The roundabout handler processes all roundabout related instructions.
|
||||
// It performs both the distinction between rotaries and roundabouts and
|
||||
// assigns appropriate entry/exit instructions.
|
||||
class RoundaboutHandler : public IntersectionHandler
|
||||
{
|
||||
public:
|
||||
RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const util::NameTable &name_table);
|
||||
|
||||
~RoundaboutHandler() override final;
|
||||
|
||||
// check whether the handler can actually handle the intersection
|
||||
bool canProcess(const NodeID from_nid, const EdgeID via_eid, const Intersection &intersection) const override final;
|
||||
|
||||
// process the intersection
|
||||
Intersection operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersection) const override final;
|
||||
|
||||
private:
|
||||
detail::RoundaboutFlags getRoundaboutFlags(const NodeID from_nid, const EdgeID via_eid, const Intersection &intersection) const;
|
||||
|
||||
// decide whether we lookk at a roundabout or a rotary
|
||||
bool isRotary(const NodeID nid) const;
|
||||
|
||||
// TODO handle bike/walk cases that allow crossing a roundabout!
|
||||
// Processing of roundabouts
|
||||
// Produces instructions to enter/exit a roundabout or to stay on it.
|
||||
// Performs the distinction between roundabout and rotaries.
|
||||
Intersection handleRoundabouts(const bool is_rotary,
|
||||
const EdgeID via_edge,
|
||||
const bool on_roundabout,
|
||||
const bool can_exit_roundabout,
|
||||
Intersection intersection) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif /*OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_*/
|
@ -2,11 +2,18 @@
|
||||
#define OSRM_EXTRACTOR_TURN_ANALYSIS
|
||||
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
#include "extractor/guidance/turn_classification.hpp"
|
||||
#include "extractor/guidance/roundabout_handler.hpp"
|
||||
#include "extractor/guidance/motorway_handler.hpp"
|
||||
#include "extractor/guidance/turn_handler.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "extractor/restriction_map.hpp"
|
||||
|
||||
#include "util/name_table.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@ -23,46 +30,6 @@ namespace extractor
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
// What is exposed to the outside
|
||||
struct TurnOperation final
|
||||
{
|
||||
EdgeID eid;
|
||||
double angle;
|
||||
TurnInstruction instruction;
|
||||
};
|
||||
|
||||
// For the turn analysis, we require a full list of all connected roads to determine the outcome.
|
||||
// Invalid turns can influence the perceived angles
|
||||
//
|
||||
// aaa(2)aa
|
||||
// a - bbbbb
|
||||
// aaa(1)aa
|
||||
//
|
||||
// will not be perceived as a turn from (1) -> b, and as a U-turn from (1) -> (2).
|
||||
// In addition, they can influence whether a turn is obvious or not.
|
||||
struct ConnectedRoad final
|
||||
{
|
||||
ConnectedRoad(const TurnOperation turn, const bool entry_allowed = false);
|
||||
|
||||
TurnOperation turn;
|
||||
bool entry_allowed; // a turn may be relevant to good instructions, even if we cannot take
|
||||
// the road
|
||||
|
||||
std::string toString() const
|
||||
{
|
||||
std::string result = "[connection] ";
|
||||
result += std::to_string(turn.eid);
|
||||
result += " allows entry: ";
|
||||
result += std::to_string(entry_allowed);
|
||||
result += " angle: ";
|
||||
result += std::to_string(turn.angle);
|
||||
result += " instruction: ";
|
||||
result += std::to_string(static_cast<std::int32_t>(turn.instruction.type)) + " " +
|
||||
std::to_string(static_cast<std::int32_t>(turn.instruction.direction_modifier));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
class TurnAnalysis
|
||||
{
|
||||
|
||||
@ -79,123 +46,14 @@ class TurnAnalysis
|
||||
|
||||
private:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
const std::vector<QueryNode> &node_info_list;
|
||||
const RestrictionMap &restriction_map;
|
||||
const std::unordered_set<NodeID> &barrier_nodes;
|
||||
const CompressedEdgeContainer &compressed_edge_container;
|
||||
const util::NameTable &name_table;
|
||||
|
||||
// Check for restrictions/barriers and generate a list of valid and invalid turns present at
|
||||
// the
|
||||
// node reached
|
||||
// from `from_node` via `via_eid`
|
||||
// The resulting candidates have to be analysed for their actual instructions later on.
|
||||
std::vector<ConnectedRoad> getConnectedRoads(const NodeID from_node,
|
||||
const EdgeID via_eid) const;
|
||||
|
||||
// Merge segregated roads to omit invalid turns in favor of treating segregated roads as
|
||||
// one.
|
||||
// This function combines roads the following way:
|
||||
//
|
||||
// * *
|
||||
// * is converted to *
|
||||
// v ^ +
|
||||
// v ^ +
|
||||
//
|
||||
// The treatment results in a straight turn angle of 180º rather than a turn angle of approx
|
||||
// 160
|
||||
std::vector<ConnectedRoad> mergeSegregatedRoads(std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// TODO distinguish roundabouts and rotaries
|
||||
// TODO handle bike/walk cases that allow crossing a roundabout!
|
||||
|
||||
// Processing of roundabouts
|
||||
// Produces instructions to enter/exit a roundabout or to stay on it.
|
||||
// Performs the distinction between roundabout and rotaries.
|
||||
std::vector<ConnectedRoad> handleRoundabouts(const bool is_rotary,
|
||||
const EdgeID via_edge,
|
||||
const bool on_roundabout,
|
||||
const bool can_exit_roundabout,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Indicates a Junction containing a motoryway
|
||||
bool isMotorwayJunction(const EdgeID via_edge,
|
||||
const std::vector<ConnectedRoad> &intersection) const;
|
||||
|
||||
// Decide whether a turn is a turn or a ramp access
|
||||
TurnType findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
|
||||
|
||||
// Get the Instruction for an obvious turn
|
||||
// Instruction will be a silent instruction
|
||||
TurnInstruction getInstructionForObvious(const std::size_t number_of_candidates,
|
||||
const EdgeID via_edge,
|
||||
const bool through_street,
|
||||
const ConnectedRoad &candidate) const;
|
||||
|
||||
// Helper Function that decides between NoTurn or NewName
|
||||
TurnInstruction
|
||||
noTurnOrNewName(const NodeID from, const EdgeID via_edge, const ConnectedRoad &candidate) const;
|
||||
|
||||
// Basic Turn Handling
|
||||
|
||||
// Dead end.
|
||||
std::vector<ConnectedRoad> handleOneWayTurn(std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Mode Changes, new names...
|
||||
std::vector<ConnectedRoad> handleTwoWayTurn(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Forks, T intersections and similar
|
||||
std::vector<ConnectedRoad> handleThreeWayTurn(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Handling of turns larger then degree three
|
||||
std::vector<ConnectedRoad> handleComplexTurn(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Any Junction containing motorways
|
||||
std::vector<ConnectedRoad>
|
||||
handleMotorwayJunction(const EdgeID via_edge, std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
std::vector<ConnectedRoad> handleFromMotorway(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
std::vector<ConnectedRoad> handleMotorwayRamp(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
const IntersectionGenerator intersection_generator;
|
||||
const RoundaboutHandler roundabout_handler;
|
||||
const MotorwayHandler motorway_handler;
|
||||
const TurnHandler turn_handler;
|
||||
|
||||
// Utility function, setting basic turn types. Prepares for normal turn handling.
|
||||
std::vector<ConnectedRoad> setTurnTypes(const NodeID from,
|
||||
const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Assignment of specific turn types
|
||||
void assignFork(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
|
||||
void assignFork(const EdgeID via_edge,
|
||||
ConnectedRoad &left,
|
||||
ConnectedRoad ¢er,
|
||||
ConnectedRoad &right) const;
|
||||
|
||||
void
|
||||
handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
|
||||
|
||||
// Type specific fallbacks
|
||||
std::vector<ConnectedRoad>
|
||||
fallbackTurnAssignmentMotorway(std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Classification
|
||||
std::size_t findObviousTurn(const EdgeID via_edge,
|
||||
const std::vector<ConnectedRoad> &intersection) const;
|
||||
std::pair<std::size_t, std::size_t>
|
||||
findFork(const std::vector<ConnectedRoad> &intersection) const;
|
||||
|
||||
std::vector<ConnectedRoad> assignLeftTurns(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection,
|
||||
const std::size_t starting_at) const;
|
||||
std::vector<ConnectedRoad> assignRightTurns(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection,
|
||||
const std::size_t up_to) const;
|
||||
|
||||
bool isRotary(const NodeID nid) const;
|
||||
Intersection
|
||||
setTurnTypes(const NodeID from, const EdgeID via_edge, Intersection intersection) const;
|
||||
}; // class TurnAnalysis
|
||||
|
||||
} // namespace guidance
|
||||
|
74
include/extractor/guidance/turn_handler.hpp
Normal file
74
include/extractor/guidance/turn_handler.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_
|
||||
|
||||
#include "extractor/guidance/intersection_handler.hpp"
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
|
||||
#include "util/name_table.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
// Intersection handlers deal with all issues related to intersections.
|
||||
// They assign appropriate turn operations to the TurnOperations.
|
||||
class TurnHandler : public IntersectionHandler
|
||||
{
|
||||
public:
|
||||
TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const util::NameTable &name_table);
|
||||
~TurnHandler() override final;
|
||||
|
||||
// check whether the handler can actually handle the intersection
|
||||
bool canProcess(const NodeID nid,
|
||||
const EdgeID via_eid,
|
||||
const Intersection &intersection) const override final;
|
||||
|
||||
// process the intersection
|
||||
Intersection operator()(const NodeID nid,
|
||||
const EdgeID via_eid,
|
||||
Intersection intersection) const override final;
|
||||
|
||||
private:
|
||||
// Dead end.
|
||||
Intersection handleOneWayTurn(Intersection intersection) const;
|
||||
|
||||
// Mode Changes, new names...
|
||||
Intersection handleTwoWayTurn(const EdgeID via_edge, Intersection intersection) const;
|
||||
|
||||
// Forks, T intersections and similar
|
||||
Intersection handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const;
|
||||
|
||||
// Handling of turns larger then degree three
|
||||
Intersection handleComplexTurn(const EdgeID via_edge, Intersection intersection) const;
|
||||
|
||||
void
|
||||
handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
|
||||
|
||||
// Classification
|
||||
std::size_t findObviousTurn(const EdgeID via_edge, const Intersection &intersection) const;
|
||||
std::pair<std::size_t, std::size_t> findFork(const Intersection &intersection) const;
|
||||
|
||||
Intersection assignLeftTurns(const EdgeID via_edge,
|
||||
Intersection intersection,
|
||||
const std::size_t starting_at) const;
|
||||
Intersection assignRightTurns(const EdgeID via_edge,
|
||||
Intersection intersection,
|
||||
const std::size_t up_to) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif /*OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_*/
|
31
src/extractor/guidance/intersection.cpp
Normal file
31
src/extractor/guidance/intersection.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
ConnectedRoad::ConnectedRoad(const TurnOperation turn, const bool entry_allowed)
|
||||
: entry_allowed(entry_allowed), turn(turn)
|
||||
{
|
||||
}
|
||||
|
||||
std::string toString(const ConnectedRoad &road)
|
||||
{
|
||||
std::string result = "[connection] ";
|
||||
result += std::to_string(road.turn.eid);
|
||||
result += " allows entry: ";
|
||||
result += std::to_string(road.entry_allowed);
|
||||
result += " angle: ";
|
||||
result += std::to_string(road.turn.angle);
|
||||
result += " instruction: ";
|
||||
result += std::to_string(static_cast<std::int32_t>(road.turn.instruction.type)) + " " +
|
||||
std::to_string(static_cast<std::int32_t>(road.turn.instruction.direction_modifier));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
255
src/extractor/guidance/intersection_generator.cpp
Normal file
255
src/extractor/guidance/intersection_generator.cpp
Normal file
@ -0,0 +1,255 @@
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
IntersectionGenerator::IntersectionGenerator(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 CompressedEdgeContainer &compressed_edge_container)
|
||||
: node_based_graph(node_based_graph), restriction_map(restriction_map),
|
||||
barrier_nodes(barrier_nodes), node_info_list(node_info_list),
|
||||
compressed_edge_container(compressed_edge_container)
|
||||
{
|
||||
}
|
||||
|
||||
Intersection IntersectionGenerator::operator()(const NodeID from_node, const EdgeID via_eid) const
|
||||
{
|
||||
return getConnectedRoads(from_node, via_eid);
|
||||
}
|
||||
|
||||
// a
|
||||
// |
|
||||
// |
|
||||
// v
|
||||
// For an intersection from_node --via_edi--> turn_node ----> c
|
||||
// ^
|
||||
// |
|
||||
// |
|
||||
// b
|
||||
// This functions returns _all_ turns as if the graph was undirected.
|
||||
// That means we not only get (from_node, turn_node, c) in the above example
|
||||
// but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are
|
||||
// marked as invalid and only needed for intersection classification.
|
||||
Intersection IntersectionGenerator::getConnectedRoads(const NodeID from_node,
|
||||
const EdgeID via_eid) const
|
||||
{
|
||||
Intersection intersection;
|
||||
const NodeID turn_node = node_based_graph.GetTarget(via_eid);
|
||||
const NodeID only_restriction_to_node =
|
||||
restriction_map.CheckForEmanatingIsOnlyTurn(from_node, turn_node);
|
||||
const bool is_barrier_node = barrier_nodes.find(turn_node) != barrier_nodes.end();
|
||||
|
||||
bool has_uturn_edge = false;
|
||||
for (const EdgeID onto_edge : node_based_graph.GetAdjacentEdgeRange(turn_node))
|
||||
{
|
||||
BOOST_ASSERT(onto_edge != SPECIAL_EDGEID);
|
||||
const NodeID to_node = node_based_graph.GetTarget(onto_edge);
|
||||
|
||||
bool turn_is_valid =
|
||||
// reverse edges are never valid turns because the resulting turn would look like this:
|
||||
// from_node --via_edge--> turn_node <--onto_edge-- to_node
|
||||
// however we need this for capture intersection shape for incoming one-ways
|
||||
!node_based_graph.GetEdgeData(onto_edge).reversed &&
|
||||
// we are not turning over a barrier
|
||||
(!is_barrier_node || from_node == to_node) &&
|
||||
// We are at an only_-restriction but not at the right turn.
|
||||
(only_restriction_to_node == SPECIAL_NODEID || to_node == only_restriction_to_node) &&
|
||||
// the turn is not restricted
|
||||
!restriction_map.CheckIfTurnIsRestricted(from_node, turn_node, to_node);
|
||||
|
||||
auto angle = 0.;
|
||||
if (from_node == to_node)
|
||||
{
|
||||
if (turn_is_valid && !is_barrier_node)
|
||||
{
|
||||
// we only add u-turns for dead-end streets.
|
||||
if (node_based_graph.GetOutDegree(turn_node) > 1)
|
||||
{
|
||||
auto number_of_emmiting_bidirectional_edges = 0;
|
||||
for (auto edge : node_based_graph.GetAdjacentEdgeRange(turn_node))
|
||||
{
|
||||
auto target = node_based_graph.GetTarget(edge);
|
||||
auto reverse_edge = node_based_graph.FindEdge(target, turn_node);
|
||||
BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID);
|
||||
if (!node_based_graph.GetEdgeData(reverse_edge).reversed)
|
||||
{
|
||||
++number_of_emmiting_bidirectional_edges;
|
||||
}
|
||||
}
|
||||
// is a dead-end
|
||||
turn_is_valid = number_of_emmiting_bidirectional_edges <= 1;
|
||||
}
|
||||
}
|
||||
has_uturn_edge = true;
|
||||
BOOST_ASSERT(angle >= 0. && angle < std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
else
|
||||
{
|
||||
// unpack first node of second segment if packed
|
||||
const auto first_coordinate = getRepresentativeCoordinate(
|
||||
from_node, turn_node, via_eid, INVERT, compressed_edge_container, node_info_list);
|
||||
const auto third_coordinate = getRepresentativeCoordinate(
|
||||
turn_node, to_node, onto_edge, !INVERT, compressed_edge_container, node_info_list);
|
||||
angle = util::coordinate_calculation::computeAngle(
|
||||
first_coordinate, node_info_list[turn_node], third_coordinate);
|
||||
if (angle < std::numeric_limits<double>::epsilon())
|
||||
has_uturn_edge = true;
|
||||
}
|
||||
|
||||
intersection.push_back(ConnectedRoad(
|
||||
TurnOperation{onto_edge, angle, {TurnType::Invalid, DirectionModifier::UTurn}},
|
||||
turn_is_valid));
|
||||
}
|
||||
|
||||
// We hit the case of a street leading into nothing-ness. Since the code here assumes that this
|
||||
// will
|
||||
// never happen we add an artificial invalid uturn in this case.
|
||||
if (!has_uturn_edge)
|
||||
{
|
||||
intersection.push_back(
|
||||
{TurnOperation{via_eid, 0., {TurnType::Invalid, DirectionModifier::UTurn}}, false});
|
||||
}
|
||||
|
||||
const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second) {
|
||||
return first.turn.angle < second.turn.angle;
|
||||
};
|
||||
std::sort(std::begin(intersection), std::end(intersection), ByAngle);
|
||||
|
||||
BOOST_ASSERT(intersection[0].turn.angle >= 0. &&
|
||||
intersection[0].turn.angle < std::numeric_limits<double>::epsilon());
|
||||
|
||||
return mergeSegregatedRoads(std::move(intersection));
|
||||
}
|
||||
|
||||
/*
|
||||
* Segregated Roads often merge onto a single intersection.
|
||||
* While technically representing different roads, they are
|
||||
* often looked at as a single road.
|
||||
* Due to the merging, turn Angles seem off, wenn we compute them from the
|
||||
* initial positions.
|
||||
*
|
||||
* b<b<b<b(1)<b<b<b
|
||||
* aaaaa-b
|
||||
* b>b>b>b(2)>b>b>b
|
||||
*
|
||||
* Would be seen as a slight turn going fro a to (2). A Sharp turn going from
|
||||
* (1) to (2).
|
||||
*
|
||||
* In cases like these, we megre this segregated roads into a single road to
|
||||
* end up with a case like:
|
||||
*
|
||||
* aaaaa-bbbbbb
|
||||
*
|
||||
* for the turn representation.
|
||||
* Anything containing the first u-turn in a merge affects all other angles
|
||||
* and is handled separately from all others.
|
||||
*/
|
||||
Intersection IntersectionGenerator::mergeSegregatedRoads(Intersection intersection) const
|
||||
{
|
||||
const auto getRight = [&](std::size_t index) {
|
||||
return (index + intersection.size() - 1) % intersection.size();
|
||||
};
|
||||
|
||||
const auto mergable = [&](std::size_t first, std::size_t second) -> bool {
|
||||
const auto &first_data = node_based_graph.GetEdgeData(intersection[first].turn.eid);
|
||||
const auto &second_data = node_based_graph.GetEdgeData(intersection[second].turn.eid);
|
||||
|
||||
return first_data.name_id != INVALID_NAME_ID && first_data.name_id == second_data.name_id &&
|
||||
!first_data.roundabout && !second_data.roundabout &&
|
||||
first_data.travel_mode == second_data.travel_mode &&
|
||||
first_data.road_classification == second_data.road_classification &&
|
||||
// compatible threshold
|
||||
angularDeviation(intersection[first].turn.angle, intersection[second].turn.angle) <
|
||||
60 &&
|
||||
first_data.reversed != second_data.reversed;
|
||||
};
|
||||
|
||||
const auto merge = [](const ConnectedRoad &first,
|
||||
const ConnectedRoad &second) -> ConnectedRoad {
|
||||
if (!first.entry_allowed)
|
||||
{
|
||||
ConnectedRoad result = second;
|
||||
result.turn.angle = (first.turn.angle + second.turn.angle) / 2;
|
||||
if (first.turn.angle - second.turn.angle > 180)
|
||||
result.turn.angle += 180;
|
||||
if (result.turn.angle > 360)
|
||||
result.turn.angle -= 360;
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(!second.entry_allowed);
|
||||
ConnectedRoad result = first;
|
||||
result.turn.angle = (first.turn.angle + second.turn.angle) / 2;
|
||||
|
||||
if (first.turn.angle - second.turn.angle > 180)
|
||||
result.turn.angle += 180;
|
||||
if (result.turn.angle > 360)
|
||||
result.turn.angle -= 360;
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
if (intersection.size() == 1)
|
||||
return intersection;
|
||||
|
||||
// check for merges including the basic u-turn
|
||||
// these result in an adjustment of all other angles
|
||||
if (mergable(0, intersection.size() - 1))
|
||||
{
|
||||
const double correction_factor =
|
||||
(360 - intersection[intersection.size() - 1].turn.angle) / 2;
|
||||
for (std::size_t i = 1; i + 1 < intersection.size(); ++i)
|
||||
intersection[i].turn.angle += correction_factor;
|
||||
intersection[0] = merge(intersection.front(), intersection.back());
|
||||
intersection[0].turn.angle = 0;
|
||||
intersection.pop_back();
|
||||
}
|
||||
|
||||
else if (mergable(0, 1))
|
||||
{
|
||||
const double correction_factor = (intersection[1].turn.angle) / 2;
|
||||
for (std::size_t i = 2; i < intersection.size(); ++i)
|
||||
intersection[i].turn.angle += correction_factor;
|
||||
intersection[0] = merge(intersection[0], intersection[1]);
|
||||
intersection[0].turn.angle = 0;
|
||||
intersection.erase(intersection.begin() + 1);
|
||||
}
|
||||
|
||||
// a merge including the first u-turn requres an adjustment of the turn angles
|
||||
// therefore these are handled prior to this step
|
||||
for (std::size_t index = 2; index < intersection.size(); ++index)
|
||||
{
|
||||
if (mergable(index, getRight(index)))
|
||||
{
|
||||
intersection[getRight(index)] =
|
||||
merge(intersection[getRight(index)], intersection[index]);
|
||||
intersection.erase(intersection.begin() + index);
|
||||
--index;
|
||||
}
|
||||
}
|
||||
|
||||
const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second) {
|
||||
return first.turn.angle < second.turn.angle;
|
||||
};
|
||||
std::sort(std::begin(intersection), std::end(intersection), ByAngle);
|
||||
return intersection;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
307
src/extractor/guidance/intersection_handler.cpp
Normal file
307
src/extractor/guidance/intersection_handler.cpp
Normal file
@ -0,0 +1,307 @@
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/intersection_handler.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
inline bool requiresAnnouncement(const EdgeData &from, const EdgeData &to)
|
||||
{
|
||||
return !from.IsCompatibleTo(to);
|
||||
}
|
||||
}
|
||||
|
||||
IntersectionHandler::IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const util::NameTable &name_table)
|
||||
: node_based_graph(node_based_graph), node_info_list(node_info_list), name_table(name_table)
|
||||
{
|
||||
}
|
||||
|
||||
IntersectionHandler::~IntersectionHandler() {}
|
||||
|
||||
std::size_t IntersectionHandler::countValid(const Intersection &intersection) const
|
||||
{
|
||||
return std::count_if(intersection.begin(), intersection.end(),
|
||||
[](const ConnectedRoad &road) { return road.entry_allowed; });
|
||||
}
|
||||
|
||||
TurnType IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
|
||||
const ConnectedRoad &road) const
|
||||
{
|
||||
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||
|
||||
bool on_ramp = isRampClass(in_data.road_classification.road_class);
|
||||
|
||||
bool onto_ramp = isRampClass(out_data.road_classification.road_class);
|
||||
|
||||
if (!on_ramp && onto_ramp)
|
||||
return TurnType::Ramp;
|
||||
|
||||
if (in_data.name_id == out_data.name_id && in_data.name_id != INVALID_NAME_ID)
|
||||
{
|
||||
return TurnType::Continue;
|
||||
}
|
||||
|
||||
return TurnType::Turn;
|
||||
}
|
||||
|
||||
TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t num_roads,
|
||||
const EdgeID via_edge,
|
||||
const bool through_street,
|
||||
const ConnectedRoad &road) const
|
||||
{
|
||||
const auto type = findBasicTurnType(via_edge, road);
|
||||
// handle travel modes:
|
||||
const auto in_mode = node_based_graph.GetEdgeData(via_edge).travel_mode;
|
||||
const auto out_mode = node_based_graph.GetEdgeData(road.turn.eid).travel_mode;
|
||||
if (type == TurnType::Ramp)
|
||||
{
|
||||
return {TurnType::Ramp, getTurnDirection(road.turn.angle)};
|
||||
}
|
||||
|
||||
if (angularDeviation(road.turn.angle, 0) < 0.01)
|
||||
{
|
||||
return {TurnType::Turn, DirectionModifier::UTurn};
|
||||
}
|
||||
if (type == TurnType::Turn)
|
||||
{
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||
if (in_data.name_id != out_data.name_id &&
|
||||
requiresNameAnnounced(name_table.GetNameForID(in_data.name_id),
|
||||
name_table.GetNameForID(out_data.name_id)))
|
||||
{
|
||||
// obvious turn onto a through street is a merge
|
||||
if (through_street)
|
||||
{
|
||||
return {TurnType::Merge, road.turn.angle > STRAIGHT_ANGLE
|
||||
? DirectionModifier::SlightRight
|
||||
: DirectionModifier::SlightLeft};
|
||||
}
|
||||
else
|
||||
{
|
||||
return {TurnType::NewName, getTurnDirection(road.turn.angle)};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (in_mode == out_mode)
|
||||
return {TurnType::Suppressed, getTurnDirection(road.turn.angle)};
|
||||
else
|
||||
return {TurnType::Notification, getTurnDirection(road.turn.angle)};
|
||||
}
|
||||
}
|
||||
BOOST_ASSERT(type == TurnType::Continue);
|
||||
if (in_mode != out_mode)
|
||||
{
|
||||
return {TurnType::Notification, getTurnDirection(road.turn.angle)};
|
||||
}
|
||||
if (num_roads > 2)
|
||||
{
|
||||
return {TurnType::Suppressed, getTurnDirection(road.turn.angle)};
|
||||
}
|
||||
else
|
||||
{
|
||||
return {TurnType::NoTurn, getTurnDirection(road.turn.angle)};
|
||||
}
|
||||
}
|
||||
|
||||
void IntersectionHandler::assignFork(const EdgeID via_edge,
|
||||
ConnectedRoad &left,
|
||||
ConnectedRoad &right) const
|
||||
{
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
||||
const bool low_priority_left = isLowPriorityRoadClass(
|
||||
node_based_graph.GetEdgeData(left.turn.eid).road_classification.road_class);
|
||||
const bool low_priority_right = isLowPriorityRoadClass(
|
||||
node_based_graph.GetEdgeData(right.turn.eid).road_classification.road_class);
|
||||
if ((angularDeviation(left.turn.angle, STRAIGHT_ANGLE) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
|
||||
angularDeviation(right.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE))
|
||||
{
|
||||
// left side is actually straight
|
||||
const auto &out_data = node_based_graph.GetEdgeData(left.turn.eid);
|
||||
if (detail::requiresAnnouncement(in_data, out_data))
|
||||
{
|
||||
if (low_priority_right && !low_priority_left)
|
||||
{
|
||||
left.turn.instruction = getInstructionForObvious(3, via_edge, false, left);
|
||||
right.turn.instruction = {findBasicTurnType(via_edge, right),
|
||||
DirectionModifier::SlightRight};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (low_priority_left && !low_priority_right)
|
||||
{
|
||||
left.turn.instruction = {findBasicTurnType(via_edge, left),
|
||||
DirectionModifier::SlightLeft};
|
||||
right.turn.instruction = {findBasicTurnType(via_edge, right),
|
||||
DirectionModifier::SlightRight};
|
||||
}
|
||||
else
|
||||
{
|
||||
left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
|
||||
right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight};
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
left.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight};
|
||||
right.turn.instruction = {findBasicTurnType(via_edge, right),
|
||||
DirectionModifier::SlightRight};
|
||||
}
|
||||
}
|
||||
else if (angularDeviation(right.turn.angle, STRAIGHT_ANGLE) <
|
||||
MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
|
||||
angularDeviation(left.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE)
|
||||
{
|
||||
// right side is actually straight
|
||||
const auto &out_data = node_based_graph.GetEdgeData(right.turn.eid);
|
||||
if (angularDeviation(right.turn.angle, STRAIGHT_ANGLE) <
|
||||
MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
|
||||
angularDeviation(left.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE)
|
||||
{
|
||||
if (detail::requiresAnnouncement(in_data, out_data))
|
||||
{
|
||||
if (low_priority_left && !low_priority_right)
|
||||
{
|
||||
left.turn.instruction = {findBasicTurnType(via_edge, left),
|
||||
DirectionModifier::SlightLeft};
|
||||
right.turn.instruction = getInstructionForObvious(3, via_edge, false, right);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (low_priority_right && !low_priority_left)
|
||||
{
|
||||
left.turn.instruction = {findBasicTurnType(via_edge, left),
|
||||
DirectionModifier::SlightLeft};
|
||||
right.turn.instruction = {findBasicTurnType(via_edge, right),
|
||||
DirectionModifier::SlightRight};
|
||||
}
|
||||
else
|
||||
{
|
||||
right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight};
|
||||
left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
right.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight};
|
||||
left.turn.instruction = {findBasicTurnType(via_edge, left),
|
||||
DirectionModifier::SlightLeft};
|
||||
}
|
||||
}
|
||||
}
|
||||
// left side of fork
|
||||
if (low_priority_right && !low_priority_left)
|
||||
left.turn.instruction = {TurnType::Suppressed, DirectionModifier::SlightLeft};
|
||||
else
|
||||
{
|
||||
if (low_priority_left && !low_priority_right)
|
||||
left.turn.instruction = {TurnType::Turn, DirectionModifier::SlightLeft};
|
||||
else
|
||||
left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
|
||||
}
|
||||
|
||||
// right side of fork
|
||||
if (low_priority_left && !low_priority_right)
|
||||
right.turn.instruction = {TurnType::Suppressed, DirectionModifier::SlightLeft};
|
||||
else
|
||||
{
|
||||
if (low_priority_right && !low_priority_left)
|
||||
right.turn.instruction = {TurnType::Turn, DirectionModifier::SlightRight};
|
||||
else
|
||||
right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight};
|
||||
}
|
||||
}
|
||||
|
||||
void IntersectionHandler::assignFork(const EdgeID via_edge,
|
||||
ConnectedRoad &left,
|
||||
ConnectedRoad ¢er,
|
||||
ConnectedRoad &right) const
|
||||
{
|
||||
// TODO handle low priority road classes in a reasonable way
|
||||
if (left.entry_allowed && center.entry_allowed && right.entry_allowed)
|
||||
{
|
||||
left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
|
||||
if (angularDeviation(center.turn.angle, 180) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
|
||||
{
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
||||
const auto &out_data = node_based_graph.GetEdgeData(center.turn.eid);
|
||||
if (detail::requiresAnnouncement(in_data, out_data))
|
||||
{
|
||||
center.turn.instruction = {TurnType::Fork, DirectionModifier::Straight};
|
||||
}
|
||||
else
|
||||
{
|
||||
center.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
center.turn.instruction = {TurnType::Fork, DirectionModifier::Straight};
|
||||
}
|
||||
right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight};
|
||||
}
|
||||
else if (left.entry_allowed)
|
||||
{
|
||||
if (right.entry_allowed)
|
||||
assignFork(via_edge, left, right);
|
||||
else if (center.entry_allowed)
|
||||
assignFork(via_edge, left, center);
|
||||
else
|
||||
left.turn.instruction = {findBasicTurnType(via_edge, left),
|
||||
getTurnDirection(left.turn.angle)};
|
||||
}
|
||||
else if (right.entry_allowed)
|
||||
{
|
||||
if (center.entry_allowed)
|
||||
assignFork(via_edge, center, right);
|
||||
else
|
||||
right.turn.instruction = {findBasicTurnType(via_edge, right),
|
||||
getTurnDirection(right.turn.angle)};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (center.entry_allowed)
|
||||
center.turn.instruction = {findBasicTurnType(via_edge, center),
|
||||
getTurnDirection(center.turn.angle)};
|
||||
}
|
||||
}
|
||||
|
||||
bool IntersectionHandler::isThroughStreet(const std::size_t index,
|
||||
const Intersection &intersection) const
|
||||
{
|
||||
if (node_based_graph.GetEdgeData(intersection[index].turn.eid).name_id == INVALID_NAME_ID)
|
||||
return false;
|
||||
for (const auto &road : intersection)
|
||||
{
|
||||
// a through street cannot start at our own position
|
||||
if (road.turn.angle < std::numeric_limits<double>::epsilon())
|
||||
continue;
|
||||
if (angularDeviation(road.turn.angle, intersection[index].turn.angle) >
|
||||
(STRAIGHT_ANGLE - NARROW_TURN_ANGLE) &&
|
||||
node_based_graph.GetEdgeData(road.turn.eid).name_id ==
|
||||
node_based_graph.GetEdgeData(intersection[index].turn.eid).name_id)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
524
src/extractor/guidance/motorway_handler.cpp
Normal file
524
src/extractor/guidance/motorway_handler.cpp
Normal file
@ -0,0 +1,524 @@
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/motorway_handler.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
|
||||
#include "util/simple_logger.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
inline bool isMotorwayClass(const FunctionalRoadClass road_class)
|
||||
{
|
||||
return road_class == FunctionalRoadClass::MOTORWAY || road_class == FunctionalRoadClass::TRUNK;
|
||||
}
|
||||
|
||||
inline bool isMotorwayClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_based_graph)
|
||||
{
|
||||
return isMotorwayClass(node_based_graph.GetEdgeData(eid).road_classification.road_class);
|
||||
}
|
||||
inline FunctionalRoadClass roadClass(const ConnectedRoad &road,
|
||||
const util::NodeBasedDynamicGraph &graph)
|
||||
{
|
||||
return graph.GetEdgeData(road.turn.eid).road_classification.road_class;
|
||||
}
|
||||
|
||||
inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_based_graph)
|
||||
{
|
||||
return isRampClass(node_based_graph.GetEdgeData(eid).road_classification.road_class);
|
||||
}
|
||||
}
|
||||
|
||||
MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const util::NameTable &name_table)
|
||||
: IntersectionHandler(node_based_graph, node_info_list, name_table)
|
||||
{
|
||||
}
|
||||
|
||||
MotorwayHandler::~MotorwayHandler() {}
|
||||
|
||||
bool MotorwayHandler::canProcess(const NodeID,
|
||||
const EdgeID via_eid,
|
||||
const Intersection &intersection) const
|
||||
{
|
||||
bool has_motorway = false;
|
||||
bool has_normal_roads = false;
|
||||
|
||||
for (const auto &road : intersection)
|
||||
{
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||
// not merging or forking?
|
||||
if (road.entry_allowed && angularDeviation(road.turn.angle, STRAIGHT_ANGLE) > 60)
|
||||
return false;
|
||||
else if (out_data.road_classification.road_class == FunctionalRoadClass::MOTORWAY ||
|
||||
out_data.road_classification.road_class == FunctionalRoadClass::TRUNK)
|
||||
{
|
||||
if (road.entry_allowed)
|
||||
has_motorway = true;
|
||||
}
|
||||
else if (!isRampClass(out_data.road_classification.road_class))
|
||||
has_normal_roads = true;
|
||||
}
|
||||
|
||||
if (has_normal_roads)
|
||||
return false;
|
||||
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_eid);
|
||||
return has_motorway ||
|
||||
in_data.road_classification.road_class == FunctionalRoadClass::MOTORWAY ||
|
||||
in_data.road_classification.road_class == FunctionalRoadClass::TRUNK;
|
||||
}
|
||||
|
||||
Intersection MotorwayHandler::
|
||||
operator()(const NodeID, const EdgeID via_eid, Intersection intersection) const
|
||||
{
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_eid);
|
||||
|
||||
// coming from motorway
|
||||
if (detail::isMotorwayClass(in_data.road_classification.road_class))
|
||||
{
|
||||
return fromMotorway(via_eid, std::move(intersection));
|
||||
}
|
||||
else // coming from a ramp
|
||||
{
|
||||
return fromRamp(via_eid, std::move(intersection));
|
||||
// ramp merging straight onto motorway
|
||||
}
|
||||
}
|
||||
|
||||
Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection intersection) const
|
||||
{
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_eid);
|
||||
BOOST_ASSERT(detail::isMotorwayClass(in_data.road_classification.road_class));
|
||||
|
||||
const auto countExitingMotorways = [this](const Intersection &intersection) {
|
||||
unsigned count = 0;
|
||||
for (const auto &road : intersection)
|
||||
{
|
||||
if (road.entry_allowed && detail::isMotorwayClass(road.turn.eid, node_based_graph))
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
};
|
||||
|
||||
// find the angle that continues on our current highway
|
||||
const auto getContinueAngle = [this, in_data](const Intersection &intersection) {
|
||||
for (const auto &road : intersection)
|
||||
{
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||
if (road.turn.angle != 0 && in_data.name_id == out_data.name_id &&
|
||||
in_data.name_id != INVALID_NAME_ID &&
|
||||
detail::isMotorwayClass(out_data.road_classification.road_class))
|
||||
return road.turn.angle;
|
||||
}
|
||||
return intersection[0].turn.angle;
|
||||
};
|
||||
|
||||
const auto getMostLikelyContinue = [this, in_data](const Intersection &intersection) {
|
||||
double angle = intersection[0].turn.angle;
|
||||
double best = 180;
|
||||
for (const auto &road : intersection)
|
||||
{
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||
if (detail::isMotorwayClass(out_data.road_classification.road_class) &&
|
||||
angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < best)
|
||||
{
|
||||
best = angularDeviation(road.turn.angle, STRAIGHT_ANGLE);
|
||||
angle = road.turn.angle;
|
||||
}
|
||||
}
|
||||
return angle;
|
||||
};
|
||||
|
||||
const auto findBestContinue = [&]() {
|
||||
const double continue_angle = getContinueAngle(intersection);
|
||||
if (continue_angle != intersection[0].turn.angle)
|
||||
return continue_angle;
|
||||
else
|
||||
return getMostLikelyContinue(intersection);
|
||||
};
|
||||
|
||||
// find continue angle
|
||||
const double continue_angle = findBestContinue();
|
||||
// highway does not continue and has no obvious choice
|
||||
if (continue_angle == intersection[0].turn.angle)
|
||||
{
|
||||
if (intersection.size() == 2)
|
||||
{
|
||||
// do not announce ramps at the end of a highway
|
||||
intersection[1].turn.instruction = {TurnType::NoTurn,
|
||||
getTurnDirection(intersection[1].turn.angle)};
|
||||
}
|
||||
else if (intersection.size() == 3)
|
||||
{
|
||||
// splitting ramp at the end of a highway
|
||||
if (intersection[1].entry_allowed && intersection[2].entry_allowed)
|
||||
{
|
||||
assignFork(via_eid, intersection[2], intersection[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ending in a passing ramp
|
||||
if (intersection[1].entry_allowed)
|
||||
intersection[1].turn.instruction = {
|
||||
TurnType::NoTurn, getTurnDirection(intersection[1].turn.angle)};
|
||||
else
|
||||
intersection[2].turn.instruction = {
|
||||
TurnType::NoTurn, getTurnDirection(intersection[2].turn.angle)};
|
||||
}
|
||||
}
|
||||
else if (intersection.size() == 4 &&
|
||||
detail::roadClass(intersection[1], node_based_graph) ==
|
||||
detail::roadClass(intersection[2], node_based_graph) &&
|
||||
detail::roadClass(intersection[2], node_based_graph) ==
|
||||
detail::roadClass(intersection[3], node_based_graph))
|
||||
{
|
||||
// tripple fork at the end
|
||||
assignFork(via_eid, intersection[3], intersection[2], intersection[1]);
|
||||
}
|
||||
|
||||
else if (countValid(intersection) > 0) // check whether turns exist at all
|
||||
{
|
||||
// FALLBACK, this should hopefully never be reached
|
||||
util::SimpleLogger().Write(logDEBUG)
|
||||
<< "Fallback reached from motorway, no continue angle, " << intersection.size()
|
||||
<< " roads, " << countValid(intersection) << " valid ones.";
|
||||
return fallback(std::move(intersection));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned exiting_motorways = countExitingMotorways(intersection);
|
||||
|
||||
if (exiting_motorways == 0)
|
||||
{
|
||||
// Ending in Ramp
|
||||
for (auto &road : intersection)
|
||||
{
|
||||
if (road.entry_allowed)
|
||||
{
|
||||
BOOST_ASSERT(detail::isRampClass(road.turn.eid, node_based_graph));
|
||||
road.turn.instruction =
|
||||
TurnInstruction::SUPPRESSED(getTurnDirection(road.turn.angle));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (exiting_motorways == 1)
|
||||
{
|
||||
// normal motorway passing some ramps or mering onto another motorway
|
||||
if (intersection.size() == 2)
|
||||
{
|
||||
BOOST_ASSERT(!detail::isRampClass(intersection[1].turn.eid, node_based_graph));
|
||||
|
||||
intersection[1].turn.instruction =
|
||||
getInstructionForObvious(intersection.size(), via_eid,
|
||||
isThroughStreet(1,intersection), intersection[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal Highway exit or merge
|
||||
for (auto &road : intersection)
|
||||
{
|
||||
// ignore invalid uturns/other
|
||||
if (!road.entry_allowed)
|
||||
continue;
|
||||
|
||||
if (road.turn.angle == continue_angle)
|
||||
{
|
||||
road.turn.instruction = getInstructionForObvious(
|
||||
intersection.size(), via_eid, isThroughStreet(1,intersection), road);
|
||||
}
|
||||
else if (road.turn.angle < continue_angle)
|
||||
{
|
||||
road.turn.instruction = {
|
||||
detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::Ramp
|
||||
: TurnType::Turn,
|
||||
(road.turn.angle < 145) ? DirectionModifier::Right
|
||||
: DirectionModifier::SlightRight};
|
||||
}
|
||||
else if (road.turn.angle > continue_angle)
|
||||
{
|
||||
road.turn.instruction = {
|
||||
detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::Ramp
|
||||
: TurnType::Turn,
|
||||
(road.turn.angle > 215) ? DirectionModifier::Left
|
||||
: DirectionModifier::SlightLeft};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// handle motorway forks
|
||||
else if (exiting_motorways > 1)
|
||||
{
|
||||
if (exiting_motorways == 2 && intersection.size() == 2)
|
||||
{
|
||||
intersection[1].turn.instruction =
|
||||
getInstructionForObvious(intersection.size(), via_eid,
|
||||
isThroughStreet(1,intersection), intersection[1]);
|
||||
util::SimpleLogger().Write(logDEBUG) << "Disabled U-Turn on a freeway";
|
||||
intersection[0].entry_allowed = false; // UTURN on the freeway
|
||||
}
|
||||
else if (exiting_motorways == 2)
|
||||
{
|
||||
// standard fork
|
||||
std::size_t first_valid = std::numeric_limits<std::size_t>::max(),
|
||||
second_valid = std::numeric_limits<std::size_t>::max();
|
||||
for (std::size_t i = 0; i < intersection.size(); ++i)
|
||||
{
|
||||
if (intersection[i].entry_allowed &&
|
||||
detail::isMotorwayClass(intersection[i].turn.eid, node_based_graph))
|
||||
{
|
||||
if (first_valid < intersection.size())
|
||||
{
|
||||
second_valid = i;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
first_valid = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
assignFork(via_eid, intersection[second_valid], intersection[first_valid]);
|
||||
}
|
||||
else if (exiting_motorways == 3)
|
||||
{
|
||||
// triple fork
|
||||
std::size_t first_valid = std::numeric_limits<std::size_t>::max(),
|
||||
second_valid = std::numeric_limits<std::size_t>::max(),
|
||||
third_valid = std::numeric_limits<std::size_t>::max();
|
||||
for (std::size_t i = 0; i < intersection.size(); ++i)
|
||||
{
|
||||
if (intersection[i].entry_allowed &&
|
||||
detail::isMotorwayClass(intersection[i].turn.eid, node_based_graph))
|
||||
{
|
||||
if (second_valid < intersection.size())
|
||||
{
|
||||
third_valid = i;
|
||||
break;
|
||||
}
|
||||
else if (first_valid < intersection.size())
|
||||
{
|
||||
second_valid = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
first_valid = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
assignFork(via_eid, intersection[third_valid], intersection[second_valid],
|
||||
intersection[first_valid]);
|
||||
}
|
||||
else
|
||||
{
|
||||
util::SimpleLogger().Write(logDEBUG) << "Found motorway junction with more than "
|
||||
"2 exiting motorways or additional ramps";
|
||||
return fallback(std::move(intersection));
|
||||
}
|
||||
} // done for more than one highway exit
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
|
||||
Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection intersection) const
|
||||
{
|
||||
auto num_valid_turns = countValid(intersection);
|
||||
// ramp straight into a motorway/ramp
|
||||
if (intersection.size() == 2 && num_valid_turns == 1)
|
||||
{
|
||||
BOOST_ASSERT(!intersection[0].entry_allowed);
|
||||
BOOST_ASSERT(detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph));
|
||||
|
||||
intersection[1].turn.instruction = getInstructionForObvious(
|
||||
intersection.size(), via_eid, isThroughStreet(1,intersection), intersection[1]);
|
||||
}
|
||||
else if (intersection.size() == 3)
|
||||
{
|
||||
// merging onto a passing highway / or two ramps merging onto the same highway
|
||||
if (num_valid_turns == 1)
|
||||
{
|
||||
BOOST_ASSERT(!intersection[0].entry_allowed);
|
||||
// check order of highways
|
||||
// 4
|
||||
// 5 3
|
||||
//
|
||||
// 6 2
|
||||
//
|
||||
// 7 1
|
||||
// 0
|
||||
if (intersection[1].entry_allowed)
|
||||
{
|
||||
if (detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph) &&
|
||||
node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id !=
|
||||
INVALID_NAME_ID &&
|
||||
node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id ==
|
||||
node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id)
|
||||
{
|
||||
// circular order indicates a merge to the left (0-3 onto 4
|
||||
if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) <
|
||||
NARROW_TURN_ANGLE)
|
||||
intersection[1].turn.instruction = {TurnType::Merge,
|
||||
DirectionModifier::SlightLeft};
|
||||
else // fallback
|
||||
intersection[1].turn.instruction = {
|
||||
TurnType::Merge, getTurnDirection(intersection[1].turn.angle)};
|
||||
}
|
||||
else // passing by the end of a motorway
|
||||
{
|
||||
intersection[1].turn.instruction =
|
||||
getInstructionForObvious(intersection.size(), via_eid,
|
||||
isThroughStreet(1,intersection), intersection[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(intersection[2].entry_allowed);
|
||||
if (detail::isMotorwayClass(intersection[2].turn.eid, node_based_graph) &&
|
||||
node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id !=
|
||||
INVALID_NAME_ID &&
|
||||
node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id ==
|
||||
node_based_graph.GetEdgeData(intersection[0].turn.eid).name_id)
|
||||
{
|
||||
// circular order (5-0) onto 4
|
||||
if (angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) <
|
||||
NARROW_TURN_ANGLE)
|
||||
intersection[2].turn.instruction = {TurnType::Merge,
|
||||
DirectionModifier::SlightRight};
|
||||
else // fallback
|
||||
intersection[2].turn.instruction = {
|
||||
TurnType::Merge, getTurnDirection(intersection[2].turn.angle)};
|
||||
}
|
||||
else // passing the end of a highway
|
||||
{
|
||||
intersection[2].turn.instruction =
|
||||
getInstructionForObvious(intersection.size(), via_eid,
|
||||
isThroughStreet(2,intersection), intersection[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(num_valid_turns == 2);
|
||||
// UTurn on ramps is not possible
|
||||
BOOST_ASSERT(!intersection[0].entry_allowed);
|
||||
BOOST_ASSERT(intersection[1].entry_allowed);
|
||||
BOOST_ASSERT(intersection[2].entry_allowed);
|
||||
// two motorways starting at end of ramp (fork)
|
||||
// M M
|
||||
// \ /
|
||||
// |
|
||||
// R
|
||||
if (detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph) &&
|
||||
detail::isMotorwayClass(intersection[2].turn.eid, node_based_graph))
|
||||
{
|
||||
assignFork(via_eid, intersection[2], intersection[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// continued ramp passing motorway entry
|
||||
// M R
|
||||
// M R
|
||||
// | /
|
||||
// R
|
||||
if (detail::isMotorwayClass(node_based_graph.GetEdgeData(intersection[1].turn.eid)
|
||||
.road_classification.road_class))
|
||||
{
|
||||
intersection[1].turn.instruction = {TurnType::Turn,
|
||||
DirectionModifier::SlightRight};
|
||||
intersection[2].turn.instruction = {TurnType::Continue,
|
||||
DirectionModifier::SlightLeft};
|
||||
}
|
||||
else
|
||||
{
|
||||
assignFork(via_eid, intersection[2], intersection[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// On - Off Ramp on passing Motorway, Ramp onto Fork(?)
|
||||
else if (intersection.size() == 4)
|
||||
{
|
||||
bool passed_highway_entry = false;
|
||||
for (auto &road : intersection)
|
||||
{
|
||||
const auto &edge_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||
if (!road.entry_allowed &&
|
||||
detail::isMotorwayClass(edge_data.road_classification.road_class))
|
||||
{
|
||||
passed_highway_entry = true;
|
||||
}
|
||||
else if (detail::isMotorwayClass(edge_data.road_classification.road_class))
|
||||
{
|
||||
road.turn.instruction = {TurnType::Merge, passed_highway_entry
|
||||
? DirectionModifier::SlightRight
|
||||
: DirectionModifier::SlightLeft};
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(isRampClass(edge_data.road_classification.road_class));
|
||||
road.turn.instruction = {TurnType::Ramp, getTurnDirection(road.turn.angle)};
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // FALLBACK, hopefully this should never been reached
|
||||
util::SimpleLogger().Write(logDEBUG) << "Reached fallback on motorway ramp with "
|
||||
<< intersection.size() << " roads and "
|
||||
<< countValid(intersection) << " valid turns.";
|
||||
return fallback(std::move(intersection));
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
|
||||
Intersection MotorwayHandler::fallback(Intersection intersection) const
|
||||
{
|
||||
for (auto &road : intersection)
|
||||
{
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||
|
||||
util::SimpleLogger().Write(logDEBUG)
|
||||
<< "road: " << toString(road) << " Name: " << out_data.name_id
|
||||
<< " Road Class: " << (int)out_data.road_classification.road_class;
|
||||
|
||||
if (!road.entry_allowed)
|
||||
continue;
|
||||
|
||||
const auto type = detail::isMotorwayClass(out_data.road_classification.road_class)
|
||||
? TurnType::Merge
|
||||
: TurnType::Turn;
|
||||
|
||||
if (type == TurnType::Turn)
|
||||
{
|
||||
if (angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE)
|
||||
road.turn.instruction = {type, DirectionModifier::Straight};
|
||||
else
|
||||
{
|
||||
road.turn.instruction = {type, road.turn.angle > STRAIGHT_ANGLE
|
||||
? DirectionModifier::SlightLeft
|
||||
: DirectionModifier::SlightRight};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
road.turn.instruction = {type, road.turn.angle < STRAIGHT_ANGLE
|
||||
? DirectionModifier::SlightLeft
|
||||
: DirectionModifier::SlightRight};
|
||||
}
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
257
src/extractor/guidance/roundabout_handler.cpp
Normal file
257
src/extractor/guidance/roundabout_handler.cpp
Normal file
@ -0,0 +1,257 @@
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/roundabout_handler.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
|
||||
#include "util/simple_logger.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
RoundaboutHandler::RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const util::NameTable &name_table)
|
||||
: IntersectionHandler(node_based_graph, node_info_list, name_table)
|
||||
{
|
||||
}
|
||||
|
||||
RoundaboutHandler::~RoundaboutHandler() {}
|
||||
|
||||
bool RoundaboutHandler::canProcess(const NodeID from_nid,
|
||||
const EdgeID via_eid,
|
||||
const Intersection &intersection) const
|
||||
{
|
||||
const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
|
||||
return flags.on_roundabout || flags.can_enter;
|
||||
}
|
||||
|
||||
Intersection RoundaboutHandler::
|
||||
operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersection) const
|
||||
{
|
||||
const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
|
||||
const bool is_rotary = isRotary(node_based_graph.GetTarget(via_eid));
|
||||
// find the radius of the roundabout
|
||||
return handleRoundabouts(is_rotary, via_eid, flags.on_roundabout, flags.can_exit_separately,
|
||||
std::move(intersection));
|
||||
}
|
||||
|
||||
detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags(
|
||||
const NodeID from_nid, const EdgeID via_eid, const Intersection &intersection) const
|
||||
{
|
||||
const auto &in_edge_data = node_based_graph.GetEdgeData(via_eid);
|
||||
bool on_roundabout = in_edge_data.roundabout;
|
||||
bool can_enter_roundabout = false;
|
||||
bool can_exit_roundabout_separately = false;
|
||||
for (const auto &road : intersection)
|
||||
{
|
||||
const auto &edge_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||
// only check actual outgoing edges
|
||||
if (edge_data.reversed)
|
||||
continue;
|
||||
|
||||
if (edge_data.roundabout)
|
||||
{
|
||||
can_enter_roundabout = true;
|
||||
}
|
||||
// Exiting roundabouts at an entry point is technically a data-modelling issue.
|
||||
// This workaround handles cases in which an exit follows the entry.
|
||||
// To correctly represent perceived exits, we only count exits leading to a
|
||||
// separate vertex than the one we are coming from that are in the direction of
|
||||
// the roundabout.
|
||||
// The sorting of the angles represents a problem for left-sided driving, though.
|
||||
// FIXME in case of left-sided driving, we have to check whether we can enter the
|
||||
// roundabout later in the cycle, rather than prior.
|
||||
// FIXME requires consideration of crossing the roundabout
|
||||
else if (node_based_graph.GetTarget(road.turn.eid) != from_nid && !can_enter_roundabout)
|
||||
{
|
||||
can_exit_roundabout_separately = true;
|
||||
}
|
||||
}
|
||||
return {on_roundabout, can_enter_roundabout, can_exit_roundabout_separately};
|
||||
}
|
||||
|
||||
bool RoundaboutHandler::isRotary(const NodeID nid) const
|
||||
{
|
||||
// translate a node ID into its respective coordinate stored in the node_info_list
|
||||
const auto getCoordinate = [this](const NodeID node) {
|
||||
return util::Coordinate(node_info_list[node].lon, node_info_list[node].lat);
|
||||
};
|
||||
|
||||
unsigned roundabout_name_id = 0;
|
||||
std::unordered_set<unsigned> connected_names;
|
||||
|
||||
const auto getNextOnRoundabout = [this, &roundabout_name_id,
|
||||
&connected_names](const NodeID node) {
|
||||
EdgeID continue_edge = SPECIAL_EDGEID;
|
||||
for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const auto &edge_data = node_based_graph.GetEdgeData(edge);
|
||||
if (!edge_data.reversed && edge_data.roundabout)
|
||||
{
|
||||
if (SPECIAL_EDGEID != continue_edge)
|
||||
{
|
||||
// fork in roundabout
|
||||
return SPECIAL_EDGEID;
|
||||
}
|
||||
// roundabout does not keep its name
|
||||
if (roundabout_name_id != 0 && roundabout_name_id != edge_data.name_id &&
|
||||
requiresNameAnnounced(name_table.GetNameForID(roundabout_name_id),
|
||||
name_table.GetNameForID(edge_data.name_id)))
|
||||
{
|
||||
return SPECIAL_EDGEID;
|
||||
}
|
||||
|
||||
roundabout_name_id = edge_data.name_id;
|
||||
|
||||
continue_edge = edge;
|
||||
}
|
||||
else if (!edge_data.roundabout)
|
||||
{
|
||||
// remember all connected road names
|
||||
connected_names.insert(edge_data.name_id);
|
||||
}
|
||||
}
|
||||
return continue_edge;
|
||||
};
|
||||
// the roundabout radius has to be the same for all locations we look at it from
|
||||
// to guarantee this, we search the full roundabout for its vertices
|
||||
// and select the three smalles ids
|
||||
std::set<NodeID> roundabout_nodes; // needs to be sorted
|
||||
|
||||
// this value is a hard abort to deal with potential self-loops
|
||||
NodeID last_node = nid;
|
||||
while (0 == roundabout_nodes.count(last_node))
|
||||
{
|
||||
roundabout_nodes.insert(last_node);
|
||||
const auto eid = getNextOnRoundabout(last_node);
|
||||
|
||||
if (eid == SPECIAL_EDGEID)
|
||||
{
|
||||
util::SimpleLogger().Write(logDEBUG) << "Non-Loop Roundabout found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
last_node = node_based_graph.GetTarget(eid);
|
||||
}
|
||||
|
||||
// do we have a dedicated name for the rotary, if not its a roundabout
|
||||
// This function can theoretically fail if the roundabout name is partly
|
||||
// used with a reference and without. This will be fixed automatically
|
||||
// when we handle references separately or if the useage is more consistent
|
||||
if (roundabout_name_id == 0 || connected_names.count(roundabout_name_id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (roundabout_nodes.size() <= 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// calculate the radius of the roundabout/rotary. For two coordinates, we assume a minimal
|
||||
// circle
|
||||
// with both vertices right at the other side (so half their distance in meters).
|
||||
// Otherwise, we construct a circle through the first tree vertices.
|
||||
const auto getRadius = [&roundabout_nodes, &getCoordinate]() {
|
||||
auto node_itr = roundabout_nodes.begin();
|
||||
if (roundabout_nodes.size() == 2)
|
||||
{
|
||||
const auto first = getCoordinate(*node_itr++), second = getCoordinate(*node_itr++);
|
||||
return 0.5 * util::coordinate_calculation::haversineDistance(first, second);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto first = getCoordinate(*node_itr++), second = getCoordinate(*node_itr++),
|
||||
third = getCoordinate(*node_itr++);
|
||||
return util::coordinate_calculation::circleRadius(first, second, third);
|
||||
}
|
||||
};
|
||||
const double radius = getRadius();
|
||||
|
||||
// check whether the circle computation has gone wrong
|
||||
// The radius computation can result in infinity, if the three coordinates are non-distinct.
|
||||
// To stay on the safe side, we say its not a rotary
|
||||
if (std::isinf(radius))
|
||||
return false;
|
||||
|
||||
return radius > MAX_ROUNDABOUT_RADIUS;
|
||||
}
|
||||
|
||||
Intersection RoundaboutHandler::handleRoundabouts(const bool is_rotary,
|
||||
const EdgeID via_eid,
|
||||
const bool on_roundabout,
|
||||
const bool can_exit_roundabout_separately,
|
||||
Intersection intersection) const
|
||||
{
|
||||
// TODO requires differentiation between roundabouts and rotaries
|
||||
// detect via radius (get via circle through three vertices)
|
||||
NodeID node_v = node_based_graph.GetTarget(via_eid);
|
||||
if (on_roundabout)
|
||||
{
|
||||
// Shoule hopefully have only a single exit and continue
|
||||
// at least for cars. How about bikes?
|
||||
for (auto &road : intersection)
|
||||
{
|
||||
auto &turn = road.turn;
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||
if (out_data.roundabout)
|
||||
{
|
||||
// TODO can forks happen in roundabouts? E.g. required lane changes
|
||||
if (1 == node_based_graph.GetDirectedOutDegree(node_v))
|
||||
{
|
||||
// No turn possible.
|
||||
turn.instruction = TurnInstruction::NO_TURN();
|
||||
}
|
||||
else
|
||||
{
|
||||
turn.instruction =
|
||||
TurnInstruction::REMAIN_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
turn.instruction =
|
||||
TurnInstruction::EXIT_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
|
||||
}
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
else
|
||||
for (auto &road : intersection)
|
||||
{
|
||||
if (!road.entry_allowed)
|
||||
continue;
|
||||
auto &turn = road.turn;
|
||||
const auto &out_data = node_based_graph.GetEdgeData(turn.eid);
|
||||
if (out_data.roundabout)
|
||||
{
|
||||
turn.instruction =
|
||||
TurnInstruction::ENTER_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
|
||||
if (can_exit_roundabout_separately)
|
||||
{
|
||||
if (turn.instruction.type == TurnType::EnterRotary)
|
||||
turn.instruction.type = TurnType::EnterRotaryAtExit;
|
||||
if (turn.instruction.type == TurnType::EnterRoundabout)
|
||||
turn.instruction.type = TurnType::EnterRoundaboutAtExit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
turn.instruction = TurnInstruction::ENTER_AND_EXIT_ROUNDABOUT(
|
||||
is_rotary, getTurnDirection(turn.angle));
|
||||
}
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
File diff suppressed because it is too large
Load Diff
1171
src/extractor/guidance/turn_handler.cpp
Normal file
1171
src/extractor/guidance/turn_handler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user