Turn Angles in OSRM were computed using a lookahead of 10 meters.

This PR adds more advanced coordinate extraction, analysing the road
to detect offsets due to OSM way modelling.

In addition it improves the handling of bearings. Right now OSM reports
bearings simply based on the very first coordinate along a way.
With this PR, we store the bearings for a turn correctly, making the
bearings for turns correct.
This commit is contained in:
Moritz Kobitzsch 2016-08-17 09:49:19 +02:00
parent 1f8ca2879f
commit 5e167b8745
62 changed files with 3451 additions and 679 deletions

View File

@ -11,6 +11,9 @@
- Properly handle destinations on `oneway=-1` roads
- Guidance
- Notifications are now exposed more prominently, announcing turns onto a ferry/pushing your bike more prominently
- Improved turn angle calculation, detecting offsets due to lanes / minor variations due to inaccuracies
- Corrected the bearings returned for intermediate steps - requires reprocessing
- Improved turn locations for collapsed turns
- Trip Plugin
- changed internal behaviour to prefer the smallest lexicographic result over the largest one
- Bugfixes

View File

@ -414,21 +414,21 @@ Feature: Turn Lane Guidance
"""
And the ways
| nodes | turn:lanes:forward | name |
| ab | through\|right\|right\|right | top |
| be | | top |
| bq | | off |
| ef | left\|through\|through\|through\|through\|right | main |
| fg | left\|left\|right\|right | main |
| fs | | off |
| ft | | off |
| gh | | top |
| hi | | top |
| cd | left\|left\|left\|through | bot |
| de | | bot |
| dr | | off |
| gj | | bot |
| jk | | bot |
| nodes | turn:lanes:forward | name | highway | oneway |
| ab | through\|right\|right\|right | top | primary | yes |
| be | | top | primary | yes |
| bq | | off | primary | yes |
| ef | left\|through\|through\|through\|through\|right | main | primary | yes |
| fg | left\|left\|right\|right | main | primary | yes |
| fs | | off | primary | yes |
| ft | | off | primary | yes |
| gh | | top | primary | yes |
| hi | | top | primary | yes |
| cd | left\|left\|left\|through | bot | primary | yes |
| de | | bot | primary | yes |
| dr | | off | primary | yes |
| gj | | bot | primary | yes |
| jk | | bot | primary | yes |
When I route I should get
| waypoints | route | turns | lanes |

View File

@ -19,8 +19,8 @@ Feature: Collapse
| nodes | highway | name | oneway |
| abc | primary | road | yes |
| defg | primary | road | yes |
| fb | primary_link | | |
| be | primary_link | | |
| fb | primary_link | | yes |
| be | primary_link | | yes |
When I route I should get
| waypoints | route | turns |

View File

@ -888,18 +888,18 @@ Feature: Collapse
And the ways
| nodes | highway | route | name | oneway |
| bf | primary | | road | yes |
| abf | primary | | road | yes |
| hcd | primary | | road | yes |
| bc | primary | | | yes |
| di | service | | serv | yes |
| ed | | ferry | ferry | |
| gab | | ferry | ferry | |
| ga | | ferry | ferry | |
| kg | primary | | on | yes |
| ej | primary | | off | yes |
When I route I should get
| waypoints | route | turns |
| k,j | on,ferry,,ferry,off,off | depart,notification straight,continue uturn,turn straight,notification straight,arrive |
| waypoints | route | turns |
| k,j | on,ferry,road,road,ferry,off,off | depart,notification straight,notification straight,continue uturn,turn straight,notification straight,arrive |
# http://www.openstreetmap.org/#map=19/37.78090/-122.41251
Scenario: U-Turn onto unnamed-road
@ -922,5 +922,84 @@ Feature: Collapse
| ef | secondary | | down | yes |
When I route I should get
| waypoints | route | turns |
| a,1 | up,turn,down, | depart,turn right,turn right,arrive |
| waypoints | route | turns |
| a,1 | up,turn,, | depart,turn right,turn sharp right,arrive |
#http://www.openstreetmap.org/#map=19/52.48778/13.30024
Scenario: Hohenzollerdammbrücke
Given the node map
"""
q s
p o
.. . .
. . . .
j - i - - - h - - - g - f
> k < > l <
a - b - - - c - - - d - e
. . . .
.. ..
m n
t r
"""
And the ways
| nodes | highway | name | oneway |
| ab | secondary | hohe | yes |
| bc | secondary | hohebruecke | yes |
| cd | secondary | hohebruecke | yes |
| bk | secondary | hohebruecke | yes |
| kh | secondary | hohebruecke | yes |
| ki | secondary | hohebruecke | yes |
| ck | secondary | hohebruecke | yes |
| de | secondary | hohe | yes |
| fg | secondary | hohe | yes |
| gh | secondary | hohebruecke | yes |
| hi | secondary | hohebruecke | yes |
| gl | secondary | hohebruecke | yes |
| lc | secondary | hohebruecke | yes |
| hl | secondary | hohebruecke | yes |
| ld | secondary | hohebruecke | yes |
| ij | secondary | hohe | yes |
| bm | motorway_link | a100 | yes |
| cm | motorway_link | a100 | yes |
| nc | motorway_link | a100 | yes |
| nd | motorway_link | a100 | yes |
| go | motorway_link | a100 | yes |
| ho | motorway_link | a100 | yes |
| ph | motorway_link | a100 | yes |
| pi | motorway_link | a100 | yes |
| qp | motorway_link | a100 | yes |
| mt | motorway_link | a100 | yes |
| rn | motorway_link | a100 | yes |
| os | motorway_link | a100 | yes |
And the relations
| type | way:from | way:to | node:via | restriction |
| restriction | ck | kh | k | no_right_turn |
| restriction | bk | ki | k | no_left_turn |
| restriction | hl | lc | l | no_right_turn |
| restriction | gl | ld | l | no_left_turn |
| restriction | bc | cm | c | no_right_turn |
| restriction | bc | ck | c | no_left_turn |
| restriction | nc | cm | c | no_left_turn |
| restriction | nc | cd | c | no_right_turn |
| restriction | lc | ck | c | no_left_turn |
| restriction | lc | cd | c | no_right_turn |
| restriction | gh | ho | h | no_right_turn |
| restriction | gh | hl | h | no_left_turn |
| restriction | kh | hi | h | no_left_turn |
| restriction | kh | hl | h | no_right_turn |
| restriction | ph | ho | h | no_left_turn |
| restriction | ph | hi | h | no_right_turn |
When I route I should get
| waypoints | route | turns |
| a,e | hohe,hohe | depart,arrive |
| a,s | hohe,a100,a100 | depart,on ramp left,arrive |
| a,t | hohe,a100,a100 | depart,on ramp right,arrive |
| a,j | | |
| f,j | hohe,hohe | depart,arrive |
| a,t | hohe,a100,a100 | depart,on ramp right,arrive |
| f,e | | |
| q,j | a100,hohe,hohe | depart,turn right,arrive |
| q,e | a100,a100,hohe | depart,continue left,arrive |

View File

@ -134,4 +134,5 @@ Feature: Continue Instructions
| a,c | abcdefb,abcdefb,abcdefb | depart,continue right,arrive |
| a,f | abcdefb,abcdefb,abcdefb | depart,continue left,arrive |
| a,d | abcdefb,abcdefb,abcdefb | depart,continue right,arrive |
| a,e | abcdefb,abcdefb,abcdefb | depart,continue left,arrive |
# continuing right here, since the turn to the left is more expensive
| a,e | abcdefb,abcdefb,abcdefb | depart,continue right,arrive |

View File

@ -43,6 +43,10 @@ Feature: Slipways and Dedicated Turn Lanes
f
@ -59,6 +63,31 @@ Feature: Slipways and Dedicated Turn Lanes
| waypoints | route | turns |
| a,g | first,,second,second | depart,off ramp slight right,turn straight,arrive |
Scenario: Turn Instead of Ramp
Given the node map
"""
e
a b c d
h
f
g
"""
And the ways
| nodes | highway | name |
| abcd | motorway | first |
| bhf | motorway_link | |
| efg | primary | second |
When I route I should get
| waypoints | route | turns |
| a,g | first,,second,second | depart,off ramp slight right,turn straight,arrive |
Scenario: Inner city expressway with on road
Given the node map
"""
@ -208,11 +237,11 @@ Feature: Slipways and Dedicated Turn Lanes
| jcghf | primary | Brauerstrasse | yes |
When I route I should get
| waypoints | route | turns |
| a,i | Ebertstrasse,Ebertstrasse | depart,arrive |
| a,l | Ebertstrasse,Ebertstrasse | depart,arrive |
| a,f | Ebertstrasse,Brauerstrasse,Brauerstrasse | depart,turn right,arrive |
| a,1 | Ebertstrasse,, | depart,turn slight right,arrive |
| waypoints | route | turns |
| a,i | Ebertstrasse,Ebertstrasse | depart,arrive |
| a,l | Ebertstrasse,Ebertstrasse | depart,arrive |
| a,f | Ebertstrasse,Brauerstrasse,Brauerstrasse | depart,turn right,arrive |
| a,1 | Ebertstrasse,, | depart,turn right,arrive |
#2839
Scenario: Self-Loop

View File

@ -435,9 +435,9 @@ Feature: Basic Roundabout
When I route I should get
| waypoints | route | turns | bearing |
| a,d | ab,cd,cd | depart,roundabout turn left exit-3,arrive | 0->180,180->224,90->0 |
| a,f | ab,ef,ef | depart,roundabout turn straight exit-2,arrive | 0->180,180->224,180->0 |
| a,h | ab,gh,gh | depart,roundabout turn right exit-1,arrive | 0->180,180->224,270->0 |
| a,d | ab,cd,cd | depart,roundabout turn left exit-3,arrive | 0->180,180->225,90->0 |
| a,f | ab,ef,ef | depart,roundabout turn straight exit-2,arrive | 0->180,180->225,180->0 |
| a,h | ab,gh,gh | depart,roundabout turn right exit-1,arrive | 0->180,180->225,270->0 |
Scenario: Enter and Exit - Bearings
Given the node map

View File

@ -377,8 +377,8 @@ Feature: Basic Roundabout
| h | give_way |
When I route I should get
| waypoints | route | turns |
| waypoints | route | turns |
# since we cannot handle these invalid roundabout tags yet, we cannout output roundabout taggings. This will hopefully change some day
#| w,x | ll,egg,egg,tr,tr | depart,roundabout-exit-1,roundabout-exit-2,arrive |
| w,x | ll,egg,egg,tr,tr | depart,turn right,continue left,turn slight left,arrive |
| w,x | ll,egg,egg,tr,tr | depart,turn right,continue left,turn straight,arrive |

File diff suppressed because it is too large Load Diff

View File

@ -901,10 +901,10 @@ Feature: Turn Lane Guidance
Given the node map
"""
f e
c
a b
| |
| |
| c
a - b ' |
g d
"""
@ -1091,3 +1091,26 @@ Feature: Turn Lane Guidance
| waypoints | route | turns | lanes |
| a,f | ,ksd,ksd | depart,turn left,arrive | ,left:true none:true right:false, |
| a,i | ,ksd,ksd | depart,turn right,arrive | ,left:false none:true right:true, |
Scenario: Reverse Not Allowed
Given the node map
"""
n o
f - - e\- - g-j-m
d | |
a - 1 b/- - c-i-l
h k
"""
And the ways
| nodes | name | highway | oneway | turn:lanes:forward |
| abc | road | secondary | yes | left\|through\|right |
| cil | road | secondary | yes | |
| mjgef | road | secondary | yes | |
| bde | road | secondary_link | yes | |
| ngch | turn | secondary | yes | |
| kijo | turn | secondary | yes | |
When I route I should get
| waypoints | bearings | route | turns |
| 1,a | 90,2 180,180 | | |

View File

@ -827,16 +827,16 @@ Feature: Simple Turns
And the ways
| nodes | highway | oneway |
| abc | primary | no |
| db | primary | no |
| abc | primary | yes |
| bd | primary | yes |
| eb | primary | yes |
| fb | primary | no |
| bg | primary | no |
| bf | primary | yes |
| bg | primary | yes |
When I route I should get
| waypoints | route | turns |
| a,d | abc,db,db | depart,turn sharp right,arrive |
| a,f | abc,fb,fb | depart,turn right,arrive |
| a,d | abc,bd,bd | depart,turn sharp right,arrive |
| a,f | abc,bf,bf | depart,turn right,arrive |
Scenario: Right Turn Assignment Three Conflicting Turns with invalid - 3
Given the node map
@ -907,11 +907,10 @@ Feature: Simple Turns
Scenario: Turn Lane on Splitting up Road
Given the node map
"""
g f
h e c d
a b
g - - - f
\
. h - - e - - c - - d
a - - b _______/
i
"""

View File

