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:
parent
1f8ca2879f
commit
5e167b8745
@ -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
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 |
|
||||
|
||||
|
1151
features/guidance/turn-angles.feature
Normal file
1151
features/guidance/turn-angles.feature
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 | | |
|
||||
|
@ -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
|
||||
"""
|
||||
|
||||
|
@ -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) => {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
203
include/extractor/guidance/coordinate_extractor.hpp
Normal file
203
include/extractor/guidance/coordinate_extractor.hpp
Normal 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_
|
@ -21,6 +21,7 @@ struct TurnOperation final
|
||||
{
|
||||
EdgeID eid;
|
||||
double angle;
|
||||
double bearing;
|
||||
TurnInstruction instruction;
|
||||
LaneDataID lane_data_id;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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: ";
|
||||
|
@ -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
|
||||
|
30
include/util/guidance/turn_bearing.hpp
Normal file
30
include/util/guidance/turn_bearing.hpp
Normal 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_ */
|
@ -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); }
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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 )
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 ¤t_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 ¤t_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 ¤t
|
||||
|
||||
} // 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 ¤t_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]);
|
||||
}
|
||||
}
|
||||
|
@ -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});
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
816
src/extractor/guidance/coordinate_extractor.cpp
Normal file
816
src/extractor/guidance/coordinate_extractor.cpp
Normal 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
|
@ -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)) +
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
|
@ -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<>())
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
24
src/util/guidance/turn_bearing.cpp
Normal file
24
src/util/guidance/turn_bearing.cpp
Normal 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
|
@ -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"},
|
||||
|
@ -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
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user