Free functions for guidance intersections analysis
This commit is contained in:
		
							parent
							
								
									3c3322173c
								
							
						
					
					
						commit
						9c033ff461
					
				@ -100,8 +100,10 @@ typedef util::ConcurrentIDMap<guidance::TurnLaneDescription,
 | 
			
		||||
                              guidance::TurnLaneDescription_hash>
 | 
			
		||||
    LaneDescriptionMap;
 | 
			
		||||
 | 
			
		||||
inline std::tuple<std::vector<std::uint32_t>, std::vector<TurnLaneType::Mask>>
 | 
			
		||||
transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map)
 | 
			
		||||
using TurnLanesIndexedArray =
 | 
			
		||||
    std::tuple<std::vector<std::uint32_t>, std::vector<TurnLaneType::Mask>>;
 | 
			
		||||
 | 
			
		||||
inline TurnLanesIndexedArray transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map)
 | 
			
		||||
{
 | 
			
		||||
    // could use some additional capacity? To avoid a copy during processing, though small data so
 | 
			
		||||
    // probably not that important.
 | 
			
		||||
@ -111,8 +113,7 @@ transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map)
 | 
			
		||||
    //
 | 
			
		||||
    // turn lane offsets points into the locations of the turn_lane_masks array. We use a standard
 | 
			
		||||
    // adjacency array like structure to store the turn lane masks.
 | 
			
		||||
    std::vector<std::uint32_t> turn_lane_offsets(turn_lane_map.data.size() +
 | 
			
		||||
                                                 2); // empty ID + sentinel
 | 
			
		||||
    std::vector<std::uint32_t> turn_lane_offsets(turn_lane_map.data.size() + 1); // + sentinel
 | 
			
		||||
    for (auto entry = turn_lane_map.data.begin(); entry != turn_lane_map.data.end(); ++entry)
 | 
			
		||||
        turn_lane_offsets[entry->second + 1] = entry->first.size();
 | 
			
		||||
 | 
			
		||||
@ -125,6 +126,7 @@ transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map)
 | 
			
		||||
        std::copy(entry->first.begin(),
 | 
			
		||||
                  entry->first.end(),
 | 
			
		||||
                  turn_lane_masks.begin() + turn_lane_offsets[entry->second]);
 | 
			
		||||
 | 
			
		||||
    return std::make_tuple(std::move(turn_lane_offsets), std::move(turn_lane_masks));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										50
									
								
								include/extractor/intersection/intersection_analysis.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								include/extractor/intersection/intersection_analysis.hpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
#ifndef OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_ANALYSIS_HPP
 | 
			
		||||
#define OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_ANALYSIS_HPP
 | 
			
		||||
 | 
			
		||||
#include "extractor/compressed_edge_container.hpp"
 | 
			
		||||
#include "extractor/guidance/turn_lane_types.hpp"
 | 
			
		||||
#include "extractor/intersection/intersection_edge.hpp"
 | 
			
		||||
#include "extractor/restriction_index.hpp"
 | 
			
		||||
 | 
			
		||||
#include "util/coordinate.hpp"
 | 
			
		||||
#include "util/node_based_graph.hpp"
 | 
			
		||||
 | 
			
		||||