@ -121,8 +121,7 @@ module.exports = function () {
return this.requestPath('match', params, callback);
};
this.extractInstructionList = (instructions, keyFinder, postfix) => {
postfix = postfix || null;
this.extractInstructionList = (instructions, keyFinder) => {
if (instructions) {
return instructions.legs.reduce((m, v) => m.concat(v.steps), [])
.map(keyFinder)
@ -152,8 +151,16 @@ module.exports = function () {
return this.extractInstructionList(instructions, s => s.destinations || '');
};
this.reverseBearing = (bearing) => {
if (bearing >= 180)
return bearing - 180.;
return bearing + 180;
};
this.bearingList = (instructions) => {
return this.extractInstructionList(instructions, s => s.maneuver.bearing_before + '->' + s.maneuver.bearing_after);
return this.extractInstructionList(instructions, s => ('in' in s.intersections[0] ? this.reverseBearing(s.intersections[0].bearings[s.intersections[0].in]) : 0)
+ '->' +
('out' in s.intersections[0] ? s.intersections[0].bearings[s.intersections[0].out] : 0));
};
this.annotationList = (instructions) => {

View File

@ -13,6 +13,8 @@
#include "util/exception.hpp"
#include "util/guidance/bearing_class.hpp"
#include "util/guidance/entry_class.hpp"
#include "util/guidance/turn_bearing.hpp"
#include "util/guidance/turn_lanes.hpp"
#include "util/integer_range.hpp"
#include "util/string_util.hpp"
#include "util/typedefs.hpp"
@ -173,6 +175,9 @@ class BaseDataFacade
virtual BearingClassID GetBearingClassID(const NodeID id) const = 0;
virtual util::guidance::TurnBearing PreTurnBearing(const EdgeID eid) const = 0;
virtual util::guidance::TurnBearing PostTurnBearing(const EdgeID eid) const = 0;
virtual util::guidance::BearingClass
GetBearingClass(const BearingClassID bearing_class_id) const = 0;

View File

@ -18,6 +18,7 @@
#include "storage/io.hpp"
#include "engine/geospatial_query.hpp"
#include "util/graph_loader.hpp"
#include "util/guidance/turn_bearing.hpp"
#include "util/guidance/turn_lanes.hpp"
#include "util/io.hpp"
#include "util/packed_vector.hpp"
@ -104,6 +105,9 @@ class InternalDataFacade final : public BaseDataFacade
util::ShM<BearingClassID, false>::vector m_bearing_class_id_table;
// entry class IDs by edge based egde
util::ShM<EntryClassID, false>::vector m_entry_class_id_list;
// bearings pre/post turn
util::ShM<util::guidance::TurnBearing, false>::vector m_pre_turn_bearing;
util::ShM<util::guidance::TurnBearing, false>::vector m_post_turn_bearing;
// the look-up table for entry classes. An entry class lists the possibility of entry for all
// available turns. For every turn, there is an associated entry class.
util::ShM<util::guidance::EntryClass, false>::vector m_entry_class_table;
@ -205,6 +209,8 @@ class InternalDataFacade final : public BaseDataFacade
m_lane_data_id.resize(number_of_edges);
m_travel_mode_list.resize(number_of_edges);
m_entry_class_id_list.resize(number_of_edges);
m_pre_turn_bearing.resize(number_of_edges);
m_post_turn_bearing.resize(number_of_edges);
extractor::OriginalEdgeData current_edge_data;
for (unsigned i = 0; i < number_of_edges; ++i)
@ -217,6 +223,8 @@ class InternalDataFacade final : public BaseDataFacade
m_lane_data_id[i] = current_edge_data.lane_data_id;
m_travel_mode_list[i] = current_edge_data.travel_mode;
m_entry_class_id_list[i] = current_edge_data.entry_classid;
m_pre_turn_bearing[i] = current_edge_data.pre_turn_bearing;
m_post_turn_bearing[i] = current_edge_data.post_turn_bearing;
}
}
@ -921,6 +929,15 @@ class InternalDataFacade final : public BaseDataFacade
return m_entry_class_id_list.at(eid);
}
util::guidance::TurnBearing PreTurnBearing(const EdgeID eid) const override final
{
return m_pre_turn_bearing.at(eid);
}
util::guidance::TurnBearing PostTurnBearing(const EdgeID eid) const override final
{
return m_post_turn_bearing.at(eid);
}
util::guidance::EntryClass GetEntryClass(const EntryClassID entry_class_id) const override final
{
return m_entry_class_table.at(entry_class_id);

View File

@ -18,6 +18,7 @@
#include "engine/geospatial_query.hpp"
#include "util/packed_vector.hpp"
#include "util/guidance/turn_bearing.hpp"
#include "util/range_table.hpp"
#include "util/rectangle.hpp"
#include "util/simple_logger.hpp"
@ -85,6 +86,8 @@ class SharedDataFacade final : public BaseDataFacade
util::ShM<LaneDataID, true>::vector m_lane_data_id;
util::ShM<extractor::guidance::TurnInstruction, true>::vector m_turn_instruction_list;
util::ShM<extractor::TravelMode, true>::vector m_travel_mode_list;
util::ShM<util::guidance::TurnBearing, true>::vector m_pre_turn_bearing;
util::ShM<util::guidance::TurnBearing, true>::vector m_post_turn_bearing;
util::ShM<char, true>::vector m_names_char_list;
util::ShM<unsigned, true>::vector m_name_begin_indices;
util::ShM<unsigned, true>::vector m_geometry_indices;
@ -104,11 +107,11 @@ class SharedDataFacade final : public BaseDataFacade
boost::filesystem::path file_index_path;
std::shared_ptr<util::RangeTable<16, true>> m_name_table;
// bearing classes by node based node
util::ShM<BearingClassID, true>::vector m_bearing_class_id_table;
// entry class IDs
util::ShM<EntryClassID, true>::vector m_entry_class_id_list;
// the look-up table for entry classes. An entry class lists the possibility of entry for all
// available turns. Such a class id is stored with every edge.
util::ShM<util::guidance::EntryClass, true>::vector m_entry_class_table;
@ -182,7 +185,7 @@ class SharedDataFacade final : public BaseDataFacade
void LoadNodeAndEdgeInformation()
{
auto coordinate_list_ptr = data_layout->GetBlockPtr<util::Coordinate>(
const auto coordinate_list_ptr = data_layout->GetBlockPtr<util::Coordinate>(
shared_memory, storage::SharedDataLayout::COORDINATE_LIST);
m_coordinate_list.reset(
coordinate_list_ptr,
@ -193,7 +196,7 @@ class SharedDataFacade final : public BaseDataFacade
BOOST_ASSERT(GetCoordinateOfNode(i).IsValid());
}
auto osmnodeid_list_ptr = data_layout->GetBlockPtr<std::uint64_t>(
const auto osmnodeid_list_ptr = data_layout->GetBlockPtr<std::uint64_t>(
shared_memory, storage::SharedDataLayout::OSM_NODE_ID_LIST);
m_osmnodeid_list.reset(
osmnodeid_list_ptr,
@ -202,26 +205,27 @@ class SharedDataFacade final : public BaseDataFacade
m_osmnodeid_list.set_number_of_entries(
data_layout->num_entries[storage::SharedDataLayout::COORDINATE_LIST]);
auto travel_mode_list_ptr = data_layout->GetBlockPtr<extractor::TravelMode>(
const auto travel_mode_list_ptr = data_layout->GetBlockPtr<extractor::TravelMode>(
shared_memory, storage::SharedDataLayout::TRAVEL_MODE);
util::ShM<extractor::TravelMode, true>::vector travel_mode_list(
travel_mode_list_ptr, data_layout->num_entries[storage::SharedDataLayout::TRAVEL_MODE]);
m_travel_mode_list = std::move(travel_mode_list);
auto lane_data_id_ptr = data_layout->GetBlockPtr<LaneDataID>(
const auto lane_data_id_ptr = data_layout->GetBlockPtr<LaneDataID>(
shared_memory, storage::SharedDataLayout::LANE_DATA_ID);
util::ShM<LaneDataID, true>::vector lane_data_id(
lane_data_id_ptr, data_layout->num_entries[storage::SharedDataLayout::LANE_DATA_ID]);
m_lane_data_id = std::move(lane_data_id);
auto lane_tupel_id_pair_ptr = data_layout->GetBlockPtr<util::guidance::LaneTupleIdPair>(
shared_memory, storage::SharedDataLayout::TURN_LANE_DATA);
const auto lane_tupel_id_pair_ptr =
data_layout->GetBlockPtr<util::guidance::LaneTupleIdPair>(
shared_memory, storage::SharedDataLayout::TURN_LANE_DATA);
util::ShM<util::guidance::LaneTupleIdPair, true>::vector lane_tupel_id_pair(
lane_tupel_id_pair_ptr,
data_layout->num_entries[storage::SharedDataLayout::TURN_LANE_DATA]);
m_lane_tupel_id_pairs = std::move(lane_tupel_id_pair);
auto turn_instruction_list_ptr =
const auto turn_instruction_list_ptr =
data_layout->GetBlockPtr<extractor::guidance::TurnInstruction>(
shared_memory, storage::SharedDataLayout::TURN_INSTRUCTION);
util::ShM<extractor::guidance::TurnInstruction, true>::vector turn_instruction_list(
@ -229,18 +233,32 @@ class SharedDataFacade final : public BaseDataFacade
data_layout->num_entries[storage::SharedDataLayout::TURN_INSTRUCTION]);
m_turn_instruction_list = std::move(turn_instruction_list);
auto name_id_list_ptr = data_layout->GetBlockPtr<unsigned>(
const auto name_id_list_ptr = data_layout->GetBlockPtr<unsigned>(
shared_memory, storage::SharedDataLayout::NAME_ID_LIST);
util::ShM<unsigned, true>::vector name_id_list(
name_id_list_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_ID_LIST]);
m_name_ID_list = std::move(name_id_list);
auto entry_class_id_list_ptr = data_layout->GetBlockPtr<EntryClassID>(
const auto entry_class_id_list_ptr = data_layout->GetBlockPtr<EntryClassID>(
shared_memory, storage::SharedDataLayout::ENTRY_CLASSID);
typename util::ShM<EntryClassID, true>::vector entry_class_id_list(
entry_class_id_list_ptr,
data_layout->num_entries[storage::SharedDataLayout::ENTRY_CLASSID]);
m_entry_class_id_list = std::move(entry_class_id_list);
const auto pre_turn_bearing_ptr = data_layout->GetBlockPtr<util::guidance::TurnBearing>(
shared_memory, storage::SharedDataLayout::PRE_TURN_BEARING);
typename util::ShM<util::guidance::TurnBearing, true>::vector pre_turn_bearing(
pre_turn_bearing_ptr,
data_layout->num_entries[storage::SharedDataLayout::PRE_TURN_BEARING]);
m_pre_turn_bearing = std::move(pre_turn_bearing);
const auto post_turn_bearing_ptr = data_layout->GetBlockPtr<util::guidance::TurnBearing>(
shared_memory, storage::SharedDataLayout::POST_TURN_BEARING);
typename util::ShM<util::guidance::TurnBearing, true>::vector post_turn_bearing(
post_turn_bearing_ptr,
data_layout->num_entries[storage::SharedDataLayout::POST_TURN_BEARING]);
m_post_turn_bearing = std::move(post_turn_bearing);
}
void LoadViaNodeList()
@ -931,6 +949,15 @@ class SharedDataFacade final : public BaseDataFacade
return m_entry_class_id_list.at(eid);
}
util::guidance::TurnBearing PreTurnBearing(const EdgeID eid) const override final
{
return m_pre_turn_bearing.at(eid);
}
util::guidance::TurnBearing PostTurnBearing(const EdgeID eid) const override final
{
return m_post_turn_bearing.at(eid);
}
util::guidance::EntryClass GetEntryClass(const EntryClassID entry_class_id) const override final
{
return m_entry_class_table.at(entry_class_id);

View File

@ -372,8 +372,10 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
int forward_offset = 0, forward_weight = 0;
int reverse_offset = 0, reverse_weight = 0;
const std::vector<EdgeWeight> forward_weight_vector = datafacade.GetUncompressedForwardWeights(data.packed_geometry_id);
const std::vector<EdgeWeight> reverse_weight_vector = datafacade.GetUncompressedReverseWeights(data.packed_geometry_id);
const std::vector<EdgeWeight> forward_weight_vector =
datafacade.GetUncompressedForwardWeights(data.packed_geometry_id);
const std::vector<EdgeWeight> reverse_weight_vector =
datafacade.GetUncompressedReverseWeights(data.packed_geometry_id);
for (std::size_t i = 0; i < data.fwd_segment_position; i++)
{
@ -383,8 +385,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
BOOST_ASSERT(data.fwd_segment_position < reverse_weight_vector.size());
for (std::size_t i = 0;
i < reverse_weight_vector.size() - data.fwd_segment_position - 1;
for (std::size_t i = 0; i < reverse_weight_vector.size() - data.fwd_segment_position - 1;
i++)
{
reverse_offset += reverse_weight_vector[i];
@ -469,16 +470,18 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
bool forward_edge_valid = false;
bool reverse_edge_valid = false;
const std::vector<EdgeWeight> forward_weight_vector = datafacade.GetUncompressedForwardWeights(segment.data.packed_geometry_id);
const std::vector<EdgeWeight> forward_weight_vector =
datafacade.GetUncompressedForwardWeights(segment.data.packed_geometry_id);
if (forward_weight_vector[segment.data.fwd_segment_position] != INVALID_EDGE_WEIGHT)
{
forward_edge_valid = segment.data.forward_segment_id.enabled;
}
const std::vector<EdgeWeight> reverse_weight_vector = datafacade.GetUncompressedReverseWeights(segment.data.packed_geometry_id);
if (reverse_weight_vector[reverse_weight_vector.size() -
segment.data.fwd_segment_position - 1] != INVALID_EDGE_WEIGHT)
const std::vector<EdgeWeight> reverse_weight_vector =
datafacade.GetUncompressedReverseWeights(segment.data.packed_geometry_id);
if (reverse_weight_vector[reverse_weight_vector.size() - segment.data.fwd_segment_position -
1] != INVALID_EDGE_WEIGHT)
{
reverse_edge_valid = segment.data.reverse_segment_id.enabled;
}

View File

@ -47,8 +47,8 @@ inline LegGeometry assembleGeometry(const datafacade::BaseDataFacade &facade,
// TODO: check if this was traversed in reverse?
const std::vector<NodeID> source_geometry =
facade.GetUncompressedForwardGeometry(source_node.packed_geometry_id);
geometry.osm_node_ids.push_back(facade.GetOSMNodeIDOfNode(
source_geometry[source_node.fwd_segment_position]));
geometry.osm_node_ids.push_back(
facade.GetOSMNodeIDOfNode(source_geometry[source_node.fwd_segment_position]));
auto cumulative_distance = 0.;
auto current_distance = 0.;

View File

@ -33,8 +33,6 @@ namespace detail
{
std::pair<short, short> getDepartBearings(const LegGeometry &leg_geometry);
std::pair<short, short> getArriveBearings(const LegGeometry &leg_geometry);
std::pair<short, short> getIntermediateBearings(const LegGeometry &leg_geometry,
const std::size_t segment_index);
} // ns detail
inline std::vector<RouteStep> assembleSteps(const datafacade::BaseDataFacade &facade,
@ -132,12 +130,14 @@ inline std::vector<RouteStep> assembleSteps(const datafacade::BaseDataFacade &fa
step_name_id = target_node.name_id;
}
bearings = detail::getIntermediateBearings(leg_geometry, segment_index);
// extract bearings
bearings = std::make_pair<std::uint16_t, std::uint16_t>(
path_point.pre_turn_bearing.Get(), path_point.post_turn_bearing.Get());
const auto entry_class = facade.GetEntryClass(path_point.entry_classid);
const auto bearing_class =
facade.GetBearingClass(facade.GetBearingClassID(path_point.turn_via_node));
intersection.in = bearing_class.findMatchingBearing(
util::bearing::reverseBearing(bearings.first));
auto bearing_data = bearing_class.getAvailableBearings();
intersection.in = bearing_class.findMatchingBearing(bearings.first);
intersection.out = bearing_class.findMatchingBearing(bearings.second);
intersection.location = facade.GetCoordinateOfNode(path_point.turn_via_node);
intersection.bearings.clear();
@ -155,8 +155,10 @@ inline std::vector<RouteStep> assembleSteps(const datafacade::BaseDataFacade &fa
{
intersection.entry.push_back(entry_class.allowsEntry(idx));
}
std::int16_t bearing_in_driving_direction =
util::bearing::reverseBearing(std::round(bearings.first));
maneuver = {intersection.location,
bearings.first,
bearing_in_driving_direction,
bearings.second,
path_point.turn_instruction,
WaypointType::None,

View File

@ -5,6 +5,7 @@
#include "extractor/travel_mode.hpp"
#include "engine/phantom_node.hpp"
#include "osrm/coordinate.hpp"
#include "util/guidance/turn_bearing.hpp"
#include "util/guidance/turn_lanes.hpp"
#include "util/typedefs.hpp"
@ -36,6 +37,11 @@ struct PathData
// Source of the speed value on this road segment
DatasourceID datasource_id;
// bearing (as seen from the intersection) pre-turn
util::guidance::TurnBearing pre_turn_bearing;
// bearing (as seen from the intersection) post-turn
util::guidance::TurnBearing post_turn_bearing;
};
struct InternalRouteResult

View File

@ -64,10 +64,10 @@ struct PhantomNode
: forward_segment_id(forward_segment_id), reverse_segment_id(reverse_segment_id),
name_id(name_id), forward_weight(forward_weight), reverse_weight(reverse_weight),
forward_offset(forward_offset), reverse_offset(reverse_offset),
packed_geometry_id(packed_geometry_id_),
component{component_id, is_tiny_component}, location(std::move(location)),
input_location(std::move(input_location)), fwd_segment_position(fwd_segment_position),
forward_travel_mode(forward_travel_mode), backward_travel_mode(backward_travel_mode)
packed_geometry_id(packed_geometry_id_), component{component_id, is_tiny_component},
location(std::move(location)), input_location(std::move(input_location)),
fwd_segment_position(fwd_segment_position), forward_travel_mode(forward_travel_mode),
backward_travel_mode(backward_travel_mode)
{
}
@ -76,9 +76,8 @@ struct PhantomNode
reverse_segment_id{SPECIAL_SEGMENTID, false},
name_id(std::numeric_limits<unsigned>::max()), forward_weight(INVALID_EDGE_WEIGHT),
reverse_weight(INVALID_EDGE_WEIGHT), forward_offset(0), reverse_offset(0),
packed_geometry_id(SPECIAL_GEOMETRYID),
component{INVALID_COMPONENTID, false}, fwd_segment_position(0),
forward_travel_mode(TRAVEL_MODE_INACCESSIBLE),
packed_geometry_id(SPECIAL_GEOMETRYID), component{INVALID_COMPONENTID, false},
fwd_segment_position(0), forward_travel_mode(TRAVEL_MODE_INACCESSIBLE),
backward_travel_mode(TRAVEL_MODE_INACCESSIBLE)
{
}

View File

@ -6,6 +6,7 @@
#include "engine/internal_route_result.hpp"
#include "engine/search_engine_data.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/guidance/turn_bearing.hpp"
#include "util/typedefs.hpp"
#include <boost/assert.hpp>
@ -250,81 +251,83 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
{
id_vector = facade.GetUncompressedForwardGeometry(geometry_index.id);
weight_vector = facade.GetUncompressedForwardWeights(geometry_index.id);
datasource_vector =
facade.GetUncompressedForwardDatasources(geometry_index.id);
datasource_vector = facade.GetUncompressedForwardDatasources(geometry_index.id);
}
else
{
id_vector = facade.GetUncompressedReverseGeometry(geometry_index.id);
weight_vector = facade.GetUncompressedReverseWeights(geometry_index.id);
datasource_vector =
facade.GetUncompressedReverseDatasources(geometry_index.id);
datasource_vector = facade.GetUncompressedReverseDatasources(geometry_index.id);
}
BOOST_ASSERT(id_vector.size() > 0);
BOOST_ASSERT(weight_vector.size() > 0);
BOOST_ASSERT(datasource_vector.size() > 0);
const auto total_weight =
std::accumulate(weight_vector.begin(), weight_vector.end(), 0);
const auto total_weight =
std::accumulate(weight_vector.begin(), weight_vector.end(), 0);
BOOST_ASSERT(weight_vector.size() == id_vector.size() - 1);
const bool is_first_segment = unpacked_path.empty();
BOOST_ASSERT(weight_vector.size() == id_vector.size() - 1);
const bool is_first_segment = unpacked_path.empty();
const std::size_t start_index =
(is_first_segment
? ((start_traversed_in_reverse)
? weight_vector.size() -
phantom_node_pair.source_phantom.fwd_segment_position - 1
: phantom_node_pair.source_phantom.fwd_segment_position)
: 0);
const std::size_t end_index = weight_vector.size();
const std::size_t start_index =
(is_first_segment
? ((start_traversed_in_reverse)
? weight_vector.size() -
phantom_node_pair.source_phantom.fwd_segment_position - 1
: phantom_node_pair.source_phantom.fwd_segment_position)
: 0);
const std::size_t end_index = weight_vector.size();
BOOST_ASSERT(start_index >= 0);
BOOST_ASSERT(start_index < end_index);
for (std::size_t segment_idx = start_index; segment_idx < end_index; ++segment_idx)
{
unpacked_path.push_back(
PathData{id_vector[segment_idx + 1],
name_index,
weight_vector[segment_idx],
extractor::guidance::TurnInstruction::NO_TURN(),
{{0, INVALID_LANEID}, INVALID_LANE_DESCRIPTIONID},
travel_mode,
INVALID_ENTRY_CLASSID,
datasource_vector[segment_idx]});
}
BOOST_ASSERT(unpacked_path.size() > 0);
if (facade.hasLaneData(edge_data.id))
unpacked_path.back().lane_data = facade.GetLaneData(edge_data.id);
BOOST_ASSERT(start_index >= 0);
BOOST_ASSERT(start_index < end_index);
for (std::size_t segment_idx = start_index; segment_idx < end_index; ++segment_idx)
{
unpacked_path.push_back(
PathData{id_vector[segment_idx + 1],
name_index,
weight_vector[segment_idx],
extractor::guidance::TurnInstruction::NO_TURN(),
{{0, INVALID_LANEID}, INVALID_LANE_DESCRIPTIONID},
travel_mode,
INVALID_ENTRY_CLASSID,
datasource_vector[segment_idx],
util::guidance::TurnBearing(0),
util::guidance::TurnBearing(0)});
}
BOOST_ASSERT(unpacked_path.size() > 0);
if (facade.hasLaneData(edge_data.id))
unpacked_path.back().lane_data = facade.GetLaneData(edge_data.id);
unpacked_path.back().entry_classid = facade.GetEntryClassID(edge_data.id);
unpacked_path.back().turn_instruction = turn_instruction;
unpacked_path.back().duration_until_turn += (edge_data.distance - total_weight);
});
unpacked_path.back().entry_classid = facade.GetEntryClassID(edge_data.id);
unpacked_path.back().turn_instruction = turn_instruction;
unpacked_path.back().duration_until_turn += (edge_data.distance - total_weight);
unpacked_path.back().pre_turn_bearing = facade.PreTurnBearing(edge_data.id);
unpacked_path.back().post_turn_bearing = facade.PostTurnBearing(edge_data.id);
});
std::size_t start_index = 0, end_index = 0;
std::vector<unsigned> id_vector;
std::vector<EdgeWeight> weight_vector;
std::vector<DatasourceID> datasource_vector;
const bool is_local_path = (phantom_node_pair.source_phantom.packed_geometry_id ==
phantom_node_pair.target_phantom.packed_geometry_id) &&
unpacked_path.empty();
std::size_t start_index = 0, end_index = 0;
std::vector<unsigned> id_vector;
std::vector<EdgeWeight> weight_vector;
std::vector<DatasourceID> datasource_vector;
const bool is_local_path = (phantom_node_pair.source_phantom.packed_geometry_id ==
phantom_node_pair.target_phantom.packed_geometry_id) &&
unpacked_path.empty();
if (target_traversed_in_reverse)
{
id_vector = facade.GetUncompressedReverseGeometry(
phantom_node_pair.target_phantom.packed_geometry_id);
if (target_traversed_in_reverse)
{
id_vector = facade.GetUncompressedReverseGeometry(
phantom_node_pair.target_phantom.packed_geometry_id);
weight_vector = facade.GetUncompressedReverseWeights(
phantom_node_pair.target_phantom.packed_geometry_id);
weight_vector = facade.GetUncompressedReverseWeights(
phantom_node_pair.target_phantom.packed_geometry_id);
datasource_vector = facade.GetUncompressedReverseDatasources(
phantom_node_pair.target_phantom.packed_geometry_id);
datasource_vector = facade.GetUncompressedReverseDatasources(
phantom_node_pair.target_phantom.packed_geometry_id);
if (is_local_path)
{
start_index =
weight_vector.size() - phantom_node_pair.source_phantom.fwd_segment_position - 1;
start_index = weight_vector.size() -
phantom_node_pair.source_phantom.fwd_segment_position - 1;
}
end_index =
weight_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position - 1;
@ -354,7 +357,8 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
// t: fwd_segment 3
// -> (U, v), (v, w), (w, x)
// note that (x, t) is _not_ included but needs to be added later.
for (std::size_t segment_idx = start_index; segment_idx != end_index; (start_index < end_index ? ++segment_idx : --segment_idx))
for (std::size_t segment_idx = start_index; segment_idx != end_index;
(start_index < end_index ? ++segment_idx : --segment_idx))
{
BOOST_ASSERT(segment_idx < id_vector.size() - 1);
BOOST_ASSERT(phantom_node_pair.target_phantom.forward_travel_mode > 0);
@ -367,7 +371,9 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
target_traversed_in_reverse ? phantom_node_pair.target_phantom.backward_travel_mode
: phantom_node_pair.target_phantom.forward_travel_mode,
INVALID_ENTRY_CLASSID,
datasource_vector[segment_idx]});
datasource_vector[segment_idx],
util::guidance::TurnBearing(0),
util::guidance::TurnBearing(0)});
}
if (unpacked_path.size() > 0)

View File

@ -22,8 +22,7 @@ struct EdgeBasedNode
EdgeBasedNode()
: forward_segment_id{SPECIAL_SEGMENTID, false},
reverse_segment_id{SPECIAL_SEGMENTID, false}, u(SPECIAL_NODEID), v(SPECIAL_NODEID),
name_id(0), packed_geometry_id(SPECIAL_GEOMETRYID),
component{INVALID_COMPONENTID, false},
name_id(0), packed_geometry_id(SPECIAL_GEOMETRYID), component{INVALID_COMPONENTID, false},
fwd_segment_position(std::numeric_limits<unsigned short>::max()),
forward_travel_mode(TRAVEL_MODE_INACCESSIBLE),
backward_travel_mode(TRAVEL_MODE_INACCESSIBLE)

View File

@ -34,7 +34,7 @@ const int constexpr MAX_SLIPROAD_THRESHOLD = 250;
// Road priorities give an idea of how obvious a turn is. If two priorities differ greatly (e.g.
// service road over a primary road, the better priority can be seen as obvious due to its road
// category).
const double constexpr PRIORITY_DISTINCTION_FACTOR = 2.0;
const double constexpr PRIORITY_DISTINCTION_FACTOR = 1.75;
} // namespace guidance
} // namespace extractor

View File

@ -0,0 +1,203 @@
#ifndef OSRM_EXTRACTOR_COORDINATE_EXTRACTOR_HPP_
#define OSRM_EXTRACTOR_COORDINATE_EXTRACTOR_HPP_
#include <vector>
#include "extractor/query_node.hpp"
#include "util/coordinate.hpp"
#include "util/coordinate_calculation.hpp"
#include "extractor/compressed_edge_container.hpp"
#include "util/node_based_graph.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
class CoordinateExtractor
{
public:
CoordinateExtractor(const util::NodeBasedDynamicGraph &node_based_graph,
const extractor::CompressedEdgeContainer &compressed_geometries,
const std::vector<extractor::QueryNode> &node_coordinates);
/* Find a interpolated coordinate a long the compressed geometries. The desired coordinate
* should be in a certain distance. This method is dedicated to find representative coordinates
* at turns.
*/
util::Coordinate GetCoordinateAlongRoad(const NodeID intersection_node,
const EdgeID turn_edge,
const bool traversed_in_reverse,
const NodeID to_node,
const std::uint8_t number_of_in_lanes) const;
// instead of finding only a single coordinate, we can also list all coordinates along a road.
std::vector<util::Coordinate> GetCoordinatesAlongRoad(const NodeID intersection_node,
const EdgeID turn_edge,
const bool traversed_in_reverse,
const NodeID to_node) const;
/* When extracting the coordinates, we first extract all coordinates. We don't care about most
* of them, though.
*
* Our very first step trims the coordinates to a saller set, close to the intersection.. The
* idea here is to filter all coordinates at the end of the road and consider only the formi
* close to the intersection:
*
* a -------------- v ----------.
* .
* .
* .
* b
*
* For calculating the turn angle for the intersection at `a`, we do not care about the turn
* between `v` and `b`. This calculation trims the coordinates to the ones immediately at the
* intersection.
*/
std::vector<util::Coordinate> TrimCoordinatesToLength(std::vector<util::Coordinate> coordinates,
const double desired_length) const;
/* when looking at a set of coordinates, this function allows trimming the vector to a smaller,
* only containing coordinates up to a given distance along the path. The last coordinate might
* be interpolated
*/
std::vector<util::Coordinate>
TrimCoordinatesByLengthFront(std::vector<util::Coordinate> coordinates,
const double desired_length) const;
/*
* to correct for the initial offset, we move the lookahead coordinate close
* to the original road. We do so by subtracting the difference between the
* turn coordinate and the offset coordinate from the lookahead coordinge:
*
* a ------ b ------ c
* |
* d
* \
* \
* e
*
* is converted to:
*
* a ------ b ------ c
* \
* \
* e
*
* for fixpoint `b`, vector_base `d` and vector_head `e`
*/
util::Coordinate GetCorrectedCoordinate(const util::Coordinate fixpoint,
const util::Coordinate vector_base,
const util::Coordinate vector_head) const;
/* generate a uniform vector of coordinates in same range distances
*
* Turns:
* x ------------ x -- x - x
*
* Into:
* x -- x -- x -- x -- x - x
*/
std::vector<util::Coordinate>
SampleCoordinates(const std::vector<util::Coordinate> &coordinates,
const double length,
const double rate) const;
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const extractor::CompressedEdgeContainer &compressed_geometries;
const std::vector<extractor::QueryNode> &node_coordinates;
double ComputeInterpolationFactor(const double desired_distance,
const double distance_to_first,
const double distance_to_second) const;
std::pair<util::Coordinate, util::Coordinate>
RegressionLine(const std::vector<util::Coordinate> &coordinates) const;
/* In an ideal world, the road would only have two coordinates if it goes mainly straigt. Since
* OSM is operating on noisy data, we have some variations going straight.
*
* b d
* a ---------------------------------------------- e
* c
*
* The road from a-e offers a lot of variation, even though it is mostly straight. Here we
* calculate the distances of all nodes in between to the straight line between a and e. If the
* distances inbetween are small, we assume a straight road. To calculate these distances, we
* don't use the coordinates of the road itself but our just calculated regression vector
*/
double GetMaxDeviation(std::vector<util::Coordinate>::const_iterator range_begin,
const std::vector<util::Coordinate>::const_iterator &range_end,
const util::Coordinate straight_begin,
const util::Coordinate straight_end) const;
/*
* the curve is still best described as looking at the very first vector for the turn angle.
* Consider:
*
* |
* a - 1
* | o
* | 2
* | o
* | 3
* | o
* | 4
*
* The turn itself from a-1 would be considered as a 90 degree turn, even though the road is
* taking a turn later.
* In this situaiton we return the very first coordinate, describing the road just at the
* turn.
* As an added benefit, we get a straight turn at a curved road:
*
* o b o
* o o
* o o
* o o
* o o
* a c
*
* The turn from a-b to b-c is straight. With every vector we go further down the road, the
* turn
* angle would get stronger. Therefore we consider the very first coordinate as our best
* choice
*/
bool IsCurve(const std::vector<util::Coordinate> &coordinates,
const std::vector<double> &segment_distances,
const double segment_length,
const double considered_lane_width,
const util::NodeBasedEdgeData &edge_data) const;
/*
* If the very first coordinate is within lane offsets and the rest offers a near straight line,
* we use an offset coordinate.
*
* ----------------------------------------
*
* ----------------------------------------
* a -
* ----------------------------------------
* \
* ----------------------------------------
* \
* b --------------------c
*
* Will be considered a very slight turn, instead of the near 90 degree turn we see right here.
*/
bool IsDirectOffset(const std::vector<util::Coordinate> &coordinates,
const std::size_t straight_index,
const double straight_distance,
const double segment_length,
const std::vector<double> &segment_distances,
const std::uint8_t considered_lanes) const;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_COORDINATE_EXTRACTOR_HPP_

View File

@ -21,6 +21,7 @@ struct TurnOperation final
{
EdgeID eid;
double angle;
double bearing;
TurnInstruction instruction;
LaneDataID lane_data_id;
};

View File

@ -2,6 +2,7 @@
#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/query_node.hpp"
#include "extractor/restriction_map.hpp"
@ -51,7 +52,7 @@ class IntersectionGenerator
const RestrictionMap &restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const std::vector<QueryNode> &node_info_list;
const CompressedEdgeContainer &compressed_edge_container;
const CoordinateExtractor coordinate_extractor;
// Check for restrictions/barriers and generate a list of valid and invalid turns present at
// the

View File

@ -59,21 +59,24 @@ class RoadClassification
// (difference <=1), we can see the road as a fork. Else one of the road classes is seen as
// obvious choice
RoadPriorityClass::Enum road_priority_class : 5;
// the number of lanes in the road
std::uint8_t number_of_lanes;
public:
// default construction
RoadClassification()
: motorway_class(0), link_class(0), may_be_ignored(1),
road_priority_class(RoadPriorityClass::CONNECTIVITY)
road_priority_class(RoadPriorityClass::CONNECTIVITY), number_of_lanes(0)
{
}
RoadClassification(bool motorway_class,
bool link_class,
bool may_be_ignored,
RoadPriorityClass::Enum road_priority_class)
RoadPriorityClass::Enum road_priority_class,
std::uint8_t number_of_lanes)
: motorway_class(motorway_class), link_class(link_class), may_be_ignored(may_be_ignored),
road_priority_class(road_priority_class)
road_priority_class(road_priority_class), number_of_lanes(number_of_lanes)
{
}
@ -88,6 +91,9 @@ class RoadClassification
bool IsLowPriorityRoadClass() const { return (0 != may_be_ignored); }
void SetLowPriorityFlag(const bool new_value) { may_be_ignored = new_value; }
std::uint8_t GetNumberOfLanes() const { return number_of_lanes; }
void SetNumberOfLanes(const std::uint8_t new_value) { number_of_lanes = new_value; }
std::uint32_t GetPriority() const { return static_cast<std::uint32_t>(road_priority_class); }
RoadPriorityClass::Enum GetClass() const { return road_priority_class; }
@ -112,8 +118,8 @@ class RoadClassification
#pragma pack(pop)
static_assert(
sizeof(RoadClassification) == 1,
"Road Classification should fit a byte. Increasing this has a severe impact on memory.");
sizeof(RoadClassification) == 2,
"Road Classification should fit two bytes. Increasing this has a severe impact on memory.");
inline bool canBeSeenAsFork(const RoadClassification first, const RoadClassification second)
{

View File

@ -2,6 +2,7 @@
#define OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_
#include "extractor/compressed_edge_container.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/intersection_handler.hpp"
@ -87,6 +88,8 @@ class RoundaboutHandler : public IntersectionHandler
const CompressedEdgeContainer &compressed_edge_container;
const ProfileProperties &profile_properties;
const CoordinateExtractor coordinate_extractor;
};
} // namespace guidance

View File

@ -7,12 +7,15 @@
#include "util/coordinate_calculation.hpp"
#include "util/guidance/toolkit.hpp"
#include "util/guidance/turn_lanes.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
#include "extractor/compressed_edge_container.hpp"
#include "extractor/query_node.hpp"
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/road_classification.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "engine/guidance/route_step.hpp"
@ -24,6 +27,7 @@
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/functional/hash.hpp>
@ -44,113 +48,6 @@ using util::guidance::angularDeviation;
using util::guidance::entersRoundabout;
using util::guidance::leavesRoundabout;
namespace detail
{
const constexpr double DESIRED_SEGMENT_LENGTH = 10.0;
template <typename IteratorType>
util::Coordinate
getCoordinateFromCompressedRange(util::Coordinate current_coordinate,
const IteratorType compressed_geometry_begin,
const IteratorType compressed_geometry_end,
const util::Coordinate final_coordinate,
const std::vector<extractor::QueryNode> &query_nodes)
{
const auto extractCoordinateFromNode =
[](const extractor::QueryNode &node) -> util::Coordinate {
return {node.lon, node.lat};
};
double distance_to_current_coordinate = 0;
double distance_to_next_coordinate = 0;
// get the length that is missing from the current segment to reach DESIRED_SEGMENT_LENGTH
const auto getFactor = [](const double first_distance, const double second_distance) {
BOOST_ASSERT(first_distance < detail::DESIRED_SEGMENT_LENGTH);
double segment_length = second_distance - first_distance;
BOOST_ASSERT(segment_length > 0);
BOOST_ASSERT(second_distance >= detail::DESIRED_SEGMENT_LENGTH);
double missing_distance = detail::DESIRED_SEGMENT_LENGTH - first_distance;
return std::max(0., std::min(missing_distance / segment_length, 1.0));
};
for (auto compressed_geometry_itr = compressed_geometry_begin;
compressed_geometry_itr != compressed_geometry_end;
++compressed_geometry_itr)
{
const auto next_coordinate =
extractCoordinateFromNode(query_nodes[compressed_geometry_itr->node_id]);
distance_to_next_coordinate =
distance_to_current_coordinate +
util::coordinate_calculation::haversineDistance(current_coordinate, next_coordinate);
// reached point where coordinates switch between
if (distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH)
return util::coordinate_calculation::interpolateLinear(
getFactor(distance_to_current_coordinate, distance_to_next_coordinate),
current_coordinate,
next_coordinate);
// prepare for next iteration
current_coordinate = next_coordinate;
distance_to_current_coordinate = distance_to_next_coordinate;
}
distance_to_next_coordinate =
distance_to_current_coordinate +
util::coordinate_calculation::haversineDistance(current_coordinate, final_coordinate);
// reached point where coordinates switch between
if (distance_to_current_coordinate < detail::DESIRED_SEGMENT_LENGTH &&
distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH)
return util::coordinate_calculation::interpolateLinear(
getFactor(distance_to_current_coordinate, distance_to_next_coordinate),
current_coordinate,
final_coordinate);
else
return final_coordinate;
}
} // namespace detail
// Finds a (potentially interpolated) coordinate that is DESIRED_SEGMENT_LENGTH away
// from the start of an edge
inline util::Coordinate
getRepresentativeCoordinate(const NodeID from_node,
const NodeID to_node,
const EdgeID via_edge_id,
const bool traverse_in_reverse,
const extractor::CompressedEdgeContainer &compressed_geometries,
const std::vector<extractor::QueryNode> &query_nodes)
{
const auto extractCoordinateFromNode =
[](const extractor::QueryNode &node) -> util::Coordinate {
return {node.lon, node.lat};
};
// Uncompressed roads are simple, return the coordinate at the end
if (!compressed_geometries.HasZippedEntryForForwardID(via_edge_id) && !compressed_geometries.HasZippedEntryForReverseID(via_edge_id))
{
return extractCoordinateFromNode(traverse_in_reverse ? query_nodes[from_node]
: query_nodes[to_node]);
}
else
{
const auto &geometry = compressed_geometries.GetBucketReference(via_edge_id);
const auto base_node_id = (traverse_in_reverse) ? to_node : from_node;
const auto base_coordinate = extractCoordinateFromNode(query_nodes[base_node_id]);
const auto final_node = (traverse_in_reverse) ? from_node : to_node;
const auto final_coordinate = extractCoordinateFromNode(query_nodes[final_node]);
if (traverse_in_reverse)
return detail::getCoordinateFromCompressedRange(
base_coordinate, geometry.rbegin(), geometry.rend(), final_coordinate, query_nodes);
else
return detail::getCoordinateFromCompressedRange(
base_coordinate, geometry.begin(), geometry.end(), final_coordinate, query_nodes);
}
}
// To simplify handling of Left/Right hand turns, we can mirror turns and write an intersection
// handler only for one side. The mirror function turns a left-hand turn in a equivalent right-hand
// turn and vice versa.
@ -313,6 +210,78 @@ auto inline lanesToTheRight(const engine::guidance::RouteStep &step)
return boost::make_iterator_range(description.end() - num_lanes_right, description.end());
}
inline std::uint8_t getLaneCountAtIntersection(const NodeID intersection_node,
const util::NodeBasedDynamicGraph &node_based_graph)
{
std::uint8_t lanes = 0;
for (const EdgeID onto_edge : node_based_graph.GetAdjacentEdgeRange(intersection_node))
lanes = std::max(
lanes, node_based_graph.GetEdgeData(onto_edge).road_classification.GetNumberOfLanes());
return lanes;
}
inline bool obviousByRoadClass(const RoadClassification in_classification,
const RoadClassification obvious_candidate,
const RoadClassification compare_candidate)
{
const bool has_high_priority = PRIORITY_DISTINCTION_FACTOR * obvious_candidate.GetPriority() <
compare_candidate.GetPriority();
const bool continues_on_same_class = in_classification == obvious_candidate;
return (has_high_priority && continues_on_same_class) ||
(!obvious_candidate.IsLowPriorityRoadClass() &&
!in_classification.IsLowPriorityRoadClass() &&
compare_candidate.IsLowPriorityRoadClass());
}
/* We use the sum of least squares to calculate a linear regression through our
* coordinates.
* This regression gives a good idea of how the road can be perceived and corrects for
* initial and final corrections
*/
inline std::pair<util::Coordinate, util::Coordinate>
leastSquareRegression(const std::vector<util::Coordinate> &coordinates)
{
BOOST_ASSERT(coordinates.size() >= 2);
double sum_lon = 0, sum_lat = 0, sum_lon_lat = 0, sum_lon_lon = 0;
double min_lon = static_cast<double>(toFloating(coordinates.front().lon));
double max_lon = static_cast<double>(toFloating(coordinates.front().lon));
for (const auto coord : coordinates)
{
min_lon = std::min(min_lon, static_cast<double>(toFloating(coord.lon)));
max_lon = std::max(max_lon, static_cast<double>(toFloating(coord.lon)));
sum_lon += static_cast<double>(toFloating(coord.lon));
sum_lon_lon +=
static_cast<double>(toFloating(coord.lon)) * static_cast<double>(toFloating(coord.lon));
sum_lat += static_cast<double>(toFloating(coord.lat));
sum_lon_lat +=
static_cast<double>(toFloating(coord.lon)) * static_cast<double>(toFloating(coord.lat));
}
const auto dividend = coordinates.size() * sum_lon_lat - sum_lon * sum_lat;
const auto divisor = coordinates.size() * sum_lon_lon - sum_lon * sum_lon;
if (std::abs(divisor) < std::numeric_limits<double>::epsilon())
return std::make_pair(coordinates.front(), coordinates.back());
// slope of the regression line
const auto slope = dividend / divisor;
const auto intercept = (sum_lat - slope * sum_lon) / coordinates.size();
const auto GetLatAtLon = [intercept,
slope](const util::FloatLongitude longitude) -> util::FloatLatitude {
return {intercept + slope * static_cast<double>((longitude))};
};
const util::Coordinate regression_first = {
toFixed(util::FloatLongitude{min_lon - 1}),
toFixed(util::FloatLatitude(GetLatAtLon(util::FloatLongitude{min_lon - 1})))};
const util::Coordinate regression_end = {
toFixed(util::FloatLongitude{max_lon + 1}),
toFixed(util::FloatLatitude(GetLatAtLon(util::FloatLongitude{max_lon + 1})))};
return {regression_first, regression_end};
}
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -25,11 +25,7 @@ namespace guidance
{
std::pair<util::guidance::EntryClass, util::guidance::BearingClass>
classifyIntersection(NodeID nid,
const Intersection &intersection,
const util::NodeBasedDynamicGraph &node_based_graph,
const extractor::CompressedEdgeContainer &compressed_geometries,
const std::vector<extractor::QueryNode> &query_nodes);
classifyIntersection(Intersection intersection);
} // namespace guidance
} // namespace extractor

View File

@ -6,7 +6,6 @@
#include <boost/assert.hpp>
#include "extractor/guidance/roundabout_type.hpp"
#include "util/guidance/turn_lanes.hpp"
#include "util/typedefs.hpp"
namespace osrm
@ -68,10 +67,8 @@ const constexpr Enum Sliproad =
const constexpr Enum MaxTurnType = 27; // Special value for static asserts
}
// turn angle in 1.40625 degree -> 128 == 180 degree
struct TurnInstruction
{
using LaneTuple = util::guidance::LaneTuple;
TurnInstruction(const TurnType::Enum type = TurnType::Invalid,
const DirectionModifier::Enum direction_modifier = DirectionModifier::UTurn)
: type(type), direction_modifier(direction_modifier)

View File

@ -3,6 +3,7 @@
#include "extractor/guidance/turn_instruction.hpp"
#include "extractor/travel_mode.hpp"
#include "util/guidance/turn_bearing.hpp"
#include "util/typedefs.hpp"
#include <cstddef>
@ -20,9 +21,12 @@ struct OriginalEdgeData
LaneDataID lane_data_id,
guidance::TurnInstruction turn_instruction,
EntryClassID entry_classid,
TravelMode travel_mode)
TravelMode travel_mode,
util::guidance::TurnBearing pre_turn_bearing,
util::guidance::TurnBearing post_turn_bearing)
: via_geometry(via_geometry), name_id(name_id), entry_classid(entry_classid),
lane_data_id(lane_data_id), turn_instruction(turn_instruction), travel_mode(travel_mode)
lane_data_id(lane_data_id), turn_instruction(turn_instruction), travel_mode(travel_mode),
pre_turn_bearing(pre_turn_bearing), post_turn_bearing(post_turn_bearing)
{
}
@ -30,16 +34,18 @@ struct OriginalEdgeData
: via_geometry{std::numeric_limits<unsigned>::max() >> 1, false},
name_id(std::numeric_limits<unsigned>::max()), entry_classid(INVALID_ENTRY_CLASSID),
lane_data_id(INVALID_LANE_DATAID), turn_instruction(guidance::TurnInstruction::INVALID()),
travel_mode(TRAVEL_MODE_INACCESSIBLE)
travel_mode(TRAVEL_MODE_INACCESSIBLE), pre_turn_bearing(0.0), post_turn_bearing(0.0)
{
}
GeometryID via_geometry;
unsigned name_id;
NameID name_id;
EntryClassID entry_classid;
LaneDataID lane_data_id;
guidance::TurnInstruction turn_instruction;
TravelMode travel_mode;
util::guidance::TurnBearing pre_turn_bearing;
util::guidance::TurnBearing post_turn_bearing;
};
static_assert(sizeof(OriginalEdgeData) == 16,

View File

@ -46,6 +46,8 @@ const constexpr char *block_id_to_name[] = {"NAME_OFFSETS",
"BEARING_VALUES",
"ENTRY_CLASS",
"LANE_DATA_ID",
"PRE_TURN_BEARING",
"POST_TURN_BEARING",
"TURN_LANE_DATA",
"LANE_DESCRIPTION_OFFSETS",
"LANE_DESCRIPTION_MASKS"};
@ -84,6 +86,8 @@ struct SharedDataLayout
BEARING_VALUES,
ENTRY_CLASS,
LANE_DATA_ID,
PRE_TURN_BEARING,
POST_TURN_BEARING,
TURN_LANE_DATA,
LANE_DESCRIPTION_OFFSETS,
LANE_DESCRIPTION_MASKS,

View File

@ -30,6 +30,13 @@ double haversineDistance(const Coordinate first_coordinate, const Coordinate sec
double greatCircleDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
// Find the closest distance and location between coordinate and the line connecting source and
// target:
// coordinate
// |
// |
// source -------- x -------- target.
// returns x as well as the distance between source and x as ratio ([0,1])
inline std::pair<double, FloatCoordinate> projectPointOnSegment(const FloatCoordinate &source,
const FloatCoordinate &target,
const FloatCoordinate &coordinate)
@ -99,6 +106,16 @@ double circleRadius(const Coordinate first_coordinate,
// returns to
Coordinate interpolateLinear(double factor, const Coordinate from, const Coordinate to);
// compute the signed area of a triangle
double signedArea(const Coordinate first_coordinate,
const Coordinate second_coordinate,
const Coordinate third_coordinate);
// check if a set of three coordinates is given in CCW order
bool isCCW(const Coordinate first_coordinate,
const Coordinate second_coordinate,
const Coordinate third_coordinate);
} // ns coordinate_calculation
} // ns util
} // ns osrm

