Rember Intersection Shapes
Changes the processing order in the edge based graph factory. Instead of iterating over all outgoing edges in order, we compute the edge expanded graph in the order of intersections. This allows to remember intersection shapes and re-use them for all possible ingoing edges. Also: use low accuracry mode for intersections degree 2 intersections We can use lower accuracy here, since the `bearing` after the turn is not as relevant for off-route detection. Getting lost is near impossible here.
This commit is contained in:
parent
5775679f64
commit
561b7cc58e
@ -72,16 +72,16 @@ Feature: Turn Lane Guidance
|
||||
Given the node map
|
||||
"""
|
||||
e
|
||||
a b c g
|
||||
d
|
||||
f
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
i h j
|
||||
a - - b.-.- - c-g
|
||||
| ' 'd
|
||||
| f
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
i - - h - - - j
|
||||
"""
|
||||
|
||||
And the ways
|
||||
|
@ -10,12 +10,12 @@ Feature: Simple Turns
|
||||
"""
|
||||
a
|
||||
b
|
||||
|
||||
|
||||
^
|
||||
/ \
|
||||
c d
|
||||
|
||||
e
|
||||
|
||||
|\
|
||||
| e
|
||||
|
|
||||
f
|
||||
"""
|
||||
|
||||
@ -96,16 +96,16 @@ Feature: Simple Turns
|
||||
Given the node map
|
||||
"""
|
||||
a
|
||||
|
||||
b
|
||||
|
|
||||
.b.
|
||||
c h
|
||||
|
||||
|
||||
| |
|
||||
| |
|
||||
1 2
|
||||
|
||||
| |
|
||||
d g
|
||||
e
|
||||
|
||||
'e'
|
||||
|
|
||||
f
|
||||
"""
|
||||
|
||||
|
@ -443,9 +443,9 @@ Feature: Simple Turns
|
||||
| ef | residential | road | 2 | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| g,f | turn,road | depart,arrive |
|
||||
| c,f | road,road,road | depart,continue right,arrive |
|
||||
| waypoints | route | turns | locations |
|
||||
| g,f | turn,road,road | depart,turn left,arrive | g,e,f |
|
||||
| c,f | road,road,road | depart,continue right,arrive | c,b,f |
|
||||
|
||||
#http://www.openstreetmap.org/search?query=52.479264%2013.295617#map=19/52.47926/13.29562
|
||||
Scenario: Splitting Roads with curved split
|
||||
|
@ -117,23 +117,24 @@ Feature: Turn Lane Guidance
|
||||
Given the node map
|
||||
"""
|
||||
e
|
||||
a 1 b c
|
||||
f a-1-b---c
|
||||
d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | turn:lanes | turn:lanes:forward | name |
|
||||
| ab | | reverse;left\|through;right | in |
|
||||
| bc | | | straight |
|
||||
| bd | | | right |
|
||||
| be | | | left |
|
||||
| nodes | turn:lanes | turn:lanes:forward | name | # |
|
||||
| ab | | reverse;left\|through;right | in | |
|
||||
| bc | | | straight | |
|
||||
| bd | | | right | |
|
||||
| be | | | left | |
|
||||
| fa | | | uturn-avoider | #due to https://github.com/Project-OSRM/osrm-backend/issues/3359 |
|
||||
|
||||
When I route I should get
|
||||
| from | to | bearings | route | turns | lanes |
|
||||
| a | c | 180,180 180,180 | in,straight,straight | depart,new name straight,arrive | ,left;uturn:false straight;right:true, |
|
||||
| a | d | 180,180 180,180 | in,right,right | depart,turn right,arrive | ,left;uturn:false straight;right:true, |
|
||||
| a | e | 180,180 180,180 | in,left,left | depart,turn left,arrive | ,left;uturn:true straight;right:false, |
|
||||
| 1 | a | 90,2 270,2 | in,in,in | depart,turn uturn,arrive | ,left;uturn:true straight;right:false, |
|
||||
| from | to | bearings | route | turns | lanes | locations |
|
||||
| a | c | 180,180 180,180 | in,straight,straight | depart,new name straight,arrive | ,left;uturn:false straight;right:true, | a,b,c |
|
||||
| a | d | 180,180 180,180 | in,right,right | depart,turn right,arrive | ,left;uturn:false straight;right:true, | a,b,d |
|
||||
| a | e | 180,180 180,180 | in,left,left | depart,turn left,arrive | ,left;uturn:true straight;right:false, | a,b,e |
|
||||
| 1 | a | 90,2 270,2 | in,in,in | depart,turn uturn,arrive | ,left;uturn:true straight;right:false, | _,b,a |
|
||||
|
||||
|
||||
#this next test requires decision on how to announce lanes for going straight if there is no turn
|
||||
|
@ -1179,8 +1179,8 @@ Feature: Simple Turns
|
||||
Scenario: Obvious Index wigh very narrow turn to the right
|
||||
Given the node map
|
||||
"""
|
||||
a b c
|
||||
d
|
||||
a - b -.-.- - - c
|
||||
' ' 'd
|
||||
"""
|
||||
|
||||
And the ways
|
||||
@ -1198,8 +1198,8 @@ Feature: Simple Turns
|
||||
Scenario: Obvious Index wigh very narrow turn to the right
|
||||
Given the node map
|
||||
"""
|
||||
a b c
|
||||
e d f
|
||||
a - b - . -.- - c
|
||||
e - -'-'d-f
|
||||
"""
|
||||
|
||||
And the ways
|
||||
@ -1218,8 +1218,8 @@ Feature: Simple Turns
|
||||
Scenario: Obvious Index wigh very narrow turn to the left
|
||||
Given the node map
|
||||
"""
|
||||
d
|
||||
a b c
|
||||
. . .d
|
||||
a - b -'-'- - - c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
@ -1237,8 +1237,8 @@ Feature: Simple Turns
|
||||
Scenario: Obvious Index wigh very narrow turn to the left
|
||||
Given the node map
|
||||
"""
|
||||
e d f
|
||||
a b c
|
||||
e - -.- d-f
|
||||
a - b - ' - - - c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
|
@ -26,6 +26,9 @@ struct QueryEdge
|
||||
forward = other.forward;
|
||||
backward = other.backward;
|
||||
}
|
||||
// this ID is either the middle node of the shortcut, or the ID of the edge based node (node
|
||||
// based edge) storing the appropriate data. If `shortcut` is set to true, we get the middle
|
||||
// node. Otherwise we see the edge based node to access node data.
|
||||
NodeID id : 31;
|
||||
bool shortcut : 1;
|
||||
int weight : 30;
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
#include <tuple>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@ -60,19 +61,12 @@ inline EdgeBasedEdge::EdgeBasedEdge(const NodeID source,
|
||||
|
||||
inline bool EdgeBasedEdge::operator<(const EdgeBasedEdge &other) const
|
||||
{
|
||||
if (source == other.source)
|
||||
{
|
||||
if (target == other.target)
|
||||
{
|
||||
if (weight == other.weight)
|
||||
{
|
||||
return forward && backward && ((!other.forward) || (!other.backward));
|
||||
}
|
||||
return weight < other.weight;
|
||||
}
|
||||
return target < other.target;
|
||||
}
|
||||
return source < other.source;
|
||||
const auto unidirectional = (!forward || !backward);
|
||||
const auto other_is_unidirectional = (!other.forward || !other.backward);
|
||||
// if all items are the same, we want to keep bidirectional edges. due to the `<` operator,
|
||||
// preferring 0 (false) over 1 (true), we need to compare the inverse of `bidirectional`
|
||||
return std::tie(source, target, weight, unidirectional) <
|
||||
std::tie(other.source, other.target, other.weight, other_is_unidirectional);
|
||||
}
|
||||
} // ns extractor
|
||||
} // ns osrm
|
||||
|
@ -38,7 +38,8 @@ class CoordinateExtractor
|
||||
const NodeID to_node,
|
||||
const std::uint8_t number_of_in_lanes) const;
|
||||
|
||||
// same as above, only with precomputed coordinate vector (move it in)
|
||||
// Given a set of precomputed coordinates, select the representative coordinate along the road
|
||||
// that best describes the turn
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
util::Coordinate
|
||||
ExtractRepresentativeCoordinate(const NodeID intersection_node,
|
||||
@ -240,6 +241,14 @@ class CoordinateExtractor
|
||||
const double segment_length,
|
||||
const std::vector<double> &segment_distances,
|
||||
const std::uint8_t considered_lanes) const;
|
||||
|
||||
// find the coordinate at a specific location in the vector
|
||||
util::Coordinate ExtractCoordinateAtLength(const double distance,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const std::vector<double> &length_cache) const;
|
||||
util::Coordinate
|
||||
ExtractCoordinateAtLength(const double distance,
|
||||
const std::vector<util::Coordinate> &coordinates) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
|
@ -5,12 +5,11 @@
|
||||
#include <vector>
|
||||
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/guidance/toolkit.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
#include "util/typedefs.hpp" // EdgeID
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
@ -18,16 +17,37 @@ namespace extractor
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
// Every Turn Operation describes a way of switching onto a segment, indicated by an EdgeID. The
|
||||
// associated turn is described by an angle and an instruction that is used to announce it.
|
||||
// The Turn Operation indicates what is exposed to the outside of the turn analysis.
|
||||
struct TurnOperation
|
||||
// the shape of an intersection only knows about edge IDs and bearings
|
||||
struct IntersectionShapeData
|
||||
{
|
||||
EdgeID eid;
|
||||
double angle;
|
||||
double bearing;
|
||||
TurnInstruction instruction;
|
||||
LaneDataID lane_data_id;
|
||||
double segment_length;
|
||||
};
|
||||
|
||||
inline auto makeCompareShapeDataByBearing(const double base_bearing)
|
||||
{
|
||||
return [base_bearing](const IntersectionShapeData &lhs, const IntersectionShapeData &rhs) {
|
||||
return util::bearing::angleBetweenBearings(base_bearing, lhs.bearing) <
|
||||
util::bearing::angleBetweenBearings(base_bearing, rhs.bearing);
|
||||
};
|
||||
};
|
||||
|
||||
// When viewing an intersection from an incoming edge, we can transform a shape into a view which
|
||||
// gives additional information on angles and whether a turn is allowed
|
||||
struct IntersectionViewData : IntersectionShapeData
|
||||
{
|
||||
IntersectionViewData(const IntersectionShapeData &shape,
|
||||
const bool entry_allowed,
|
||||
const double angle)
|
||||
: IntersectionShapeData(shape), entry_allowed(entry_allowed), angle(angle)
|
||||
{
|
||||
}
|
||||
|
||||
bool entry_allowed;
|
||||
double angle;
|
||||
|
||||
bool CompareByAngle(const IntersectionViewData &other) const;
|
||||
};
|
||||
|
||||
// A Connected Road is the internal representation of a potential turn. Internally, we require
|
||||
@ -51,17 +71,17 @@ struct TurnOperation
|
||||
// aaaaaaaa
|
||||
//
|
||||
// We would perceive a->c as a sharp turn, a->b as a slight turn, and b->c as a slight turn.
|
||||
struct ConnectedRoad final : public TurnOperation
|
||||
struct ConnectedRoad final : IntersectionViewData
|
||||
{
|
||||
using Base = TurnOperation;
|
||||
ConnectedRoad(const IntersectionViewData &view,
|
||||
const TurnInstruction instruction,
|
||||
const LaneDataID lane_data_id)
|
||||
: IntersectionViewData(view), instruction(instruction), lane_data_id(lane_data_id)
|
||||
{
|
||||
}
|
||||
|
||||
ConnectedRoad(const TurnOperation turn,
|
||||
const bool entry_allowed = false,
|
||||
const boost::optional<double> segment_length = {});
|
||||
|
||||
// a turn may be relevant to good instructions, even if we cannot enter the road
|
||||
bool entry_allowed;
|
||||
boost::optional<double> segment_length;
|
||||
TurnInstruction instruction;
|
||||
LaneDataID lane_data_id;
|
||||
|
||||
// used to sort the set of connected roads (we require sorting throughout turn handling)
|
||||
bool compareByAngle(const ConnectedRoad &other) const;
|
||||
@ -76,10 +96,24 @@ struct ConnectedRoad final : public TurnOperation
|
||||
// small helper function to print the content of a connected road
|
||||
std::string toString(const ConnectedRoad &road);
|
||||
|
||||
struct Intersection final : public std::vector<ConnectedRoad>
|
||||
using IntersectionShape = std::vector<IntersectionShapeData>;
|
||||
|
||||
struct IntersectionView final : std::vector<IntersectionViewData>
|
||||
{
|
||||
using Base = std::vector<IntersectionViewData>;
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return std::is_sorted(begin(), end(), std::mem_fn(&IntersectionViewData::CompareByAngle));
|
||||
};
|
||||
|
||||
Base::iterator findClosestTurn(double angle);
|
||||
Base::const_iterator findClosestTurn(double angle) const;
|
||||
};
|
||||
|
||||
struct Intersection final : std::vector<ConnectedRoad>
|
||||
{
|
||||
using Base = std::vector<ConnectedRoad>;
|
||||
|
||||
/*
|
||||
* find the turn whose angle offers the least angularDeviation to the specified angle
|
||||
* E.g. for turn angles [0,90,260] and a query of 180 we return the 260 degree turn (difference
|
||||
|
@ -11,8 +11,11 @@
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
@ -32,7 +35,18 @@ class IntersectionGenerator
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const CompressedEdgeContainer &compressed_edge_container);
|
||||
|
||||
Intersection operator()(const NodeID nid, const EdgeID via_eid) const;
|
||||
IntersectionView operator()(const NodeID nid, const EdgeID via_eid) const;
|
||||
|
||||
/*
|
||||
* Compute the shape of an intersection, returning a set of connected roads, without any further
|
||||
* concern for which of the entries are actually allowed.
|
||||
* The shape also only comes with turn bearings, not with turn angles. All turn angles will be
|
||||
* set to zero
|
||||
*/
|
||||
IntersectionShape
|
||||
ComputeIntersectionShape(const NodeID center_node,
|
||||
const boost::optional<NodeID> sorting_base = boost::none,
|
||||
bool use_low_precision_angles = false) const;
|
||||
|
||||
// Graph Compression cannot compress every setting. For example any barrier/traffic light cannot
|
||||
// be compressed. As a result, a simple road of the form `a ----- b` might end up as having an
|
||||
@ -40,7 +54,7 @@ class IntersectionGenerator
|
||||
// down a road, finding the next actual decision requires the look at multiple intersections.
|
||||
// Here we follow the road until we either reach a dead end or find the next intersection with
|
||||
// more than a single next road.
|
||||
Intersection GetActualNextIntersection(const NodeID starting_node,
|
||||
IntersectionView GetActualNextIntersection(const NodeID starting_node,
|
||||
const EdgeID via_edge,
|
||||
NodeID *resulting_from_node,
|
||||
EdgeID *resulting_via_edge) const;
|
||||
@ -56,10 +70,29 @@ class IntersectionGenerator
|
||||
// turns. Even good enough to do some simple angle verifications. It is mostly available to
|
||||
// allow for faster graph traversal in the extraction phase.
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection GetConnectedRoads(const NodeID from_node,
|
||||
IntersectionView GetConnectedRoads(const NodeID from_node,
|
||||
const EdgeID via_eid,
|
||||
const bool use_low_precision_angles = false) const;
|
||||
|
||||
/*
|
||||
* To be used in the road network, we need to check for valid/restricted turns. These two
|
||||
* functions transform a basic intersection / a normalised intersection into the
|
||||
* correct view when entering via a given edge.
|
||||
*/
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
IntersectionView
|
||||
TransformIntersectionShapeIntoView(const NodeID previous_node,
|
||||
const EdgeID entering_via_edge,
|
||||
const IntersectionShape &intersection) const;
|
||||
// version for normalised intersection
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
IntersectionView TransformIntersectionShapeIntoView(
|
||||
const NodeID previous_node,
|
||||
const EdgeID entering_via_edge,
|
||||
const IntersectionShape &normalised_intersection,
|
||||
const IntersectionShape &intersection,
|
||||
const std::vector<std::pair<EdgeID, EdgeID>> &merging_map) const;
|
||||
|
||||
private:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
const RestrictionMap &restriction_map;
|
||||
@ -68,6 +101,14 @@ class IntersectionGenerator
|
||||
|
||||
// own state, used to find the correct coordinates along a road
|
||||
const CoordinateExtractor coordinate_extractor;
|
||||
|
||||
// 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`
|
||||
boost::optional<NodeID> GetOnlyAllowedTurnIfExistent(const NodeID coming_from_node,
|
||||
const NodeID node_at_intersection) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "extractor/suffix_table.hpp"
|
||||
#include "util/name_table.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
@ -45,7 +46,8 @@ class IntersectionNormalizer
|
||||
// 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
|
||||
Intersection operator()(const NodeID node_at_intersection, Intersection intersection) const;
|
||||
std::pair<IntersectionShape, std::vector<std::pair<EdgeID, EdgeID>>>
|
||||
operator()(const NodeID node_at_intersection, IntersectionShape intersection) const;
|
||||
|
||||
private:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
@ -57,9 +59,23 @@ class IntersectionNormalizer
|
||||
|
||||
// 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
|
||||
// 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 Intersection &intersection,
|
||||
const IntersectionShape &intersection,
|
||||
std::size_t first_index,
|
||||
std::size_t second_index) const;
|
||||
|
||||
// A tool called by CanMerge. It checks whether two indices can be merged, not concerned without
|
||||
// remaining parts of the intersection.
|
||||
bool InnerCanMerge(const NodeID intersection_node,
|
||||
const IntersectionShape &intersection,
|
||||
std::size_t first_index,
|
||||
std::size_t second_index) const;
|
||||
|
||||
@ -75,8 +91,8 @@ class IntersectionNormalizer
|
||||
// The treatment results in a straight turn angle of 180º rather than a turn angle of approx
|
||||
// 160
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection MergeSegregatedRoads(const NodeID intersection_node,
|
||||
Intersection intersection) const;
|
||||
std::pair<IntersectionShape, std::vector<std::pair<EdgeID, EdgeID>>>
|
||||
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.
|
||||
@ -90,8 +106,8 @@ class IntersectionNormalizer
|
||||
//
|
||||
// for the local view of b at a.
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection AdjustForJoiningRoads(const NodeID node_at_intersection,
|
||||
Intersection intersection) const;
|
||||
IntersectionShape AdjustBearingsForMergeAtDestination(const NodeID node_at_intersection,
|
||||
IntersectionShape intersection) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
|
@ -133,7 +133,7 @@ struct IntersectionFinderAccumulator
|
||||
// the result we are looking for
|
||||
NodeID nid;
|
||||
EdgeID via_edge_id;
|
||||
Intersection intersection;
|
||||
IntersectionView intersection;
|
||||
};
|
||||
|
||||
template <class accumulator_type, class selector_type>
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@ -36,7 +37,6 @@ namespace guidance
|
||||
|
||||
class TurnAnalysis
|
||||
{
|
||||
|
||||
public:
|
||||
TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
@ -47,29 +47,35 @@ class TurnAnalysis
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const ProfileProperties &profile_properties);
|
||||
|
||||
/* Full Analysis Process for a single node/edge combination. Use with caution, as the process is
|
||||
* relatively expensive */
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
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;
|
||||
// normalised shape, merged some roads into others, adjusted bearings
|
||||
// see intersection_normaliser for further explanations
|
||||
IntersectionShape normalised_intersection_shape;
|
||||
// map containing information about which road was merged into which
|
||||
std::vector<std::pair<EdgeID, EdgeID>> merging_map;
|
||||
};
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection operator()(const NodeID from_node, const EdgeID via_eid) const;
|
||||
ShapeResult ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const;
|
||||
|
||||
/*
|
||||
* Post-Processing a generated intersection is useful for any intersection that was simply
|
||||
* generated using an intersection generator. In the normal use case, you don't have to call
|
||||
* this function.
|
||||
* This function is part of the normal process of the operator().
|
||||
*/
|
||||
// Select turn types based on the intersection shape
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection
|
||||
PostProcess(const NodeID from_node, const EdgeID via_eid, Intersection intersection) const;
|
||||
|
||||
std::vector<TurnOperation>
|
||||
transformIntersectionIntoTurns(const Intersection &intersection) const;
|
||||
|
||||
Intersection
|
||||
assignTurnTypes(const NodeID from_node, const EdgeID via_eid, Intersection intersection) const;
|
||||
Intersection AssignTurnTypes(const NodeID from_node,
|
||||
const EdgeID via_eid,
|
||||
const IntersectionView &intersection) const;
|
||||
|
||||
const IntersectionGenerator &GetIntersectionGenerator() const;
|
||||
|
||||
|
@ -28,7 +28,7 @@ bool findPreviousIntersection(
|
||||
// output parameters, will be in an arbitrary state on failure
|
||||
NodeID &result_node,
|
||||
EdgeID &result_via_edge,
|
||||
Intersection &result_intersection);
|
||||
IntersectionView &result_intersection);
|
||||
|
||||
} // namespace lanes
|
||||
} // namespace guidance
|
||||
|
@ -73,8 +73,9 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge
|
||||
|
||||
inline NodeBasedEdge::NodeBasedEdge()
|
||||
: source(SPECIAL_NODEID), target(SPECIAL_NODEID), name_id(0), weight(0), forward(false),
|
||||
backward(false), roundabout(false), circular(false), access_restricted(false), startpoint(true),
|
||||
is_split(false), travel_mode(false), lane_description_id(INVALID_LANE_DESCRIPTIONID)
|
||||
backward(false), roundabout(false), circular(false), access_restricted(false),
|
||||
startpoint(true), is_split(false), travel_mode(false),
|
||||
lane_description_id(INVALID_LANE_DESCRIPTIONID)
|
||||
{
|
||||
}
|
||||
|
||||
@ -93,9 +94,10 @@ inline NodeBasedEdge::NodeBasedEdge(NodeID source,
|
||||
const LaneDescriptionID lane_description_id,
|
||||
guidance::RoadClassification road_classification)
|
||||
: source(source), target(target), name_id(name_id), weight(weight), forward(forward),
|
||||
backward(backward), roundabout(roundabout), circular(circular), access_restricted(access_restricted),
|
||||
startpoint(startpoint), is_split(is_split), travel_mode(travel_mode),
|
||||
lane_description_id(lane_description_id), road_classification(std::move(road_classification))
|
||||
backward(backward), roundabout(roundabout), circular(circular),
|
||||
access_restricted(access_restricted), startpoint(startpoint), is_split(is_split),
|
||||
travel_mode(travel_mode), lane_description_id(lane_description_id),
|
||||
road_classification(std::move(road_classification))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -8,9 +8,9 @@ namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
namespace bearing
|
||||
{
|
||||
|
||||
inline std::string get(const double heading)
|
||||
{
|
||||
BOOST_ASSERT(heading >= 0);
|
||||
@ -96,8 +96,40 @@ inline double reverseBearing(const double bearing)
|
||||
return bearing - 180.;
|
||||
return bearing + 180;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the angle between two bearings on a normal turn circle
|
||||
//
|
||||
// Bearings Angles
|
||||
//
|
||||
// 0 180
|
||||
// 315 45 225 135
|
||||
//
|
||||
// 270 x 90 270 x 90
|
||||
//
|
||||
// 225 135 315 45
|
||||
// 180 0
|
||||
//
|
||||
// A turn from north to north-east offerst bearing 0 and 45 has to be translated
|
||||
// into a turn of 135 degrees. The same holdes for 90 - 135 (east to south
|
||||
// east).
|
||||
// For north, the transformation works by angle = 540 (360 + 180) - exit_bearing
|
||||
// % 360;
|
||||
// All other cases are handled by first rotating both bearings to an
|
||||
// entry_bearing of 0.
|
||||
inline double angleBetweenBearings(const double entry_bearing, const double exit_bearing)
|
||||
{
|
||||
const double offset = 360 - entry_bearing;
|
||||
const double rotated_exit = [](double bearing, const double offset) {
|
||||
bearing += offset;
|
||||
return bearing > 360 ? bearing - 360 : bearing;
|
||||
}(exit_bearing, offset);
|
||||
|
||||
const auto angle = 540 - rotated_exit;
|
||||
return angle >= 360 ? angle - 360 : angle;
|
||||
}
|
||||
|
||||
} // namespace bearing
|
||||
} // namespace util
|
||||
} // namespace osrm
|
||||
|
||||
#endif // BEARING_HPP
|
||||
|
@ -1,13 +1,13 @@
|
||||
#include "engine/guidance/post_processing.hpp"
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "engine/guidance/post_processing.hpp"
|
||||
#include "engine/guidance/toolkit.hpp"
|
||||
|
||||
#include "engine/guidance/assemble_steps.hpp"
|
||||
#include "engine/guidance/lane_processing.hpp"
|
||||
#include "engine/guidance/toolkit.hpp"
|
||||
|
||||
#include "util/attributes.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/guidance/toolkit.hpp"
|
||||
#include "util/guidance/turn_lanes.hpp"
|
||||
|
||||
@ -92,37 +92,6 @@ bool compatible(const RouteStep &lhs, const RouteStep &rhs) { return lhs.mode ==
|
||||
// invalidate a step and set its content to nothing
|
||||
void invalidateStep(RouteStep &step) { step = getInvalidRouteStep(); }
|
||||
|
||||
// Compute the angle between two bearings on a normal turn circle
|
||||
//
|
||||
// Bearings Angles
|
||||
//
|
||||
// 0 180
|
||||
// 315 45 225 135
|
||||
//
|
||||
// 270 x 90 270 x 90
|
||||
//
|
||||
// 225 135 315 45
|
||||
// 180 0
|
||||
//
|
||||
// A turn from north to north-east offerst bearing 0 and 45 has to be translated
|
||||
// into a turn of 135 degrees. The same holdes for 90 - 135 (east to south
|
||||
// east).
|
||||
// For north, the transformation works by angle = 540 (360 + 180) - exit_bearing
|
||||
// % 360;
|
||||
// All other cases are handled by first rotating both bearings to an
|
||||
// entry_bearing of 0.
|
||||
double turn_angle(const double entry_bearing, const double exit_bearing)
|
||||
{
|
||||
const double offset = 360 - entry_bearing;
|
||||
const double rotated_exit = [](double bearing, const double offset) {
|
||||
bearing += offset;
|
||||
return bearing > 360 ? bearing - 360 : bearing;
|
||||
}(exit_bearing, offset);
|
||||
|
||||
const auto angle = 540 - rotated_exit;
|
||||
return angle > 360 ? angle - 360 : angle;
|
||||
}
|
||||
|
||||
// Checks if name change happens the user wants to know about.
|
||||
// Treats e.g. "Name (Ref)" -> "Name" changes still as same name.
|
||||
bool isNoticeableNameChange(const RouteStep &lhs, const RouteStep &rhs)
|
||||
@ -330,8 +299,8 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
TurnType::EnterRoundaboutIntersectionAtExit)
|
||||
{
|
||||
BOOST_ASSERT(!propagation_step.intersections.empty());
|
||||
const double angle =
|
||||
turn_angle(util::bearing::reverseBearing(
|
||||
const double angle = util::bearing::angleBetweenBearings(
|
||||
util::bearing::reverseBearing(
|
||||
entry_intersection.bearings[entry_intersection.in]),
|
||||
exit_bearing);
|
||||
|
||||
@ -396,10 +365,13 @@ double findTotalTurnAngle(const RouteStep &entry_step, const RouteStep &exit_ste
|
||||
util::bearing::reverseBearing(entry_intersection.bearings[entry_intersection.in]);
|
||||
const auto entry_step_exit_bearing = entry_intersection.bearings[entry_intersection.out];
|
||||
|
||||
const auto exit_angle = turn_angle(exit_step_entry_bearing, exit_step_exit_bearing);
|
||||
const auto entry_angle = turn_angle(entry_step_entry_bearing, entry_step_exit_bearing);
|
||||
const auto exit_angle =
|
||||
util::bearing::angleBetweenBearings(exit_step_entry_bearing, exit_step_exit_bearing);
|
||||
const auto entry_angle =
|
||||
util::bearing::angleBetweenBearings(entry_step_entry_bearing, entry_step_exit_bearing);
|
||||
|
||||
const double total_angle = turn_angle(entry_step_entry_bearing, exit_step_exit_bearing);
|
||||
const double total_angle =
|
||||
util::bearing::angleBetweenBearings(entry_step_entry_bearing, exit_step_exit_bearing);
|
||||
// We allow for minor deviations from a straight line
|
||||
if (((entry_step.distance < MAX_COLLAPSE_DISTANCE && exit_step.intersections.size() == 1) ||
|
||||
(entry_angle <= 185 && exit_angle <= 185) || (entry_angle >= 175 && exit_angle >= 175)) &&
|
||||
@ -608,12 +580,12 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
||||
return step.intersections.front().bearings[step.intersections.front().out];
|
||||
};
|
||||
|
||||
const auto first_angle =
|
||||
turn_angle(in_bearing(one_back_step), out_bearing(one_back_step));
|
||||
const auto second_angle =
|
||||
turn_angle(in_bearing(current_step), out_bearing(current_step));
|
||||
const auto bearing_turn_angle =
|
||||
turn_angle(in_bearing(one_back_step), out_bearing(current_step));
|
||||
const auto first_angle = util::bearing::angleBetweenBearings(
|
||||
in_bearing(one_back_step), out_bearing(one_back_step));
|
||||
const auto second_angle = util::bearing::angleBetweenBearings(
|
||||
in_bearing(current_step), out_bearing(current_step));
|
||||
const auto bearing_turn_angle = util::bearing::angleBetweenBearings(
|
||||
in_bearing(one_back_step), out_bearing(current_step));
|
||||
|
||||
// When looking at an intersection, some angles, even though present, feel more like
|
||||
// a straight turn. This happens most often at segregated intersections.
|
||||
@ -834,7 +806,7 @@ bool isStaggeredIntersection(const std::vector<RouteStep> &steps,
|
||||
const auto &intersection = step.intersections.front();
|
||||
const auto entry_bearing = intersection.bearings[intersection.in];
|
||||
const auto exit_bearing = intersection.bearings[intersection.out];
|
||||
return turn_angle(entry_bearing, exit_bearing);
|
||||
return util::bearing::angleBetweenBearings(entry_bearing, exit_bearing);
|
||||
};
|
||||
|
||||
// Instead of using turn modifiers (e.g. as in isRightTurn) we want to be more strict here.
|
||||
|
@ -363,25 +363,60 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
bearing_class_by_node_based_node.resize(m_node_based_graph->GetNumberOfNodes(),
|
||||
std::numeric_limits<std::uint32_t>::max());
|
||||
|
||||
for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
|
||||
// going over all nodes (which form the center of an intersection), we compute all
|
||||
// possible turns along these intersections.
|
||||
for (const auto node_at_center_of_intersection :
|
||||
util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
|
||||
{
|
||||
progress.PrintStatus(node_u);
|
||||
for (const EdgeID edge_from_u : m_node_based_graph->GetAdjacentEdgeRange(node_u))
|
||||
{
|
||||
if (m_node_based_graph->GetEdgeData(edge_from_u).reversed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
progress.PrintStatus(node_at_center_of_intersection);
|
||||
|
||||
const auto shape_result =
|
||||
turn_analysis.ComputeIntersectionShapes(node_at_center_of_intersection);
|
||||
|
||||
// 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, but the cost we need to pay to
|
||||
// generate edges here is worth the additional search overhead.
|
||||
//
|
||||
// a -> b <-> c
|
||||
// |
|
||||
// v
|
||||
// d
|
||||
//
|
||||
// will have:
|
||||
// a: b,rev=0
|
||||
// b: a,rev=1 c,rev=0 d,rev=0
|
||||
// c: b,rev=0
|
||||
//
|
||||
// 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`
|
||||
for (const EdgeID outgoing_edge :
|
||||
m_node_based_graph->GetAdjacentEdgeRange(node_at_center_of_intersection))
|
||||
{
|
||||
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;
|
||||
|
||||
const NodeID node_v = m_node_based_graph->GetTarget(edge_from_u);
|
||||
++node_based_edge_counter;
|
||||
auto intersection = turn_analysis(node_u, edge_from_u);
|
||||
|
||||
auto intersection_with_flags_and_angles =
|
||||
turn_analysis.GetIntersectionGenerator().TransformIntersectionShapeIntoView(
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
shape_result.normalised_intersection_shape,
|
||||
shape_result.intersection_shape,
|
||||
shape_result.merging_map);
|
||||
|
||||
auto intersection = turn_analysis.AssignTurnTypes(
|
||||
node_along_road_entering, incoming_edge, intersection_with_flags_and_angles);
|
||||
|
||||
BOOST_ASSERT(intersection.valid());
|
||||
|
||||
intersection =
|
||||
turn_lane_handler.assignTurnLanes(node_u, edge_from_u, std::move(intersection));
|
||||
|
||||
const auto possible_turns = turn_analysis.transformIntersectionIntoTurns(intersection);
|
||||
intersection = turn_lane_handler.assignTurnLanes(
|
||||
node_along_road_entering, incoming_edge, std::move(intersection));
|
||||
|
||||
// the entry class depends on the turn, so we have to classify the interesction for
|
||||
// every edge
|
||||
@ -412,12 +447,16 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
return bearing_class_hash.find(bearing_class)->second;
|
||||
}
|
||||
}(turn_classification.second);
|
||||
bearing_class_by_node_based_node[node_v] = bearing_class_id;
|
||||
bearing_class_by_node_based_node[node_at_center_of_intersection] = bearing_class_id;
|
||||
|
||||
for (const auto turn : possible_turns)
|
||||
for (const auto &turn : intersection)
|
||||
{
|
||||
// only keep valid turns
|
||||
if (!turn.entry_allowed)
|
||||
continue;
|
||||
|
||||
// only add an edge if turn is not prohibited
|
||||
const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(edge_from_u);
|
||||
const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(incoming_edge);
|
||||
const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(turn.eid);
|
||||
|
||||
BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id);
|
||||
@ -426,7 +465,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
|
||||
// the following is the core of the loop.
|
||||
unsigned distance = edge_data1.distance;
|
||||
if (m_traffic_lights.find(node_v) != m_traffic_lights.end())
|
||||
if (m_traffic_lights.find(node_at_center_of_intersection) != m_traffic_lights.end())
|
||||
{
|
||||
distance += profile_properties.traffic_signal_penalty;
|
||||
}
|
||||
@ -435,7 +474,6 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
scripting_environment.GetTurnPenalty(180. - turn.angle);
|
||||
|
||||
const auto turn_instruction = turn.instruction;
|
||||
|
||||
if (turn_instruction.direction_modifier == guidance::DirectionModifier::UTurn)
|
||||
{
|
||||
distance += profile_properties.u_turn_penalty;
|
||||
@ -447,15 +485,15 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
distance += turn_penalty;
|
||||
|
||||
const bool is_encoded_forwards =
|
||||
m_compressed_edge_container.HasZippedEntryForForwardID(edge_from_u);
|
||||
m_compressed_edge_container.HasZippedEntryForForwardID(incoming_edge);
|
||||
const bool is_encoded_backwards =
|
||||
m_compressed_edge_container.HasZippedEntryForReverseID(edge_from_u);
|
||||
m_compressed_edge_container.HasZippedEntryForReverseID(incoming_edge);
|
||||
BOOST_ASSERT(is_encoded_forwards || is_encoded_backwards);
|
||||
if (is_encoded_forwards)
|
||||
{
|
||||
original_edge_data_vector.emplace_back(
|
||||
GeometryID{
|
||||
m_compressed_edge_container.GetZippedPositionForForwardID(edge_from_u),
|
||||
GeometryID{m_compressed_edge_container.GetZippedPositionForForwardID(
|
||||
incoming_edge),
|
||||
true},
|
||||
edge_data1.name_id,
|
||||
turn.lane_data_id,
|
||||
@ -468,8 +506,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
else if (is_encoded_backwards)
|
||||
{
|
||||
original_edge_data_vector.emplace_back(
|
||||
GeometryID{
|
||||
m_compressed_edge_container.GetZippedPositionForReverseID(edge_from_u),
|
||||
GeometryID{m_compressed_edge_container.GetZippedPositionForReverseID(
|
||||
incoming_edge),
|
||||
false},
|
||||
edge_data1.name_id,
|
||||
turn.lane_data_id,
|
||||
@ -498,6 +536,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
distance,
|
||||
true,
|
||||
false);
|
||||
BOOST_ASSERT(original_edges_counter == m_edge_based_edge_list.size());
|
||||
|
||||
// Here is where we write out the mapping between the edge-expanded edges, and
|
||||
// the node-based edges that are originally used to calculate the `distance`
|
||||
@ -514,8 +553,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
if (generate_edge_lookup)
|
||||
{
|
||||
const auto node_based_edges =
|
||||
m_compressed_edge_container.GetBucketReference(edge_from_u);
|
||||
NodeID previous = node_u;
|
||||
m_compressed_edge_container.GetBucketReference(incoming_edge);
|
||||
NodeID previous = node_along_road_entering;
|
||||
|
||||
const unsigned node_count = node_based_edges.size() + 1;
|
||||
const QueryNode &first_node = m_node_info_list[previous];
|
||||
@ -547,18 +586,19 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
|
||||
// If this edge is 'trivial' -- where the compressed edge corresponds
|
||||
// exactly to an original OSM segment -- we can pull the turn's preceding
|
||||
// node ID directly with `node_u`; otherwise, we need to look up the node
|
||||
// node ID directly with `node_along_road_entering`; otherwise, we need to look
|
||||
// up the node
|
||||
// immediately preceding the turn from the compressed edge container.
|
||||
const bool isTrivial = m_compressed_edge_container.IsTrivial(edge_from_u);
|
||||
const bool isTrivial = m_compressed_edge_container.IsTrivial(incoming_edge);
|
||||
|
||||
const auto &from_node =
|
||||
isTrivial
|
||||
? m_node_info_list[node_u]
|
||||
? m_node_info_list[node_along_road_entering]
|
||||
: m_node_info_list[m_compressed_edge_container.GetLastEdgeSourceID(
|
||||
edge_from_u)];
|
||||
incoming_edge)];
|
||||
const auto &via_node =
|
||||
m_node_info_list[m_compressed_edge_container.GetLastEdgeTargetID(
|
||||
edge_from_u)];
|
||||
incoming_edge)];
|
||||
const auto &to_node =
|
||||
m_node_info_list[m_compressed_edge_container.GetFirstEdgeTargetID(
|
||||
turn.eid)];
|
||||
|
@ -245,7 +245,6 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
// Transform the node-based graph that OSM is based on into an edge-based graph
|
||||
// that is better for routing. Every edge becomes a node, and every valid
|
||||
// movement (e.g. turn from A->B, and B->A) becomes an edge
|
||||
//
|
||||
util::SimpleLogger().Write() << "Generating edge-expanded graph representation";
|
||||
|
||||
TIMER_START(expansion);
|
||||
|
@ -32,7 +32,7 @@ const constexpr double LOOKAHEAD_DISTANCE_WITHOUT_LANES = 10.0;
|
||||
// smaller widths, ranging from 2.5 to 3.25 meters. As a compromise, we use
|
||||
// the 3.25 here for our angle calculations
|
||||
const constexpr double ASSUMED_LANE_WIDTH = 3.25;
|
||||
const constexpr double FAR_LOOKAHEAD_DISTANCE = 20.0;
|
||||
const constexpr double FAR_LOOKAHEAD_DISTANCE = 40.0;
|
||||
|
||||
// The count of lanes assumed when no lanes are present. Since most roads will have lanes for both
|
||||
// directions or a lane count specified, we use 2. Overestimating only makes our calculations safer,
|
||||
@ -94,16 +94,34 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
const std::uint8_t intersection_lanes,
|
||||
std::vector<util::Coordinate> coordinates) const
|
||||
{
|
||||
const auto is_valid_result = [&](const util::Coordinate coordinate) {
|
||||
return util::Coordinate(traversed_in_reverse
|
||||
? node_coordinates[to_node]
|
||||
: node_coordinates[intersection_node]) != coordinate;
|
||||
};
|
||||
// this is only used for debug purposes in assertions. We don't want warnings about it
|
||||
(void)is_valid_result;
|
||||
|
||||
// the lane count might not always be set. We need to assume a positive number, though. Here we
|
||||
// select the number of lanes to operate on
|
||||
const auto considered_lanes =
|
||||
GetOffsetCorrectionFactor(node_based_graph.GetEdgeData(turn_edge).road_classification) *
|
||||
((intersection_lanes == 0) ? ASSUMED_LANE_COUNT : intersection_lanes);
|
||||
|
||||
// Fallback. These roads are small broken self-loops that shouldn't be in the data at all
|
||||
if (intersection_node == to_node)
|
||||
return coordinates[1];
|
||||
|
||||
/* if we are looking at a straight line, we don't care where exactly the coordinate
|
||||
* is. Simply return the final coordinate. Turn angles/turn vectors are the same no matter which
|
||||
* coordinate we look at.
|
||||
*/
|
||||
if (coordinates.size() <= 2)
|
||||
{
|
||||
// Here we can't check for validity, due to possible dead-ends with repeated coordinates
|
||||
// BOOST_ASSERT(is_valid_result(coordinates.back()));
|
||||
return coordinates.back();
|
||||
}
|
||||
|
||||
// due to repeated coordinates / smaller offset errors we skip over the very first parts of the
|
||||
// coordinate set to add a small level of fault tolerance
|
||||
@ -111,9 +129,12 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
|
||||
// fallback, mostly necessary for dead ends
|
||||
if (intersection_node == to_node)
|
||||
return TrimCoordinatesToLength(std::move(coordinates),
|
||||
distance_to_skip_over_due_to_coordinate_inaccuracies)
|
||||
.back();
|
||||
{
|
||||
const auto result = ExtractCoordinateAtLength(
|
||||
distance_to_skip_over_due_to_coordinate_inaccuracies, coordinates);
|
||||
BOOST_ASSERT(is_valid_result(coordinates.back()));
|
||||
return result;
|
||||
}
|
||||
|
||||
// If this reduction leaves us with only two coordinates, the turns/angles are represented in a
|
||||
// valid way. Only curved roads and other difficult scenarios will require multiple coordinates.
|
||||
@ -124,9 +145,12 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
|
||||
// roundabouts, check early to avoid other costly checks
|
||||
if (turn_edge_data.roundabout || turn_edge_data.circular)
|
||||
return TrimCoordinatesToLength(std::move(coordinates),
|
||||
distance_to_skip_over_due_to_coordinate_inaccuracies)
|
||||
.back();
|
||||
{
|
||||
const auto result = ExtractCoordinateAtLength(
|
||||
distance_to_skip_over_due_to_coordinate_inaccuracies, coordinates);
|
||||
BOOST_ASSERT(is_valid_result(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
const util::Coordinate turn_coordinate =
|
||||
node_coordinates[traversed_in_reverse ? to_node : intersection_node];
|
||||
@ -144,10 +168,18 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
if (coordinates.size() > 2 &&
|
||||
util::coordinate_calculation::haversineDistance(turn_coordinate, coordinates[1]) <
|
||||
ASSUMED_LANE_WIDTH)
|
||||
return GetCorrectedCoordinate(turn_coordinate, coordinates[1], coordinates.back());
|
||||
{
|
||||
const auto result =
|
||||
GetCorrectedCoordinate(turn_coordinate, coordinates[1], coordinates.back());
|
||||
BOOST_ASSERT(is_valid_result(result));
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(is_valid_result(coordinates.back()));
|
||||
return coordinates.back();
|
||||
}
|
||||
}
|
||||
|
||||
const auto first_distance =
|
||||
util::coordinate_calculation::haversineDistance(coordinates[0], coordinates[1]);
|
||||
@ -165,6 +197,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
|
||||
if (first_coordinate_is_far_away)
|
||||
{
|
||||
BOOST_ASSERT(is_valid_result(coordinates[1]));
|
||||
return coordinates[1];
|
||||
}
|
||||
|
||||
@ -197,9 +230,16 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
segment_distances.back() = std::min(segment_distances.back(), lookahead_distance);
|
||||
BOOST_ASSERT(segment_distances.size() == coordinates.size());
|
||||
|
||||
// if we are now left with two, well than we don't have to worry
|
||||
if (coordinates.size() == 2)
|
||||
const auto total_distance =
|
||||
std::accumulate(segment_distances.begin(), segment_distances.end(), 0.);
|
||||
|
||||
// if we are now left with two, well than we don't have to worry, or the segment is very small
|
||||
if (coordinates.size() == 2 ||
|
||||
total_distance <= distance_to_skip_over_due_to_coordinate_inaccuracies)
|
||||
{
|
||||
BOOST_ASSERT(is_valid_result(coordinates.back()));
|
||||
return coordinates.back();
|
||||
}
|
||||
|
||||
const double max_deviation_from_straight = GetMaxDeviation(
|
||||
coordinates.begin(), coordinates.end(), coordinates.front(), coordinates.back());
|
||||
@ -207,7 +247,22 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
// if the deviation from a straight line is small, we can savely use the coordinate. We use half
|
||||
// a lane as heuristic to determine if the road is straight enough.
|
||||
if (max_deviation_from_straight < 0.5 * ASSUMED_LANE_WIDTH)
|
||||
{
|
||||
// At loops in traffic circles, we can have small deviations as well (if the circle is tiny)
|
||||
// As a back-up, we have to check for this case
|
||||
if (coordinates.front() == coordinates.back())
|
||||
{
|
||||
const auto result = ExtractCoordinateAtLength(
|
||||
distance_to_skip_over_due_to_coordinate_inaccuracies, coordinates);
|
||||
BOOST_ASSERT(is_valid_result(result));
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(is_valid_result(coordinates.back()));
|
||||
return coordinates.back();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* if a road turns barely in the beginning, it is similar to the first coordinate being
|
||||
@ -243,7 +298,9 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
if (starts_of_without_turn)
|
||||
{
|
||||
// skip over repeated coordinates
|
||||
return TrimCoordinatesToLength(std::move(coordinates), 5, segment_distances).back();
|
||||
const auto result = ExtractCoordinateAtLength(5, coordinates, segment_distances);
|
||||
BOOST_ASSERT(is_valid_result(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
// compute the regression vector based on the sum of least squares
|
||||
@ -274,18 +331,20 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
util::coordinate_calculation::projectPointOnSegment(
|
||||
regression_line.first, regression_line.second, coordinates.back())
|
||||
.second;
|
||||
return GetCorrectedCoordinate(turn_coordinate, coord_between_front, coord_between_back);
|
||||
const auto result =
|
||||
GetCorrectedCoordinate(turn_coordinate, coord_between_front, coord_between_back);
|
||||
BOOST_ASSERT(is_valid_result(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto total_distance =
|
||||
std::accumulate(segment_distances.begin(), segment_distances.end(), 0.);
|
||||
|
||||
if (IsCurve(coordinates,
|
||||
segment_distances,
|
||||
total_distance,
|
||||
considered_lanes * 0.5 * ASSUMED_LANE_WIDTH,
|
||||
turn_edge_data))
|
||||
{
|
||||
if (total_distance <= distance_to_skip_over_due_to_coordinate_inaccuracies)
|
||||
return coordinates.back();
|
||||
/*
|
||||
* In curves we now have to distinguish between larger curves and tiny curves modelling the
|
||||
* actual turn in the beginnig.
|
||||
@ -293,7 +352,8 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
* We distinguish between turns that simply model the initial way of getting onto the
|
||||
* destination lanes and the ones that performa a larger turn.
|
||||
*/
|
||||
const double offset = 0.5 * considered_lanes * ASSUMED_LANE_WIDTH;
|
||||
const double offset =
|
||||
std::min(0.5 * considered_lanes * ASSUMED_LANE_WIDTH, 0.2 * segment_distances.back());
|
||||
coordinates = TrimCoordinatesToLength(std::move(coordinates), offset, segment_distances);
|
||||
BOOST_ASSERT(coordinates.size() >= 2);
|
||||
segment_distances.resize(coordinates.size());
|
||||
@ -303,7 +363,10 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
coordinates =
|
||||
TrimCoordinatesToLength(std::move(coordinates), 0.5 * offset, segment_distances);
|
||||
BOOST_ASSERT(coordinates.size() >= 2);
|
||||
return GetCorrectedCoordinate(turn_coordinate, coordinates.back(), vector_head);
|
||||
const auto result =
|
||||
GetCorrectedCoordinate(turn_coordinate, coordinates.back(), vector_head);
|
||||
BOOST_ASSERT(is_valid_result(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (IsDirectOffset(coordinates,
|
||||
@ -317,8 +380,11 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
// compare
|
||||
// http://www.openstreetmap.org/search?query=52.411243%2013.363575#map=19/52.41124/13.36357
|
||||
const auto offset_index = std::max<decltype(straight_index)>(1, straight_index);
|
||||
return GetCorrectedCoordinate(
|
||||
const auto result = GetCorrectedCoordinate(
|
||||
turn_coordinate, coordinates[offset_index], coordinates[offset_index + 1]);
|
||||
|
||||
BOOST_ASSERT(is_valid_result(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
{
|
||||
@ -340,16 +406,95 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
|
||||
regression_line_trimmed.second);
|
||||
|
||||
if (max_deviation_from_trimmed_regression < 0.5 * ASSUMED_LANE_WIDTH)
|
||||
return GetCorrectedCoordinate(
|
||||
{
|
||||
const auto result = GetCorrectedCoordinate(
|
||||
turn_coordinate, regression_line_trimmed.first, regression_line_trimmed.second);
|
||||
BOOST_ASSERT(is_valid_result(result));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We use the locations on the regression line to offset the regression line onto the
|
||||
// intersection.
|
||||
return TrimCoordinatesToLength(
|
||||
std::move(coordinates), LOOKAHEAD_DISTANCE_WITHOUT_LANES, segment_distances)
|
||||
.back();
|
||||
const auto result =
|
||||
ExtractCoordinateAtLength(LOOKAHEAD_DISTANCE_WITHOUT_LANES, coordinates, segment_distances);
|
||||
BOOST_ASSERT(is_valid_result(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
util::Coordinate
|
||||
CoordinateExtractor::ExtractCoordinateAtLength(const double distance,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const std::vector<double> &length_cache) const
|
||||
{
|
||||
BOOST_ASSERT(length_cache.size() == coordinates.size());
|
||||
BOOST_ASSERT(!coordinates.empty());
|
||||
|
||||
double accumulated_distance = 0.;
|
||||
auto length_cache_itr = length_cache.begin() + 1;
|
||||
// find the end of the segment containing the coordinate which is at least distance away
|
||||
const auto find_coordinate_at_distance = [distance, &accumulated_distance, &length_cache_itr](
|
||||
const util::Coordinate /*coordinate*/) mutable {
|
||||
const auto result = (accumulated_distance + *length_cache_itr) >= distance;
|
||||
if (!result)
|
||||
{
|
||||
accumulated_distance += *length_cache_itr;
|
||||
++length_cache_itr;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// find the beginning fo the segment (begin here and above for the length cache need to match
|
||||
// up!)
|
||||
const auto coordinate_base =
|
||||
std::find_if(coordinates.begin() + 1, coordinates.end(), find_coordinate_at_distance) - 1;
|
||||
|
||||
if (static_cast<std::size_t>(std::distance(coordinates.begin(), coordinate_base) + 1) >=
|
||||
coordinates.size())
|
||||
return coordinates.back();
|
||||
|
||||
const auto interpolation_factor =
|
||||
ComputeInterpolationFactor(distance - accumulated_distance, 0, *length_cache_itr);
|
||||
|
||||
return util::coordinate_calculation::interpolateLinear(
|
||||
interpolation_factor, *coordinate_base, *(coordinate_base + 1));
|
||||
}
|
||||
|
||||
util::Coordinate CoordinateExtractor::ExtractCoordinateAtLength(
|
||||
const double distance, const std::vector<util::Coordinate> &coordinates) const
|
||||
{
|
||||
BOOST_ASSERT(!coordinates.empty());
|
||||
double accumulated_distance = 0.;
|
||||
// checks (via its state) for an accumulated distance
|
||||
const auto coordinate_at_distance =
|
||||
[ distance, &accumulated_distance, last_coordinate = coordinates.front() ](
|
||||
const util::Coordinate coordinate) mutable
|
||||
{
|
||||
const double segment_distance =
|
||||
util::coordinate_calculation::haversineDistance(last_coordinate, coordinate);
|
||||
const auto result = (accumulated_distance + segment_distance) >= distance;
|
||||
if (!result)
|
||||
accumulated_distance += segment_distance;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// find the begin of the segment containing the coordinate
|
||||
const auto coordinate_base =
|
||||
std::find_if(coordinates.begin() + 1, coordinates.end(), coordinate_at_distance) - 1;
|
||||
|
||||
if (static_cast<std::size_t>(std::distance(coordinates.begin(), coordinate_base) + 1) >=
|
||||
coordinates.size())
|
||||
return coordinates.back();
|
||||
|
||||
const auto interpolation_factor = ComputeInterpolationFactor(
|
||||
distance - accumulated_distance,
|
||||
0,
|
||||
util::coordinate_calculation::haversineDistance(*coordinate_base, *(coordinate_base + 1)));
|
||||
|
||||
return util::coordinate_calculation::interpolateLinear(
|
||||
interpolation_factor, *coordinate_base, *(coordinate_base + 1));
|
||||
}
|
||||
|
||||
util::Coordinate CoordinateExtractor::GetCoordinateCloseToTurn(const NodeID from_node,
|
||||
@ -691,7 +836,7 @@ bool CoordinateExtractor::IsDirectOffset(const std::vector<util::Coordinate> &co
|
||||
return false;
|
||||
|
||||
// the segment itself cannot be short
|
||||
if (segment_length < 0.8 * FAR_LOOKAHEAD_DISTANCE)
|
||||
if (segment_length < 0.4 * FAR_LOOKAHEAD_DISTANCE)
|
||||
return false;
|
||||
|
||||
// if the remaining segment is short, we don't consider it an offset
|
||||
@ -733,16 +878,18 @@ CoordinateExtractor::PrepareLengthCache(const std::vector<util::Coordinate> &coo
|
||||
segment_distances.reserve(coordinates.size());
|
||||
segment_distances.push_back(0);
|
||||
// sentinel
|
||||
auto last_coordinate = coordinates.front();
|
||||
std::find_if(
|
||||
std::next(std::begin(coordinates)),
|
||||
std::end(coordinates),
|
||||
[&last_coordinate, limit, &segment_distances](const util::Coordinate current_coordinate) {
|
||||
const auto distance = util::coordinate_calculation::haversineDistance(
|
||||
last_coordinate, current_coordinate);
|
||||
std::find_if(std::next(std::begin(coordinates)), std::end(coordinates), [
|
||||
last_coordinate = coordinates.front(),
|
||||
limit,
|
||||
&segment_distances,
|
||||
accumulated_distance = 0.
|
||||
](const util::Coordinate current_coordinate) mutable {
|
||||
const auto distance =
|
||||
util::coordinate_calculation::haversineDistance(last_coordinate, current_coordinate);
|
||||
accumulated_distance += distance;
|
||||
last_coordinate = current_coordinate;
|
||||
segment_distances.push_back(distance);
|
||||
return distance >= limit;
|
||||
return accumulated_distance >= limit;
|
||||
});
|
||||
return segment_distances;
|
||||
}
|
||||
@ -775,22 +922,19 @@ CoordinateExtractor::TrimCoordinatesToLength(std::vector<util::Coordinate> coord
|
||||
|
||||
if (use_cache && length_cache.back() < desired_length && coordinates.size() >= 2)
|
||||
{
|
||||
if (coordinates.size() == length_cache.size())
|
||||
return coordinates;
|
||||
if (coordinates.size() > length_cache.size())
|
||||
coordinates.erase(coordinates.begin() + length_cache.size(), coordinates.end());
|
||||
|
||||
else
|
||||
{
|
||||
const auto distance_between_last_coordinates =
|
||||
util::coordinate_calculation::haversineDistance(*(coordinates.end() - 2),
|
||||
*(coordinates.end() - 1));
|
||||
const auto interpolation_factor = ComputeInterpolationFactor(
|
||||
desired_length - length_cache.back(), 0, distance_between_last_coordinates);
|
||||
const auto interpolation_factor =
|
||||
ComputeInterpolationFactor(length_cache.back(), 0, distance_between_last_coordinates);
|
||||
|
||||
coordinates.back() = util::coordinate_calculation::interpolateLinear(
|
||||
interpolation_factor, *(coordinates.end() - 2), coordinates.back());
|
||||
return coordinates;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(!use_cache || length_cache.back() >= desired_length);
|
||||
|
@ -17,11 +17,9 @@ namespace extractor
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
ConnectedRoad::ConnectedRoad(const TurnOperation turn,
|
||||
const bool entry_allowed,
|
||||
boost::optional<double> segment_length)
|
||||
: TurnOperation(turn), entry_allowed(entry_allowed), segment_length(segment_length)
|
||||
bool IntersectionViewData::CompareByAngle(const IntersectionViewData &other) const
|
||||
{
|
||||
return angle < other.angle;
|
||||
}
|
||||
|
||||
bool ConnectedRoad::compareByAngle(const ConnectedRoad &other) const { return angle < other.angle; }
|
||||
@ -72,6 +70,23 @@ std::string toString(const ConnectedRoad &road)
|
||||
return result;
|
||||
}
|
||||
|
||||
IntersectionView::Base::iterator IntersectionView::findClosestTurn(double angle)
|
||||
{
|
||||
// use the const operator to avoid code duplication
|
||||
return begin() +
|
||||
std::distance(cbegin(),
|
||||
static_cast<const IntersectionView *>(this)->findClosestTurn(angle));
|
||||
}
|
||||
|
||||
IntersectionView::Base::const_iterator IntersectionView::findClosestTurn(double angle) const
|
||||
{
|
||||
return std::min_element(
|
||||
begin(), end(), [angle](const IntersectionViewData &lhs, const IntersectionViewData &rhs) {
|
||||
return util::guidance::angularDeviation(lhs.angle, angle) <
|
||||
util::guidance::angularDeviation(rhs.angle, angle);
|
||||
});
|
||||
}
|
||||
|
||||
Intersection::Base::iterator Intersection::findClosestTurn(double angle)
|
||||
{
|
||||
// use the const operator to avoid code duplication
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/guidance/toolkit.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
@ -39,11 +40,88 @@ IntersectionGenerator::IntersectionGenerator(
|
||||
{
|
||||
}
|
||||
|
||||
Intersection IntersectionGenerator::operator()(const NodeID from_node, const EdgeID via_eid) const
|
||||
IntersectionView IntersectionGenerator::operator()(const NodeID from_node,
|
||||
const EdgeID via_eid) const
|
||||
{
|
||||
return GetConnectedRoads(from_node, via_eid, USE_HIGH_PRECISION_MODE);
|
||||
}
|
||||
|
||||
IntersectionShape
|
||||
IntersectionGenerator::ComputeIntersectionShape(const NodeID node_at_center_of_intersection,
|
||||
const boost::optional<NodeID> sorting_base,
|
||||
const bool use_low_precision_angles) const
|
||||
{
|
||||
IntersectionShape intersection;
|
||||
// reserve enough items (+ the possibly missing u-turn edge)
|
||||
const auto intersection_degree = node_based_graph.GetOutDegree(node_at_center_of_intersection);
|
||||
intersection.reserve(intersection_degree);
|
||||
const util::Coordinate turn_coordinate = node_info_list[node_at_center_of_intersection];
|
||||
|
||||
// number of lanes at the intersection changes how far we look down the road
|
||||
const auto intersection_lanes =
|
||||
getLaneCountAtIntersection(node_at_center_of_intersection, node_based_graph);
|
||||
|
||||
for (const EdgeID edge_connected_to_intersection :
|
||||
node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection))
|
||||
{
|
||||
BOOST_ASSERT(edge_connected_to_intersection != SPECIAL_EDGEID);
|
||||
const NodeID to_node = node_based_graph.GetTarget(edge_connected_to_intersection);
|
||||
double bearing = 0.;
|
||||
|
||||
auto coordinates = coordinate_extractor.GetCoordinatesAlongRoad(
|
||||
node_at_center_of_intersection, edge_connected_to_intersection, !INVERT, to_node);
|
||||
|
||||
const auto segment_length = util::coordinate_calculation::getLength(
|
||||
coordinates, util::coordinate_calculation::haversineDistance);
|
||||
|
||||
const auto extract_coordinate = [&](const NodeID from_node,
|
||||
const EdgeID via_eid,
|
||||
const bool traversed_in_reverse,
|
||||
const NodeID to_node) {
|
||||
return (use_low_precision_angles || intersection_degree <= 2)
|
||||
? coordinate_extractor.GetCoordinateCloseToTurn(
|
||||
from_node, via_eid, traversed_in_reverse, to_node)
|
||||
: coordinate_extractor.ExtractRepresentativeCoordinate(
|
||||
from_node,
|
||||
via_eid,
|
||||
traversed_in_reverse,
|
||||
to_node,
|
||||
intersection_lanes,
|
||||
std::move(coordinates));
|
||||
};
|
||||
|
||||
// we have to look down the road a bit to get the correct turn
|
||||
const auto coordinate_along_edge_leaving = extract_coordinate(
|
||||
node_at_center_of_intersection, edge_connected_to_intersection, !INVERT, to_node);
|
||||
|
||||
bearing =
|
||||
util::coordinate_calculation::bearing(turn_coordinate, coordinate_along_edge_leaving);
|
||||
|
||||
intersection.push_back({edge_connected_to_intersection, bearing, segment_length});
|
||||
}
|
||||
|
||||
if (!intersection.empty())
|
||||
{
|
||||
const auto base_bearing = [&]() {
|
||||
if (sorting_base)
|
||||
{
|
||||
const auto itr =
|
||||
std::find_if(intersection.begin(),
|
||||
intersection.end(),
|
||||
[&](const IntersectionShapeData &data) {
|
||||
return node_based_graph.GetTarget(data.eid) == *sorting_base;
|
||||
});
|
||||
if (itr != intersection.end())
|
||||
return util::bearing::reverseBearing(itr->bearing);
|
||||
}
|
||||
return util::bearing::reverseBearing(intersection.begin()->bearing);
|
||||
}();
|
||||
std::sort(
|
||||
intersection.begin(), intersection.end(), makeCompareShapeDataByBearing(base_bearing));
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
|
||||
// a
|
||||
// |
|
||||
// |
|
||||
@ -57,185 +135,22 @@ Intersection IntersectionGenerator::operator()(const NodeID from_node, const Edg
|
||||
// That means we not only get (from_node, turn_node, c) in the above example
|
||||
// but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are
|
||||
// marked as invalid and only needed for intersection classification.
|
||||
Intersection IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
|
||||
IntersectionView IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
|
||||
const EdgeID via_eid,
|
||||
const bool use_low_precision_angles) const
|
||||
{
|
||||
Intersection intersection;
|
||||
const NodeID turn_node = node_based_graph.GetTarget(via_eid);
|
||||
// reserve enough items (+ the possibly missing u-turn edge)
|
||||
intersection.reserve(node_based_graph.GetOutDegree(turn_node) + 1);
|
||||
const NodeID only_restriction_to_node = [&]() {
|
||||
// If only restrictions refer to invalid ways somewhere far away, we rather ignore the
|
||||
// restriction than to not route over the intersection at all.
|
||||
const auto only_restriction_to_node =
|
||||
restriction_map.CheckForEmanatingIsOnlyTurn(from_node, turn_node);
|
||||
if (only_restriction_to_node != SPECIAL_NODEID)
|
||||
{
|
||||
// check if we can find an edge in the edge-rage
|
||||
for (const auto onto_edge : node_based_graph.GetAdjacentEdgeRange(turn_node))
|
||||
if (only_restriction_to_node == node_based_graph.GetTarget(onto_edge))
|
||||
return only_restriction_to_node;
|
||||
}
|
||||
// Ignore broken only restrictions.
|
||||
return SPECIAL_NODEID;
|
||||
}();
|
||||
const bool is_barrier_node = barrier_nodes.find(turn_node) != barrier_nodes.end();
|
||||
// make sure the via-eid is valid
|
||||
BOOST_ASSERT([this](const NodeID from_node, const EdgeID via_eid) {
|
||||
const auto range = node_based_graph.GetAdjacentEdgeRange(from_node);
|
||||
return range.front() <= via_eid && via_eid <= range.back();
|
||||
}(from_node, via_eid));
|
||||
|
||||
bool has_uturn_edge = false;
|
||||
bool uturn_could_be_valid = false;
|
||||
const util::Coordinate turn_coordinate = node_info_list[turn_node];
|
||||
|
||||
const auto intersection_lanes = getLaneCountAtIntersection(turn_node, node_based_graph);
|
||||
|
||||
const auto extract_coordinate = [&](const NodeID from_node,
|
||||
const EdgeID via_eid,
|
||||
const bool traversed_in_reverse,
|
||||
const NodeID to_node) {
|
||||
return use_low_precision_angles
|
||||
? coordinate_extractor.GetCoordinateCloseToTurn(
|
||||
from_node, via_eid, traversed_in_reverse, to_node)
|
||||
: coordinate_extractor.GetCoordinateAlongRoad(
|
||||
from_node, via_eid, traversed_in_reverse, to_node, intersection_lanes);
|
||||
};
|
||||
|
||||
// The first coordinate (the origin) can depend on the number of lanes turning onto,
|
||||
// just as the target coordinate can. Here we compute the corrected coordinate for the
|
||||
// incoming edge
|
||||
|
||||
// to compute the length along the path
|
||||
const auto in_segment_length = [&]() {
|
||||
const auto in_coordinates =
|
||||
coordinate_extractor.GetCoordinatesAlongRoad(from_node, via_eid, INVERT, turn_node);
|
||||
return util::coordinate_calculation::getLength(
|
||||
in_coordinates, util::coordinate_calculation::haversineDistance);
|
||||
}();
|
||||
const auto first_coordinate = extract_coordinate(from_node, via_eid, INVERT, turn_node);
|
||||
|
||||
for (const EdgeID onto_edge : node_based_graph.GetAdjacentEdgeRange(turn_node))
|
||||
{
|
||||
BOOST_ASSERT(onto_edge != SPECIAL_EDGEID);
|
||||
const NodeID to_node = node_based_graph.GetTarget(onto_edge);
|
||||
const auto &onto_data = node_based_graph.GetEdgeData(onto_edge);
|
||||
|
||||
bool turn_is_valid =
|
||||
// reverse edges are never valid turns because the resulting turn would look like this:
|
||||
// from_node --via_edge--> turn_node <--onto_edge-- to_node
|
||||
// however we need this for capture intersection shape for incoming one-ways
|
||||
!onto_data.reversed &&
|
||||
// we are not turning over a barrier
|
||||
(!is_barrier_node || from_node == to_node) &&
|
||||
// We are at an only_-restriction but not at the right turn.
|
||||
(only_restriction_to_node == SPECIAL_NODEID || to_node == only_restriction_to_node) &&
|
||||
// the turn is not restricted
|
||||
!restriction_map.CheckIfTurnIsRestricted(from_node, turn_node, to_node);
|
||||
|
||||
double bearing = 0., out_segment_length = 0., angle = 0.;
|
||||
if (from_node == to_node)
|
||||
{
|
||||
bearing = util::coordinate_calculation::bearing(turn_coordinate, first_coordinate);
|
||||
uturn_could_be_valid = turn_is_valid;
|
||||
if (turn_is_valid && !is_barrier_node)
|
||||
{
|
||||
// we only add u-turns for dead-end streets.
|
||||
if (node_based_graph.GetOutDegree(turn_node) > 1)
|
||||
{
|
||||
auto number_of_emmiting_bidirectional_edges = 0;
|
||||
for (auto edge : node_based_graph.GetAdjacentEdgeRange(turn_node))
|
||||
{
|
||||
auto target = node_based_graph.GetTarget(edge);
|
||||
auto reverse_edge = node_based_graph.FindEdge(target, turn_node);
|
||||
BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID);
|
||||
if (!node_based_graph.GetEdgeData(reverse_edge).reversed)
|
||||
{
|
||||
++number_of_emmiting_bidirectional_edges;
|
||||
}
|
||||
}
|
||||
// is a dead-end, only possible road is to go back
|
||||
turn_is_valid = number_of_emmiting_bidirectional_edges <= 1;
|
||||
}
|
||||
}
|
||||
has_uturn_edge = true;
|
||||
out_segment_length = in_segment_length;
|
||||
BOOST_ASSERT(angle >= 0. && angle < std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
else
|
||||
{
|
||||
// the default distance we lookahead on a road. This distance prevents small mapping
|
||||
// errors to impact the turn angles.
|
||||
{
|
||||
// segment of out segment
|
||||
const auto out_coordinates = coordinate_extractor.GetCoordinatesAlongRoad(
|
||||
turn_node, onto_edge, !INVERT, to_node);
|
||||
out_segment_length = util::coordinate_calculation::getLength(
|
||||
out_coordinates, util::coordinate_calculation::haversineDistance);
|
||||
}
|
||||
const auto third_coordinate = extract_coordinate(turn_node, onto_edge, !INVERT, to_node);
|
||||
|
||||
angle = util::coordinate_calculation::computeAngle(
|
||||
first_coordinate, turn_coordinate, third_coordinate);
|
||||
|
||||
bearing = util::coordinate_calculation::bearing(turn_coordinate, third_coordinate);
|
||||
|
||||
if (std::abs(angle) < std::numeric_limits<double>::epsilon())
|
||||
has_uturn_edge = true;
|
||||
}
|
||||
intersection.push_back(
|
||||
ConnectedRoad(TurnOperation{onto_edge,
|
||||
angle,
|
||||
bearing,
|
||||
{TurnType::Invalid, DirectionModifier::UTurn},
|
||||
INVALID_LANE_DATAID},
|
||||
turn_is_valid,
|
||||
out_segment_length));
|
||||
}
|
||||
|
||||
// We hit the case of a street leading into nothing-ness. Since the code here assumes
|
||||
// that this
|
||||
// will never happen we add an artificial invalid uturn in this case.
|
||||
if (!has_uturn_edge)
|
||||
{
|
||||
const double bearing =
|
||||
util::coordinate_calculation::bearing(turn_coordinate, first_coordinate);
|
||||
|
||||
intersection.push_back({TurnOperation{via_eid,
|
||||
0.,
|
||||
bearing,
|
||||
{TurnType::Invalid, DirectionModifier::UTurn},
|
||||
INVALID_LANE_DATAID},
|
||||
false,
|
||||
in_segment_length});
|
||||
}
|
||||
|
||||
std::sort(std::begin(intersection),
|
||||
std::end(intersection),
|
||||
std::mem_fn(&ConnectedRoad::compareByAngle));
|
||||
|
||||
BOOST_ASSERT(intersection[0].angle >= 0. &&
|
||||
intersection[0].angle < std::numeric_limits<double>::epsilon());
|
||||
|
||||
const auto valid_count =
|
||||
boost::count_if(intersection, [](const ConnectedRoad &road) { return road.entry_allowed; });
|
||||
if (0 == valid_count && uturn_could_be_valid)
|
||||
{
|
||||
// after intersections sorting by angles, find the u-turn with (from_node ==
|
||||
// to_node)
|
||||
// that was inserted together with setting uturn_could_be_valid flag
|
||||
std::size_t self_u_turn = 0;
|
||||
while (self_u_turn < intersection.size() &&
|
||||
intersection[self_u_turn].angle < std::numeric_limits<double>::epsilon() &&
|
||||
from_node != node_based_graph.GetTarget(intersection[self_u_turn].eid))
|
||||
{
|
||||
++self_u_turn;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(from_node == node_based_graph.GetTarget(intersection[self_u_turn].eid));
|
||||
intersection[self_u_turn].entry_allowed = true;
|
||||
}
|
||||
return intersection;
|
||||
auto intersection = ComputeIntersectionShape(
|
||||
node_based_graph.GetTarget(via_eid), boost::none, use_low_precision_angles);
|
||||
return TransformIntersectionShapeIntoView(from_node, via_eid, std::move(intersection));
|
||||
}
|
||||
|
||||
Intersection
|
||||
IntersectionView
|
||||
IntersectionGenerator::GetActualNextIntersection(const NodeID starting_node,
|
||||
const EdgeID via_edge,
|
||||
NodeID *resulting_from_node = nullptr,
|
||||
@ -278,6 +193,191 @@ IntersectionGenerator::GetActualNextIntersection(const NodeID starting_node,
|
||||
return GetConnectedRoads(query_node, query_edge);
|
||||
}
|
||||
|
||||
IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
|
||||
const NodeID previous_node,
|
||||
const EdgeID entering_via_edge,
|
||||
const IntersectionShape &intersection_shape) const
|
||||
{
|
||||
// requires a copy of the intersection
|
||||
return TransformIntersectionShapeIntoView(previous_node,
|
||||
entering_via_edge,
|
||||
intersection_shape, // creates a copy
|
||||
intersection_shape, // reference to local
|
||||
{}); // empty vector of performed merges
|
||||
}
|
||||
|
||||
IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
|
||||
const NodeID previous_node,
|
||||
const EdgeID entering_via_edge,
|
||||
const IntersectionShape &normalised_intersection,
|
||||
const IntersectionShape &intersection,
|
||||
const std::vector<std::pair<EdgeID, EdgeID>> &performed_merges) const
|
||||
{
|
||||
const auto node_at_intersection = node_based_graph.GetTarget(entering_via_edge);
|
||||
|
||||
// check if there is a single valid turn entering the current intersection
|
||||
const auto only_valid_turn = GetOnlyAllowedTurnIfExistent(previous_node, node_at_intersection);
|
||||
|
||||
// barriers change our behaviour regarding u-turns
|
||||
const bool is_barrier_node = barrier_nodes.find(node_at_intersection) != barrier_nodes.end();
|
||||
|
||||
const auto connect_to_previous_node = [this, previous_node](const IntersectionShapeData road) {
|
||||
return node_based_graph.GetTarget(road.eid) == previous_node;
|
||||
};
|
||||
|
||||
// check which of the edges is the u-turn edge
|
||||
const auto uturn_edge_itr =
|
||||
std::find_if(intersection.begin(), intersection.end(), connect_to_previous_node);
|
||||
|
||||
// there needs to be a connection, otherwise stuff went seriously wrong. Note that this is not
|
||||
// necessarily the same id as `entering_via_edge`.
|
||||
// In cases where parallel edges are present, we only remember the minimal edge. Both share
|
||||
// exactly the same coordinates, so the u-turn is still the best choice here.
|
||||
BOOST_ASSERT(uturn_edge_itr != intersection.end());
|
||||
|
||||
const auto is_restricted = [&](const NodeID destination) {
|
||||
// check if we have a dedicated destination
|
||||
if (only_valid_turn && *only_valid_turn != destination)
|
||||
return true;
|
||||
|
||||
// not explicitly forbidden
|
||||
return restriction_map.CheckIfTurnIsRestricted(
|
||||
previous_node, node_at_intersection, destination);
|
||||
};
|
||||
|
||||
const auto is_allowed_turn = [&](const IntersectionShapeData &road) {
|
||||
const auto &road_data = node_based_graph.GetEdgeData(road.eid);
|
||||
const NodeID road_destination_node = node_based_graph.GetTarget(road.eid);
|
||||
// reverse edges are never valid turns because the resulting turn would look like this:
|
||||
// from_node --via_edge--> node_at_intersection <--onto_edge-- to_node
|
||||
// however we need this for capture intersection shape for incoming one-ways
|
||||
return !road_data.reversed &&
|
||||
// we are not turning over a barrier
|
||||
(!is_barrier_node || road_destination_node == previous_node) &&
|
||||
// don't allow restricted turns
|
||||
!is_restricted(road_destination_node);
|
||||
|
||||
};
|
||||
|
||||
// due to merging of roads, the u-turn might actually not be part of the intersection anymore
|
||||
const auto uturn_bearing = [&]() {
|
||||
const auto merge_entry = std::find_if(
|
||||
performed_merges.begin(), performed_merges.end(), [&uturn_edge_itr](const auto entry) {
|
||||
return entry.first == uturn_edge_itr->eid;
|
||||
});
|
||||
if (merge_entry != performed_merges.end())
|
||||
{
|
||||
const auto merged_into_id = merge_entry->second;
|
||||
const auto merged_u_turn = std::find_if(
|
||||
normalised_intersection.begin(),
|
||||
normalised_intersection.end(),
|
||||
[&](const IntersectionShapeData &road) { return road.eid == merged_into_id; });
|
||||
BOOST_ASSERT(merged_u_turn != normalised_intersection.end());
|
||||
return util::bearing::reverseBearing(merged_u_turn->bearing);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto uturn_edge_at_normalised_intersection_itr =
|
||||
std::find_if(normalised_intersection.begin(),
|
||||
normalised_intersection.end(),
|
||||
connect_to_previous_node);
|
||||
BOOST_ASSERT(uturn_edge_at_normalised_intersection_itr !=
|
||||
normalised_intersection.end());
|
||||
return util::bearing::reverseBearing(
|
||||
uturn_edge_at_normalised_intersection_itr->bearing);
|
||||
}
|
||||
}();
|
||||
|
||||
IntersectionView intersection_view;
|
||||
intersection_view.reserve(normalised_intersection.size());
|
||||
std::transform(normalised_intersection.begin(),
|
||||
normalised_intersection.end(),
|
||||
std::back_inserter(intersection_view),
|
||||
[&](const IntersectionShapeData &road) {
|
||||
return IntersectionViewData(
|
||||
road,
|
||||
is_allowed_turn(road),
|
||||
util::bearing::angleBetweenBearings(uturn_bearing, road.bearing));
|
||||
});
|
||||
|
||||
const auto uturn_edge_at_intersection_view_itr =
|
||||
std::find_if(intersection_view.begin(), intersection_view.end(), connect_to_previous_node);
|
||||
// number of found valid exit roads
|
||||
const auto valid_count =
|
||||
std::count_if(intersection_view.begin(),
|
||||
intersection_view.end(),
|
||||
[](const IntersectionViewData &road) { return road.entry_allowed; });
|
||||
// in general, we don't wan't to allow u-turns. If we don't look at a barrier, we have to check
|
||||
// for dead end streets. These are the only ones that we allow uturns for, next to barriers
|
||||
// (which are also kind of a dead end, but we don't have to check these again :))
|
||||
if (uturn_edge_at_intersection_view_itr != intersection_view.end() &&
|
||||
((uturn_edge_at_intersection_view_itr->entry_allowed && !is_barrier_node &&
|
||||
valid_count != 1) ||
|
||||
valid_count == 0))
|
||||
{
|
||||
const auto allow_uturn_at_dead_end = [&]() {
|
||||
const auto &uturn_data = node_based_graph.GetEdgeData(uturn_edge_itr->eid);
|
||||
|
||||
// we can't turn back onto oneway streets
|
||||
if (uturn_data.reversed)
|
||||
return false;
|
||||
|
||||
// don't allow explicitly restricted turns
|
||||
if (is_restricted(previous_node))
|
||||
return false;
|
||||
|
||||
// we define dead ends as roads that can only be entered via the possible u-turn
|
||||
const auto is_bidirectional = [&](const EdgeID entering_via_edge) {
|
||||
const auto to_node = node_based_graph.GetTarget(entering_via_edge);
|
||||
const auto reverse_edge = node_based_graph.FindEdge(to_node, node_at_intersection);
|
||||
BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID);
|
||||
return !node_based_graph.GetEdgeData(reverse_edge).reversed;
|
||||
};
|
||||
|
||||
const auto bidirectional_edges = [&]() {
|
||||
std::uint32_t count = 0;
|
||||
for (const auto eid : node_based_graph.GetAdjacentEdgeRange(node_at_intersection))
|
||||
if (is_bidirectional(eid))
|
||||
++count;
|
||||
return count;
|
||||
}();
|
||||
|
||||
// Checking for dead-end streets is kind of difficult. There is obvious dead ends
|
||||
// (single road connected)
|
||||
return bidirectional_edges <= 1;
|
||||
}();
|
||||
uturn_edge_at_intersection_view_itr->entry_allowed = allow_uturn_at_dead_end;
|
||||
}
|
||||
std::sort(std::begin(intersection_view),
|
||||
std::end(intersection_view),
|
||||
std::mem_fn(&IntersectionViewData::CompareByAngle));
|
||||
|
||||
BOOST_ASSERT(intersection_view[0].angle >= 0. &&
|
||||
intersection_view[0].angle < std::numeric_limits<double>::epsilon());
|
||||
|
||||
return intersection_view;
|
||||
}
|
||||
|
||||
boost::optional<NodeID>
|
||||
IntersectionGenerator::GetOnlyAllowedTurnIfExistent(const NodeID coming_from_node,
|
||||
const NodeID node_at_intersection) const
|
||||
{
|
||||
// If only restrictions refer to invalid ways somewhere far away, we rather ignore the
|
||||
// restriction than to not route over the intersection at all.
|
||||
const auto only_restriction_to_node =
|
||||
restriction_map.CheckForEmanatingIsOnlyTurn(coming_from_node, node_at_intersection);
|
||||
if (only_restriction_to_node != SPECIAL_NODEID)
|
||||
{
|
||||
// if the mentioned node does not exist anymore, we don't return it. This checks for broken
|
||||
// turn restrictions
|
||||
for (const auto onto_edge : node_based_graph.GetAdjacentEdgeRange(node_at_intersection))
|
||||
if (only_restriction_to_node == node_based_graph.GetTarget(onto_edge))
|
||||
return only_restriction_to_node;
|
||||
}
|
||||
// Ignore broken only restrictions.
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
const CoordinateExtractor &IntersectionGenerator::GetCoordinateExtractor() const
|
||||
{
|
||||
return coordinate_extractor;
|
||||
|
@ -749,16 +749,9 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
// obvious due to a very short segment in between. So if the segment coming in is very
|
||||
// short, we check the previous intersection for other continues in the opposite bearing.
|
||||
const auto node_at_intersection = node_based_graph.GetTarget(via_edge);
|
||||
const util::Coordinate coordinate_at_intersection = node_info_list[node_at_intersection];
|
||||
|
||||
const auto node_at_u_turn = node_based_graph.GetTarget(intersection[0].eid);
|
||||
const util::Coordinate coordinate_at_u_turn = node_info_list[node_at_u_turn];
|
||||
|
||||
const double constexpr MAX_COLLAPSE_DISTANCE = 30;
|
||||
const auto distance_at_u_turn = intersection[0].segment_length
|
||||
? *intersection[0].segment_length
|
||||
: util::coordinate_calculation::haversineDistance(
|
||||
coordinate_at_intersection, coordinate_at_u_turn);
|
||||
const auto distance_at_u_turn = intersection[0].segment_length;
|
||||
if (distance_at_u_turn < MAX_COLLAPSE_DISTANCE)
|
||||
{
|
||||
// this request here actually goes against the direction of the ingoing edgeid. This can
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "extractor/guidance/intersection_normalizer.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/guidance/toolkit.hpp"
|
||||
|
||||
namespace osrm
|
||||
@ -21,18 +22,53 @@ IntersectionNormalizer::IntersectionNormalizer(
|
||||
{
|
||||
}
|
||||
|
||||
Intersection IntersectionNormalizer::operator()(const NodeID node_at_intersection,
|
||||
Intersection intersection) const
|
||||
std::pair<IntersectionShape, std::vector<std::pair<EdgeID, EdgeID>>> IntersectionNormalizer::
|
||||
operator()(const NodeID node_at_intersection, IntersectionShape intersection) const
|
||||
{
|
||||
return AdjustForJoiningRoads(
|
||||
node_at_intersection, MergeSegregatedRoads(node_at_intersection, std::move(intersection)));
|
||||
auto merged_shape_and_merges =
|
||||
MergeSegregatedRoads(node_at_intersection, std::move(intersection));
|
||||
merged_shape_and_merges.first = AdjustBearingsForMergeAtDestination(
|
||||
node_at_intersection, std::move(merged_shape_and_merges.first));
|
||||
return merged_shape_and_merges;
|
||||
}
|
||||
|
||||
bool IntersectionNormalizer::CanMerge(const NodeID intersection_node,
|
||||
const IntersectionShape &intersection,
|
||||
std::size_t first_index,
|
||||
std::size_t second_index) const
|
||||
{
|
||||
BOOST_ASSERT(((first_index + 1) % intersection.size()) == second_index);
|
||||
|
||||
// call wrapper to capture intersection_node and intersection
|
||||
const auto mergable = [this, intersection_node, &intersection](const std::size_t left_index,
|
||||
const std::size_t right_index) {
|
||||
return InnerCanMerge(intersection_node, intersection, left_index, right_index);
|
||||
};
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
if (mergable(first_index, second_index))
|
||||
{
|
||||
const auto is_distinct_merge =
|
||||
!mergable(second_index, (second_index + 1) % intersection.size()) &&
|
||||
!mergable((first_index + intersection.size() - 1) % intersection.size(), first_index) &&
|
||||
!mergable(second_index,
|
||||
(first_index + intersection.size() - 1) % intersection.size()) &&
|
||||
!mergable(first_index, (second_index + 1) % intersection.size());
|
||||
return is_distinct_merge;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks for mergability of two ways that represent the same intersection. For further
|
||||
// information
|
||||
// see interface documentation in header.
|
||||
bool IntersectionNormalizer::CanMerge(const NodeID node_at_intersection,
|
||||
const Intersection &intersection,
|
||||
// information see interface documentation in header.
|
||||
bool IntersectionNormalizer::InnerCanMerge(const NodeID node_at_intersection,
|
||||
const IntersectionShape &intersection,
|
||||
std::size_t first_index,
|
||||
std::size_t second_index) const
|
||||
{
|
||||
@ -40,14 +76,17 @@ bool IntersectionNormalizer::CanMerge(const NodeID node_at_intersection,
|
||||
const auto &second_data = node_based_graph.GetEdgeData(intersection[second_index].eid);
|
||||
|
||||
// only merge named ids
|
||||
if (first_data.name_id == EMPTY_NAMEID)
|
||||
if (first_data.name_id == EMPTY_NAMEID || second_data.name_id == EMPTY_NAMEID)
|
||||
return false;
|
||||
|
||||
// need to be same name
|
||||
if (second_data.name_id != EMPTY_NAMEID &&
|
||||
util::guidance::requiresNameAnnounced(
|
||||
if (util::guidance::requiresNameAnnounced(
|
||||
first_data.name_id, second_data.name_id, name_table, street_name_suffix_table))
|
||||
return false;
|
||||
// needs to be symmetrical for names
|
||||
if (util::guidance::requiresNameAnnounced(
|
||||
second_data.name_id, first_data.name_id, name_table, street_name_suffix_table))
|
||||
return false;
|
||||
|
||||
// compatibility is required
|
||||
if (first_data.travel_mode != second_data.travel_mode)
|
||||
@ -64,35 +103,17 @@ bool IntersectionNormalizer::CanMerge(const NodeID node_at_intersection,
|
||||
if (first_data.reversed == second_data.reversed)
|
||||
return false;
|
||||
|
||||
// one of them needs to be invalid
|
||||
if (intersection[first_index].entry_allowed && intersection[second_index].entry_allowed)
|
||||
return false;
|
||||
|
||||
// mergeable if the angle is not too big
|
||||
const auto angle_between =
|
||||
angularDeviation(intersection[first_index].angle, intersection[second_index].angle);
|
||||
|
||||
const auto intersection_lanes = intersection.getHighestConnectedLaneCount(node_based_graph);
|
||||
|
||||
const auto coordinate_at_in_edge =
|
||||
intersection_generator.GetCoordinateExtractor().GetCoordinateAlongRoad(
|
||||
node_at_intersection,
|
||||
intersection[0].eid,
|
||||
!INVERT,
|
||||
node_based_graph.GetTarget(intersection[0].eid),
|
||||
intersection_lanes);
|
||||
angularDeviation(intersection[first_index].bearing, intersection[second_index].bearing);
|
||||
|
||||
const auto coordinate_at_intersection = node_coordinates[node_at_intersection];
|
||||
|
||||
if (angle_between >= 120)
|
||||
return false;
|
||||
|
||||
const auto isValidYArm = [this,
|
||||
intersection,
|
||||
coordinate_at_in_edge,
|
||||
coordinate_at_intersection,
|
||||
node_at_intersection](const std::size_t index,
|
||||
const std::size_t other_index) {
|
||||
const auto isValidYArm = [this, intersection, coordinate_at_intersection, node_at_intersection](
|
||||
const std::size_t index, const std::size_t other_index) {
|
||||
const auto GetActualTarget = [&](const std::size_t index) {
|
||||
EdgeID last_in_edge_id;
|
||||
intersection_generator.GetActualNextIntersection(
|
||||
@ -108,21 +129,19 @@ bool IntersectionNormalizer::CanMerge(const NodeID node_at_intersection,
|
||||
const auto coordinate_at_target = node_coordinates[target_id];
|
||||
const auto coordinate_at_other_target = node_coordinates[other_target_id];
|
||||
|
||||
const auto turn_angle = util::coordinate_calculation::computeAngle(
|
||||
coordinate_at_in_edge, coordinate_at_intersection, coordinate_at_target);
|
||||
const auto other_turn_angle = util::coordinate_calculation::computeAngle(
|
||||
coordinate_at_in_edge, coordinate_at_intersection, coordinate_at_other_target);
|
||||
const auto turn_bearing =
|
||||
util::coordinate_calculation::bearing(coordinate_at_intersection, coordinate_at_target);
|
||||
const auto other_turn_bearing = util::coordinate_calculation::bearing(
|
||||
coordinate_at_intersection, coordinate_at_other_target);
|
||||
|
||||
// fuzzy becomes narrower due to minor differences in angle computations, yay floating point
|
||||
const bool becomes_narrower =
|
||||
angularDeviation(turn_angle, other_turn_angle) < NARROW_TURN_ANGLE &&
|
||||
angularDeviation(turn_angle, other_turn_angle) <=
|
||||
angularDeviation(intersection[index].angle, intersection[other_index].angle);
|
||||
|
||||
const bool has_same_deviation =
|
||||
std::abs(angularDeviation(intersection[index].angle, STRAIGHT_ANGLE) -
|
||||
angularDeviation(intersection[other_index].angle, STRAIGHT_ANGLE)) <
|
||||
angularDeviation(turn_bearing, other_turn_bearing) < NARROW_TURN_ANGLE &&
|
||||
angularDeviation(turn_bearing, other_turn_bearing) <=
|
||||
angularDeviation(intersection[index].bearing, intersection[other_index].bearing) +
|
||||
MAXIMAL_ALLOWED_NO_TURN_DEVIATION;
|
||||
return becomes_narrower || has_same_deviation;
|
||||
|
||||
return becomes_narrower;
|
||||
};
|
||||
|
||||
const bool is_y_arm_first = isValidYArm(first_index, second_index);
|
||||
@ -162,8 +181,8 @@ bool IntersectionNormalizer::CanMerge(const NodeID node_at_intersection,
|
||||
// we only allow collapsing of a Y like fork. So the angle to the third index has to be
|
||||
// roughly equal:
|
||||
const auto y_angle_difference = angularDeviation(
|
||||
angularDeviation(intersection[third_index].angle, intersection[first_index].angle),
|
||||
angularDeviation(intersection[third_index].angle, intersection[second_index].angle));
|
||||
angularDeviation(intersection[third_index].bearing, intersection[first_index].bearing),
|
||||
angularDeviation(intersection[third_index].bearing, intersection[second_index].bearing));
|
||||
// Allow larger angles if its three roads only of the same name
|
||||
// This is a heuristic and might need to be revised.
|
||||
const bool assume_y_intersection =
|
||||
@ -194,8 +213,9 @@ bool IntersectionNormalizer::CanMerge(const NodeID node_at_intersection,
|
||||
* Anything containing the first u-turn in a merge affects all other angles
|
||||
* and is handled separately from all others.
|
||||
*/
|
||||
Intersection IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
|
||||
Intersection intersection) const
|
||||
std::pair<IntersectionShape, std::vector<std::pair<EdgeID, EdgeID>>>
|
||||
IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
|
||||
IntersectionShape intersection) const
|
||||
{
|
||||
const auto getRight = [&](std::size_t index) {
|
||||
return (index + intersection.size() - 1) % intersection.size();
|
||||
@ -216,34 +236,34 @@ Intersection IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersect
|
||||
{
|
||||
const auto offset = angularDeviation(first, second);
|
||||
auto new_angle = std::max(first, second) + .5 * offset;
|
||||
if (new_angle > 360)
|
||||
if (new_angle >= 360)
|
||||
return new_angle - 360;
|
||||
return new_angle;
|
||||
}
|
||||
};
|
||||
|
||||
const auto merge = [combineAngles](const ConnectedRoad &first,
|
||||
const ConnectedRoad &second) -> ConnectedRoad {
|
||||
ConnectedRoad result = first.entry_allowed ? first : second;
|
||||
result.angle = combineAngles(first.angle, second.angle);
|
||||
// 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<std::pair<EdgeID, EdgeID>> merging_map;
|
||||
|
||||
const auto merge = [this, combineAngles, &merging_map](const IntersectionShapeData &first,
|
||||
const IntersectionShapeData &second) {
|
||||
IntersectionShapeData result =
|
||||
!node_based_graph.GetEdgeData(first.eid).reversed ? first : second;
|
||||
result.bearing = combineAngles(first.bearing, second.bearing);
|
||||
BOOST_ASSERT(0 <= result.angle && result.angle <= 360.0);
|
||||
BOOST_ASSERT(0 <= result.bearing && result.bearing <= 360.0);
|
||||
BOOST_ASSERT(0 <= result.bearing && result.bearing < 360.0);
|
||||
// the other ID
|
||||
const auto merged_from = result.eid == first.eid ? second.eid : first.eid;
|
||||
BOOST_ASSERT(
|
||||
std::find_if(merging_map.begin(), merging_map.end(), [merged_from](const auto pair) {
|
||||
return pair.first == merged_from;
|
||||
}) == merging_map.end());
|
||||
merging_map.push_back(std::make_pair(merged_from, result.eid));
|
||||
return result;
|
||||
};
|
||||
|
||||
if (intersection.size() <= 1)
|
||||
return intersection;
|
||||
|
||||
const bool is_connected_to_roundabout = [this, &intersection]() {
|
||||
for (const auto &road : intersection)
|
||||
{
|
||||
if (node_based_graph.GetEdgeData(road.eid).roundabout ||
|
||||
node_based_graph.GetEdgeData(road.eid).circular)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
return std::make_pair(intersection, merging_map);
|
||||
|
||||
// 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
|
||||
@ -278,52 +298,27 @@ Intersection IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersect
|
||||
// the difference to all angles. Otherwise we subtract it.
|
||||
bool merged_first = false;
|
||||
// these result in an adjustment of all other angles
|
||||
if (CanMerge(intersection_node, intersection, 0, intersection.size() - 1))
|
||||
if (CanMerge(intersection_node, intersection, intersection.size() - 1, 0))
|
||||
{
|
||||
merged_first = true;
|
||||
// moving `a` to the left
|
||||
const double correction_factor = (360 - intersection[intersection.size() - 1].angle) / 2;
|
||||
for (std::size_t i = 1; i + 1 < intersection.size(); ++i)
|
||||
intersection[i].angle += correction_factor;
|
||||
|
||||
intersection[0] = merge(intersection.front(), intersection.back());
|
||||
// FIXME if we have a left-sided country, we need to switch this off and enable it
|
||||
// below
|
||||
intersection[0] = merge(intersection.front(), intersection.back());
|
||||
intersection[0].angle = 0;
|
||||
intersection.pop_back();
|
||||
}
|
||||
else if (CanMerge(intersection_node, intersection, 0, 1))
|
||||
{
|
||||
merged_first = true;
|
||||
// moving `a` to the right
|
||||
const double correction_factor = (intersection[1].angle) / 2;
|
||||
for (std::size_t i = 2; i < intersection.size(); ++i)
|
||||
intersection[i].angle -= correction_factor;
|
||||
intersection[0] = merge(intersection[0], intersection[1]);
|
||||
intersection[0].angle = 0;
|
||||
intersection[0] = merge(intersection.front(), intersection[1]);
|
||||
intersection.erase(intersection.begin() + 1);
|
||||
}
|
||||
|
||||
if (merged_first && is_connected_to_roundabout)
|
||||
{
|
||||
/*
|
||||
* We are merging a u-turn against the direction of a roundabout
|
||||
*
|
||||
* -----------> roundabout
|
||||
* / \
|
||||
* out in
|
||||
*
|
||||
* These cases have to be disabled, even if they are not forbidden specifically by a
|
||||
* relation
|
||||
*/
|
||||
intersection[0].entry_allowed = false;
|
||||
}
|
||||
|
||||
// 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, index, getRight(index)))
|
||||
if (CanMerge(intersection_node, intersection, getRight(index), index))
|
||||
{
|
||||
intersection[getRight(index)] =
|
||||
merge(intersection[getRight(index)], intersection[index]);
|
||||
@ -332,10 +327,7 @@ Intersection IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersect
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(std::begin(intersection),
|
||||
std::end(intersection),
|
||||
std::mem_fn(&ConnectedRoad::compareByAngle));
|
||||
return intersection;
|
||||
return std::make_pair(intersection, merging_map);
|
||||
}
|
||||
|
||||
// OSM can have some very steep angles for joining roads. Considering the following intersection:
|
||||
@ -358,8 +350,9 @@ Intersection IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersect
|
||||
// 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.
|
||||
Intersection IntersectionNormalizer::AdjustForJoiningRoads(const NodeID node_at_intersection,
|
||||
Intersection intersection) const
|
||||
IntersectionShape
|
||||
IntersectionNormalizer::AdjustBearingsForMergeAtDestination(const NodeID node_at_intersection,
|
||||
IntersectionShape intersection) const
|
||||
{
|
||||
// nothing to do for dead ends
|
||||
if (intersection.size() <= 1)
|
||||
@ -369,18 +362,18 @@ Intersection IntersectionNormalizer::AdjustForJoiningRoads(const NodeID node_at_
|
||||
// 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 = 1; index < intersection.size(); ++index)
|
||||
for (std::size_t index = 0; index < intersection.size(); ++index)
|
||||
{
|
||||
auto &road = intersection[index];
|
||||
// only consider roads that are close
|
||||
if (road.segment_length && *(road.segment_length) > PRUNING_DISTANCE)
|
||||
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(node_at_intersection, road.eid);
|
||||
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;
|
||||
@ -401,18 +394,20 @@ Intersection IntersectionNormalizer::AdjustForJoiningRoads(const NodeID node_at_
|
||||
continue;
|
||||
|
||||
// the order does not matter
|
||||
const auto get_offset = [](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
|
||||
return 0.5 * angularDeviation(lhs.angle, rhs.angle);
|
||||
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 ConnectedRoad &road,
|
||||
const ConnectedRoad &next_road_in_offset_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.angle, next_road_in_offset_direction.angle);
|
||||
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;
|
||||
@ -426,28 +421,28 @@ Intersection IntersectionNormalizer::AdjustForJoiningRoads(const NodeID node_at_
|
||||
const auto offset =
|
||||
get_offset(next_intersection_along_road[0], next_intersection_along_road[1]);
|
||||
|
||||
const auto corrected_offset =
|
||||
get_corrected_offset(offset, road, intersection[(index + 1) % intersection.size()]);
|
||||
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.angle = adjustAngle(road.angle, corrected_offset);
|
||||
road.bearing = adjustAngle(road.bearing, corrected_offset);
|
||||
road.bearing = adjustAngle(road.bearing, -corrected_offset);
|
||||
}
|
||||
else if (CanMerge(node_at_next_intersection,
|
||||
next_intersection_along_road,
|
||||
0,
|
||||
next_intersection_along_road.size() - 1))
|
||||
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]);
|
||||
|
||||
const auto corrected_offset =
|
||||
get_corrected_offset(offset, road, intersection[index - 1]);
|
||||
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.angle = adjustAngle(road.angle, -corrected_offset);
|
||||
road.bearing = adjustAngle(road.bearing, -corrected_offset);
|
||||
road.bearing = adjustAngle(road.bearing, corrected_offset);
|
||||
}
|
||||
}
|
||||
return intersection;
|
||||
|
@ -106,7 +106,7 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection)
|
||||
// itself. If that is the case, the road is obviously not a sliproad.
|
||||
|
||||
// a sliproad has to enter a road without choice
|
||||
const auto couldBeSliproad = [](const Intersection &intersection) {
|
||||
const auto couldBeSliproad = [](const IntersectionView &intersection) {
|
||||
if (intersection.size() != 3)
|
||||
return false;
|
||||
if ((intersection[1].entry_allowed && intersection[2].entry_allowed) ||
|
||||
@ -218,7 +218,7 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection)
|
||||
std::find_if(
|
||||
target_intersection.begin() + 1,
|
||||
target_intersection.end(),
|
||||
[this, &link_data](const ConnectedRoad &road) {
|
||||
[this, &link_data](const IntersectionViewData &road) {
|
||||
const auto &road_edge_data = node_based_graph.GetEdgeData(road.eid);
|
||||
|
||||
const auto same_name =
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <set>
|
||||
@ -74,36 +75,74 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
{
|
||||
}
|
||||
|
||||
Intersection TurnAnalysis::assignTurnTypes(const NodeID from_nid,
|
||||
const EdgeID via_eid,
|
||||
Intersection intersection) const
|
||||
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 normalised_shape
|
||||
const auto intersection_view = intersection_generator.TransformIntersectionShapeIntoView(
|
||||
node_prior_to_intersection,
|
||||
entering_via_edge,
|
||||
shape_result.normalised_intersection_shape,
|
||||
shape_result.intersection_shape,
|
||||
shape_result.merging_map);
|
||||
|
||||
// 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
|
||||
{
|
||||
// Roundabouts are a main priority. If there is a roundabout instruction present, we process the
|
||||
// turn as a roundabout
|
||||
if (roundabout_handler.canProcess(from_nid, via_eid, intersection))
|
||||
|
||||
// the following lines create a partly invalid intersection object. We might want to refactor
|
||||
// this at some point
|
||||
Intersection intersection;
|
||||
intersection.reserve(intersection_view.size());
|
||||
std::transform(intersection_view.begin(),
|
||||
intersection_view.end(),
|
||||
std::back_inserter(intersection),
|
||||
[&](const IntersectionViewData &data) {
|
||||
return ConnectedRoad(data,
|
||||
{TurnType::Invalid, DirectionModifier::UTurn},
|
||||
INVALID_LANE_DATAID);
|
||||
});
|
||||
if (roundabout_handler.canProcess(node_prior_to_intersection, entering_via_edge, intersection))
|
||||
{
|
||||
intersection = roundabout_handler(from_nid, via_eid, std::move(intersection));
|
||||
intersection = roundabout_handler(
|
||||
node_prior_to_intersection, entering_via_edge, std::move(intersection));
|
||||
}
|
||||
else
|
||||
{
|
||||
// set initial defaults for normal turns and modifier based on angle
|
||||
intersection = setTurnTypes(from_nid, via_eid, std::move(intersection));
|
||||
if (motorway_handler.canProcess(from_nid, via_eid, intersection))
|
||||
intersection =
|
||||
setTurnTypes(node_prior_to_intersection, entering_via_edge, std::move(intersection));
|
||||
if (motorway_handler.canProcess(
|
||||
node_prior_to_intersection, entering_via_edge, intersection))
|
||||
{
|
||||
intersection = motorway_handler(from_nid, via_eid, std::move(intersection));
|
||||
intersection = motorway_handler(
|
||||
node_prior_to_intersection, entering_via_edge, std::move(intersection));
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(turn_handler.canProcess(from_nid, via_eid, intersection));
|
||||
intersection = turn_handler(from_nid, via_eid, std::move(intersection));
|
||||
BOOST_ASSERT(turn_handler.canProcess(
|
||||
node_prior_to_intersection, entering_via_edge, intersection));
|
||||
intersection = turn_handler(
|
||||
node_prior_to_intersection, entering_via_edge, std::move(intersection));
|
||||
}
|
||||
}
|
||||
// Handle sliproads
|
||||
if (sliproad_handler.canProcess(from_nid, via_eid, intersection))
|
||||
intersection = sliproad_handler(from_nid, via_eid, std::move(intersection));
|
||||
if (sliproad_handler.canProcess(node_prior_to_intersection, entering_via_edge, intersection))
|
||||
intersection = sliproad_handler(
|
||||
node_prior_to_intersection, entering_via_edge, std::move(intersection));
|
||||
|
||||
// Turn On Ramps Into Off Ramps, if we come from a motorway-like road
|
||||
if (node_based_graph.GetEdgeData(via_eid).road_classification.IsMotorwayClass())
|
||||
if (node_based_graph.GetEdgeData(entering_via_edge).road_classification.IsMotorwayClass())
|
||||
{
|
||||
std::for_each(intersection.begin(), intersection.end(), [](ConnectedRoad &road) {
|
||||
if (road.instruction.type == TurnType::OnRamp)
|
||||
@ -113,34 +152,24 @@ Intersection TurnAnalysis::assignTurnTypes(const NodeID from_nid,
|
||||
return intersection;
|
||||
}
|
||||
|
||||
std::vector<TurnOperation>
|
||||
TurnAnalysis::transformIntersectionIntoTurns(const Intersection &intersection) const
|
||||
TurnAnalysis::ShapeResult
|
||||
TurnAnalysis::ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const
|
||||
{
|
||||
std::vector<TurnOperation> turns;
|
||||
for (auto road : intersection)
|
||||
if (road.entry_allowed)
|
||||
turns.emplace_back(road);
|
||||
ShapeResult intersection_shape;
|
||||
intersection_shape.intersection_shape =
|
||||
intersection_generator.ComputeIntersectionShape(node_at_center_of_intersection);
|
||||
|
||||
return turns;
|
||||
}
|
||||
std::tie(intersection_shape.normalised_intersection_shape, intersection_shape.merging_map) =
|
||||
intersection_normalizer(node_at_center_of_intersection,
|
||||
intersection_shape.intersection_shape);
|
||||
|
||||
Intersection TurnAnalysis::operator()(const NodeID from_nid, const EdgeID via_eid) const
|
||||
{
|
||||
return PostProcess(from_nid, via_eid, intersection_generator(from_nid, via_eid));
|
||||
}
|
||||
|
||||
Intersection TurnAnalysis::PostProcess(const NodeID from_node,
|
||||
const EdgeID via_eid,
|
||||
Intersection intersection) const
|
||||
{
|
||||
const auto node_at_intersection = node_based_graph.GetTarget(via_eid);
|
||||
return assignTurnTypes(
|
||||
from_node, via_eid, intersection_normalizer(node_at_intersection, std::move(intersection)));
|
||||
return intersection_shape;
|
||||
}
|
||||
|
||||
// Sets basic turn types as fallback for otherwise unhandled turns
|
||||
Intersection
|
||||
TurnAnalysis::setTurnTypes(const NodeID from_nid, const EdgeID, Intersection intersection) const
|
||||
Intersection TurnAnalysis::setTurnTypes(const NodeID node_prior_to_intersection,
|
||||
const EdgeID,
|
||||
Intersection intersection) const
|
||||
{
|
||||
for (auto &road : intersection)
|
||||
{
|
||||
@ -151,7 +180,7 @@ TurnAnalysis::setTurnTypes(const NodeID from_nid, const EdgeID, Intersection int
|
||||
const NodeID to_nid = node_based_graph.GetTarget(onto_edge);
|
||||
|
||||
road.instruction = {TurnType::Turn,
|
||||
(from_nid == to_nid) ? DirectionModifier::UTurn
|
||||
(node_prior_to_intersection == to_nid) ? DirectionModifier::UTurn
|
||||
: getTurnDirection(road.angle)};
|
||||
}
|
||||
return intersection;
|
||||
|
@ -25,7 +25,7 @@ bool findPreviousIntersection(const NodeID node_v,
|
||||
// output parameters
|
||||
NodeID &result_node,
|
||||
EdgeID &result_via_edge,
|
||||
Intersection &result_intersection)
|
||||
IntersectionView &result_intersection)
|
||||
{
|
||||
/* We need to find the intersection that is located prior to via_edge.
|
||||
|
||||
@ -55,6 +55,7 @@ bool findPreviousIntersection(const NodeID node_v,
|
||||
// (looking at the reverse direction).
|
||||
const auto node_w = node_based_graph.GetTarget(via_edge);
|
||||
const auto u_turn_at_node_w = intersection[0].eid;
|
||||
|
||||
// make sure the ID is actually valid
|
||||
BOOST_ASSERT(node_based_graph.BeginEdges(node_w) <= u_turn_at_node_w &&
|
||||
u_turn_at_node_w <= node_based_graph.EndEdges(node_w));
|
||||
@ -65,7 +66,6 @@ bool findPreviousIntersection(const NodeID node_v,
|
||||
|
||||
const auto node_v_reverse_intersection =
|
||||
intersection_generator.GetConnectedRoads(node_w, u_turn_at_node_w, USE_LOW_PRECISION_MODE);
|
||||
|
||||
// Continue along the straightmost turn. If there is no straight turn, we cannot find a valid
|
||||
// previous intersection.
|
||||
const auto straightmost_at_v_in_reverse =
|
||||
@ -102,7 +102,7 @@ bool findPreviousIntersection(const NodeID node_v,
|
||||
result_intersection.end() !=
|
||||
std::find_if(result_intersection.begin(),
|
||||
result_intersection.end(),
|
||||
[via_edge](const ConnectedRoad &road) { return road.eid == via_edge; });
|
||||
[via_edge](const IntersectionViewData &road) { return road.eid == via_edge; });
|
||||
|
||||
if (!check_via_edge)
|
||||
{
|
||||
|
@ -186,6 +186,7 @@ TurnLaneScenario TurnLaneHandler::deduceScenario(const NodeID at,
|
||||
// Due to sliproads, we might need access to the previous intersection at this point already;
|
||||
previous_node = SPECIAL_NODEID;
|
||||
previous_via_edge = SPECIAL_EDGEID;
|
||||
IntersectionView previous_intersection_view;
|
||||
if (findPreviousIntersection(at,
|
||||
via_edge,
|
||||
intersection,
|
||||
@ -193,11 +194,11 @@ TurnLaneScenario TurnLaneHandler::deduceScenario(const NodeID at,
|
||||
node_based_graph,
|
||||
previous_node,
|
||||
previous_via_edge,
|
||||
previous_intersection))
|
||||
previous_intersection_view))
|
||||
{
|
||||
extractLaneData(previous_via_edge, previous_description_id, previous_lane_data);
|
||||
previous_intersection = turn_analysis.assignTurnTypes(
|
||||
previous_node, previous_via_edge, std::move(previous_intersection));
|
||||
previous_intersection = turn_analysis.AssignTurnTypes(
|
||||
previous_node, previous_via_edge, previous_intersection_view);
|
||||
for (std::size_t road_index = 0; road_index < previous_intersection.size(); ++road_index)
|
||||
{
|
||||
const auto &road = previous_intersection[road_index];
|
||||
@ -542,7 +543,7 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
|
||||
// find out about the next intersection. To check for valid matches, we also need the turn
|
||||
// types. We can skip merging/angle adjustments, though
|
||||
const auto next_intersection = turn_analysis.assignTurnTypes(
|
||||
const auto next_intersection = turn_analysis.AssignTurnTypes(
|
||||
at, straightmost->eid, turn_analysis.GetIntersectionGenerator()(at, straightmost->eid));
|
||||
|
||||
// check where we can match turn lanes
|
||||
|
@ -117,7 +117,8 @@ BOOST_AUTO_TEST_CASE(io_read_lines)
|
||||
auto startiter = infile.GetLineIteratorBegin();
|
||||
auto enditer = infile.GetLineIteratorEnd();
|
||||
std::vector<std::string> resultlines;
|
||||
while (startiter != enditer) {
|
||||
while (startiter != enditer)
|
||||
{
|
||||
resultlines.push_back(*startiter);
|
||||
++startiter;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user