#include <unordered_set>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace osrm
 | 
			
		||||
{
 | 
			
		||||
namespace extractor
 | 
			
		||||
{
 | 
			
		||||
namespace intersection
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
IntersectionEdges getIncomingEdges(const util::NodeBasedDynamicGraph &graph,
 | 
			
		||||
                                   const NodeID intersection);
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
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 guidance::TurnLanesIndexedArray &turn_lanes_data,
 | 
			
		||||
                   const IntersectionEdge &from,
 | 
			
		||||
                   const IntersectionEdge &to);
 | 
			
		||||
 | 
			
		||||
double computeTurnAngle(const IntersectionEdgeBearings &bearings,
 | 
			
		||||
                        const IntersectionEdge &from,
 | 
			
		||||
                        const IntersectionEdge &to);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										42
									
								
								include/extractor/intersection/intersection_edge.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								include/extractor/intersection/intersection_edge.hpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
#ifndef OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_EDGE_HPP
 | 
			
		||||
#define OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_EDGE_HPP
 | 
			
		||||
 | 
			
		||||
#include "util/typedefs.hpp"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace osrm
 | 
			
		||||
{
 | 
			
		||||
namespace extractor
 | 
			
		||||
{
 | 
			
		||||
namespace intersection
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
// IntersectionEdge is an alias for incoming and outgoing node-based graph edges of an intersection
 | 
			
		||||
struct IntersectionEdge
 | 
			
		||||
{
 | 
			
		||||
    NodeID node;
 | 
			
		||||
    EdgeID edge;
 | 
			
		||||
 | 
			
		||||
    bool operator<(const IntersectionEdge &other) const
 | 
			
		||||
    {
 | 
			
		||||
        return std::tie(node, edge) < std::tie(other.node, other.edge);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using IntersectionEdges = std::vector<IntersectionEdge>;
 | 
			
		||||
 | 
			
		||||
struct IntersectionEdgeBearing
 | 
			
		||||
{
 | 
			
		||||
    EdgeID edge;
 | 
			
		||||
    float bearing;
 | 
			
		||||
 | 
			
		||||
    bool operator<(const IntersectionEdgeBearing &other) const { return edge < other.edge; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using IntersectionEdgeBearings = std::vector<IntersectionEdgeBearing>;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@ -144,18 +144,6 @@ inline double restrictAngleToValidRange(const double angle)
 | 
			
		||||
        return angle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// finds the angle between two angles, based on the minum difference between the two
 | 
			
		||||
inline double angleBetween(const double lhs, const double rhs)
 | 
			
		||||
{
 | 
			
		||||
    const auto difference = std::abs(lhs - rhs);
 | 
			
		||||
    const auto is_clockwise_difference = difference <= 180;
 | 
			
		||||
    const auto angle_between_candidate = .5 * (lhs + rhs);
 | 
			
		||||
    if (is_clockwise_difference)
 | 
			
		||||
        return angle_between_candidate;
 | 
			
		||||
    else
 | 
			
		||||
        return restrictAngleToValidRange(angle_between_candidate + 180);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace util
 | 
			
		||||
} // namespace osrm
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,8 @@
 | 
			
		||||
#include "extractor/scripting_environment.hpp"
 | 
			
		||||
#include "extractor/suffix_table.hpp"
 | 
			
		||||
 | 
			
		||||
#include "extractor/intersection/intersection_analysis.hpp"
 | 
			
		||||
 | 
			
		||||
#include "extractor/serialization.hpp"
 | 
			
		||||
#include "storage/io.hpp"
 | 
			
		||||
 | 
			
		||||
@ -444,6 +446,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
 | 
			
		||||
    bearing_class_by_node_based_node.resize(m_node_based_graph.GetNumberOfNodes(),
 | 
			
		||||
                                            std::numeric_limits<std::uint32_t>::max());
 | 
			
		||||
 | 
			
		||||
    const auto &turn_lanes_data = transformTurnLaneMapIntoArrays(lane_description_map);
 | 
			
		||||
 | 
			
		||||
    // FIXME these need to be tuned in pre-allocated size
 | 
			
		||||
    std::vector<TurnPenalty> turn_weight_penalties;
 | 
			
		||||
    std::vector<TurnPenalty> turn_duration_penalties;
 | 
			
		||||
@ -661,6 +665,53 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
 | 
			
		||||
                     ++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";
 | 
			
		||||
                    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
 | 
			
		||||
 | 
			
		||||
@ -685,6 +736,8 @@ 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))
 | 
			
		||||
                    {
 | 
			
		||||
@ -747,6 +800,11 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
 | 
			
		||||
                            if (!turn.entry_allowed)
 | 
			
		||||
                                continue;
 | 
			
		||||
 | 
			
		||||
                            old_turns += 1;
 | 
			
		||||
                            std::cout << node_along_road_entering << " -> "
 | 
			
		||||
                                      << node_at_center_of_intersection << " -> "
 | 
			
		||||
                                      << m_node_based_graph.GetTarget(turn.eid) << "\n";
 | 
			
		||||
 | 
			
		||||
                            // In case a way restriction starts at a given location, add a turn onto
 | 
			
		||||
                            // every artificial node eminating here.
 | 
			
		||||
                            //
 | 
			
		||||
@ -895,6 +953,10 @@ 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;
 | 
			
		||||
 | 
			
		||||
@ -278,6 +278,7 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
 | 
			
		||||
                                             via_eid,
 | 
			
		||||
                                             isThroughStreet(1, intersection),
 | 
			
		||||
                                             intersection[1]);
 | 
			
		||||
                // TODO: no coverage by feature test cases
 | 
			
		||||
                intersection[0].entry_allowed = false; // UTURN on the freeway
 | 
			
		||||
            }
 | 
			
		||||
            else if (exiting_motorways == 2)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										317
									
								
								src/extractor/intersection/intersection_analysis.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								src/extractor/intersection/intersection_analysis.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,317 @@
 | 
			
		||||
#include "extractor/intersection/intersection_analysis.hpp"
 | 
			
		||||
 | 
			
		||||
#include "util/bearing.hpp"
 | 
			
		||||
#include "util/coordinate_calculation.hpp"
 | 
			
		||||
 | 
			
		||||
namespace osrm
 | 
			
		||||
{
 | 
			
		||||
namespace extractor
 | 
			
		||||
{
 | 
			
		||||
namespace intersection
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
IntersectionEdges getIncomingEdges(const util::NodeBasedDynamicGraph &graph,
 | 
			
		||||
                                   const NodeID intersection_node)
 | 
			
		||||
{
 | 
			
		||||
    IntersectionEdges result;
 | 
			
		||||
 | 
			
		||||
    for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
 | 
			
		||||
    {
 | 
			
		||||
        const auto from_node = graph.GetTarget(outgoing_edge);
 | 
			
		||||
        const auto incoming_edge = graph.FindEdge(from_node, intersection_node);
 | 
			
		||||
 | 
			
		||||
        if (!graph.GetEdgeData(incoming_edge).reversed)
 | 
			
		||||
        {
 | 
			
		||||
            result.push_back({from_node, incoming_edge});
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Enforce ordering of incoming edges
 | 
			
		||||
    std::sort(result.begin(), result.end());
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IntersectionEdges getOutgoingEdges(const util::NodeBasedDynamicGraph &graph,
 | 
			
		||||
                                   const NodeID intersection_node)
 | 
			
		||||
{
 | 
			
		||||
    IntersectionEdges result;
 | 
			
		||||
 | 
			
		||||
    for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
 | 
			
		||||
    {
 | 
			
		||||
        if (!graph.GetEdgeData(outgoing_edge).reversed)
 | 
			
		||||
        {
 | 
			
		||||
            result.push_back({intersection_node, outgoing_edge});
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Enforce ordering of outgoing edges
 | 
			
		||||
    std::sort(result.begin(), result.end());
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<util::Coordinate>
 | 
			
		||||
getEdgeCoordinates(const extractor::CompressedEdgeContainer &compressed_geometries,
 | 
			
		||||
                   const std::vector<util::Coordinate> &node_coordinates,
 | 
			
		||||
                   const NodeID from_node,
 | 
			
		||||
                   const EdgeID edge,
 | 
			
		||||
                   const NodeID to_node)
 | 
			
		||||
{
 | 
			
		||||
    if (!compressed_geometries.HasEntryForID(edge))
 | 
			
		||||
        return {node_coordinates[from_node], node_coordinates[to_node]};
 | 
			
		||||
 | 
			
		||||
    BOOST_ASSERT(from_node < node_coordinates.size());
 | 
			
		||||
    BOOST_ASSERT(to_node < node_coordinates.size());
 | 
			
		||||
 | 
			
		||||
    // 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.push_back(node_coordinates[from_node]);
 | 
			
		||||
    std::transform(geometry.begin(),
 | 
			
		||||
                   geometry.end(),
 | 
			
		||||
                   std::back_inserter(result),
 | 
			
		||||
                   [&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)
 | 
			
		||||
{
 | 
			
		||||
    IntersectionEdgeBearings result;
 | 
			
		||||
 | 
			
		||||
    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]);
 | 
			
		||||
 | 
			
		||||
        result.push_back({outgoing_edge, static_cast<float>(outgoing_bearing)});
 | 
			
		||||
        result.push_back(
 | 
			
		||||
            {incoming_edge, static_cast<float>(util::bearing::reverse(outgoing_bearing))});
 | 
			
		||||
 | 
			
		||||
        for (auto x : geometry)
 | 
			
		||||
            std::cout << x << ", ";
 | 
			
		||||
        std::cout << "\n";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (auto x : result)
 | 
			
		||||
        std::cout << x.edge << "," << x.bearing << ";   ";
 | 
			
		||||
    std::cout << "\n";
 | 
			
		||||
 | 
			
		||||
    // Enforce ordering of edges
 | 
			
		||||
    std::sort(result.begin(), result.end());
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto findEdgeBearing(const IntersectionEdgeBearings &bearings, 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;
 | 
			
		||||
        });
 | 
			
		||||
    BOOST_ASSERT(it != bearings.end() && it->edge == edge);
 | 
			
		||||
    return it->bearing;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double computeTurnAngle(const IntersectionEdgeBearings &bearings,
 | 
			
		||||
                        const IntersectionEdge &from,
 | 
			
		||||
                        const IntersectionEdge &to)
 | 
			
		||||
{
 | 
			
		||||
    return util::bearing::angleBetween(findEdgeBearing(bearings, from.edge),
 | 
			
		||||
                                       findEdgeBearing(bearings, to.edge));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename RestrictionsRange>
 | 
			
		||||
bool isTurnRestricted(const RestrictionsRange &restrictions, const NodeID to)
 | 
			
		||||
{
 | 
			
		||||
    // Check turn restrictions to find a node that is the only allowed target when coming from a
 | 
			
		||||
    // node to an intersection
 | 
			
		||||
    //     d
 | 
			
		||||
    //     |
 | 
			
		||||
    // a - b - c  and `only_straight_on ab | bc would return `c` for `a,b`
 | 
			
		||||
    const auto is_only = std::find_if(restrictions.first,
 | 
			
		||||
                                      restrictions.second,
 | 
			
		||||
                                      [](const auto &pair) { return pair.second->is_only; });
 | 
			
		||||
    if (is_only != restrictions.second)
 | 
			
		||||
        return is_only->second->AsNodeRestriction().to != to;
 | 
			
		||||
 | 
			
		||||
    // Check if explicitly forbidden
 | 
			
		||||
    const auto no_turn =
 | 
			
		||||
        std::find_if(restrictions.first, restrictions.second, [&to](const auto &restriction) {
 | 
			
		||||
            return restriction.second->AsNodeRestriction().to == to;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    return no_turn != restrictions.second;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 guidance::TurnLanesIndexedArray &turn_lanes_data,
 | 
			
		||||
                   const IntersectionEdge &from,
 | 
			
		||||
                   const IntersectionEdge &to)
 | 
			
		||||
{
 | 
			
		||||
    BOOST_ASSERT(graph.GetTarget(from.edge) == to.node);
 | 
			
		||||
 | 
			
		||||
    const auto intersection_node = to.node;
 | 
			
		||||
    const auto destination_node = graph.GetTarget(to.edge);
 | 
			
		||||
    auto const &restrictions = restriction_map.Restrictions(from.node, intersection_node);
 | 
			
		||||
 | 
			
		||||
    // Check if turn is explicitly restricted by a turn restriction
 | 
			
		||||
    if (isTurnRestricted(restrictions, destination_node))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    // Precompute reversed bearing of the `from` edge
 | 
			
		||||
    const auto from_edge_reversed_bearing =
 | 
			
		||||
        util::bearing::reverse(findEdgeBearing(bearings, from.edge));
 | 
			
		||||
 | 
			
		||||
    // Collect some information about the intersection
 | 
			
		||||
    // 1) number of allowed exits and adjacent bidirectional edges
 | 
			
		||||
    std::uint32_t allowed_exits = 0, bidirectional_edges = 0;
 | 
			
		||||
    // 2) edge IDs of roundabouts edges
 | 
			
		||||
    EdgeID roundabout_from = SPECIAL_EDGEID, roundabout_to = SPECIAL_EDGEID;
 | 
			
		||||
    double roundabout_from_angle = 0., roundabout_to_angle = 0.;
 | 
			
		||||
 | 
			
		||||
    for (const auto eid : graph.GetAdjacentEdgeRange(intersection_node))
 | 
			
		||||
    {
 | 
			
		||||
        const auto &edge_data = graph.GetEdgeData(eid);
 | 
			
		||||
        const auto &edge_class = edge_data.flags;
 | 
			
		||||
        const auto to_node = graph.GetTarget(eid);
 | 
			
		||||
        const auto reverse_edge = graph.FindEdge(to_node, intersection_node);
 | 
			
		||||
        BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID);
 | 
			
		||||
 | 
			
		||||
        const auto is_exit_edge = !edge_data.reversed && !isTurnRestricted(restrictions, to_node);
 | 
			
		||||
        const auto is_bidirectional = !graph.GetEdgeData(reverse_edge).reversed;
 | 
			
		||||
        allowed_exits += is_exit_edge;
 | 
			
		||||
        bidirectional_edges += is_bidirectional;
 | 
			
		||||
 | 
			
		||||
        if (edge_class.roundabout || edge_class.circular)
 | 
			
		||||
        {
 | 
			
		||||
            if (edge_data.reversed)
 | 
			
		||||
            {
 | 
			
		||||
                // "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);
 | 
			
		||||
                if (angle > roundabout_from_angle)
 | 
			
		||||
                {
 | 
			
		||||
                    roundabout_from = reverse_edge;
 | 
			
		||||
                    roundabout_from_angle = angle;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                // a tie breaker that maximizes ∠(¬from_edge_bearing, roundabout_to_bearing)
 | 
			
		||||
                const auto angle = util::bearing::angleBetween(from_edge_reversed_bearing,
 | 
			
		||||
                                                               findEdgeBearing(bearings, eid));
 | 
			
		||||
                if (angle > roundabout_to_angle)
 | 
			
		||||
                {
 | 
			
		||||
                    roundabout_to = eid;
 | 
			
		||||
                    roundabout_to_angle = angle;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 3) if the intersection has a barrier
 | 
			
		||||
    const bool is_barrier_node = barrier_nodes.find(intersection_node) != barrier_nodes.end();
 | 
			
		||||
 | 
			
		||||
    // Check a U-turn
 | 
			
		||||
    if (from.node == destination_node)
 | 
			
		||||
    {
 | 
			
		||||
        // Allow U-turns before barrier nodes
 | 
			
		||||
        if (is_barrier_node)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        // Allow U-turns at dead-ends
 | 
			
		||||
        if (graph.GetAdjacentEdgeRange(intersection_node).size() == 1)
 | 
			
		||||
            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)
 | 
			
		||||
        //   a→b
 | 
			
		||||
        //   ↕ ↕
 | 
			
		||||
        //   d↔c
 | 
			
		||||
        if (allowed_exits == 1 || bidirectional_edges <= 1)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        // Allow U-turn if the incoming edge has a U-turn lane
 | 
			
		||||
        const auto &incoming_edge_annotation_id = graph.GetEdgeData(from.edge).annotation_data;
 | 
			
		||||
        const auto lane_description_id = static_cast<std::size_t>(
 | 
			
		||||
            node_data_container.GetAnnotation(incoming_edge_annotation_id).lane_description_id);
 | 
			
		||||
        if (lane_description_id != INVALID_LANE_DESCRIPTIONID)
 | 
			
		||||
        {
 | 
			
		||||
            const auto &turn_lane_offsets = std::get<0>(turn_lanes_data);
 | 
			
		||||
            const auto &turn_lanes = std::get<1>(turn_lanes_data);
 | 
			
		||||
            BOOST_ASSERT(lane_description_id + 1 < turn_lane_offsets.size());
 | 
			
		||||
 | 
			
		||||
            if (std::any_of(turn_lanes.begin() + turn_lane_offsets[lane_description_id],
 | 
			
		||||
                            turn_lanes.begin() + turn_lane_offsets[lane_description_id + 1],
 | 
			
		||||
                            [](const auto &lane) { return lane & guidance::TurnLaneType::uturn; }))
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Don't allow U-turns on usual intersections
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Don't allow turns via barriers for not U-turn maneuvers
 | 
			
		||||
    if (is_barrier_node)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    // Check for roundabouts exits in the opposite direction of roundabout flow
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
        // Get angles from the roundabout edge to three other edges
 | 
			
		||||
        const auto roundabout_angle =
 | 
			
		||||
            util::bearing::angleBetween(roundabout_from_bearing, roundabout_to_bearing);
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        // Restrict turning over a roundabout if `roundabout_to_angle` is in
 | 
			
		||||
        // a sector between `roundabout_from_bearing` to `from_bearing`
 | 
			
		||||
        //
 | 
			
		||||
        //             150°                            150°
 | 
			
		||||
        //              v░░░░░░                ░░░░░░░░░v
 | 
			
		||||
        //             v░░░░░░░                ░░░░░░░░v
 | 
			
		||||
        // 270° <-ooo- v -ttt-> 90°       270° <-ttt- v -ooo-> 90°
 | 
			
		||||
        //             ^░░░░░░░                ░░░░░░░^
 | 
			
		||||
        //             r░░░░░░░                ░░░░░░░r
 | 
			
		||||
        //             r░░░░░░░                ░░░░░░░r
 | 
			
		||||
        if ((roundabout_from_angle < roundabout_angle &&
 | 
			
		||||
             roundabout_to_angle < roundabout_from_angle) ||
 | 
			
		||||
            (roundabout_from_angle > roundabout_angle &&
 | 
			
		||||
             roundabout_to_angle > roundabout_from_angle))
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user