View File

@ -10,12 +10,15 @@
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
namespace osrm
{
namespace util
{
namespace guidance
{
inline void print(const engine::guidance::RouteStep &step)
@ -30,7 +33,9 @@ inline void print(const engine::guidance::RouteStep &step)
for (const auto &intersection : step.intersections)
{
std::cout << "(Lanes: " << static_cast<int>(intersection.lanes.lanes_in_turn) << " "
<< static_cast<int>(intersection.lanes.first_lane_from_the_right) << " bearings:";
<< static_cast<int>(intersection.lanes.first_lane_from_the_right) << " ["
<< intersection.in << "," << intersection.out << "]"
<< " bearings:";
for (auto bearing : intersection.bearings)
std::cout << " " << bearing;
std::cout << ", entry: ";

View File

@ -32,6 +32,12 @@ inline double angularDeviation(const double angle, const double from)
return std::min(360 - deviation, deviation);
}
inline bool hasRampType(const extractor::guidance::TurnInstruction instruction)
{
return instruction.type == extractor::guidance::TurnType::OffRamp ||
instruction.type == extractor::guidance::TurnType::OnRamp;
}
inline extractor::guidance::DirectionModifier::Enum getTurnDirection(const double angle)
{
// An angle of zero is a u-turn

View File

@ -0,0 +1,30 @@
#ifndef OSRM_INCLUDE_UTIL_TURN_BEARING_HPP_
#define OSRM_INCLUDE_UTIL_TURN_BEARING_HPP_
#include <cstdint>
namespace osrm
{
namespace util
{
namespace guidance
{
#pragma pack(push, 1)
class TurnBearing
{
public:
TurnBearing(const double value = 0);
double Get() const;
private:
std::uint8_t bearing;
};
#pragma pack(pop)
} // namespace guidance
} // namespace util
} // namespace osrm
#endif /* OSRM_INCLUDE_UTIL_TURN_BEARING_HPP_ */

View File

@ -46,7 +46,8 @@ template <typename DataT> class ShMemIterator : public std::iterator<std::input_
DataT &operator*() { return *p; }
};
template <typename DataT> class ShMemReverseIterator : public std::iterator<std::input_iterator_tag, DataT>
template <typename DataT>
class ShMemReverseIterator : public std::iterator<std::input_iterator_tag, DataT>
{
DataT *p;
@ -99,7 +100,10 @@ template <typename DataT> class SharedMemoryWrapper
ShMemIterator<DataT> end() const { return ShMemIterator<DataT>(m_ptr + m_size); }
ShMemReverseIterator<DataT> rbegin() const { return ShMemReverseIterator<DataT>(m_ptr + m_size - 1); }
ShMemReverseIterator<DataT> rbegin() const
{
return ShMemReverseIterator<DataT>(m_ptr + m_size - 1);
}
ShMemReverseIterator<DataT> rend() const { return ShMemReverseIterator<DataT>(m_ptr - 1); }

View File

@ -112,7 +112,6 @@ struct GeometryID
std::uint32_t forward : 1;
};
static_assert(sizeof(SegmentID) == 4, "SegmentID needs to be 4 bytes big");
#endif /* TYPEDEFS_H */

View File

@ -395,7 +395,7 @@ function way_function (way, result)
end
-- set the road classification based on guidance globals configuration
set_classification(highway,result)
set_classification(highway,result,way)
-- maxspeed
limit( result, maxspeed, maxspeed_forward, maxspeed_backward )

View File

@ -425,7 +425,7 @@ function way_function (way, result)
end
-- set the road classification based on guidance globals configuration
set_classification(highway,result)
set_classification(highway,result,way)
-- parse the remaining tags
local name = way:get_value_by_key("name")

View File

@ -42,7 +42,7 @@ road_types = { ["motorway"] = true,
link_types = { ["motorway_link"] = true, ["trunk_link"] = true, ["primary_link"] = true, ["secondary_link"] = true, ["tertiary_link"] = true }
function Guidance.set_classification (highway, result)
function Guidance.set_classification (highway, result, input_way)
if motorway_types[highway] then
result.road_classification.motorway_class = true;
end
@ -59,6 +59,33 @@ function Guidance.set_classification (highway, result)
else
result.road_classification.may_be_ignored = true;
end
local lane_count = input_way:get_value_by_key("lanes")
if lane_count and lane_count ~= "" then
local lc = tonumber(lane_count)
if lane_count ~= nil then
result.road_classification.num_lanes = lc
end
else
local total_count = 0
local forward_count = input_way:get_value_by_key("lanes:forward")
if forward_count and forward_count ~= "" then
local fc = tonumber(forward_count)
if fc ~= nil then
total_count = fc
end
end
local backward_count = input_way:get_value_by_key("lanes:backward")
if backward_count and backward_count ~= "" then
local bc = tonumber(backward_count)
if bc ~= nil then
total_count = total_count + bc
end
end
if total_count ~= 0 then
result.road_classification.num_lanes = total_count
end
end
end
-- returns forward,backward psv lane count
@ -69,19 +96,19 @@ local function get_psv_counts(way)
local fw = 0;
local bw = 0;
if( psv and psv ~= "" ) then
if psv and psv ~= "" then
fw = tonumber(psv)
if( fw == nil ) then
fw = 0
end
end
if( psv_forward and psv_forward ~= "" ) then
end
if psv_forward and psv_forward ~= "" then
fw = tonumber(psv_forward)
if( fw == nil ) then
fw = 0
end
end
if( psv_backward and psv_backward ~= "" ) then
end
if psv_backward and psv_backward ~= "" then
bw = tonumber(psv_backward);
if( bw == nil ) then
bw = 0

View File

@ -598,10 +598,10 @@ EdgeID Contractor::LoadEdgeExpandedGraph(
const auto current_segment =
&(m_geometry_list[forward_begin + leaf_object.fwd_segment_position]);
u = &(internal_to_external_node_map
[m_geometry_list[forward_begin +
leaf_object.fwd_segment_position]
.node_id]);
u = &(
internal_to_external_node_map[m_geometry_list[forward_begin +
leaf_object.fwd_segment_position]
.node_id]);
v = &(internal_to_external_node_map
[m_geometry_list[forward_begin + leaf_object.fwd_segment_position + 1]
.node_id]);
@ -619,8 +619,8 @@ EdgeID Contractor::LoadEdgeExpandedGraph(
current_segment->forward_weight,
log_edge_updates_factor);
m_geometry_list[forward_begin + 1 + leaf_object.fwd_segment_position].forward_weight =
new_segment_weight;
m_geometry_list[forward_begin + 1 + leaf_object.fwd_segment_position]
.forward_weight = new_segment_weight;
m_geometry_datasource[forward_begin + 1 + leaf_object.fwd_segment_position] =
forward_speed_iter->speed_source.source;
@ -642,8 +642,8 @@ EdgeID Contractor::LoadEdgeExpandedGraph(
segment_speed_filenames,
current_segment->reverse_weight,
log_edge_updates_factor);
m_geometry_list[forward_begin + leaf_object.fwd_segment_position].reverse_weight =
new_segment_weight;
m_geometry_list[forward_begin + leaf_object.fwd_segment_position]
.reverse_weight = new_segment_weight;
m_geometry_datasource[forward_begin + leaf_object.fwd_segment_position] =
reverse_speed_iter->speed_source.source;

View File

@ -13,22 +13,6 @@ namespace guidance
{
namespace detail
{
std::pair<short, short> getIntermediateBearings(const LegGeometry &leg_geometry,
const std::size_t segment_index)
{
auto turn_index = leg_geometry.BackIndex(segment_index);
BOOST_ASSERT(turn_index > 0);
BOOST_ASSERT(turn_index + 1 < leg_geometry.locations.size());
// TODO chose a bigger look-a-head to smooth complex geometry
const auto pre_turn_coordinate = leg_geometry.locations[turn_index - 1];
const auto turn_coordinate = leg_geometry.locations[turn_index];
const auto post_turn_coordinate = leg_geometry.locations[turn_index + 1];
return std::make_pair<short, short>(
std::round(util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate)),
std::round(util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate)));
}
std::pair<short, short> getDepartBearings(const LegGeometry &leg_geometry)
{

View File

@ -1,6 +1,6 @@
#include "engine/guidance/post_processing.hpp"
#include "extractor/guidance/toolkit.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "engine/guidance/post_processing.hpp"
#include "engine/guidance/assemble_steps.hpp"
#include "engine/guidance/lane_processing.hpp"
@ -42,8 +42,10 @@ const constexpr double MAX_COLLAPSE_DISTANCE = 30;
// check if at least one of the turns is actually a maneuver
inline bool hasManeuver(const RouteStep &first, const RouteStep &second)
{
return first.maneuver.instruction.type != TurnType::Suppressed ||
second.maneuver.instruction.type != TurnType::Suppressed;
return (first.maneuver.instruction.type != TurnType::Suppressed ||
second.maneuver.instruction.type != TurnType::Suppressed) &&
(first.maneuver.instruction.type != TurnType::NoTurn &&
second.maneuver.instruction.type != TurnType::NoTurn);
}
// forward all signage/name data from one step to another.
@ -67,6 +69,7 @@ inline bool choiceless(const RouteStep &step, const RouteStep &previous)
1 >= std::count(step.intersections.front().entry.begin(),
step.intersections.front().entry.end(),
true);
return is_without_choice;
}
@ -330,8 +333,9 @@ void closeOffRoundabout(const bool on_roundabout,
entry_intersection.bearings[entry_intersection.in]),
exit_bearing);
auto bearings = propagation_step.intersections.front().bearings;
propagation_step.maneuver.instruction.direction_modifier =
::osrm::util::guidance::getTurnDirection(angle);
util::guidance::getTurnDirection(angle);
}
forwardStepSignage(propagation_step, destination_copy);
@ -347,6 +351,142 @@ void closeOffRoundabout(const bool on_roundabout,
}
}
bool bearingsAreReversed(const double bearing_in, const double bearing_out)
{
// Nearly perfectly reversed angles have a difference close to 180 degrees (straight)
const double left_turn_angle = [&]() {
if (0 <= bearing_out && bearing_out <= bearing_in)
return bearing_in - bearing_out;
return bearing_in + 360 - bearing_out;
}();
return angularDeviation(left_turn_angle, 180) <= 35;
}
bool isLinkroad(const RouteStep &step)
{
const constexpr double MAX_LINK_ROAD_LENGTH = 60.0;
return step.distance <= MAX_LINK_ROAD_LENGTH && step.name_id == EMPTY_NAMEID;
}
bool isUTurn(const RouteStep &in_step, const RouteStep &out_step, const RouteStep &pre_in_step)
{
const bool is_possible_candidate = in_step.distance <= MAX_COLLAPSE_DISTANCE ||
choiceless(out_step, in_step) ||
(isLinkroad(in_step) && out_step.name_id != EMPTY_NAMEID &&
pre_in_step.name_id == out_step.name_id);
const bool takes_u_turn = bearingsAreReversed(
util::bearing::reverseBearing(
in_step.intersections.front().bearings[in_step.intersections.front().in]),
out_step.intersections.front().bearings[out_step.intersections.front().out]);
return is_possible_candidate && takes_u_turn && compatible(in_step, out_step);
}
double findTotalTurnAngle(const RouteStep &entry_step, const RouteStep &exit_step)
{
const auto exit_intersection = exit_step.intersections.front();
const auto exit_step_exit_bearing = exit_intersection.bearings[exit_intersection.out];
const auto exit_step_entry_bearing =
util::bearing::reverseBearing(exit_intersection.bearings[exit_intersection.in]);
const auto entry_intersection = entry_step.intersections.front();
const auto entry_step_entry_bearing =
util::bearing::reverseBearing(entry_intersection.bearings[entry_intersection.in]);
const auto entry_step_exit_bearing = entry_intersection.bearings[entry_intersection.out];
const auto exit_angle = turn_angle(exit_step_entry_bearing, exit_step_exit_bearing);
const auto entry_angle = turn_angle(entry_step_entry_bearing, entry_step_exit_bearing);
const double total_angle = turn_angle(entry_step_entry_bearing, exit_step_exit_bearing);
// We allow for minor deviations from a straight line
if (((entry_step.distance < MAX_COLLAPSE_DISTANCE && exit_step.intersections.size() == 1) ||
(entry_angle <= 185 && exit_angle <= 185) || (entry_angle >= 175 && exit_angle >= 175)) &&
angularDeviation(total_angle, 180) > 20)
{
// both angles are in the same direction, the total turn gets increased
//
// a ---- b
// \
// c
// |
// d
//
// Will be considered just like
// a -----b
// |
// c
// |
// d
return total_angle;
}
else
{
// to prevent ignoring angles like
// a -- b
// |
// c -- d
// We don't combine both turn angles here but keep the very first turn angle.
// We choose the first one, since we consider the first maneuver in a merge range the
// important one
return entry_angle;
}
}
std::size_t getPreviousIndex(std::size_t index, const std::vector<RouteStep> &steps)
{
BOOST_ASSERT(index > 0);
BOOST_ASSERT(index < steps.size());
--index;
while (index > 0 && steps[index].maneuver.instruction.type == TurnType::NoTurn)
--index;
return index;
};
void collapseUTurn(std::vector<RouteStep> &steps,
const std::size_t two_back_index,
const std::size_t one_back_index,
const std::size_t step_index)
{
BOOST_ASSERT(two_back_index < steps.size());
BOOST_ASSERT(step_index < steps.size());
BOOST_ASSERT(one_back_index < steps.size());
const auto &current_step = steps[step_index];
// the simple case is a u-turn that changes directly into the in-name again
const bool direct_u_turn = !isNoticeableNameChange(steps[two_back_index], current_step);
// however, we might also deal with a dual-collapse scenario in which we have to
// additionall collapse a name-change as welll
const auto next_step_index = step_index + 1;
const bool continues_with_name_change =
(next_step_index < steps.size()) &&
((steps[next_step_index].maneuver.instruction.type == TurnType::UseLane &&
steps[next_step_index].maneuver.instruction.direction_modifier ==
DirectionModifier::Straight) ||
isCollapsableInstruction(steps[next_step_index].maneuver.instruction));
const bool u_turn_with_name_change =
continues_with_name_change && steps[next_step_index].name_id != EMPTY_NAMEID &&
!isNoticeableNameChange(steps[two_back_index], steps[next_step_index]);
if (direct_u_turn || u_turn_with_name_change)
{
steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
invalidateStep(steps[step_index]);
if (u_turn_with_name_change)
{
steps[one_back_index] =
elongate(std::move(steps[one_back_index]), steps[next_step_index]);
invalidateStep(steps[next_step_index]); // will be skipped due to the
// continue statement at the
// beginning of this function
}
forwardStepSignage(steps[one_back_index], steps[two_back_index]);
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
steps[one_back_index].maneuver.instruction.direction_modifier = DirectionModifier::UTurn;
}
}
void collapseTurnAt(std::vector<RouteStep> &steps,
const std::size_t two_back_index,
const std::size_t one_back_index,
@ -357,45 +497,81 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
const auto &current_step = steps[step_index];
const auto &one_back_step = steps[one_back_index];
// FIXME: this function assumes driving on the right hand side of the streat
const auto bearingsAreReversed = [](const double bearing_in, const double bearing_out) {
// Nearly perfectly reversed angles have a difference close to 180 degrees (straight)
const double left_turn_angle = [&]() {
if (0 <= bearing_out && bearing_out <= bearing_in)
return bearing_in - bearing_out;
return bearing_in + 360 - bearing_out;
}();
return angularDeviation(left_turn_angle, 180) <= 35;
};
// This function assumes driving on the right hand side of the streat
BOOST_ASSERT(!one_back_step.intersections.empty() && !current_step.intersections.empty());
if (!hasManeuver(one_back_step, current_step))
return;
// Very Short New Name
if (((collapsable(one_back_step, current_step) ||
(isCollapsableInstruction(one_back_step.maneuver.instruction) &&
choiceless(current_step, one_back_step))) &&
!(one_back_step.maneuver.instruction.type == TurnType::Merge)))
// the check against merge is a workaround for motorways
// A maneuver is preceded by a name change if the instruction just before can be collapsed
// normally or the instruction itself is collapsable and does not actually present a choice
const auto maneuverPrecededByNameChange = [](const RouteStep &turning_point,
const RouteStep &possible_name_change_location,
const RouteStep &preceeding_step) {
// the check against merge is a workaround for motorways
if (possible_name_change_location.maneuver.instruction.type == TurnType::Merge ||
!compatible(possible_name_change_location, preceeding_step))
return false;
return collapsable(possible_name_change_location, turning_point) ||
(isCollapsableInstruction(possible_name_change_location.maneuver.instruction) &&
choiceless(possible_name_change_location, preceeding_step));
};
// check if the actual turn we wan't to announce is delayed. This situation describes a turn
// that is expressed by two turns,
const auto isDelayedTurn = [](const RouteStep &openining_turn,
const RouteStep &finishing_turn) {
// only possible if both are compatible
if (!compatible(openining_turn, finishing_turn))
return false;
else
{
const auto is_short_and_collapsable =
openining_turn.distance <= MAX_COLLAPSE_DISTANCE &&
isCollapsableInstruction(finishing_turn.maneuver.instruction);
const auto without_choice = choiceless(finishing_turn, openining_turn);
const auto is_not_too_long_and_choiceless =
openining_turn.distance <= 2 * MAX_COLLAPSE_DISTANCE && without_choice;
// for ramps we allow longer stretches, since they are often on some major brides/large
// roads. A combined distance of of 4 intersections would be to long for a normal
// collapse. In case of a ramp though, we also account for situations that have the ramp
// tagged late
const auto is_delayed_turn_onto_a_ramp =
openining_turn.distance <= 4 * MAX_COLLAPSE_DISTANCE && without_choice &&
util::guidance::hasRampType(finishing_turn.maneuver.instruction);
return !util::guidance::hasRampType(openining_turn.maneuver.instruction) &&
(is_short_and_collapsable || is_not_too_long_and_choiceless ||
isLinkroad(openining_turn) || is_delayed_turn_onto_a_ramp);
}
};
// Handle possible u-turns
if (isUTurn(one_back_step, current_step, steps[two_back_index]))
collapseUTurn(steps, two_back_index, one_back_index, step_index);
// Very Short New Name that will be suppressed. Turn location remains at current_step
else if (maneuverPrecededByNameChange(current_step, one_back_step, steps[two_back_index]))
{
BOOST_ASSERT(two_back_index < steps.size());
if (compatible(one_back_step, steps[two_back_index]))
BOOST_ASSERT(!one_back_step.intersections.empty());
if (TurnType::Merge == current_step.maneuver.instruction.type)
{
steps[step_index].maneuver.instruction.direction_modifier =
util::guidance::mirrorDirectionModifier(
steps[step_index].maneuver.instruction.direction_modifier);
steps[step_index].maneuver.instruction.type = TurnType::Turn;
}
else
{
BOOST_ASSERT(!one_back_step.intersections.empty());
if (TurnType::Continue == current_step.maneuver.instruction.type ||
(TurnType::Suppressed == current_step.maneuver.instruction.type &&
current_step.maneuver.instruction.direction_modifier !=
DirectionModifier::Straight))
steps[step_index].maneuver.instruction.type = TurnType::Turn;
else if (TurnType::Merge == current_step.maneuver.instruction.type)
{
steps[step_index].maneuver.instruction.direction_modifier =
util::guidance::mirrorDirectionModifier(
steps[step_index].maneuver.instruction.direction_modifier);
steps[step_index].maneuver.instruction.type = TurnType::Turn;
}
else if (TurnType::NewName == current_step.maneuver.instruction.type &&
current_step.maneuver.instruction.direction_modifier !=
DirectionModifier::Straight &&
@ -407,111 +583,100 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
one_back_step.intersections.front().bearings.size() > 2)
steps[step_index].maneuver.instruction.type = TurnType::Turn;
steps[two_back_index] = elongate(std::move(steps[two_back_index]), one_back_step);
// If the previous instruction asked to continue, the name change will have to
// be changed into a turn
invalidateStep(steps[one_back_index]);
const auto total_angle = findTotalTurnAngle(steps[one_back_index], current_step);
steps[step_index].maneuver.instruction.direction_modifier =
getTurnDirection(total_angle);
}
steps[two_back_index] = elongate(std::move(steps[two_back_index]), one_back_step);
// If the previous instruction asked to continue, the name change will have to
// be changed into a turn
invalidateStep(steps[one_back_index]);
}
// very short segment after turn
else if (one_back_step.distance <= MAX_COLLAPSE_DISTANCE &&
isCollapsableInstruction(current_step.maneuver.instruction))
// very short segment after turn, turn location remains at one_back_step
else if (isDelayedTurn(one_back_step, current_step))
{
steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
// TODO check for lanes (https://github.com/Project-OSRM/osrm-backend/issues/2553)
if (compatible(one_back_step, current_step))
if (TurnType::Continue == one_back_step.maneuver.instruction.type &&
isNoticeableNameChange(steps[two_back_index], current_step))
{
steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
if ((TurnType::Continue == one_back_step.maneuver.instruction.type ||
TurnType::Suppressed == one_back_step.maneuver.instruction.type) &&
isNoticeableNameChange(steps[two_back_index], current_step))
{
if (current_step.maneuver.instruction.type == TurnType::OnRamp ||
current_step.maneuver.instruction.type == TurnType::OffRamp)
steps[one_back_index].maneuver.instruction.type =
current_step.maneuver.instruction.type;
else
steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
}
else if (TurnType::Turn == one_back_step.maneuver.instruction.type &&
!isNoticeableNameChange(steps[two_back_index], current_step))
{
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
}
else if (TurnType::Turn == one_back_step.maneuver.instruction.type &&
!isNoticeableNameChange(steps[two_back_index], current_step))
{
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
const auto getBearing = [](bool in, const RouteStep &step) {
const auto index =
in ? step.intersections.front().in : step.intersections.front().out;
return step.intersections.front().bearings[index];
};
const auto getBearing = [](bool in, const RouteStep &step) {
const auto index =
in ? step.intersections.front().in : step.intersections.front().out;
return step.intersections.front().bearings[index];
};
// If we Merge onto the same street, we end up with a u-turn in some cases
if (bearingsAreReversed(
util::bearing::reverseBearing(getBearing(true, one_back_step)),
getBearing(false, current_step)))
{
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
steps[one_back_index].maneuver.instruction.direction_modifier =
DirectionModifier::UTurn;
}
}
else if (TurnType::Merge == one_back_step.maneuver.instruction.type &&
current_step.maneuver.instruction.type !=
TurnType::Suppressed) // This suppressed is a check for highways. We might
// need a highway-suppressed to get the turn onto a
// highway...
// If we Merge onto the same street, we end up with a u-turn in some cases
if (bearingsAreReversed(util::bearing::reverseBearing(getBearing(true, one_back_step)),
getBearing(false, current_step)))
{
steps[one_back_index].maneuver.instruction.direction_modifier =
util::guidance::mirrorDirectionModifier(
steps[one_back_index].maneuver.instruction.direction_modifier);
DirectionModifier::UTurn;
}
forwardStepSignage(steps[one_back_index], current_step);
invalidateStep(steps[step_index]);
}
}
// Potential U-Turn
else if ((one_back_step.distance <= MAX_COLLAPSE_DISTANCE ||
choiceless(current_step, one_back_step)) &&
bearingsAreReversed(util::bearing::reverseBearing(
one_back_step.intersections.front()
.bearings[one_back_step.intersections.front().in]),
current_step.intersections.front()
.bearings[current_step.intersections.front().out]) &&
compatible(one_back_step, current_step))
{
BOOST_ASSERT(two_back_index < steps.size());
// the simple case is a u-turn that changes directly into the in-name again
const bool direct_u_turn = !isNoticeableNameChange(steps[two_back_index], current_step);
else if (TurnType::NewName == one_back_step.maneuver.instruction.type ||
(TurnType::NewName == current_step.maneuver.instruction.type &&
steps[one_back_index].maneuver.instruction.type == TurnType::Suppressed))
steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
// however, we might also deal with a dual-collapse scenario in which we have to
// additionall collapse a name-change as welll
const auto next_step_index = step_index + 1;
const bool continues_with_name_change =
(next_step_index < steps.size()) &&
((steps[next_step_index].maneuver.instruction.type == TurnType::UseLane &&
steps[next_step_index].maneuver.instruction.direction_modifier ==
DirectionModifier::Straight) ||
isCollapsableInstruction(steps[next_step_index].maneuver.instruction));
const bool u_turn_with_name_change =
continues_with_name_change && steps[next_step_index].name_id != EMPTY_NAMEID &&
!isNoticeableNameChange(steps[two_back_index], steps[next_step_index]);
if (direct_u_turn || u_turn_with_name_change)
if (TurnType::Merge == one_back_step.maneuver.instruction.type &&
current_step.maneuver.instruction.type !=
TurnType::Suppressed) // This suppressed is a check for highways. We might
// need a highway-suppressed to get the turn onto a
// highway...
{
steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
invalidateStep(steps[step_index]);
if (u_turn_with_name_change &&
compatible(steps[one_back_index], steps[next_step_index]))
{
steps[one_back_index] =
elongate(std::move(steps[one_back_index]), steps[next_step_index]);
invalidateStep(steps[next_step_index]); // will be skipped due to the
// continue statement at the
// beginning of this function
forwardStepSignage(steps[one_back_index], steps[two_back_index]);
}
if (direct_u_turn)
forwardStepSignage(steps[one_back_index], steps[two_back_index]);
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
steps[one_back_index].maneuver.instruction.direction_modifier =
DirectionModifier::UTurn;
util::guidance::mirrorDirectionModifier(
steps[one_back_index].maneuver.instruction.direction_modifier);
}
// on non merge-types, we check for a combined turn angle
else if (TurnType::Merge != one_back_step.maneuver.instruction.type)
{
const auto combined_angle = findTotalTurnAngle(one_back_step, current_step);
steps[one_back_index].maneuver.instruction.direction_modifier =
getTurnDirection(combined_angle);
}
steps[one_back_index].name = current_step.name;
steps[one_back_index].name_id = current_step.name_id;
invalidateStep(steps[step_index]);
}
else if (TurnType::Suppressed == current_step.maneuver.instruction.type &&
!isNoticeableNameChange(one_back_step, current_step))
{
steps[one_back_index] = elongate(std::move(steps[one_back_index]), current_step);
const auto angle = findTotalTurnAngle(one_back_step, current_step);
steps[one_back_index].maneuver.instruction.direction_modifier =
util::guidance::getTurnDirection(angle);
invalidateStep(steps[step_index]);
}
else if (TurnType::Turn == one_back_step.maneuver.instruction.type &&
TurnType::OnRamp == current_step.maneuver.instruction.type)
{
// turning onto a ramp makes the first turn into a ramp
steps[one_back_index] = elongate(std::move(steps[one_back_index]), current_step);
steps[one_back_index].maneuver.instruction.type = TurnType::OnRamp;
const auto angle = findTotalTurnAngle(one_back_step, current_step);
steps[one_back_index].maneuver.instruction.direction_modifier =
util::guidance::getTurnDirection(angle);
forwardStepSignage(steps[one_back_index], current_step);
invalidateStep(steps[step_index]);
}
}
@ -559,10 +724,33 @@ bool isStaggeredIntersection(const RouteStep &previous, const RouteStep &current
} // namespace
// A check whether two instructions can be treated as one. This is only the case for very short
// maneuvers that can, in some form, be seen as one. Lookahead of one step.
bool collapsable(const RouteStep &step, const RouteStep &next)
{
const auto is_short_step = step.distance < MAX_COLLAPSE_DISTANCE;
const auto instruction_can_be_collapsed = isCollapsableInstruction(step.maneuver.instruction);
const auto is_use_lane = step.maneuver.instruction.type == TurnType::UseLane;
const auto lanes_dont_change =
step.intersections.front().lanes == next.intersections.front().lanes;
if (is_short_step && instruction_can_be_collapsed)
return true;
// Prevent collapsing away important lane change steps
if (is_short_step && is_use_lane && lanes_dont_change)
return true;
return false;
}
// elongate a step by another. the data is added either at the front, or the back
OSRM_ATTR_WARN_UNUSED
RouteStep elongate(RouteStep step, const RouteStep &by_step)
{
BOOST_ASSERT(step.mode == by_step.mode);
step.duration += by_step.duration;
step.distance += by_step.distance;
BOOST_ASSERT(step.mode == by_step.mode);
@ -597,27 +785,6 @@ RouteStep elongate(RouteStep step, const RouteStep &by_step)
// Post processing can invalidate some instructions. For example StayOnRoundabout
// is turned into exit counts. These instructions are removed by the following function
// A check whether two instructions can be treated as one. This is only the case for very short
// maneuvers that can, in some form, be seen as one. Lookahead of one step.
bool collapsable(const RouteStep &step, const RouteStep &next)
{
const auto is_short_step = step.distance < MAX_COLLAPSE_DISTANCE;
const auto instruction_can_be_collapsed = isCollapsableInstruction(step.maneuver.instruction);
const auto is_use_lane = step.maneuver.instruction.type == TurnType::UseLane;
const auto lanes_dont_change =
step.intersections.front().lanes == next.intersections.front().lanes;
if (is_short_step && instruction_can_be_collapsed)
return true;
// Prevent collapsing away important lane change steps
if (is_short_step && is_use_lane && lanes_dont_change)
return true;
return false;
}
std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps)
{
// finally clean up the post-processed instructions.
@ -733,17 +900,6 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
if (steps.size() <= 2)
return steps;
// Get the previous non-invalid instruction
const auto getPreviousIndex = [&steps](std::size_t index) {
BOOST_ASSERT(index > 0);
BOOST_ASSERT(index < steps.size());
--index;
while (index > 0 && steps[index].maneuver.instruction.type == TurnType::NoTurn)
--index;
return index;
};
const auto getPreviousNameIndex = [&steps](std::size_t index) {
BOOST_ASSERT(index > 0);
BOOST_ASSERT(index < steps.size());
@ -774,9 +930,7 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
{
const auto &current_step = steps[step_index];
const auto next_step_index = step_index + 1;
if (current_step.maneuver.instruction.type == TurnType::NoTurn)
continue;
const auto one_back_index = getPreviousIndex(step_index);
const auto one_back_index = getPreviousIndex(step_index, steps);
BOOST_ASSERT(one_back_index < steps.size());
const auto &one_back_step = steps[one_back_index];
@ -808,13 +962,24 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
else
{
// Handle possible u-turns between highways that look like slip-roads
if (steps[getPreviousIndex(one_back_index, steps)].name_id ==
steps[step_index].name_id &&
steps[step_index].name_id != EMPTY_NAMEID)
{
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
}
else
{
steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
}
if (compatible(one_back_step, current_step))
{
// Turn Types in the response depend on whether we find the same road name
// (sliproad indcating a u-turn) or if we are turning onto a different road, in
// which case we use a turn.
if (!isNoticeableNameChange(steps[getPreviousIndex(one_back_index)],
steps[step_index]))
if (!isNoticeableNameChange(steps[getPreviousIndex(one_back_index, steps)],
current_step) &&
current_step.name_id != EMPTY_NAMEID)
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
else
steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
@ -830,16 +995,9 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
steps[one_back_index].intersections.front().lane_description =
current_step.intersections.front().lane_description;
const auto exit_intersection = steps[step_index].intersections.front();
const auto exit_bearing = exit_intersection.bearings[exit_intersection.out];
const auto entry_intersection = steps[one_back_index].intersections.front();
const auto entry_bearing = entry_intersection.bearings[entry_intersection.in];
const double angle =
turn_angle(util::bearing::reverseBearing(entry_bearing), exit_bearing);
const auto angle = findTotalTurnAngle(one_back_step, current_step);
steps[one_back_index].maneuver.instruction.direction_modifier =
::osrm::util::guidance::getTurnDirection(angle);
util::guidance::getTurnDirection(angle);
invalidateStep(steps[step_index]);
}
else
@ -875,7 +1033,7 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
isCollapsableInstruction(one_back_step.maneuver.instruction)) ||
isStaggeredIntersection(one_back_step, current_step)))
{
const auto two_back_index = getPreviousIndex(one_back_index);
const auto two_back_index = getPreviousIndex(one_back_index, steps);
BOOST_ASSERT(two_back_index < steps.size());
// valid, since one_back is collapsable or a turn and therefore not depart:
if (!isNoticeableNameChange(steps[two_back_index], current_step))
@ -930,27 +1088,44 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
{
// check for one of the multiple collapse scenarios and, if possible, collapse the
// turn
const auto two_back_index = getPreviousIndex(one_back_index);
const auto two_back_index = getPreviousIndex(one_back_index, steps);
BOOST_ASSERT(two_back_index < steps.size());
collapseTurnAt(steps, two_back_index, one_back_index, step_index);
}
}
else if (one_back_index > 0 && (one_back_step.distance <= MAX_COLLAPSE_DISTANCE ||
choiceless(current_step, one_back_step)))
else if (one_back_index > 0 &&
(one_back_step.distance <= MAX_COLLAPSE_DISTANCE ||
choiceless(current_step, one_back_step) || isLinkroad(one_back_step)))
{
// check for one of the multiple collapse scenarios and, if possible, collapse the turn
const auto two_back_index = getPreviousIndex(one_back_index);
const auto two_back_index = getPreviousIndex(one_back_index, steps);
BOOST_ASSERT(two_back_index < steps.size());
// all turns that are handled lower down are also compatible
collapseTurnAt(steps, two_back_index, one_back_index, step_index);
}
if (steps[step_index].maneuver.instruction.type == TurnType::Turn)
{
const auto u_turn_one_back_index = getPreviousIndex(step_index, steps);
if (u_turn_one_back_index > 0)
{
const auto u_turn_two_back_index = getPreviousIndex(u_turn_one_back_index, steps);
if (isUTurn(steps[u_turn_one_back_index],
steps[step_index],
steps[u_turn_two_back_index]))
{
collapseUTurn(steps, u_turn_two_back_index, u_turn_one_back_index, step_index);
}
}
}
}
// handle final sliproad
if (steps.size() >= 3 &&
steps[getPreviousIndex(steps.size() - 1)].maneuver.instruction.type == TurnType::Sliproad)
steps[getPreviousIndex(steps.size() - 1, steps)].maneuver.instruction.type ==
TurnType::Sliproad)
{
steps[getPreviousIndex(steps.size() - 1)].maneuver.instruction.type = TurnType::Turn;
steps[getPreviousIndex(steps.size() - 1, steps)].maneuver.instruction.type = TurnType::Turn;
}
BOOST_ASSERT(steps.front().intersections.size() >= 1);
@ -1260,6 +1435,7 @@ std::vector<RouteStep> buildIntersections(std::vector<RouteStep> steps)
steps[last_valid_instruction] =
elongate(std::move(steps[last_valid_instruction]), step);
step.maneuver.instruction = TurnInstruction::NO_TURN();
invalidateStep(steps[step_index]);
}
else if (!isSilent(instruction))
{
@ -1296,16 +1472,6 @@ std::vector<RouteStep> collapseUseLane(std::vector<RouteStep> steps)
return (mask & tag) != extractor::guidance::TurnLaneType::empty;
};
const auto getPreviousIndex = [&steps](std::size_t index) {
BOOST_ASSERT(index > 0);
BOOST_ASSERT(index < steps.size());
--index;
while (index > 0 && steps[index].maneuver.instruction.type == TurnType::NoTurn)
--index;
return index;
};
const auto canCollapseUseLane = [containsTag](const RouteStep &step) {
// the lane description is given left to right, lanes are counted from the right.
// Therefore we access the lane description using the reverse iterator
@ -1330,9 +1496,8 @@ std::vector<RouteStep> collapseUseLane(std::vector<RouteStep> steps)
const auto &step = steps[step_index];
if (step.maneuver.instruction.type == TurnType::UseLane && canCollapseUseLane(step))
{
const auto previous = getPreviousIndex(step_index);
const auto previous = getPreviousIndex(step_index, steps);
steps[previous] = elongate(std::move(steps[previous]), steps[step_index]);
// elongate(steps[step_index-1], steps[step_index]);
invalidateStep(steps[step_index]);
}
}

