Compare commits
15 Commits
master
...
v5.15.0-la
Author | SHA1 | Date | |
---|---|---|---|
|
d1022043ba | ||
|
98566bc7e6 | ||
|
9d2e57ac7b | ||
|
5effc60819 | ||
|
807e4b5ea2 | ||
|
deb865d421 | ||
|
dd8dc76c1b | ||
|
203036a11d | ||
|
22d127bc7c | ||
|
12dfa709f1 | ||
|
1d4d6dca41 | ||
|
1f8b41489c | ||
|
68c581c9e6 | ||
|
0c954fafae | ||
|
4316886d4b |
@ -2,6 +2,9 @@
|
|||||||
- Changes from 5.14.1:
|
- Changes from 5.14.1:
|
||||||
- Bugfixes:
|
- Bugfixes:
|
||||||
- FIXED #4727: Erroring when a old .core file is present.
|
- FIXED #4727: Erroring when a old .core file is present.
|
||||||
|
- FIXED #4704: Fixed regression in bearings reordering introduced in 5.13 [#4704](https://github.com/Project-OSRM/osrm-backend/issues/4704)
|
||||||
|
- Guidance:
|
||||||
|
- CHANGED #4706: Guidance refactoring step to decouple intersection connectivity analysis and turn instructions generation [#4706](https://github.com/Project-OSRM/osrm-backend/pull/4706)
|
||||||
|
|
||||||
# 5.14.1
|
# 5.14.1
|
||||||
- Changes from 5.14.0
|
- Changes from 5.14.0
|
||||||
|
@ -884,7 +884,7 @@ Feature: Car - Turn restrictions
|
|||||||
| a | c | albic,dobe,dobe,albic,albic | depart,turn left,continue uturn,turn left,arrive |
|
| a | c | albic,dobe,dobe,albic,albic | depart,turn left,continue uturn,turn left,arrive |
|
||||||
| a | e | albic,dobe,dobe | depart,turn left,arrive |
|
| a | e | albic,dobe,dobe | depart,turn left,arrive |
|
||||||
|
|
||||||
@no_turning @conditionals
|
@no_turning @conditionals @restriction-way
|
||||||
Scenario: Car - Conditional restriction with multiple time windows
|
Scenario: Car - Conditional restriction with multiple time windows
|
||||||
Given the extract extra arguments "--parse-conditional-restrictions"
|
Given the extract extra arguments "--parse-conditional-restrictions"
|
||||||
# 5pm Wed 02 May, 2017 GMT
|
# 5pm Wed 02 May, 2017 GMT
|
||||||
@ -1054,4 +1054,3 @@ Feature: Car - Turn restrictions
|
|||||||
| a | f | ab,be,ef,ef | depart,turn right,turn left,arrive | a,b,e,f |
|
| a | f | ab,be,ef,ef | depart,turn right,turn left,arrive | a,b,e,f |
|
||||||
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
|
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
|
||||||
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
||||||
|
|
||||||
|
@ -575,7 +575,7 @@ Feature: Car - Turn restrictions
|
|||||||
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
|
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
|
||||||
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
||||||
|
|
||||||
@restriction @overlap
|
@restriction-way @overlap
|
||||||
Scenario: Car - prohibit turn
|
Scenario: Car - prohibit turn
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
@ -710,7 +710,7 @@ Feature: Car - Turn restrictions
|
|||||||
| a | j | left,first,right,right |
|
| a | j | left,first,right,right |
|
||||||
| f | e | right,third,left,left |
|
| f | e | right,third,left,left |
|
||||||
|
|
||||||
@restriction
|
@restriction-way
|
||||||
Scenario: Car - allow only turn
|
Scenario: Car - allow only turn
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
@ -742,7 +742,7 @@ Feature: Car - Turn restrictions
|
|||||||
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
|
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
|
||||||
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
||||||
|
|
||||||
@restriction
|
@restriction-way
|
||||||
Scenario: Car - allow only turn
|
Scenario: Car - allow only turn
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
@ -771,7 +771,7 @@ Feature: Car - Turn restrictions
|
|||||||
| from | to | route |
|
| from | to | route |
|
||||||
| a | d | ab,be,de,de |
|
| a | d | ab,be,de,de |
|
||||||
|
|
||||||
@restriction
|
@restriction-way
|
||||||
Scenario: Multi Way restriction
|
Scenario: Multi Way restriction
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
@ -808,7 +808,7 @@ Feature: Car - Turn restrictions
|
|||||||
| from | to | route |
|
| from | to | route |
|
||||||
| a | h | horiz,vert,horiz,horiz |
|
| a | h | horiz,vert,horiz,horiz |
|
||||||
|
|
||||||
@restriction
|
@restriction-way
|
||||||
Scenario: Multi-Way overlapping single-way
|
Scenario: Multi-Way overlapping single-way
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
@ -847,7 +847,7 @@ Feature: Car - Turn restrictions
|
|||||||
| h | d | hfb,abcd,abcd | depart,end of road right,arrive | h,b,d |
|
| h | d | hfb,abcd,abcd | depart,end of road right,arrive | h,b,d |
|
||||||
|
|
||||||
|
|
||||||
@restriction
|
@restriction-way
|
||||||
Scenario: Car - prohibit turn, traffic lights
|
Scenario: Car - prohibit turn, traffic lights
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
@ -890,7 +890,7 @@ Feature: Car - Turn restrictions
|
|||||||
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
||||||
|
|
||||||
|
|
||||||
@restriction @overlap @geometry
|
@restriction-way @overlap @geometry
|
||||||
Scenario: Geometry
|
Scenario: Geometry
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
@ -925,7 +925,7 @@ Feature: Car - Turn restrictions
|
|||||||
| c | d | bc,bge,de,de |
|
| c | d | bc,bge,de,de |
|
||||||
| c | f | bc,bge,de,de,ef,ef |
|
| c | f | bc,bge,de,de,ef,ef |
|
||||||
|
|
||||||
@restriction @overlap @geometry @traffic-signals
|
@restriction-way @overlap @geometry @traffic-signals
|
||||||
Scenario: Geometry
|
Scenario: Geometry
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
@ -967,7 +967,7 @@ Feature: Car - Turn restrictions
|
|||||||
| c | f | bc,bge,de,de,ef,ef |
|
| c | f | bc,bge,de,de,ef,ef |
|
||||||
|
|
||||||
# don't crash hard on invalid restrictions
|
# don't crash hard on invalid restrictions
|
||||||
@restriction @invalid
|
@restriction-way @invalid
|
||||||
Scenario: Geometry
|
Scenario: Geometry
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
@ -999,7 +999,7 @@ Feature: Car - Turn restrictions
|
|||||||
| a | f | ab,be,ef,ef |
|
| a | f | ab,be,ef,ef |
|
||||||
|
|
||||||
|
|
||||||
@restriction @overlap @geometry
|
@restriction @restriction-way @overlap @geometry
|
||||||
Scenario: Duplicated restriction
|
Scenario: Duplicated restriction
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
|
@ -394,12 +394,16 @@ Feature: Merge Segregated Roads
|
|||||||
a
|
a
|
||||||
|
|
|
|
||||||
b
|
b
|
||||||
|
/ \
|
||||||
c h
|
c h
|
||||||
| |
|
| |
|
||||||
| |
|
| |
|
||||||
| |
|
| |
|
||||||
| |
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
d g
|
d g
|
||||||
|
\ /
|
||||||
e
|
e
|
||||||
|
|
|
|
||||||
f
|
f
|
||||||
|
@ -961,12 +961,12 @@ Feature: Simple Turns
|
|||||||
g
|
g
|
||||||
.
|
.
|
||||||
.
|
.
|
||||||
.
|
.
|
||||||
.
|
.
|
||||||
f
|
h f
|
||||||
h .
|
.
|
||||||
. .
|
. .
|
||||||
. j
|
. j
|
||||||
. .
|
. .
|
||||||
c
|
c
|
||||||
. . .
|
. . .
|
||||||
|
@ -12,6 +12,10 @@ module.exports = {
|
|||||||
|
|
||||||
FuzzyMatch: class {
|
FuzzyMatch: class {
|
||||||
match (got, want) {
|
match (got, want) {
|
||||||
|
// don't fail if bearings input and extected string is empty and actual result is undefined
|
||||||
|
if (want === '' && (got === '' || got === undefined))
|
||||||
|
return true;
|
||||||
|
|
||||||
var matchPercent = want.match(/(.*)\s+~(.+)%$/),
|
var matchPercent = want.match(/(.*)\s+~(.+)%$/),
|
||||||
matchAbs = want.match(/(.*)\s+\+\-(.+)$/),
|
matchAbs = want.match(/(.*)\s+\+\-(.+)$/),
|
||||||
matchRe = want.match(/^\/(.*)\/$/),
|
matchRe = want.match(/^\/(.*)\/$/),
|
||||||
|
@ -60,7 +60,7 @@ operator()(const NodeID intersection_node,
|
|||||||
const boost::optional<util::json::Object> &way_style) const
|
const boost::optional<util::json::Object> &way_style) const
|
||||||
{
|
{
|
||||||
// request the number of lanes. This process needs to be in sync with what happens over at
|
// request the number of lanes. This process needs to be in sync with what happens over at
|
||||||
// intersection_generator
|
// intersection analysis
|
||||||
const auto intersection_lanes =
|
const auto intersection_lanes =
|
||||||
intersection.FindMaximum(guidance::makeExtractLanesForRoad(node_based_graph));
|
intersection.FindMaximum(guidance::makeExtractLanesForRoad(node_based_graph));
|
||||||
|
|
||||||
|
@ -14,10 +14,13 @@ namespace guidance
|
|||||||
class DrivewayHandler final : public IntersectionHandler
|
class DrivewayHandler final : public IntersectionHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DrivewayHandler(const IntersectionGenerator &intersection_generator,
|
DrivewayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table);
|
const SuffixTable &street_name_suffix_table);
|
||||||
|
|
||||||
|
@ -1,127 +0,0 @@
|
|||||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_
|
|
||||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_
|
|
||||||
|
|
||||||
#include "extractor/compressed_edge_container.hpp"
|
|
||||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
|
||||||
#include "extractor/guidance/intersection.hpp"
|
|
||||||
#include "extractor/guidance/intersection_normalization_operation.hpp"
|
|
||||||
#include "extractor/query_node.hpp"
|
|
||||||
#include "extractor/restriction_index.hpp"
|
|
||||||
#include "util/attributes.hpp"
|
|
||||||
#include "util/node_based_graph.hpp"
|
|
||||||
#include "util/typedefs.hpp"
|
|
||||||
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
namespace osrm
|
|
||||||
{
|
|
||||||
namespace extractor
|
|
||||||
{
|
|
||||||
namespace guidance
|
|
||||||
{
|
|
||||||
|
|
||||||
struct IntersectionGenerationParameters
|
|
||||||
{
|
|
||||||
NodeID nid;
|
|
||||||
EdgeID via_eid;
|
|
||||||
};
|
|
||||||
|
|
||||||
// The Intersection Generator is given a turn location and generates an intersection representation
|
|
||||||
// from it. For this all turn possibilities are analysed.
|
|
||||||
// We consider turn restrictions to indicate possible turns. U-turns are generated based on profile
|
|
||||||
// decisions.
|
|
||||||
class IntersectionGenerator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
IntersectionGenerator(const util::NodeBasedDynamicGraph &node_based_graph,
|
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
|
||||||
const RestrictionMap &restriction_map,
|
|
||||||
const std::unordered_set<NodeID> &barrier_nodes,
|
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
|
||||||
const CompressedEdgeContainer &compressed_edge_container);
|
|
||||||
|
|
||||||
// For a source node `a` and a via edge `ab` creates an intersection at target `b`.
|
|
||||||
//
|
|
||||||
// a . . . b . .
|
|
||||||
// .
|
|
||||||
// .
|
|
||||||
//
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
OSRM_ATTR_WARN_UNUSED
|
|
||||||
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
|
|
||||||
// intermediate intersection, if there is a traffic light in between. If we want to look farther
|
|
||||||
// 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. This function skips over degree two nodes to find coorect input
|
|
||||||
// for GetConnectedRoads.
|
|
||||||
OSRM_ATTR_WARN_UNUSED
|
|
||||||
IntersectionGenerationParameters SkipDegreeTwoNodes(const NodeID starting_node,
|
|
||||||
const EdgeID via_edge) const;
|
|
||||||
|
|
||||||
// Allow access to the coordinate extractor for all owners
|
|
||||||
const CoordinateExtractor &GetCoordinateExtractor() const;
|
|
||||||
|
|
||||||
// Check for restrictions/barriers and generate a list of valid and invalid turns present at
|
|
||||||
// the node reached from `from_node` via `via_eid`. The resulting candidates have to be analysed
|
|
||||||
// for their actual instructions later on.
|
|
||||||
// The switch for `use_low_precision_angles` enables a faster mode that will procude less
|
|
||||||
// accurate coordinates. It should be good enough to check order of turns, find straightmost
|
|
||||||
// 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
|
|
||||||
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<IntersectionNormalizationOperation> &merging_map) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
|
||||||
const EdgeBasedNodeDataContainer &node_data_container;
|
|
||||||
const RestrictionMap &restriction_map;
|
|
||||||
const std::unordered_set<NodeID> &barrier_nodes;
|
|
||||||
const std::vector<util::Coordinate> &coordinates;
|
|
||||||
|
|
||||||
// own state, used to find the correct coordinates along a road
|
|
||||||
const CoordinateExtractor coordinate_extractor;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace guidance
|
|
||||||
} // namespace extractor
|
|
||||||
} // namespace osrm
|
|
||||||
|
|
||||||
#endif /* OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_ */
|
|
@ -2,8 +2,8 @@
|
|||||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_
|
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_
|
||||||
|
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
#include "extractor/guidance/intersection_generator.hpp"
|
|
||||||
#include "extractor/guidance/node_based_graph_walker.hpp"
|
#include "extractor/guidance/node_based_graph_walker.hpp"
|
||||||
|
#include "extractor/intersection/intersection_analysis.hpp"
|
||||||
#include "extractor/query_node.hpp"
|
#include "extractor/query_node.hpp"
|
||||||
#include "extractor/suffix_table.hpp"
|
#include "extractor/suffix_table.hpp"
|
||||||
|
|
||||||
@ -34,10 +34,13 @@ class IntersectionHandler
|
|||||||
public:
|
public:
|
||||||
IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table,
|
const SuffixTable &street_name_suffix_table);
|
||||||
const IntersectionGenerator &intersection_generator);
|
|
||||||
|
|
||||||
virtual ~IntersectionHandler() = default;
|
virtual ~IntersectionHandler() = default;
|
||||||
|
|
||||||
@ -52,10 +55,13 @@ class IntersectionHandler
|
|||||||
protected:
|
protected:
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||||
const EdgeBasedNodeDataContainer &node_data_container;
|
const EdgeBasedNodeDataContainer &node_data_container;
|
||||||
const std::vector<util::Coordinate> &coordinates;
|
const std::vector<util::Coordinate> &node_coordinates;
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries;
|
||||||
|
const RestrictionMap &node_restriction_map;
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes;
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data;
|
||||||
const util::NameTable &name_table;
|
const util::NameTable &name_table;
|
||||||
const SuffixTable &street_name_suffix_table;
|
const SuffixTable &street_name_suffix_table;
|
||||||
const IntersectionGenerator &intersection_generator;
|
|
||||||
const NodeBasedGraphWalker graph_walker; // for skipping traffic signal, distances etc.
|
const NodeBasedGraphWalker graph_walker; // for skipping traffic signal, distances etc.
|
||||||
|
|
||||||
// Decide on a basic turn types
|
// Decide on a basic turn types
|
||||||
@ -567,11 +573,19 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
|||||||
// try to find whether there is a turn going to the opposite direction of our obvious
|
// try to find whether there is a turn going to the opposite direction of our obvious
|
||||||
// turn, this should be alright.
|
// turn, this should be alright.
|
||||||
const auto previous_intersection = [&]() -> IntersectionView {
|
const auto previous_intersection = [&]() -> IntersectionView {
|
||||||
const auto parameters = intersection_generator.SkipDegreeTwoNodes(
|
const auto parameters = intersection::skipDegreeTwoNodes(
|
||||||
node_at_intersection, intersection[0].eid);
|
node_based_graph, {node_at_intersection, intersection[0].eid});
|
||||||
if (node_based_graph.GetTarget(parameters.via_eid) == node_at_intersection)
|
if (node_based_graph.GetTarget(parameters.edge) == node_at_intersection)
|
||||||
return {};
|
return {};
|
||||||
return intersection_generator.GetConnectedRoads(parameters.nid, parameters.via_eid);
|
|
||||||
|
return intersection::getConnectedRoads<false>(node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
|
parameters);
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (!previous_intersection.empty())
|
if (!previous_intersection.empty())
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_
|
|
||||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_
|
|
||||||
|
|
||||||
#include "util/typedefs.hpp"
|
|
||||||
|
|
||||||
namespace osrm
|
|
||||||
{
|
|
||||||
namespace extractor
|
|
||||||
{
|
|
||||||
namespace guidance
|
|
||||||
{
|
|
||||||
|
|
||||||
struct IntersectionNormalizationOperation
|
|
||||||
{
|
|
||||||
// the source of the merge, not part of the intersection after the merge is performed.
|
|
||||||
EdgeID merged_eid;
|
|
||||||
// the edge that is covering the `merged_eid`
|
|
||||||
EdgeID into_eid;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace guidance
|
|
||||||
} // namespace extractor
|
|
||||||
} // namespace osrm
|
|
||||||
|
|
||||||
#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_*/
|
|
@ -1,125 +0,0 @@
|
|||||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_
|
|
||||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_
|
|
||||||
|
|
||||||
#include "util/attributes.hpp"
|
|
||||||
#include "util/name_table.hpp"
|
|
||||||
#include "util/typedefs.hpp"
|
|
||||||
|
|
||||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
|
||||||
#include "extractor/guidance/intersection.hpp"
|
|
||||||
#include "extractor/guidance/intersection_generator.hpp"
|
|
||||||
#include "extractor/guidance/intersection_normalization_operation.hpp"
|
|
||||||
#include "extractor/guidance/mergable_road_detector.hpp"
|
|
||||||
#include "extractor/query_node.hpp"
|
|
||||||
#include "extractor/suffix_table.hpp"
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace osrm
|
|
||||||
{
|
|
||||||
namespace extractor
|
|
||||||
{
|
|
||||||
namespace guidance
|
|
||||||
{
|
|
||||||
|
|
||||||
/*
|
|
||||||
* An intersection is a central part in computing guidance decisions. However the model in OSM and
|
|
||||||
* the view we want to use in guidance are not necessarily the same thing. We have to account for
|
|
||||||
* some models that are chosen explicitly in OSM and that don't actually describe how a human would
|
|
||||||
* experience an intersection.
|
|
||||||
*
|
|
||||||
* For example, if a small pedestrian island is located at a traffic light right in the middle of a
|
|
||||||
* road, OSM tends to model the road as two separate ways. A human would consider these two ways a
|
|
||||||
* single road, though. In this normalizer, we try to account for these subtle differences between
|
|
||||||
* OSM data and human perception to improve our decision base for guidance later on.
|
|
||||||
*/
|
|
||||||
class IntersectionNormalizer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
struct NormalizationResult
|
|
||||||
{
|
|
||||||
IntersectionShape normalized_shape;
|
|
||||||
std::vector<IntersectionNormalizationOperation> performed_merges;
|
|
||||||
};
|
|
||||||
IntersectionNormalizer(const util::NodeBasedDynamicGraph &node_based_graph,
|
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
|
||||||
const std::vector<util::Coordinate> &node_coordinates,
|
|
||||||
const util::NameTable &name_table,
|
|
||||||
const SuffixTable &street_name_suffix_table,
|
|
||||||
const IntersectionGenerator &intersection_generator);
|
|
||||||
|
|
||||||
// The function takes an intersection an converts it to a `perceived` intersection which closer
|
|
||||||
// represents how a human might experience the intersection
|
|
||||||
OSRM_ATTR_WARN_UNUSED
|
|
||||||
NormalizationResult operator()(const NodeID node_at_intersection,
|
|
||||||
IntersectionShape intersection) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
|
||||||
const IntersectionGenerator &intersection_generator;
|
|
||||||
const MergableRoadDetector mergable_road_detector;
|
|
||||||
|
|
||||||
/* check if two indices in an intersection can be seen as a single road in the perceived
|
|
||||||
* intersection representation. See below for an example. Utility function for
|
|
||||||
* MergeSegregatedRoads. It also checks for neighboring merges.
|
|
||||||
* This is due possible segments where multiple roads could end up being merged into one.
|
|
||||||
* We only support merging two roads, not three or more, though.
|
|
||||||
* c c
|
|
||||||
* / /
|
|
||||||
* a - b -> a - b - (c,d) but not a - b d -> a,b,(cde)
|
|
||||||
* \ \
|
|
||||||
* d e
|
|
||||||
*/
|
|
||||||
bool CanMerge(const NodeID intersection_node,
|
|
||||||
const IntersectionShape &intersection,
|
|
||||||
std::size_t first_index,
|
|
||||||
std::size_t second_index) const;
|
|
||||||
|
|
||||||
// Perform an Actual Merge
|
|
||||||
IntersectionNormalizationOperation
|
|
||||||
DetermineMergeDirection(const IntersectionShapeData &lhs,
|
|
||||||
const IntersectionShapeData &rhs) const;
|
|
||||||
IntersectionShapeData MergeRoads(const IntersectionShapeData &destination,
|
|
||||||
const IntersectionShapeData &source) const;
|
|
||||||
IntersectionShapeData MergeRoads(const IntersectionNormalizationOperation direction,
|
|
||||||
const IntersectionShapeData &lhs,
|
|
||||||
const IntersectionShapeData &rhs,
|
|
||||||
const double opposite_bearing) const;
|
|
||||||
|
|
||||||
// Merge segregated roads to omit invalid turns in favor of treating segregated roads as
|
|
||||||
// one.
|
|
||||||
// This function combines roads the following way:
|
|
||||||
//
|
|
||||||
// * *
|
|
||||||
// * is converted to *
|
|
||||||
// v ^ +
|
|
||||||
// v ^ +
|
|
||||||
//
|
|
||||||
// The treatment results in a straight turn angle of 180º rather than a turn angle of approx
|
|
||||||
// 160
|
|
||||||
OSRM_ATTR_WARN_UNUSED
|
|
||||||
NormalizationResult MergeSegregatedRoads(const NodeID intersection_node,
|
|
||||||
IntersectionShape intersection) const;
|
|
||||||
|
|
||||||
// The counterpiece to mergeSegregatedRoads. While we can adjust roads that split up at the
|
|
||||||
// intersection itself, it can also happen that intersections are connected to joining roads.
|
|
||||||
//
|
|
||||||
// * *
|
|
||||||
// * is converted to *
|
|
||||||
// v a --- a ---
|
|
||||||
// v ^ +
|
|
||||||
// v ^ +
|
|
||||||
// b
|
|
||||||
//
|
|
||||||
// for the local view of b at a.
|
|
||||||
OSRM_ATTR_WARN_UNUSED
|
|
||||||
IntersectionShape AdjustBearingsForMergeAtDestination(const NodeID node_at_intersection,
|
|
||||||
IntersectionShape intersection) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace guidance
|
|
||||||
} // namespace extractor
|
|
||||||
} // namespace osrm
|
|
||||||
|
|
||||||
#endif /* OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_ */
|
|
@ -1,7 +1,11 @@
|
|||||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_MERGEABLE_ROADS
|
#ifndef OSRM_EXTRACTOR_GUIDANCE_MERGEABLE_ROADS
|
||||||
#define OSRM_EXTRACTOR_GUIDANCE_MERGEABLE_ROADS
|
#define OSRM_EXTRACTOR_GUIDANCE_MERGEABLE_ROADS
|
||||||
|
|
||||||
|
#include "extractor/compressed_edge_container.hpp"
|
||||||
|
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
|
#include "extractor/guidance/turn_lane_types.hpp"
|
||||||
|
#include "extractor/restriction_index.hpp"
|
||||||
#include "util/coordinate.hpp"
|
#include "util/coordinate.hpp"
|
||||||
#include "util/node_based_graph.hpp"
|
#include "util/node_based_graph.hpp"
|
||||||
#include "util/typedefs.hpp"
|
#include "util/typedefs.hpp"
|
||||||
@ -9,6 +13,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace osrm
|
namespace osrm
|
||||||
@ -39,8 +44,10 @@ class MergableRoadDetector
|
|||||||
MergableRoadDetector(const util::NodeBasedDynamicGraph &node_based_graph,
|
MergableRoadDetector(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &node_coordinates,
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
const IntersectionGenerator &intersection_generator,
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
const CoordinateExtractor &coordinate_extractor,
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table);
|
const SuffixTable &street_name_suffix_table);
|
||||||
|
|
||||||
@ -159,15 +166,19 @@ class MergableRoadDetector
|
|||||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||||
const EdgeBasedNodeDataContainer &node_data_container;
|
const EdgeBasedNodeDataContainer &node_data_container;
|
||||||
const std::vector<util::Coordinate> &node_coordinates;
|
const std::vector<util::Coordinate> &node_coordinates;
|
||||||
const IntersectionGenerator &intersection_generator;
|
const extractor::CompressedEdgeContainer &compressed_geometries;
|
||||||
const CoordinateExtractor &coordinate_extractor;
|
const RestrictionMap &node_restriction_map;
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes;
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data;
|
||||||
|
|
||||||
// name detection
|
// name detection
|
||||||
const util::NameTable &name_table;
|
const util::NameTable &name_table;
|
||||||
const SuffixTable &street_name_suffix_table;
|
const SuffixTable &street_name_suffix_table;
|
||||||
|
|
||||||
|
const CoordinateExtractor coordinate_extractor;
|
||||||
|
|
||||||
// limit for detecting circles / parallel roads
|
// limit for detecting circles / parallel roads
|
||||||
const static double constexpr distance_to_extract = 150;
|
const static double constexpr distance_to_extract = 120;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace guidance
|
} // namespace guidance
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
|
#define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
|
||||||
|
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
#include "extractor/guidance/intersection_generator.hpp"
|
|
||||||
#include "extractor/guidance/intersection_handler.hpp"
|
#include "extractor/guidance/intersection_handler.hpp"
|
||||||
#include "extractor/query_node.hpp"
|
#include "extractor/query_node.hpp"
|
||||||
|
|
||||||
@ -26,9 +25,12 @@ class MotorwayHandler : public IntersectionHandler
|
|||||||
MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table,
|
const SuffixTable &street_name_suffix_table);
|
||||||
const IntersectionGenerator &intersection_generator);
|
|
||||||
|
|
||||||
~MotorwayHandler() override final = default;
|
~MotorwayHandler() override final = default;
|
||||||
|
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
#define OSRM_EXTRACTOR_GUIDANCE_NODE_BASED_GRAPH_WALKER
|
#define OSRM_EXTRACTOR_GUIDANCE_NODE_BASED_GRAPH_WALKER
|
||||||
|
|
||||||
#include "extractor/guidance/constants.hpp"
|
#include "extractor/guidance/constants.hpp"
|
||||||
#include "extractor/guidance/intersection_generator.hpp"
|
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||||
|
#include "extractor/guidance/turn_lane_data.hpp"
|
||||||
|
#include "extractor/intersection/intersection_analysis.hpp"
|
||||||
#include "util/coordinate.hpp"
|
#include "util/coordinate.hpp"
|
||||||
#include "util/coordinate_calculation.hpp"
|
#include "util/coordinate_calculation.hpp"
|
||||||
#include "util/node_based_graph.hpp"
|
#include "util/node_based_graph.hpp"
|
||||||
@ -29,7 +31,11 @@ class NodeBasedGraphWalker
|
|||||||
public:
|
public:
|
||||||
NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &node_based_graph,
|
NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const IntersectionGenerator &intersection_generator);
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* the returned node-id, edge-id are either the last ones used, just prior accumulator
|
* the returned node-id, edge-id are either the last ones used, just prior accumulator
|
||||||
@ -48,7 +54,11 @@ class NodeBasedGraphWalker
|
|||||||
private:
|
private:
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||||
const EdgeBasedNodeDataContainer &node_data_container;
|
const EdgeBasedNodeDataContainer &node_data_container;
|
||||||
const IntersectionGenerator &intersection_generator;
|
const std::vector<util::Coordinate> &node_coordinates;
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries;
|
||||||
|
const RestrictionMap &node_restriction_map;
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes;
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -149,7 +159,13 @@ struct SelectStraightmostRoadByNameAndOnlyChoice
|
|||||||
struct IntersectionFinderAccumulator
|
struct IntersectionFinderAccumulator
|
||||||
{
|
{
|
||||||
IntersectionFinderAccumulator(const std::uint8_t hop_limit,
|
IntersectionFinderAccumulator(const std::uint8_t hop_limit,
|
||||||
const IntersectionGenerator &intersection_generator);
|
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data);
|
||||||
// true if the path has traversed enough distance
|
// true if the path has traversed enough distance
|
||||||
bool terminate();
|
bool terminate();
|
||||||
|
|
||||||
@ -159,13 +175,19 @@ struct IntersectionFinderAccumulator
|
|||||||
std::uint8_t hops;
|
std::uint8_t hops;
|
||||||
const std::uint8_t hop_limit;
|
const std::uint8_t hop_limit;
|
||||||
|
|
||||||
// we need to be able to look-up the intersection
|
|
||||||
const IntersectionGenerator &intersection_generator;
|
|
||||||
|
|
||||||
// the result we are looking for
|
// the result we are looking for
|
||||||
NodeID nid;
|
NodeID nid;
|
||||||
EdgeID via_edge_id;
|
EdgeID via_edge_id;
|
||||||
IntersectionView intersection;
|
IntersectionView intersection;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||||
|
const EdgeBasedNodeDataContainer &node_data_container;
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates;
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries;
|
||||||
|
const RestrictionMap &node_restriction_map;
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes;
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class accumulator_type, class selector_type>
|
template <class accumulator_type, class selector_type>
|
||||||
@ -199,9 +221,15 @@ NodeBasedGraphWalker::TraverseRoad(NodeID current_node_id,
|
|||||||
return {};
|
return {};
|
||||||
|
|
||||||
// look at the next intersection
|
// look at the next intersection
|
||||||
const constexpr auto LOW_PRECISION = true;
|
const auto next_intersection =
|
||||||
const auto next_intersection = intersection_generator.GetConnectedRoads(
|
intersection::getConnectedRoads<true>(node_based_graph,
|
||||||
current_node_id, current_edge_id, LOW_PRECISION);
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
|
{current_node_id, current_edge_id});
|
||||||
|
|
||||||
// don't follow u-turns or go past our initial intersection
|
// don't follow u-turns or go past our initial intersection
|
||||||
if (next_intersection.size() <= 1)
|
if (next_intersection.size() <= 1)
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#include "extractor/compressed_edge_container.hpp"
|
#include "extractor/compressed_edge_container.hpp"
|
||||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
#include "extractor/guidance/intersection_generator.hpp"
|
|
||||||
#include "extractor/guidance/intersection_handler.hpp"
|
#include "extractor/guidance/intersection_handler.hpp"
|
||||||
#include "extractor/guidance/roundabout_type.hpp"
|
#include "extractor/guidance/roundabout_type.hpp"
|
||||||
#include "extractor/query_node.hpp"
|
#include "extractor/query_node.hpp"
|
||||||
@ -42,10 +41,12 @@ class RoundaboutHandler : public IntersectionHandler
|
|||||||
RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &coordinates,
|
||||||
const CompressedEdgeContainer &compressed_edge_container,
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table,
|
const SuffixTable &street_name_suffix_table);
|
||||||
const IntersectionGenerator &intersection_generator);
|
|
||||||
|
|
||||||
~RoundaboutHandler() override final = default;
|
~RoundaboutHandler() override final = default;
|
||||||
|
|
||||||
@ -64,10 +65,6 @@ class RoundaboutHandler : public IntersectionHandler
|
|||||||
const EdgeID via_eid,
|
const EdgeID via_eid,
|
||||||
const Intersection &intersection) const;
|
const Intersection &intersection) const;
|
||||||
|
|
||||||
void invalidateExitAgainstDirection(const NodeID from_nid,
|
|
||||||
const EdgeID via_eid,
|
|
||||||
Intersection &intersection) const;
|
|
||||||
|
|
||||||
// decide whether we lookk at a roundabout or a rotary
|
// decide whether we lookk at a roundabout or a rotary
|
||||||
RoundaboutType getRoundaboutType(const NodeID nid) const;
|
RoundaboutType getRoundaboutType(const NodeID nid) const;
|
||||||
|
|
||||||
@ -84,7 +81,6 @@ class RoundaboutHandler : public IntersectionHandler
|
|||||||
bool
|
bool
|
||||||
qualifiesAsRoundaboutIntersection(const std::unordered_set<NodeID> &roundabout_nodes) const;
|
qualifiesAsRoundaboutIntersection(const std::unordered_set<NodeID> &roundabout_nodes) const;
|
||||||
|
|
||||||
const CompressedEdgeContainer &compressed_edge_container;
|
|
||||||
const CoordinateExtractor coordinate_extractor;
|
const CoordinateExtractor coordinate_extractor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#define OSRM_EXTRACTOR_GUIDANCE_SLIPROAD_HANDLER_HPP_
|
#define OSRM_EXTRACTOR_GUIDANCE_SLIPROAD_HANDLER_HPP_
|
||||||
|
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
#include "extractor/guidance/intersection_generator.hpp"
|
|
||||||
#include "extractor/guidance/intersection_handler.hpp"
|
#include "extractor/guidance/intersection_handler.hpp"
|
||||||
#include "extractor/query_node.hpp"
|
#include "extractor/query_node.hpp"
|
||||||
|
|
||||||
@ -24,10 +23,13 @@ namespace guidance
|
|||||||
class SliproadHandler final : public IntersectionHandler
|
class SliproadHandler final : public IntersectionHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SliproadHandler(const IntersectionGenerator &intersection_generator,
|
SliproadHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table);
|
const SuffixTable &street_name_suffix_table);
|
||||||
|
|
||||||
@ -78,6 +80,8 @@ class SliproadHandler final : public IntersectionHandler
|
|||||||
// The return value is guaranteed to not be larger than `threshold`.
|
// The return value is guaranteed to not be larger than `threshold`.
|
||||||
static double scaledThresholdByRoadClass(const double max_threshold,
|
static double scaledThresholdByRoadClass(const double max_threshold,
|
||||||
const RoadClassification &classification);
|
const RoadClassification &classification);
|
||||||
|
|
||||||
|
const CoordinateExtractor coordinate_extractor;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace guidance
|
} // namespace guidance
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@ -27,18 +27,24 @@ namespace guidance
|
|||||||
class StatisticsHandler final : public IntersectionHandler
|
class StatisticsHandler final : public IntersectionHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StatisticsHandler(const IntersectionGenerator &intersection_generator,
|
StatisticsHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table)
|
const SuffixTable &street_name_suffix_table)
|
||||||
: IntersectionHandler(node_based_graph,
|
: IntersectionHandler(node_based_graph,
|
||||||
node_data_container,
|
node_data_container,
|
||||||
coordinates,
|
coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table,
|
street_name_suffix_table)
|
||||||
intersection_generator)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +61,7 @@ class StatisticsHandler final : public IntersectionHandler
|
|||||||
|
|
||||||
for (const auto &kv : type_hist)
|
for (const auto &kv : type_hist)
|
||||||
if (kv.second > 0)
|
if (kv.second > 0)
|
||||||
util::Log() << std::fixed << std::setprecision(2)
|
util::Log() << " " << std::fixed << std::setprecision(2)
|
||||||
<< internalInstructionTypeToString(kv.first) << ": " << kv.second
|
<< internalInstructionTypeToString(kv.first) << ": " << kv.second
|
||||||
<< " (" << (kv.second / static_cast<float>(num_types) * 100.) << "%)";
|
<< " (" << (kv.second / static_cast<float>(num_types) * 100.) << "%)";
|
||||||
|
|
||||||
@ -63,7 +69,7 @@ class StatisticsHandler final : public IntersectionHandler
|
|||||||
|
|
||||||
for (const auto &kv : modifier_hist)
|
for (const auto &kv : modifier_hist)
|
||||||
if (kv.second > 0)
|
if (kv.second > 0)
|
||||||
util::Log() << std::fixed << std::setprecision(2)
|
util::Log() << " " << std::fixed << std::setprecision(2)
|
||||||
<< instructionModifierToString(kv.first) << ": " << kv.second << " ("
|
<< instructionModifierToString(kv.first) << ": " << kv.second << " ("
|
||||||
<< (kv.second / static_cast<float>(num_modifiers) * 100.) << "%)";
|
<< (kv.second / static_cast<float>(num_modifiers) * 100.) << "%)";
|
||||||
}
|
}
|
||||||
@ -84,12 +90,14 @@ class StatisticsHandler final : public IntersectionHandler
|
|||||||
// numbers closer to the handlers and see how often handlers ran.
|
// numbers closer to the handlers and see how often handlers ran.
|
||||||
for (const auto &road : intersection)
|
for (const auto &road : intersection)
|
||||||
{
|
{
|
||||||
|
if (road.entry_allowed)
|
||||||
|
{
|
||||||
|
const auto type = road.instruction.type;
|
||||||
|
const auto modifier = road.instruction.direction_modifier;
|
||||||
|
|
||||||
const auto type = road.instruction.type;
|
type_hist[type] += 1;
|
||||||
const auto modifier = road.instruction.direction_modifier;
|
modifier_hist[modifier] += 1;
|
||||||
|
}
|
||||||
type_hist[type] += 1;
|
|
||||||
modifier_hist[modifier] += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return intersection;
|
return intersection;
|
||||||
@ -97,8 +105,8 @@ class StatisticsHandler final : public IntersectionHandler
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
mutable std::mutex lock;
|
mutable std::mutex lock;
|
||||||
mutable std::unordered_map<TurnType::Enum, std::uint64_t> type_hist;
|
mutable std::map<TurnType::Enum, std::uint64_t> type_hist;
|
||||||
mutable std::unordered_map<DirectionModifier::Enum, std::uint64_t> modifier_hist;
|
mutable std::map<DirectionModifier::Enum, std::uint64_t> modifier_hist;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace guidance
|
} // namespace guidance
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include "extractor/guidance/constants.hpp"
|
#include "extractor/guidance/constants.hpp"
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
#include "extractor/guidance/intersection_generator.hpp"
|
|
||||||
#include "extractor/guidance/intersection_handler.hpp"
|
#include "extractor/guidance/intersection_handler.hpp"
|
||||||
#include "extractor/travel_mode.hpp"
|
#include "extractor/travel_mode.hpp"
|
||||||
#include "util/node_based_graph.hpp"
|
#include "util/node_based_graph.hpp"
|
||||||
@ -21,10 +20,13 @@ namespace guidance
|
|||||||
class SuppressModeHandler final : public IntersectionHandler
|
class SuppressModeHandler final : public IntersectionHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SuppressModeHandler(const IntersectionGenerator &intersection_generator,
|
SuppressModeHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table);
|
const SuffixTable &street_name_suffix_table);
|
||||||
|
|
||||||
|
@ -4,9 +4,6 @@
|
|||||||
#include "extractor/compressed_edge_container.hpp"
|
#include "extractor/compressed_edge_container.hpp"
|
||||||
#include "extractor/guidance/driveway_handler.hpp"
|
#include "extractor/guidance/driveway_handler.hpp"
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
#include "extractor/guidance/intersection_generator.hpp"
|
|
||||||
#include "extractor/guidance/intersection_normalization_operation.hpp"
|
|
||||||
#include "extractor/guidance/intersection_normalizer.hpp"
|
|
||||||
#include "extractor/guidance/motorway_handler.hpp"
|
#include "extractor/guidance/motorway_handler.hpp"
|
||||||
#include "extractor/guidance/roundabout_handler.hpp"
|
#include "extractor/guidance/roundabout_handler.hpp"
|
||||||
#include "extractor/guidance/sliproad_handler.hpp"
|
#include "extractor/guidance/sliproad_handler.hpp"
|
||||||
@ -43,10 +40,11 @@ class TurnAnalysis
|
|||||||
public:
|
public:
|
||||||
TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
|
TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const CompressedEdgeContainer &compressed_edge_container,
|
||||||
const RestrictionMap &restriction_map,
|
const RestrictionMap &restriction_map,
|
||||||
const std::unordered_set<NodeID> &barrier_nodes,
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
const CompressedEdgeContainer &compressed_edge_container,
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table);
|
const SuffixTable &street_name_suffix_table);
|
||||||
|
|
||||||
@ -56,34 +54,14 @@ class TurnAnalysis
|
|||||||
Intersection operator()(const NodeID node_prior_to_intersection,
|
Intersection operator()(const NodeID node_prior_to_intersection,
|
||||||
const EdgeID entering_via_edge) const;
|
const EdgeID entering_via_edge) const;
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns a normalized intersection without any assigned turn types.
|
|
||||||
* This intersection can be used as input for intersection classification, turn lane assignment
|
|
||||||
* and similar.
|
|
||||||
*/
|
|
||||||
struct ShapeResult
|
|
||||||
{
|
|
||||||
// the basic shape, containing all turns
|
|
||||||
IntersectionShape intersection_shape;
|
|
||||||
// normalized shape, merged some roads into others, adjusted bearings
|
|
||||||
// see intersection_normalizer for further explanations
|
|
||||||
IntersectionNormalizer::NormalizationResult annotated_normalized_shape;
|
|
||||||
};
|
|
||||||
OSRM_ATTR_WARN_UNUSED
|
|
||||||
ShapeResult ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const;
|
|
||||||
|
|
||||||
// Select turn types based on the intersection shape
|
// Select turn types based on the intersection shape
|
||||||
OSRM_ATTR_WARN_UNUSED
|
OSRM_ATTR_WARN_UNUSED
|
||||||
Intersection AssignTurnTypes(const NodeID from_node,
|
Intersection AssignTurnTypes(const NodeID from_node,
|
||||||
const EdgeID via_eid,
|
const EdgeID via_eid,
|
||||||
const IntersectionView &intersection) const;
|
const IntersectionView &intersection) const;
|
||||||
|
|
||||||
const IntersectionGenerator &GetIntersectionGenerator() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||||
const IntersectionGenerator intersection_generator;
|
|
||||||
const IntersectionNormalizer intersection_normalizer;
|
|
||||||
const RoundaboutHandler roundabout_handler;
|
const RoundaboutHandler roundabout_handler;
|
||||||
const MotorwayHandler motorway_handler;
|
const MotorwayHandler motorway_handler;
|
||||||
const TurnHandler turn_handler;
|
const TurnHandler turn_handler;
|
||||||
|
@ -2,15 +2,27 @@
|
|||||||
#define OSRM_EXTRACTOR_GUIDANCE_TURN_DISCOVERY_HPP_
|
#define OSRM_EXTRACTOR_GUIDANCE_TURN_DISCOVERY_HPP_
|
||||||
|
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
#include "extractor/guidance/intersection_generator.hpp"
|
#include "extractor/guidance/turn_lane_data.hpp"
|
||||||
|
#include "extractor/restriction_index.hpp"
|
||||||
#include "util/typedefs.hpp"
|
#include "util/typedefs.hpp"
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace osrm
|
namespace osrm
|
||||||
{
|
{
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
class Coordinate;
|
||||||
|
}
|
||||||
|
|
||||||
namespace extractor
|
namespace extractor
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class CompressedEdgeContainer;
|
||||||
|
|
||||||
namespace guidance
|
namespace guidance
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace lanes
|
namespace lanes
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -21,8 +33,13 @@ bool findPreviousIntersection(
|
|||||||
const NodeID node,
|
const NodeID node,
|
||||||
const EdgeID via_edge,
|
const EdgeID via_edge,
|
||||||
const Intersection &intersection,
|
const Intersection &intersection,
|
||||||
const IntersectionGenerator &intersection_generator,
|
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph, // query edge data
|
const util::NodeBasedDynamicGraph &node_based_graph, // query edge data
|
||||||
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
// output parameters, will be in an arbitrary state on failure
|
// output parameters, will be in an arbitrary state on failure
|
||||||
NodeID &result_node,
|
NodeID &result_node,
|
||||||
EdgeID &result_via_edge,
|
EdgeID &result_via_edge,
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#define OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_
|
#define OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_
|
||||||
|
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
#include "extractor/guidance/intersection_generator.hpp"
|
|
||||||
#include "extractor/guidance/intersection_handler.hpp"
|
#include "extractor/guidance/intersection_handler.hpp"
|
||||||
#include "extractor/query_node.hpp"
|
#include "extractor/query_node.hpp"
|
||||||
|
|
||||||
@ -30,9 +29,12 @@ class TurnHandler : public IntersectionHandler
|
|||||||
TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table,
|
const SuffixTable &street_name_suffix_table);
|
||||||
const IntersectionGenerator &intersection_generator);
|
|
||||||
|
|
||||||
~TurnHandler() override final = default;
|
~TurnHandler() override final = default;
|
||||||
|
|
||||||
|
@ -74,6 +74,11 @@ class TurnLaneHandler
|
|||||||
|
|
||||||
TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
LaneDescriptionMap &lane_description_map,
|
LaneDescriptionMap &lane_description_map,
|
||||||
const TurnAnalysis &turn_analysis,
|
const TurnAnalysis &turn_analysis,
|
||||||
util::guidance::LaneDataIdMap &id_map);
|
util::guidance::LaneDataIdMap &id_map);
|
||||||
@ -90,6 +95,12 @@ class TurnLaneHandler
|
|||||||
// lanes for a turn
|
// lanes for a turn
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||||
const EdgeBasedNodeDataContainer &node_data_container;
|
const EdgeBasedNodeDataContainer &node_data_container;
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates;
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries;
|
||||||
|
const RestrictionMap &node_restriction_map;
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes;
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data;
|
||||||
|
|
||||||
std::vector<std::uint32_t> turn_lane_offsets;
|
std::vector<std::uint32_t> turn_lane_offsets;
|
||||||
std::vector<TurnLaneType::Mask> turn_lane_masks;
|
std::vector<TurnLaneType::Mask> turn_lane_masks;
|
||||||
LaneDescriptionMap &lane_description_map;
|
LaneDescriptionMap &lane_description_map;
|
||||||
|
@ -100,8 +100,10 @@ typedef util::ConcurrentIDMap<guidance::TurnLaneDescription,
|
|||||||
guidance::TurnLaneDescription_hash>
|
guidance::TurnLaneDescription_hash>
|
||||||
LaneDescriptionMap;
|
LaneDescriptionMap;
|
||||||
|
|
||||||
inline std::tuple<std::vector<std::uint32_t>, std::vector<TurnLaneType::Mask>>
|
using TurnLanesIndexedArray =
|
||||||
transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map)
|
std::tuple<std::vector<std::uint32_t>, std::vector<TurnLaneType::Mask>>;
|
||||||
|
|
||||||
|
inline TurnLanesIndexedArray transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map)
|
||||||
{
|
{
|
||||||
// could use some additional capacity? To avoid a copy during processing, though small data so
|
// could use some additional capacity? To avoid a copy during processing, though small data so
|
||||||
// probably not that important.
|
// probably not that important.
|
||||||
@ -111,8 +113,7 @@ transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map)
|
|||||||
//
|
//
|
||||||
// turn lane offsets points into the locations of the turn_lane_masks array. We use a standard
|
// turn lane offsets points into the locations of the turn_lane_masks array. We use a standard
|
||||||
// adjacency array like structure to store the turn lane masks.
|
// adjacency array like structure to store the turn lane masks.
|
||||||
std::vector<std::uint32_t> turn_lane_offsets(turn_lane_map.data.size() +
|
std::vector<std::uint32_t> turn_lane_offsets(turn_lane_map.data.size() + 1); // + sentinel
|
||||||
2); // empty ID + sentinel
|
|
||||||
for (auto entry = turn_lane_map.data.begin(); entry != turn_lane_map.data.end(); ++entry)
|
for (auto entry = turn_lane_map.data.begin(); entry != turn_lane_map.data.end(); ++entry)
|
||||||
turn_lane_offsets[entry->second + 1] = entry->first.size();
|
turn_lane_offsets[entry->second + 1] = entry->first.size();
|
||||||
|
|
||||||
@ -125,6 +126,7 @@ transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map)
|
|||||||
std::copy(entry->first.begin(),
|
std::copy(entry->first.begin(),
|
||||||
entry->first.end(),
|
entry->first.end(),
|
||||||
turn_lane_masks.begin() + turn_lane_offsets[entry->second]);
|
turn_lane_masks.begin() + turn_lane_offsets[entry->second]);
|
||||||
|
|
||||||
return std::make_tuple(std::move(turn_lane_offsets), std::move(turn_lane_masks));
|
return std::make_tuple(std::move(turn_lane_offsets), std::move(turn_lane_masks));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
87
include/extractor/intersection/intersection_analysis.hpp
Normal file
87
include/extractor/intersection/intersection_analysis.hpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#ifndef OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_ANALYSIS_HPP
|
||||||
|
#define OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_ANALYSIS_HPP
|
||||||
|
|
||||||
|
#include "extractor/compressed_edge_container.hpp"
|
||||||
|
#include "extractor/guidance/mergable_road_detector.hpp"
|
||||||
|
#include "extractor/guidance/turn_lane_types.hpp"
|
||||||
|
#include "extractor/intersection/intersection_edge.hpp"
|
||||||
|
#include "extractor/restriction_index.hpp"
|
||||||
|
|
||||||
|
#include "util/coordinate.hpp"
|
||||||
|
#include "util/node_based_graph.hpp"
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace osrm
|
||||||
|
{
|
||||||
|
namespace extractor
|
||||||
|
{
|
||||||
|
namespace intersection
|
||||||
|
{
|
||||||
|
|
||||||
|
IntersectionEdges getIncomingEdges(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const NodeID intersection);
|
||||||
|
|
||||||
|
IntersectionEdges getOutgoingEdges(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const NodeID intersection);
|
||||||
|
|
||||||
|
bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const RestrictionMap &restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const IntersectionEdgeGeometries &geometries,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
|
const IntersectionEdge &from,
|
||||||
|
const IntersectionEdge &to);
|
||||||
|
|
||||||
|
double findEdgeBearing(const IntersectionEdgeGeometries &geometries, const EdgeID &edge);
|
||||||
|
|
||||||
|
double findEdgeLength(const IntersectionEdgeGeometries &geometries, const EdgeID &edge);
|
||||||
|
|
||||||
|
std::pair<IntersectionEdgeGeometries, std::unordered_set<EdgeID>>
|
||||||
|
getIntersectionGeometries(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const guidance::MergableRoadDetector &detector,
|
||||||
|
const NodeID intersection);
|
||||||
|
|
||||||
|
guidance::IntersectionView
|
||||||
|
convertToIntersectionView(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const RestrictionMap &restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const IntersectionEdgeGeometries &edge_geometries,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
|
const IntersectionEdge &incoming_edge,
|
||||||
|
const IntersectionEdges &outgoing_edges,
|
||||||
|
const std::unordered_set<EdgeID> &merged_edges);
|
||||||
|
|
||||||
|
// Check for restrictions/barriers and generate a list of valid and invalid turns present at
|
||||||
|
// the node reached from `incoming_edge`. The resulting candidates have to be analyzed
|
||||||
|
// for their actual instructions later on.
|
||||||
|
template <bool USE_CLOSE_COORDINATE>
|
||||||
|
guidance::IntersectionView
|
||||||
|
getConnectedRoads(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
|
const IntersectionEdge &incoming_edge);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// intermediate intersection, if there is a traffic light in between. If we want to look farther
|
||||||
|
// 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. This function skips over degree two nodes to find correct input
|
||||||
|
// for getConnectedRoads.
|
||||||
|
IntersectionEdge skipDegreeTwoNodes(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
IntersectionEdge road);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
44
include/extractor/intersection/intersection_edge.hpp
Normal file
44
include/extractor/intersection/intersection_edge.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_EDGE_HPP
|
||||||
|
#define OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_EDGE_HPP
|
||||||
|
|
||||||
|
#include "util/typedefs.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace osrm
|
||||||
|
{
|
||||||
|
namespace extractor
|
||||||
|
{
|
||||||
|
namespace intersection
|
||||||
|
{
|
||||||
|
|
||||||
|
// IntersectionEdge is an alias for incoming and outgoing node-based graph edges of an intersection
|
||||||
|
struct IntersectionEdge
|
||||||
|
{
|
||||||
|
NodeID node;
|
||||||
|
EdgeID edge;
|
||||||
|
|
||||||
|
bool operator<(const IntersectionEdge &other) const
|
||||||
|
{
|
||||||
|
return std::tie(node, edge) < std::tie(other.node, other.edge);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using IntersectionEdges = std::vector<IntersectionEdge>;
|
||||||
|
|
||||||
|
struct IntersectionEdgeGeometry
|
||||||
|
{
|
||||||
|
EdgeID edge;
|
||||||
|
double initial_bearing;
|
||||||
|
double perceived_bearing;
|
||||||
|
double length;
|
||||||
|
|
||||||
|
bool operator<(const IntersectionEdgeGeometry &other) const { return edge < other.edge; }
|
||||||
|
};
|
||||||
|
|
||||||
|
using IntersectionEdgeGeometries = std::vector<IntersectionEdgeGeometry>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -144,18 +144,6 @@ inline double restrictAngleToValidRange(const double angle)
|
|||||||
return angle;
|
return angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// finds the angle between two angles, based on the minum difference between the two
|
|
||||||
inline double angleBetween(const double lhs, const double rhs)
|
|
||||||
{
|
|
||||||
const auto difference = std::abs(lhs - rhs);
|
|
||||||
const auto is_clockwise_difference = difference <= 180;
|
|
||||||
const auto angle_between_candidate = .5 * (lhs + rhs);
|
|
||||||
if (is_clockwise_difference)
|
|
||||||
return angle_between_candidate;
|
|
||||||
else
|
|
||||||
return restrictAngleToValidRange(angle_between_candidate + 180);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace osrm
|
} // namespace osrm
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "osrm",
|
"name": "osrm",
|
||||||
"version": "5.15.0-latest.1",
|
"version": "5.15.0-latest.2",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.",
|
"description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include "extractor/scripting_environment.hpp"
|
#include "extractor/scripting_environment.hpp"
|
||||||
#include "extractor/suffix_table.hpp"
|
#include "extractor/suffix_table.hpp"
|
||||||
|
|
||||||
|
#include "extractor/intersection/intersection_analysis.hpp"
|
||||||
|
|
||||||
#include "extractor/serialization.hpp"
|
#include "extractor/serialization.hpp"
|
||||||
#include "storage/io.hpp"
|
#include "storage/io.hpp"
|
||||||
|
|
||||||
@ -419,22 +421,39 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
|
|
||||||
TurnDataExternalContainer turn_data_container;
|
TurnDataExternalContainer turn_data_container;
|
||||||
|
|
||||||
|
SuffixTable street_name_suffix_table(scripting_environment);
|
||||||
|
const auto &turn_lanes_data = transformTurnLaneMapIntoArrays(lane_description_map);
|
||||||
|
guidance::MergableRoadDetector mergable_road_detector(m_node_based_graph,
|
||||||
|
m_edge_based_node_container,
|
||||||
|
m_coordinates,
|
||||||
|
m_compressed_edge_container,
|
||||||
|
node_restriction_map,
|
||||||
|
m_barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
|
name_table,
|
||||||
|
street_name_suffix_table);
|
||||||
|
|
||||||
// Loop over all turns and generate new set of edges.
|
// Loop over all turns and generate new set of edges.
|
||||||
// Three nested loop look super-linear, but we are dealing with a (kind of)
|
// Three nested loop look super-linear, but we are dealing with a (kind of)
|
||||||
// linear number of turns only.
|
// linear number of turns only.
|
||||||
SuffixTable street_name_suffix_table(scripting_environment);
|
|
||||||
guidance::TurnAnalysis turn_analysis(m_node_based_graph,
|
guidance::TurnAnalysis turn_analysis(m_node_based_graph,
|
||||||
m_edge_based_node_container,
|
m_edge_based_node_container,
|
||||||
m_coordinates,
|
m_coordinates,
|
||||||
|
m_compressed_edge_container,
|
||||||
node_restriction_map,
|
node_restriction_map,
|
||||||
m_barrier_nodes,
|
m_barrier_nodes,
|
||||||
m_compressed_edge_container,
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table);
|
street_name_suffix_table);
|
||||||
|
|
||||||
util::guidance::LaneDataIdMap lane_data_map;
|
util::guidance::LaneDataIdMap lane_data_map;
|
||||||
guidance::lanes::TurnLaneHandler turn_lane_handler(m_node_based_graph,
|
guidance::lanes::TurnLaneHandler turn_lane_handler(m_node_based_graph,
|
||||||
m_edge_based_node_container,
|
m_edge_based_node_container,
|
||||||
|
m_coordinates,
|
||||||
|
m_compressed_edge_container,
|
||||||
|
node_restriction_map,
|
||||||
|
m_barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
lane_description_map,
|
lane_description_map,
|
||||||
turn_analysis,
|
turn_analysis,
|
||||||
lane_data_map);
|
lane_data_map);
|
||||||
@ -537,14 +556,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
// the situation of the turn
|
// the situation of the turn
|
||||||
const auto node_along_road_entering,
|
const auto node_along_road_entering,
|
||||||
const auto node_based_edge_from,
|
const auto node_based_edge_from,
|
||||||
const auto node_at_center_of_intersection,
|
const auto intersection_node,
|
||||||
const auto node_based_edge_to,
|
const auto node_based_edge_to,
|
||||||
const auto &intersection,
|
const auto incoming_bearing,
|
||||||
const auto &turn,
|
const auto &turn,
|
||||||
const auto entry_class_id) {
|
const auto entry_class_id) {
|
||||||
|
|
||||||
const auto node_restricted = isRestricted(node_along_road_entering,
|
const auto node_restricted = isRestricted(node_along_road_entering,
|
||||||
node_at_center_of_intersection,
|
intersection_node,
|
||||||
m_node_based_graph.GetTarget(turn.eid),
|
m_node_based_graph.GetTarget(turn.eid),
|
||||||
conditional_restriction_map);
|
conditional_restriction_map);
|
||||||
|
|
||||||
@ -556,7 +575,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
conditional = {{edge_based_node_from,
|
conditional = {{edge_based_node_from,
|
||||||
edge_based_node_to,
|
edge_based_node_to,
|
||||||
{static_cast<std::uint64_t>(-1),
|
{static_cast<std::uint64_t>(-1),
|
||||||
m_coordinates[node_at_center_of_intersection],
|
m_coordinates[intersection_node],
|
||||||
conditions}}};
|
conditions}}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -572,14 +591,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
TurnData turn_data = {turn.instruction,
|
TurnData turn_data = {turn.instruction,
|
||||||
turn.lane_data_id,
|
turn.lane_data_id,
|
||||||
entry_class_id,
|
entry_class_id,
|
||||||
util::guidance::TurnBearing(intersection[0].bearing),
|
util::guidance::TurnBearing(incoming_bearing),
|
||||||
util::guidance::TurnBearing(turn.bearing)};
|
util::guidance::TurnBearing(turn.bearing)};
|
||||||
|
|
||||||
// compute weight and duration penalties
|
// compute weight and duration penalties
|
||||||
auto is_traffic_light = m_traffic_lights.count(node_at_center_of_intersection);
|
auto is_traffic_light = m_traffic_lights.count(intersection_node);
|
||||||
ExtractionTurn extracted_turn(
|
ExtractionTurn extracted_turn(
|
||||||
turn.angle,
|
turn.angle,
|
||||||
m_node_based_graph.GetOutDegree(node_at_center_of_intersection),
|
m_node_based_graph.GetOutDegree(intersection_node),
|
||||||
turn.instruction.direction_modifier == guidance::DirectionModifier::UTurn,
|
turn.instruction.direction_modifier == guidance::DirectionModifier::UTurn,
|
||||||
is_traffic_light,
|
is_traffic_light,
|
||||||
edge_data1.flags.restricted,
|
edge_data1.flags.restricted,
|
||||||
@ -630,8 +649,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
: m_compressed_edge_container.GetLastEdgeSourceID(node_based_edge_from);
|
: m_compressed_edge_container.GetLastEdgeSourceID(node_based_edge_from);
|
||||||
const auto &to_node = m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid);
|
const auto &to_node = m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid);
|
||||||
|
|
||||||
lookup::TurnIndexBlock turn_index_block = {
|
lookup::TurnIndexBlock turn_index_block = {from_node, intersection_node, to_node};
|
||||||
from_node, node_at_center_of_intersection, to_node};
|
|
||||||
|
|
||||||
// insert data into the designated buffer
|
// insert data into the designated buffer
|
||||||
return std::make_pair(
|
return std::make_pair(
|
||||||
@ -653,17 +671,26 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
if (buffer->nodes_processed == 0)
|
if (buffer->nodes_processed == 0)
|
||||||
return buffer;
|
return buffer;
|
||||||
|
|
||||||
for (auto node_at_center_of_intersection = intersection_node_range.begin(),
|
for (auto intersection_node = intersection_node_range.begin(),
|
||||||
end = intersection_node_range.end();
|
end = intersection_node_range.end();
|
||||||
node_at_center_of_intersection < end;
|
intersection_node < end;
|
||||||
++node_at_center_of_intersection)
|
++intersection_node)
|
||||||
{
|
{
|
||||||
|
|
||||||
// We capture the thread-local work in these objects, then flush
|
// We capture the thread-local work in these objects, then flush
|
||||||
// them in a controlled manner at the end of the parallel range
|
// them in a controlled manner at the end of the parallel range
|
||||||
|
const auto &incoming_edges =
|
||||||
|
intersection::getIncomingEdges(m_node_based_graph, intersection_node);
|
||||||
|
const auto &outgoing_edges =
|
||||||
|
intersection::getOutgoingEdges(m_node_based_graph, intersection_node);
|
||||||
|
|
||||||
const auto shape_result =
|
intersection::IntersectionEdgeGeometries edge_geometries;
|
||||||
turn_analysis.ComputeIntersectionShapes(node_at_center_of_intersection);
|
std::unordered_set<EdgeID> merged_edge_ids;
|
||||||
|
std::tie(edge_geometries, merged_edge_ids) =
|
||||||
|
intersection::getIntersectionGeometries(m_node_based_graph,
|
||||||
|
m_compressed_edge_container,
|
||||||
|
m_coordinates,
|
||||||
|
mergable_road_detector,
|
||||||
|
intersection_node);
|
||||||
|
|
||||||
// all nodes in the graph are connected in both directions. We check all
|
// 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,
|
// outgoing nodes to find the incoming edge. This is a larger search overhead,
|
||||||
@ -683,45 +710,33 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
// From the flags alone, we cannot determine which nodes are connected to
|
// 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
|
// `b` by an outgoing edge. Therefore, we have to search all connected edges for
|
||||||
// edges entering `b`
|
// edges entering `b`
|
||||||
for (const EdgeID outgoing_edge :
|
|
||||||
m_node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection))
|
for (const auto &incoming_edge : incoming_edges)
|
||||||
{
|
{
|
||||||
const NodeID node_along_road_entering =
|
|
||||||
m_node_based_graph.GetTarget(outgoing_edge);
|
|
||||||
|
|
||||||
const auto incoming_edge = m_node_based_graph.FindEdge(
|
|
||||||
node_along_road_entering, node_at_center_of_intersection);
|
|
||||||
|
|
||||||
if (m_node_based_graph.GetEdgeData(incoming_edge).reversed)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
++node_based_edge_counter;
|
++node_based_edge_counter;
|
||||||
|
|
||||||
auto intersection_with_flags_and_angles =
|
const auto intersection_view =
|
||||||
turn_analysis.GetIntersectionGenerator()
|
convertToIntersectionView(m_node_based_graph,
|
||||||
.TransformIntersectionShapeIntoView(
|
m_edge_based_node_container,
|
||||||
node_along_road_entering,
|
node_restriction_map,
|
||||||
incoming_edge,
|
m_barrier_nodes,
|
||||||
shape_result.annotated_normalized_shape.normalized_shape,
|
edge_geometries,
|
||||||
shape_result.intersection_shape,
|
turn_lanes_data,
|
||||||
shape_result.annotated_normalized_shape.performed_merges);
|
incoming_edge,
|
||||||
|
outgoing_edges,
|
||||||
|
merged_edge_ids);
|
||||||
|
|
||||||
auto intersection =
|
auto intersection = turn_analysis.AssignTurnTypes(
|
||||||
turn_analysis.AssignTurnTypes(node_along_road_entering,
|
incoming_edge.node, incoming_edge.edge, intersection_view);
|
||||||
incoming_edge,
|
|
||||||
intersection_with_flags_and_angles);
|
|
||||||
|
|
||||||
OSRM_ASSERT(intersection.valid(),
|
|
||||||
m_coordinates[node_at_center_of_intersection]);
|
|
||||||
|
|
||||||
|
OSRM_ASSERT(intersection.valid(), m_coordinates[intersection_node]);
|
||||||
intersection = turn_lane_handler.assignTurnLanes(
|
intersection = turn_lane_handler.assignTurnLanes(
|
||||||
node_along_road_entering, incoming_edge, std::move(intersection));
|
incoming_edge.node, incoming_edge.edge, std::move(intersection));
|
||||||
|
|
||||||
// the entry class depends on the turn, so we have to classify the
|
// the entry class depends on the turn, so we have to classify the
|
||||||
// interesction for
|
// interesction for every edge
|
||||||
// every edge
|
const auto turn_classification =
|
||||||
const auto turn_classification = classifyIntersection(
|
classifyIntersection(intersection, m_coordinates[intersection_node]);
|
||||||
intersection, m_coordinates[node_at_center_of_intersection]);
|
|
||||||
|
|
||||||
const auto entry_class_id =
|
const auto entry_class_id =
|
||||||
entry_class_hash.ConcurrentFindOrAdd(turn_classification.first);
|
entry_class_hash.ConcurrentFindOrAdd(turn_classification.first);
|
||||||
@ -732,19 +747,37 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
// Note - this is strictly speaking not thread safe, but we know we
|
// Note - this is strictly speaking not thread safe, but we know we
|
||||||
// should never be touching the same element twice, so we should
|
// should never be touching the same element twice, so we should
|
||||||
// be fine.
|
// be fine.
|
||||||
bearing_class_by_node_based_node[node_at_center_of_intersection] =
|
bearing_class_by_node_based_node[intersection_node] = bearing_class_id;
|
||||||
bearing_class_id;
|
|
||||||
|
|
||||||
// check if we are turning off a via way
|
// check if we are turning off a via way
|
||||||
const auto turning_off_via_way = way_restriction_map.IsViaWay(
|
const auto turning_off_via_way =
|
||||||
node_along_road_entering, node_at_center_of_intersection);
|
way_restriction_map.IsViaWay(incoming_edge.node, intersection_node);
|
||||||
|
|
||||||
for (const auto &turn : intersection)
|
// Save reversed incoming bearing to compute turn angles
|
||||||
|
const auto reversed_incoming_bearing = util::bearing::reverse(
|
||||||
|
findEdgeBearing(edge_geometries, incoming_edge.edge));
|
||||||
|
|
||||||
|
for (const auto &outgoing_edge : outgoing_edges)
|
||||||
{
|
{
|
||||||
// only keep valid turns
|
if (!intersection::isTurnAllowed(m_node_based_graph,
|
||||||
if (!turn.entry_allowed)
|
m_edge_based_node_container,
|
||||||
|
node_restriction_map,
|
||||||
|
m_barrier_nodes,
|
||||||
|
edge_geometries,
|
||||||
|
turn_lanes_data,
|
||||||
|
incoming_edge,
|
||||||
|
outgoing_edge))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
const auto turn =
|
||||||
|
std::find_if(intersection.begin(),
|
||||||
|
intersection.end(),
|
||||||
|
[edge = outgoing_edge.edge](const auto &road) {
|
||||||
|
return road.eid == edge;
|
||||||
|
});
|
||||||
|
OSRM_ASSERT(turn != intersection.end(),
|
||||||
|
m_coordinates[intersection_node]);
|
||||||
|
|
||||||
// In case a way restriction starts at a given location, add a turn onto
|
// In case a way restriction starts at a given location, add a turn onto
|
||||||
// every artificial node eminating here.
|
// every artificial node eminating here.
|
||||||
//
|
//
|
||||||
@ -766,22 +799,22 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
// duplicated node associated with the turn. (e.g. ab via bc switches bc
|
// duplicated node associated with the turn. (e.g. ab via bc switches bc
|
||||||
// to bc_dup)
|
// to bc_dup)
|
||||||
auto const target_id = way_restriction_map.RemapIfRestricted(
|
auto const target_id = way_restriction_map.RemapIfRestricted(
|
||||||
nbe_to_ebn_mapping[turn.eid],
|
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||||
node_along_road_entering,
|
incoming_edge.node,
|
||||||
node_at_center_of_intersection,
|
outgoing_edge.node,
|
||||||
m_node_based_graph.GetTarget(turn.eid),
|
m_node_based_graph.GetTarget(outgoing_edge.edge),
|
||||||
m_number_of_edge_based_nodes);
|
m_number_of_edge_based_nodes);
|
||||||
|
|
||||||
{ // scope to forget edge_with_data after
|
{ // scope to forget edge_with_data after
|
||||||
const auto edge_with_data_and_condition =
|
const auto edge_with_data_and_condition =
|
||||||
generate_edge(nbe_to_ebn_mapping[incoming_edge],
|
generate_edge(nbe_to_ebn_mapping[incoming_edge.edge],
|
||||||
target_id,
|
target_id,
|
||||||
node_along_road_entering,
|
incoming_edge.node,
|
||||||
incoming_edge,
|
incoming_edge.edge,
|
||||||
node_at_center_of_intersection,
|
outgoing_edge.node,
|
||||||
turn.eid,
|
outgoing_edge.edge,
|
||||||
intersection,
|
reversed_incoming_bearing,
|
||||||
turn,
|
*turn,
|
||||||
entry_class_id);
|
entry_class_id);
|
||||||
|
|
||||||
buffer->continuous_data.edges_list.push_back(
|
buffer->continuous_data.edges_list.push_back(
|
||||||
@ -808,7 +841,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
if (turning_off_via_way)
|
if (turning_off_via_way)
|
||||||
{
|
{
|
||||||
const auto duplicated_nodes = way_restriction_map.DuplicatedNodeIDs(
|
const auto duplicated_nodes = way_restriction_map.DuplicatedNodeIDs(
|
||||||
node_along_road_entering, node_at_center_of_intersection);
|
incoming_edge.node, intersection_node);
|
||||||
|
|
||||||
// next to the normal restrictions tracked in `entry_allowed`, via
|
// next to the normal restrictions tracked in `entry_allowed`, via
|
||||||
// ways might introduce additional restrictions. These are handled
|
// ways might introduce additional restrictions. These are handled
|
||||||
@ -816,12 +849,12 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
for (auto duplicated_node_id : duplicated_nodes)
|
for (auto duplicated_node_id : duplicated_nodes)
|
||||||
{
|
{
|
||||||
const auto from_id =
|
const auto from_id =
|
||||||
m_number_of_edge_based_nodes -
|
NodeID(m_number_of_edge_based_nodes -
|
||||||
way_restriction_map.NumberOfDuplicatedNodes() +
|
way_restriction_map.NumberOfDuplicatedNodes() +
|
||||||
duplicated_node_id;
|
duplicated_node_id);
|
||||||
|
|
||||||
auto const node_at_end_of_turn =
|
auto const node_at_end_of_turn =
|
||||||
m_node_based_graph.GetTarget(turn.eid);
|
m_node_based_graph.GetTarget(outgoing_edge.edge);
|
||||||
|
|
||||||
const auto is_way_restricted = way_restriction_map.IsRestricted(
|
const auto is_way_restricted = way_restriction_map.IsRestricted(
|
||||||
duplicated_node_id, node_at_end_of_turn);
|
duplicated_node_id, node_at_end_of_turn);
|
||||||
@ -836,14 +869,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
|
|
||||||
// add into delayed data
|
// add into delayed data
|
||||||
auto edge_with_data_and_condition =
|
auto edge_with_data_and_condition =
|
||||||
generate_edge(NodeID(from_id),
|
generate_edge(from_id,
|
||||||
nbe_to_ebn_mapping[turn.eid],
|
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||||
node_along_road_entering,
|
incoming_edge.node,
|
||||||
incoming_edge,
|
incoming_edge.edge,
|
||||||
node_at_center_of_intersection,
|
outgoing_edge.node,
|
||||||
turn.eid,
|
outgoing_edge.edge,
|
||||||
intersection,
|
reversed_incoming_bearing,
|
||||||
turn,
|
*turn,
|
||||||
entry_class_id);
|
entry_class_id);
|
||||||
|
|
||||||
buffer->delayed_data.push_back(
|
buffer->delayed_data.push_back(
|
||||||
@ -860,24 +893,24 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
{
|
{
|
||||||
// add a new conditional for the edge we just created
|
// add a new conditional for the edge we just created
|
||||||
buffer->conditionals.push_back(
|
buffer->conditionals.push_back(
|
||||||
{NodeID(from_id),
|
{from_id,
|
||||||
nbe_to_ebn_mapping[turn.eid],
|
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||||
{static_cast<std::uint64_t>(-1),
|
{static_cast<std::uint64_t>(-1),
|
||||||
m_coordinates[node_at_center_of_intersection],
|
m_coordinates[intersection_node],
|
||||||
restriction.condition}});
|
restriction.condition}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto edge_with_data_and_condition =
|
auto edge_with_data_and_condition =
|
||||||
generate_edge(NodeID(from_id),
|
generate_edge(from_id,
|
||||||
nbe_to_ebn_mapping[turn.eid],
|
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||||
node_along_road_entering,
|
incoming_edge.node,
|
||||||
incoming_edge,
|
incoming_edge.edge,
|
||||||
node_at_center_of_intersection,
|
outgoing_edge.node,
|
||||||
turn.eid,
|
outgoing_edge.edge,
|
||||||
intersection,
|
reversed_incoming_bearing,
|
||||||
turn,
|
*turn,
|
||||||
entry_class_id);
|
entry_class_id);
|
||||||
|
|
||||||
buffer->delayed_data.push_back(
|
buffer->delayed_data.push_back(
|
||||||
|
@ -12,18 +12,24 @@ namespace extractor
|
|||||||
namespace guidance
|
namespace guidance
|
||||||
{
|
{
|
||||||
|
|
||||||
DrivewayHandler::DrivewayHandler(const IntersectionGenerator &intersection_generator,
|
DrivewayHandler::DrivewayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table)
|
const SuffixTable &street_name_suffix_table)
|
||||||
: IntersectionHandler(node_based_graph,
|
: IntersectionHandler(node_based_graph,
|
||||||
node_data_container,
|
node_data_container,
|
||||||
coordinates,
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table,
|
street_name_suffix_table)
|
||||||
intersection_generator)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,12 +70,12 @@ operator()(const NodeID nid, const EdgeID source_edge_id, Intersection intersect
|
|||||||
});
|
});
|
||||||
|
|
||||||
(void)nid;
|
(void)nid;
|
||||||
OSRM_ASSERT(road != intersection.end(), coordinates[nid]);
|
OSRM_ASSERT(road != intersection.end(), node_coordinates[nid]);
|
||||||
|
|
||||||
if (road->instruction == TurnInstruction::INVALID())
|
if (road->instruction == TurnInstruction::INVALID())
|
||||||
return intersection;
|
return intersection;
|
||||||
|
|
||||||
OSRM_ASSERT(road->instruction.type == TurnType::Turn, coordinates[nid]);
|
OSRM_ASSERT(road->instruction.type == TurnType::Turn, node_coordinates[nid]);
|
||||||
|
|
||||||
road->instruction.type =
|
road->instruction.type =
|
||||||
isSameName(source_edge_id, road->eid) ? TurnType::NoTurn : TurnType::NewName;
|
isSameName(source_edge_id, road->eid) ? TurnType::NoTurn : TurnType::NewName;
|
||||||
|
@ -1,488 +0,0 @@
|
|||||||
#include "extractor/guidance/intersection_generator.hpp"
|
|
||||||
|
|
||||||
#include "extractor/geojson_debug_policies.hpp"
|
|
||||||
|
|
||||||
#include "util/geojson_debug_logger.hpp"
|
|
||||||
|
|
||||||
#include "util/assert.hpp"
|
|
||||||
#include "util/bearing.hpp"
|
|
||||||
#include "util/coordinate_calculation.hpp"
|
|
||||||
#include "util/log.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
|
||||||
#include <functional> // mem_fn
|
|
||||||
#include <limits>
|
|
||||||
#include <numeric>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <boost/range/algorithm/count_if.hpp>
|
|
||||||
|
|
||||||
namespace osrm
|
|
||||||
{
|
|
||||||
namespace extractor
|
|
||||||
{
|
|
||||||
namespace guidance
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
const constexpr bool USE_LOW_PRECISION_MODE = true;
|
|
||||||
// the inverse of use low precision mode
|
|
||||||
const constexpr bool USE_HIGH_PRECISION_MODE = !USE_LOW_PRECISION_MODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
IntersectionGenerator::IntersectionGenerator(
|
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
|
||||||
const RestrictionMap &restriction_map,
|
|
||||||
const std::unordered_set<NodeID> &barrier_nodes,
|
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
|
||||||
const CompressedEdgeContainer &compressed_edge_container)
|
|
||||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
|
||||||
restriction_map(restriction_map), barrier_nodes(barrier_nodes), coordinates(coordinates),
|
|
||||||
coordinate_extractor(node_based_graph, compressed_edge_container, coordinates)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
const auto intersection_degree = node_based_graph.GetOutDegree(node_at_center_of_intersection);
|
|
||||||
const util::Coordinate turn_coordinate = coordinates[node_at_center_of_intersection];
|
|
||||||
|
|
||||||
// compute bearings in a relatively small circle to prevent wrong roads order with true bearings
|
|
||||||
struct RoadWithInitialBearing
|
|
||||||
{
|
|
||||||
double bearing;
|
|
||||||
IntersectionShapeData road;
|
|
||||||
};
|
|
||||||
std::vector<RoadWithInitialBearing> initial_roads_ordering;
|
|
||||||
// reserve enough items (+ the possibly missing u-turn edge)
|
|
||||||
initial_roads_ordering.reserve(intersection_degree);
|
|
||||||
|
|
||||||
// number of lanes at the intersection changes how far we look down the road
|
|
||||||
const auto edge_range = node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection);
|
|
||||||
const auto max_lanes_intersection =
|
|
||||||
std::accumulate(edge_range.begin(),
|
|
||||||
edge_range.end(),
|
|
||||||
std::uint8_t{0},
|
|
||||||
[this](const auto current_max, const auto current_eid) {
|
|
||||||
return std::max(current_max,
|
|
||||||
node_based_graph.GetEdgeData(current_eid)
|
|
||||||
.flags.road_classification.GetNumberOfLanes());
|
|
||||||
});
|
|
||||||
|
|
||||||
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 close_coordinate =
|
|
||||||
coordinate_extractor.ExtractCoordinateAtLength(2. /*m*/, coordinates);
|
|
||||||
const auto initial_bearing =
|
|
||||||
util::coordinate_calculation::bearing(turn_coordinate, close_coordinate);
|
|
||||||
|
|
||||||
const auto segment_length = util::coordinate_calculation::getLength(
|
|
||||||
coordinates.begin(),
|
|
||||||
coordinates.end(),
|
|
||||||
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,
|
|
||||||
max_lanes_intersection,
|
|
||||||
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);
|
|
||||||
|
|
||||||
// OSM data sometimes contains duplicated nodes with identical coordinates, or
|
|
||||||
// because of coordinate precision rounding, end up at the same coordinate.
|
|
||||||
// It's impossible to calculate a bearing between these, so we log a warning
|
|
||||||
// that the data should be checked.
|
|
||||||
// The bearing calculation should return 0 in these cases, which may not be correct,
|
|
||||||
// but is at least not random.
|
|
||||||
if (turn_coordinate == coordinate_along_edge_leaving)
|
|
||||||
{
|
|
||||||
util::Log(logDEBUG) << "Zero length segment at " << coordinate_along_edge_leaving
|
|
||||||
<< " could cause invalid intersection exit bearing.";
|
|
||||||
BOOST_ASSERT(std::abs(bearing) <= 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
initial_roads_ordering.push_back(
|
|
||||||
{initial_bearing, {edge_connected_to_intersection, bearing, segment_length}});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!initial_roads_ordering.empty())
|
|
||||||
{
|
|
||||||
const auto base_initial_bearing = [&]() {
|
|
||||||
if (sorting_base)
|
|
||||||
{
|
|
||||||
const auto itr = std::find_if(initial_roads_ordering.begin(),
|
|
||||||
initial_roads_ordering.end(),
|
|
||||||
[&](const auto &data) {
|
|
||||||
return node_based_graph.GetTarget(
|
|
||||||
data.road.eid) == *sorting_base;
|
|
||||||
});
|
|
||||||
if (itr != initial_roads_ordering.end())
|
|
||||||
return util::bearing::reverse(itr->bearing);
|
|
||||||
}
|
|
||||||
return util::bearing::reverse(initial_roads_ordering.begin()->bearing);
|
|
||||||
}();
|
|
||||||
|
|
||||||
// sort roads with respect to the initial bearings, a tie-breaker for equal initial bearings
|
|
||||||
// is to order roads via final bearings to have roads in clockwise order
|
|
||||||
//
|
|
||||||
// rhs <---. lhs <----.
|
|
||||||
// / /
|
|
||||||
// lhs / rhs /
|
|
||||||
//
|
|
||||||
// lhs road is before rhs one rhs road is before lhs one
|
|
||||||
// bearing::angleBetween < 180 bearing::angleBetween > 180
|
|
||||||
const auto initial_bearing_order = makeCompareShapeDataAngleToBearing(base_initial_bearing);
|
|
||||||
std::sort(initial_roads_ordering.begin(),
|
|
||||||
initial_roads_ordering.end(),
|
|
||||||
[&initial_bearing_order](const auto &lhs, const auto &rhs) {
|
|
||||||
return initial_bearing_order(lhs, rhs) ||
|
|
||||||
(lhs.bearing == rhs.bearing &&
|
|
||||||
util::bearing::angleBetween(lhs.road.bearing, rhs.road.bearing) <
|
|
||||||
180);
|
|
||||||
});
|
|
||||||
|
|
||||||
// copy intersection data in the initial order
|
|
||||||
IntersectionShape intersection;
|
|
||||||
intersection.reserve(initial_roads_ordering.size());
|
|
||||||
std::transform(initial_roads_ordering.begin(),
|
|
||||||
initial_roads_ordering.end(),
|
|
||||||
std::back_inserter(intersection),
|
|
||||||
[](const auto &entry) { return entry.road; });
|
|
||||||
|
|
||||||
if (intersection.size() > 2)
|
|
||||||
{ // Check bearings ordering with respect to true bearings
|
|
||||||
const auto base_bearing = intersection.front().bearing;
|
|
||||||
const auto bearings_order =
|
|
||||||
makeCompareShapeDataAngleToBearing(util::bearing::reverse(base_bearing));
|
|
||||||
for (auto curr = intersection.begin(), next = std::next(curr);
|
|
||||||
next != intersection.end();
|
|
||||||
++curr, ++next)
|
|
||||||
{
|
|
||||||
if (bearings_order(*next, *curr))
|
|
||||||
{ // If the true bearing is out of the initial order (next before current) then
|
|
||||||
// adjust the next bearing to keep the order. The adjustment angle is at most
|
|
||||||
// 0.5° or a half-angle between the current bearing and the base bearing.
|
|
||||||
// to prevent overlapping over base bearing + 360°.
|
|
||||||
const auto angle_adjustment = std::min(
|
|
||||||
.5, util::restrictAngleToValidRange(base_bearing - curr->bearing) / 2.);
|
|
||||||
next->bearing =
|
|
||||||
util::restrictAngleToValidRange(curr->bearing + angle_adjustment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return intersection;
|
|
||||||
}
|
|
||||||
|
|
||||||
return IntersectionShape{};
|
|
||||||
}
|
|
||||||
|
|
||||||
// a
|
|
||||||
// |
|
|
||||||
// |
|
|
||||||
// v
|
|
||||||
// For an intersection from_node --via_eid--> turn_node ----> c
|
|
||||||
// ^
|
|
||||||
// |
|
|
||||||
// |
|
|
||||||
// b
|
|
||||||
// This functions returns _all_ turns as if the graph was undirected.
|
|
||||||
// That means we not only get (from_node, turn_node, c) in the above example
|
|
||||||
// but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are
|
|
||||||
// marked as invalid and only needed for intersection classification.
|
|
||||||
IntersectionView IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
|
|
||||||
const EdgeID via_eid,
|
|
||||||
const bool use_low_precision_angles) const
|
|
||||||
{
|
|
||||||
// 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));
|
|
||||||
|
|
||||||
auto intersection = ComputeIntersectionShape(
|
|
||||||
node_based_graph.GetTarget(via_eid), boost::none, use_low_precision_angles);
|
|
||||||
return TransformIntersectionShapeIntoView(from_node, via_eid, std::move(intersection));
|
|
||||||
}
|
|
||||||
|
|
||||||
IntersectionGenerationParameters
|
|
||||||
IntersectionGenerator::SkipDegreeTwoNodes(const NodeID starting_node, const EdgeID via_edge) const
|
|
||||||
{
|
|
||||||
NodeID query_node = starting_node;
|
|
||||||
EdgeID query_edge = via_edge;
|
|
||||||
|
|
||||||
const auto get_next_edge = [this](const NodeID from, const EdgeID via) {
|
|
||||||
const NodeID new_node = node_based_graph.GetTarget(via);
|
|
||||||
BOOST_ASSERT(node_based_graph.GetOutDegree(new_node) == 2);
|
|
||||||
const EdgeID begin_edges_new_node = node_based_graph.BeginEdges(new_node);
|
|
||||||
return (node_based_graph.GetTarget(begin_edges_new_node) == from) ? begin_edges_new_node + 1
|
|
||||||
: begin_edges_new_node;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_set<NodeID> visited_nodes;
|
|
||||||
// skip trivial nodes without generating the intersection in between, stop at the very first
|
|
||||||
// intersection of degree > 2
|
|
||||||
while (0 == visited_nodes.count(query_node) &&
|
|
||||||
2 == node_based_graph.GetOutDegree(node_based_graph.GetTarget(query_edge)))
|
|
||||||
{
|
|
||||||
visited_nodes.insert(query_node);
|
|
||||||
const auto next_node = node_based_graph.GetTarget(query_edge);
|
|
||||||
const auto next_edge = get_next_edge(query_node, query_edge);
|
|
||||||
|
|
||||||
query_node = next_node;
|
|
||||||
query_edge = next_edge;
|
|
||||||
|
|
||||||
// check if there is a relevant change in the graph
|
|
||||||
if (!CanBeCompressed(node_based_graph.GetEdgeData(query_edge),
|
|
||||||
node_based_graph.GetEdgeData(next_edge),
|
|
||||||
node_data_container) ||
|
|
||||||
(node_based_graph.GetTarget(next_edge) == starting_node))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {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 &normalized_intersection,
|
|
||||||
const IntersectionShape &intersection,
|
|
||||||
const std::vector<IntersectionNormalizationOperation> &performed_merges) const
|
|
||||||
{
|
|
||||||
const auto node_at_intersection = node_based_graph.GetTarget(entering_via_edge);
|
|
||||||
|
|
||||||
// request all turn restrictions
|
|
||||||
auto const restrictions = restriction_map.Restrictions(previous_node, node_at_intersection);
|
|
||||||
|
|
||||||
// check turn restrictions to find a node that is the only allowed target when coming from a
|
|
||||||
// node to an intersection
|
|
||||||
// d
|
|
||||||
// |
|
|
||||||
// a - b - c and `only_straight_on ab | bc would return `c` for `a,b`
|
|
||||||
const auto find_only_valid_turn = [&]() -> boost::optional<NodeID> {
|
|
||||||
const auto itr = std::find_if(restrictions.first, restrictions.second, [](auto pair) {
|
|
||||||
return pair.second->is_only;
|
|
||||||
});
|
|
||||||
if (itr != restrictions.second)
|
|
||||||
return itr->second->AsNodeRestriction().to;
|
|
||||||
else
|
|
||||||
return boost::none;
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto only_valid_turn = find_only_valid_turn();
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
return *only_valid_turn != destination;
|
|
||||||
|
|
||||||
// check if explicitly forbidden
|
|
||||||
return restrictions.second !=
|
|
||||||
std::find_if(restrictions.first, restrictions.second, [&](const auto &restriction) {
|
|
||||||
return restriction.second->AsNodeRestriction().to == 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
|
|
||||||
// uturn is a pair of {edge id, bearing}
|
|
||||||
const auto uturn = [&]() {
|
|
||||||
const auto merge_entry = std::find_if(
|
|
||||||
performed_merges.begin(), performed_merges.end(), [&uturn_edge_itr](const auto entry) {
|
|
||||||
return entry.merged_eid == uturn_edge_itr->eid;
|
|
||||||
});
|
|
||||||
if (merge_entry != performed_merges.end())
|
|
||||||
{
|
|
||||||
const auto merged_into_id = merge_entry->into_eid;
|
|
||||||
const auto merged_u_turn = std::find_if(
|
|
||||||
normalized_intersection.begin(),
|
|
||||||
normalized_intersection.end(),
|
|
||||||
[&](const IntersectionShapeData &road) { return road.eid == merged_into_id; });
|
|
||||||
BOOST_ASSERT(merged_u_turn != normalized_intersection.end());
|
|
||||||
return std::make_pair(merged_u_turn->eid,
|
|
||||||
util::bearing::reverse(merged_u_turn->bearing));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto uturn_edge_at_normalized_intersection_itr =
|
|
||||||
std::find_if(normalized_intersection.begin(),
|
|
||||||
normalized_intersection.end(),
|
|
||||||
connect_to_previous_node);
|
|
||||||
BOOST_ASSERT(uturn_edge_at_normalized_intersection_itr !=
|
|
||||||
normalized_intersection.end());
|
|
||||||
return std::make_pair(
|
|
||||||
uturn_edge_at_normalized_intersection_itr->eid,
|
|
||||||
util::bearing::reverse(uturn_edge_at_normalized_intersection_itr->bearing));
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
|
|
||||||
IntersectionView intersection_view;
|
|
||||||
intersection_view.reserve(normalized_intersection.size());
|
|
||||||
std::transform(normalized_intersection.begin(),
|
|
||||||
normalized_intersection.end(),
|
|
||||||
std::back_inserter(intersection_view),
|
|
||||||
[&](const IntersectionShapeData &road) {
|
|
||||||
return IntersectionViewData(
|
|
||||||
road,
|
|
||||||
is_allowed_turn(road),
|
|
||||||
util::bearing::angleBetween(uturn.second, 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));
|
|
||||||
|
|
||||||
// Move entering_via_edge to intersection front and place all roads prior entering_via_edge
|
|
||||||
// at the end of the intersection view with 360° angle
|
|
||||||
auto entering_via_it = std::find_if(intersection_view.begin(),
|
|
||||||
intersection_view.end(),
|
|
||||||
[&uturn](auto &road) { return road.eid == uturn.first; });
|
|
||||||
|
|
||||||
OSRM_ASSERT(entering_via_it != intersection_view.end() && entering_via_it->angle >= 0. &&
|
|
||||||
entering_via_it->angle < std::numeric_limits<double>::epsilon(),
|
|
||||||
coordinates[node_at_intersection]);
|
|
||||||
|
|
||||||
if (entering_via_it != intersection_view.begin() && entering_via_it != intersection_view.end())
|
|
||||||
{
|
|
||||||
std::for_each(
|
|
||||||
intersection_view.begin(), entering_via_it, [](auto &road) { road.angle = 360.; });
|
|
||||||
std::rotate(intersection_view.begin(), entering_via_it, intersection_view.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
return intersection_view;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CoordinateExtractor &IntersectionGenerator::GetCoordinateExtractor() const
|
|
||||||
{
|
|
||||||
return coordinate_extractor;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace guidance
|
|
||||||
} // namespace extractor
|
|
||||||
} // namespace osrm
|
|
@ -1,5 +1,6 @@
|
|||||||
#include "extractor/guidance/intersection_handler.hpp"
|
#include "extractor/guidance/intersection_handler.hpp"
|
||||||
#include "extractor/guidance/constants.hpp"
|
#include "extractor/guidance/constants.hpp"
|
||||||
|
#include "extractor/intersection/intersection_analysis.hpp"
|
||||||
|
|
||||||
#include "util/coordinate_calculation.hpp"
|
#include "util/coordinate_calculation.hpp"
|
||||||
#include "util/guidance/name_announcements.hpp"
|
#include "util/guidance/name_announcements.hpp"
|
||||||
@ -45,17 +46,27 @@ inline bool requiresAnnouncement(const util::NodeBasedDynamicGraph &node_based_g
|
|||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
IntersectionHandler::IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
IntersectionHandler::IntersectionHandler(
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const util::NameTable &name_table,
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
const SuffixTable &street_name_suffix_table,
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
const IntersectionGenerator &intersection_generator)
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
|
const util::NameTable &name_table,
|
||||||
|
const SuffixTable &street_name_suffix_table)
|
||||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||||
coordinates(coordinates), name_table(name_table),
|
node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
|
||||||
street_name_suffix_table(street_name_suffix_table),
|
node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
|
||||||
intersection_generator(intersection_generator),
|
turn_lanes_data(turn_lanes_data), name_table(name_table),
|
||||||
graph_walker(node_based_graph, node_data_container, intersection_generator)
|
street_name_suffix_table(street_name_suffix_table), graph_walker(node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,8 +173,8 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t
|
|||||||
// or actually follow the full road. When 2399 lands, we can exchange here for a
|
// or actually follow the full road. When 2399 lands, we can exchange here for a
|
||||||
// precalculated distance value.
|
// precalculated distance value.
|
||||||
const auto distance = util::coordinate_calculation::haversineDistance(
|
const auto distance = util::coordinate_calculation::haversineDistance(
|
||||||
coordinates[node_based_graph.GetTarget(via_edge)],
|
node_coordinates[node_based_graph.GetTarget(via_edge)],
|
||||||
coordinates[node_based_graph.GetTarget(road.eid)]);
|
node_coordinates[node_based_graph.GetTarget(road.eid)]);
|
||||||
|
|
||||||
return {TurnType::Turn,
|
return {TurnType::Turn,
|
||||||
(angularDeviation(road.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE &&
|
(angularDeviation(road.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE &&
|
||||||
@ -462,17 +473,24 @@ IntersectionHandler::getNextIntersection(const NodeID at, const EdgeID via) cons
|
|||||||
// Starting at node `a` via edge `e0` the intersection generator returns the intersection at `c`
|
// Starting at node `a` via edge `e0` the intersection generator returns the intersection at `c`
|
||||||
// writing `tl` (traffic signal) node and the edge `e1` which has the intersection as target.
|
// writing `tl` (traffic signal) node and the edge `e1` which has the intersection as target.
|
||||||
|
|
||||||
const auto intersection_parameters = intersection_generator.SkipDegreeTwoNodes(at, via);
|
const auto intersection_parameters =
|
||||||
|
intersection::skipDegreeTwoNodes(node_based_graph, {at, via});
|
||||||
// This should never happen, guard against nevertheless
|
// This should never happen, guard against nevertheless
|
||||||
if (intersection_parameters.nid == SPECIAL_NODEID ||
|
if (intersection_parameters.node == SPECIAL_NODEID ||
|
||||||
intersection_parameters.via_eid == SPECIAL_EDGEID)
|
intersection_parameters.edge == SPECIAL_EDGEID)
|
||||||
{
|
{
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto intersection =
|
auto intersection = intersection::getConnectedRoads<false>(node_based_graph,
|
||||||
intersection_generator(intersection_parameters.nid, intersection_parameters.via_eid);
|
node_data_container,
|
||||||
auto intersection_node = node_based_graph.GetTarget(intersection_parameters.via_eid);
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
|
intersection_parameters);
|
||||||
|
auto intersection_node = node_based_graph.GetTarget(intersection_parameters.edge);
|
||||||
|
|
||||||
if (intersection.size() <= 2 || intersection.isTrafficSignalOrBarrier())
|
if (intersection.size() <= 2 || intersection.isTrafficSignalOrBarrier())
|
||||||
{
|
{
|
||||||
|
@ -1,430 +0,0 @@
|
|||||||
#include "extractor/guidance/intersection_normalizer.hpp"
|
|
||||||
#include "util/bearing.hpp"
|
|
||||||
#include "util/coordinate_calculation.hpp"
|
|
||||||
|
|
||||||
#include <tuple>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
using osrm::util::angularDeviation;
|
|
||||||
|
|
||||||
namespace osrm
|
|
||||||
{
|
|
||||||
namespace extractor
|
|
||||||
{
|
|
||||||
namespace guidance
|
|
||||||
{
|
|
||||||
|
|
||||||
IntersectionNormalizer::IntersectionNormalizer(
|
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
|
||||||
const util::NameTable &name_table,
|
|
||||||
const SuffixTable &street_name_suffix_table,
|
|
||||||
const IntersectionGenerator &intersection_generator)
|
|
||||||
: node_based_graph(node_based_graph), intersection_generator(intersection_generator),
|
|
||||||
mergable_road_detector(node_based_graph,
|
|
||||||
node_data_container,
|
|
||||||
coordinates,
|
|
||||||
intersection_generator,
|
|
||||||
intersection_generator.GetCoordinateExtractor(),
|
|
||||||
name_table,
|
|
||||||
street_name_suffix_table)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
IntersectionNormalizer::NormalizationResult IntersectionNormalizer::
|
|
||||||
operator()(const NodeID node_at_intersection, IntersectionShape intersection) const
|
|
||||||
{
|
|
||||||
const auto intersection_copy = intersection;
|
|
||||||
auto merged_shape_and_merges =
|
|
||||||
MergeSegregatedRoads(node_at_intersection, std::move(intersection));
|
|
||||||
merged_shape_and_merges.normalized_shape = AdjustBearingsForMergeAtDestination(
|
|
||||||
node_at_intersection, std::move(merged_shape_and_merges.normalized_shape));
|
|
||||||
return merged_shape_and_merges;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IntersectionNormalizer::CanMerge(const NodeID intersection_node,
|
|
||||||
const IntersectionShape &intersection,
|
|
||||||
std::size_t fist_index_in_ccw,
|
|
||||||
std::size_t second_index_in_ccw) const
|
|
||||||
{
|
|
||||||
BOOST_ASSERT(((fist_index_in_ccw + 1) % intersection.size()) == second_index_in_ccw);
|
|
||||||
|
|
||||||
// don't merge on degree two, since it's most likely a bollard/traffic light or a round way
|
|
||||||
if (intersection.size() <= 2)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const auto can_merge = mergable_road_detector.CanMergeRoad(
|
|
||||||
intersection_node, intersection[fist_index_in_ccw], intersection[second_index_in_ccw]);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Merging should never depend on order/never merge more than two roads. To ensure that we don't
|
|
||||||
* merge anything that is impacted by neighboring roads (e.g. three roads of the same name as in
|
|
||||||
* parking lots/border checkpoints), we check if the neigboring roads would be merged as well.
|
|
||||||
* In that case, we cannot merge, since we would end up merging multiple items together
|
|
||||||
*/
|
|
||||||
const auto is_distinct = [&]() {
|
|
||||||
const auto next_index_in_ccw = (second_index_in_ccw + 1) % intersection.size();
|
|
||||||
const auto distinct_to_next_in_ccw = mergable_road_detector.IsDistinctFrom(
|
|
||||||
intersection[second_index_in_ccw], intersection[next_index_in_ccw]);
|
|
||||||
const auto prev_index_in_ccw =
|
|
||||||
(fist_index_in_ccw + intersection.size() - 1) % intersection.size();
|
|
||||||
const auto distinct_to_prev_in_ccw = mergable_road_detector.IsDistinctFrom(
|
|
||||||
intersection[prev_index_in_ccw], intersection[fist_index_in_ccw]);
|
|
||||||
return distinct_to_next_in_ccw && distinct_to_prev_in_ccw;
|
|
||||||
};
|
|
||||||
|
|
||||||
// use lazy evaluation to check only if mergable
|
|
||||||
return can_merge && is_distinct();
|
|
||||||
}
|
|
||||||
|
|
||||||
IntersectionNormalizationOperation
|
|
||||||
IntersectionNormalizer::DetermineMergeDirection(const IntersectionShapeData &lhs,
|
|
||||||
const IntersectionShapeData &rhs) const
|
|
||||||
{
|
|
||||||
if (node_based_graph.GetEdgeData(lhs.eid).reversed)
|
|
||||||
return {lhs.eid, rhs.eid};
|
|
||||||
else
|
|
||||||
return {rhs.eid, lhs.eid};
|
|
||||||
}
|
|
||||||
|
|
||||||
IntersectionShapeData IntersectionNormalizer::MergeRoads(const IntersectionShapeData &into,
|
|
||||||
const IntersectionShapeData &from) const
|
|
||||||
{
|
|
||||||
// we only merge small angles. If the difference between both is large, we are looking at a
|
|
||||||
// bearing leading north. Such a bearing cannot be handled via the basic average. In this
|
|
||||||
// case we actually need to shift the bearing by half the difference.
|
|
||||||
const auto aroundZero = [](const double first, const double second) {
|
|
||||||
return (std::max(first, second) - std::min(first, second)) >= 180;
|
|
||||||
};
|
|
||||||
|
|
||||||
// find the angle between two other angles
|
|
||||||
const auto combineAngles = [aroundZero](const double first, const double second) {
|
|
||||||
if (!aroundZero(first, second))
|
|
||||||
return .5 * (first + second);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto offset = angularDeviation(first, second);
|
|
||||||
auto new_angle = std::max(first, second) + .5 * offset;
|
|
||||||
if (new_angle >= 360)
|
|
||||||
return new_angle - 360;
|
|
||||||
return new_angle;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
auto result = into;
|
|
||||||
BOOST_ASSERT(!node_based_graph.GetEdgeData(into.eid).reversed);
|
|
||||||
result.bearing = combineAngles(into.bearing, from.bearing);
|
|
||||||
BOOST_ASSERT(0 <= result.bearing && result.bearing < 360.0);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
IntersectionShapeData
|
|
||||||
IntersectionNormalizer::MergeRoads(const IntersectionNormalizationOperation direction,
|
|
||||||
const IntersectionShapeData &lhs,
|
|
||||||
const IntersectionShapeData &rhs,
|
|
||||||
const double opposite_bearing) const
|
|
||||||
{
|
|
||||||
// In some intersections, turning roads can introduce artificial turns if we merge here.
|
|
||||||
// Consider a scenario like:
|
|
||||||
//
|
|
||||||
// a . g - f
|
|
||||||
// | .
|
|
||||||
// | .
|
|
||||||
// |.
|
|
||||||
// d-b--------e
|
|
||||||
// |
|
|
||||||
// c
|
|
||||||
//
|
|
||||||
// Merging `bgf` and `be` would introduce an angle, even though d-b-e is perfectly straight
|
|
||||||
// We don't change the angle, if such an opposite road exists
|
|
||||||
if (direction.merged_eid == lhs.eid)
|
|
||||||
{
|
|
||||||
// change the angle only if the opposite direction is not nearly straight
|
|
||||||
if (angularDeviation(opposite_bearing, rhs.bearing) >
|
|
||||||
(STRAIGHT_ANGLE - MAXIMAL_ALLOWED_NO_TURN_DEVIATION))
|
|
||||||
return rhs;
|
|
||||||
else
|
|
||||||
return MergeRoads(rhs, lhs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (angularDeviation(opposite_bearing, lhs.bearing) >
|
|
||||||
(STRAIGHT_ANGLE - MAXIMAL_ALLOWED_NO_TURN_DEVIATION))
|
|
||||||
return lhs;
|
|
||||||
else
|
|
||||||
return MergeRoads(lhs, rhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Segregated Roads often merge onto a single intersection.
|
|
||||||
* While technically representing different roads, they are
|
|
||||||
* often looked at as a single road.
|
|
||||||
* Due to the merging, turn Angles seem off, wenn we compute them from the
|
|
||||||
* initial positions.
|
|
||||||
*
|
|
||||||
* b<b<b<b(1)<b<b<b
|
|
||||||
* aaaaa-b
|
|
||||||
* b>b>b>b(2)>b>b>b
|
|
||||||
*
|
|
||||||
* Would be seen as a slight turn going fro a to (2). A Sharp turn going from
|
|
||||||
* (1) to (2).
|
|
||||||
*
|
|
||||||
* In cases like these, we megre this segregated roads into a single road to
|
|
||||||
* end up with a case like:
|
|
||||||
*
|
|
||||||
* aaaaa-bbbbbb
|
|
||||||
*
|
|
||||||
* for the turn representation.
|
|
||||||
* Anything containing the first u-turn in a merge affects all other angles
|
|
||||||
* and is handled separately from all others.
|
|
||||||
*/
|
|
||||||
IntersectionNormalizer::NormalizationResult
|
|
||||||
IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
|
|
||||||
IntersectionShape intersection) const
|
|
||||||
{
|
|
||||||
const auto getRight = [&](std::size_t index) {
|
|
||||||
return (index + intersection.size() - 1) % intersection.size();
|
|
||||||
};
|
|
||||||
|
|
||||||
// This map stores for all edges that participated in a merging operation in which edge id they
|
|
||||||
// end up in the end. We only store what we have merged into other edges.
|
|
||||||
std::vector<IntersectionNormalizationOperation> merging_map;
|
|
||||||
const auto merge = [this, &merging_map](const IntersectionShapeData &first,
|
|
||||||
const IntersectionShapeData &second,
|
|
||||||
const double opposite_bearing) {
|
|
||||||
|
|
||||||
const auto direction = DetermineMergeDirection(first, second);
|
|
||||||
BOOST_ASSERT(
|
|
||||||
std::find_if(merging_map.begin(), merging_map.end(), [direction](const auto pair) {
|
|
||||||
return pair.merged_eid == direction.merged_eid;
|
|
||||||
}) == merging_map.end());
|
|
||||||
merging_map.push_back(direction);
|
|
||||||
return MergeRoads(direction, first, second, opposite_bearing);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (intersection.size() <= 1)
|
|
||||||
return {intersection, merging_map};
|
|
||||||
|
|
||||||
const auto intersection_copy = intersection;
|
|
||||||
const auto opposite_bearing = [this, intersection_copy](const IntersectionShapeData &lhs,
|
|
||||||
const IntersectionShapeData &rhs) {
|
|
||||||
if (node_based_graph.GetEdgeData(lhs.eid).reversed)
|
|
||||||
{
|
|
||||||
return intersection_copy.FindClosestBearing(util::bearing::reverse(rhs.bearing))
|
|
||||||
->bearing;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BOOST_ASSERT(node_based_graph.GetEdgeData(rhs.eid).reversed);
|
|
||||||
return intersection_copy.FindClosestBearing(util::bearing::reverse(lhs.bearing))
|
|
||||||
->bearing;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// check for merges including the basic u-turn
|
|
||||||
// these result in an adjustment of all other angles. This is due to how these angles are
|
|
||||||
// perceived. Considering the following example:
|
|
||||||
//
|
|
||||||
// c b
|
|
||||||
// Y
|
|
||||||
// a
|
|
||||||
//
|
|
||||||
// coming from a to b (given a road that splits at the fork into two one-ways), the turn is not
|
|
||||||
// considered as a turn but rather as going straight.
|
|
||||||
// Now if we look at the situation merging:
|
|
||||||
//
|
|
||||||
// a b
|
|
||||||
// \ /
|
|
||||||
// e - + - d
|
|
||||||
// |
|
|
||||||
// c
|
|
||||||
//
|
|
||||||
// With a,b representing the same road, the intersection itself represents a classif for way
|
|
||||||
// intersection so we handle it like
|
|
||||||
//
|
|
||||||
// (a),b
|
|
||||||
// |
|
|
||||||
// e - + - d
|
|
||||||
// |
|
|
||||||
// c
|
|
||||||
//
|
|
||||||
// To be able to consider this adjusted representation down the line, we merge some roads.
|
|
||||||
// If the merge occurs at the u-turn edge, we need to adjust all angles, though, since they are
|
|
||||||
// with respect to the now changed perceived location of a. If we move (a) to the left, we add
|
|
||||||
// the difference to all angles. Otherwise we subtract it.
|
|
||||||
// these result in an adjustment of all other angles
|
|
||||||
if (CanMerge(intersection_node, intersection, intersection.size() - 1, 0))
|
|
||||||
{
|
|
||||||
// moving `a` to the left
|
|
||||||
const auto opposite = opposite_bearing(intersection.front(), intersection.back());
|
|
||||||
intersection[0] = merge(intersection.front(), intersection.back(), opposite);
|
|
||||||
// FIXME if we have a left-sided country, we need to switch this off and enable it
|
|
||||||
// below
|
|
||||||
intersection.pop_back();
|
|
||||||
}
|
|
||||||
else if (CanMerge(intersection_node, intersection, 0, 1))
|
|
||||||
{
|
|
||||||
const auto opposite = opposite_bearing(intersection.front(), intersection[1]);
|
|
||||||
intersection[0] = merge(intersection.front(), intersection[1], opposite);
|
|
||||||
intersection.erase(intersection.begin() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// a merge including the first u-turn requires an adjustment of the turn angles
|
|
||||||
// therefore these are handled prior to this step
|
|
||||||
for (std::size_t index = 2; index < intersection.size(); ++index)
|
|
||||||
{
|
|
||||||
if (CanMerge(intersection_node, intersection, getRight(index), index))
|
|
||||||
{
|
|
||||||
const auto opposite =
|
|
||||||
opposite_bearing(intersection[getRight(index)], intersection[index]);
|
|
||||||
intersection[getRight(index)] =
|
|
||||||
merge(intersection[getRight(index)], intersection[index], opposite);
|
|
||||||
intersection.erase(intersection.begin() + index);
|
|
||||||
--index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {intersection, merging_map};
|
|
||||||
}
|
|
||||||
|
|
||||||
// OSM can have some very steep angles for joining roads. Considering the following intersection:
|
|
||||||
// x
|
|
||||||
// |
|
|
||||||
// v __________c
|
|
||||||
// /
|
|
||||||
// a ---d
|
|
||||||
// \ __________b
|
|
||||||
//
|
|
||||||
// with c->d as a oneway
|
|
||||||
// and d->b as a oneway, the turn von x->d is actually a turn from x->a. So when looking at the
|
|
||||||
// intersection coming from x, we want to interpret the situation as
|
|
||||||
// x
|
|
||||||
// |
|
|
||||||
// a __ d __ v__________c
|
|
||||||
// |
|
|
||||||
// |_______________b
|
|
||||||
//
|
|
||||||
// Where we see the turn to `d` as a right turn, rather than going straight.
|
|
||||||
// We do this by adjusting the local turn angle at `x` to turn onto `d` to be reflective of this
|
|
||||||
// situation, where `v` would be the node at the intersection.
|
|
||||||
IntersectionShape
|
|
||||||
IntersectionNormalizer::AdjustBearingsForMergeAtDestination(const NodeID node_at_intersection,
|
|
||||||
IntersectionShape intersection) const
|
|
||||||
{
|
|
||||||
// nothing to do for dead ends
|
|
||||||
if (intersection.size() <= 1)
|
|
||||||
return intersection;
|
|
||||||
|
|
||||||
// we don't adjust any road that is longer than 30 meters (between centers of intersections),
|
|
||||||
// since the road is probably too long otherwise to impact perception.
|
|
||||||
const double constexpr PRUNING_DISTANCE = 30;
|
|
||||||
// never adjust u-turns
|
|
||||||
for (std::size_t index = 0; index < intersection.size(); ++index)
|
|
||||||
{
|
|
||||||
auto &road = intersection[index];
|
|
||||||
// only consider roads that are close
|
|
||||||
if (road.segment_length > PRUNING_DISTANCE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// to find out about the above situation, we need to look at the next intersection (at d in
|
|
||||||
// the example). If the initial road can be merged to the left/right, we are about to adjust
|
|
||||||
// the angle.
|
|
||||||
const auto next_intersection_along_road = intersection_generator.ComputeIntersectionShape(
|
|
||||||
node_based_graph.GetTarget(road.eid), node_at_intersection);
|
|
||||||
|
|
||||||
if (next_intersection_along_road.size() <= 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const auto node_at_next_intersection = node_based_graph.GetTarget(road.eid);
|
|
||||||
|
|
||||||
const auto adjustAngle = [](double angle, double offset) {
|
|
||||||
angle += offset;
|
|
||||||
if (angle > 360)
|
|
||||||
return angle - 360.;
|
|
||||||
else if (angle < 0)
|
|
||||||
return angle + 360.;
|
|
||||||
return angle;
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto range = node_based_graph.GetAdjacentEdgeRange(node_at_next_intersection);
|
|
||||||
if (range.size() <= 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// the order does not matter
|
|
||||||
const auto get_offset = [](const IntersectionShapeData &lhs,
|
|
||||||
const IntersectionShapeData &rhs) {
|
|
||||||
return 0.5 * angularDeviation(lhs.bearing, rhs.bearing);
|
|
||||||
};
|
|
||||||
|
|
||||||
// When offsetting angles in our turns, we don't want to get past the next turn. This
|
|
||||||
// function simply limits an offset to be at most half the distance to the next turn in the
|
|
||||||
// offfset direction
|
|
||||||
const auto get_corrected_offset = [](
|
|
||||||
const double offset,
|
|
||||||
const IntersectionShapeData &road,
|
|
||||||
const IntersectionShapeData &next_road_in_offset_direction) {
|
|
||||||
const auto offset_limit =
|
|
||||||
angularDeviation(road.bearing, next_road_in_offset_direction.bearing);
|
|
||||||
// limit the offset with an additional buffer
|
|
||||||
return (offset + MAXIMAL_ALLOWED_NO_TURN_DEVIATION > offset_limit) ? 0.5 * offset_limit
|
|
||||||
: offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
// only if straighmost angles get smaller, we consider it an improvement
|
|
||||||
auto const improves_straightmost = [&](auto const index, auto const offset) {
|
|
||||||
const auto itr = next_intersection_along_road.FindClosestBearing(
|
|
||||||
util::bearing::reverse(next_intersection_along_road[index].bearing));
|
|
||||||
const auto angle = util::bearing::angleBetween(
|
|
||||||
util::bearing::reverse(itr->bearing), next_intersection_along_road[index].bearing);
|
|
||||||
|
|
||||||
return util::angularDeviation(angle, STRAIGHT_ANGLE) >
|
|
||||||
util::angularDeviation(angle + offset, STRAIGHT_ANGLE);
|
|
||||||
};
|
|
||||||
|
|
||||||
// check if the u-turn edge at the next intersection could be merged to the left/right. If
|
|
||||||
// this is the case and the road is not far away (see previous distance check), if
|
|
||||||
// influences the perceived angle.
|
|
||||||
if (CanMerge(node_at_next_intersection, next_intersection_along_road, 0, 1))
|
|
||||||
{
|
|
||||||
const auto offset =
|
|
||||||
get_offset(next_intersection_along_road[0], next_intersection_along_road[1]);
|
|
||||||
|
|
||||||
if (improves_straightmost(0, -offset) && improves_straightmost(1, offset))
|
|
||||||
{
|
|
||||||
const auto corrected_offset = get_corrected_offset(
|
|
||||||
offset,
|
|
||||||
road,
|
|
||||||
intersection[(intersection.size() + index - 1) % intersection.size()]);
|
|
||||||
// at the target intersection, we merge to the right, so we need to shift the
|
|
||||||
// current
|
|
||||||
// angle to the left
|
|
||||||
road.bearing = adjustAngle(road.bearing, corrected_offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (CanMerge(node_at_next_intersection,
|
|
||||||
next_intersection_along_road,
|
|
||||||
next_intersection_along_road.size() - 1,
|
|
||||||
0))
|
|
||||||
{
|
|
||||||
const auto offset =
|
|
||||||
get_offset(next_intersection_along_road[0],
|
|
||||||
next_intersection_along_road[next_intersection_along_road.size() - 1]);
|
|
||||||
|
|
||||||
if (improves_straightmost(0, offset) &&
|
|
||||||
improves_straightmost(next_intersection_along_road.size() - 1, -offset))
|
|
||||||
{
|
|
||||||
const auto corrected_offset = get_corrected_offset(
|
|
||||||
offset, road, intersection[(index + 1) % intersection.size()]);
|
|
||||||
// at the target intersection, we merge to the left, so we need to shift the current
|
|
||||||
// angle to the right
|
|
||||||
road.bearing = adjustAngle(road.bearing, -corrected_offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return intersection;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace guidance
|
|
||||||
} // namespace extractor
|
|
||||||
} // namespace osrm
|
|
@ -1,8 +1,7 @@
|
|||||||
#include "extractor/guidance/mergable_road_detector.hpp"
|
#include "extractor/guidance/mergable_road_detector.hpp"
|
||||||
#include "extractor/guidance/constants.hpp"
|
#include "extractor/guidance/constants.hpp"
|
||||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
|
||||||
#include "extractor/guidance/intersection_generator.hpp"
|
|
||||||
#include "extractor/guidance/node_based_graph_walker.hpp"
|
#include "extractor/guidance/node_based_graph_walker.hpp"
|
||||||
|
#include "extractor/intersection/intersection_analysis.hpp"
|
||||||
#include "extractor/query_node.hpp"
|
#include "extractor/query_node.hpp"
|
||||||
#include "extractor/suffix_table.hpp"
|
#include "extractor/suffix_table.hpp"
|
||||||
|
|
||||||
@ -52,17 +51,22 @@ inline auto makeCheckRoadForName(const NameID name_id,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MergableRoadDetector::MergableRoadDetector(const util::NodeBasedDynamicGraph &node_based_graph,
|
MergableRoadDetector::MergableRoadDetector(
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const std::vector<util::Coordinate> &node_coordinates,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const IntersectionGenerator &intersection_generator,
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
const CoordinateExtractor &coordinate_extractor,
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
const util::NameTable &name_table,
|
const RestrictionMap &node_restriction_map,
|
||||||
const SuffixTable &street_name_suffix_table)
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
|
const util::NameTable &name_table,
|
||||||
|
const SuffixTable &street_name_suffix_table)
|
||||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||||
node_coordinates(node_coordinates), intersection_generator(intersection_generator),
|
node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
|
||||||
coordinate_extractor(coordinate_extractor), name_table(name_table),
|
node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
|
||||||
street_name_suffix_table(street_name_suffix_table)
|
turn_lanes_data(turn_lanes_data), name_table(name_table),
|
||||||
|
street_name_suffix_table(street_name_suffix_table),
|
||||||
|
coordinate_extractor(node_based_graph, compressed_geometries, node_coordinates)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,8 +174,9 @@ bool MergableRoadDetector::EdgeDataSupportsMerge(
|
|||||||
bool MergableRoadDetector::IsTrafficLoop(const NodeID intersection_node,
|
bool MergableRoadDetector::IsTrafficLoop(const NodeID intersection_node,
|
||||||
const MergableRoadData &road) const
|
const MergableRoadData &road) const
|
||||||
{
|
{
|
||||||
const auto connection = intersection_generator.SkipDegreeTwoNodes(intersection_node, road.eid);
|
const auto connection =
|
||||||
return intersection_node == node_based_graph.GetTarget(connection.via_eid);
|
intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, road.eid});
|
||||||
|
return intersection_node == node_based_graph.GetTarget(connection.edge);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
||||||
@ -180,8 +185,22 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
|||||||
{
|
{
|
||||||
// selection data to the right and left
|
// selection data to the right and left
|
||||||
const auto constexpr SMALL_RANDOM_HOPLIMIT = 5;
|
const auto constexpr SMALL_RANDOM_HOPLIMIT = 5;
|
||||||
IntersectionFinderAccumulator left_accumulator(SMALL_RANDOM_HOPLIMIT, intersection_generator),
|
IntersectionFinderAccumulator left_accumulator(SMALL_RANDOM_HOPLIMIT,
|
||||||
right_accumulator(SMALL_RANDOM_HOPLIMIT, intersection_generator);
|
node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data),
|
||||||
|
right_accumulator(SMALL_RANDOM_HOPLIMIT,
|
||||||
|
node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data);
|
||||||
|
|
||||||
/* Standard following the straightmost road
|
/* Standard following the straightmost road
|
||||||
* Since both items have the same id, we can `select` based on any setup
|
* Since both items have the same id, we can `select` based on any setup
|
||||||
@ -193,8 +212,13 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
|||||||
/*requires entry=*/false,
|
/*requires entry=*/false,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
NodeBasedGraphWalker graph_walker(
|
NodeBasedGraphWalker graph_walker(node_based_graph,
|
||||||
node_based_graph, node_data_container, intersection_generator);
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data);
|
||||||
graph_walker.TraverseRoad(intersection_node, lhs.eid, left_accumulator, selector);
|
graph_walker.TraverseRoad(intersection_node, lhs.eid, left_accumulator, selector);
|
||||||
/* if the intersection does not have a right turn, we continue onto the next one once
|
/* if the intersection does not have a right turn, we continue onto the next one once
|
||||||
* (skipping over a single small side street)
|
* (skipping over a single small side street)
|
||||||
@ -266,7 +290,13 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
|||||||
|
|
||||||
// check if both intersections are connected
|
// check if both intersections are connected
|
||||||
IntersectionFinderAccumulator connect_accumulator(SMALL_RANDOM_HOPLIMIT,
|
IntersectionFinderAccumulator connect_accumulator(SMALL_RANDOM_HOPLIMIT,
|
||||||
intersection_generator);
|
node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data);
|
||||||
graph_walker.TraverseRoad(node_based_graph.GetTarget(left_accumulator.via_edge_id),
|
graph_walker.TraverseRoad(node_based_graph.GetTarget(left_accumulator.via_edge_id),
|
||||||
connector_turn->eid,
|
connector_turn->eid,
|
||||||
connect_accumulator,
|
connect_accumulator,
|
||||||
@ -281,8 +311,13 @@ bool MergableRoadDetector::IsCircularShape(const NodeID intersection_node,
|
|||||||
const MergableRoadData &lhs,
|
const MergableRoadData &lhs,
|
||||||
const MergableRoadData &rhs) const
|
const MergableRoadData &rhs) const
|
||||||
{
|
{
|
||||||
NodeBasedGraphWalker graph_walker(
|
NodeBasedGraphWalker graph_walker(node_based_graph,
|
||||||
node_based_graph, node_data_container, intersection_generator);
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data);
|
||||||
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
|
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
|
||||||
LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length);
|
LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length);
|
||||||
SelectStraightmostRoadByNameAndOnlyChoice selector(
|
SelectStraightmostRoadByNameAndOnlyChoice selector(
|
||||||
@ -348,8 +383,13 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Find a coordinate following a road that is far away
|
// Find a coordinate following a road that is far away
|
||||||
NodeBasedGraphWalker graph_walker(
|
NodeBasedGraphWalker graph_walker(node_based_graph,
|
||||||
node_based_graph, node_data_container, intersection_generator);
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data);
|
||||||
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
|
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
|
||||||
LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length);
|
LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length);
|
||||||
SelectStraightmostRoadByNameAndOnlyChoice selector(
|
SelectStraightmostRoadByNameAndOnlyChoice selector(
|
||||||
@ -419,10 +459,16 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// compare reference distance:
|
// compare reference distance:
|
||||||
const auto distance_between_roads = util::coordinate_calculation::findClosestDistance(
|
const auto distance_mid_left_to_right = util::coordinate_calculation::findClosestDistance(
|
||||||
coordinates_to_the_left[coordinates_to_the_left.size() / 2],
|
coordinates_to_the_left[coordinates_to_the_left.size() / 2],
|
||||||
coordinates_to_the_right.begin(),
|
coordinates_to_the_right.begin(),
|
||||||
coordinates_to_the_right.end());
|
coordinates_to_the_right.end());
|
||||||
|
const auto distance_mid_right_to_left = util::coordinate_calculation::findClosestDistance(
|
||||||
|
coordinates_to_the_right[coordinates_to_the_right.size() / 2],
|
||||||
|
coordinates_to_the_left.begin(),
|
||||||
|
coordinates_to_the_left.end());
|
||||||
|
const auto distance_between_roads =
|
||||||
|
std::min(distance_mid_left_to_right, distance_mid_right_to_left);
|
||||||
|
|
||||||
const auto lane_count_lhs = std::max<int>(
|
const auto lane_count_lhs = std::max<int>(
|
||||||
1, node_based_graph.GetEdgeData(lhs.eid).flags.road_classification.GetNumberOfLanes());
|
1, node_based_graph.GetEdgeData(lhs.eid).flags.road_classification.GetNumberOfLanes());
|
||||||
@ -443,12 +489,12 @@ bool MergableRoadDetector::IsTrafficIsland(const NodeID intersection_node,
|
|||||||
* location with the same name repeatet at least three times
|
* location with the same name repeatet at least three times
|
||||||
*/
|
*/
|
||||||
const auto left_connection =
|
const auto left_connection =
|
||||||
intersection_generator.SkipDegreeTwoNodes(intersection_node, lhs.eid);
|
intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, lhs.eid});
|
||||||
const auto right_connection =
|
const auto right_connection =
|
||||||
intersection_generator.SkipDegreeTwoNodes(intersection_node, rhs.eid);
|
intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, rhs.eid});
|
||||||
|
|
||||||
const auto left_candidate = node_based_graph.GetTarget(left_connection.via_eid);
|
const auto left_candidate = node_based_graph.GetTarget(left_connection.edge);
|
||||||
const auto right_candidate = node_based_graph.GetTarget(right_connection.via_eid);
|
const auto right_candidate = node_based_graph.GetTarget(right_connection.edge);
|
||||||
|
|
||||||
const auto candidate_is_valid =
|
const auto candidate_is_valid =
|
||||||
left_candidate == right_candidate && left_candidate != intersection_node;
|
left_candidate == right_candidate && left_candidate != intersection_node;
|
||||||
@ -516,9 +562,16 @@ bool MergableRoadDetector::IsLinkRoad(const NodeID intersection_node,
|
|||||||
const MergableRoadData &road) const
|
const MergableRoadData &road) const
|
||||||
{
|
{
|
||||||
const auto next_intersection_parameters =
|
const auto next_intersection_parameters =
|
||||||
intersection_generator.SkipDegreeTwoNodes(intersection_node, road.eid);
|
intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, road.eid});
|
||||||
const auto next_intersection_along_road = intersection_generator.GetConnectedRoads(
|
const auto next_intersection_along_road =
|
||||||
next_intersection_parameters.nid, next_intersection_parameters.via_eid);
|
intersection::getConnectedRoads<false>(node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
|
next_intersection_parameters);
|
||||||
const auto extract_name_id = [this](const MergableRoadData &road) {
|
const auto extract_name_id = [this](const MergableRoadData &road) {
|
||||||
return node_data_container
|
return node_data_container
|
||||||
.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
||||||
@ -543,7 +596,7 @@ bool MergableRoadDetector::IsLinkRoad(const NodeID intersection_node,
|
|||||||
|
|
||||||
// we cannot be looking at the same road we came from
|
// we cannot be looking at the same road we came from
|
||||||
if (node_based_graph.GetTarget(opposite_of_next_road_along_path->eid) ==
|
if (node_based_graph.GetTarget(opposite_of_next_road_along_path->eid) ==
|
||||||
next_intersection_parameters.nid)
|
next_intersection_parameters.node)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* check if the opposite of the next road decision was sane. It could have been just as well our
|
/* check if the opposite of the next road decision was sane. It could have been just as well our
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "extractor/guidance/constants.hpp"
|
#include "extractor/guidance/constants.hpp"
|
||||||
#include "extractor/guidance/road_classification.hpp"
|
#include "extractor/guidance/road_classification.hpp"
|
||||||
|
|
||||||
|
#include "util/assert.hpp"
|
||||||
#include "util/bearing.hpp"
|
#include "util/bearing.hpp"
|
||||||
#include "util/guidance/name_announcements.hpp"
|
#include "util/guidance/name_announcements.hpp"
|
||||||
|
|
||||||
@ -42,15 +43,21 @@ inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_base
|
|||||||
MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table,
|
const SuffixTable &street_name_suffix_table)
|
||||||
const IntersectionGenerator &intersection_generator)
|
|
||||||
: IntersectionHandler(node_based_graph,
|
: IntersectionHandler(node_based_graph,
|
||||||
node_data_container,
|
node_data_container,
|
||||||
coordinates,
|
coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table,
|
street_name_suffix_table)
|
||||||
intersection_generator)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,17 +278,11 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
|
|||||||
// handle motorway forks
|
// handle motorway forks
|
||||||
else if (exiting_motorways > 1)
|
else if (exiting_motorways > 1)
|
||||||
{
|
{
|
||||||
if (exiting_motorways == 2 && intersection.size() == 2)
|
if (exiting_motorways == 2)
|
||||||
{
|
|
||||||
intersection[1].instruction =
|
|
||||||
getInstructionForObvious(intersection.size(),
|
|
||||||
via_eid,
|
|
||||||
isThroughStreet(1, intersection),
|
|
||||||
intersection[1]);
|
|
||||||
intersection[0].entry_allowed = false; // UTURN on the freeway
|
|
||||||
}
|
|
||||||
else if (exiting_motorways == 2)
|
|
||||||
{
|
{
|
||||||
|
OSRM_ASSERT(intersection.size() != 2,
|
||||||
|
node_coordinates[node_based_graph.GetTarget(via_eid)]);
|
||||||
|
|
||||||
// standard fork
|
// standard fork
|
||||||
std::size_t first_valid = std::numeric_limits<std::size_t>::max(),
|
std::size_t first_valid = std::numeric_limits<std::size_t>::max(),
|
||||||
second_valid = std::numeric_limits<std::size_t>::max();
|
second_valid = std::numeric_limits<std::size_t>::max();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "extractor/guidance/node_based_graph_walker.hpp"
|
#include "extractor/guidance/node_based_graph_walker.hpp"
|
||||||
|
#include "extractor/intersection/intersection_analysis.hpp"
|
||||||
#include "util/bearing.hpp"
|
#include "util/bearing.hpp"
|
||||||
#include "util/coordinate_calculation.hpp"
|
#include "util/coordinate_calculation.hpp"
|
||||||
|
|
||||||
@ -14,11 +15,18 @@ namespace guidance
|
|||||||
{
|
{
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------
|
||||||
NodeBasedGraphWalker::NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &node_based_graph,
|
NodeBasedGraphWalker::NodeBasedGraphWalker(
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const IntersectionGenerator &intersection_generator)
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data)
|
||||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||||
intersection_generator(intersection_generator)
|
node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
|
||||||
|
node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
|
||||||
|
turn_lanes_data(turn_lanes_data)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,8 +243,18 @@ operator()(const NodeID /*nid*/,
|
|||||||
|
|
||||||
// ---------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------
|
||||||
IntersectionFinderAccumulator::IntersectionFinderAccumulator(
|
IntersectionFinderAccumulator::IntersectionFinderAccumulator(
|
||||||
const std::uint8_t hop_limit, const IntersectionGenerator &intersection_generator)
|
const std::uint8_t hop_limit,
|
||||||
: hops(0), hop_limit(hop_limit), intersection_generator(intersection_generator)
|
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data)
|
||||||
|
: hops(0), hop_limit(hop_limit), node_based_graph(node_based_graph),
|
||||||
|
node_data_container(node_data_container), node_coordinates(node_coordinates),
|
||||||
|
compressed_geometries(compressed_geometries), node_restriction_map(node_restriction_map),
|
||||||
|
barrier_nodes(barrier_nodes), turn_lanes_data(turn_lanes_data)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +279,14 @@ void IntersectionFinderAccumulator::update(const NodeID from_node,
|
|||||||
nid = from_node;
|
nid = from_node;
|
||||||
via_edge_id = via_edge;
|
via_edge_id = via_edge;
|
||||||
|
|
||||||
intersection = intersection_generator.GetConnectedRoads(from_node, via_edge, true);
|
intersection = intersection::getConnectedRoads<true>(node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
|
{from_node, via_edge});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace guidance
|
} // namespace guidance
|
||||||
|
@ -23,21 +23,26 @@ namespace extractor
|
|||||||
namespace guidance
|
namespace guidance
|
||||||
{
|
{
|
||||||
|
|
||||||
RoundaboutHandler::RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
RoundaboutHandler::RoundaboutHandler(
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const CompressedEdgeContainer &compressed_edge_container,
|
const std::vector<util::Coordinate> &coordinates,
|
||||||
const util::NameTable &name_table,
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
const SuffixTable &street_name_suffix_table,
|
const RestrictionMap &node_restriction_map,
|
||||||
const IntersectionGenerator &intersection_generator)
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
|
const util::NameTable &name_table,
|
||||||
|
const SuffixTable &street_name_suffix_table)
|
||||||
: IntersectionHandler(node_based_graph,
|
: IntersectionHandler(node_based_graph,
|
||||||
node_data_container,
|
node_data_container,
|
||||||
coordinates,
|
coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table,
|
street_name_suffix_table),
|
||||||
intersection_generator),
|
coordinate_extractor(node_based_graph, compressed_geometries, coordinates)
|
||||||
compressed_edge_container(compressed_edge_container),
|
|
||||||
coordinate_extractor(node_based_graph, compressed_edge_container, coordinates)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +61,6 @@ bool RoundaboutHandler::canProcess(const NodeID from_nid,
|
|||||||
Intersection RoundaboutHandler::
|
Intersection RoundaboutHandler::
|
||||||
operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersection) const
|
operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersection) const
|
||||||
{
|
{
|
||||||
invalidateExitAgainstDirection(from_nid, via_eid, intersection);
|
|
||||||
const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
|
const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
|
||||||
const auto roundabout_type = getRoundaboutType(node_based_graph.GetTarget(via_eid));
|
const auto roundabout_type = getRoundaboutType(node_based_graph.GetTarget(via_eid));
|
||||||
// find the radius of the roundabout
|
// find the radius of the roundabout
|
||||||
@ -107,77 +111,6 @@ detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags(
|
|||||||
return {on_roundabout, can_enter_roundabout, can_exit_roundabout_separately};
|
return {on_roundabout, can_enter_roundabout, can_exit_roundabout_separately};
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid,
|
|
||||||
const EdgeID via_eid,
|
|
||||||
Intersection &intersection) const
|
|
||||||
{
|
|
||||||
const auto &in_edge_class = node_based_graph.GetEdgeData(via_eid).flags;
|
|
||||||
if (in_edge_class.roundabout || in_edge_class.circular)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Find range in which exits that must be invalidated (shaded areas):
|
|
||||||
// exit..end exit..end begin..exit for ↺ roundabouts
|
|
||||||
// *************************************
|
|
||||||
// * <--. ^ <--. / <--. *
|
|
||||||
// * | / | /░ | *
|
|
||||||
// * |/ |v░░ -->| *
|
|
||||||
// * |^ |\ ░ ░░░|\ *
|
|
||||||
// * |░\ |░\░ ░░░| \ *
|
|
||||||
// * --'░░░\ --'░░░v --' v *
|
|
||||||
// *************************************
|
|
||||||
//
|
|
||||||
// begin..exit begin..exit exit..end for ↻ roundabouts
|
|
||||||
// *************************************
|
|
||||||
// * --.░░░^ --.░░░/ --. ^ *
|
|
||||||
// * |░/░ |░/ ░░░| / *
|
|
||||||
// * |/░░ |v ░░░|/ *
|
|
||||||
// * |^░░ |\ -->| *
|
|
||||||
// * | \░ | \ | *
|
|
||||||
// * <--' \ <--' v <--' *
|
|
||||||
// *************************************
|
|
||||||
bool roundabout_entry_first = false;
|
|
||||||
auto invalidate_from = intersection.end(), invalidate_to = intersection.end();
|
|
||||||
for (auto road = intersection.begin(); road != intersection.end(); ++road)
|
|
||||||
{
|
|
||||||
const auto &edge = node_based_graph.GetEdgeData(road->eid);
|
|
||||||
if (edge.flags.roundabout || edge.flags.circular)
|
|
||||||
{
|
|
||||||
if (edge.reversed)
|
|
||||||
{
|
|
||||||
if (roundabout_entry_first)
|
|
||||||
{ // invalidate turns in range exit..end
|
|
||||||
invalidate_from = road + 1;
|
|
||||||
invalidate_to = intersection.end();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ // invalidate turns in range begin..exit
|
|
||||||
invalidate_from = intersection.begin() + 1;
|
|
||||||
invalidate_to = road;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
roundabout_entry_first = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OSRM_ASSERT(invalidate_from <= invalidate_to, coordinates[from_nid]);
|
|
||||||
|
|
||||||
// Exiting roundabouts at an entry point is technically a data-modelling issue.
|
|
||||||
// This workaround handles cases in which an exit precedes and entry. The resulting
|
|
||||||
// u-turn against the roundabout direction is invalidated.
|
|
||||||
for (; invalidate_from != invalidate_to; ++invalidate_from)
|
|
||||||
{
|
|
||||||
const auto &edge = node_based_graph.GetEdgeData(invalidate_from->eid);
|
|
||||||
if (!edge.flags.roundabout && !edge.flags.circular &&
|
|
||||||
node_based_graph.GetTarget(invalidate_from->eid) != from_nid)
|
|
||||||
{
|
|
||||||
invalidate_from->entry_allowed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we want to see a roundabout as a turn, the exits have to be distinct enough to be seen a
|
// If we want to see a roundabout as a turn, the exits have to be distinct enough to be seen a
|
||||||
// dedicated turns. We are limiting it to four-way intersections with well distinct bearings.
|
// dedicated turns. We are limiting it to four-way intersections with well distinct bearings.
|
||||||
// All entry/roads and exit roads have to be simple. Not segregated roads.
|
// All entry/roads and exit roads have to be simple. Not segregated roads.
|
||||||
@ -215,7 +148,7 @@ bool RoundaboutHandler::qualifiesAsRoundaboutIntersection(
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// there is a single non-roundabout edge
|
// there is a single non-roundabout edge
|
||||||
const auto src_coordinate = coordinates[node];
|
const auto src_coordinate = node_coordinates[node];
|
||||||
|
|
||||||
const auto edge_range = node_based_graph.GetAdjacentEdgeRange(node);
|
const auto edge_range = node_based_graph.GetAdjacentEdgeRange(node);
|
||||||
const auto number_of_lanes_at_intersection = std::accumulate(
|
const auto number_of_lanes_at_intersection = std::accumulate(
|
||||||
@ -334,11 +267,11 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
|
|||||||
|
|
||||||
const auto getEdgeLength = [&](const NodeID source_node, EdgeID eid) {
|
const auto getEdgeLength = [&](const NodeID source_node, EdgeID eid) {
|
||||||
double length = 0.;
|
double length = 0.;
|
||||||
auto last_coord = coordinates[source_node];
|
auto last_coord = node_coordinates[source_node];
|
||||||
const auto &edge_bucket = compressed_edge_container.GetBucketReference(eid);
|
const auto &edge_bucket = compressed_geometries.GetBucketReference(eid);
|
||||||
for (const auto &compressed_edge : edge_bucket)
|
for (const auto &compressed_edge : edge_bucket)
|
||||||
{
|
{
|
||||||
const auto next_coord = coordinates[compressed_edge.node_id];
|
const auto next_coord = node_coordinates[compressed_edge.node_id];
|
||||||
length += util::coordinate_calculation::haversineDistance(last_coord, next_coord);
|
length += util::coordinate_calculation::haversineDistance(last_coord, next_coord);
|
||||||
last_coord = next_coord;
|
last_coord = next_coord;
|
||||||
}
|
}
|
||||||
|
@ -22,18 +22,25 @@ namespace extractor
|
|||||||
namespace guidance
|
namespace guidance
|
||||||
{
|
{
|
||||||
|
|
||||||
SliproadHandler::SliproadHandler(const IntersectionGenerator &intersection_generator,
|
SliproadHandler::SliproadHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table)
|
const SuffixTable &street_name_suffix_table)
|
||||||
: IntersectionHandler(node_based_graph,
|
: IntersectionHandler(node_based_graph,
|
||||||
node_data_container,
|
node_data_container,
|
||||||
coordinates,
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table,
|
street_name_suffix_table),
|
||||||
intersection_generator)
|
coordinate_extractor(node_based_graph, compressed_geometries, node_coordinates)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,7 +250,14 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
|||||||
|
|
||||||
// Starting out at the intersection and going onto the Sliproad we skip artificial
|
// Starting out at the intersection and going onto the Sliproad we skip artificial
|
||||||
// degree two intersections and limit the max hop count in doing so.
|
// degree two intersections and limit the max hop count in doing so.
|
||||||
IntersectionFinderAccumulator intersection_finder{10, intersection_generator};
|
IntersectionFinderAccumulator intersection_finder{10,
|
||||||
|
node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data};
|
||||||
const SkipTrafficSignalBarrierRoadSelector road_selector{};
|
const SkipTrafficSignalBarrierRoadSelector road_selector{};
|
||||||
(void)graph_walker.TraverseRoad(intersection_node_id, // start node
|
(void)graph_walker.TraverseRoad(intersection_node_id, // start node
|
||||||
sliproad_edge, // onto edge
|
sliproad_edge, // onto edge
|
||||||
@ -372,8 +386,6 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
|||||||
//
|
//
|
||||||
// Sliproad Not a Sliproad
|
// Sliproad Not a Sliproad
|
||||||
{
|
{
|
||||||
const auto &coordinate_extractor = intersection_generator.GetCoordinateExtractor();
|
|
||||||
|
|
||||||
const NodeID start = intersection_node_id; // b
|
const NodeID start = intersection_node_id; // b
|
||||||
const EdgeID edge = sliproad_edge; // bd
|
const EdgeID edge = sliproad_edge; // bd
|
||||||
|
|
||||||
@ -424,8 +436,8 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
|||||||
// Only check for curvature and ~90 degree when it makes sense to do so.
|
// Only check for curvature and ~90 degree when it makes sense to do so.
|
||||||
const constexpr auto MIN_LENGTH = 3.;
|
const constexpr auto MIN_LENGTH = 3.;
|
||||||
|
|
||||||
const auto length = haversineDistance(coordinates[intersection_node_id],
|
const auto length = haversineDistance(node_coordinates[intersection_node_id],
|
||||||
coordinates[main_road_intersection->node]);
|
node_coordinates[main_road_intersection->node]);
|
||||||
|
|
||||||
const double minimal_crossroad_angle_of_intersection = 40.;
|
const double minimal_crossroad_angle_of_intersection = 40.;
|
||||||
|
|
||||||
@ -549,8 +561,15 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto skip_traffic_light_intersection = intersection_generator(
|
const auto skip_traffic_light_intersection = intersection::getConnectedRoads<false>(
|
||||||
node_based_graph.GetTarget(sliproad_edge), candidate_road.eid);
|
node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
|
{node_based_graph.GetTarget(sliproad_edge), candidate_road.eid});
|
||||||
if (skip_traffic_light_intersection.isTrafficSignalOrBarrier() &&
|
if (skip_traffic_light_intersection.isTrafficSignalOrBarrier() &&
|
||||||
node_based_graph.GetTarget(skip_traffic_light_intersection[1].eid) ==
|
node_based_graph.GetTarget(skip_traffic_light_intersection[1].eid) ==
|
||||||
main_road_intersection->node)
|
main_road_intersection->node)
|
||||||
@ -582,7 +601,7 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
|||||||
}
|
}
|
||||||
else if (!name_table.GetNameForID(main_annotation.name_id).empty())
|
else if (!name_table.GetNameForID(main_annotation.name_id).empty())
|
||||||
{
|
{
|
||||||
OSRM_ASSERT(false, coordinates[intersection_node_id]);
|
OSRM_ASSERT(false, node_coordinates[intersection_node_id]);
|
||||||
intersection[*obvious].instruction.type = TurnType::NewName;
|
intersection[*obvious].instruction.type = TurnType::NewName;
|
||||||
intersection[*obvious].instruction.direction_modifier =
|
intersection[*obvious].instruction.direction_modifier =
|
||||||
getTurnDirection(intersection[*obvious].angle);
|
getTurnDirection(intersection[*obvious].angle);
|
||||||
@ -660,8 +679,6 @@ bool SliproadHandler::nextIntersectionIsTooFarAway(const NodeID start, const Edg
|
|||||||
BOOST_ASSERT(start != SPECIAL_NODEID);
|
BOOST_ASSERT(start != SPECIAL_NODEID);
|
||||||
BOOST_ASSERT(onto != SPECIAL_EDGEID);
|
BOOST_ASSERT(onto != SPECIAL_EDGEID);
|
||||||
|
|
||||||
const auto &coordinate_extractor = intersection_generator.GetCoordinateExtractor();
|
|
||||||
|
|
||||||
// Base max distance threshold on the current road class we're on
|
// Base max distance threshold on the current road class we're on
|
||||||
const auto &data = node_based_graph.GetEdgeData(onto).flags;
|
const auto &data = node_based_graph.GetEdgeData(onto).flags;
|
||||||
const auto threshold = scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, // <- scales down
|
const auto threshold = scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, // <- scales down
|
||||||
@ -732,9 +749,9 @@ bool SliproadHandler::isValidSliproadArea(const double max_area,
|
|||||||
{
|
{
|
||||||
using namespace util::coordinate_calculation;
|
using namespace util::coordinate_calculation;
|
||||||
|
|
||||||
const auto first = coordinates[a];
|
const auto first = node_coordinates[a];
|
||||||
const auto second = coordinates[b];
|
const auto second = node_coordinates[b];
|
||||||
const auto third = coordinates[c];
|
const auto third = node_coordinates[c];
|
||||||
|
|
||||||
const auto length = haversineDistance(first, second);
|
const auto length = haversineDistance(first, second);
|
||||||
const auto heigth = haversineDistance(second, third);
|
const auto heigth = haversineDistance(second, third);
|
||||||
|
@ -11,18 +11,25 @@ namespace extractor
|
|||||||
namespace guidance
|
namespace guidance
|
||||||
{
|
{
|
||||||
|
|
||||||
SuppressModeHandler::SuppressModeHandler(const IntersectionGenerator &intersection_generator,
|
SuppressModeHandler::SuppressModeHandler(
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &coordinates,
|
||||||
const util::NameTable &name_table,
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
const SuffixTable &street_name_suffix_table)
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
|
const util::NameTable &name_table,
|
||||||
|
const SuffixTable &street_name_suffix_table)
|
||||||
: IntersectionHandler(node_based_graph,
|
: IntersectionHandler(node_based_graph,
|
||||||
node_data_container,
|
node_data_container,
|
||||||
coordinates,
|
coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table,
|
street_name_suffix_table)
|
||||||
intersection_generator)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,89 +23,79 @@ using EdgeData = util::NodeBasedDynamicGraph::EdgeData;
|
|||||||
|
|
||||||
TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
|
TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const CompressedEdgeContainer &compressed_edge_container,
|
||||||
const RestrictionMap &restriction_map,
|
const RestrictionMap &restriction_map,
|
||||||
const std::unordered_set<NodeID> &barrier_nodes,
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
const CompressedEdgeContainer &compressed_edge_container,
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table)
|
const SuffixTable &street_name_suffix_table)
|
||||||
: node_based_graph(node_based_graph), intersection_generator(node_based_graph,
|
: node_based_graph(node_based_graph), roundabout_handler(node_based_graph,
|
||||||
node_data_container,
|
node_data_container,
|
||||||
restriction_map,
|
node_coordinates,
|
||||||
barrier_nodes,
|
compressed_edge_container,
|
||||||
coordinates,
|
restriction_map,
|
||||||
compressed_edge_container),
|
barrier_nodes,
|
||||||
intersection_normalizer(node_based_graph,
|
turn_lanes_data,
|
||||||
node_data_container,
|
name_table,
|
||||||
coordinates,
|
street_name_suffix_table),
|
||||||
name_table,
|
|
||||||
street_name_suffix_table,
|
|
||||||
intersection_generator),
|
|
||||||
roundabout_handler(node_based_graph,
|
|
||||||
node_data_container,
|
|
||||||
coordinates,
|
|
||||||
compressed_edge_container,
|
|
||||||
name_table,
|
|
||||||
street_name_suffix_table,
|
|
||||||
intersection_generator),
|
|
||||||
motorway_handler(node_based_graph,
|
motorway_handler(node_based_graph,
|
||||||
node_data_container,
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
coordinates,
|
compressed_edge_container,
|
||||||
|
restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table,
|
street_name_suffix_table),
|
||||||
intersection_generator),
|
|
||||||
turn_handler(node_based_graph,
|
turn_handler(node_based_graph,
|
||||||
node_data_container,
|
node_data_container,
|
||||||
coordinates,
|
node_coordinates,
|
||||||
|
compressed_edge_container,
|
||||||
|
restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table,
|
street_name_suffix_table),
|
||||||
intersection_generator),
|
sliproad_handler(node_based_graph,
|
||||||
sliproad_handler(intersection_generator,
|
|
||||||
node_based_graph,
|
|
||||||
node_data_container,
|
node_data_container,
|
||||||
coordinates,
|
node_coordinates,
|
||||||
|
compressed_edge_container,
|
||||||
|
restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table),
|
street_name_suffix_table),
|
||||||
suppress_mode_handler(intersection_generator,
|
suppress_mode_handler(node_based_graph,
|
||||||
node_based_graph,
|
|
||||||
node_data_container,
|
node_data_container,
|
||||||
coordinates,
|
node_coordinates,
|
||||||
|
compressed_edge_container,
|
||||||
|
restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table),
|
street_name_suffix_table),
|
||||||
driveway_handler(intersection_generator,
|
driveway_handler(node_based_graph,
|
||||||
node_based_graph,
|
|
||||||
node_data_container,
|
node_data_container,
|
||||||
coordinates,
|
node_coordinates,
|
||||||
|
compressed_edge_container,
|
||||||
|
restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table),
|
street_name_suffix_table),
|
||||||
statistics_handler(intersection_generator,
|
statistics_handler(node_based_graph,
|
||||||
node_based_graph,
|
|
||||||
node_data_container,
|
node_data_container,
|
||||||
coordinates,
|
node_coordinates,
|
||||||
|
compressed_edge_container,
|
||||||
|
restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table)
|
street_name_suffix_table)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersection TurnAnalysis::operator()(const NodeID node_prior_to_intersection,
|
|
||||||
const EdgeID entering_via_edge) const
|
|
||||||
{
|
|
||||||
TurnAnalysis::ShapeResult shape_result =
|
|
||||||
ComputeIntersectionShapes(node_based_graph.GetTarget(entering_via_edge));
|
|
||||||
|
|
||||||
// assign valid flags to normalized_shape
|
|
||||||
const auto intersection_view = intersection_generator.TransformIntersectionShapeIntoView(
|
|
||||||
node_prior_to_intersection,
|
|
||||||
entering_via_edge,
|
|
||||||
shape_result.annotated_normalized_shape.normalized_shape,
|
|
||||||
shape_result.intersection_shape,
|
|
||||||
shape_result.annotated_normalized_shape.performed_merges);
|
|
||||||
|
|
||||||
// assign the turn types to the intersection
|
|
||||||
return AssignTurnTypes(node_prior_to_intersection, entering_via_edge, intersection_view);
|
|
||||||
}
|
|
||||||
|
|
||||||
Intersection TurnAnalysis::AssignTurnTypes(const NodeID node_prior_to_intersection,
|
Intersection TurnAnalysis::AssignTurnTypes(const NodeID node_prior_to_intersection,
|
||||||
const EdgeID entering_via_edge,
|
const EdgeID entering_via_edge,
|
||||||
const IntersectionView &intersection_view) const
|
const IntersectionView &intersection_view) const
|
||||||
@ -191,19 +181,6 @@ Intersection TurnAnalysis::AssignTurnTypes(const NodeID node_prior_to_intersecti
|
|||||||
return intersection;
|
return intersection;
|
||||||
}
|
}
|
||||||
|
|
||||||
TurnAnalysis::ShapeResult
|
|
||||||
TurnAnalysis::ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const
|
|
||||||
{
|
|
||||||
ShapeResult intersection_shape;
|
|
||||||
intersection_shape.intersection_shape =
|
|
||||||
intersection_generator.ComputeIntersectionShape(node_at_center_of_intersection);
|
|
||||||
|
|
||||||
intersection_shape.annotated_normalized_shape = intersection_normalizer(
|
|
||||||
node_at_center_of_intersection, intersection_shape.intersection_shape);
|
|
||||||
|
|
||||||
return intersection_shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets basic turn types as fallback for otherwise unhandled turns
|
// Sets basic turn types as fallback for otherwise unhandled turns
|
||||||
Intersection TurnAnalysis::setTurnTypes(const NodeID node_prior_to_intersection,
|
Intersection TurnAnalysis::setTurnTypes(const NodeID node_prior_to_intersection,
|
||||||
const EdgeID,
|
const EdgeID,
|
||||||
@ -225,11 +202,6 @@ Intersection TurnAnalysis::setTurnTypes(const NodeID node_prior_to_intersection,
|
|||||||
return intersection;
|
return intersection;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IntersectionGenerator &TurnAnalysis::GetIntersectionGenerator() const
|
|
||||||
{
|
|
||||||
return intersection_generator;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace guidance
|
} // namespace guidance
|
||||||
} // namespace extractor
|
} // namespace extractor
|
||||||
} // namespace osrm
|
} // namespace osrm
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "extractor/guidance/turn_discovery.hpp"
|
#include "extractor/guidance/turn_discovery.hpp"
|
||||||
#include "extractor/guidance/constants.hpp"
|
#include "extractor/guidance/constants.hpp"
|
||||||
|
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||||
|
#include "extractor/intersection/intersection_analysis.hpp"
|
||||||
#include "util/bearing.hpp"
|
#include "util/bearing.hpp"
|
||||||
#include "util/coordinate_calculation.hpp"
|
#include "util/coordinate_calculation.hpp"
|
||||||
|
|
||||||
@ -14,16 +16,16 @@ namespace guidance
|
|||||||
namespace lanes
|
namespace lanes
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
const constexpr bool USE_LOW_PRECISION_MODE = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool findPreviousIntersection(const NodeID node_v,
|
bool findPreviousIntersection(const NodeID node_v,
|
||||||
const EdgeID via_edge,
|
const EdgeID via_edge,
|
||||||
const Intersection &intersection,
|
const Intersection &intersection,
|
||||||
const IntersectionGenerator &intersection_generator,
|
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
// output parameters
|
// output parameters
|
||||||
NodeID &result_node,
|
NodeID &result_node,
|
||||||
EdgeID &result_via_edge,
|
EdgeID &result_via_edge,
|
||||||
@ -43,7 +45,9 @@ bool findPreviousIntersection(const NodeID node_v,
|
|||||||
*/
|
*/
|
||||||
const constexpr double COMBINE_DISTANCE_CUTOFF = 30;
|
const constexpr double COMBINE_DISTANCE_CUTOFF = 30;
|
||||||
|
|
||||||
const auto coordinate_extractor = intersection_generator.GetCoordinateExtractor();
|
const CoordinateExtractor coordinate_extractor(
|
||||||
|
node_based_graph, compressed_geometries, node_coordinates);
|
||||||
|
|
||||||
const auto coordinates_along_via_edge =
|
const auto coordinates_along_via_edge =
|
||||||
coordinate_extractor.GetForwardCoordinatesAlongRoad(node_v, via_edge);
|
coordinate_extractor.GetForwardCoordinatesAlongRoad(node_v, via_edge);
|
||||||
const auto via_edge_length =
|
const auto via_edge_length =
|
||||||
@ -71,7 +75,14 @@ bool findPreviousIntersection(const NodeID node_v,
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
const auto node_v_reverse_intersection =
|
const auto node_v_reverse_intersection =
|
||||||
intersection_generator.GetConnectedRoads(node_w, u_turn_at_node_w, USE_LOW_PRECISION_MODE);
|
intersection::getConnectedRoads<true>(node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
|
{node_w, u_turn_at_node_w});
|
||||||
// Continue along the straightmost turn. If there is no straight turn, we cannot find a valid
|
// Continue along the straightmost turn. If there is no straight turn, we cannot find a valid
|
||||||
// previous intersection.
|
// previous intersection.
|
||||||
const auto straightmost_at_v_in_reverse =
|
const auto straightmost_at_v_in_reverse =
|
||||||
@ -83,8 +94,15 @@ bool findPreviousIntersection(const NodeID node_v,
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
const auto node_u = node_based_graph.GetTarget(straightmost_at_v_in_reverse->eid);
|
const auto node_u = node_based_graph.GetTarget(straightmost_at_v_in_reverse->eid);
|
||||||
const auto node_u_reverse_intersection = intersection_generator.GetConnectedRoads(
|
const auto node_u_reverse_intersection =
|
||||||
node_v, straightmost_at_v_in_reverse->eid, USE_LOW_PRECISION_MODE);
|
intersection::getConnectedRoads<true>(node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
|
{node_v, straightmost_at_v_in_reverse->eid});
|
||||||
|
|
||||||
// now check that the u-turn at the given intersection connects to via-edge
|
// now check that the u-turn at the given intersection connects to via-edge
|
||||||
// The u-turn at the now found intersection should, hopefully, represent the previous edge.
|
// The u-turn at the now found intersection should, hopefully, represent the previous edge.
|
||||||
@ -102,7 +120,14 @@ bool findPreviousIntersection(const NodeID node_v,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result_intersection = intersection_generator(node_u, result_via_edge);
|
result_intersection = intersection::getConnectedRoads<false>(node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
|
{node_u, result_via_edge});
|
||||||
const auto check_via_edge =
|
const auto check_via_edge =
|
||||||
result_intersection.end() !=
|
result_intersection.end() !=
|
||||||
std::find_if(result_intersection.begin(),
|
std::find_if(result_intersection.begin(),
|
||||||
|
@ -113,15 +113,21 @@ std::size_t TurnHandler::Fork::getLeftIndex() const
|
|||||||
TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
const std::vector<util::Coordinate> &coordinates,
|
const std::vector<util::Coordinate> &coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table,
|
const SuffixTable &street_name_suffix_table)
|
||||||
const IntersectionGenerator &intersection_generator)
|
|
||||||
: IntersectionHandler(node_based_graph,
|
: IntersectionHandler(node_based_graph,
|
||||||
node_data_container,
|
node_data_container,
|
||||||
coordinates,
|
coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
name_table,
|
name_table,
|
||||||
street_name_suffix_table,
|
street_name_suffix_table)
|
||||||
intersection_generator)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "extractor/guidance/turn_discovery.hpp"
|
#include "extractor/guidance/turn_discovery.hpp"
|
||||||
#include "extractor/guidance/turn_lane_augmentation.hpp"
|
#include "extractor/guidance/turn_lane_augmentation.hpp"
|
||||||
#include "extractor/guidance/turn_lane_matcher.hpp"
|
#include "extractor/guidance/turn_lane_matcher.hpp"
|
||||||
|
#include "extractor/intersection/intersection_analysis.hpp"
|
||||||
#include "util/bearing.hpp"
|
#include "util/bearing.hpp"
|
||||||
#include "util/log.hpp"
|
#include "util/log.hpp"
|
||||||
#include "util/typedefs.hpp"
|
#include "util/typedefs.hpp"
|
||||||
@ -35,14 +36,21 @@ std::size_t getNumberOfTurns(const Intersection &intersection)
|
|||||||
|
|
||||||
TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const EdgeBasedNodeDataContainer &node_data_container,
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
LaneDescriptionMap &lane_description_map,
|
LaneDescriptionMap &lane_description_map,
|
||||||
const TurnAnalysis &turn_analysis,
|
const TurnAnalysis &turn_analysis,
|
||||||
util::guidance::LaneDataIdMap &id_map)
|
util::guidance::LaneDataIdMap &id_map)
|
||||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||||
lane_description_map(lane_description_map), turn_analysis(turn_analysis), id_map(id_map)
|
node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
|
||||||
|
node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
|
||||||
|
turn_lanes_data(turn_lanes_data), lane_description_map(lane_description_map),
|
||||||
|
turn_analysis(turn_analysis), id_map(id_map)
|
||||||
{
|
{
|
||||||
std::tie(turn_lane_offsets, turn_lane_masks) =
|
std::tie(turn_lane_offsets, turn_lane_masks) = turn_lanes_data;
|
||||||
transformTurnLaneMapIntoArrays(lane_description_map);
|
|
||||||
count_handled = count_called = 0;
|
count_handled = count_called = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,8 +213,13 @@ TurnLaneScenario TurnLaneHandler::deduceScenario(const NodeID at,
|
|||||||
if (findPreviousIntersection(at,
|
if (findPreviousIntersection(at,
|
||||||
via_edge,
|
via_edge,
|
||||||
intersection,
|
intersection,
|
||||||
turn_analysis.GetIntersectionGenerator(),
|
|
||||||
node_based_graph,
|
node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
previous_node,
|
previous_node,
|
||||||
previous_via_edge,
|
previous_via_edge,
|
||||||
previous_intersection_view))
|
previous_intersection_view))
|
||||||
@ -560,7 +573,16 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
|||||||
// find out about the next intersection. To check for valid matches, we also need the turn
|
// find out about the next intersection. To check for valid matches, we also need the turn
|
||||||
// types. We can skip merging/angle adjustments, though
|
// 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));
|
at,
|
||||||
|
straightmost->eid,
|
||||||
|
intersection::getConnectedRoads<false>(node_based_graph,
|
||||||
|
node_data_container,
|
||||||
|
node_coordinates,
|
||||||
|
compressed_geometries,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
turn_lanes_data,
|
||||||
|
{at, straightmost->eid}));
|
||||||
|
|
||||||
// check where we can match turn lanes
|
// check where we can match turn lanes
|
||||||
std::size_t straightmost_tag_index = turn_lane_data.size();
|
std::size_t straightmost_tag_index = turn_lane_data.size();
|
||||||
|
@ -228,7 +228,6 @@ Intersection triviallyMatchLanesToTurns(Intersection intersection,
|
|||||||
u_turn = 1;
|
u_turn = 1;
|
||||||
road_index = 2;
|
road_index = 2;
|
||||||
}
|
}
|
||||||
intersection[u_turn].entry_allowed = true;
|
|
||||||
intersection[u_turn].instruction.type = TurnType::Continue;
|
intersection[u_turn].instruction.type = TurnType::Continue;
|
||||||
intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn;
|
intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn;
|
||||||
|
|
||||||
@ -268,7 +267,6 @@ Intersection triviallyMatchLanesToTurns(Intersection intersection,
|
|||||||
}
|
}
|
||||||
u_turn = intersection.size() - 1;
|
u_turn = intersection.size() - 1;
|
||||||
}
|
}
|
||||||
intersection[u_turn].entry_allowed = true;
|
|
||||||
intersection[u_turn].instruction.type = TurnType::Continue;
|
intersection[u_turn].instruction.type = TurnType::Continue;
|
||||||
intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn;
|
intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn;
|
||||||
|
|
||||||
|
841
src/extractor/intersection/intersection_analysis.cpp
Normal file
841
src/extractor/intersection/intersection_analysis.cpp
Normal file
@ -0,0 +1,841 @@
|
|||||||
|
#include "extractor/intersection/intersection_analysis.hpp"
|
||||||
|
|
||||||
|
#include "util/assert.hpp"
|
||||||
|
#include "util/bearing.hpp"
|
||||||
|
#include "util/coordinate_calculation.hpp"
|
||||||
|
|
||||||
|
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||||
|
|
||||||
|
#include <boost/optional/optional_io.hpp>
|
||||||
|
|
||||||
|
namespace osrm
|
||||||
|
{
|
||||||
|
namespace extractor
|
||||||
|
{
|
||||||
|
namespace intersection
|
||||||
|
{
|
||||||
|
|
||||||
|
IntersectionEdges getIncomingEdges(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const NodeID intersection_node)
|
||||||
|
{
|
||||||
|
IntersectionEdges result;
|
||||||
|
|
||||||
|
for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
|
||||||
|
{
|
||||||
|
const auto from_node = graph.GetTarget(outgoing_edge);
|
||||||
|
const auto incoming_edge = graph.FindEdge(from_node, intersection_node);
|
||||||
|
|
||||||
|
if (!graph.GetEdgeData(incoming_edge).reversed)
|
||||||
|
{
|
||||||
|
result.push_back({from_node, incoming_edge});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce ordering of incoming edges
|
||||||
|
std::sort(result.begin(), result.end());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntersectionEdges getOutgoingEdges(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const NodeID intersection_node)
|
||||||
|
{
|
||||||
|
IntersectionEdges result;
|
||||||
|
|
||||||
|
for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
|
||||||
|
{
|
||||||
|
result.push_back({intersection_node, outgoing_edge});
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_ASSERT(std::is_sorted(result.begin(), result.end()));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<util::Coordinate>
|
||||||
|
getEdgeCoordinates(const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const NodeID from_node,
|
||||||
|
const EdgeID edge,
|
||||||
|
const NodeID to_node)
|
||||||
|
{
|
||||||
|
if (!compressed_geometries.HasEntryForID(edge))
|
||||||
|
return {node_coordinates[from_node], node_coordinates[to_node]};
|
||||||
|
|
||||||
|
BOOST_ASSERT(from_node < node_coordinates.size());
|
||||||
|
BOOST_ASSERT(to_node < node_coordinates.size());
|
||||||
|
|
||||||
|
// extracts the geometry in coordinates from the compressed edge container
|
||||||
|
std::vector<util::Coordinate> result;
|
||||||
|
const auto &geometry = compressed_geometries.GetBucketReference(edge);
|
||||||
|
result.reserve(geometry.size() + 1);
|
||||||
|
|
||||||
|
result.push_back(node_coordinates[from_node]);
|
||||||
|
std::transform(geometry.begin(),
|
||||||
|
geometry.end(),
|
||||||
|
std::back_inserter(result),
|
||||||
|
[&node_coordinates](const auto &compressed_edge) {
|
||||||
|
return node_coordinates[compressed_edge.node_id];
|
||||||
|
});
|
||||||
|
|
||||||
|
// filter duplicated coordinates
|
||||||
|
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
double findAngleBisector(double alpha, double beta)
|
||||||
|
{
|
||||||
|
alpha *= M_PI / 180.;
|
||||||
|
beta *= M_PI / 180.;
|
||||||
|
const auto average =
|
||||||
|
180. * std::atan2(std::sin(alpha) + std::sin(beta), std::cos(alpha) + std::cos(beta)) /
|
||||||
|
M_PI;
|
||||||
|
return std::fmod(average + 360., 360.);
|
||||||
|
}
|
||||||
|
|
||||||
|
double findClosestOppositeBearing(const IntersectionEdgeGeometries &edge_geometries,
|
||||||
|
const double bearing)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!edge_geometries.empty());
|
||||||
|
const auto min = std::min_element(
|
||||||
|
edge_geometries.begin(),
|
||||||
|
edge_geometries.end(),
|
||||||
|
[bearing = util::bearing::reverse(bearing)](const auto &lhs, const auto &rhs) {
|
||||||
|
return util::angularDeviation(lhs.perceived_bearing, bearing) <
|
||||||
|
util::angularDeviation(rhs.perceived_bearing, bearing);
|
||||||
|
});
|
||||||
|
return util::bearing::reverse(min->perceived_bearing);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<bool, double> findMergedBearing(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const IntersectionEdgeGeometries &edge_geometries,
|
||||||
|
std::size_t lhs_index,
|
||||||
|
std::size_t rhs_index,
|
||||||
|
bool neighbor_intersection)
|
||||||
|
{
|
||||||
|
// Function returns a pair with a flag and a value of bearing for merged roads
|
||||||
|
// If the flag is false the bearing must not be used as a merged value at neighbor intersections
|
||||||
|
|
||||||
|
using guidance::STRAIGHT_ANGLE;
|
||||||
|
using guidance::MAXIMAL_ALLOWED_NO_TURN_DEVIATION;
|
||||||
|
using util::bearing::angleBetween;
|
||||||
|
using util::angularDeviation;
|
||||||
|
|
||||||
|
const auto &lhs = edge_geometries[lhs_index];
|
||||||
|
const auto &rhs = edge_geometries[rhs_index];
|
||||||
|
BOOST_ASSERT(graph.GetEdgeData(lhs.edge).reversed != graph.GetEdgeData(rhs.edge).reversed);
|
||||||
|
|
||||||
|
const auto &entry = graph.GetEdgeData(lhs.edge).reversed ? rhs : lhs;
|
||||||
|
const auto opposite_bearing =
|
||||||
|
findClosestOppositeBearing(edge_geometries, entry.perceived_bearing);
|
||||||
|
const auto merged_bearing = findAngleBisector(rhs.perceived_bearing, lhs.perceived_bearing);
|
||||||
|
|
||||||
|
if (angularDeviation(angleBetween(opposite_bearing, entry.perceived_bearing), STRAIGHT_ANGLE) <
|
||||||
|
MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
|
||||||
|
{
|
||||||
|
// In some intersections, turning roads can introduce artificial turns if we merge here.
|
||||||
|
// Consider a scenario like:
|
||||||
|
//
|
||||||
|
// a . g - f
|
||||||
|
// | .
|
||||||
|
// | .
|
||||||
|
// |.
|
||||||
|
// d-b--------e
|
||||||
|
// |
|
||||||
|
// c
|
||||||
|
//
|
||||||
|
// Merging `bgf` and `be` would introduce an angle, even though d-b-e is perfectly straight
|
||||||
|
// We don't change the angle, if such an opposite road exists
|
||||||
|
return {false, entry.perceived_bearing};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (neighbor_intersection)
|
||||||
|
{
|
||||||
|
// Check that the merged bearing makes both turns closer to straight line
|
||||||
|
const auto turn_angle_lhs = angleBetween(opposite_bearing, lhs.perceived_bearing);
|
||||||
|
const auto turn_angle_rhs = angleBetween(opposite_bearing, rhs.perceived_bearing);
|
||||||
|
const auto turn_angle_new = angleBetween(opposite_bearing, merged_bearing);
|
||||||
|
|
||||||
|
if (util::angularDeviation(turn_angle_lhs, STRAIGHT_ANGLE) <
|
||||||
|
util::angularDeviation(turn_angle_new, STRAIGHT_ANGLE) ||
|
||||||
|
util::angularDeviation(turn_angle_rhs, STRAIGHT_ANGLE) <
|
||||||
|
util::angularDeviation(turn_angle_new, STRAIGHT_ANGLE))
|
||||||
|
return {false, opposite_bearing};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {true, merged_bearing};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isRoadsPairMergeable(const guidance::MergableRoadDetector &detector,
|
||||||
|
const IntersectionEdgeGeometries &edge_geometries,
|
||||||
|
const NodeID intersection_node,
|
||||||
|
const std::size_t index)
|
||||||
|
{
|
||||||
|
const auto size = edge_geometries.size();
|
||||||
|
BOOST_ASSERT(index < size);
|
||||||
|
|
||||||
|
const auto &llhs = edge_geometries[(index + size - 1) % size];
|
||||||
|
const auto &lhs = edge_geometries[index];
|
||||||
|
const auto &rhs = edge_geometries[(index + 1) % size];
|
||||||
|
const auto &rrhs = edge_geometries[(index + 2) % size];
|
||||||
|
|
||||||
|
// TODO: check IsDistinctFrom - it is an angle and name-only check
|
||||||
|
// also check CanMergeRoad for all merging scenarios
|
||||||
|
return detector.IsDistinctFrom({llhs.edge, llhs.perceived_bearing, llhs.length},
|
||||||
|
{lhs.edge, lhs.perceived_bearing, lhs.length}) &&
|
||||||
|
detector.CanMergeRoad(intersection_node,
|
||||||
|
{lhs.edge, lhs.perceived_bearing, lhs.length},
|
||||||
|
{rhs.edge, rhs.perceived_bearing, rhs.length}) &&
|
||||||
|
detector.IsDistinctFrom({rhs.edge, rhs.perceived_bearing, rhs.length},
|
||||||
|
{rrhs.edge, rrhs.perceived_bearing, rrhs.length});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getIntersectionLanes(const util::NodeBasedDynamicGraph &graph, const NodeID intersection_node)
|
||||||
|
{
|
||||||
|
std::uint8_t max_lanes_intersection = 0;
|
||||||
|
for (auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
|
||||||
|
{
|
||||||
|
max_lanes_intersection =
|
||||||
|
std::max(max_lanes_intersection,
|
||||||
|
graph.GetEdgeData(outgoing_edge).flags.road_classification.GetNumberOfLanes());
|
||||||
|
}
|
||||||
|
return max_lanes_intersection;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool USE_CLOSE_COORDINATE>
|
||||||
|
IntersectionEdgeGeometries
|
||||||
|
getIntersectionOutgoingGeometries(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const NodeID intersection_node)
|
||||||
|
{
|
||||||
|
IntersectionEdgeGeometries edge_geometries;
|
||||||
|
|
||||||
|
// TODO: keep CoordinateExtractor to reproduce bearings, simplify later
|
||||||
|
const guidance::CoordinateExtractor coordinate_extractor(
|
||||||
|
graph, compressed_geometries, node_coordinates);
|
||||||
|
|
||||||
|
const auto max_lanes_intersection = getIntersectionLanes(graph, intersection_node);
|
||||||
|
|
||||||
|
// Collect outgoing edges
|
||||||
|
for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
|
||||||
|
{
|
||||||
|
const auto remote_node = graph.GetTarget(outgoing_edge);
|
||||||
|
|
||||||
|
const auto &geometry = getEdgeCoordinates(
|
||||||
|
compressed_geometries, node_coordinates, intersection_node, outgoing_edge, remote_node);
|
||||||
|
|
||||||
|
// OSRM_ASSERT(geometry.size() >= 2, node_coordinates[intersection_node]);
|
||||||
|
|
||||||
|
const auto close_coordinate =
|
||||||
|
coordinate_extractor.ExtractCoordinateAtLength(2. /*m*/, geometry);
|
||||||
|
const auto initial_bearing =
|
||||||
|
util::coordinate_calculation::bearing(geometry[0], close_coordinate);
|
||||||
|
|
||||||
|
const auto representative_coordinate =
|
||||||
|
USE_CLOSE_COORDINATE || graph.GetOutDegree(intersection_node) <= 2
|
||||||
|
? coordinate_extractor.GetCoordinateCloseToTurn(
|
||||||
|
intersection_node, outgoing_edge, false, remote_node)
|
||||||
|
: coordinate_extractor.ExtractRepresentativeCoordinate(intersection_node,
|
||||||
|
outgoing_edge,
|
||||||
|
false,
|
||||||
|
remote_node,
|
||||||
|
max_lanes_intersection,
|
||||||
|
geometry);
|
||||||
|
const auto perceived_bearing =
|
||||||
|
util::coordinate_calculation::bearing(geometry[0], representative_coordinate);
|
||||||
|
|
||||||
|
const auto edge_length = util::coordinate_calculation::getLength(
|
||||||
|
geometry.begin(), geometry.end(), util::coordinate_calculation::haversineDistance);
|
||||||
|
|
||||||
|
edge_geometries.push_back({outgoing_edge, initial_bearing, perceived_bearing, edge_length});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort edges in the clockwise bearings order
|
||||||
|
std::sort(edge_geometries.begin(), edge_geometries.end(), [](const auto &lhs, const auto &rhs) {
|
||||||
|
return lhs.perceived_bearing < rhs.perceived_bearing;
|
||||||
|
});
|
||||||
|
return edge_geometries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<IntersectionEdgeGeometries, std::unordered_set<EdgeID>>
|
||||||
|
getIntersectionGeometries(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const guidance::MergableRoadDetector &detector,
|
||||||
|
const NodeID intersection_node)
|
||||||
|
{
|
||||||
|
IntersectionEdgeGeometries edge_geometries = getIntersectionOutgoingGeometries<false>(
|
||||||
|
graph, compressed_geometries, node_coordinates, intersection_node);
|
||||||
|
|
||||||
|
const auto edges_number = edge_geometries.size();
|
||||||
|
|
||||||
|
std::vector<bool> merged_edges(edges_number, false);
|
||||||
|
|
||||||
|
// TODO: intersection views do not contain merged and not allowed edges
|
||||||
|
// but contain other restricted edges that are used in TurnAnalysis,
|
||||||
|
// to be deleted after TurnAnalysis refactoring
|
||||||
|
std::unordered_set<EdgeID> merged_edge_ids;
|
||||||
|
|
||||||
|
if (edges_number >= 3)
|
||||||
|
{ // Adjust bearings of mergeable roads
|
||||||
|
for (std::size_t index = 0; index < edges_number; ++index)
|
||||||
|
{
|
||||||
|
if (isRoadsPairMergeable(detector, edge_geometries, intersection_node, index))
|
||||||
|
{ // Merge bearings of roads left & right
|
||||||
|
const auto next = (index + 1) % edges_number;
|
||||||
|
auto &lhs = edge_geometries[index];
|
||||||
|
auto &rhs = edge_geometries[next];
|
||||||
|
merged_edges[index] = true;
|
||||||
|
merged_edges[next] = true;
|
||||||
|
|
||||||
|
const auto merge = findMergedBearing(graph, edge_geometries, index, next, false);
|
||||||
|
|
||||||
|
lhs.perceived_bearing = lhs.initial_bearing = merge.second;
|
||||||
|
rhs.perceived_bearing = rhs.initial_bearing = merge.second;
|
||||||
|
merged_edge_ids.insert(lhs.edge);
|
||||||
|
merged_edge_ids.insert(rhs.edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edges_number >= 2)
|
||||||
|
{ // Adjust bearings of roads that will be merged at the neighbor intersections
|
||||||
|
const double constexpr PRUNING_DISTANCE = 30.;
|
||||||
|
|
||||||
|
for (std::size_t index = 0; index < edges_number; ++index)
|
||||||
|
{
|
||||||
|
auto &edge_geometry = edge_geometries[index];
|
||||||
|
|
||||||
|
// Don't adjust bearings of roads that were merged at the current intersection
|
||||||
|
// or have neighbor intersection farer than the pruning distance
|
||||||
|
if (merged_edges[index] || edge_geometry.length > PRUNING_DISTANCE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto neighbor_intersection_node = graph.GetTarget(edge_geometry.edge);
|
||||||
|
|
||||||
|
const auto neighbor_geometries = getIntersectionOutgoingGeometries<false>(
|
||||||
|
graph, compressed_geometries, node_coordinates, neighbor_intersection_node);
|
||||||
|
|
||||||
|
const auto neighbor_edges = neighbor_geometries.size();
|
||||||
|
if (neighbor_edges <= 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto neighbor_curr = std::distance(
|
||||||
|
neighbor_geometries.begin(),
|
||||||
|
std::find_if(neighbor_geometries.begin(),
|
||||||
|
neighbor_geometries.end(),
|
||||||
|
[&graph, &intersection_node](const auto &road) {
|
||||||
|
return graph.GetTarget(road.edge) == intersection_node;
|
||||||
|
}));
|
||||||
|
BOOST_ASSERT(static_cast<std::size_t>(neighbor_curr) != neighbor_geometries.size());
|
||||||
|
const auto neighbor_prev = (neighbor_curr + neighbor_edges - 1) % neighbor_edges;
|
||||||
|
const auto neighbor_next = (neighbor_curr + 1) % neighbor_edges;
|
||||||
|
|
||||||
|
if (isRoadsPairMergeable(
|
||||||
|
detector, neighbor_geometries, neighbor_intersection_node, neighbor_prev))
|
||||||
|
{ // Neighbor intersection has mergable neighbor_prev and neighbor_curr roads
|
||||||
|
BOOST_ASSERT(!isRoadsPairMergeable(
|
||||||
|
detector, neighbor_geometries, neighbor_intersection_node, neighbor_curr));
|
||||||
|
|
||||||
|
// TODO: merge with an angle bisector, but not a reversed closed turn, to be
|
||||||
|
// checked as a difference with the previous implementation
|
||||||
|
const auto merge = findMergedBearing(
|
||||||
|
graph, neighbor_geometries, neighbor_prev, neighbor_curr, true);
|
||||||
|
|
||||||
|
if (merge.first)
|
||||||
|
{
|
||||||
|
const auto offset = util::angularDeviation(
|
||||||
|
merge.second, neighbor_geometries[neighbor_curr].perceived_bearing);
|
||||||
|
|
||||||
|
// Adjust bearing of AB at the node A if at the node B roads BA (neighbor_curr)
|
||||||
|
// and BC (neighbor_prev) will be merged and will have merged bearing Bb.
|
||||||
|
// The adjustment value is ∠bBA with negative sign (counter-clockwise) to Aa
|
||||||
|
// A ~~~ a
|
||||||
|
// \
|
||||||
|
// b --- B ---
|
||||||
|
// /
|
||||||
|
// C
|
||||||
|
edge_geometry.perceived_bearing = edge_geometry.initial_bearing =
|
||||||
|
std::fmod(edge_geometry.perceived_bearing + 360. - offset, 360.);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isRoadsPairMergeable(
|
||||||
|
detector, neighbor_geometries, neighbor_intersection_node, neighbor_curr))
|
||||||
|
{ // Neighbor intersection has mergable neighbor_curr and neighbor_next roads
|
||||||
|
BOOST_ASSERT(!isRoadsPairMergeable(
|
||||||
|
detector, neighbor_geometries, neighbor_intersection_node, neighbor_prev));
|
||||||
|
|
||||||
|
// TODO: merge with an angle bisector, but not a reversed closed turn, to be
|
||||||
|
// checked as a difference with the previous implementation
|
||||||
|
const auto merge = findMergedBearing(
|
||||||
|
graph, neighbor_geometries, neighbor_curr, neighbor_next, true);
|
||||||
|
if (merge.first)
|
||||||
|
{
|
||||||
|
const auto offset = util::angularDeviation(
|
||||||
|
merge.second, neighbor_geometries[neighbor_curr].perceived_bearing);
|
||||||
|
|
||||||
|
// Adjust bearing of AB at the node A if at the node B roads BA (neighbor_curr)
|
||||||
|
// and BC (neighbor_next) will be merged and will have merged bearing Bb.
|
||||||
|
// The adjustment value is ∠bBA with positive sign (clockwise) to Aa
|
||||||
|
// a ~~~ A
|
||||||
|
// /
|
||||||
|
// --- B --- b
|
||||||
|
// \
|
||||||
|
// C
|
||||||
|
edge_geometry.perceived_bearing = edge_geometry.initial_bearing =
|
||||||
|
std::fmod(edge_geometry.perceived_bearing + offset, 360.);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add incoming edges with reversed bearings
|
||||||
|
edge_geometries.resize(2 * edges_number);
|
||||||
|
for (std::size_t index = 0; index < edges_number; ++index)
|
||||||
|
{
|
||||||
|
const auto &geometry = edge_geometries[index];
|
||||||
|
const auto remote_node = graph.GetTarget(geometry.edge);
|
||||||
|
const auto incoming_edge = graph.FindEdge(remote_node, intersection_node);
|
||||||
|
edge_geometries[edges_number + index] = {incoming_edge,
|
||||||
|
util::bearing::reverse(geometry.initial_bearing),
|
||||||
|
util::bearing::reverse(geometry.perceived_bearing),
|
||||||
|
geometry.length};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce ordering of edges by IDs
|
||||||
|
std::sort(edge_geometries.begin(), edge_geometries.end());
|
||||||
|
|
||||||
|
return std::make_pair(edge_geometries, merged_edge_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto findEdge(const IntersectionEdgeGeometries &geometries, const EdgeID &edge)
|
||||||
|
{
|
||||||
|
const auto it = std::lower_bound(
|
||||||
|
geometries.begin(), geometries.end(), edge, [](const auto &geometry, const auto edge) {
|
||||||
|
return geometry.edge < edge;
|
||||||
|
});
|
||||||
|
BOOST_ASSERT(it != geometries.end() && it->edge == edge);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
double findEdgeBearing(const IntersectionEdgeGeometries &geometries, const EdgeID &edge)
|
||||||
|
{
|
||||||
|
return findEdge(geometries, edge)->perceived_bearing;
|
||||||
|
}
|
||||||
|
|
||||||
|
double findEdgeLength(const IntersectionEdgeGeometries &geometries, const EdgeID &edge)
|
||||||
|
{
|
||||||
|
return findEdge(geometries, edge)->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename RestrictionsRange>
|
||||||
|
bool isTurnRestricted(const RestrictionsRange &restrictions, const NodeID to)
|
||||||
|
{
|
||||||
|
// Check turn restrictions to find a node that is the only allowed target when coming from a
|
||||||
|
// node to an intersection
|
||||||
|
// d
|
||||||
|
// |
|
||||||
|
// a - b - c and `only_straight_on ab | bc would return `c` for `a,b`
|
||||||
|
const auto is_only = std::find_if(restrictions.first,
|
||||||
|
restrictions.second,
|
||||||
|
[](const auto &pair) { return pair.second->is_only; });
|
||||||
|
if (is_only != restrictions.second)
|
||||||
|
return is_only->second->AsNodeRestriction().to != to;
|
||||||
|
|
||||||
|
// Check if explicitly forbidden
|
||||||
|
const auto no_turn =
|
||||||
|
std::find_if(restrictions.first, restrictions.second, [&to](const auto &restriction) {
|
||||||
|
return restriction.second->AsNodeRestriction().to == to;
|
||||||
|
});
|
||||||
|
|
||||||
|
return no_turn != restrictions.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const RestrictionMap &restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const IntersectionEdgeGeometries &geometries,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
|
const IntersectionEdge &from,
|
||||||
|
const IntersectionEdge &to)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(graph.GetTarget(from.edge) == to.node);
|
||||||
|
|
||||||
|
// TODO: to use TurnAnalysis all outgoing edges are required, to be removed later
|
||||||
|
if (graph.GetEdgeData(from.edge).reversed || graph.GetEdgeData(to.edge).reversed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto intersection_node = to.node;
|
||||||
|
const auto destination_node = graph.GetTarget(to.edge);
|
||||||
|
auto const &restrictions = restriction_map.Restrictions(from.node, intersection_node);
|
||||||
|
|
||||||
|
// Check if turn is explicitly restricted by a turn restriction
|
||||||
|
if (isTurnRestricted(restrictions, destination_node))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Precompute reversed bearing of the `from` edge
|
||||||
|
const auto from_edge_reversed_bearing =
|
||||||
|
util::bearing::reverse(findEdgeBearing(geometries, from.edge));
|
||||||
|
|
||||||
|
// Collect some information about the intersection
|
||||||
|
// 1) number of allowed exits and adjacent bidirectional edges
|
||||||
|
std::uint32_t allowed_exits = 0, bidirectional_edges = 0;
|
||||||
|
// 2) edge IDs of roundabouts edges
|
||||||
|
EdgeID roundabout_from = SPECIAL_EDGEID, roundabout_to = SPECIAL_EDGEID;
|
||||||
|
double roundabout_from_angle = 0., roundabout_to_angle = 0.;
|
||||||
|
|
||||||
|
for (const auto eid : graph.GetAdjacentEdgeRange(intersection_node))
|
||||||
|
{
|
||||||
|
const auto &edge_data = graph.GetEdgeData(eid);
|
||||||
|
const auto &edge_class = edge_data.flags;
|
||||||
|
const auto to_node = graph.GetTarget(eid);
|
||||||
|
const auto reverse_edge = graph.FindEdge(to_node, intersection_node);
|
||||||
|
BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID);
|
||||||
|
|
||||||
|
const auto is_exit_edge = !edge_data.reversed && !isTurnRestricted(restrictions, to_node);
|
||||||
|
const auto is_bidirectional = !graph.GetEdgeData(reverse_edge).reversed;
|
||||||
|
allowed_exits += is_exit_edge;
|
||||||
|
bidirectional_edges += is_bidirectional;
|
||||||
|
|
||||||
|
if (edge_class.roundabout || edge_class.circular)
|
||||||
|
{
|
||||||
|
if (edge_data.reversed)
|
||||||
|
{
|
||||||
|
// "Linked Roundabouts" is an example of tie between two linked roundabouts
|
||||||
|
// A tie breaker for that maximizes ∠(roundabout_from_bearing, ¬from_edge_bearing)
|
||||||
|
const auto angle = util::bearing::angleBetween(
|
||||||
|
findEdgeBearing(geometries, reverse_edge), from_edge_reversed_bearing);
|
||||||
|
if (angle > roundabout_from_angle)
|
||||||
|
{
|
||||||
|
roundabout_from = reverse_edge;
|
||||||
|
roundabout_from_angle = angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// a tie breaker that maximizes ∠(¬from_edge_bearing, roundabout_to_bearing)
|
||||||
|
const auto angle = util::bearing::angleBetween(from_edge_reversed_bearing,
|
||||||
|
findEdgeBearing(geometries, eid));
|
||||||
|
if (angle > roundabout_to_angle)
|
||||||
|
{
|
||||||
|
roundabout_to = eid;
|
||||||
|
roundabout_to_angle = angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) if the intersection has a barrier
|
||||||
|
const bool is_barrier_node = barrier_nodes.find(intersection_node) != barrier_nodes.end();
|
||||||
|
|
||||||
|
// Check a U-turn
|
||||||
|
if (from.node == destination_node)
|
||||||
|
{
|
||||||
|
// Allow U-turns before barrier nodes
|
||||||
|
if (is_barrier_node)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Allow U-turns at dead-ends
|
||||||
|
if (graph.GetAdjacentEdgeRange(intersection_node).size() == 1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Allow U-turns at dead-ends if there is at most one bidirectional road at the intersection
|
||||||
|
// The condition allows U-turns d→a→d and c→b→c ("Bike - Around the Block" test)
|
||||||
|
// a→b
|
||||||
|
// ↕ ↕
|
||||||
|
// d↔c
|
||||||
|
if (allowed_exits == 1 || bidirectional_edges <= 1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Allow U-turn if the incoming edge has a U-turn lane
|
||||||
|
// TODO: revisit the use-case, related PR #2753
|
||||||
|
const auto &incoming_edge_annotation_id = graph.GetEdgeData(from.edge).annotation_data;
|
||||||
|
const auto lane_description_id = static_cast<std::size_t>(
|
||||||
|
node_data_container.GetAnnotation(incoming_edge_annotation_id).lane_description_id);
|
||||||
|
if (lane_description_id != INVALID_LANE_DESCRIPTIONID)
|
||||||
|
{
|
||||||
|
const auto &turn_lane_offsets = std::get<0>(turn_lanes_data);
|
||||||
|
const auto &turn_lanes = std::get<1>(turn_lanes_data);
|
||||||
|
BOOST_ASSERT(lane_description_id + 1 < turn_lane_offsets.size());
|
||||||
|
|
||||||
|
if (std::any_of(turn_lanes.begin() + turn_lane_offsets[lane_description_id],
|
||||||
|
turn_lanes.begin() + turn_lane_offsets[lane_description_id + 1],
|
||||||
|
[](const auto &lane) { return lane & guidance::TurnLaneType::uturn; }))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't allow U-turns on usual intersections
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't allow turns via barriers for not U-turn maneuvers
|
||||||
|
if (is_barrier_node)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check for roundabouts exits in the opposite direction of roundabout flow
|
||||||
|
if (roundabout_from != SPECIAL_EDGEID && roundabout_to != SPECIAL_EDGEID)
|
||||||
|
{
|
||||||
|
// Get bearings of edges
|
||||||
|
const auto roundabout_from_bearing = findEdgeBearing(geometries, roundabout_from);
|
||||||
|
const auto roundabout_to_bearing = findEdgeBearing(geometries, roundabout_to);
|
||||||
|
const auto to_edge_bearing = findEdgeBearing(geometries, to.edge);
|
||||||
|
|
||||||
|
// Get angles from the roundabout edge to three other edges
|
||||||
|
const auto roundabout_angle =
|
||||||
|
util::bearing::angleBetween(roundabout_from_bearing, roundabout_to_bearing);
|
||||||
|
const auto roundabout_from_angle =
|
||||||
|
util::bearing::angleBetween(roundabout_from_bearing, from_edge_reversed_bearing);
|
||||||
|
const auto roundabout_to_angle =
|
||||||
|
util::bearing::angleBetween(roundabout_from_bearing, to_edge_bearing);
|
||||||
|
|
||||||
|
// Restrict turning over a roundabout if `roundabout_to_angle` is in
|
||||||
|
// a sector between `roundabout_from_bearing` to `from_bearing` (shaded area)
|
||||||
|
//
|
||||||
|
// roundabout_angle = 270° roundabout_angle = 90°
|
||||||
|
// roundabout_from_angle = 150° roundabout_from_angle = 150°
|
||||||
|
// roundabout_to_angle = 90° roundabout_to_angle = 270°
|
||||||
|
//
|
||||||
|
// 150° 150°
|
||||||
|
// v░░░░░░ ░░░░░░░░░v
|
||||||
|
// v░░░░░░░ ░░░░░░░░v
|
||||||
|
// 270° <-ooo- v -ttt-> 90° 270° <-ttt- v -ooo-> 90°
|
||||||
|
// ^░░░░░░░ ░░░░░░░^
|
||||||
|
// r░░░░░░░ ░░░░░░░r
|
||||||
|
// r░░░░░░░ ░░░░░░░r
|
||||||
|
if ((roundabout_from_angle < roundabout_angle &&
|
||||||
|
roundabout_to_angle < roundabout_from_angle) ||
|
||||||
|
(roundabout_from_angle > roundabout_angle &&
|
||||||
|
roundabout_to_angle > roundabout_from_angle))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The function adapts intersection geometry data to TurnAnalysis
|
||||||
|
guidance::IntersectionView
|
||||||
|
convertToIntersectionView(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const RestrictionMap &restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const IntersectionEdgeGeometries &edge_geometries,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
|
const IntersectionEdge &incoming_edge,
|
||||||
|
const IntersectionEdges &outgoing_edges,
|
||||||
|
const std::unordered_set<EdgeID> &merged_edges)
|
||||||
|
{
|
||||||
|
using util::bearing::angleBetween;
|
||||||
|
|
||||||
|
const auto edge_it = findEdge(edge_geometries, incoming_edge.edge);
|
||||||
|
const auto incoming_bearing = edge_it->perceived_bearing;
|
||||||
|
const auto initial_incoming_bearing = edge_it->initial_bearing;
|
||||||
|
|
||||||
|
using IntersectionViewDataWithAngle = std::pair<guidance::IntersectionViewData, double>;
|
||||||
|
std::vector<IntersectionViewDataWithAngle> pre_intersection_view;
|
||||||
|
guidance::IntersectionViewData uturn{{SPECIAL_EDGEID, 0., 0.}, false, 0.};
|
||||||
|
std::size_t allowed_uturns_number = 0;
|
||||||
|
for (const auto &outgoing_edge : outgoing_edges)
|
||||||
|
{
|
||||||
|
const auto is_uturn = [](const auto angle) {
|
||||||
|
return std::fabs(angle) < std::numeric_limits<double>::epsilon();
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto edge_it = findEdge(edge_geometries, outgoing_edge.edge);
|
||||||
|
const auto segment_length = edge_it->length;
|
||||||
|
const auto is_merged = merged_edges.count(outgoing_edge.edge) != 0;
|
||||||
|
const auto is_turn_allowed = intersection::isTurnAllowed(graph,
|
||||||
|
node_data_container,
|
||||||
|
restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
edge_geometries,
|
||||||
|
turn_lanes_data,
|
||||||
|
incoming_edge,
|
||||||
|
outgoing_edge);
|
||||||
|
|
||||||
|
// Compute angles
|
||||||
|
const auto outgoing_bearing = edge_it->perceived_bearing;
|
||||||
|
const auto initial_outgoing_bearing = edge_it->initial_bearing;
|
||||||
|
auto turn_angle = std::fmod(
|
||||||
|
std::round(angleBetween(incoming_bearing, outgoing_bearing) * 1e8) / 1e8, 360.);
|
||||||
|
auto initial_angle = angleBetween(initial_incoming_bearing, initial_outgoing_bearing);
|
||||||
|
|
||||||
|
// If angle of the allowed turn is in a neighborhood of 0° (±15°) but the initial OSM angle
|
||||||
|
// is in the opposite semi-plane then assume explicitly a U-turn to avoid incorrect
|
||||||
|
// adjustments due to numerical noise in selection of representative_coordinate
|
||||||
|
if (is_turn_allowed &&
|
||||||
|
((turn_angle < 15 && initial_angle > 180) || (turn_angle > 345 && initial_angle < 180)))
|
||||||
|
{
|
||||||
|
turn_angle = 0;
|
||||||
|
initial_angle = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto is_uturn_angle = is_uturn(turn_angle);
|
||||||
|
|
||||||
|
guidance::IntersectionViewData road{
|
||||||
|
{outgoing_edge.edge, outgoing_bearing, segment_length}, is_turn_allowed, turn_angle};
|
||||||
|
|
||||||
|
if (graph.GetTarget(outgoing_edge.edge) == incoming_edge.node)
|
||||||
|
{ // Save the true U-turn road to add later if no allowed U-turns will be added
|
||||||
|
uturn = road;
|
||||||
|
}
|
||||||
|
else if (is_turn_allowed || (!is_merged && !is_uturn_angle))
|
||||||
|
{ // Add roads that have allowed entry or not U-turns and not merged
|
||||||
|
allowed_uturns_number += is_uturn_angle;
|
||||||
|
|
||||||
|
// Adjust computed initial turn angle for non-U-turn road edge cases:
|
||||||
|
// 1) use 0° or 360° if the road has 0° initial angle
|
||||||
|
// 2) use turn angle if the smallest arc between turn and initial angles passes 0°
|
||||||
|
const auto use_turn_angle = (turn_angle > 270 && initial_angle < 90) ||
|
||||||
|
(turn_angle < 90 && initial_angle > 270);
|
||||||
|
const auto adjusted_angle = is_uturn(initial_angle)
|
||||||
|
? (turn_angle > 180. ? 360. : 0.)
|
||||||
|
: use_turn_angle ? turn_angle : initial_angle;
|
||||||
|
pre_intersection_view.push_back({road, adjusted_angle});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_ASSERT(uturn.eid != SPECIAL_EDGEID);
|
||||||
|
if (uturn.entry_allowed || allowed_uturns_number == 0)
|
||||||
|
{ // Add the true U-turn if it is allowed or no other U-turns found
|
||||||
|
pre_intersection_view.insert(pre_intersection_view.begin(), {uturn, 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order roads in counter-clockwise order starting from the U-turn edge in the OSM order
|
||||||
|
std::stable_sort(pre_intersection_view.begin(),
|
||||||
|
pre_intersection_view.end(),
|
||||||
|
[](const auto &lhs, const auto &rhs) {
|
||||||
|
return std::tie(lhs.second, lhs.first.angle) <
|
||||||
|
std::tie(rhs.second, rhs.first.angle);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Adjust perceived bearings to keep the initial OSM order with respect to the first edge
|
||||||
|
for (auto curr = pre_intersection_view.begin(), next = std::next(curr);
|
||||||
|
next != pre_intersection_view.end();
|
||||||
|
++curr, ++next)
|
||||||
|
{
|
||||||
|
// Check that the perceived angles order is the same as the initial OSM one
|
||||||
|
if (next->first.angle < curr->first.angle)
|
||||||
|
{ // If the true bearing is out of the initial order (next before current) then
|
||||||
|
// adjust the next road angle to keep the order. The adjustment angle is at most
|
||||||
|
// 0.5° or a half-angle between the current angle and 360° to prevent overlapping
|
||||||
|
const auto angle_adjustment =
|
||||||
|
std::min(.5, util::restrictAngleToValidRange(360. - curr->first.angle) / 2.);
|
||||||
|
next->first.angle =
|
||||||
|
util::restrictAngleToValidRange(curr->first.angle + angle_adjustment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy intersection view data
|
||||||
|
guidance::IntersectionView intersection_view;
|
||||||
|
intersection_view.reserve(pre_intersection_view.size());
|
||||||
|
std::transform(pre_intersection_view.begin(),
|
||||||
|
pre_intersection_view.end(),
|
||||||
|
std::back_inserter(intersection_view),
|
||||||
|
[](const auto &road) { return road.first; });
|
||||||
|
|
||||||
|
return intersection_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// a
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// v
|
||||||
|
// For an intersection from_node --via_eid--> turn_node ----> c
|
||||||
|
// ^
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// b
|
||||||
|
// This functions returns _all_ turns as if the graph was undirected.
|
||||||
|
// That means we not only get (from_node, turn_node, c) in the above example
|
||||||
|
// but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are
|
||||||
|
// marked as invalid and only needed for intersection classification.
|
||||||
|
template <bool USE_CLOSE_COORDINATE>
|
||||||
|
guidance::IntersectionView
|
||||||
|
getConnectedRoads(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
|
const IntersectionEdge &incoming_edge)
|
||||||
|
{
|
||||||
|
const auto intersection_node = graph.GetTarget(incoming_edge.edge);
|
||||||
|
const auto &outgoing_edges = intersection::getOutgoingEdges(graph, intersection_node);
|
||||||
|
auto edge_geometries = getIntersectionOutgoingGeometries<USE_CLOSE_COORDINATE>(
|
||||||
|
graph, compressed_geometries, node_coordinates, intersection_node);
|
||||||
|
|
||||||
|
// Add incoming edges with reversed bearings
|
||||||
|
const auto edges_number = edge_geometries.size();
|
||||||
|
edge_geometries.resize(2 * edges_number);
|
||||||
|
for (std::size_t index = 0; index < edges_number; ++index)
|
||||||
|
{
|
||||||
|
const auto &geometry = edge_geometries[index];
|
||||||
|
const auto remote_node = graph.GetTarget(geometry.edge);
|
||||||
|
const auto incoming_edge = graph.FindEdge(remote_node, intersection_node);
|
||||||
|
edge_geometries[edges_number + index] = {incoming_edge,
|
||||||
|
util::bearing::reverse(geometry.initial_bearing),
|
||||||
|
util::bearing::reverse(geometry.perceived_bearing),
|
||||||
|
geometry.length};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce ordering of edges by IDs
|
||||||
|
std::sort(edge_geometries.begin(), edge_geometries.end());
|
||||||
|
|
||||||
|
return convertToIntersectionView(graph,
|
||||||
|
node_data_container,
|
||||||
|
node_restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
edge_geometries,
|
||||||
|
turn_lanes_data,
|
||||||
|
incoming_edge,
|
||||||
|
outgoing_edges,
|
||||||
|
std::unordered_set<EdgeID>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template guidance::IntersectionView
|
||||||
|
getConnectedRoads<false>(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
|
const IntersectionEdge &incoming_edge);
|
||||||
|
|
||||||
|
template guidance::IntersectionView
|
||||||
|
getConnectedRoads<true>(const util::NodeBasedDynamicGraph &graph,
|
||||||
|
const EdgeBasedNodeDataContainer &node_data_container,
|
||||||
|
const std::vector<util::Coordinate> &node_coordinates,
|
||||||
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||||
|
const RestrictionMap &node_restriction_map,
|
||||||
|
const std::unordered_set<NodeID> &barrier_nodes,
|
||||||
|
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||||
|
const IntersectionEdge &incoming_edge);
|
||||||
|
|
||||||
|
IntersectionEdge skipDegreeTwoNodes(const util::NodeBasedDynamicGraph &graph, IntersectionEdge road)
|
||||||
|
{
|
||||||
|
std::unordered_set<NodeID> visited_nodes;
|
||||||
|
(void)visited_nodes;
|
||||||
|
|
||||||
|
// Skip trivial nodes without generating the intersection in between, stop at the very first
|
||||||
|
// intersection of degree > 2
|
||||||
|
const auto starting_node = road.node;
|
||||||
|
auto next_node = graph.GetTarget(road.edge);
|
||||||
|
while (graph.GetOutDegree(next_node) == 2 && next_node != starting_node)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(visited_nodes.insert(next_node).second);
|
||||||
|
const auto next_edge = graph.BeginEdges(next_node);
|
||||||
|
road.edge = graph.GetTarget(next_edge) == road.node ? next_edge + 1 : next_edge;
|
||||||
|
road.node = next_node;
|
||||||
|
next_node = graph.GetTarget(road.edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
return road;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,10 +26,8 @@ namespace
|
|||||||
// creates a default edge of unit weight
|
// creates a default edge of unit weight
|
||||||
inline InputEdge MakeUnitEdge(const NodeID from, const NodeID to)
|
inline InputEdge MakeUnitEdge(const NodeID from, const NodeID to)
|
||||||
{
|
{
|
||||||
// src, tgt, dist, edge_id, name_id, fwd, bkwd, roundabout, circular, startpoint, local access,
|
return {from, // source
|
||||||
// split edge, travel_mode
|
to, // target
|
||||||
return {from,
|
|
||||||
to,
|
|
||||||
1, // weight
|
1, // weight
|
||||||
1, // duration
|
1, // duration
|
||||||
GeometryID{0, false}, // geometry_id
|
GeometryID{0, false}, // geometry_id
|
||||||
|
315
unit_tests/extractor/intersection_analysis_tests.cpp
Normal file
315
unit_tests/extractor/intersection_analysis_tests.cpp
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
#include "extractor/intersection/intersection_analysis.hpp"
|
||||||
|
|
||||||
|
#include "extractor/graph_compressor.hpp"
|
||||||
|
|
||||||
|
#include "../common/range_tools.hpp"
|
||||||
|
#include "../unit_tests/mocks/mock_scripting_environment.hpp"
|
||||||
|
|
||||||
|
#include <boost/test/test_case_template.hpp>
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(intersection_analysis_tests)
|
||||||
|
|
||||||
|
using namespace osrm;
|
||||||
|
using namespace osrm::extractor;
|
||||||
|
using namespace osrm::extractor::guidance;
|
||||||
|
using namespace osrm::extractor::intersection;
|
||||||
|
using InputEdge = util::NodeBasedDynamicGraph::InputEdge;
|
||||||
|
using Graph = util::NodeBasedDynamicGraph;
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(simple_intersection_connectivity)
|
||||||
|
{
|
||||||
|
std::unordered_set<NodeID> barrier_nodes{6};
|
||||||
|
std::unordered_set<NodeID> traffic_lights;
|
||||||
|
std::vector<NodeBasedEdgeAnnotation> annotations{
|
||||||
|
{EMPTY_NAMEID, 0, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false},
|
||||||
|
{EMPTY_NAMEID, 1, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}};
|
||||||
|
std::vector<TurnRestriction> restrictions{TurnRestriction{NodeRestriction{0, 2, 1}, false}};
|
||||||
|
std::vector<ConditionalTurnRestriction> conditional_restrictions;
|
||||||
|
CompressedEdgeContainer container;
|
||||||
|
test::MockScriptingEnvironment scripting_environment;
|
||||||
|
|
||||||
|
TurnLanesIndexedArray turn_lanes_data{{0, 0, 3},
|
||||||
|
{TurnLaneType::uturn | TurnLaneType::left,
|
||||||
|
TurnLaneType::straight,
|
||||||
|
TurnLaneType::straight | TurnLaneType::right}};
|
||||||
|
|
||||||
|
// Graph with an additional turn restriction 0→2→1 and bollard at 6
|
||||||
|
// 0→5↔6↔7
|
||||||
|
// ↕
|
||||||
|
// 1↔2←3
|
||||||
|
// ↓
|
||||||
|
// 4
|
||||||
|
const auto unit_edge =
|
||||||
|
[](const NodeID from, const NodeID to, bool allowed, AnnotationID annotation) {
|
||||||
|
return InputEdge{from,
|
||||||
|
to,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
GeometryID{0, false},
|
||||||
|
!allowed,
|
||||||
|
NodeBasedEdgeClassification(),
|
||||||
|
annotation};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<InputEdge> edges = {unit_edge(0, 2, true, 1),
|
||||||
|
unit_edge(0, 5, true, 0),
|
||||||
|
unit_edge(1, 2, true, 0),
|
||||||
|
unit_edge(2, 0, true, 0),
|
||||||
|
unit_edge(2, 1, true, 0),
|
||||||
|
unit_edge(2, 3, false, 0),
|
||||||
|
unit_edge(2, 4, true, 0),
|
||||||
|
unit_edge(3, 2, true, 0),
|
||||||
|
unit_edge(4, 2, false, 0),
|
||||||
|
unit_edge(5, 0, false, 0),
|
||||||
|
unit_edge(5, 6, true, 0),
|
||||||
|
unit_edge(6, 5, true, 0),
|
||||||
|
unit_edge(6, 7, true, 0),
|
||||||
|
unit_edge(7, 6, true, 0)};
|
||||||
|
IntersectionEdgeGeometries edge_geometries{
|
||||||
|
{0, 180, 180, 10.}, // 0→2
|
||||||
|
{1, 90, 90, 10.}, // 0→5
|
||||||
|
{2, 90, 90, 10.}, // 1→2
|
||||||
|
{3, 0, 0, 10.}, // 2→0
|
||||||
|
{4, 270, 270, 10.}, // 2→1
|
||||||
|
{5, 90, 90, 10.}, // 2→3
|
||||||
|
{6, 180, 180, 10.}, // 2→4
|
||||||
|
{7, 270, 270, 10.}, // 3→2
|
||||||
|
{8, 0, 0, 10.}, // 4→2
|
||||||
|
{9, 270, 270, 10.}, // 5→0
|
||||||
|
{10, 90, 90, 10.}, // 5→6
|
||||||
|
{11, 270, 270, 10.}, // 6→5
|
||||||
|
{12, 90, 90, 10.}, // 6→7
|
||||||
|
{13, 270, 270, 10.} // 7→6
|
||||||
|
};
|
||||||
|
|
||||||
|
Graph graph(8, edges);
|
||||||
|
|
||||||
|
GraphCompressor().Compress(barrier_nodes,
|
||||||
|
traffic_lights,
|
||||||
|
scripting_environment,
|
||||||
|
restrictions,
|
||||||
|
conditional_restrictions,
|
||||||
|
graph,
|
||||||
|
annotations,
|
||||||
|
container);
|
||||||
|
|
||||||
|
REQUIRE_SIZE_RANGE(getIncomingEdges(graph, 2), 3);
|
||||||
|
REQUIRE_SIZE_RANGE(getOutgoingEdges(graph, 2), 4);
|
||||||
|
|
||||||
|
EdgeBasedNodeDataContainer node_data_container(
|
||||||
|
std::vector<EdgeBasedNode>(graph.GetNumberOfEdges()), annotations);
|
||||||
|
RestrictionMap restriction_map(restrictions, IndexNodeByFromAndVia());
|
||||||
|
|
||||||
|
const auto connectivity_matrix = [&](NodeID node) {
|
||||||
|
std::vector<bool> result;
|
||||||
|
const auto incoming_edges = getIncomingEdges(graph, node);
|
||||||
|
const auto outgoing_edges = getOutgoingEdges(graph, node);
|
||||||
|
for (const auto incoming_edge : incoming_edges)
|
||||||
|
{
|
||||||
|
for (const auto outgoing_edge : outgoing_edges)
|
||||||
|
{
|
||||||
|
result.push_back(isTurnAllowed(graph,
|
||||||
|
node_data_container,
|
||||||
|
restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
edge_geometries,
|
||||||
|
turn_lanes_data,
|
||||||
|
incoming_edge,
|
||||||
|
outgoing_edge));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
CHECK_EQUAL_RANGE(connectivity_matrix(0), 1, 1); // from node 2 allowed U-turn and to node 5
|
||||||
|
CHECK_EQUAL_RANGE(connectivity_matrix(1), 1); // from node 2 allowed U-turn
|
||||||
|
CHECK_EQUAL_RANGE(connectivity_matrix(2),
|
||||||
|
// clang-format off
|
||||||
|
1, 0, 0, 1, // from node 0 to node 4 and a U-turn at 2
|
||||||
|
1, 0, 0, 1, // from node 1 to nodes 0 and 4
|
||||||
|
1, 1, 0, 1 // from node 3 to nodes 0, 1 and 4
|
||||||
|
// clang-format on
|
||||||
|
);
|
||||||
|
REQUIRE_SIZE_RANGE(connectivity_matrix(3), 0); // no incoming edges, empty matrix
|
||||||
|
CHECK_EQUAL_RANGE(connectivity_matrix(4), 0); // from node 2 not allowed U-turn
|
||||||
|
CHECK_EQUAL_RANGE(connectivity_matrix(5),
|
||||||
|
// clang-format off
|
||||||
|
0, 1, // from node 0 to node 6
|
||||||
|
0, 1, // from node 6 a U-turn to node 6
|
||||||
|
// clang-format on
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK_EQUAL_RANGE(connectivity_matrix(6),
|
||||||
|
// clang-format off
|
||||||
|
1, 0, // from node 5 a U-turn to node 5
|
||||||
|
0, 1, // from node 7 a U-turn to node 7
|
||||||
|
// clang-format on
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity)
|
||||||
|
{
|
||||||
|
std::unordered_set<NodeID> barrier_nodes;
|
||||||
|
std::unordered_set<NodeID> traffic_lights;
|
||||||
|
std::vector<NodeBasedEdgeAnnotation> annotations;
|
||||||
|
std::vector<TurnRestriction> restrictions;
|
||||||
|
std::vector<ConditionalTurnRestriction> conditional_restrictions;
|
||||||
|
CompressedEdgeContainer container;
|
||||||
|
test::MockScriptingEnvironment scripting_environment;
|
||||||
|
|
||||||
|
TurnLanesIndexedArray turn_lanes_data;
|
||||||
|
|
||||||
|
// Graph with roundabout edges 5→0→2
|
||||||
|
// 1 2 3
|
||||||
|
// ↘ ↑ ↙
|
||||||
|
// 0
|
||||||
|
// ↙ ↑ ↘
|
||||||
|
// 4 5 6
|
||||||
|
const auto unit_edge = [](const NodeID from, const NodeID to, bool allowed, bool roundabout) {
|
||||||
|
return InputEdge{
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
GeometryID{0, false},
|
||||||
|
!allowed,
|
||||||
|
NodeBasedEdgeClassification{true, false, false, roundabout, false, false, false, {}},
|
||||||
|
0};
|
||||||
|
};
|
||||||
|
std::vector<InputEdge> edges = {unit_edge(0, 1, false, false),
|
||||||
|
unit_edge(0, 2, true, true),
|
||||||
|
unit_edge(0, 3, false, false),
|
||||||
|
unit_edge(0, 4, true, false),
|
||||||
|
unit_edge(0, 5, false, true),
|
||||||
|
unit_edge(0, 6, true, false),
|
||||||
|
unit_edge(1, 0, true, false),
|
||||||
|
unit_edge(2, 0, false, true),
|
||||||
|
unit_edge(3, 0, true, false),
|
||||||
|
unit_edge(4, 0, false, false),
|
||||||
|
unit_edge(5, 0, true, true),
|
||||||
|
unit_edge(6, 0, false, false)};
|
||||||
|
IntersectionEdgeGeometries edge_geometries{
|
||||||
|
{0, 315, 315, 10}, // 0→1
|
||||||
|
{1, 0, 0, 10}, // 0→2
|
||||||
|
{2, 45, 45, 10}, // 0→3
|
||||||
|
{3, 225, 225, 10}, // 0→4
|
||||||
|
{4, 180, 180, 10}, // 0→5
|
||||||
|
{5, 135, 135, 10}, // 0→6
|
||||||
|
{6, 135, 135, 10}, // 1→0
|
||||||
|
{7, 180, 180, 10}, // 2→0
|
||||||
|
{8, 225, 225, 10}, // 3→0
|
||||||
|
{9, 45, 45, 10}, // 4→0
|
||||||
|
{10, 0, 0, 10}, // 5→0
|
||||||
|
{11, 315, 315, 10} // 6→0
|
||||||
|
};
|
||||||
|
|
||||||
|
Graph graph(7, edges);
|
||||||
|
|
||||||
|
GraphCompressor().Compress(barrier_nodes,
|
||||||
|
traffic_lights,
|
||||||
|
scripting_environment,
|
||||||
|
restrictions,
|
||||||
|
conditional_restrictions,
|
||||||
|
graph,
|
||||||
|
annotations,
|
||||||
|
container);
|
||||||
|
|
||||||
|
REQUIRE_SIZE_RANGE(getIncomingEdges(graph, 0), 3);
|
||||||
|
REQUIRE_SIZE_RANGE(getOutgoingEdges(graph, 0), 6);
|
||||||
|
|
||||||
|
EdgeBasedNodeDataContainer node_data_container(
|
||||||
|
std::vector<EdgeBasedNode>(graph.GetNumberOfEdges()), annotations);
|
||||||
|
RestrictionMap restriction_map(restrictions, IndexNodeByFromAndVia());
|
||||||
|
|
||||||
|
const auto connectivity_matrix = [&](NodeID node) {
|
||||||
|
std::vector<bool> result;
|
||||||
|
const auto incoming_edges = getIncomingEdges(graph, node);
|
||||||
|
const auto outgoing_edges = getOutgoingEdges(graph, node);
|
||||||
|
for (const auto incoming_edge : incoming_edges)
|
||||||
|
{
|
||||||
|
for (const auto outgoing_edge : outgoing_edges)
|
||||||
|
{
|
||||||
|
result.push_back(isTurnAllowed(graph,
|
||||||
|
node_data_container,
|
||||||
|
restriction_map,
|
||||||
|
barrier_nodes,
|
||||||
|
edge_geometries,
|
||||||
|
turn_lanes_data,
|
||||||
|
incoming_edge,
|
||||||
|
outgoing_edge));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
CHECK_EQUAL_RANGE(connectivity_matrix(0),
|
||||||
|
// clang-format off
|
||||||
|
0, 1, 0, 0, 0, 1, // from node 1 to nodes 2 and 6
|
||||||
|
0, 1, 0, 1, 0, 0, // from node 3 to nodes 2 and 4
|
||||||
|
0, 1, 0, 1, 0, 1 // from node 5 to nodes 2, 4 and 6
|
||||||
|
// clang-format on
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(skip_degree_two_nodes)
|
||||||
|
{
|
||||||
|
std::unordered_set<NodeID> barrier_nodes{1};
|
||||||
|
std::unordered_set<NodeID> traffic_lights{2};
|
||||||
|
std::vector<NodeBasedEdgeAnnotation> annotations(1);
|
||||||
|
std::vector<TurnRestriction> restrictions;
|
||||||
|
std::vector<ConditionalTurnRestriction> conditional_restrictions;
|
||||||
|
CompressedEdgeContainer container;
|
||||||
|
test::MockScriptingEnvironment scripting_environment;
|
||||||
|
|
||||||
|
TurnLanesIndexedArray turn_lanes_data;
|
||||||
|
|
||||||
|
// Graph
|
||||||
|
//
|
||||||
|
// 0↔1→2↔3↔4→5 7
|
||||||
|
// ↑ ↕ ↕
|
||||||
|
// 6 8 ↔ 9
|
||||||
|
//
|
||||||
|
const auto unit_edge = [](const NodeID from, const NodeID to, bool allowed) {
|
||||||
|
return InputEdge{
|
||||||
|
from, to, 1, 1, GeometryID{0, false}, !allowed, NodeBasedEdgeClassification{}, 0};
|
||||||
|
};
|
||||||
|
std::vector<InputEdge> edges = {unit_edge(0, 1, true), // 0
|
||||||
|
unit_edge(1, 0, true),
|
||||||
|
unit_edge(1, 2, true),
|
||||||
|
unit_edge(2, 1, false),
|
||||||
|
unit_edge(2, 3, true),
|
||||||
|
unit_edge(3, 2, true), // 5
|
||||||
|
unit_edge(3, 4, true),
|
||||||
|
unit_edge(4, 3, true),
|
||||||
|
unit_edge(4, 5, true),
|
||||||
|
unit_edge(4, 6, false),
|
||||||
|
unit_edge(5, 4, false), // 10
|
||||||
|
unit_edge(6, 4, true),
|
||||||
|
// Circle
|
||||||
|
unit_edge(7, 8, true), // 12
|
||||||
|
unit_edge(7, 9, true),
|
||||||
|
unit_edge(8, 7, true),
|
||||||
|
unit_edge(8, 9, true),
|
||||||
|
unit_edge(9, 7, true),
|
||||||
|
unit_edge(9, 8, true)};
|
||||||
|
|
||||||
|
Graph graph(10, edges);
|
||||||
|
|
||||||
|
GraphCompressor().Compress(barrier_nodes,
|
||||||
|
traffic_lights,
|
||||||
|
scripting_environment,
|
||||||
|
restrictions,
|
||||||
|
conditional_restrictions,
|
||||||
|
graph,
|
||||||
|
annotations,
|
||||||
|
container);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {0, 0}).edge), 4);
|
||||||
|
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {4, 7}).edge), 0);
|
||||||
|
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {5, 10}).edge), 4);
|
||||||
|
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {6, 11}).edge), 4);
|
||||||
|
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {7, 12}).edge), 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
Loading…
Reference in New Issue
Block a user