Remove usage of IntersectionGenerator in EBGF
This commit is contained in:
parent
9c033ff461
commit
cc1a5ea78d
@ -884,7 +884,7 @@ Feature: Car - Turn restrictions
|
||||
| a | c | albic,dobe,dobe,albic,albic | depart,turn left,continue uturn,turn left,arrive |
|
||||
| a | e | albic,dobe,dobe | depart,turn left,arrive |
|
||||
|
||||
@no_turning @conditionals
|
||||
@no_turning @conditionals @restriction-way
|
||||
Scenario: Car - Conditional restriction with multiple time windows
|
||||
Given the extract extra arguments "--parse-conditional-restrictions"
|
||||
# 5pm Wed 02 May, 2017 GMT
|
||||
@ -1054,4 +1054,3 @@ Feature: Car - Turn restrictions
|
||||
| a | f | ab,be,ef,ef | depart,turn right,turn left,arrive | a,b,e,f |
|
||||
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
|
||||
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
||||
|
||||
|
@ -575,7 +575,7 @@ Feature: Car - Turn restrictions
|
||||
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
|
||||
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
||||
|
||||
@restriction @overlap
|
||||
@restriction-way @overlap
|
||||
Scenario: Car - prohibit turn
|
||||
Given the node map
|
||||
"""
|
||||
@ -710,7 +710,7 @@ Feature: Car - Turn restrictions
|
||||
| a | j | left,first,right,right |
|
||||
| f | e | right,third,left,left |
|
||||
|
||||
@restriction
|
||||
@restriction-way
|
||||
Scenario: Car - allow only turn
|
||||
Given the node map
|
||||
"""
|
||||
@ -742,7 +742,7 @@ Feature: Car - Turn restrictions
|
||||
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
|
||||
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
||||
|
||||
@restriction
|
||||
@restriction-way
|
||||
Scenario: Car - allow only turn
|
||||
Given the node map
|
||||
"""
|
||||
@ -771,7 +771,7 @@ Feature: Car - Turn restrictions
|
||||
| from | to | route |
|
||||
| a | d | ab,be,de,de |
|
||||
|
||||
@restriction
|
||||
@restriction-way
|
||||
Scenario: Multi Way restriction
|
||||
Given the node map
|
||||
"""
|
||||
@ -808,7 +808,7 @@ Feature: Car - Turn restrictions
|
||||
| from | to | route |
|
||||
| a | h | horiz,vert,horiz,horiz |
|
||||
|
||||
@restriction
|
||||
@restriction-way
|
||||
Scenario: Multi-Way overlapping single-way
|
||||
Given the node map
|
||||
"""
|
||||
@ -847,7 +847,7 @@ Feature: Car - Turn restrictions
|
||||
| h | d | hfb,abcd,abcd | depart,end of road right,arrive | h,b,d |
|
||||
|
||||
|
||||
@restriction
|
||||
@restriction-way
|
||||
Scenario: Car - prohibit turn, traffic lights
|
||||
Given the node map
|
||||
"""
|
||||
@ -890,7 +890,7 @@ Feature: Car - Turn restrictions
|
||||
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
||||
|
||||
|
||||
@restriction @overlap @geometry
|
||||
@restriction-way @overlap @geometry
|
||||
Scenario: Geometry
|
||||
Given the node map
|
||||
"""
|
||||
@ -925,7 +925,7 @@ Feature: Car - Turn restrictions
|
||||
| c | d | bc,bge,de,de |
|
||||
| c | f | bc,bge,de,de,ef,ef |
|
||||
|
||||
@restriction @overlap @geometry @traffic-signals
|
||||
@restriction-way @overlap @geometry @traffic-signals
|
||||
Scenario: Geometry
|
||||
Given the node map
|
||||
"""
|
||||
@ -967,7 +967,7 @@ Feature: Car - Turn restrictions
|
||||
| c | f | bc,bge,de,de,ef,ef |
|
||||
|
||||
# don't crash hard on invalid restrictions
|
||||
@restriction @invalid
|
||||
@restriction-way @invalid
|
||||
Scenario: Geometry
|
||||
Given the node map
|
||||
"""
|
||||
@ -999,7 +999,7 @@ Feature: Car - Turn restrictions
|
||||
| a | f | ab,be,ef,ef |
|
||||
|
||||
|
||||
@restriction @overlap @geometry
|
||||
@restriction @restriction-way @overlap @geometry
|
||||
Scenario: Duplicated restriction
|
||||
Given the node map
|
||||
"""
|
||||
|
@ -393,31 +393,44 @@ Feature: Merge Segregated Roads
|
||||
"""
|
||||
a
|
||||
|
|
||||
b
|
||||
b-----z
|
||||
/ \
|
||||
c h
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
d g
|
||||
\ /
|
||||
e
|
||||
|
|
||||
f
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | name | oneway |
|
||||
| ab | road | no |
|
||||
| ef | road | no |
|
||||
| bcde | road | yes |
|
||||
| eghb | road | yes |
|
||||
| nodes | name | oneway |
|
||||
| ab | road | no |
|
||||
| ef | road | no |
|
||||
| bcde | road | yes |
|
||||
| eghb | road | yes |
|
||||
| bz | cross | no |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
| restriction | bz | bcde | b | no_left_turn |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | turns | route |
|
||||
| a,f | depart,arrive | road,road |
|
||||
| c,f | depart,arrive | road,road |
|
||||
| f,a | depart,arrive | road,road |
|
||||
| g,a | depart,arrive | road,road |
|
||||
| waypoints | turns | route |
|
||||
| a,f | depart,arrive | road,road |
|
||||
| c,f | depart,arrive | road,road |
|
||||
| f,a | depart,arrive | road,road |
|
||||
| g,a | depart,arrive | road,road |
|
||||
| z,a | depart,turn right,arrive | cross,road,road |
|
||||
|
||||
Scenario: Traffic Island
|
||||
Given the node map
|
||||
@ -588,10 +601,10 @@ Feature: Merge Segregated Roads
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,c | germ,ober | depart,arrive |
|
||||
| a,g | germ,germ,germ | depart,continue right,arrive |
|
||||
| a,1 | germ,germ,germ | depart,continue left,arrive |
|
||||
| d,g | ober,germ,germ | depart,turn left,arrive |
|
||||
| a,c | germ,ober | depart,arrive |
|
||||
| a,g | germ,germ,germ | depart,continue right,arrive |
|
||||
| a,1 | germ,germ,germ | depart,continue left,arrive |
|
||||
| d,g | ober,germ,germ | depart,turn left,arrive |
|
||||
|
||||
# https://www.openstreetmap.org/#map=19/51.32888/6.57059
|
||||
Scenario: Places in presence of oneways
|
||||
@ -623,16 +636,16 @@ Feature: Merge Segregated Roads
|
||||
| cf | albrecht | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,l | schwert,albrecht,marianne,marianne | depart,new name straight,turn left,arrive |
|
||||
| a,j | schwert,luise,luise | depart,turn right,arrive |
|
||||
| a,1 | schwert,albrecht,albrecht,albrecht | depart,new name straight,continue uturn,arrive |
|
||||
| k,l | marianne,marianne | depart,arrive |
|
||||
| k,j | marianne,albrecht,luise,luise | depart,turn left,turn left,arrive |
|
||||
| k,d | marianne,schwert,schwert | depart,turn right,arrive |
|
||||
| i,j | luise,luise | depart,arrive |
|
||||
| i,d | luise,albrecht,schwert,schwert | depart,turn left,turn straight,arrive |
|
||||
| i,l | luise,albrecht,marianne,marianne | depart,turn left,turn left,arrive |
|
||||
| waypoints | route | turns |
|
||||
| a,l | schwert,albrecht,marianne,marianne | depart,new name straight,turn left,arrive |
|
||||
| a,j | schwert,luise,luise | depart,turn right,arrive |
|
||||
| a,1 | schwert,albrecht,albrecht,albrecht | depart,new name straight,continue uturn,arrive |
|
||||
| k,l | marianne,marianne | depart,arrive |
|
||||
| k,j | marianne,albrecht,luise,luise | depart,turn left,turn left,arrive |
|
||||
| k,d | marianne,schwert,schwert | depart,turn right,arrive |
|
||||
| i,j | luise,luise | depart,arrive |
|
||||
| i,d | luise,albrecht,schwert,schwert | depart,turn left,turn straight,arrive |
|
||||
| i,l | luise,albrecht,marianne,marianne | depart,turn left,turn left,arrive |
|
||||
|
||||
# https://www.openstreetmap.org/#map=19/52.46339/13.40272
|
||||
Scenario: Do not merge links between segregated roads
|
||||
|
@ -961,12 +961,12 @@ Feature: Simple Turns
|
||||
g
|
||||
.
|
||||
.
|
||||
.
|
||||
.
|
||||
f
|
||||
h .
|
||||
. .
|
||||
. j
|
||||
.
|
||||
.
|
||||
h f
|
||||
.
|
||||
. .
|
||||
. j
|
||||
. .
|
||||
c
|
||||
. . .
|
||||
|
@ -1,125 +0,0 @@
|
||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_
|
||||
|
||||
#include "util/attributes.hpp"
|
||||
#include "util/name_table.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "extractor/guidance/intersection_normalization_operation.hpp"
|
||||
#include "extractor/guidance/mergable_road_detector.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "extractor/suffix_table.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
/*
|
||||
* An intersection is a central part in computing guidance decisions. However the model in OSM and
|
||||
* the view we want to use in guidance are not necessarily the same thing. We have to account for
|
||||
* some models that are chosen explicitly in OSM and that don't actually describe how a human would
|
||||
* experience an intersection.
|
||||
*
|
||||
* For example, if a small pedestrian island is located at a traffic light right in the middle of a
|
||||
* road, OSM tends to model the road as two separate ways. A human would consider these two ways a
|
||||
* single road, though. In this normalizer, we try to account for these subtle differences between
|
||||
* OSM data and human perception to improve our decision base for guidance later on.
|
||||
*/
|
||||
class IntersectionNormalizer
|
||||
{
|
||||
public:
|
||||
struct NormalizationResult
|
||||
{
|
||||
IntersectionShape normalized_shape;
|
||||
std::vector<IntersectionNormalizationOperation> performed_merges;
|
||||
};
|
||||
IntersectionNormalizer(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &node_coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator);
|
||||
|
||||
// The function takes an intersection an converts it to a `perceived` intersection which closer
|
||||
// represents how a human might experience the intersection
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
NormalizationResult operator()(const NodeID node_at_intersection,
|
||||
IntersectionShape intersection) const;
|
||||
|
||||
private:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
const IntersectionGenerator &intersection_generator;
|
||||
const MergableRoadDetector mergable_road_detector;
|
||||
|
||||
/* 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. It also checks for neighboring merges.
|
||||
* This is due possible segments where multiple roads could end up being merged into one.
|
||||
* We only support merging two roads, not three or more, though.
|
||||
* c c
|
||||
* / /
|
||||
* a - b -> a - b - (c,d) but not a - b d -> a,b,(cde)
|
||||
* \ \
|
||||
* d e
|
||||
*/
|
||||
bool CanMerge(const NodeID intersection_node,
|
||||
const IntersectionShape &intersection,
|
||||
std::size_t first_index,
|
||||
std::size_t second_index) const;
|
||||
|
||||
// Perform an Actual Merge
|
||||
IntersectionNormalizationOperation
|
||||
DetermineMergeDirection(const IntersectionShapeData &lhs,
|
||||
const IntersectionShapeData &rhs) const;
|
||||
IntersectionShapeData MergeRoads(const IntersectionShapeData &destination,
|
||||
const IntersectionShapeData &source) const;
|
||||
IntersectionShapeData MergeRoads(const IntersectionNormalizationOperation direction,
|
||||
const IntersectionShapeData &lhs,
|
||||
const IntersectionShapeData &rhs,
|
||||
const double opposite_bearing) 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
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
NormalizationResult MergeSegregatedRoads(const NodeID intersection_node,
|
||||
IntersectionShape intersection) const;
|
||||
|
||||
// The counterpiece to mergeSegregatedRoads. While we can adjust roads that split up at the
|
||||
// intersection itself, it can also happen that intersections are connected to joining roads.
|
||||
//
|
||||
// * *
|
||||
// * is converted to *
|
||||
// v a --- a ---
|
||||
// v ^ +
|
||||
// v ^ +
|
||||
// b
|
||||
//
|
||||
// for the local view of b at a.
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
IntersectionShape AdjustBearingsForMergeAtDestination(const NodeID node_at_intersection,
|
||||
IntersectionShape intersection) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif /* OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_ */
|
@ -64,10 +64,6 @@ class RoundaboutHandler : public IntersectionHandler
|
||||
const EdgeID via_eid,
|
||||
const Intersection &intersection) const;
|
||||
|
||||
void invalidateExitAgainstDirection(const NodeID from_nid,
|
||||
const EdgeID via_eid,
|
||||
Intersection &intersection) const;
|
||||
|
||||
// decide whether we lookk at a roundabout or a rotary
|
||||
RoundaboutType getRoundaboutType(const NodeID nid) const;
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "extractor/guidance/intersection_normalization_operation.hpp"
|
||||
#include "extractor/guidance/intersection_normalizer.hpp"
|
||||
#include "extractor/guidance/motorway_handler.hpp"
|
||||
#include "extractor/guidance/roundabout_handler.hpp"
|
||||
#include "extractor/guidance/sliproad_handler.hpp"
|
||||
@ -56,22 +55,6 @@ class TurnAnalysis
|
||||
Intersection operator()(const NodeID node_prior_to_intersection,
|
||||
const EdgeID entering_via_edge) const;
|
||||
|
||||
/*
|
||||
* Returns a normalized intersection without any assigned turn types.
|
||||
* This intersection can be used as input for intersection classification, turn lane assignment
|
||||
* and similar.
|
||||
*/
|
||||
struct ShapeResult
|
||||
{
|
||||
// the basic shape, containing all turns
|
||||
IntersectionShape intersection_shape;
|
||||
// normalized shape, merged some roads into others, adjusted bearings
|
||||
// see intersection_normalizer for further explanations
|
||||
IntersectionNormalizer::NormalizationResult annotated_normalized_shape;
|
||||
};
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
ShapeResult ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const;
|
||||
|
||||
// Select turn types based on the intersection shape
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection AssignTurnTypes(const NodeID from_node,
|
||||
@ -83,7 +66,6 @@ class TurnAnalysis
|
||||
private:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
const IntersectionGenerator intersection_generator;
|
||||
const IntersectionNormalizer intersection_normalizer;
|
||||
const RoundaboutHandler roundabout_handler;
|
||||
const MotorwayHandler motorway_handler;
|
||||
const TurnHandler turn_handler;
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_ANALYSIS_HPP
|
||||
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/guidance/mergable_road_detector.hpp"
|
||||
#include "extractor/guidance/turn_lane_types.hpp"
|
||||
#include "extractor/intersection/intersection_edge.hpp"
|
||||
#include "extractor/restriction_index.hpp"
|
||||
@ -25,24 +26,36 @@ IntersectionEdges getIncomingEdges(const util::NodeBasedDynamicGraph &graph,
|
||||
IntersectionEdges getOutgoingEdges(const util::NodeBasedDynamicGraph &graph,
|
||||
const NodeID intersection);
|
||||
|
||||
IntersectionEdgeBearings
|
||||
getIntersectionBearings(const util::NodeBasedDynamicGraph &graph,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<util::Coordinate> &node_coordinates,
|
||||
const NodeID intersection);
|
||||
std::pair<IntersectionEdgeGeometries, std::unordered_set<EdgeID>>
|
||||
getIntersectionGeometries(const util::NodeBasedDynamicGraph &graph,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<util::Coordinate> &node_coordinates,
|
||||
const guidance::MergableRoadDetector &detector,
|
||||
const NodeID intersection);
|
||||
|
||||
guidance::IntersectionView
|
||||
convertToIntersectionView(const util::NodeBasedDynamicGraph &graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const IntersectionEdgeGeometries &edge_geometries,
|
||||
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||
const IntersectionEdge &incoming_edge,
|
||||
const IntersectionEdges &outgoing_edges,
|
||||
const std::unordered_set<EdgeID> &merged_edges);
|
||||
|
||||
bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const IntersectionEdgeBearings &bearings,
|
||||
const IntersectionEdgeGeometries &geometries,
|
||||
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||
const IntersectionEdge &from,
|
||||
const IntersectionEdge &to);
|
||||
|
||||
double computeTurnAngle(const IntersectionEdgeBearings &bearings,
|
||||
const IntersectionEdge &from,
|
||||
const IntersectionEdge &to);
|
||||
double findEdgeBearing(const IntersectionEdgeGeometries &geometries, const EdgeID &edge);
|
||||
|
||||
double findEdgeLength(const IntersectionEdgeGeometries &geometries, const EdgeID &edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,15 +26,17 @@ struct IntersectionEdge
|
||||
|
||||
using IntersectionEdges = std::vector<IntersectionEdge>;
|
||||
|
||||
struct IntersectionEdgeBearing
|
||||
struct IntersectionEdgeGeometry
|
||||
{
|
||||
EdgeID edge;
|
||||
float bearing;
|
||||
double initial_bearing;
|
||||
double perceived_bearing;
|
||||
double length;
|
||||
|
||||
bool operator<(const IntersectionEdgeBearing &other) const { return edge < other.edge; }
|
||||
bool operator<(const IntersectionEdgeGeometry &other) const { return edge < other.edge; }
|
||||
};
|
||||
|
||||
using IntersectionEdgeBearings = std::vector<IntersectionEdgeBearing>;
|
||||
using IntersectionEdgeGeometries = std::vector<IntersectionEdgeGeometry>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -443,6 +443,17 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
turn_analysis,
|
||||
lane_data_map);
|
||||
|
||||
// TODO: add a MergableRoadDetector instance, to be deleted later
|
||||
guidance::CoordinateExtractor coordinate_extractor(
|
||||
m_node_based_graph, m_compressed_edge_container, m_coordinates);
|
||||
guidance::MergableRoadDetector mergable_road_detector(m_node_based_graph,
|
||||
m_edge_based_node_container,
|
||||
m_coordinates,
|
||||
turn_analysis.GetIntersectionGenerator(),
|
||||
coordinate_extractor,
|
||||
name_table,
|
||||
street_name_suffix_table);
|
||||
|
||||
bearing_class_by_node_based_node.resize(m_node_based_graph.GetNumberOfNodes(),
|
||||
std::numeric_limits<std::uint32_t>::max());
|
||||
|
||||
@ -545,7 +556,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
const auto node_based_edge_from,
|
||||
const auto node_at_center_of_intersection,
|
||||
const auto node_based_edge_to,
|
||||
const auto &intersection,
|
||||
const auto incoming_bearing,
|
||||
const auto &turn,
|
||||
const auto entry_class_id) {
|
||||
|
||||
@ -578,7 +589,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
TurnData turn_data = {turn.instruction,
|
||||
turn.lane_data_id,
|
||||
entry_class_id,
|
||||
util::guidance::TurnBearing(intersection[0].bearing),
|
||||
util::guidance::TurnBearing(incoming_bearing),
|
||||
util::guidance::TurnBearing(turn.bearing)};
|
||||
|
||||
// compute weight and duration penalties
|
||||
@ -664,59 +675,20 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
node_at_center_of_intersection < end;
|
||||
++node_at_center_of_intersection)
|
||||
{
|
||||
|
||||
int new_turns = 0, old_turns = 0;
|
||||
|
||||
std::cout << "=== node_at_center_of_intersection "
|
||||
<< node_at_center_of_intersection << "\n";
|
||||
// We capture the thread-local work in these objects, then flush
|
||||
// them in a controlled manner at the end of the parallel range
|
||||
const auto &incoming_edges = intersection::getIncomingEdges(
|
||||
m_node_based_graph, node_at_center_of_intersection);
|
||||
const auto &outgoing_edges = intersection::getOutgoingEdges(
|
||||
m_node_based_graph, node_at_center_of_intersection);
|
||||
const auto &edge_bearings =
|
||||
intersection::getIntersectionBearings(m_node_based_graph,
|
||||
m_compressed_edge_container,
|
||||
m_coordinates,
|
||||
node_at_center_of_intersection);
|
||||
|
||||
std::cout << "=== new turns \n";
|
||||
for (const auto &incoming_edge : incoming_edges)
|
||||
{
|
||||
for (const auto &outgoing_edge : outgoing_edges)
|
||||
{
|
||||
const auto turn_angle = intersection::computeTurnAngle(
|
||||
edge_bearings, incoming_edge, outgoing_edge);
|
||||
|
||||
std::cout << incoming_edge.node << "," << incoming_edge.edge << " -> "
|
||||
<< outgoing_edge.node << "," << outgoing_edge.edge << " -> "
|
||||
<< m_node_based_graph.GetTarget(outgoing_edge.edge)
|
||||
<< " is allowed "
|
||||
<< intersection::isTurnAllowed(m_node_based_graph,
|
||||
m_edge_based_node_container,
|
||||
node_restriction_map,
|
||||
m_barrier_nodes,
|
||||
edge_bearings,
|
||||
turn_lanes_data,
|
||||
incoming_edge,
|
||||
outgoing_edge)
|
||||
<< " angle " << turn_angle << "\n";
|
||||
|
||||
new_turns += intersection::isTurnAllowed(m_node_based_graph,
|
||||
m_edge_based_node_container,
|
||||
node_restriction_map,
|
||||
m_barrier_nodes,
|
||||
edge_bearings,
|
||||
turn_lanes_data,
|
||||
incoming_edge,
|
||||
outgoing_edge);
|
||||
}
|
||||
}
|
||||
|
||||
// We capture the thread-local work in these objects, then flush
|
||||
// them in a controlled manner at the end of the parallel range
|
||||
|
||||
const auto shape_result =
|
||||
turn_analysis.ComputeIntersectionShapes(node_at_center_of_intersection);
|
||||
const auto &edge_geometries_and_merged_edges =
|
||||
intersection::getIntersectionGeometries(m_node_based_graph,
|
||||
m_compressed_edge_container,
|
||||
m_coordinates,
|
||||
mergable_road_detector,
|
||||
node_at_center_of_intersection);
|
||||
const auto &edge_geometries = edge_geometries_and_merged_edges.first;
|
||||
const auto &merged_edge_ids = edge_geometries_and_merged_edges.second;
|
||||
|
||||
// all nodes in the graph are connected in both directions. We check all
|
||||
// outgoing nodes to find the incoming edge. This is a larger search overhead,
|
||||
@ -736,45 +708,32 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
// From the flags alone, we cannot determine which nodes are connected to
|
||||
// `b` by an outgoing edge. Therefore, we have to search all connected edges for
|
||||
// edges entering `b`
|
||||
std::cout << "=== old turns \n";
|
||||
|
||||
for (const EdgeID outgoing_edge :
|
||||
m_node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection))
|
||||
for (const auto &incoming_edge : incoming_edges)
|
||||
{
|
||||
const NodeID node_along_road_entering =
|
||||
m_node_based_graph.GetTarget(outgoing_edge);
|
||||
|
||||
const auto incoming_edge = m_node_based_graph.FindEdge(
|
||||
node_along_road_entering, node_at_center_of_intersection);
|
||||
|
||||
if (m_node_based_graph.GetEdgeData(incoming_edge).reversed)
|
||||
continue;
|
||||
|
||||
++node_based_edge_counter;
|
||||
|
||||
auto intersection_with_flags_and_angles =
|
||||
turn_analysis.GetIntersectionGenerator()
|
||||
.TransformIntersectionShapeIntoView(
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
shape_result.annotated_normalized_shape.normalized_shape,
|
||||
shape_result.intersection_shape,
|
||||
shape_result.annotated_normalized_shape.performed_merges);
|
||||
const auto intersection_view =
|
||||
convertToIntersectionView(m_node_based_graph,
|
||||
m_edge_based_node_container,
|
||||
node_restriction_map,
|
||||
m_barrier_nodes,
|
||||
edge_geometries,
|
||||
turn_lanes_data,
|
||||
incoming_edge,
|
||||
outgoing_edges,
|
||||
merged_edge_ids);
|
||||
|
||||
auto intersection =
|
||||
turn_analysis.AssignTurnTypes(node_along_road_entering,
|
||||
incoming_edge,
|
||||
intersection_with_flags_and_angles);
|
||||
auto intersection = turn_analysis.AssignTurnTypes(
|
||||
incoming_edge.node, incoming_edge.edge, intersection_view);
|
||||
|
||||
OSRM_ASSERT(intersection.valid(),
|
||||
m_coordinates[node_at_center_of_intersection]);
|
||||
|
||||
intersection = turn_lane_handler.assignTurnLanes(
|
||||
node_along_road_entering, incoming_edge, std::move(intersection));
|
||||
incoming_edge.node, incoming_edge.edge, std::move(intersection));
|
||||
|
||||
// the entry class depends on the turn, so we have to classify the
|
||||
// interesction for
|
||||
// every edge
|
||||
// interesction for every edge
|
||||
const auto turn_classification = classifyIntersection(
|
||||
intersection, m_coordinates[node_at_center_of_intersection]);
|
||||
|
||||
@ -792,18 +751,31 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
|
||||
// check if we are turning off a via way
|
||||
const auto turning_off_via_way = way_restriction_map.IsViaWay(
|
||||
node_along_road_entering, node_at_center_of_intersection);
|
||||
incoming_edge.node, node_at_center_of_intersection);
|
||||
|
||||
for (const auto &turn : intersection)
|
||||
// Save reversed incoming bearing to compute turn angles
|
||||
const auto reversed_incoming_bearing = util::bearing::reverse(
|
||||
findEdgeBearing(edge_geometries, incoming_edge.edge));
|
||||
|
||||
for (const auto &outgoing_edge : outgoing_edges)
|
||||
{
|
||||
// only keep valid turns
|
||||
if (!turn.entry_allowed)
|
||||
if (!intersection::isTurnAllowed(m_node_based_graph,
|
||||
m_edge_based_node_container,
|
||||
node_restriction_map,
|
||||
m_barrier_nodes,
|
||||
edge_geometries,
|
||||
turn_lanes_data,
|
||||
incoming_edge,
|
||||
outgoing_edge))
|
||||
continue;
|
||||
|
||||
old_turns += 1;
|
||||
std::cout << node_along_road_entering << " -> "
|
||||
<< node_at_center_of_intersection << " -> "
|
||||
<< m_node_based_graph.GetTarget(turn.eid) << "\n";
|
||||
const auto turn =
|
||||
std::find_if(intersection.begin(),
|
||||
intersection.end(),
|
||||
[edge = outgoing_edge.edge](const auto &road) {
|
||||
return road.eid == edge;
|
||||
});
|
||||
BOOST_ASSERT(turn != intersection.end());
|
||||
|
||||
// In case a way restriction starts at a given location, add a turn onto
|
||||
// every artificial node eminating here.
|
||||
@ -826,22 +798,22 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
// duplicated node associated with the turn. (e.g. ab via bc switches bc
|
||||
// to bc_dup)
|
||||
auto const target_id = way_restriction_map.RemapIfRestricted(
|
||||
nbe_to_ebn_mapping[turn.eid],
|
||||
node_along_road_entering,
|
||||
node_at_center_of_intersection,
|
||||
m_node_based_graph.GetTarget(turn.eid),
|
||||
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||
incoming_edge.node,
|
||||
outgoing_edge.node,
|
||||
m_node_based_graph.GetTarget(outgoing_edge.edge),
|
||||
m_number_of_edge_based_nodes);
|
||||
|
||||
{ // scope to forget edge_with_data after
|
||||
const auto edge_with_data_and_condition =
|
||||
generate_edge(nbe_to_ebn_mapping[incoming_edge],
|
||||
generate_edge(nbe_to_ebn_mapping[incoming_edge.edge],
|
||||
target_id,
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
node_at_center_of_intersection,
|
||||
turn.eid,
|
||||
intersection,
|
||||
turn,
|
||||
incoming_edge.node,
|
||||
incoming_edge.edge,
|
||||
outgoing_edge.node,
|
||||
outgoing_edge.edge,
|
||||
reversed_incoming_bearing,
|
||||
*turn,
|
||||
entry_class_id);
|
||||
|
||||
buffer->continuous_data.edges_list.push_back(
|
||||
@ -868,7 +840,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
if (turning_off_via_way)
|
||||
{
|
||||
const auto duplicated_nodes = way_restriction_map.DuplicatedNodeIDs(
|
||||
node_along_road_entering, node_at_center_of_intersection);
|
||||
incoming_edge.node, node_at_center_of_intersection);
|
||||
|
||||
// next to the normal restrictions tracked in `entry_allowed`, via
|
||||
// ways might introduce additional restrictions. These are handled
|
||||
@ -876,12 +848,12 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
for (auto duplicated_node_id : duplicated_nodes)
|
||||
{
|
||||
const auto from_id =
|
||||
m_number_of_edge_based_nodes -
|
||||
way_restriction_map.NumberOfDuplicatedNodes() +
|
||||
duplicated_node_id;
|
||||
NodeID(m_number_of_edge_based_nodes -
|
||||
way_restriction_map.NumberOfDuplicatedNodes() +
|
||||
duplicated_node_id);
|
||||
|
||||
auto const node_at_end_of_turn =
|
||||
m_node_based_graph.GetTarget(turn.eid);
|
||||
m_node_based_graph.GetTarget(outgoing_edge.edge);
|
||||
|
||||
const auto is_way_restricted = way_restriction_map.IsRestricted(
|
||||
duplicated_node_id, node_at_end_of_turn);
|
||||
@ -896,14 +868,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
|
||||
// add into delayed data
|
||||
auto edge_with_data_and_condition =
|
||||
generate_edge(NodeID(from_id),
|
||||
nbe_to_ebn_mapping[turn.eid],
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
node_at_center_of_intersection,
|
||||
turn.eid,
|
||||
intersection,
|
||||
turn,
|
||||
generate_edge(from_id,
|
||||
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||
incoming_edge.node,
|
||||
incoming_edge.edge,
|
||||
outgoing_edge.node,
|
||||
outgoing_edge.edge,
|
||||
reversed_incoming_bearing,
|
||||
*turn,
|
||||
entry_class_id);
|
||||
|
||||
buffer->delayed_data.push_back(
|
||||
@ -920,8 +892,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
{
|
||||
// add a new conditional for the edge we just created
|
||||
buffer->conditionals.push_back(
|
||||
{NodeID(from_id),
|
||||
nbe_to_ebn_mapping[turn.eid],
|
||||
{from_id,
|
||||
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||
{static_cast<std::uint64_t>(-1),
|
||||
m_coordinates[node_at_center_of_intersection],
|
||||
restriction.condition}});
|
||||
@ -930,14 +902,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
else
|
||||
{
|
||||
auto edge_with_data_and_condition =
|
||||
generate_edge(NodeID(from_id),
|
||||
nbe_to_ebn_mapping[turn.eid],
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
node_at_center_of_intersection,
|
||||
turn.eid,
|
||||
intersection,
|
||||
turn,
|
||||
generate_edge(from_id,
|
||||
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||
incoming_edge.node,
|
||||
incoming_edge.edge,
|
||||
outgoing_edge.node,
|
||||
outgoing_edge.edge,
|
||||
reversed_incoming_bearing,
|
||||
*turn,
|
||||
entry_class_id);
|
||||
|
||||
buffer->delayed_data.push_back(
|
||||
@ -953,10 +925,6 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "new_turns " << new_turns << " old_turns " << old_turns << "\n";
|
||||
OSRM_ASSERT(new_turns == old_turns,
|
||||
m_coordinates[node_at_center_of_intersection]);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
|
@ -1,430 +0,0 @@
|
||||
#include "extractor/guidance/intersection_normalizer.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
using osrm::util::angularDeviation;
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
IntersectionNormalizer::IntersectionNormalizer(
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: node_based_graph(node_based_graph), intersection_generator(intersection_generator),
|
||||
mergable_road_detector(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
intersection_generator,
|
||||
intersection_generator.GetCoordinateExtractor(),
|
||||
name_table,
|
||||
street_name_suffix_table)
|
||||
{
|
||||
}
|
||||
|
||||
IntersectionNormalizer::NormalizationResult IntersectionNormalizer::
|
||||
operator()(const NodeID node_at_intersection, IntersectionShape intersection) const
|
||||
{
|
||||
const auto intersection_copy = intersection;
|
||||
auto merged_shape_and_merges =
|
||||
MergeSegregatedRoads(node_at_intersection, std::move(intersection));
|
||||
merged_shape_and_merges.normalized_shape = AdjustBearingsForMergeAtDestination(
|
||||
node_at_intersection, std::move(merged_shape_and_merges.normalized_shape));
|
||||
return merged_shape_and_merges;
|
||||
}
|
||||
|
||||
bool IntersectionNormalizer::CanMerge(const NodeID intersection_node,
|
||||
const IntersectionShape &intersection,
|
||||
std::size_t fist_index_in_ccw,
|
||||
std::size_t second_index_in_ccw) const
|
||||
{
|
||||
BOOST_ASSERT(((fist_index_in_ccw + 1) % intersection.size()) == second_index_in_ccw);
|
||||
|
||||
// don't merge on degree two, since it's most likely a bollard/traffic light or a round way
|
||||
if (intersection.size() <= 2)
|
||||
return false;
|
||||
|
||||
const auto can_merge = mergable_road_detector.CanMergeRoad(
|
||||
intersection_node, intersection[fist_index_in_ccw], intersection[second_index_in_ccw]);
|
||||
|
||||
/*
|
||||
* Merging should never depend on order/never merge more than two roads. To ensure that we don't
|
||||
* merge anything that is impacted by neighboring roads (e.g. three roads of the same name as in
|
||||
* parking lots/border checkpoints), we check if the neigboring roads would be merged as well.
|
||||
* In that case, we cannot merge, since we would end up merging multiple items together
|
||||
*/
|
||||
const auto is_distinct = [&]() {
|
||||
const auto next_index_in_ccw = (second_index_in_ccw + 1) % intersection.size();
|
||||
const auto distinct_to_next_in_ccw = mergable_road_detector.IsDistinctFrom(
|
||||
intersection[second_index_in_ccw], intersection[next_index_in_ccw]);
|
||||
const auto prev_index_in_ccw =
|
||||
(fist_index_in_ccw + intersection.size() - 1) % intersection.size();
|
||||
const auto distinct_to_prev_in_ccw = mergable_road_detector.IsDistinctFrom(
|
||||
intersection[prev_index_in_ccw], intersection[fist_index_in_ccw]);
|
||||
return distinct_to_next_in_ccw && distinct_to_prev_in_ccw;
|
||||
};
|
||||
|
||||
// use lazy evaluation to check only if mergable
|
||||
return can_merge && is_distinct();
|
||||
}
|
||||
|
||||
IntersectionNormalizationOperation
|
||||
IntersectionNormalizer::DetermineMergeDirection(const IntersectionShapeData &lhs,
|
||||
const IntersectionShapeData &rhs) const
|
||||
{
|
||||
if (node_based_graph.GetEdgeData(lhs.eid).reversed)
|
||||
return {lhs.eid, rhs.eid};
|
||||
else
|
||||
return {rhs.eid, lhs.eid};
|
||||
}
|
||||
|
||||
IntersectionShapeData IntersectionNormalizer::MergeRoads(const IntersectionShapeData &into,
|
||||
const IntersectionShapeData &from) const
|
||||
{
|
||||
// we only merge small angles. If the difference between both is large, we are looking at a
|
||||
// bearing leading north. Such a bearing cannot be handled via the basic average. In this
|
||||
// case we actually need to shift the bearing by half the difference.
|
||||
const auto aroundZero = [](const double first, const double second) {
|
||||
return (std::max(first, second) - std::min(first, second)) >= 180;
|
||||
};
|
||||
|
||||
// find the angle between two other angles
|
||||
const auto combineAngles = [aroundZero](const double first, const double second) {
|
||||
if (!aroundZero(first, second))
|
||||
return .5 * (first + second);
|
||||
else
|
||||
{
|
||||
const auto offset = angularDeviation(first, second);
|
||||
auto new_angle = std::max(first, second) + .5 * offset;
|
||||
if (new_angle >= 360)
|
||||
return new_angle - 360;
|
||||
return new_angle;
|
||||
}
|
||||
};
|
||||
|
||||
auto result = into;
|
||||
BOOST_ASSERT(!node_based_graph.GetEdgeData(into.eid).reversed);
|
||||
result.bearing = combineAngles(into.bearing, from.bearing);
|
||||
BOOST_ASSERT(0 <= result.bearing && result.bearing < 360.0);
|
||||
return result;
|
||||
}
|
||||
|
||||
IntersectionShapeData
|
||||
IntersectionNormalizer::MergeRoads(const IntersectionNormalizationOperation direction,
|
||||
const IntersectionShapeData &lhs,
|
||||
const IntersectionShapeData &rhs,
|
||||
const double opposite_bearing) const
|
||||
{
|
||||
// In some intersections, turning roads can introduce artificial turns if we merge here.
|
||||
// Consider a scenario like:
|
||||
//
|
||||
// a . g - f
|
||||
// | .
|
||||
// | .
|
||||
// |.
|
||||
// d-b--------e
|
||||
// |
|
||||
// c
|
||||
//
|
||||
// Merging `bgf` and `be` would introduce an angle, even though d-b-e is perfectly straight
|
||||
// We don't change the angle, if such an opposite road exists
|
||||
if (direction.merged_eid == lhs.eid)
|
||||
{
|
||||
// change the angle only if the opposite direction is not nearly straight
|
||||
if (angularDeviation(opposite_bearing, rhs.bearing) >
|
||||
(STRAIGHT_ANGLE - MAXIMAL_ALLOWED_NO_TURN_DEVIATION))
|
||||
return rhs;
|
||||
else
|
||||
return MergeRoads(rhs, lhs);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (angularDeviation(opposite_bearing, lhs.bearing) >
|
||||
(STRAIGHT_ANGLE - MAXIMAL_ALLOWED_NO_TURN_DEVIATION))
|
||||
return lhs;
|
||||
else
|
||||
return MergeRoads(lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
IntersectionNormalizer::NormalizationResult
|
||||
IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
|
||||
IntersectionShape intersection) const
|
||||
{
|
||||
const auto getRight = [&](std::size_t index) {
|
||||
return (index + intersection.size() - 1) % intersection.size();
|
||||
};
|
||||
|
||||
// This map stores for all edges that participated in a merging operation in which edge id they
|
||||
// end up in the end. We only store what we have merged into other edges.
|
||||
std::vector<IntersectionNormalizationOperation> merging_map;
|
||||
const auto merge = [this, &merging_map](const IntersectionShapeData &first,
|
||||
const IntersectionShapeData &second,
|
||||
const double opposite_bearing) {
|
||||
|
||||
const auto direction = DetermineMergeDirection(first, second);
|
||||
BOOST_ASSERT(
|
||||
std::find_if(merging_map.begin(), merging_map.end(), [direction](const auto pair) {
|
||||
return pair.merged_eid == direction.merged_eid;
|
||||
}) == merging_map.end());
|
||||
merging_map.push_back(direction);
|
||||
return MergeRoads(direction, first, second, opposite_bearing);
|
||||
};
|
||||
|
||||
if (intersection.size() <= 1)
|
||||
return {intersection, merging_map};
|
||||
|
||||
const auto intersection_copy = intersection;
|
||||
const auto opposite_bearing = [this, intersection_copy](const IntersectionShapeData &lhs,
|
||||
const IntersectionShapeData &rhs) {
|
||||
if (node_based_graph.GetEdgeData(lhs.eid).reversed)
|
||||
{
|
||||
return intersection_copy.FindClosestBearing(util::bearing::reverse(rhs.bearing))
|
||||
->bearing;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(node_based_graph.GetEdgeData(rhs.eid).reversed);
|
||||
return intersection_copy.FindClosestBearing(util::bearing::reverse(lhs.bearing))
|
||||
->bearing;
|
||||
}
|
||||
};
|
||||
// check for merges including the basic u-turn
|
||||
// these result in an adjustment of all other angles. This is due to how these angles are
|
||||
// perceived. Considering the following example:
|
||||
//
|
||||
// c b
|
||||
// Y
|
||||
// a
|
||||
//
|
||||
// coming from a to b (given a road that splits at the fork into two one-ways), the turn is not
|
||||
// considered as a turn but rather as going straight.
|
||||
// Now if we look at the situation merging:
|
||||
//
|
||||
// a b
|
||||
// \ /
|
||||
// e - + - d
|
||||
// |
|
||||
// c
|
||||
//
|
||||
// With a,b representing the same road, the intersection itself represents a classif for way
|
||||
// intersection so we handle it like
|
||||
//
|
||||
// (a),b
|
||||
// |
|
||||
// e - + - d
|
||||
// |
|
||||
// c
|
||||
//
|
||||
// To be able to consider this adjusted representation down the line, we merge some roads.
|
||||
// If the merge occurs at the u-turn edge, we need to adjust all angles, though, since they are
|
||||
// with respect to the now changed perceived location of a. If we move (a) to the left, we add
|
||||
// the difference to all angles. Otherwise we subtract it.
|
||||
// these result in an adjustment of all other angles
|
||||
if (CanMerge(intersection_node, intersection, intersection.size() - 1, 0))
|
||||
{
|
||||
// moving `a` to the left
|
||||
const auto opposite = opposite_bearing(intersection.front(), intersection.back());
|
||||
intersection[0] = merge(intersection.front(), intersection.back(), opposite);
|
||||
// FIXME if we have a left-sided country, we need to switch this off and enable it
|
||||
// below
|
||||
intersection.pop_back();
|
||||
}
|
||||
else if (CanMerge(intersection_node, intersection, 0, 1))
|
||||
{
|
||||
const auto opposite = opposite_bearing(intersection.front(), intersection[1]);
|
||||
intersection[0] = merge(intersection.front(), intersection[1], opposite);
|
||||
intersection.erase(intersection.begin() + 1);
|
||||
}
|
||||
|
||||
// a merge including the first u-turn requires 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 (CanMerge(intersection_node, intersection, getRight(index), index))
|
||||
{
|
||||
const auto opposite =
|
||||
opposite_bearing(intersection[getRight(index)], intersection[index]);
|
||||
intersection[getRight(index)] =
|
||||
merge(intersection[getRight(index)], intersection[index], opposite);
|
||||
intersection.erase(intersection.begin() + index);
|
||||
--index;
|
||||
}
|
||||
}
|
||||
return {intersection, merging_map};
|
||||
}
|
||||
|
||||
// OSM can have some very steep angles for joining roads. Considering the following intersection:
|
||||
// x
|
||||
// |
|
||||
// v __________c
|
||||
// /
|
||||
// a ---d
|
||||
// \ __________b
|
||||
//
|
||||
// with c->d as a oneway
|
||||
// and d->b as a oneway, the turn von x->d is actually a turn from x->a. So when looking at the
|
||||
// intersection coming from x, we want to interpret the situation as
|
||||
// x
|
||||
// |
|
||||
// a __ d __ v__________c
|
||||
// |
|
||||
// |_______________b
|
||||
//
|
||||
// Where we see the turn to `d` as a right turn, rather than going straight.
|
||||
// We do this by adjusting the local turn angle at `x` to turn onto `d` to be reflective of this
|
||||
// situation, where `v` would be the node at the intersection.
|
||||
IntersectionShape
|
||||
IntersectionNormalizer::AdjustBearingsForMergeAtDestination(const NodeID node_at_intersection,
|
||||
IntersectionShape intersection) const
|
||||
{
|
||||
// nothing to do for dead ends
|
||||
if (intersection.size() <= 1)
|
||||
return intersection;
|
||||
|
||||
// we don't adjust any road that is longer than 30 meters (between centers of intersections),
|
||||
// since the road is probably too long otherwise to impact perception.
|
||||
const double constexpr PRUNING_DISTANCE = 30;
|
||||
// never adjust u-turns
|
||||
for (std::size_t index = 0; index < intersection.size(); ++index)
|
||||
{
|
||||
auto &road = intersection[index];
|
||||
// only consider roads that are close
|
||||
if (road.segment_length > PRUNING_DISTANCE)
|
||||
continue;
|
||||
|
||||
// to find out about the above situation, we need to look at the next intersection (at d in
|
||||
// the example). If the initial road can be merged to the left/right, we are about to adjust
|
||||
// the angle.
|
||||
const auto next_intersection_along_road = intersection_generator.ComputeIntersectionShape(
|
||||
node_based_graph.GetTarget(road.eid), node_at_intersection);
|
||||
|
||||
if (next_intersection_along_road.size() <= 1)
|
||||
continue;
|
||||
|
||||
const auto node_at_next_intersection = node_based_graph.GetTarget(road.eid);
|
||||
|
||||
const auto adjustAngle = [](double angle, double offset) {
|
||||
angle += offset;
|
||||
if (angle > 360)
|
||||
return angle - 360.;
|
||||
else if (angle < 0)
|
||||
return angle + 360.;
|
||||
return angle;
|
||||
};
|
||||
|
||||
const auto range = node_based_graph.GetAdjacentEdgeRange(node_at_next_intersection);
|
||||
if (range.size() <= 1)
|
||||
continue;
|
||||
|
||||
// the order does not matter
|
||||
const auto get_offset = [](const IntersectionShapeData &lhs,
|
||||
const IntersectionShapeData &rhs) {
|
||||
return 0.5 * angularDeviation(lhs.bearing, rhs.bearing);
|
||||
};
|
||||
|
||||
// When offsetting angles in our turns, we don't want to get past the next turn. This
|
||||
// function simply limits an offset to be at most half the distance to the next turn in the
|
||||
// offfset direction
|
||||
const auto get_corrected_offset = [](
|
||||
const double offset,
|
||||
const IntersectionShapeData &road,
|
||||
const IntersectionShapeData &next_road_in_offset_direction) {
|
||||
const auto offset_limit =
|
||||
angularDeviation(road.bearing, next_road_in_offset_direction.bearing);
|
||||
// limit the offset with an additional buffer
|
||||
return (offset + MAXIMAL_ALLOWED_NO_TURN_DEVIATION > offset_limit) ? 0.5 * offset_limit
|
||||
: offset;
|
||||
};
|
||||
|
||||
// only if straighmost angles get smaller, we consider it an improvement
|
||||
auto const improves_straightmost = [&](auto const index, auto const offset) {
|
||||
const auto itr = next_intersection_along_road.FindClosestBearing(
|
||||
util::bearing::reverse(next_intersection_along_road[index].bearing));
|
||||
const auto angle = util::bearing::angleBetween(
|
||||
util::bearing::reverse(itr->bearing), next_intersection_along_road[index].bearing);
|
||||
|
||||
return util::angularDeviation(angle, STRAIGHT_ANGLE) >
|
||||
util::angularDeviation(angle + offset, STRAIGHT_ANGLE);
|
||||
};
|
||||
|
||||
// check if the u-turn edge at the next intersection could be merged to the left/right. If
|
||||
// this is the case and the road is not far away (see previous distance check), if
|
||||
// influences the perceived angle.
|
||||
if (CanMerge(node_at_next_intersection, next_intersection_along_road, 0, 1))
|
||||
{
|
||||
const auto offset =
|
||||
get_offset(next_intersection_along_road[0], next_intersection_along_road[1]);
|
||||
|
||||
if (improves_straightmost(0, -offset) && improves_straightmost(1, offset))
|
||||
{
|
||||
const auto corrected_offset = get_corrected_offset(
|
||||
offset,
|
||||
road,
|
||||
intersection[(intersection.size() + index - 1) % intersection.size()]);
|
||||
// at the target intersection, we merge to the right, so we need to shift the
|
||||
// current
|
||||
// angle to the left
|
||||
road.bearing = adjustAngle(road.bearing, corrected_offset);
|
||||
}
|
||||
}
|
||||
else if (CanMerge(node_at_next_intersection,
|
||||
next_intersection_along_road,
|
||||
next_intersection_along_road.size() - 1,
|
||||
0))
|
||||
{
|
||||
const auto offset =
|
||||
get_offset(next_intersection_along_road[0],
|
||||
next_intersection_along_road[next_intersection_along_road.size() - 1]);
|
||||
|
||||
if (improves_straightmost(0, offset) &&
|
||||
improves_straightmost(next_intersection_along_road.size() - 1, -offset))
|
||||
{
|
||||
const auto corrected_offset = get_corrected_offset(
|
||||
offset, road, intersection[(index + 1) % intersection.size()]);
|
||||
// at the target intersection, we merge to the left, so we need to shift the current
|
||||
// angle to the right
|
||||
road.bearing = adjustAngle(road.bearing, -corrected_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
@ -56,7 +56,6 @@ bool RoundaboutHandler::canProcess(const NodeID from_nid,
|
||||
Intersection RoundaboutHandler::
|
||||
operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersection) const
|
||||
{
|
||||
invalidateExitAgainstDirection(from_nid, via_eid, intersection);
|
||||
const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
|
||||
const auto roundabout_type = getRoundaboutType(node_based_graph.GetTarget(via_eid));
|
||||
// find the radius of the roundabout
|
||||
@ -107,77 +106,6 @@ detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags(
|
||||
return {on_roundabout, can_enter_roundabout, can_exit_roundabout_separately};
|
||||
}
|
||||
|
||||
void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid,
|
||||
const EdgeID via_eid,
|
||||
Intersection &intersection) const
|
||||
{
|
||||
const auto &in_edge_class = node_based_graph.GetEdgeData(via_eid).flags;
|
||||
if (in_edge_class.roundabout || in_edge_class.circular)
|
||||
return;
|
||||
|
||||
// Find range in which exits that must be invalidated (shaded areas):
|
||||
// exit..end exit..end begin..exit for ↺ roundabouts
|
||||
// *************************************
|
||||
// * <--. ^ <--. / <--. *
|
||||
// * | / | /░ | *
|
||||
// * |/ |v░░ -->| *
|
||||
// * |^ |\ ░ ░░░|\ *
|
||||
// * |░\ |░\░ ░░░| \ *
|
||||
// * --'░░░\ --'░░░v --' v *
|
||||
// *************************************
|
||||
//
|
||||
// begin..exit begin..exit exit..end for ↻ roundabouts
|
||||
// *************************************
|
||||
// * --.░░░^ --.░░░/ --. ^ *
|
||||
// * |░/░ |░/ ░░░| / *
|
||||
// * |/░░ |v ░░░|/ *
|
||||
// * |^░░ |\ -->| *
|
||||
// * | \░ | \ | *
|
||||
// * <--' \ <--' v <--' *
|
||||
// *************************************
|
||||
bool roundabout_entry_first = false;
|
||||
auto invalidate_from = intersection.end(), invalidate_to = intersection.end();
|
||||
for (auto road = intersection.begin(); road != intersection.end(); ++road)
|
||||
{
|
||||
const auto &edge = node_based_graph.GetEdgeData(road->eid);
|
||||
if (edge.flags.roundabout || edge.flags.circular)
|
||||
{
|
||||
if (edge.reversed)
|
||||
{
|
||||
if (roundabout_entry_first)
|
||||
{ // invalidate turns in range exit..end
|
||||
invalidate_from = road + 1;
|
||||
invalidate_to = intersection.end();
|
||||
}
|
||||
else
|
||||
{ // invalidate turns in range begin..exit
|
||||
invalidate_from = intersection.begin() + 1;
|
||||
invalidate_to = road;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
roundabout_entry_first = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OSRM_ASSERT(invalidate_from <= invalidate_to, coordinates[from_nid]);
|
||||
|
||||
// Exiting roundabouts at an entry point is technically a data-modelling issue.
|
||||
// This workaround handles cases in which an exit precedes and entry. The resulting
|
||||
// u-turn against the roundabout direction is invalidated.
|
||||
for (; invalidate_from != invalidate_to; ++invalidate_from)
|
||||
{
|
||||
const auto &edge = node_based_graph.GetEdgeData(invalidate_from->eid);
|
||||
if (!edge.flags.roundabout && !edge.flags.circular &&
|
||||
node_based_graph.GetTarget(invalidate_from->eid) != from_nid)
|
||||
{
|
||||
invalidate_from->entry_allowed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we want to see a roundabout as a turn, the exits have to be distinct enough to be seen a
|
||||
// dedicated turns. We are limiting it to four-way intersections with well distinct bearings.
|
||||
// All entry/roads and exit roads have to be simple. Not segregated roads.
|
||||
|
@ -35,12 +35,6 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
barrier_nodes,
|
||||
coordinates,
|
||||
compressed_edge_container),
|
||||
intersection_normalizer(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
intersection_generator),
|
||||
roundabout_handler(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
@ -88,24 +82,6 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
{
|
||||
}
|
||||
|
||||
Intersection TurnAnalysis::operator()(const NodeID node_prior_to_intersection,
|
||||
const EdgeID entering_via_edge) const
|
||||
{
|
||||
TurnAnalysis::ShapeResult shape_result =
|
||||
ComputeIntersectionShapes(node_based_graph.GetTarget(entering_via_edge));
|
||||
|
||||
// assign valid flags to normalized_shape
|
||||
const auto intersection_view = intersection_generator.TransformIntersectionShapeIntoView(
|
||||
node_prior_to_intersection,
|
||||
entering_via_edge,
|
||||
shape_result.annotated_normalized_shape.normalized_shape,
|
||||
shape_result.intersection_shape,
|
||||
shape_result.annotated_normalized_shape.performed_merges);
|
||||
|
||||
// assign the turn types to the intersection
|
||||
return AssignTurnTypes(node_prior_to_intersection, entering_via_edge, intersection_view);
|
||||
}
|
||||
|
||||
Intersection TurnAnalysis::AssignTurnTypes(const NodeID node_prior_to_intersection,
|
||||
const EdgeID entering_via_edge,
|
||||
const IntersectionView &intersection_view) const
|
||||
@ -191,19 +167,6 @@ Intersection TurnAnalysis::AssignTurnTypes(const NodeID node_prior_to_intersecti
|
||||
return intersection;
|
||||
}
|
||||
|
||||
TurnAnalysis::ShapeResult
|
||||
TurnAnalysis::ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const
|
||||
{
|
||||
ShapeResult intersection_shape;
|
||||
intersection_shape.intersection_shape =
|
||||
intersection_generator.ComputeIntersectionShape(node_at_center_of_intersection);
|
||||
|
||||
intersection_shape.annotated_normalized_shape = intersection_normalizer(
|
||||
node_at_center_of_intersection, intersection_shape.intersection_shape);
|
||||
|
||||
return intersection_shape;
|
||||
}
|
||||
|
||||
// Sets basic turn types as fallback for otherwise unhandled turns
|
||||
Intersection TurnAnalysis::setTurnTypes(const NodeID node_prior_to_intersection,
|
||||
const EdgeID,
|
||||
|
@ -228,7 +228,6 @@ Intersection triviallyMatchLanesToTurns(Intersection intersection,
|
||||
u_turn = 1;
|
||||
road_index = 2;
|
||||
}
|
||||
intersection[u_turn].entry_allowed = true;
|
||||
intersection[u_turn].instruction.type = TurnType::Continue;
|
||||
intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn;
|
||||
|
||||
@ -268,7 +267,6 @@ Intersection triviallyMatchLanesToTurns(Intersection intersection,
|
||||
}
|
||||
u_turn = intersection.size() - 1;
|
||||
}
|
||||
intersection[u_turn].entry_allowed = true;
|
||||
intersection[u_turn].instruction.type = TurnType::Continue;
|
||||
intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn;
|
||||
|
||||
|
@ -1,8 +1,13 @@
|
||||
#include "extractor/intersection/intersection_analysis.hpp"
|
||||
|
||||
#include "util/assert.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
|
||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||
|
||||
#include <boost/optional/optional_io.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
@ -38,14 +43,14 @@ IntersectionEdges getOutgoingEdges(const util::NodeBasedDynamicGraph &graph,
|
||||
|
||||
for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
|
||||
{
|
||||
if (!graph.GetEdgeData(outgoing_edge).reversed)
|
||||
// TODO: to use TurnAnalysis all outgoing edges are required, to be uncommented later
|
||||
// if (!graph.GetEdgeData(outgoing_edge).reversed)
|
||||
{
|
||||
result.push_back({intersection_node, outgoing_edge});
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce ordering of outgoing edges
|
||||
std::sort(result.begin(), result.end());
|
||||
BOOST_ASSERT(std::is_sorted(result.begin(), result.end()));
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -65,7 +70,7 @@ getEdgeCoordinates(const extractor::CompressedEdgeContainer &compressed_geometri
|
||||
// extracts the geometry in coordinates from the compressed edge container
|
||||
std::vector<util::Coordinate> result;
|
||||
const auto &geometry = compressed_geometries.GetBucketReference(edge);
|
||||
result.reserve(geometry.size() + 2);
|
||||
result.reserve(geometry.size() + 1);
|
||||
|
||||
result.push_back(node_coordinates[from_node]);
|
||||
std::transform(geometry.begin(),
|
||||
@ -74,67 +79,399 @@ getEdgeCoordinates(const extractor::CompressedEdgeContainer &compressed_geometri
|
||||
[&node_coordinates](const auto &compressed_edge) {
|
||||
return node_coordinates[compressed_edge.node_id];
|
||||
});
|
||||
result.push_back(node_coordinates[to_node]);
|
||||
|
||||
// filter duplicated coordinates
|
||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
IntersectionEdgeBearings
|
||||
getIntersectionBearings(const util::NodeBasedDynamicGraph &graph,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<util::Coordinate> &node_coordinates,
|
||||
const NodeID intersection_node)
|
||||
namespace
|
||||
{
|
||||
IntersectionEdgeBearings result;
|
||||
double findAngleBisector(double alpha, double beta)
|
||||
{
|
||||
alpha *= M_PI / 180.;
|
||||
beta *= M_PI / 180.;
|
||||
const auto average =
|
||||
180. * std::atan2(std::sin(alpha) + std::sin(beta), std::cos(alpha) + std::cos(beta)) /
|
||||
M_PI;
|
||||
return std::fmod(average + 360., 360.);
|
||||
}
|
||||
|
||||
double findClosestOppositeBearing(const IntersectionEdgeGeometries &edge_geometries,
|
||||
const double bearing)
|
||||
{
|
||||
BOOST_ASSERT(!edge_geometries.empty());
|
||||
const auto min = std::min_element(
|
||||
edge_geometries.begin(),
|
||||
edge_geometries.end(),
|
||||
[bearing = util::bearing::reverse(bearing)](const auto &lhs, const auto &rhs) {
|
||||
return util::angularDeviation(lhs.perceived_bearing, bearing) <
|
||||
util::angularDeviation(rhs.perceived_bearing, bearing);
|
||||
});
|
||||
return util::bearing::reverse(min->perceived_bearing);
|
||||
}
|
||||
|
||||
std::pair<bool, double> findMergedBearing(const util::NodeBasedDynamicGraph &graph,
|
||||
const IntersectionEdgeGeometries &edge_geometries,
|
||||
std::size_t lhs_index,
|
||||
std::size_t rhs_index,
|
||||
bool neighbor_intersection)
|
||||
{
|
||||
// Function returns a pair with a flag and a value of bearing for merged roads
|
||||
// If the flag is false the bearing must not be used as a merged value at neighbor intersections
|
||||
|
||||
using guidance::STRAIGHT_ANGLE;
|
||||
using guidance::MAXIMAL_ALLOWED_NO_TURN_DEVIATION;
|
||||
using util::bearing::angleBetween;
|
||||
using util::angularDeviation;
|
||||
|
||||
const auto &lhs = edge_geometries[lhs_index];
|
||||
const auto &rhs = edge_geometries[rhs_index];
|
||||
BOOST_ASSERT(graph.GetEdgeData(lhs.edge).reversed != graph.GetEdgeData(rhs.edge).reversed);
|
||||
|
||||
const auto &entry = graph.GetEdgeData(lhs.edge).reversed ? rhs : lhs;
|
||||
const auto opposite_bearing =
|
||||
findClosestOppositeBearing(edge_geometries, entry.perceived_bearing);
|
||||
const auto merged_bearing = findAngleBisector(rhs.perceived_bearing, lhs.perceived_bearing);
|
||||
|
||||
if (angularDeviation(angleBetween(opposite_bearing, entry.perceived_bearing), STRAIGHT_ANGLE) <
|
||||
MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
|
||||
{
|
||||
// In some intersections, turning roads can introduce artificial turns if we merge here.
|
||||
// Consider a scenario like:
|
||||
//
|
||||
// a . g - f
|
||||
// | .
|
||||
// | .
|
||||
// |.
|
||||
// d-b--------e
|
||||
// |
|
||||
// c
|
||||
//
|
||||
// Merging `bgf` and `be` would introduce an angle, even though d-b-e is perfectly straight
|
||||
// We don't change the angle, if such an opposite road exists
|
||||
return {false, entry.perceived_bearing};
|
||||
}
|
||||
|
||||
if (neighbor_intersection)
|
||||
{
|
||||
// Check that the merged bearing makes both turns closer to straight line
|
||||
const auto turn_angle_lhs = angleBetween(opposite_bearing, lhs.perceived_bearing);
|
||||
const auto turn_angle_rhs = angleBetween(opposite_bearing, rhs.perceived_bearing);
|
||||
const auto turn_angle_new = angleBetween(opposite_bearing, merged_bearing);
|
||||
|
||||
if (util::angularDeviation(turn_angle_lhs, STRAIGHT_ANGLE) <
|
||||
util::angularDeviation(turn_angle_new, STRAIGHT_ANGLE) ||
|
||||
util::angularDeviation(turn_angle_rhs, STRAIGHT_ANGLE) <
|
||||
util::angularDeviation(turn_angle_new, STRAIGHT_ANGLE))
|
||||
return {false, opposite_bearing};
|
||||
}
|
||||
|
||||
return {true, merged_bearing};
|
||||
}
|
||||
|
||||
bool isRoadsPairMergeable(const guidance::MergableRoadDetector &detector,
|
||||
const IntersectionEdgeGeometries &edge_geometries,
|
||||
const NodeID intersection_node,
|
||||
const std::size_t index)
|
||||
{
|
||||
const auto size = edge_geometries.size();
|
||||
BOOST_ASSERT(index < size);
|
||||
|
||||
const auto &llhs = edge_geometries[(index + size - 1) % size];
|
||||
const auto &lhs = edge_geometries[index];
|
||||
const auto &rhs = edge_geometries[(index + 1) % size];
|
||||
const auto &rrhs = edge_geometries[(index + 2) % size];
|
||||
|
||||
// TODO: check IsDistinctFrom - it is an angle and name-only check
|
||||
// also check CanMergeRoad for all merging scenarios
|
||||
return detector.IsDistinctFrom({llhs.edge, llhs.perceived_bearing, llhs.length},
|
||||
{lhs.edge, lhs.perceived_bearing, lhs.length}) &&
|
||||
detector.CanMergeRoad(intersection_node,
|
||||
{lhs.edge, lhs.perceived_bearing, lhs.length},
|
||||
{rhs.edge, rhs.perceived_bearing, rhs.length}) &&
|
||||
detector.IsDistinctFrom({rhs.edge, rhs.perceived_bearing, rhs.length},
|
||||
{rrhs.edge, rrhs.perceived_bearing, rrhs.length});
|
||||
}
|
||||
|
||||
auto getIntersectionLanes(const util::NodeBasedDynamicGraph &graph, const NodeID intersection_node)
|
||||
{
|
||||
std::uint8_t max_lanes_intersection = 0;
|
||||
for (auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
|
||||
{
|
||||
max_lanes_intersection =
|
||||
std::max(max_lanes_intersection,
|
||||
graph.GetEdgeData(outgoing_edge).flags.road_classification.GetNumberOfLanes());
|
||||
}
|
||||
return max_lanes_intersection;
|
||||
}
|
||||
|
||||
IntersectionEdgeGeometries
|
||||
getIntersectionOutgoingGeometries(const util::NodeBasedDynamicGraph &graph,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<util::Coordinate> &node_coordinates,
|
||||
const NodeID intersection_node)
|
||||
{
|
||||
IntersectionEdgeGeometries edge_geometries;
|
||||
|
||||
// TODO: keep CoordinateExtractor to reproduce bearings, simplify later
|
||||
const guidance::CoordinateExtractor coordinate_extractor(
|
||||
graph, compressed_geometries, node_coordinates);
|
||||
|
||||
const auto max_lanes_intersection = getIntersectionLanes(graph, intersection_node);
|
||||
|
||||
// Collect outgoing edges
|
||||
for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
|
||||
{
|
||||
const auto remote_node = graph.GetTarget(outgoing_edge);
|
||||
const auto incoming_edge = graph.FindEdge(remote_node, intersection_node);
|
||||
|
||||
const auto &geometry = getEdgeCoordinates(
|
||||
compressed_geometries, node_coordinates, intersection_node, outgoing_edge, remote_node);
|
||||
|
||||
// TODO: add MergableRoadDetector logic
|
||||
const auto outgoing_bearing =
|
||||
util::coordinate_calculation::bearing(geometry[0], geometry[1]);
|
||||
// OSRM_ASSERT(geometry.size() >= 2, node_coordinates[intersection_node]);
|
||||
|
||||
result.push_back({outgoing_edge, static_cast<float>(outgoing_bearing)});
|
||||
result.push_back(
|
||||
{incoming_edge, static_cast<float>(util::bearing::reverse(outgoing_bearing))});
|
||||
const auto close_coordinate =
|
||||
coordinate_extractor.ExtractCoordinateAtLength(2. /*m*/, geometry);
|
||||
const auto initial_bearing =
|
||||
util::coordinate_calculation::bearing(geometry[0], close_coordinate);
|
||||
|
||||
for (auto x : geometry)
|
||||
std::cout << x << ", ";
|
||||
std::cout << "\n";
|
||||
const auto representative_coordinate =
|
||||
graph.GetOutDegree(intersection_node) <= 2
|
||||
? coordinate_extractor.GetCoordinateCloseToTurn(
|
||||
intersection_node, outgoing_edge, false, remote_node)
|
||||
: coordinate_extractor.ExtractRepresentativeCoordinate(intersection_node,
|
||||
outgoing_edge,
|
||||
false,
|
||||
remote_node,
|
||||
max_lanes_intersection,
|
||||
geometry);
|
||||
const auto perceived_bearing =
|
||||
util::coordinate_calculation::bearing(geometry[0], representative_coordinate);
|
||||
|
||||
const auto edge_length = util::coordinate_calculation::getLength(
|
||||
geometry.begin(), geometry.end(), util::coordinate_calculation::haversineDistance);
|
||||
|
||||
edge_geometries.push_back({outgoing_edge, initial_bearing, perceived_bearing, edge_length});
|
||||
}
|
||||
|
||||
for (auto x : result)
|
||||
std::cout << x.edge << "," << x.bearing << "; ";
|
||||
std::cout << "\n";
|
||||
// TODO: remove to fix https://github.com/Project-OSRM/osrm-backend/issues/4704
|
||||
if (!edge_geometries.empty())
|
||||
{ // Adjust perceived bearings to keep the initial order with respect to the first edge
|
||||
// Sort geometries by initial bearings
|
||||
std::sort(edge_geometries.begin(),
|
||||
edge_geometries.end(),
|
||||
[base_bearing = util::bearing::reverse(edge_geometries.front().initial_bearing)](
|
||||
const auto &lhs, const auto &rhs) {
|
||||
return (util::bearing::angleBetween(lhs.initial_bearing, base_bearing) <
|
||||
util::bearing::angleBetween(rhs.initial_bearing, base_bearing)) ||
|
||||
(lhs.initial_bearing == rhs.initial_bearing &&
|
||||
util::bearing::angleBetween(lhs.perceived_bearing,
|
||||
rhs.perceived_bearing) < 180.);
|
||||
});
|
||||
|
||||
// Enforce ordering of edges
|
||||
std::sort(result.begin(), result.end());
|
||||
return result;
|
||||
// Make a bearings ordering functor
|
||||
const auto base_bearing = util::bearing::reverse(edge_geometries.front().perceived_bearing);
|
||||
const auto bearings_order = [base_bearing](const auto &lhs, const auto &rhs) {
|
||||
return util::bearing::angleBetween(lhs.perceived_bearing, base_bearing) <
|
||||
util::bearing::angleBetween(rhs.perceived_bearing, base_bearing);
|
||||
};
|
||||
|
||||
// Check the perceived bearings order is the same as the initial one
|
||||
for (auto curr = edge_geometries.begin(), next = std::next(curr);
|
||||
next != edge_geometries.end();
|
||||
++curr, ++next)
|
||||
{
|
||||
if (bearings_order(*next, *curr))
|
||||
{ // If the true bearing is out of the initial order (next before current) then
|
||||
// adjust the next bearing to keep the order. The adjustment angle is at most
|
||||
// 0.5° or a half-angle between the current bearing and the base bearing.
|
||||
// to prevent overlapping over base bearing + 360°.
|
||||
const auto angle_adjustment = std::min(
|
||||
.5,
|
||||
util::restrictAngleToValidRange(base_bearing - curr->perceived_bearing) / 2.);
|
||||
next->perceived_bearing =
|
||||
util::restrictAngleToValidRange(curr->perceived_bearing + angle_adjustment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return edge_geometries;
|
||||
}
|
||||
}
|
||||
|
||||
auto findEdgeBearing(const IntersectionEdgeBearings &bearings, const EdgeID &edge)
|
||||
std::pair<IntersectionEdgeGeometries, std::unordered_set<EdgeID>>
|
||||
getIntersectionGeometries(const util::NodeBasedDynamicGraph &graph,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<util::Coordinate> &node_coordinates,
|
||||
const guidance::MergableRoadDetector &detector,
|
||||
const NodeID intersection_node)
|
||||
{
|
||||
IntersectionEdgeGeometries edge_geometries = getIntersectionOutgoingGeometries(
|
||||
graph, compressed_geometries, node_coordinates, intersection_node);
|
||||
|
||||
const auto edges_number = edge_geometries.size();
|
||||
|
||||
std::vector<bool> merged_edges(edges_number, false);
|
||||
|
||||
// TODO: intersection views do not contain merged and not allowed edges
|
||||
// but contain other restricted edges that are used in TurnAnalysis,
|
||||
// to be deleted after TurnAnalysis refactoring
|
||||
std::unordered_set<EdgeID> merged_edge_ids;
|
||||
|
||||
if (edges_number >= 3)
|
||||
{ // Adjust bearings of mergeable roads
|
||||
for (std::size_t index = 0; index < edges_number; ++index)
|
||||
{
|
||||
if (isRoadsPairMergeable(detector, edge_geometries, intersection_node, index))
|
||||
{ // Merge bearings of roads left & right
|
||||
const auto next = (index + 1) % edges_number;
|
||||
auto &lhs = edge_geometries[index];
|
||||
auto &rhs = edge_geometries[next];
|
||||
merged_edges[index] = true;
|
||||
merged_edges[next] = true;
|
||||
|
||||
const auto merge = findMergedBearing(graph, edge_geometries, index, next, false);
|
||||
|
||||
lhs.perceived_bearing = merge.second;
|
||||
rhs.perceived_bearing = merge.second;
|
||||
|
||||
// Only one of the edges must be reversed, mark it as merged to remove from
|
||||
// intersection view
|
||||
BOOST_ASSERT(graph.GetEdgeData(lhs.edge).reversed ^
|
||||
graph.GetEdgeData(rhs.edge).reversed);
|
||||
merged_edge_ids.insert(graph.GetEdgeData(lhs.edge).reversed ? lhs.edge : rhs.edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (edges_number >= 2)
|
||||
{ // Adjust bearings of roads that will be merged at the neighbor intersections
|
||||
const double constexpr PRUNING_DISTANCE = 30.;
|
||||
|
||||
for (std::size_t index = 0; index < edges_number; ++index)
|
||||
{
|
||||
auto &edge_geometry = edge_geometries[index];
|
||||
|
||||
// Don't adjust bearings of roads that were merged at the current intersection
|
||||
// or have neighbor intersection farer than the pruning distance
|
||||
if (merged_edges[index] || edge_geometry.length > PRUNING_DISTANCE)
|
||||
continue;
|
||||
|
||||
const auto neighbor_intersection_node = graph.GetTarget(edge_geometry.edge);
|
||||
|
||||
const auto neighbor_geometries = getIntersectionOutgoingGeometries(
|
||||
graph, compressed_geometries, node_coordinates, neighbor_intersection_node);
|
||||
|
||||
const auto neighbor_edges = neighbor_geometries.size();
|
||||
if (neighbor_edges <= 1)
|
||||
continue;
|
||||
|
||||
const auto neighbor_curr = std::distance(
|
||||
neighbor_geometries.begin(),
|
||||
std::find_if(neighbor_geometries.begin(),
|
||||
neighbor_geometries.end(),
|
||||
[&graph, &intersection_node](const auto &road) {
|
||||
return graph.GetTarget(road.edge) == intersection_node;
|
||||
}));
|
||||
BOOST_ASSERT(static_cast<std::size_t>(neighbor_curr) != neighbor_geometries.size());
|
||||
const auto neighbor_prev = (neighbor_curr + neighbor_edges - 1) % neighbor_edges;
|
||||
const auto neighbor_next = (neighbor_curr + 1) % neighbor_edges;
|
||||
|
||||
if (isRoadsPairMergeable(
|
||||
detector, neighbor_geometries, neighbor_intersection_node, neighbor_prev))
|
||||
{ // Neighbor intersection has mergable neighbor_prev and neighbor_curr roads
|
||||
BOOST_ASSERT(!isRoadsPairMergeable(
|
||||
detector, neighbor_geometries, neighbor_intersection_node, neighbor_curr));
|
||||
|
||||
// TODO: merge with an angle bisector, but not a reversed closed turn, to be
|
||||
// checked as a difference with the previous implementation
|
||||
const auto merge = findMergedBearing(
|
||||
graph, neighbor_geometries, neighbor_prev, neighbor_curr, true);
|
||||
|
||||
if (merge.first)
|
||||
{
|
||||
const auto offset = util::angularDeviation(
|
||||
merge.second, neighbor_geometries[neighbor_curr].perceived_bearing);
|
||||
|
||||
// Adjust bearing of AB at the node A if at the node B roads BA (neighbor_curr)
|
||||
// and BC (neighbor_prev) will be merged and will have merged bearing Bb.
|
||||
// The adjustment value is ∠bBA with negative sign (counter-clockwise) to Aa
|
||||
// A ~~~ a
|
||||
// \
|
||||
// b --- B ---
|
||||
// /
|
||||
// C
|
||||
edge_geometry.perceived_bearing =
|
||||
std::fmod(edge_geometry.perceived_bearing + 360. - offset, 360.);
|
||||
}
|
||||
}
|
||||
else if (isRoadsPairMergeable(
|
||||
detector, neighbor_geometries, neighbor_intersection_node, neighbor_curr))
|
||||
{ // Neighbor intersection has mergable neighbor_curr and neighbor_next roads
|
||||
BOOST_ASSERT(!isRoadsPairMergeable(
|
||||
detector, neighbor_geometries, neighbor_intersection_node, neighbor_prev));
|
||||
|
||||
// TODO: merge with an angle bisector, but not a reversed closed turn, to be
|
||||
// checked as a difference with the previous implementation
|
||||
const auto merge = findMergedBearing(
|
||||
graph, neighbor_geometries, neighbor_curr, neighbor_next, true);
|
||||
if (merge.first)
|
||||
{
|
||||
const auto offset = util::angularDeviation(
|
||||
merge.second, neighbor_geometries[neighbor_curr].perceived_bearing);
|
||||
|
||||
// Adjust bearing of AB at the node A if at the node B roads BA (neighbor_curr)
|
||||
// and BC (neighbor_next) will be merged and will have merged bearing Bb.
|
||||
// The adjustment value is ∠bBA with positive sign (clockwise) to Aa
|
||||
// a ~~~ A
|
||||
// /
|
||||
// --- B --- b
|
||||
// \
|
||||
// C
|
||||
edge_geometry.perceived_bearing =
|
||||
std::fmod(edge_geometry.perceived_bearing + offset, 360.);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add incoming edges with reversed bearings
|
||||
edge_geometries.resize(2 * edges_number);
|
||||
for (std::size_t index = 0; index < edges_number; ++index)
|
||||
{
|
||||
const auto &geometry = edge_geometries[index];
|
||||
const auto remote_node = graph.GetTarget(geometry.edge);
|
||||
const auto incoming_edge = graph.FindEdge(remote_node, intersection_node);
|
||||
edge_geometries[edges_number + index] = {incoming_edge,
|
||||
util::bearing::reverse(geometry.initial_bearing),
|
||||
util::bearing::reverse(geometry.perceived_bearing),
|
||||
geometry.length};
|
||||
}
|
||||
|
||||
// Enforce ordering of edges by IDs
|
||||
std::sort(edge_geometries.begin(), edge_geometries.end());
|
||||
|
||||
return std::make_pair(edge_geometries, merged_edge_ids);
|
||||
}
|
||||
|
||||
inline auto findEdge(const IntersectionEdgeGeometries &geometries, const EdgeID &edge)
|
||||
{
|
||||
const auto it = std::lower_bound(
|
||||
bearings.begin(), bearings.end(), edge, [](const auto &edge_bearing, const auto edge) {
|
||||
return edge_bearing.edge < edge;
|
||||
geometries.begin(), geometries.end(), edge, [](const auto &geometry, const auto edge) {
|
||||
return geometry.edge < edge;
|
||||
});
|
||||
BOOST_ASSERT(it != bearings.end() && it->edge == edge);
|
||||
return it->bearing;
|
||||
BOOST_ASSERT(it != geometries.end() && it->edge == edge);
|
||||
return it;
|
||||
}
|
||||
|
||||
double computeTurnAngle(const IntersectionEdgeBearings &bearings,
|
||||
const IntersectionEdge &from,
|
||||
const IntersectionEdge &to)
|
||||
double findEdgeBearing(const IntersectionEdgeGeometries &geometries, const EdgeID &edge)
|
||||
{
|
||||
return util::bearing::angleBetween(findEdgeBearing(bearings, from.edge),
|
||||
findEdgeBearing(bearings, to.edge));
|
||||
return findEdge(geometries, edge)->perceived_bearing;
|
||||
}
|
||||
|
||||
double findEdgeLength(const IntersectionEdgeGeometries &geometries, const EdgeID &edge)
|
||||
{
|
||||
return findEdge(geometries, edge)->length;
|
||||
}
|
||||
|
||||
template <typename RestrictionsRange>
|
||||
@ -164,13 +501,17 @@ bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const IntersectionEdgeBearings &bearings,
|
||||
const IntersectionEdgeGeometries &geometries,
|
||||
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||
const IntersectionEdge &from,
|
||||
const IntersectionEdge &to)
|
||||
{
|
||||
BOOST_ASSERT(graph.GetTarget(from.edge) == to.node);
|
||||
|
||||
// TODO: to use TurnAnalysis all outgoing edges are required, to be removed later
|
||||
if (graph.GetEdgeData(from.edge).reversed || graph.GetEdgeData(to.edge).reversed)
|
||||
return false;
|
||||
|
||||
const auto intersection_node = to.node;
|
||||
const auto destination_node = graph.GetTarget(to.edge);
|
||||
auto const &restrictions = restriction_map.Restrictions(from.node, intersection_node);
|
||||
@ -181,7 +522,7 @@ bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
|
||||
|
||||
// Precompute reversed bearing of the `from` edge
|
||||
const auto from_edge_reversed_bearing =
|
||||
util::bearing::reverse(findEdgeBearing(bearings, from.edge));
|
||||
util::bearing::reverse(findEdgeBearing(geometries, from.edge));
|
||||
|
||||
// Collect some information about the intersection
|
||||
// 1) number of allowed exits and adjacent bidirectional edges
|
||||
@ -210,7 +551,7 @@ bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
|
||||
// "Linked Roundabouts" is an example of tie between two linked roundabouts
|
||||
// A tie breaker for that maximizes ∠(roundabout_from_bearing, ¬from_edge_bearing)
|
||||
const auto angle = util::bearing::angleBetween(
|
||||
findEdgeBearing(bearings, reverse_edge), from_edge_reversed_bearing);
|
||||
findEdgeBearing(geometries, reverse_edge), from_edge_reversed_bearing);
|
||||
if (angle > roundabout_from_angle)
|
||||
{
|
||||
roundabout_from = reverse_edge;
|
||||
@ -221,7 +562,7 @@ bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
|
||||
{
|
||||
// a tie breaker that maximizes ∠(¬from_edge_bearing, roundabout_to_bearing)
|
||||
const auto angle = util::bearing::angleBetween(from_edge_reversed_bearing,
|
||||
findEdgeBearing(bearings, eid));
|
||||
findEdgeBearing(geometries, eid));
|
||||
if (angle > roundabout_to_angle)
|
||||
{
|
||||
roundabout_to = eid;
|
||||
@ -246,7 +587,7 @@ bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
|
||||
return true;
|
||||
|
||||
// Allow U-turns at dead-ends if there is at most one bidirectional road at the intersection
|
||||
// The condition allows a U-turns d→a→d and c→b→c ("Bike - Around the Block" test)
|
||||
// The condition allows U-turns d→a→d and c→b→c ("Bike - Around the Block" test)
|
||||
// a→b
|
||||
// ↕ ↕
|
||||
// d↔c
|
||||
@ -281,9 +622,9 @@ bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
|
||||
if (roundabout_from != SPECIAL_EDGEID && roundabout_to != SPECIAL_EDGEID)
|
||||
{
|
||||
// Get bearings of edges
|
||||
const auto roundabout_from_bearing = findEdgeBearing(bearings, roundabout_from);
|
||||
const auto roundabout_to_bearing = findEdgeBearing(bearings, roundabout_to);
|
||||
const auto to_bearing = findEdgeBearing(bearings, to.edge);
|
||||
const auto roundabout_from_bearing = findEdgeBearing(geometries, roundabout_from);
|
||||
const auto roundabout_to_bearing = findEdgeBearing(geometries, roundabout_to);
|
||||
const auto to_edge_bearing = findEdgeBearing(geometries, to.edge);
|
||||
|
||||
// Get angles from the roundabout edge to three other edges
|
||||
const auto roundabout_angle =
|
||||
@ -291,10 +632,14 @@ bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
|
||||
const auto roundabout_from_angle =
|
||||
util::bearing::angleBetween(roundabout_from_bearing, from_edge_reversed_bearing);
|
||||
const auto roundabout_to_angle =
|
||||
util::bearing::angleBetween(roundabout_from_bearing, to_bearing);
|
||||
util::bearing::angleBetween(roundabout_from_bearing, to_edge_bearing);
|
||||
|
||||
// Restrict turning over a roundabout if `roundabout_to_angle` is in
|
||||
// a sector between `roundabout_from_bearing` to `from_bearing`
|
||||
// a sector between `roundabout_from_bearing` to `from_bearing` (shaded area)
|
||||
//
|
||||
// roundabout_angle = 270° roundabout_angle = 90°
|
||||
// roundabout_from_angle = 150° roundabout_from_angle = 150°
|
||||
// roundabout_to_angle = 90° roundabout_to_angle = 270°
|
||||
//
|
||||
// 150° 150°
|
||||
// v░░░░░░ ░░░░░░░░░v
|
||||
@ -312,6 +657,76 @@ bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: the function adapts intersection geometry data to TurnAnalysis
|
||||
guidance::IntersectionView
|
||||
convertToIntersectionView(const util::NodeBasedDynamicGraph &graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const IntersectionEdgeGeometries &edge_geometries,
|
||||
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||
const IntersectionEdge &incoming_edge,
|
||||
const IntersectionEdges &outgoing_edges,
|
||||
const std::unordered_set<EdgeID> &merged_edges)
|
||||
{
|
||||
const auto incoming_bearing = findEdgeBearing(edge_geometries, incoming_edge.edge);
|
||||
|
||||
guidance::IntersectionView intersection_view;
|
||||
guidance::IntersectionViewData uturn{{SPECIAL_EDGEID, 0., 0.}, false, 0.};
|
||||
std::size_t allowed_uturns_number = 0;
|
||||
for (const auto &outgoing_edge : outgoing_edges)
|
||||
{
|
||||
const auto is_uturn = [](const auto angle) {
|
||||
return std::fabs(angle) < std::numeric_limits<double>::epsilon();
|
||||
};
|
||||
|
||||
const auto edge_it = findEdge(edge_geometries, outgoing_edge.edge);
|
||||
const auto outgoing_bearing = edge_it->perceived_bearing;
|
||||
const auto initial_outgoing_bearing = edge_it->initial_bearing;
|
||||
const auto segment_length = edge_it->length;
|
||||
const auto turn_angle = std::fmod(
|
||||
std::round(util::bearing::angleBetween(incoming_bearing, outgoing_bearing) * 1e8) / 1e8,
|
||||
360.);
|
||||
const auto is_turn_allowed = intersection::isTurnAllowed(graph,
|
||||
node_data_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
edge_geometries,
|
||||
turn_lanes_data,
|
||||
incoming_edge,
|
||||
outgoing_edge);
|
||||
const auto is_uturn_angle = is_uturn(turn_angle);
|
||||
const auto is_merged = merged_edges.count(outgoing_edge.edge) != 0;
|
||||
|
||||
guidance::IntersectionViewData road{
|
||||
{outgoing_edge.edge, outgoing_bearing, segment_length}, is_turn_allowed, turn_angle};
|
||||
|
||||
if (graph.GetTarget(outgoing_edge.edge) == incoming_edge.node)
|
||||
{ // Save the true U-turn road to add later if no allowed U-turns will be added
|
||||
uturn = road;
|
||||
}
|
||||
else if (is_turn_allowed || (!is_merged && !is_uturn_angle))
|
||||
{ // Add roads that have allowed entry or not U-turns and not merged
|
||||
allowed_uturns_number += is_uturn_angle;
|
||||
|
||||
intersection_view.push_back(road);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_ASSERT(uturn.eid != SPECIAL_EDGEID);
|
||||
if (uturn.entry_allowed || allowed_uturns_number == 0)
|
||||
{ // Add the true U-turn if it is allowed or no other U-turns found
|
||||
intersection_view.insert(intersection_view.begin(), uturn);
|
||||
}
|
||||
|
||||
// Order roads in counter-clockwise order starting from the U-turn edge
|
||||
std::sort(intersection_view.begin(),
|
||||
intersection_view.end(),
|
||||
[](const auto &lhs, const auto &rhs) { return lhs.angle < rhs.angle; });
|
||||
|
||||
return intersection_view;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,10 +26,8 @@ namespace
|
||||
// creates a default edge of unit weight
|
||||
inline InputEdge MakeUnitEdge(const NodeID from, const NodeID to)
|
||||
{
|
||||
// src, tgt, dist, edge_id, name_id, fwd, bkwd, roundabout, circular, startpoint, local access,
|
||||
// split edge, travel_mode
|
||||
return {from,
|
||||
to,
|
||||
return {from, // source
|
||||
to, // target
|
||||
1, // weight
|
||||
1, // duration
|
||||
GeometryID{0, false}, // geometry_id
|
||||
|
255
unit_tests/extractor/intersection_analysis_tests.cpp
Normal file
255
unit_tests/extractor/intersection_analysis_tests.cpp
Normal file
@ -0,0 +1,255 @@
|
||||
#include "extractor/intersection/intersection_analysis.hpp"
|
||||
|
||||
#include "extractor/graph_compressor.hpp"
|
||||
|
||||
#include "../common/range_tools.hpp"
|
||||
#include "../unit_tests/mocks/mock_scripting_environment.hpp"
|
||||
|
||||
#include <boost/test/test_case_template.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(intersection_analysis_tests)
|
||||
|
||||
using namespace osrm;
|
||||
using namespace osrm::extractor;
|
||||
using namespace osrm::extractor::guidance;
|
||||
using namespace osrm::extractor::intersection;
|
||||
using InputEdge = util::NodeBasedDynamicGraph::InputEdge;
|
||||
using Graph = util::NodeBasedDynamicGraph;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(simple_intersection_connectivity)
|
||||
{
|
||||
std::unordered_set<NodeID> barrier_nodes{6};
|
||||
std::unordered_set<NodeID> traffic_lights;
|
||||
std::vector<NodeBasedEdgeAnnotation> annotations{
|
||||
{EMPTY_NAMEID, 0, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false},
|
||||
{EMPTY_NAMEID, 1, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}};
|
||||
std::vector<TurnRestriction> restrictions{TurnRestriction{NodeRestriction{0, 2, 1}, false}};
|
||||
std::vector<ConditionalTurnRestriction> conditional_restrictions;
|
||||
CompressedEdgeContainer container;
|
||||
test::MockScriptingEnvironment scripting_environment;
|
||||
|
||||
TurnLanesIndexedArray turn_lanes_data{{0, 0, 3},
|
||||
{TurnLaneType::uturn | TurnLaneType::left,
|
||||
TurnLaneType::straight,
|
||||
TurnLaneType::straight | TurnLaneType::right}};
|
||||
|
||||
// Graph with an additional turn restriction 0→2→1 and bollard at 6
|
||||
// 0→5↔6↔7
|
||||
// ↕
|
||||
// 1↔2←3
|
||||
// ↓
|
||||
// 4
|
||||
const auto unit_edge =
|
||||
[](const NodeID from, const NodeID to, bool allowed, AnnotationID annotation) {
|
||||
return InputEdge{from,
|
||||
to,
|
||||
1,
|
||||
1,
|
||||
GeometryID{0, false},
|
||||
!allowed,
|
||||
NodeBasedEdgeClassification(),
|
||||
annotation};
|
||||
};
|
||||
|
||||
std::vector<InputEdge> edges = {unit_edge(0, 2, true, 1),
|
||||
unit_edge(0, 5, true, 0),
|
||||
unit_edge(1, 2, true, 0),
|
||||
unit_edge(2, 0, true, 0),
|
||||
unit_edge(2, 1, true, 0),
|
||||
unit_edge(2, 3, false, 0),
|
||||
unit_edge(2, 4, true, 0),
|
||||
unit_edge(3, 2, true, 0),
|
||||
unit_edge(4, 2, false, 0),
|
||||
unit_edge(5, 0, false, 0),
|
||||
unit_edge(5, 6, true, 0),
|
||||
unit_edge(6, 5, true, 0),
|
||||
unit_edge(6, 7, true, 0),
|
||||
unit_edge(7, 6, true, 0)};
|
||||
IntersectionEdgeGeometries edge_geometries{
|
||||
{0, 180, 180, 10.}, // 0→2
|
||||
{1, 90, 90, 10.}, // 0→5
|
||||
{2, 90, 90, 10.}, // 1→2
|
||||
{3, 0, 0, 10.}, // 2→0
|
||||
{4, 270, 270, 10.}, // 2→1
|
||||
{5, 90, 90, 10.}, // 2→3
|
||||
{6, 180, 180, 10.}, // 2→4
|
||||
{7, 270, 270, 10.}, // 3→2
|
||||
{8, 0, 0, 10.}, // 4→2
|
||||
{9, 270, 270, 10.}, // 5→0
|
||||
{10, 90, 90, 10.}, // 5→6
|
||||
{11, 270, 270, 10.}, // 6→5
|
||||
{12, 90, 90, 10.}, // 6→7
|
||||
{13, 270, 270, 10.} // 7→6
|
||||
};
|
||||
|
||||
Graph graph(8, edges);
|
||||
|
||||
GraphCompressor().Compress(barrier_nodes,
|
||||
traffic_lights,
|
||||
scripting_environment,
|
||||
restrictions,
|
||||
conditional_restrictions,
|
||||
graph,
|
||||
annotations,
|
||||
container);
|
||||
|
||||
REQUIRE_SIZE_RANGE(getIncomingEdges(graph, 2), 3);
|
||||
REQUIRE_SIZE_RANGE(getOutgoingEdges(graph, 2), 4);
|
||||
|
||||
EdgeBasedNodeDataContainer node_data_container(
|
||||
std::vector<EdgeBasedNode>(graph.GetNumberOfEdges()), annotations);
|
||||
RestrictionMap restriction_map(restrictions, IndexNodeByFromAndVia());
|
||||
|
||||
const auto connectivity_matrix = [&](NodeID node) {
|
||||
std::vector<bool> result;
|
||||
const auto incoming_edges = getIncomingEdges(graph, node);
|
||||
const auto outgoing_edges = getOutgoingEdges(graph, node);
|
||||
for (const auto incoming_edge : incoming_edges)
|
||||
{
|
||||
for (const auto outgoing_edge : outgoing_edges)
|
||||
{
|
||||
result.push_back(isTurnAllowed(graph,
|
||||
node_data_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
edge_geometries,
|
||||
turn_lanes_data,
|
||||
incoming_edge,
|
||||
outgoing_edge));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(0), 1, 1); // from node 2 allowed U-turn and to node 5
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(1), 1); // from node 2 allowed U-turn
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(2),
|
||||
// clang-format off
|
||||
1, 0, 0, 1, // from node 0 to node 4 and a U-turn at 2
|
||||
1, 0, 0, 1, // from node 1 to nodes 0 and 4
|
||||
1, 1, 0, 1 // from node 3 to nodes 0, 1 and 4
|
||||
// clang-format on
|
||||
);
|
||||
REQUIRE_SIZE_RANGE(connectivity_matrix(3), 0); // no incoming edges, empty matrix
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(4), 0); // from node 2 not allowed U-turn
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(5),
|
||||
// clang-format off
|
||||
0, 1, // from node 0 to node 6
|
||||
0, 1, // from node 6 a U-turn to node 6
|
||||
// clang-format on
|
||||
);
|
||||
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(6),
|
||||
// clang-format off
|
||||
1, 0, // from node 5 a U-turn to node 5
|
||||
0, 1, // from node 7 a U-turn to node 7
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity)
|
||||
{
|
||||
std::unordered_set<NodeID> barrier_nodes;
|
||||
std::unordered_set<NodeID> traffic_lights;
|
||||
std::vector<NodeBasedEdgeAnnotation> annotations;
|
||||
std::vector<TurnRestriction> restrictions;
|
||||
std::vector<ConditionalTurnRestriction> conditional_restrictions;
|
||||
CompressedEdgeContainer container;
|
||||
test::MockScriptingEnvironment scripting_environment;
|
||||
|
||||
TurnLanesIndexedArray turn_lanes_data;
|
||||
|
||||
// Graph with roundabout edges 5→0→2
|
||||
// 1 2 3
|
||||
// ↘ ↑ ↙
|
||||
// 0
|
||||
// ↙ ↑ ↘
|
||||
// 4 5 6
|
||||
const auto unit_edge = [](const NodeID from, const NodeID to, bool allowed, bool roundabout) {
|
||||
return InputEdge{
|
||||
from,
|
||||
to,
|
||||
1,
|
||||
1,
|
||||
GeometryID{0, false},
|
||||
!allowed,
|
||||
NodeBasedEdgeClassification{true, false, false, roundabout, false, false, false, {}},
|
||||
0};
|
||||
};
|
||||
std::vector<InputEdge> edges = {unit_edge(0, 1, false, false),
|
||||
unit_edge(0, 2, true, true),
|
||||
unit_edge(0, 3, false, false),
|
||||
unit_edge(0, 4, true, false),
|
||||
unit_edge(0, 5, false, true),
|
||||
unit_edge(0, 6, true, false),
|
||||
unit_edge(1, 0, true, false),
|
||||
unit_edge(2, 0, false, true),
|
||||
unit_edge(3, 0, true, false),
|
||||
unit_edge(4, 0, false, false),
|
||||
unit_edge(5, 0, true, true),
|
||||
unit_edge(6, 0, false, false)};
|
||||
IntersectionEdgeGeometries edge_geometries{
|
||||
{0, 315, 315, 10}, // 0→1
|
||||
{1, 0, 0, 10}, // 0→2
|
||||
{2, 45, 45, 10}, // 0→3
|
||||
{3, 225, 225, 10}, // 0→4
|
||||
{4, 180, 180, 10}, // 0→5
|
||||
{5, 135, 135, 10}, // 0→6
|
||||
{6, 135, 135, 10}, // 1→0
|
||||
{7, 180, 180, 10}, // 2→0
|
||||
{8, 225, 225, 10}, // 3→0
|
||||
{9, 45, 45, 10}, // 4→0
|
||||
{10, 0, 0, 10}, // 5→0
|
||||
{11, 315, 315, 10} // 6→0
|
||||
};
|
||||
|
||||
Graph graph(7, edges);
|
||||
|
||||
GraphCompressor().Compress(barrier_nodes,
|
||||
traffic_lights,
|
||||
scripting_environment,
|
||||
restrictions,
|
||||
conditional_restrictions,
|
||||
graph,
|
||||
annotations,
|
||||
container);
|
||||
|
||||
REQUIRE_SIZE_RANGE(getIncomingEdges(graph, 0), 3);
|
||||
REQUIRE_SIZE_RANGE(getOutgoingEdges(graph, 0), 6);
|
||||
|
||||
EdgeBasedNodeDataContainer node_data_container(
|
||||
std::vector<EdgeBasedNode>(graph.GetNumberOfEdges()), annotations);
|
||||
RestrictionMap restriction_map(restrictions, IndexNodeByFromAndVia());
|
||||
|
||||
const auto connectivity_matrix = [&](NodeID node) {
|
||||
std::vector<bool> result;
|
||||
const auto incoming_edges = getIncomingEdges(graph, node);
|
||||
const auto outgoing_edges = getOutgoingEdges(graph, node);
|
||||
for (const auto incoming_edge : incoming_edges)
|
||||
{
|
||||
for (const auto outgoing_edge : outgoing_edges)
|
||||
{
|
||||
result.push_back(isTurnAllowed(graph,
|
||||
node_data_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
edge_geometries,
|
||||
turn_lanes_data,
|
||||
incoming_edge,
|
||||
outgoing_edge));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(0),
|
||||
// clang-format off
|
||||
0, 1, 0, 0, 0, 1, // from node 1 to nodes 2 and 6
|
||||
0, 1, 0, 1, 0, 0, // from node 3 to nodes 2 and 4
|
||||
0, 1, 0, 1, 0, 1 // from node 5 to nodes 2, 4 and 6
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
Loading…
Reference in New Issue
Block a user