View File

@ -467,7 +467,8 @@ Status TilePlugin::HandleRequest(const std::shared_ptr<datafacade::BaseDataFacad
for (const auto &source_ebn : edge_based_node_info)
{
// Grab a copy of the geometry leading up to the intersection.
first_geometry = facade->GetUncompressedForwardGeometry(source_ebn.second.packed_geometry_id);
first_geometry =
facade->GetUncompressedForwardGeometry(source_ebn.second.packed_geometry_id);
// We earlier saved the source and target intersection nodes for every road section.
// We can use the target node to find all road sections that lead away from
@ -534,7 +535,8 @@ Status TilePlugin::HandleRequest(const std::shared_ptr<datafacade::BaseDataFacad
edge_based_node_info.at(target_ebn).packed_geometry_id);
// Now, calculate the sum of the weight of all the segments.
forward_weight_vector = facade->GetUncompressedForwardWeights(source_ebn.second.packed_geometry_id);
forward_weight_vector =
facade->GetUncompressedForwardWeights(source_ebn.second.packed_geometry_id);
const auto sum_node_weight = std::accumulate(
forward_weight_vector.begin(), forward_weight_vector.end(), EdgeWeight{0});

View File

@ -164,7 +164,8 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1,
BOOST_ASSERT(edge_bucket_id1 == GetPositionForID(edge_id_1));
BOOST_ASSERT(edge_bucket_id1 < m_compressed_oneway_geometries.size());
std::vector<OnewayCompressedEdge> &edge_bucket_list1 = m_compressed_oneway_geometries[edge_bucket_id1];
std::vector<OnewayCompressedEdge> &edge_bucket_list1 =
m_compressed_oneway_geometries[edge_bucket_id1];
// note we don't save the start coordinate: it is implicitly given by edge 1
// weight1 is the distance to the (currently) last coordinate in the bucket
@ -235,7 +236,8 @@ void CompressedEdgeContainer::AddUncompressedEdge(const EdgeID edge_id,
BOOST_ASSERT(edge_bucket_id == GetPositionForID(edge_id));
BOOST_ASSERT(edge_bucket_id < m_compressed_oneway_geometries.size());
std::vector<OnewayCompressedEdge> &edge_bucket_list = m_compressed_oneway_geometries[edge_bucket_id];
std::vector<OnewayCompressedEdge> &edge_bucket_list =
m_compressed_oneway_geometries[edge_bucket_id];
// note we don't save the start coordinate: it is implicitly given by edge_id
// weight is the distance to the (currently) last coordinate in the bucket
@ -264,7 +266,8 @@ unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID
std::vector<CompressedEdge> zipped_edge_bucket;
const auto &first_node = reverse_bucket.back();
zipped_edge_bucket.emplace_back(CompressedEdge{first_node.node_id, INVALID_EDGE_WEIGHT, first_node.weight});
zipped_edge_bucket.emplace_back(
CompressedEdge{first_node.node_id, INVALID_EDGE_WEIGHT, first_node.weight});
for (std::size_t i = 0; i < forward_bucket.size() - 1; ++i)
{
@ -273,11 +276,13 @@ unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID
BOOST_ASSERT(fwd_node.node_id == rev_node.node_id);
zipped_edge_bucket.emplace_back(CompressedEdge{fwd_node.node_id, fwd_node.weight, rev_node.weight});
zipped_edge_bucket.emplace_back(
CompressedEdge{fwd_node.node_id, fwd_node.weight, rev_node.weight});
}
const auto &last_node = forward_bucket.back();
zipped_edge_bucket.emplace_back(CompressedEdge{last_node.node_id, last_node.weight, INVALID_EDGE_WEIGHT});
zipped_edge_bucket.emplace_back(
CompressedEdge{last_node.node_id, last_node.weight, INVALID_EDGE_WEIGHT});
m_compressed_geometries.emplace_back(std::move(zipped_edge_bucket));
return zipped_geometry_id;

View File

@ -1,8 +1,10 @@
#include "extractor/edge_based_graph_factory.hpp"
#include "extractor/edge_based_edge.hpp"
#include "util/bearing.hpp"
#include "util/coordinate.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/exception.hpp"
#include "util/guidance/turn_bearing.hpp"
#include "util/integer_range.hpp"
#include "util/percent.hpp"
#include "util/simple_logger.hpp"
@ -377,18 +379,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
auto intersection = turn_analysis.getIntersection(node_u, edge_from_u);
intersection =
turn_analysis.assignTurnTypes(node_u, edge_from_u, std::move(intersection));
intersection =
turn_lane_handler.assignTurnLanes(node_u, edge_from_u, std::move(intersection));
const auto possible_turns = turn_analysis.transformIntersectionIntoTurns(intersection);
// the entry class depends on the turn, so we have to classify the interesction for
// every edge
const auto turn_classification = classifyIntersection(node_v,
intersection,
*m_node_based_graph,
m_compressed_edge_container,
m_node_info_list);
const auto turn_classification = classifyIntersection(intersection);
const auto entry_class_id = [&](const util::guidance::EntryClass entry_class) {
if (0 == entry_class_hash.count(entry_class))
@ -436,6 +434,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
const int32_t turn_penalty =
scripting_environment.GetTurnPenalty(180. - turn.angle);
const auto turn_instruction = turn.instruction;
if (turn_instruction.direction_modifier == guidance::DirectionModifier::UTurn)
@ -443,27 +442,43 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
distance += profile_properties.u_turn_penalty;
}
distance += turn_penalty;
// don't add turn penalty if it is not an actual turn. This heuristic is necessary
// since OSRM cannot handle looping roads/parallel roads
if (turn_instruction.type != guidance::TurnType::NoTurn)
distance += turn_penalty;
const bool is_encoded_forwards = m_compressed_edge_container.HasZippedEntryForForwardID(edge_from_u);
const bool is_encoded_backwards = m_compressed_edge_container.HasZippedEntryForReverseID(edge_from_u);
const bool is_encoded_forwards =
m_compressed_edge_container.HasZippedEntryForForwardID(edge_from_u);
const bool is_encoded_backwards =
m_compressed_edge_container.HasZippedEntryForReverseID(edge_from_u);
BOOST_ASSERT(is_encoded_forwards || is_encoded_backwards);
if (is_encoded_forwards) {
if (is_encoded_forwards)
{
original_edge_data_vector.emplace_back(
GeometryID{m_compressed_edge_container.GetZippedPositionForForwardID(edge_from_u), true},
GeometryID{
m_compressed_edge_container.GetZippedPositionForForwardID(edge_from_u),
true},
edge_data1.name_id,
turn.lane_data_id,
turn_instruction,
entry_class_id,
edge_data1.travel_mode);
} else if (is_encoded_backwards) {
edge_data1.travel_mode,
util::guidance::TurnBearing(intersection[0].turn.bearing),
util::guidance::TurnBearing(turn.bearing));
}
else if (is_encoded_backwards)
{
original_edge_data_vector.emplace_back(
GeometryID{m_compressed_edge_container.GetZippedPositionForReverseID(edge_from_u), false},
GeometryID{
m_compressed_edge_container.GetZippedPositionForReverseID(edge_from_u),
false},
edge_data1.name_id,
turn.lane_data_id,
turn_instruction,
entry_class_id,
edge_data1.travel_mode);
edge_data1.travel_mode,
util::guidance::TurnBearing(intersection[0].turn.bearing),
util::guidance::TurnBearing(turn.bearing));
}
++original_edges_counter;

View File

@ -0,0 +1,816 @@
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/toolkit.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <iomanip>
#include <limits>
#include <numeric>
#include <tuple>
#include <utility>
#include <boost/range/algorithm/transform.hpp>
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace
{
// to use the corrected coordinate, we require it to be at least a bit further down the
// road than the offset coordinate. We postulate a minimum Distance of 2 Meters
const constexpr double DESIRED_COORDINATE_DIFFERENCE = 2.0;
// the default distance we lookahead on a road. This distance prevents small mapping
// errors to impact the turn angles.
const constexpr double LOOKAHEAD_DISTANCE_WITHOUT_LANES = 10.0;
// The standard with of a interstate highway is 3.7 meters. Local roads have
// smaller widths, ranging from 2.5 to 3.25 meters. As a compromise, we use
// the 3.25 here for our angle calculations
const constexpr double ASSUMED_LANE_WIDTH = 3.25;
const constexpr double FAR_LOOKAHEAD_DISTANCE = 30.0;
// The count of lanes assumed when no lanes are present. Since most roads will have lanes for both
// directions or a lane count specified, we use 2. Overestimating only makes our calculations safer,
// so we are fine for 1-lane ways. larger than 2 lanes should usually be specified in the data.
const constexpr std::uint16_t ASSUMED_LANE_COUNT = 2;
}
CoordinateExtractor::CoordinateExtractor(
const util::NodeBasedDynamicGraph &node_based_graph,
const extractor::CompressedEdgeContainer &compressed_geometries,
const std::vector<extractor::QueryNode> &node_coordinates)
: node_based_graph(node_based_graph), compressed_geometries(compressed_geometries),
node_coordinates(node_coordinates)
{
}
util::Coordinate
CoordinateExtractor::GetCoordinateAlongRoad(const NodeID intersection_node,
const EdgeID turn_edge,
const bool traversed_in_reverse,
const NodeID to_node,
const std::uint8_t intersection_lanes) const
{
const auto considered_lanes =
(intersection_lanes == 0) ? ASSUMED_LANE_COUNT : intersection_lanes;
// we first extract all coordinates from the road
auto coordinates =
GetCoordinatesAlongRoad(intersection_node, turn_edge, traversed_in_reverse, to_node);
/* if we are looking at a straight line, we don't care where exactly the coordinate
* is. Simply return the final coordinate. Turn angles/turn vectors are the same no matter which
* coordinate we look at.
*/
if (coordinates.size() <= 2)
return coordinates.back();
// fallback, mostly necessary for dead ends
if (intersection_node == to_node)
return TrimCoordinatesToLength(coordinates, 5).back();
const auto lookahead_distance =
FAR_LOOKAHEAD_DISTANCE + considered_lanes * ASSUMED_LANE_WIDTH * 0.5;
// reduce coordinates to the ones we care about
coordinates = TrimCoordinatesToLength(std::move(coordinates), lookahead_distance);
// If this reduction leaves us with only two coordinates, the turns/angles are represented in a
// valid way. Only curved roads and other difficult scenarios will require multiple coordinates.
if (coordinates.size() == 2)
return coordinates.back();
const auto &turn_edge_data = node_based_graph.GetEdgeData(turn_edge);
const util::Coordinate turn_coordinate =
node_coordinates[traversed_in_reverse ? to_node : intersection_node];
// Low priority roads are usually modelled very strangely. The roads are so small, though, that
// our basic heuristic looking at the road should be fine.
if (turn_edge_data.road_classification.IsLowPriorityRoadClass())
{
// Look ahead a tiny bit. Low priority road classes can be modelled fairly distinct in the
// very first part of the road
coordinates = TrimCoordinatesToLength(std::move(coordinates), 10);
if (coordinates.size() > 2 &&
util::coordinate_calculation::haversineDistance(turn_coordinate, coordinates[1]) <
ASSUMED_LANE_WIDTH)
return GetCorrectedCoordinate(turn_coordinate, coordinates[1], coordinates.back());
else
return coordinates.back();
}
/*
* The coordinates along the road are in different distances from the source. If only very few
* coordinates are close to the intersection, It might just be we simply looked to far down the
* road. We can decide to weight coordinates differently based on their distance from the
* intersection.
* In addition, changes very close to an intersection indicate graphical representation of the
* intersection over perceived turn angles.
*
* a -
* \
* -------------------- b
*
* Here the initial angle close to a might simply be due to OSM-Ways being located in the middle
* of the actual roads. If a road splits in two, the ways for the separate direction can be
* modeled very far apart with a steep angle at the split, even though the roads actually don't
* take a turn. The distance between the coordinates can be an indicator for these small changes
*/
const auto segment_distances = [&coordinates]() {
std::vector<double> segment_distances;
segment_distances.reserve(coordinates.size());
// sentinel
auto last_coordinate = coordinates.front();
boost::range::transform(coordinates,
std::back_inserter(segment_distances),
[&last_coordinate](const util::Coordinate current_coordinate) {
const auto distance =
util::coordinate_calculation::haversineDistance(
last_coordinate, current_coordinate);
last_coordinate = current_coordinate;
return distance;
});
return segment_distances;
}();
/* if the very first coordinate along the road is reasonably far away from the road, we assume
* the coordinate to correctly represent the turn. This could probably be improved using
* information on the very first turn angle (requires knowledge about previous road) and the
* respective lane widths.
*/
const bool first_coordinate_is_far_away = [&segment_distances, considered_lanes]() {
const auto required_distance =
considered_lanes * 0.5 * ASSUMED_LANE_WIDTH + LOOKAHEAD_DISTANCE_WITHOUT_LANES;
return segment_distances[1] > required_distance;
}();
if (first_coordinate_is_far_away)
{
return coordinates[1];
}
const double max_deviation_from_straight = GetMaxDeviation(
coordinates.begin(), coordinates.end(), coordinates.front(), coordinates.back());
// if the deviation from a straight line is small, we can savely use the coordinate. We use half
// a lane as heuristic to determine if the road is straight enough.
if (max_deviation_from_straight < 0.5 * ASSUMED_LANE_WIDTH)
{
return coordinates.back();
}
/*
* if a road turns barely in the beginning, it is similar to the first coordinate being
* sufficiently far ahead.
* possible negative:
* http://www.openstreetmap.org/search?query=52.514503%2013.32252#map=19/52.51450/13.32252
*/
const auto straight_distance_and_index = [&]() {
auto straight_distance = segment_distances[1];
std::size_t index;
for (index = 2; index < coordinates.size(); ++index)
{
// check the deviation from a straight line
if (GetMaxDeviation(coordinates.begin(),
coordinates.begin() + index,
coordinates.front(),
*(coordinates.begin() + index)) < 0.25 * ASSUMED_LANE_WIDTH)
straight_distance += segment_distances[index];
else
break;
}
return std::make_pair(index - 1, straight_distance);
}();
const auto straight_distance = straight_distance_and_index.second;
const auto straight_index = straight_distance_and_index.first;
const bool starts_of_without_turn = [&]() {
return straight_distance >=
considered_lanes * 0.5 * ASSUMED_LANE_WIDTH + LOOKAHEAD_DISTANCE_WITHOUT_LANES;
}();
if (starts_of_without_turn)
{
// skip over repeated coordinates
return TrimCoordinatesToLength(std::move(coordinates), 5).back();
}
// compute the regression vector based on the sum of least squares
const auto regression_line = RegressionLine(coordinates);
/*
* If we can find a line that represents the full set of coordinates within a certain range in
* relation to ASSUMED_LANE_WIDTH, we use the regression line to express the turn angle.
* This yields a transformation similar to:
*
* c d d
* b -> c
* b
* a a
*/
const double max_deviation_from_regression = GetMaxDeviation(
coordinates.begin(), coordinates.end(), regression_line.first, regression_line.second);
if (max_deviation_from_regression < 0.35 * ASSUMED_LANE_WIDTH)
{
// We use the locations on the regression line to offset the regression line onto the
// intersection.
const auto coord_between_front =
util::coordinate_calculation::projectPointOnSegment(
regression_line.first, regression_line.second, coordinates.front())
.second;
const auto coord_between_back =
util::coordinate_calculation::projectPointOnSegment(
regression_line.first, regression_line.second, coordinates.back())
.second;
return GetCorrectedCoordinate(turn_coordinate, coord_between_front, coord_between_back);
}
const auto total_distance =
std::accumulate(segment_distances.begin(), segment_distances.end(), 0.);
if (IsDirectOffset(coordinates,
straight_index,
straight_distance,
total_distance,
segment_distances,
considered_lanes))
{
// could be too agressive? Depend on lanes to check how far we want to go out?
// compare
// http://www.openstreetmap.org/search?query=52.411243%2013.363575#map=19/52.41124/13.36357
const auto offset_index = std::max<decltype(straight_index)>(1, straight_index);
return GetCorrectedCoordinate(
turn_coordinate, coordinates[offset_index], coordinates[offset_index + 1]);
}
if (IsCurve(coordinates,
segment_distances,
total_distance,
considered_lanes * 0.5 * ASSUMED_LANE_WIDTH,
turn_edge_data))
{
/*
* In curves we now have to distinguish between larger curves and tiny curves modelling the
* actual turn in the beginnig.
*
* We distinguish between turns that simply model the initial way of getting onto the
* destination lanes and the ones that performa a larger turn.
*/
const double offset = 0.5 * considered_lanes * ASSUMED_LANE_WIDTH;
coordinates = TrimCoordinatesToLength(std::move(coordinates), offset);
const auto vector_head = coordinates.back();
coordinates = TrimCoordinatesToLength(std::move(coordinates), offset);
BOOST_ASSERT(coordinates.size() >= 2);
return GetCorrectedCoordinate(turn_coordinate, coordinates.back(), vector_head);
}
{
// skip over the first coordinates, in specific the assumed lane count. We add a small
// safety factor, to not overshoot on the regression
const auto trimmed_coordinates = TrimCoordinatesByLengthFront(
coordinates, 0.8 * (considered_lanes * ASSUMED_LANE_WIDTH));
if (trimmed_coordinates.size() >= 2)
{
// get the regression line
const auto regression_line_trimmed = RegressionLine(trimmed_coordinates);
// and compute the maximum deviation from it
const auto max_deviation_from_trimmed_regression =
GetMaxDeviation(trimmed_coordinates.begin(),
trimmed_coordinates.end(),
regression_line_trimmed.first,
regression_line_trimmed.second);
if (max_deviation_from_trimmed_regression < 0.5 * ASSUMED_LANE_WIDTH)
return GetCorrectedCoordinate(
turn_coordinate, regression_line_trimmed.first, regression_line_trimmed.second);
}
}
// We use the locations on the regression line to offset the regression line onto the
// intersection.
return TrimCoordinatesToLength(coordinates, LOOKAHEAD_DISTANCE_WITHOUT_LANES).back();
}
std::vector<util::Coordinate>
CoordinateExtractor::GetCoordinatesAlongRoad(const NodeID intersection_node,
const EdgeID turn_edge,
const bool traversed_in_reverse,
const NodeID to_node) const
{
if (!compressed_geometries.HasEntryForID(turn_edge))
{
if (traversed_in_reverse)
return {{node_coordinates[to_node]}, {node_coordinates[intersection_node]}};
else
return {{node_coordinates[intersection_node]}, {node_coordinates[to_node]}};
}
else
{
// extracts the geometry in coordinates from the compressed edge container
std::vector<util::Coordinate> result;
const auto &geometry = compressed_geometries.GetBucketReference(turn_edge);
result.reserve(geometry.size() + 2);
// the compressed edges contain node ids, we transfer them to coordinates accessing the
// node_coordinates array
const auto compressedGeometryToCoordinate =
[this](const CompressedEdgeContainer::OnewayCompressedEdge &compressed_edge)
-> util::Coordinate { return node_coordinates[compressed_edge.node_id]; };
// add the coordinates to the result in either normal or reversed order, based on
// traversed_in_reverse
if (traversed_in_reverse)
{
std::transform(geometry.rbegin(),
geometry.rend(),
std::back_inserter(result),
compressedGeometryToCoordinate);
result.push_back(node_coordinates[intersection_node]);
}
else
{
result.push_back(node_coordinates[intersection_node]);
std::transform(geometry.begin(),
geometry.end(),
std::back_inserter(result),
compressedGeometryToCoordinate);
}
return result;
}
}
double
CoordinateExtractor::GetMaxDeviation(std::vector<util::Coordinate>::const_iterator range_begin,
const std::vector<util::Coordinate>::const_iterator &range_end,
const util::Coordinate straight_begin,
const util::Coordinate straight_end) const
{
// compute the deviation of a single coordinate from a straight line
auto get_single_deviation = [&](const util::Coordinate coordinate) {
// find the projected coordinate
auto coord_between = util::coordinate_calculation::projectPointOnSegment(
straight_begin, straight_end, coordinate)
.second;
// and calculate the distance between the intermediate coordinate and the coordinate
// on the osrm-way
return util::coordinate_calculation::haversineDistance(coord_between, coordinate);
};
// note: we don't accumulate here but rather compute the maximum. The functor passed here is not
// summing up anything.
return std::accumulate(
range_begin, range_end, 0.0, [&](const double current, const util::Coordinate coordinate) {
return std::max(current, get_single_deviation(coordinate));
});
};
bool CoordinateExtractor::IsCurve(const std::vector<util::Coordinate> &coordinates,
const std::vector<double> &segment_distances,
const double segment_length,
const double considered_lane_width,
const util::NodeBasedEdgeData &edge_data) const
{
BOOST_ASSERT(coordinates.size() > 2);
// by default, we treat roundabout as curves
if (edge_data.roundabout)
return true;
// TODO we might have to fix this to better compensate for errors due to repeated coordinates
const bool takes_an_actual_turn = [&coordinates]() {
const auto begin_bearing =
util::coordinate_calculation::bearing(coordinates[0], coordinates[1]);
const auto end_bearing = util::coordinate_calculation::bearing(
coordinates[coordinates.size() - 2], coordinates[coordinates.size() - 1]);
const auto total_angle = angularDeviation(begin_bearing, end_bearing);
return total_angle > 0.5 * NARROW_TURN_ANGLE;
}();
if (!takes_an_actual_turn)
return false;
const auto get_deviation = [](const util::Coordinate line_start,
const util::Coordinate line_end,
const util::Coordinate point) {
// find the projected coordinate
auto coord_between =
util::coordinate_calculation::projectPointOnSegment(line_start, line_end, point).second;
// and calculate the distance between the intermediate coordinate and the coordinate
return util::coordinate_calculation::haversineDistance(coord_between, point);
};
// a curve needs to be on one side of the coordinate array
const bool all_same_side = [&]() {
if (coordinates.size() <= 3)
return true;
const bool ccw = util::coordinate_calculation::isCCW(
coordinates.front(), coordinates.back(), coordinates[1]);
return std::all_of(
coordinates.begin() + 2, coordinates.end() - 1, [&](const util::Coordinate coordinate) {
const bool compare_ccw = util::coordinate_calculation::isCCW(
coordinates.front(), coordinates.back(), coordinate);
return ccw == compare_ccw;
});
}();
if (!all_same_side)
return false;
// check if the deviation is a sequence that increases up to a maximum deviation and decreses
// after, following what we would expect from a modelled curve
bool has_up_down_deviation = false;
std::size_t maximum_deviation_index = 0;
double maximum_deviation = 0;
std::tie(has_up_down_deviation, maximum_deviation_index, maximum_deviation) =
[&coordinates, get_deviation]() -> std::tuple<bool, std::size_t, double> {
const auto increasing = [&](const util::Coordinate lhs, const util::Coordinate rhs) {
return get_deviation(coordinates.front(), coordinates.back(), lhs) <=
get_deviation(coordinates.front(), coordinates.back(), rhs);
};
const auto decreasing = [&](const util::Coordinate lhs, const util::Coordinate rhs) {
return get_deviation(coordinates.front(), coordinates.back(), lhs) >=
get_deviation(coordinates.front(), coordinates.back(), rhs);
};
if (coordinates.size() < 3)
return std::make_tuple(true, 0, 0.);
if (coordinates.size() == 3)
return std::make_tuple(
true, 1, get_deviation(coordinates.front(), coordinates.back(), coordinates[1]));
const auto maximum_itr =
std::is_sorted_until(coordinates.begin() + 1, coordinates.end(), increasing);
if (maximum_itr == coordinates.end())
return std::make_tuple(true, coordinates.size() - 1, 0.);
else if (std::is_sorted(maximum_itr, coordinates.end(), decreasing))
return std::make_tuple(
true,
std::distance(coordinates.begin(), maximum_itr),
get_deviation(coordinates.front(), coordinates.back(), *maximum_itr));
else
return std::make_tuple(false, 0, 0.);
}();
// a curve has increasing deviation from its front/back vertices to a certain point and after it
// only decreases
if (!has_up_down_deviation)
return false;
// if the maximum deviation is at a quarter of the total curve, we are probably looking at a
// normal turn
const auto distance_to_max_deviation = std::accumulate(
segment_distances.begin(), segment_distances.begin() + maximum_deviation_index, 0.);
if ((distance_to_max_deviation <= 0.35 * segment_length ||
maximum_deviation < std::max(0.3 * considered_lane_width, 0.5 * ASSUMED_LANE_WIDTH)) &&
segment_length > 10)
return false;
BOOST_ASSERT(coordinates.size() >= 3);
// Compute all turn angles along the road
const auto turn_angles = [coordinates]() {
std::vector<double> turn_angles;
turn_angles.reserve(coordinates.size() - 2);
for (std::size_t index = 0; index + 2 < coordinates.size(); ++index)
{
turn_angles.push_back(util::coordinate_calculation::computeAngle(
coordinates[index], coordinates[index + 1], coordinates[index + 2]));
}
return turn_angles;
}();
const bool curve_is_valid =
[&turn_angles, &segment_distances, &segment_length, &considered_lane_width]() {
// internal state for our lamdae
bool last_was_straight = false;
// a turn angle represents two segments between three coordinates. We initialize the
// distance with the very first segment length (in-segment) of the first turn-angle
double straight_distance = std::max(0., segment_distances[1] - considered_lane_width);
auto distance_itr = segment_distances.begin() + 1;
// every call to the lamda requires a call to the distances. They need to be aligned
BOOST_ASSERT(segment_distances.size() == turn_angles.size() + 2);
const auto detect_invalid_curve = [&](const double previous_angle,
const double current_angle) {
const auto both_actually_turn =
(angularDeviation(previous_angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE) &&
(angularDeviation(current_angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE);
// they cannot be straight, since they differ at least by FUZZY_ANGLE_DIFFERENCE
const auto turn_direction_switches =
(previous_angle > STRAIGHT_ANGLE) == (current_angle < STRAIGHT_ANGLE);
// a turn that switches direction mid-curve is not a valid curve
if (both_actually_turn && turn_direction_switches)
return true;
const bool is_straight = angularDeviation(current_angle, STRAIGHT_ANGLE) < 5;
++distance_itr;
if (is_straight)
{
// since the angle is straight, we augment it by the second part of the segment
straight_distance += *distance_itr;
if (last_was_straight && straight_distance > 0.3 * segment_length)
{
return true;
}
} // if a segment on its own is long enough, thats fair game as well
else if (straight_distance > 0.3 * segment_length)
return true;
else
{
// we reset the last distance, starting with the next in-segment again
straight_distance = *distance_itr;
}
last_was_straight = is_straight;
return false;
};
const auto end_of_straight_segment =
std::adjacent_find(turn_angles.begin(), turn_angles.end(), detect_invalid_curve);
// No curve should have a very long straight segment
return end_of_straight_segment == turn_angles.end();
}();
return (segment_length > 2 * considered_lane_width && curve_is_valid);
}
bool CoordinateExtractor::IsDirectOffset(const std::vector<util::Coordinate> &coordinates,
const std::size_t straight_index,
const double straight_distance,
const double segment_length,
const std::vector<double> &segment_distances,
const std::uint8_t considered_lanes) const
{
// check if a given length is with half a lane of the assumed lane offset
const auto IsCloseToLaneDistance = [considered_lanes](const double width) {
// a road usually is connected to the middle of the lanes. So the lane-offset has to
// consider half to road
const auto lane_offset = 0.5 * considered_lanes * ASSUMED_LANE_WIDTH;
return std::abs(width - lane_offset) < 0.5 * ASSUMED_LANE_WIDTH;
};
// Check whether the very first coordinate is simply an offset. This is the case if the initial
// vertex is close to the turn and the remaining coordinates are nearly straight.
const auto offset_index = std::max<decltype(straight_index)>(1, straight_index);
// we need at least a single coordinate
if (offset_index + 1 >= coordinates.size())
return false;
// the straight part has to be around the lane distance
if (!IsCloseToLaneDistance(segment_distances[offset_index]))
return false;
// the segment itself cannot be short
if (segment_length < 0.8 * FAR_LOOKAHEAD_DISTANCE)
return false;
// if the remaining segment is short, we don't consider it an offset
if ((segment_length - std::max(straight_distance, segment_distances[1])) > 0.1 * segment_length)
return false;
// finally, we cannot be far off from a straight line for the remaining coordinates
return 0.5 * ASSUMED_LANE_WIDTH > GetMaxDeviation(coordinates.begin() + offset_index,
coordinates.end(),
coordinates[offset_index],
coordinates.back());
}
std::vector<util::Coordinate>
CoordinateExtractor::TrimCoordinatesToLength(std::vector<util::Coordinate> coordinates,
const double desired_length) const
{
BOOST_ASSERT(coordinates.size() >= 2);
double distance_to_current_coordinate = 0;
for (std::size_t coordinate_index = 1; coordinate_index < coordinates.size();
++coordinate_index)
{
const auto distance_to_next_coordinate =
distance_to_current_coordinate +
util::coordinate_calculation::haversineDistance(coordinates[coordinate_index - 1],
coordinates[coordinate_index]);
// if we reached the number of coordinates, we can stop here
if (distance_to_next_coordinate >= desired_length)
{
coordinates.resize(coordinate_index + 1);
coordinates.back() = util::coordinate_calculation::interpolateLinear(
ComputeInterpolationFactor(
desired_length, distance_to_current_coordinate, distance_to_next_coordinate),
coordinates[coordinate_index - 1],
coordinates[coordinate_index]);
break;
}
// remember the accumulated distance
distance_to_current_coordinate = distance_to_next_coordinate;
}
if (coordinates.size() > 2 &&
util::coordinate_calculation::haversineDistance(coordinates[0], coordinates[1]) <= 1)
coordinates.erase(coordinates.begin() + 1);
BOOST_ASSERT(coordinates.size());
return coordinates;
}
util::Coordinate
CoordinateExtractor::GetCorrectedCoordinate(const util::Coordinate fixpoint,
const util::Coordinate vector_base,
const util::Coordinate vector_head) const
{
// if the coordinates are close together, we were not able to look far ahead, so
// we can use the end-coordinate
if (util::coordinate_calculation::haversineDistance(vector_base, vector_head) <
DESIRED_COORDINATE_DIFFERENCE)
return vector_head;
else
{
/* to correct for the initial offset, we move the lookahead coordinate close
* to the original road. We do so by subtracting the difference between the
* turn coordinate and the offset coordinate from the lookahead coordinge:
*
* a ------ b ------ c
* |
* d
* \
* \
* e
*
* is converted to:
*
* a ------ b ------ c
* \
* \
* e
*
* for turn node `b`, vector_base `d` and vector_head `e`
*/
const auto offset_percentage = 90;
const auto corrected_lon =
vector_head.lon -
util::FixedLongitude{offset_percentage *
static_cast<int>(vector_base.lon - fixpoint.lon) / 100};
const auto corrected_lat =
vector_head.lat -
util::FixedLatitude{offset_percentage *
static_cast<int>(vector_base.lat - fixpoint.lat) / 100};
return util::Coordinate(corrected_lon, corrected_lat);
}
};
std::vector<util::Coordinate>
CoordinateExtractor::SampleCoordinates(const std::vector<util::Coordinate> &coordinates,
const double max_sample_length,
const double rate) const
{
BOOST_ASSERT(rate > 0 && coordinates.size() >= 2);
// the return value
std::vector<util::Coordinate> sampled_coordinates;
sampled_coordinates.reserve(ceil(max_sample_length / rate) + 2);
// the very first coordinate is always part of the sample
sampled_coordinates.push_back(coordinates.front());
double carry_length = 0., total_length = 0.;
// interpolate coordinates as long as we are not past the desired length
const auto add_samples_until_length_limit = [&](const util::Coordinate previous_coordinate,
const util::Coordinate current_coordinate) {
// pretend to have found an element and stop the sampling
if (total_length > max_sample_length)
return true;
const auto distance_between = util::coordinate_calculation::haversineDistance(
previous_coordinate, current_coordinate);
if (carry_length + distance_between >= rate)
{
// within the current segment, there is at least a single coordinate that we want to
// sample. We extract all coordinates that are on our sampling intervals and update our
// local sampling item to reflect the travelled distance
const auto base_sampling = rate - carry_length;
// the number of samples in the interval is equal to the length of the interval (+ the
// already traversed part from the previous segment) divided by the sampling rate
BOOST_ASSERT(max_sample_length > total_length);
const std::size_t num_samples = std::floor(
(std::min(max_sample_length - total_length, distance_between) + carry_length) /
rate);
for (std::size_t sample_value = 0; sample_value < num_samples; ++sample_value)
{
const auto interpolation_factor = ComputeInterpolationFactor(
base_sampling + sample_value * rate, 0, distance_between);
auto sampled_coordinate = util::coordinate_calculation::interpolateLinear(
interpolation_factor, previous_coordinate, current_coordinate);
sampled_coordinates.emplace_back(sampled_coordinate);
}
// current length needs to reflect how much is missing to the next sample. Here we can
// ignore max sample range, because if we reached it, the loop is done anyhow
carry_length = (distance_between + carry_length) - (num_samples * rate);
}
else
{
// do the necessary bookkeeping and continue
carry_length += distance_between;
}
// the total length travelled is always updated by the full distance
total_length += distance_between;
return false;
};
// misuse of adjacent_find. Loop over coordinates, until a total sample length is reached
std::adjacent_find(coordinates.begin(), coordinates.end(), add_samples_until_length_limit);
return sampled_coordinates;
}
double CoordinateExtractor::ComputeInterpolationFactor(const double desired_distance,
const double distance_to_first,
const double distance_to_second) const
{
BOOST_ASSERT(distance_to_first < desired_distance);
double segment_length = distance_to_second - distance_to_first;
BOOST_ASSERT(segment_length > 0);
BOOST_ASSERT(distance_to_second >= desired_distance);
double missing_distance = desired_distance - distance_to_first;
return std::max(0., std::min(missing_distance / segment_length, 1.0));
}
std::vector<util::Coordinate>
CoordinateExtractor::TrimCoordinatesByLengthFront(std::vector<util::Coordinate> coordinates,
const double desired_length) const
{
double distance_to_index = 0;
std::size_t index = 0;
for (std::size_t next_index = 1; next_index < coordinates.size(); ++next_index)
{
const double next_distance =
distance_to_index + util::coordinate_calculation::haversineDistance(
coordinates[index], coordinates[next_index]);
if (next_distance >= desired_length)
{
const auto factor =
ComputeInterpolationFactor(desired_length, distance_to_index, next_distance);
auto interpolated_coordinate = util::coordinate_calculation::interpolateLinear(
factor, coordinates[index], coordinates[next_index]);
if (index > 0)
coordinates.erase(coordinates.begin(), coordinates.begin() + index);
coordinates.front() = interpolated_coordinate;
return coordinates;
}
distance_to_index = next_distance;
index = next_index;
}
// the coordinates in total are too short in length for the desired length
// this part is only reached when we don't return from within the above loop
coordinates.clear();
return coordinates;
}
std::pair<util::Coordinate, util::Coordinate>
CoordinateExtractor::RegressionLine(const std::vector<util::Coordinate> &coordinates) const
{
// create a sample of all coordinates to improve the quality of our regression vector
// (less dependent on modelling of the data in OSM)
const auto sampled_coordinates = SampleCoordinates(coordinates, FAR_LOOKAHEAD_DISTANCE, 1);
// compute the regression vector based on the sum of least squares
const auto regression_line = leastSquareRegression(sampled_coordinates);
const auto coord_between_front =
util::coordinate_calculation::projectPointOnSegment(
regression_line.first, regression_line.second, coordinates.front())
.second;
const auto coord_between_back =
util::coordinate_calculation::projectPointOnSegment(
regression_line.first, regression_line.second, coordinates.back())
.second;
return {coord_between_front, coord_between_back};
}
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -21,6 +21,8 @@ std::string toString(const ConnectedRoad &road)
result += std::to_string(road.entry_allowed);
result += " angle: ";
result += std::to_string(road.turn.angle);
result += " bearing: ";
result += std::to_string(road.turn.bearing);
result += " instruction: ";
result += std::to_string(static_cast<std::int32_t>(road.turn.instruction.type)) + " " +
std::to_string(static_cast<std::int32_t>(road.turn.instruction.direction_modifier)) +

View File

@ -26,7 +26,7 @@ IntersectionGenerator::IntersectionGenerator(
const CompressedEdgeContainer &compressed_edge_container)
: node_based_graph(node_based_graph), restriction_map(restriction_map),
barrier_nodes(barrier_nodes), node_info_list(node_info_list),
compressed_edge_container(compressed_edge_container)
coordinate_extractor(node_based_graph, compressed_edge_container, node_info_list)
{
}
@ -75,16 +75,21 @@ Intersection IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
bool has_uturn_edge = false;
bool uturn_could_be_valid = false;
const util::Coordinate turn_coordinate = node_info_list[turn_node];
const auto intersection_lanes = getLaneCountAtIntersection(turn_node, node_based_graph);
for (const EdgeID onto_edge : node_based_graph.GetAdjacentEdgeRange(turn_node))
{
BOOST_ASSERT(onto_edge != SPECIAL_EDGEID);
const NodeID to_node = node_based_graph.GetTarget(onto_edge);
const auto &onto_data = node_based_graph.GetEdgeData(onto_edge);
bool turn_is_valid =
// reverse edges are never valid turns because the resulting turn would look like this:
// from_node --via_edge--> turn_node <--onto_edge-- to_node
// however we need this for capture intersection shape for incoming one-ways
!node_based_graph.GetEdgeData(onto_edge).reversed &&
!onto_data.reversed &&
// we are not turning over a barrier
(!is_barrier_node || from_node == to_node) &&
// We are at an only_-restriction but not at the right turn.
@ -93,8 +98,17 @@ Intersection IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
!restriction_map.CheckIfTurnIsRestricted(from_node, turn_node, to_node);
auto angle = 0.;
double bearing = 0.;
// The first coordinate (the origin) can depend on the number of lanes turning onto,
// just as the target coordinate can. Here we compute the corrected coordinate for the
// incoming edge.
const auto first_coordinate = coordinate_extractor.GetCoordinateAlongRoad(
from_node, via_eid, INVERT, turn_node, intersection_lanes);
if (from_node == to_node)
{
bearing = util::coordinate_calculation::bearing(turn_coordinate, first_coordinate);
uturn_could_be_valid = turn_is_valid;
if (turn_is_valid && !is_barrier_node)
{
@ -121,33 +135,48 @@ Intersection IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
}
else
{
// unpack first node of second segment if packed
const auto first_coordinate = getRepresentativeCoordinate(
from_node, turn_node, via_eid, INVERT, compressed_edge_container, node_info_list);
const auto third_coordinate = getRepresentativeCoordinate(
turn_node, to_node, onto_edge, !INVERT, compressed_edge_container, node_info_list);
// the default distance we lookahead on a road. This distance prevents small mapping
// errors to impact the turn angles.
const auto third_coordinate = coordinate_extractor.GetCoordinateAlongRoad(
turn_node, onto_edge, !INVERT, to_node, intersection_lanes);
angle = util::coordinate_calculation::computeAngle(
first_coordinate, node_info_list[turn_node], third_coordinate);
first_coordinate, turn_coordinate, third_coordinate);
bearing = util::coordinate_calculation::bearing(turn_coordinate, third_coordinate);
if (std::abs(angle) < std::numeric_limits<double>::epsilon())
has_uturn_edge = true;
}
intersection.push_back(
ConnectedRoad(TurnOperation{onto_edge,
angle,
bearing,
{TurnType::Invalid, DirectionModifier::UTurn},
INVALID_LANE_DATAID},
turn_is_valid));
}
// We hit the case of a street leading into nothing-ness. Since the code here assumes that this
// We hit the case of a street leading into nothing-ness. Since the code here assumes
// that this
// will never happen we add an artificial invalid uturn in this case.
if (!has_uturn_edge)
{
intersection.push_back(
{TurnOperation{
via_eid, 0., {TurnType::Invalid, DirectionModifier::UTurn}, INVALID_LANE_DATAID},
false});
const auto first_coordinate = coordinate_extractor.GetCoordinateAlongRoad(
from_node,
via_eid,
INVERT,
turn_node,
node_based_graph.GetEdgeData(via_eid).road_classification.GetNumberOfLanes());
const double bearing =
util::coordinate_calculation::bearing(turn_coordinate, first_coordinate);
intersection.push_back({TurnOperation{via_eid,
0.,
bearing,
{TurnType::Invalid, DirectionModifier::UTurn},
INVALID_LANE_DATAID},
false});
}
const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second) {
@ -162,7 +191,8 @@ Intersection IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
boost::count_if(intersection, [](const ConnectedRoad &road) { return road.entry_allowed; });
if (0 == valid_count && uturn_could_be_valid)
{
// after intersections sorting by angles, find the u-turn with (from_node == to_node)
// after intersections sorting by angles, find the u-turn with (from_node ==
// to_node)
// that was inserted together with setting uturn_could_be_valid flag
std::size_t self_u_turn = 0;
while (self_u_turn < intersection.size() &&
@ -175,7 +205,6 @@ Intersection IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
BOOST_ASSERT(from_node == node_based_graph.GetTarget(intersection[self_u_turn].turn.eid));
intersection[self_u_turn].entry_allowed = true;
}
return intersection;
}
@ -219,15 +248,21 @@ bool IntersectionGenerator::CanMerge(const NodeID node_at_intersection,
const auto angle_between = angularDeviation(intersection[first_index].turn.angle,
intersection[second_index].turn.angle);
const auto coordinate_at_in_edge =
getRepresentativeCoordinate(node_at_intersection,
node_based_graph.GetTarget(intersection[0].turn.eid),
intersection[0].turn.eid,
false,
compressed_edge_container,
node_info_list);
const auto intersection_lanes =
getLaneCountAtIntersection(node_at_intersection, node_based_graph);
const auto coordinate_at_in_edge = coordinate_extractor.GetCoordinateAlongRoad(
node_at_intersection,
intersection[0].turn.eid,
!INVERT,
node_based_graph.GetTarget(intersection[0].turn.eid),
intersection_lanes);
const auto coordinate_at_intersection = node_info_list[node_at_intersection];
if (angle_between >= 120)
return false;
const auto isValidYArm = [this,
intersection,
coordinate_at_in_edge,
@ -253,20 +288,19 @@ bool IntersectionGenerator::CanMerge(const NodeID node_at_intersection,
coordinate_at_in_edge, coordinate_at_intersection, coordinate_at_target);
const auto other_turn_angle = util::coordinate_calculation::computeAngle(
coordinate_at_in_edge, coordinate_at_intersection, coordinate_at_other_target);
const double distance_to_target = util::coordinate_calculation::haversineDistance(
coordinate_at_intersection, coordinate_at_target);
const constexpr double MAX_COLLAPSE_DISTANCE = 30;
if (distance_to_target < MAX_COLLAPSE_DISTANCE)
return false;
const bool becomes_narrower =
angularDeviation(turn_angle, other_turn_angle) < NARROW_TURN_ANGLE &&
angularDeviation(turn_angle, other_turn_angle) <
angularDeviation(turn_angle, other_turn_angle) <=
angularDeviation(intersection[index].turn.angle,
intersection[other_index].turn.angle);
return becomes_narrower;
const bool has_same_deviation =
std::abs(angularDeviation(intersection[index].turn.angle, STRAIGHT_ANGLE) -
angularDeviation(intersection[other_index].turn.angle, STRAIGHT_ANGLE)) <
MAXIMAL_ALLOWED_NO_TURN_DEVIATION;
return becomes_narrower || has_same_deviation;
};
const bool is_y_arm_first = isValidYArm(first_index, second_index);
@ -345,33 +379,37 @@ Intersection IntersectionGenerator::MergeSegregatedRoads(const NodeID intersecti
return (index + intersection.size() - 1) % intersection.size();
};
const auto merge = [](const ConnectedRoad &first,
const ConnectedRoad &second) -> ConnectedRoad {
if (!first.entry_allowed)
{
ConnectedRoad result = second;
result.turn.angle = (first.turn.angle + second.turn.angle) / 2;
if (first.turn.angle - second.turn.angle > 180)
result.turn.angle += 180;
if (result.turn.angle > 360)
result.turn.angle -= 360;
// 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;
};
return result;
}
// 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
{
BOOST_ASSERT(!second.entry_allowed);
ConnectedRoad result = first;
result.turn.angle = (first.turn.angle + second.turn.angle) / 2;
if (first.turn.angle - second.turn.angle > 180)
result.turn.angle += 180;
if (result.turn.angle > 360)
result.turn.angle -= 360;
return result;
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;
}
};
const auto merge = [combineAngles](const ConnectedRoad &first,
const ConnectedRoad &second) -> ConnectedRoad {
ConnectedRoad result = first.entry_allowed ? first : second;
result.turn.angle = combineAngles(first.turn.angle, second.turn.angle);
result.turn.bearing = combineAngles(first.turn.bearing, second.turn.bearing);
BOOST_ASSERT(0 <= result.turn.angle && result.turn.angle <= 360.0);
BOOST_ASSERT(0 <= result.turn.bearing && result.turn.bearing <= 360.0);
return result;
};
if (intersection.size() <= 1)
return intersection;
@ -426,10 +464,10 @@ Intersection IntersectionGenerator::MergeSegregatedRoads(const NodeID intersecti
for (std::size_t i = 1; i + 1 < intersection.size(); ++i)
intersection[i].turn.angle += correction_factor;
// FIXME if we have a left-sided country, we need to switch this off and enable it below
// FIXME if we have a left-sided country, we need to switch this off and enable it
// below
intersection[0] = merge(intersection.front(), intersection.back());
intersection[0].turn.angle = 0;
intersection.pop_back();
}
else if (CanMerge(intersection_node, intersection, 0, 1))
@ -511,14 +549,6 @@ Intersection IntersectionGenerator::AdjustForJoiningRoads(const NodeID node_at_i
for (std::size_t index = 1; index < intersection.size(); ++index)
{
auto &road = intersection[index];
// 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 =
GetConnectedRoads(node_at_intersection, road.turn.eid);
if (next_intersection_along_road.size() <= 1)
continue;
const auto node_at_next_intersection = node_based_graph.GetTarget(road.turn.eid);
const util::Coordinate coordinate_at_next_intersection =
node_info_list[node_at_next_intersection];
@ -535,6 +565,16 @@ Intersection IntersectionGenerator::AdjustForJoiningRoads(const NodeID node_at_i
return angle;
};
const auto range = node_based_graph.GetAdjacentEdgeRange(node_at_next_intersection);
if (range.size() <= 1)
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 =
GetConnectedRoads(node_at_intersection, road.turn.eid);
// 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.
@ -545,6 +585,7 @@ Intersection IntersectionGenerator::AdjustForJoiningRoads(const NodeID node_at_i
// at the target intersection, we merge to the right, so we need to shift the current
// angle to the left
road.turn.angle = adjustAngle(road.turn.angle, offset);
road.turn.bearing = adjustAngle(road.turn.bearing, offset);
}
else if (CanMerge(node_at_next_intersection,
next_intersection_along_road,
@ -559,6 +600,7 @@ Intersection IntersectionGenerator::AdjustForJoiningRoads(const NodeID node_at_i
// at the target intersection, we merge to the left, so we need to shift the current
// angle to the right
road.turn.angle = adjustAngle(road.turn.angle, -offset);
road.turn.bearing = adjustAngle(road.turn.bearing, -offset);
}
}
return intersection;

View File

@ -395,18 +395,6 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
const EdgeData &in_data = node_based_graph.GetEdgeData(via_edge);
const auto in_classification = in_data.road_classification;
const auto obvious_by_road_class = [](const RoadClassification in_classification,
const RoadClassification obvious_candidate,
const RoadClassification compare_candidate) {
const bool has_high_priority =
PRIORITY_DISTINCTION_FACTOR * obvious_candidate.GetPriority() <
compare_candidate.GetPriority();
const bool continues_on_same_class = in_classification == obvious_candidate;
return (has_high_priority && continues_on_same_class) ||
(!obvious_candidate.IsLowPriorityRoadClass() &&
compare_candidate.IsLowPriorityRoadClass());
};
for (std::size_t i = 1; i < intersection.size(); ++i)
{
const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE);
@ -434,15 +422,19 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
node_based_graph.GetEdgeData(intersection[best_continue].turn.eid).road_classification;
// don't prefer low priority classes
if (out_data.road_classification.IsLowPriorityRoadClass() &&
if (best != 0 && out_data.road_classification.IsLowPriorityRoadClass() &&
!current_best_class.IsLowPriorityRoadClass())
continue;
const bool is_better_choice_by_priority = obvious_by_road_class(
in_data.road_classification, out_data.road_classification, current_best_class);
const bool is_better_choice_by_priority =
best == 0 || obviousByRoadClass(in_data.road_classification,
out_data.road_classification,
current_best_class);
const bool other_is_better_choice_by_priority = obvious_by_road_class(
in_data.road_classification, current_best_class, out_data.road_classification);
const bool other_is_better_choice_by_priority =
best != 0 && obviousByRoadClass(in_data.road_classification,
current_best_class,
out_data.road_classification);
if ((!other_is_better_choice_by_priority && deviation < best_deviation) ||
is_better_choice_by_priority)
@ -503,11 +495,53 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
// has no obvious continued road
const auto &best_data = node_based_graph.GetEdgeData(intersection[best].turn.eid);
if (best_continue == 0 || (!all_continues_are_narrow &&
(num_continue_names.first >= 2 && intersection.size() >= 4)) ||
(num_continue_names.second >= 2 && best_continue_deviation >= 2 * NARROW_TURN_ANGLE) ||
(best_deviation != best_continue_deviation && best_deviation < FUZZY_ANGLE_DIFFERENCE &&
!best_data.road_classification.IsRampClass()))
const auto check_non_continue = [&]() {
// no continue road exists
if (best_continue == 0)
return true;
// we have multiple continues and not all are narrow (treat all the same)
if (!all_continues_are_narrow &&
(num_continue_names.first >= 2 && intersection.size() >= 4))
return true;
// if the best continue is not narrow and we also have at least 2 possible choices, the
// intersection size does not matter anymore
if (num_continue_names.second >= 2 && best_continue_deviation >= 2 * NARROW_TURN_ANGLE)
return true;
// continue data now most certainly exists
const auto &continue_data =
node_based_graph.GetEdgeData(intersection[best_continue].turn.eid);
if (obviousByRoadClass(in_data.road_classification,
continue_data.road_classification,
best_data.road_classification))
return false;
if (obviousByRoadClass(in_data.road_classification,
best_data.road_classification,
continue_data.road_classification))
return true;
// the best deviation is very straight and not a ramp
if (best_deviation < best_continue_deviation && best_deviation < FUZZY_ANGLE_DIFFERENCE &&
!best_data.road_classification.IsRampClass())
return true;
// the continue road is of a lower priority, while the road continues on the same priority
// with a better angle
if (best_deviation < best_continue_deviation &&
in_data.road_classification == best_data.road_classification &&
continue_data.road_classification.GetPriority() >
best_data.road_classification.GetPriority())
return true;
return false;
}();
if (check_non_continue)
{
// Find left/right deviation
// skipping over service roads
@ -517,9 +551,9 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
return index_candidate;
const auto &candidate_data =
node_based_graph.GetEdgeData(intersection[index_candidate].turn.eid);
if (obvious_by_road_class(in_data.road_classification,
best_data.road_classification,
candidate_data.road_classification))
if (obviousByRoadClass(in_data.road_classification,
best_data.road_classification,
candidate_data.road_classification))
return (index_candidate + 1) % intersection.size();
else
return index_candidate;
@ -532,9 +566,9 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
return index_candidate;
const auto candidate_data =
node_based_graph.GetEdgeData(intersection[index_candidate].turn.eid);
if (obvious_by_road_class(in_data.road_classification,
best_data.road_classification,
candidate_data.road_classification))
if (obviousByRoadClass(in_data.road_classification,
best_data.road_classification,
candidate_data.road_classification))
return index_candidate - 1;
else
return index_candidate;
@ -553,13 +587,13 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
const auto &right_data = node_based_graph.GetEdgeData(intersection[right_index].turn.eid);
const bool obvious_to_left =
left_index == 0 || obvious_by_road_class(in_data.road_classification,
best_data.road_classification,
left_data.road_classification);
left_index == 0 || obviousByRoadClass(in_data.road_classification,
best_data.road_classification,
left_data.road_classification);
const bool obvious_to_right =
right_index == 0 || obvious_by_road_class(in_data.road_classification,
best_data.road_classification,
right_data.road_classification);
right_index == 0 || obviousByRoadClass(in_data.road_classification,
best_data.road_classification,
right_data.road_classification);
// if the best turn isn't narrow, but there is a nearly straight turn, we don't consider the
// turn obvious
@ -624,9 +658,9 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
const auto &turn_data = node_based_graph.GetEdgeData(intersection[i].turn.eid);
const bool is_obvious_by_road_class =
obvious_by_road_class(in_data.road_classification,
continue_data.road_classification,
turn_data.road_classification);
obviousByRoadClass(in_data.road_classification,
continue_data.road_classification,
turn_data.road_classification);
// if the main road is obvious by class, we ignore the current road as a potential
// prevention of obviousness

View File

@ -494,12 +494,6 @@ Intersection MotorwayHandler::fallback(Intersection intersection) const
{
for (auto &road : intersection)
{
const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
util::SimpleLogger().Write(logDEBUG)
<< "road: " << toString(road) << " Name: " << out_data.name_id
<< " Road Class: " << out_data.road_classification.ToString();
if (!road.entry_allowed)
continue;

View File

@ -32,7 +32,8 @@ RoundaboutHandler::RoundaboutHandler(const util::NodeBasedDynamicGraph &node_bas
name_table,
street_name_suffix_table,
intersection_generator),
compressed_edge_container(compressed_edge_container), profile_properties(profile_properties)
compressed_edge_container(compressed_edge_container), profile_properties(profile_properties),
coordinate_extractor(node_based_graph, compressed_edge_container, node_info_list)
{
}
@ -180,13 +181,14 @@ bool RoundaboutHandler::qualifiesAsRoundaboutIntersection(
// there is a single non-roundabout edge
const auto src_coordinate = getCoordinate(node);
const auto next_coordinate =
getRepresentativeCoordinate(node,
node_based_graph.GetTarget(edge),
edge,
edge_data.reversed,
compressed_edge_container,
node_info_list);
const auto next_coordinate = coordinate_extractor.GetCoordinateAlongRoad(
node,
edge,
edge_data.reversed,
node_based_graph.GetTarget(edge),
getLaneCountAtIntersection(node, node_based_graph));
result.push_back(
util::coordinate_calculation::bearing(src_coordinate, next_coordinate));
break;

View File

@ -27,49 +27,30 @@ struct TurnPossibility
};
std::pair<util::guidance::EntryClass, util::guidance::BearingClass>
classifyIntersection(NodeID nid,
const Intersection &intersection,
const util::NodeBasedDynamicGraph &node_based_graph,
const extractor::CompressedEdgeContainer &compressed_geometries,
const std::vector<extractor::QueryNode> &query_nodes)
classifyIntersection(Intersection intersection)
{
if (intersection.empty())
return {};
std::vector<TurnPossibility> turns;
const auto node_coordinate = util::Coordinate(query_nodes[nid].lon, query_nodes[nid].lat);
// generate a list of all turn angles between a base edge, the node and a current edge
for (const auto &road : intersection)
{
const auto eid = road.turn.eid;
const auto edge_coordinate = getRepresentativeCoordinate(
nid, node_based_graph.GetTarget(eid), eid, false, compressed_geometries, query_nodes);
const double bearing =
util::coordinate_calculation::bearing(node_coordinate, edge_coordinate);
turns.push_back({road.entry_allowed, bearing});
}
std::sort(
turns.begin(), turns.end(), [](const TurnPossibility left, const TurnPossibility right) {
return left.bearing < right.bearing;
});
std::sort(intersection.begin(),
intersection.end(),
[](const ConnectedRoad &left, const ConnectedRoad &right) {
return left.turn.bearing < right.turn.bearing;
});
util::guidance::EntryClass entry_class;
util::guidance::BearingClass bearing_class;
const bool canBeDiscretized = [&]() {
if (turns.size() <= 1)
if (intersection.size() <= 1)
return true;
DiscreteBearing last_discrete_bearing =
util::guidance::BearingClass::getDiscreteBearing(std::round(turns.back().bearing));
for (const auto turn : turns)
DiscreteBearing last_discrete_bearing = util::guidance::BearingClass::getDiscreteBearing(
std::round(intersection.back().turn.bearing));
for (const auto road : intersection)
{
const DiscreteBearing discrete_bearing =
util::guidance::BearingClass::getDiscreteBearing(std::round(turn.bearing));
util::guidance::BearingClass::getDiscreteBearing(std::round(road.turn.bearing));
if (discrete_bearing == last_discrete_bearing)
return false;
last_discrete_bearing = discrete_bearing;
@ -81,18 +62,18 @@ classifyIntersection(NodeID nid,
std::size_t number = 0;
if (canBeDiscretized)
{
if (util::guidance::BearingClass::getDiscreteBearing(turns.back().bearing) <
util::guidance::BearingClass::getDiscreteBearing(turns.front().bearing))
if (util::guidance::BearingClass::getDiscreteBearing(intersection.back().turn.bearing) <
util::guidance::BearingClass::getDiscreteBearing(intersection.front().turn.bearing))
{
turns.insert(turns.begin(), turns.back());
turns.pop_back();
intersection.insert(intersection.begin(), intersection.back());
intersection.pop_back();
}
for (const auto turn : turns)
for (const auto &road : intersection)
{
if (turn.entry_allowed)
if (road.entry_allowed)
entry_class.activate(number);
auto discrete_bearing_class =
util::guidance::BearingClass::getDiscreteBearing(std::round(turn.bearing));
util::guidance::BearingClass::getDiscreteBearing(std::round(road.turn.bearing));
bearing_class.add(std::round(discrete_bearing_class *
util::guidance::BearingClass::discrete_step_size));
++number;
@ -100,11 +81,11 @@ classifyIntersection(NodeID nid,
}
else
{
for (const auto turn : turns)
for (const auto &road : intersection)
{
if (turn.entry_allowed)
if (road.entry_allowed)
entry_class.activate(number);
bearing_class.add(std::round(turn.bearing));
bearing_class.add(std::round(road.turn.bearing));
++number;
}
}

View File

@ -136,7 +136,7 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
OOOOOOO
*/
const auto fork_range = findFork(via_edge, intersection);
if (fork_range.first == 1 && fork_range.second == 2)
if (fork_range.first == 1 && fork_range.second == 2 && obvious_index == 0)
assignFork(via_edge, intersection[2], intersection[1]);
/* T Intersection
@ -525,6 +525,7 @@ std::pair<std::size_t, std::size_t> TurnHandler::findFork(const EdgeID via_edge,
return false;
}();
// A fork can only happen between edges of similar types where none of the ones is obvious
const bool has_compatible_classes = [&]() {
const bool ramp_class = node_based_graph.GetEdgeData(intersection[right].turn.eid)
.road_classification.IsLinkClass();
@ -533,6 +534,27 @@ std::pair<std::size_t, std::size_t> TurnHandler::findFork(const EdgeID via_edge,
node_based_graph.GetEdgeData(intersection[index].turn.eid)
.road_classification.IsLinkClass())
return false;
const auto in_classification =
node_based_graph.GetEdgeData(intersection[0].turn.eid).road_classification;
for (std::size_t base_index = right; base_index <= left; ++base_index)
{
const auto base_classification =
node_based_graph.GetEdgeData(intersection[base_index].turn.eid)
.road_classification;
for (std::size_t compare_index = right; compare_index <= left; ++compare_index)
{
if (base_index == compare_index)
continue;
const auto compare_classification =
node_based_graph.GetEdgeData(intersection[compare_index].turn.eid)
.road_classification;
if (obviousByRoadClass(
in_classification, base_classification, compare_classification))
return false;
}
}
return true;
}();
@ -657,20 +679,20 @@ void TurnHandler::handleDistinctConflict(const EdgeID via_edge,
if (getTurnDirection(left.turn.angle) == DirectionModifier::Right)
{
if (angularDeviation(left.turn.angle, 90) > angularDeviation(right.turn.angle, 90))
{
left.turn.instruction = {left_type, DirectionModifier::SlightRight};
right.turn.instruction = {right_type, DirectionModifier::Right};
}
else
if (angularDeviation(left.turn.angle, 85) >= angularDeviation(right.turn.angle, 85))
{
left.turn.instruction = {left_type, DirectionModifier::Right};
right.turn.instruction = {right_type, DirectionModifier::SharpRight};
}
else
{
left.turn.instruction = {left_type, DirectionModifier::SlightRight};
right.turn.instruction = {right_type, DirectionModifier::Right};
}
}
else
{
if (angularDeviation(left.turn.angle, 270) > angularDeviation(right.turn.angle, 270))
if (angularDeviation(left.turn.angle, 265) >= angularDeviation(right.turn.angle, 265))
{
left.turn.instruction = {left_type, DirectionModifier::SharpLeft};
right.turn.instruction = {right_type, DirectionModifier::Left};

View File

@ -169,7 +169,10 @@ void LuaScriptingEnvironment::InitContext(LuaScriptingContext &context)
&guidance::RoadClassification::SetLowPriorityFlag)
.property("road_priority_class",
&guidance::RoadClassification::GetClass,
&guidance::RoadClassification::SetClass),
&guidance::RoadClassification::SetClass)
.property("num_lanes",
&guidance::RoadClassification::GetNumberOfLanes,
&guidance::RoadClassification::SetNumberOfLanes),
luabind::class_<ExtractionWay>("ResultWay")
// .def(luabind::constructor<>())

View File

@ -242,6 +242,10 @@ Storage::ReturnCode Storage::Run(int max_wait)
number_of_original_edges);
shared_layout_ptr->SetBlockSize<extractor::TravelMode>(SharedDataLayout::TRAVEL_MODE,
number_of_original_edges);
shared_layout_ptr->SetBlockSize<util::guidance::TurnBearing>(SharedDataLayout::PRE_TURN_BEARING,
number_of_original_edges);
shared_layout_ptr->SetBlockSize<util::guidance::TurnBearing>(
SharedDataLayout::POST_TURN_BEARING, number_of_original_edges);
shared_layout_ptr->SetBlockSize<extractor::guidance::TurnInstruction>(
SharedDataLayout::TURN_INSTRUCTION, number_of_original_edges);
shared_layout_ptr->SetBlockSize<LaneDataID>(SharedDataLayout::LANE_DATA_ID,
@ -550,6 +554,12 @@ Storage::ReturnCode Storage::Run(int max_wait)
extractor::TravelMode *travel_mode_ptr =
shared_layout_ptr->GetBlockPtr<extractor::TravelMode, true>(shared_memory_ptr,
SharedDataLayout::TRAVEL_MODE);
util::guidance::TurnBearing *pre_turn_bearing_ptr =
shared_layout_ptr->GetBlockPtr<util::guidance::TurnBearing, true>(
shared_memory_ptr, SharedDataLayout::PRE_TURN_BEARING);
util::guidance::TurnBearing *post_turn_bearing_ptr =
shared_layout_ptr->GetBlockPtr<util::guidance::TurnBearing, true>(
shared_memory_ptr, SharedDataLayout::POST_TURN_BEARING);
LaneDataID *lane_data_id_ptr = shared_layout_ptr->GetBlockPtr<LaneDataID, true>(
shared_memory_ptr, SharedDataLayout::LANE_DATA_ID);
@ -571,6 +581,8 @@ Storage::ReturnCode Storage::Run(int max_wait)
lane_data_id_ptr[i] = current_edge_data.lane_data_id;
turn_instructions_ptr[i] = current_edge_data.turn_instruction;
entry_class_id_ptr[i] = current_edge_data.entry_classid;
pre_turn_bearing_ptr[i] = current_edge_data.pre_turn_bearing;
post_turn_bearing_ptr[i] = current_edge_data.post_turn_bearing;
}
edges_input_stream.close();
@ -774,20 +786,24 @@ Storage::ReturnCode Storage::Run(int max_wait)
static_cast<SharedDataTimestamp *>(data_type_memory->Ptr());
{
boost::interprocess::scoped_lock<boost::interprocess::named_upgradable_mutex>
boost::interprocess::scoped_lock<boost::interprocess::named_upgradable_mutex>
current_regions_exclusive_lock;
if (max_wait > 0)
{
util::SimpleLogger().Write() << "Waiting for " << max_wait << " seconds to write new dataset timestamp";
auto end_time = boost::posix_time::microsec_clock::universal_time() + boost::posix_time::seconds(max_wait);
util::SimpleLogger().Write() << "Waiting for " << max_wait
<< " seconds to write new dataset timestamp";
auto end_time = boost::posix_time::microsec_clock::universal_time() +
boost::posix_time::seconds(max_wait);
current_regions_exclusive_lock =
boost::interprocess::scoped_lock<boost::interprocess::named_upgradable_mutex>(
std::move(current_regions_lock), end_time);
if (!current_regions_exclusive_lock.owns())
{
util::SimpleLogger().Write(logWARNING) << "Aquiring the lock timed out after " << max_wait << " seconds. Claiming the lock by force.";
util::SimpleLogger().Write(logWARNING) << "Aquiring the lock timed out after "
<< max_wait
<< " seconds. Claiming the lock by force.";
current_regions_lock.unlock();
current_regions_lock.release();
storage::SharedBarriers::resetCurrentRegions();

View File

@ -292,6 +292,29 @@ Coordinate interpolateLinear(double factor, const Coordinate from, const Coordin
return {std::move(interpolated_lon), std::move(interpolated_lat)};
}
// compute the signed area of a triangle
double signedArea(const Coordinate first_coordinate,
const Coordinate second_coordinate,
const Coordinate third_coordinate)
{
const auto lat_1 = static_cast<double>(toFloating(first_coordinate.lat));
const auto lon_1 = static_cast<double>(toFloating(first_coordinate.lon));
const auto lat_2 = static_cast<double>(toFloating(second_coordinate.lat));
const auto lon_2 = static_cast<double>(toFloating(second_coordinate.lon));
const auto lat_3 = static_cast<double>(toFloating(third_coordinate.lat));
const auto lon_3 = static_cast<double>(toFloating(third_coordinate.lon));
return 0.5 * (-lon_2 * lat_1 + lon_3 * lat_1 + lon_1 * lat_2 - lon_3 * lat_2 - lon_1 * lat_3 +
lon_2 * lat_3);
}
// check if a set of three coordinates is given in CCW order
bool isCCW(const Coordinate first_coordinate,
const Coordinate second_coordinate,
const Coordinate third_coordinate)
{
return signedArea(first_coordinate, second_coordinate, third_coordinate) > 0;
}
} // ns coordinate_calculation
} // ns util
} // ns osrm

View File

@ -0,0 +1,24 @@
#include "util/guidance/turn_bearing.hpp"
#include <boost/assert.hpp>
namespace osrm
{
namespace util
{
namespace guidance
{
constexpr double bearing_scale = 360.0 / 256.0;
// discretizes a bearing into distinct units of 1.4 degrees
TurnBearing::TurnBearing(const double value) : bearing(value / bearing_scale)
{
BOOST_ASSERT_MSG(value >= 0 && value <= 360.0, "Bearing value needs to be between 0 and 360");
}
double TurnBearing::Get() const { return bearing * bearing_scale; }
} // namespace guidance
} // namespace util
} // namespace osrm

View File

@ -209,6 +209,8 @@
{"key": "highway", "value": "default"},
{"key": "width", "description": "Penalties for narrow streets"},
{"key": "lanes", "description": "Penalties for shared single lane streets"},
{"key": "lanes:forward", "description": "Lanes in forward direction"},
{"key": "lanes:backward", "description": "Lanes in backward direction"},
{"key": "surface", "value": "asphalt"},
{"key": "surface", "value": "concrete"},
{"key": "surface", "value": "concrete:plates"},

View File

@ -9,6 +9,7 @@
#include "engine/datafacade/datafacade_base.hpp"
#include "util/guidance/bearing_class.hpp"
#include "util/guidance/entry_class.hpp"
#include "util/guidance/turn_bearing.hpp"
#include "util/typedefs.hpp"
namespace osrm
@ -193,7 +194,7 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
const int /*bearing_range*/) const override
{
return {};
};
}
unsigned GetCheckSum() const override { return 0; }
bool IsCoreNode(const NodeID /* id */) const override { return false; }
@ -205,9 +206,18 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
std::size_t GetCoreSize() const override { return 0; }
std::string GetTimestamp() const override { return ""; }
bool GetContinueStraightDefault() const override { return true; }
BearingClassID GetBearingClassID(const NodeID /*id*/) const override { return 0; };
BearingClassID GetBearingClassID(const NodeID /*id*/) const override { return 0; }
EntryClassID GetEntryClassID(const EdgeID /*id*/) const override { return 0; }
util::guidance::TurnBearing PreTurnBearing(const EdgeID /*eid*/) const override final
{
return util::guidance::TurnBearing{0.0};
}
util::guidance::TurnBearing PostTurnBearing(const EdgeID /*eid*/) const override final
{
return util::guidance::TurnBearing{0.0};
}
bool hasLaneData(const EdgeID /*id*/) const override final { return true; };
util::guidance::LaneTupleIdPair GetLaneData(const EdgeID /*id*/) const override final
{