turn lane handler moved to scenario based handling
This commit is contained in:
parent
802b93fa9a
commit
3b81b39998
186
features/guidance/advanced-lanes.feature
Normal file
186
features/guidance/advanced-lanes.feature
Normal file
@ -0,0 +1,186 @@
|
||||
@routing @guidance @turn-lanes
|
||||
Feature: Turn Lane Guidance
|
||||
|
||||
Background:
|
||||
Given the profile "car"
|
||||
Given a grid size of 3 meters
|
||||
|
||||
#requires https://github.com/cucumber/cucumber-js/issues/417
|
||||
#Due to this, we use & as a pipe character. Switch them out for \| when 417 is fixed
|
||||
@bug @WORKAROUND-FIXME
|
||||
Scenario: Separate Turn Lanes
|
||||
Given the node map
|
||||
| | | | | | | | e | |
|
||||
| a | | | b | | | | c | g |
|
||||
| | | | | | | | d | |
|
||||
| | | | | | | | f | |
|
||||
|
||||
And the ways
|
||||
| nodes | turn:lanes:forward | name | oneway |
|
||||
| ab | | in | yes |
|
||||
| bc | left\|through | in | yes |
|
||||
| bd | right | in | yes |
|
||||
| ec | | cross | no |
|
||||
| cd | | cross | no |
|
||||
| df | | cross | no |
|
||||
| cg | | straight | no |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
| restriction | bd | cd | d | no_left_turn |
|
||||
| restriction | bc | cd | c | no_right_turn |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,e | in,cross,cross | depart,turn left,arrive | ,left:true straight:false right:false, |
|
||||
| a,g | in,straight,straight | depart,new name straight,arrive | ,left:false straight:true right:false, |
|
||||
| a,f | in,cross,cross | depart,turn right,arrive | ,left:false straight:false right:true, |
|
||||
|
||||
@TODO @2650 @bug
|
||||
Scenario: Sliproad with through lane
|
||||
Given the node map
|
||||
| | | | | | | | | | f | | | |
|
||||
| | | | | | | | | | | | | |
|
||||
| | | | | | | | g | | | | | |
|
||||
| | | | | | | | | | | | | |
|
||||
| | | | | | | | | | | | | e |
|
||||
| | | | | | | | | | | | | |
|
||||
| | | | | | | | | | d | | | |
|
||||
| a | | | | | | | | | | | | |
|
||||
| | | | | | b | | | | | | | |
|
||||
| | | | | | | | | | c | | | |
|
||||
| | | | | | | | | | | | | |
|
||||
| | | | | | | | | | | | | |
|
||||
| | | | | h | | | | | i | | | |
|
||||
|
||||
And the ways
|
||||
| nodes | name | oneway | turn:lanes:forward |
|
||||
| ab | ghough | yes | |
|
||||
| bc | ghough | yes | through\|none |
|
||||
| bd | ghough | yes | none\|through |
|
||||
| de | ghough | yes | |
|
||||
| fgb | haight | yes | |
|
||||
| bh | haight | yes | left\|none |
|
||||
| fd | market | yes | |
|
||||
| dc | market | yes | |
|
||||
| ci | market | yes | |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
| restriction | fgb | bd | b | no_left_turn |
|
||||
| restriction | fgb | bc | b | no_left_turn |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,h | ghough,haight,haight | depart,turn right,arrive | |
|
||||
| a,i | ghough,market,market | depart,turn right,arrive | |
|
||||
| a,e | ghough,ghough,ghough | depart,continue slight left,arrive | |
|
||||
|
||||
@TODO @2650 @bug
|
||||
Scenario: Sliproad with through lane
|
||||
Given the node map
|
||||
| | | | | | | | f | | |
|
||||
| | | | | | | | | | |
|
||||
| | | | | | | g | | | |
|
||||
| | | | | | | | | | |
|
||||
| | | | | | | | | | e |
|
||||
| | | | | | | | | | |
|
||||
| | | | | | | | d | | |
|
||||
| a | | | | | | | | | |
|
||||
| | | | | b | | | | | |
|
||||
| | | | | | | | c | | |
|
||||
| | | | | | | | | | |
|
||||
| | | | | | | | | | |
|
||||
| | | | h | | | | i | | |
|
||||
|
||||
And the ways
|
||||
| nodes | name | oneway | turn:lanes:forward |
|
||||
| ab | ghough | yes | |
|
||||
| bc | ghough | yes | through\|none |
|
||||
| bd | ghough | yes | none\|through |
|
||||
| fgb | haight | yes | |
|
||||
| bh | haight | yes | left\|none |
|
||||
| fd | market | yes | |
|
||||
| dc | market | yes | |
|
||||
| ci | market | yes | |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
| restriction | bd | dc | d | no_right_turn |
|
||||
| restriction | fgb | bd | b | no_left_turn |
|
||||
| restriction | fgb | bc | b | no_left_turn |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,h | ghough,haight,haight | depart,turn right,arrive | |
|
||||
| a,i | ghough,market,market | depart,turn right,arrive | |
|
||||
| a,e | ghough,ghough,ghough | depart,continue slight left,arrive | |
|
||||
|
||||
Scenario: Separate Turn Lanes
|
||||
Given the node map
|
||||
| | | | | | | | e | |
|
||||
| a | | | b | | | | c | g |
|
||||
| | | | | | | | d | |
|
||||
| | | | | | | | f | |
|
||||
|
||||
And the ways
|
||||
| nodes | turn:lanes:forward | name | oneway |
|
||||
| ab | | in | yes |
|
||||
| bc | left\|through | in | yes |
|
||||
| bd | right | in | yes |
|
||||
| ec | | cross | no |
|
||||
| cd | | cross | no |
|
||||
| df | | cross | no |
|
||||
| cg | | straight | no |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
| restriction | bd | cd | d | no_left_turn |
|
||||
| restriction | bc | cd | c | no_right_turn |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,e | in,cross,cross | depart,turn left,arrive | ,left:true straight:false right:false, |
|
||||
| a,g | in,straight,straight | depart,new name straight,arrive | ,left:false straight:true right:false, |
|
||||
| a,f | in,cross,cross | depart,turn right,arrive | ,left:false straight:false right:true, |
|
||||
|
||||
@guidance @lanes @sliproads
|
||||
Scenario: Separate Turn Lanes Next to other turns
|
||||
Given the node map
|
||||
| | | | | | | | e | |
|
||||
| a | | | b | | | | c | g |
|
||||
| | | | | | | | d | |
|
||||
| | | | | | | | f | |
|
||||
| | | | | | | | | |
|
||||
| | | | | | | | | |
|
||||
| | | | | | | | | |
|
||||
| | | | | | | | | |
|
||||
| | | | | | | | | |
|
||||
| | | | | | | | | |
|
||||
| i | | | h | | | | j | |
|
||||
|
||||
And the ways
|
||||
| nodes | turn:lanes:forward | name | oneway |
|
||||
| ab | | in | yes |
|
||||
| bc | left\|through | in | yes |
|
||||
| bd | right | in | yes |
|
||||
| ec | | cross | no |
|
||||
| cd | | cross | no |
|
||||
| df | | cross | no |
|
||||
| cg | | straight | no |
|
||||
| bh | left\|right | turn | yes |
|
||||
| ihj | | other | no |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
| restriction | bd | cd | d | no_left_turn |
|
||||
| restriction | bc | cd | c | no_right_turn |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,e | in,cross,cross | depart,turn left,arrive | ,left:true straight:false right:false, |
|
||||
| a,g | in,straight,straight | depart,new name straight,arrive | ,left:false straight:true right:false, |
|
||||
| a,f | in,cross,cross | depart,turn right,arrive | ,left:false straight:false right:true, |
|
||||
| a,j | in,turn,other,other | depart,turn right,turn left,arrive | ,,left:true right:false, |
|
||||
| a,i | in,turn,other,other | depart,turn right,turn right,arrive | ,,left:false right:true, |
|
||||
|
@ -277,6 +277,7 @@ Feature: Turn Lane Guidance
|
||||
| a,j | road,cross,cross | depart,turn right,arrive | ,left:false straight:false right:true, |
|
||||
|
||||
#this can happen due to traffic lights / lanes not drawn up to the intersection itself
|
||||
@2654
|
||||
Scenario: Turn Lanes Given earlier than actual turn
|
||||
Given the node map
|
||||
| a | | b | c | | d |
|
||||
@ -295,6 +296,7 @@ Feature: Turn Lane Guidance
|
||||
| a,e | road,turn,turn | depart,turn right,arrive | ,none:false right:true, |
|
||||
| a,d | road,road | depart,arrive | , |
|
||||
|
||||
@2654
|
||||
Scenario: Turn Lanes Given earlier than actual turn
|
||||
Given the node map
|
||||
| a | | b | c | d | | e | | f | g | h | | i |
|
||||
@ -462,17 +464,19 @@ Feature: Turn Lane Guidance
|
||||
| | | | f | |
|
||||
|
||||
And the ways
|
||||
| nodes | name | turn:lanes:forward | oneway | highway |
|
||||
| ab | road | left\|left\|through\|through | yes | primary |
|
||||
| bd | road | through\|through | yes | primary |
|
||||
| bc | road | left\|left | yes | primary |
|
||||
| de | road | | yes | primary |
|
||||
| fdcg | cross | | | secondary |
|
||||
| nodes | name | turn:lanes:forward | oneway | highway |
|
||||
| ab | road | left\|left\|through\|through\|right | yes | primary |
|
||||
| bd | road | through\|through | yes | primary |
|
||||
| bc | road | left\|left | yes | primary |
|
||||
| de | road | | yes | primary |
|
||||
| fd | cross | | | secondary |
|
||||
| dc | cross | | | secondary |
|
||||
| cg | cross | | | secondary |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
| restriction | bd | fdcg | d | no_left_turn |
|
||||
| restriction | bc | fdcg | c | no_right_turn |
|
||||
| restriction | bd | dc | d | no_left_turn |
|
||||
| restriction | bc | dc | c | no_right_turn |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
@ -729,6 +733,7 @@ Feature: Turn Lane Guidance
|
||||
| waypoints | turns | route | lanes |
|
||||
| d,c | depart,merge slight left,arrive | ramp,Hwy,Hwy | ,slight right:true slight right:true, |
|
||||
|
||||
@2654
|
||||
Scenario: Fork on motorway links - don't fork on through but use lane
|
||||
Given the node map
|
||||
| i | | | | | a |
|
||||
@ -798,6 +803,27 @@ Feature: Turn Lane Guidance
|
||||
| waypoints | route | turns | lanes |
|
||||
| x,d | road,road | depart,arrive | , |
|
||||
|
||||
@partition
|
||||
Scenario: Partitioned turn, Slight Curve
|
||||
Given the node map
|
||||
| | | f | | e |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
| | | | | c |
|
||||
| a | | b | | |
|
||||
| | | g | | d |
|
||||
|
||||
And the ways
|
||||
| nodes | name | highway | oneway | turn:lanes:forward |
|
||||
| ab | road | primary | yes | left\|right |
|
||||
| bc | cross | primary | yes | |
|
||||
| fbg | cross | primary | yes | |
|
||||
| dce | cross | primary | yes | |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,g | road,cross,cross | depart,turn right,arrive | ,left:false right:true, |
|
||||
| a,e | road,cross,cross | depart,turn left,arrive | ,left:true right:false, |
|
||||
|
||||
Scenario: Lane Parsing Issue #2694
|
||||
Given the node map
|
||||
@ -930,3 +956,31 @@ Feature: Turn Lane Guidance
|
||||
| a,c | in,left,left | depart,turn left,arrive | ,left:true straight:false right;uturn:false, |
|
||||
| a,d | in,through,through | depart,new name straight,arrive | ,left:false straight:true right;uturn:false, |
|
||||
| a,e | in,right,right | depart,turn right,arrive | ,left:false straight:false right;uturn:true, |
|
||||
|
||||
@todo @2654
|
||||
#https://github.com/Project-OSRM/osrm-backend/issues/2645
|
||||
#http://www.openstreetmap.org/export#map=19/52.56054/13.32152
|
||||
Scenario: Kurt-Schuhmacher-Damm
|
||||
Given the node map
|
||||
| | | | g | | f |
|
||||
| | | | | | |
|
||||
| j | | | h | | e |
|
||||
| | | | | | |
|
||||
| a | | | b | | c |
|
||||
| | | | i | | d |
|
||||
|
||||
And the ways
|
||||
| nodes | name | highway | oneway | turn:lanes |
|
||||
| ab | | motorway_link | yes | left\|none\|right |
|
||||
| bc | | primary_link | yes | |
|
||||
| cd | ksd | secondary | yes | |
|
||||
| cef | ksd | primary | yes | |
|
||||
| hj | | motorway_link | yes | |
|
||||
| eh | | secondary_link | yes | |
|
||||
| gh | ksd | primary | yes | |
|
||||
| hbi | ksd | secondary | yes | |
|
||||
|
||||
When I route I should get
|
||||
| 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, |
|
||||
|
@ -92,8 +92,9 @@ class EdgeBasedGraphFactory
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
ProfileProperties profile_properties,
|
||||
const util::NameTable &name_table,
|
||||
const std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
const std::vector<guidance::TurnLaneType::Mask> &turn_lane_masks);
|
||||
std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
std::vector<guidance::TurnLaneType::Mask> &turn_lane_masks,
|
||||
guidance::LaneDescriptionMap &lane_description_map);
|
||||
|
||||
void Run(ScriptingEnvironment &scripting_environment,
|
||||
const std::string &original_edge_data_filename,
|
||||
@ -154,8 +155,9 @@ class EdgeBasedGraphFactory
|
||||
ProfileProperties profile_properties;
|
||||
|
||||
const util::NameTable &name_table;
|
||||
const std::vector<std::uint32_t> &turn_lane_offsets;
|
||||
const std::vector<guidance::TurnLaneType::Mask> &turn_lane_masks;
|
||||
std::vector<std::uint32_t> &turn_lane_offsets;
|
||||
std::vector<guidance::TurnLaneType::Mask> &turn_lane_masks;
|
||||
guidance::LaneDescriptionMap &lane_description_map;
|
||||
|
||||
void CompressGeometry();
|
||||
unsigned RenumberEdges();
|
||||
|
@ -39,11 +39,11 @@ class ExtractionContainers
|
||||
void WriteNodes(std::ofstream &file_out_stream) const;
|
||||
void WriteRestrictions(const std::string &restrictions_file_name) const;
|
||||
void WriteEdges(std::ofstream &file_out_stream) const;
|
||||
void WriteCharData(const std::string &file_name);
|
||||
void
|
||||
WriteTurnLaneMasks(const std::string &file_name,
|
||||
const stxxl::vector<std::uint32_t> &turn_lane_offsets,
|
||||
const stxxl::vector<guidance::TurnLaneType::Mask> &turn_lane_masks) const;
|
||||
void WriteCharData(const std::string &file_name);
|
||||
|
||||
public:
|
||||
using STXXLNodeIDVector = stxxl::vector<OSMNodeID>;
|
||||
@ -60,8 +60,6 @@ class ExtractionContainers
|
||||
STXXLNameCharData name_char_data;
|
||||
STXXLNameOffsets name_offsets;
|
||||
// an adjacency array containing all turn lane masks
|
||||
stxxl::vector<std::uint32_t> turn_lane_offsets;
|
||||
stxxl::vector<guidance::TurnLaneType::Mask> turn_lane_masks;
|
||||
STXXLRestrictionsVector restrictions_list;
|
||||
STXXLWayIDStartEndVector way_start_end_id_list;
|
||||
std::unordered_map<OSMNodeID, NodeID> external_to_internal_node_id_map;
|
||||
@ -72,8 +70,7 @@ class ExtractionContainers
|
||||
void PrepareData(ScriptingEnvironment &scripting_environment,
|
||||
const std::string &output_file_name,
|
||||
const std::string &restrictions_file_name,
|
||||
const std::string &names_file_name,
|
||||
const std::string &turn_lane_file_name);
|
||||
const std::string &names_file_name);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
#include "util/guidance/turn_lanes.hpp"
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
@ -87,6 +88,16 @@ class Extractor
|
||||
const std::vector<std::uint32_t> &node_based_intersection_classes,
|
||||
const std::vector<util::guidance::BearingClass> &bearing_classes,
|
||||
const std::vector<util::guidance::EntryClass> &entry_classes) const;
|
||||
|
||||
void WriteTurnLaneData(const std::string &turn_lane_file) const;
|
||||
|
||||
// globals persisting during the extraction process and the graph generation process
|
||||
|
||||
// during turn lane analysis, we might have to combine lanes for roads that are modelled as two
|
||||
// but are more or less experienced as one. This can be due to solid lines in between lanes, for
|
||||
// example, that genereate a small separation between them. As a result, we might have to
|
||||
// augment the turn lane map during processing, further adding more types.
|
||||
guidance::LaneDescriptionMap turn_lane_map;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -40,14 +40,12 @@ class ExtractorCallbacks
|
||||
using MapKey = std::pair<std::string, std::string>;
|
||||
using MapVal = unsigned;
|
||||
std::unordered_map<MapKey, MapVal, boost::hash<MapKey>> string_map;
|
||||
std::unordered_map<guidance::TurnLaneDescription,
|
||||
LaneDescriptionID,
|
||||
guidance::TurnLaneDescription_hash>
|
||||
lane_description_map;
|
||||
guidance::LaneDescriptionMap &lane_description_map;
|
||||
ExtractionContainers &external_memory;
|
||||
|
||||
public:
|
||||
explicit ExtractorCallbacks(ExtractionContainers &extraction_containers);
|
||||
explicit ExtractorCallbacks(ExtractionContainers &extraction_containers,
|
||||
guidance::LaneDescriptionMap &lane_description_map);
|
||||
|
||||
ExtractorCallbacks(const ExtractorCallbacks &) = delete;
|
||||
ExtractorCallbacks &operator=(const ExtractorCallbacks &) = delete;
|
||||
|
@ -28,7 +28,7 @@ typedef std::vector<TurnLaneData> LaneDataVector;
|
||||
|
||||
// convertes a string given in the OSM format into a TurnLaneData vector
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
LaneDataVector laneDataFromDescription(const TurnLaneDescription &turn_lane_description);
|
||||
LaneDataVector laneDataFromDescription(TurnLaneDescription turn_lane_description);
|
||||
|
||||
// Locate A Tag in a lane data vector (if multiple tags are set, the first one found is returned)
|
||||
LaneDataVector::const_iterator findTag(const TurnLaneType::Mask tag, const LaneDataVector &data);
|
||||
@ -37,6 +37,9 @@ LaneDataVector::iterator findTag(const TurnLaneType::Mask tag, LaneDataVector &d
|
||||
// Returns true if any of the queried tags is contained
|
||||
bool hasTag(const TurnLaneType::Mask tag, const LaneDataVector &data);
|
||||
|
||||
// Check if a set of lanes is a subset of a different set of lanes
|
||||
bool isSubsetOf(const LaneDataVector &subset_candidate, const LaneDataVector &superset_candidate);
|
||||
|
||||
} // namespace lane_data_generation
|
||||
|
||||
} // namespace guidance
|
||||
|
@ -33,27 +33,75 @@ namespace lanes
|
||||
{
|
||||
class TurnLaneHandler
|
||||
{
|
||||
typedef enum TurnLaneScenario {
|
||||
SIMPLE, // a straightforward assignment
|
||||
PARTITION_LOCAL, // an assignment that requires partitioning, using local turns
|
||||
SIMPLE_PREVIOUS, // an assignemtnn using the turns specified at the previous road (e.g.
|
||||
// traffic light, lanes not drawn up to the intersection)
|
||||
PARTITION_PREVIOUS, // a set of lanes on a turn with a traffic island. The lanes for the
|
||||
// turn end at the previous turn (parts of it remain valid without being
|
||||
// shown again)
|
||||
SLIPROAD, // Sliproads are simple assignments that, for better visual representation should
|
||||
// include turns from other roads in their listings
|
||||
MERGE, // Merging Lanes
|
||||
NONE, // not a turn lane scenario at all
|
||||
INVALID, // some error might have occurred
|
||||
UNKNOWN, // UNKNOWN describes all cases that we are currently not able to handle
|
||||
NUM_SCENARIOS
|
||||
} TurnLaneScenario;
|
||||
|
||||
const constexpr static char *scenario_names[TurnLaneScenario::NUM_SCENARIOS] = {
|
||||
"Simple",
|
||||
"Partition Local",
|
||||
"Simple Previous",
|
||||
"Partition Previous",
|
||||
"Sliproad",
|
||||
"Merge",
|
||||
"None",
|
||||
"Invalid",
|
||||
"Unknown"};
|
||||
|
||||
public:
|
||||
typedef std::vector<TurnLaneData> LaneDataVector;
|
||||
|
||||
TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
const std::vector<TurnLaneType::Mask> &turn_lane_masks,
|
||||
const TurnAnalysis &turn_analysis);
|
||||
std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
std::vector<TurnLaneType::Mask> &turn_lane_masks,
|
||||
LaneDescriptionMap &lane_description_map,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const TurnAnalysis &turn_analysis,
|
||||
LaneDataIdMap &id_map);
|
||||
|
||||
~TurnLaneHandler();
|
||||
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection assignTurnLanes(const NodeID at,
|
||||
const EdgeID via_edge,
|
||||
Intersection intersection,
|
||||
LaneDataIdMap &id_map) const;
|
||||
Intersection assignTurnLanes(const NodeID at, const EdgeID via_edge, Intersection intersection);
|
||||
|
||||
private:
|
||||
unsigned *count_handled;
|
||||
unsigned *count_called;
|
||||
// we need to be able to look at previous intersections to, in some cases, find the correct turn
|
||||
// lanes for a turn
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
const std::vector<std::uint32_t> &turn_lane_offsets;
|
||||
const std::vector<TurnLaneType::Mask> &turn_lane_masks;
|
||||
std::vector<std::uint32_t> &turn_lane_offsets;
|
||||
std::vector<TurnLaneType::Mask> &turn_lane_masks;
|
||||
LaneDescriptionMap &lane_description_map;
|
||||
const std::vector<QueryNode> &node_info_list;
|
||||
const TurnAnalysis &turn_analysis;
|
||||
LaneDataIdMap &id_map;
|
||||
|
||||
// Find out which scenario we have to handle
|
||||
TurnLaneScenario deduceScenario(const NodeID at,
|
||||
const EdgeID via_edge,
|
||||
const Intersection &intersection,
|
||||
// Output Parameters to reduce repeated creation
|
||||
LaneDescriptionID &lane_description_id,
|
||||
LaneDataVector &lane_data,
|
||||
NodeID &previous_node,
|
||||
EdgeID &previous_id,
|
||||
Intersection &previous_intersection,
|
||||
LaneDataVector &previous_lane_data,
|
||||
LaneDescriptionID &previous_description_id);
|
||||
|
||||
// check whether we can handle an intersection
|
||||
bool isSimpleIntersection(const LaneDataVector &turn_lane_data,
|
||||
@ -63,21 +111,28 @@ class TurnLaneHandler
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection simpleMatchTuplesToTurns(Intersection intersection,
|
||||
const LaneDataVector &lane_data,
|
||||
const LaneDescriptionID lane_string_id,
|
||||
LaneDataIdMap &id_map) const;
|
||||
const LaneDescriptionID lane_string_id);
|
||||
|
||||
// partition lane data into lane data relevant at current turn and at next turn
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
std::pair<TurnLaneHandler::LaneDataVector, TurnLaneHandler::LaneDataVector> partitionLaneData(
|
||||
const NodeID at, LaneDataVector turn_lane_data, const Intersection &intersection) const;
|
||||
|
||||
// if the current intersections turn string is empty, we check whether there is an incoming
|
||||
// intersection whose turns might be related to this current intersection
|
||||
// Sliproad turns have a separated lane to the right/left of other depicted lanes. These lanes
|
||||
// are not necessarily separated clearly from the rest of the way. As a result, we combine both
|
||||
// lane entries for our output, while performing the matching with the separated lanes only.
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection handleTurnAtPreviousIntersection(const NodeID at,
|
||||
const EdgeID via_edge,
|
||||
Intersection intersection,
|
||||
LaneDataIdMap &id_map) const;
|
||||
Intersection handleSliproadTurn(Intersection intersection,
|
||||
const LaneDescriptionID lane_description_id,
|
||||
LaneDataVector lane_data,
|
||||
const Intersection &previous_intersection,
|
||||
const LaneDescriptionID &previous_lane_description_id,
|
||||
const LaneDataVector &previous_lane_data);
|
||||
|
||||
// get the lane data for an intersection
|
||||
void extractLaneData(const EdgeID via_edge,
|
||||
LaneDescriptionID &lane_description_id,
|
||||
LaneDataVector &lane_data) const;
|
||||
};
|
||||
|
||||
} // namespace lanes
|
||||
|
@ -22,16 +22,20 @@ namespace lanes
|
||||
{
|
||||
|
||||
// Translate Turn Lane Tags into a matching modifier
|
||||
DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask &tag);
|
||||
DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask tag);
|
||||
|
||||
// check whether a match of a given tag and a turn instruction can be seen as valid
|
||||
bool isValidMatch(const TurnLaneType::Mask &tag, const TurnInstruction instruction);
|
||||
bool isValidMatch(const TurnLaneType::Mask tag, const TurnInstruction instruction);
|
||||
|
||||
// localisation of the best possible match for a tag
|
||||
typename Intersection::const_iterator findBestMatch(const TurnLaneType::Mask &tag,
|
||||
typename Intersection::const_iterator findBestMatch(const TurnLaneType::Mask tag,
|
||||
const Intersection &intersection);
|
||||
|
||||
// the quality of a matching to decide between first/second possibility on segregated intersections
|
||||
double getMatchingQuality( const TurnLaneType::Mask tag, const ConnectedRoad &road );
|
||||
|
||||
typename Intersection::const_iterator
|
||||
findBestMatchForReverse(const TurnLaneType::Mask &leftmost_tag, const Intersection &intersection);
|
||||
findBestMatchForReverse(const TurnLaneType::Mask leftmost_tag, const Intersection &intersection);
|
||||
|
||||
// a match is trivial if all turns can be associated with their best match in a valid way and the
|
||||
// matches occur in order
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
@ -95,6 +96,11 @@ struct TurnLaneDescription_hash
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::unordered_map<guidance::TurnLaneDescription,
|
||||
LaneDescriptionID,
|
||||
guidance::TurnLaneDescription_hash>
|
||||
LaneDescriptionMap;
|
||||
|
||||
} // guidance
|
||||
} // extractor
|
||||
} // osrm
|
||||
|
@ -25,8 +25,7 @@ inline void print(const engine::guidance::RouteStep &step)
|
||||
<< static_cast<int>(step.maneuver.waypoint_type) << " "
|
||||
<< " Duration: " << step.duration << " Distance: " << step.distance
|
||||
<< " Geometry: " << step.geometry_begin << " " << step.geometry_end
|
||||
<< " exit: " << step.maneuver.exit << " Intersections: " << step.intersections.size()
|
||||
<< " [";
|
||||
<< "\n\tIntersections: " << step.intersections.size() << " [";
|
||||
|
||||
for (const auto &intersection : step.intersections)
|
||||
{
|
||||
@ -36,9 +35,9 @@ inline void print(const engine::guidance::RouteStep &step)
|
||||
std::cout << ", entry: ";
|
||||
for (auto entry : intersection.entry)
|
||||
std::cout << " " << (entry ? "true" : "false");
|
||||
std::cout << " Lanes: (" << static_cast<int>(intersection.lanes.lanes_in_turn) << ", "
|
||||
<< static_cast<int>(intersection.lanes.first_lane_from_the_right) << ")";
|
||||
std::cout << ")";
|
||||
const auto lanes = intersection.lanes;
|
||||
std::cout<< " Lanes: (" << static_cast<int>(lanes.lanes_in_turn) << ", "
|
||||
<< static_cast<int>(lanes.first_lane_from_the_right) << "))";
|
||||
}
|
||||
std::cout << "] name[" << step.name_id << "]: " << step.name;
|
||||
}
|
||||
|
@ -331,8 +331,6 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
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;
|
||||
|
||||
@ -418,6 +416,11 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
||||
DirectionModifier::Straight &&
|
||||
one_back_step.intersections.front().bearings.size() > 2)
|
||||
steps[step_index].maneuver.instruction.type = TurnType::Turn;
|
||||
else if (TurnType::UseLane == current_step.maneuver.instruction.type &&
|
||||
current_step.maneuver.instruction.direction_modifier !=
|
||||
DirectionModifier::Straight &&
|
||||
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
|
||||
@ -741,7 +744,8 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
||||
if (current_step.maneuver.instruction.type == TurnType::Suppressed &&
|
||||
compatible(one_back_step, current_step))
|
||||
{
|
||||
// Traffic light on the sliproad
|
||||
// Traffic light on the sliproad, the road itself will be handled in the next
|
||||
// iteration, when one-back-index again points to the sliproad.
|
||||
steps[one_back_index] =
|
||||
elongate(std::move(steps[one_back_index]), steps[step_index]);
|
||||
invalidateStep(steps[step_index]);
|
||||
@ -764,6 +768,13 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
||||
elongate(std::move(steps[one_back_index]), steps[step_index]);
|
||||
steps[one_back_index].name_id = steps[step_index].name_id;
|
||||
steps[one_back_index].name = steps[step_index].name;
|
||||
steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
|
||||
// the turn lanes for this turn are on the sliproad itself, so we have to
|
||||
// remember them
|
||||
steps[one_back_index].intersections.front().lanes =
|
||||
current_step.intersections.front().lanes;
|
||||
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];
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "extractor/edge_based_graph_factory.hpp"
|
||||
#include "extractor/edge_based_edge.hpp"
|
||||
#include "extractor/edge_based_graph_factory.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/exception.hpp"
|
||||
@ -41,14 +41,16 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory(
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
ProfileProperties profile_properties,
|
||||
const util::NameTable &name_table,
|
||||
const std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
const std::vector<guidance::TurnLaneType::Mask> &turn_lane_masks)
|
||||
std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
std::vector<guidance::TurnLaneType::Mask> &turn_lane_masks,
|
||||
guidance::LaneDescriptionMap &lane_description_map)
|
||||
: m_max_edge_id(0), m_node_info_list(node_info_list),
|
||||
m_node_based_graph(std::move(node_based_graph)),
|
||||
m_restriction_map(std::move(restriction_map)), m_barrier_nodes(barrier_nodes),
|
||||
m_traffic_lights(traffic_lights), m_compressed_edge_container(compressed_edge_container),
|
||||
profile_properties(std::move(profile_properties)), name_table(name_table),
|
||||
turn_lane_offsets(turn_lane_offsets), turn_lane_masks(turn_lane_masks)
|
||||
turn_lane_offsets(turn_lane_offsets), turn_lane_masks(turn_lane_masks),
|
||||
lane_description_map(lane_description_map)
|
||||
{
|
||||
}
|
||||
|
||||
@ -344,13 +346,19 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
profile_properties);
|
||||
guidance::lanes::TurnLaneHandler turn_lane_handler(
|
||||
*m_node_based_graph, turn_lane_offsets, turn_lane_masks, turn_analysis);
|
||||
|
||||
guidance::LaneDataIdMap lane_data_map;
|
||||
guidance::lanes::TurnLaneHandler turn_lane_handler(*m_node_based_graph,
|
||||
turn_lane_offsets,
|
||||
turn_lane_masks,
|
||||
lane_description_map,
|
||||
m_node_info_list,
|
||||
turn_analysis,
|
||||
lane_data_map);
|
||||
|
||||
bearing_class_by_node_based_node.resize(m_node_based_graph->GetNumberOfNodes(),
|
||||
std::numeric_limits<std::uint32_t>::max());
|
||||
|
||||
guidance::LaneDataIdMap lane_data_map;
|
||||
for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
|
||||
{
|
||||
progress.PrintStatus(node_u);
|
||||
@ -367,8 +375,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
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), lane_data_map);
|
||||
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
|
||||
|
@ -116,11 +116,6 @@ ExtractionContainers::ExtractionContainers()
|
||||
name_offsets.push_back(0);
|
||||
// Insert the total length sentinel (corresponds to the next name string offset)
|
||||
name_offsets.push_back(0);
|
||||
|
||||
// the offsets have to be initialized with two values, since we have the empty turn string for
|
||||
// the first id
|
||||
turn_lane_offsets.push_back(0);
|
||||
turn_lane_offsets.push_back(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -136,8 +131,7 @@ ExtractionContainers::ExtractionContainers()
|
||||
void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environment,
|
||||
const std::string &output_file_name,
|
||||
const std::string &restrictions_file_name,
|
||||
const std::string &name_file_name,
|
||||
const std::string &turn_lane_file_name)
|
||||
const std::string &name_file_name)
|
||||
{
|
||||
std::ofstream file_out_stream;
|
||||
file_out_stream.open(output_file_name.c_str(), std::ios::binary);
|
||||
@ -151,35 +145,7 @@ void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environme
|
||||
|
||||
PrepareRestrictions();
|
||||
WriteRestrictions(restrictions_file_name);
|
||||
|
||||
WriteCharData(name_file_name);
|
||||
WriteTurnLaneMasks(turn_lane_file_name, turn_lane_offsets, turn_lane_masks);
|
||||
}
|
||||
|
||||
void ExtractionContainers::WriteTurnLaneMasks(
|
||||
const std::string &file_name,
|
||||
const stxxl::vector<std::uint32_t> &offsets,
|
||||
const stxxl::vector<guidance::TurnLaneType::Mask> &masks) const
|
||||
{
|
||||
util::SimpleLogger().Write() << "Writing turn lane masks...";
|
||||
TIMER_START(turn_lane_timer);
|
||||
|
||||
std::ofstream ofs(file_name, std::ios::binary);
|
||||
|
||||
if (!util::serializeVector(ofs, offsets))
|
||||
{
|
||||
util::SimpleLogger().Write(logWARNING) << "Error while writing.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!util::serializeVector(ofs, masks))
|
||||
{
|
||||
util::SimpleLogger().Write(logWARNING) << "Error while writing.";
|
||||
return;
|
||||
}
|
||||
|
||||
TIMER_STOP(turn_lane_timer);
|
||||
util::SimpleLogger().Write() << "done (" << TIMER_SEC(turn_lane_timer) << ")";
|
||||
}
|
||||
|
||||
void ExtractionContainers::WriteCharData(const std::string &file_name)
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <numeric> //partial_sum
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
@ -93,7 +94,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
util::SimpleLogger().Write() << "Threads: " << number_of_threads;
|
||||
|
||||
ExtractionContainers extraction_containers;
|
||||
auto extractor_callbacks = util::make_unique<ExtractorCallbacks>(extraction_containers);
|
||||
auto extractor_callbacks =
|
||||
util::make_unique<ExtractorCallbacks>(extraction_containers, turn_lane_map);
|
||||
|
||||
const osmium::io::File input_file(config.input_path.string());
|
||||
osmium::io::Reader reader(input_file);
|
||||
@ -194,8 +196,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
extraction_containers.PrepareData(scripting_environment,
|
||||
config.output_file_name,
|
||||
config.restriction_file_name,
|
||||
config.names_file_name,
|
||||
config.turn_lane_descriptions_file_name);
|
||||
config.names_file_name);
|
||||
|
||||
WriteProfileProperties(config.profile_properties_output_path,
|
||||
scripting_environment.GetProfileProperties());
|
||||
@ -444,13 +445,21 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment,
|
||||
|
||||
util::NameTable name_table(config.names_file_name);
|
||||
|
||||
std::vector<std::uint32_t> turn_lane_offsets;
|
||||
std::vector<guidance::TurnLaneType::Mask> turn_lane_masks;
|
||||
if (!util::deserializeAdjacencyArray(
|
||||
config.turn_lane_descriptions_file_name, turn_lane_offsets, turn_lane_masks))
|
||||
{
|
||||
util::SimpleLogger().Write(logWARNING) << "Reading Turn Lane Masks failed.";
|
||||
}
|
||||
// could use some additional capacity? To avoid a copy during processing, though small data so
|
||||
// probably not that important.
|
||||
std::vector<std::uint32_t> turn_lane_offsets(turn_lane_map.size() + 2); // empty ID + sentinel
|
||||
for (auto entry = turn_lane_map.begin(); entry != turn_lane_map.end(); ++entry)
|
||||
turn_lane_offsets[entry->second + 1] = entry->first.size();
|
||||
|
||||
// inplace prefix sum
|
||||
std::partial_sum(turn_lane_offsets.begin(), turn_lane_offsets.end(), turn_lane_offsets.begin());
|
||||
|
||||
// allocate the current masks
|
||||
std::vector<guidance::TurnLaneType::Mask> turn_lane_masks(turn_lane_offsets.back());
|
||||
for (auto entry = turn_lane_map.begin(); entry != turn_lane_map.end(); ++entry)
|
||||
std::copy(entry->first.begin(),
|
||||
entry->first.end(),
|
||||
turn_lane_masks.begin() + turn_lane_offsets[entry->second]);
|
||||
|
||||
EdgeBasedGraphFactory edge_based_graph_factory(
|
||||
node_based_graph,
|
||||
@ -462,7 +471,8 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment,
|
||||
scripting_environment.GetProfileProperties(),
|
||||
name_table,
|
||||
turn_lane_offsets,
|
||||
turn_lane_masks);
|
||||
turn_lane_masks,
|
||||
turn_lane_map);
|
||||
|
||||
edge_based_graph_factory.Run(scripting_environment,
|
||||
config.edge_output_path,
|
||||
@ -471,6 +481,8 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment,
|
||||
config.edge_penalty_path,
|
||||
config.generate_edge_lookup);
|
||||
|
||||
WriteTurnLaneData(config.turn_lane_descriptions_file_name);
|
||||
|
||||
edge_based_graph_factory.GetEdgeBasedEdges(edge_based_edge_list);
|
||||
edge_based_graph_factory.GetEdgeBasedNodes(node_based_edge_list);
|
||||
edge_based_graph_factory.GetStartPointMarkers(node_is_startpoint);
|
||||
@ -633,5 +645,44 @@ void Extractor::WriteIntersectionClassificationData(
|
||||
<< entry_classes.size() << " entry classes and " << total_bearings
|
||||
<< " bearing values." << std::endl;
|
||||
}
|
||||
|
||||
void Extractor::WriteTurnLaneData(const std::string &turn_lane_file) const
|
||||
{
|
||||
// Write the turn lane data to file
|
||||
std::vector<std::uint32_t> turn_lane_offsets(turn_lane_map.size() + 2); // empty ID + sentinel
|
||||
for (auto entry = turn_lane_map.begin(); entry != turn_lane_map.end(); ++entry)
|
||||
turn_lane_offsets[entry->second + 1] = entry->first.size();
|
||||
|
||||
// inplace prefix sum
|
||||
std::partial_sum(turn_lane_offsets.begin(), turn_lane_offsets.end(), turn_lane_offsets.begin());
|
||||
|
||||
// allocate the current masks
|
||||
std::vector<guidance::TurnLaneType::Mask> turn_lane_masks(turn_lane_offsets.back());
|
||||
for (auto entry = turn_lane_map.begin(); entry != turn_lane_map.end(); ++entry)
|
||||
std::copy(entry->first.begin(),
|
||||
entry->first.end(),
|
||||
turn_lane_masks.begin() + turn_lane_offsets[entry->second]);
|
||||
|
||||
util::SimpleLogger().Write() << "Writing turn lane masks...";
|
||||
TIMER_START(turn_lane_timer);
|
||||
|
||||
std::ofstream ofs(turn_lane_file, std::ios::binary);
|
||||
|
||||
if (!util::serializeVector(ofs, turn_lane_offsets))
|
||||
{
|
||||
util::SimpleLogger().Write(logWARNING) << "Error while writing.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!util::serializeVector(ofs, turn_lane_masks))
|
||||
{
|
||||
util::SimpleLogger().Write(logWARNING) << "Error while writing.";
|
||||
return;
|
||||
}
|
||||
|
||||
TIMER_STOP(turn_lane_timer);
|
||||
util::SimpleLogger().Write() << "done (" << TIMER_SEC(turn_lane_timer) << ")";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
@ -31,11 +31,15 @@ namespace extractor
|
||||
using TurnLaneDescription = guidance::TurnLaneDescription;
|
||||
namespace TurnLaneType = guidance::TurnLaneType;
|
||||
|
||||
ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containers)
|
||||
: external_memory(extraction_containers)
|
||||
ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containers,
|
||||
guidance::LaneDescriptionMap &lane_description_map)
|
||||
: lane_description_map(lane_description_map), external_memory(extraction_containers)
|
||||
{
|
||||
// we reserved 0, 1, 2 for the empty case
|
||||
string_map[MapKey("", "")] = 0;
|
||||
|
||||
// The map should be empty before we start initializing it
|
||||
BOOST_ASSERT(lane_description_map.empty());
|
||||
lane_description_map[TurnLaneDescription()] = 0;
|
||||
}
|
||||
|
||||
@ -165,6 +169,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
||||
"reverse",
|
||||
"merge_to_left",
|
||||
"merge_to_right"};
|
||||
|
||||
const constexpr TurnLaneType::Mask masks_by_osm_string[num_osm_tags + 1] = {
|
||||
TurnLaneType::none,
|
||||
TurnLaneType::straight,
|
||||
@ -224,16 +229,6 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
||||
const LaneDescriptionID new_id =
|
||||
boost::numeric_cast<LaneDescriptionID>(lane_description_map.size());
|
||||
lane_description_map[lane_description] = new_id;
|
||||
|
||||
// since we are getting a new ID, we can augment the current offsets
|
||||
|
||||
// and store the turn lane masks, sadly stxxl does not support insert
|
||||
for (const auto mask : lane_description)
|
||||
external_memory.turn_lane_masks.push_back(mask);
|
||||
|
||||
external_memory.turn_lane_offsets.push_back(external_memory.turn_lane_offsets.back() +
|
||||
lane_description.size());
|
||||
|
||||
return new_id;
|
||||
}
|
||||
else
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "extractor/guidance/turn_discovery.hpp"
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/turn_discovery.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@ -50,8 +50,10 @@ bool findPreviousIntersection(const NodeID node_v,
|
||||
// previous intersection.
|
||||
const auto straightmost_at_v_in_reverse =
|
||||
findClosestTurn(node_v_reverse_intersection, STRAIGHT_ANGLE);
|
||||
if (angularDeviation(straightmost_at_v_in_reverse->turn.angle, STRAIGHT_ANGLE) >
|
||||
FUZZY_ANGLE_DIFFERENCE)
|
||||
|
||||
// TODO evaluate if narrow turn is the right criterion here... Might be that other angles are
|
||||
// valid
|
||||
if (angularDeviation(straightmost_at_v_in_reverse->turn.angle, STRAIGHT_ANGLE) > GROUP_ANGLE)
|
||||
return false;
|
||||
|
||||
const auto node_u = node_based_graph.GetTarget(straightmost_at_v_in_reverse->turn.eid);
|
||||
@ -66,12 +68,25 @@ bool findPreviousIntersection(const NodeID node_v,
|
||||
// if the edge is not traversable, we obviously don't have a previous intersection or couldn't
|
||||
// find it.
|
||||
if (node_based_graph.GetEdgeData(result_via_edge).reversed)
|
||||
{
|
||||
result_via_edge = SPECIAL_EDGEID;
|
||||
result_node = SPECIAL_NODEID;
|
||||
return false;
|
||||
}
|
||||
|
||||
result_intersection = turn_analysis.getIntersection(node_u, result_via_edge);
|
||||
const auto check_via_edge = findClosestTurn(result_intersection, STRAIGHT_ANGLE)->turn.eid;
|
||||
if (check_via_edge != via_edge)
|
||||
const auto check_via_edge =
|
||||
result_intersection.end() !=
|
||||
std::find_if(result_intersection.begin(),
|
||||
result_intersection.end(),
|
||||
[via_edge](const ConnectedRoad &road) { return road.turn.eid == via_edge; });
|
||||
|
||||
if (!check_via_edge)
|
||||
{
|
||||
result_via_edge = SPECIAL_EDGEID;
|
||||
result_node = SPECIAL_NODEID;
|
||||
return false;
|
||||
}
|
||||
|
||||
result_intersection =
|
||||
turn_analysis.assignTurnTypes(node_u, result_via_edge, std::move(result_intersection));
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include "util/debug.hpp"
|
||||
|
||||
#include "extractor/guidance/turn_lane_augmentation.hpp"
|
||||
#include "extractor/guidance/turn_lane_types.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
@ -280,6 +282,13 @@ LaneDataVector handleNoneValueAtSimpleTurn(LaneDataVector lane_data,
|
||||
// we have to reduce it, assigning it to neighboring turns
|
||||
else if (connection_count < lane_data.size())
|
||||
{
|
||||
if( connection_count+1 < lane_data.size() ){
|
||||
std::cout << "[error] failed assignment" << std::endl;
|
||||
util::guidance::print(lane_data);
|
||||
std::cout << "Intersection:\n";
|
||||
for( auto road : intersection )
|
||||
std::cout << "\t" << toString(road) << std::endl;
|
||||
}
|
||||
// a pgerequisite is simple turns. Larger differences should not end up here
|
||||
// an additional line at the side is only reasonable if it is targeting public
|
||||
// service vehicles. Otherwise, we should not have it
|
||||
|
@ -64,10 +64,12 @@ bool TurnLaneData::operator<(const TurnLaneData &other) const
|
||||
std::find(tag_by_modifier, tag_by_modifier + 8, other.tag);
|
||||
}
|
||||
|
||||
LaneDataVector laneDataFromDescription(const TurnLaneDescription &turn_lane_description)
|
||||
LaneDataVector laneDataFromDescription(TurnLaneDescription turn_lane_description)
|
||||
{
|
||||
typedef std::unordered_map<TurnLaneType::Mask, std::pair<LaneID, LaneID>> LaneMap;
|
||||
//TODO need to handle cases that have none-in between two identical values
|
||||
const auto num_lanes = boost::numeric_cast<LaneID>(turn_lane_description.size());
|
||||
|
||||
const auto setLaneData = [&](
|
||||
LaneMap &map, TurnLaneType::Mask full_mask, const LaneID current_lane) {
|
||||
const auto isSet = [&](const TurnLaneType::Mask test_mask) -> bool {
|
||||
@ -151,6 +153,27 @@ bool hasTag(const TurnLaneType::Mask tag, const LaneDataVector &data)
|
||||
return findTag(tag, data) != data.cend();
|
||||
}
|
||||
|
||||
bool isSubsetOf(const LaneDataVector &subset_candidate, const LaneDataVector &superset_candidate)
|
||||
{
|
||||
auto location = superset_candidate.begin();
|
||||
for (const auto entry : subset_candidate)
|
||||
{
|
||||
location =
|
||||
std::find_if(location, superset_candidate.end(), [entry](const TurnLaneData &lane_data) {
|
||||
return lane_data.tag == entry.tag;
|
||||
});
|
||||
|
||||
if (location == superset_candidate.end())
|
||||
return false;
|
||||
|
||||
// compare the number of lanes TODO this might have be to be revisited for situations where
|
||||
// a sliproad widens into multiple lanes
|
||||
if ((location->to - location->from) != (entry.to - entry.from))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace lanes
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include "util/debug.hpp"
|
||||
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/turn_discovery.hpp"
|
||||
#include "extractor/guidance/turn_lane_augmentation.hpp"
|
||||
@ -30,13 +32,31 @@ std::size_t getNumberOfTurns(const Intersection &intersection)
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const constexpr char *TurnLaneHandler::scenario_names[TurnLaneScenario::NUM_SCENARIOS];
|
||||
|
||||
TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
const std::vector<TurnLaneType::Mask> &turn_lane_masks,
|
||||
const TurnAnalysis &turn_analysis)
|
||||
std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
std::vector<TurnLaneType::Mask> &turn_lane_masks,
|
||||
LaneDescriptionMap &lane_description_map,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const TurnAnalysis &turn_analysis,
|
||||
LaneDataIdMap &id_map)
|
||||
: node_based_graph(node_based_graph), turn_lane_offsets(turn_lane_offsets),
|
||||
turn_lane_masks(turn_lane_masks), turn_analysis(turn_analysis)
|
||||
turn_lane_masks(turn_lane_masks), lane_description_map(lane_description_map),
|
||||
node_info_list(node_info_list), turn_analysis(turn_analysis), id_map(id_map)
|
||||
{
|
||||
count_handled = new unsigned;
|
||||
count_called = new unsigned;
|
||||
*count_handled = *count_called = 0;
|
||||
}
|
||||
|
||||
TurnLaneHandler::~TurnLaneHandler()
|
||||
{
|
||||
std::cout << "Handled: " << *count_handled << " of " << *count_called
|
||||
<< " lanes: " << (double)(*count_handled * 100) / (*count_called) << " %."
|
||||
<< std::endl;
|
||||
delete count_called;
|
||||
delete count_handled;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -57,54 +77,199 @@ TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_g
|
||||
assignment onto the turns.
|
||||
For example: (130, turn slight right), (180, ramp straight), (320, turn sharp left).
|
||||
*/
|
||||
Intersection TurnLaneHandler::assignTurnLanes(const NodeID at,
|
||||
const EdgeID via_edge,
|
||||
Intersection intersection,
|
||||
LaneDataIdMap &id_map) const
|
||||
Intersection
|
||||
TurnLaneHandler::assignTurnLanes(const NodeID at, const EdgeID via_edge, Intersection intersection)
|
||||
{
|
||||
// if only a uturn exists, there is nothing we can do
|
||||
if (intersection.size() == 1)
|
||||
return intersection;
|
||||
|
||||
const auto &data = node_based_graph.GetEdgeData(via_edge);
|
||||
// Extract a lane description for the ID
|
||||
// A list of output parameters to be filled in during deduceScenario.
|
||||
// Data for the current intersection
|
||||
LaneDescriptionID lane_description_id = INVALID_LANE_DESCRIPTIONID;
|
||||
LaneDataVector lane_data;
|
||||
// Data for the previous intersection
|
||||
NodeID previous_node = SPECIAL_NODEID;
|
||||
EdgeID previous_via_edge = SPECIAL_EDGEID;
|
||||
Intersection previous_intersection;
|
||||
LaneDataVector previous_lane_data;
|
||||
LaneDescriptionID previous_description_id = INVALID_LANE_DESCRIPTIONID;
|
||||
|
||||
const auto turn_lane_description =
|
||||
data.lane_description_id != INVALID_LANE_DESCRIPTIONID
|
||||
? TurnLaneDescription(
|
||||
turn_lane_masks.begin() + turn_lane_offsets[data.lane_description_id],
|
||||
turn_lane_masks.begin() + turn_lane_offsets[data.lane_description_id + 1])
|
||||
: TurnLaneDescription();
|
||||
const auto scenario = deduceScenario(at,
|
||||
via_edge,
|
||||
intersection,
|
||||
lane_description_id,
|
||||
lane_data,
|
||||
previous_node,
|
||||
previous_via_edge,
|
||||
previous_intersection,
|
||||
previous_lane_data,
|
||||
previous_description_id);
|
||||
|
||||
BOOST_ASSERT(turn_lane_description.empty() ||
|
||||
turn_lane_description.size() == (turn_lane_offsets[data.lane_description_id + 1] -
|
||||
turn_lane_offsets[data.lane_description_id]));
|
||||
std::cout << "[turn lane] " << scenario_names[scenario] << std::endl;
|
||||
|
||||
// going straight, due to traffic signals, we can have uncompressed geometry
|
||||
if (intersection.size() == 2 &&
|
||||
((data.lane_description_id != INVALID_LANE_DESCRIPTIONID &&
|
||||
data.lane_description_id ==
|
||||
node_based_graph.GetEdgeData(intersection[1].turn.eid).lane_description_id) ||
|
||||
angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE))
|
||||
if (scenario != TurnLaneHandler::NONE)
|
||||
(*count_called)++;
|
||||
|
||||
switch (scenario)
|
||||
{
|
||||
// A turn based on current lane data
|
||||
case TurnLaneScenario::SIMPLE:
|
||||
case TurnLaneScenario::PARTITION_LOCAL:
|
||||
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
|
||||
return simpleMatchTuplesToTurns(std::move(intersection), lane_data, lane_description_id);
|
||||
|
||||
// Cases operating on data carried over from a previous lane
|
||||
case TurnLaneScenario::SIMPLE_PREVIOUS:
|
||||
case TurnLaneScenario::PARTITION_PREVIOUS:
|
||||
previous_lane_data =
|
||||
handleNoneValueAtSimpleTurn(std::move(previous_lane_data), intersection);
|
||||
return simpleMatchTuplesToTurns(
|
||||
std::move(intersection), previous_lane_data, previous_description_id);
|
||||
|
||||
// Sliproads-turns that are to be handled as a single entity
|
||||
case TurnLaneScenario::SLIPROAD:
|
||||
return handleSliproadTurn(std::move(intersection),
|
||||
lane_description_id,
|
||||
std::move(lane_data),
|
||||
previous_intersection,
|
||||
previous_description_id,
|
||||
previous_lane_data);
|
||||
case TurnLaneScenario::MERGE:
|
||||
return intersection;
|
||||
default:
|
||||
// All different values that we cannot handle. For some me might want to print debug output
|
||||
// later on, when the handling is actually improved to work in close to all cases.
|
||||
// case TurnLaneScenario::UNKNOWN:
|
||||
// case TurnLaneScenario::NONE:
|
||||
// case TurnLaneScenario::INVALID:
|
||||
{
|
||||
/*
|
||||
static int print_count = 0;
|
||||
if (TurnLaneScenario::NONE != scenario && print_count++ < 10)
|
||||
{
|
||||
std::cout << "[Unhandled] " << (int)lane_description_id << " -- "
|
||||
<< (int)previous_description_id << "\n"
|
||||
<< std::endl;
|
||||
util::guidance::printTurnAssignmentData(
|
||||
at, lane_data, intersection, node_info_list);
|
||||
|
||||
auto lane_data = laneDataFromDescription(turn_lane_description);
|
||||
if (previous_node != SPECIAL_NODEID)
|
||||
util::guidance::printTurnAssignmentData(
|
||||
previous_node, previous_lane_data, previous_intersection, node_info_list);
|
||||
}
|
||||
*/
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
}
|
||||
|
||||
// Find out which scenario we have to handle
|
||||
TurnLaneHandler::TurnLaneScenario
|
||||
TurnLaneHandler::deduceScenario(const NodeID at,
|
||||
const EdgeID via_edge,
|
||||
const Intersection &intersection,
|
||||
// Output Variables
|
||||
LaneDescriptionID &lane_description_id,
|
||||
LaneDataVector &lane_data,
|
||||
NodeID &previous_node,
|
||||
EdgeID &previous_via_edge,
|
||||
Intersection &previous_intersection,
|
||||
LaneDataVector &previous_lane_data,
|
||||
LaneDescriptionID &previous_description_id)
|
||||
{
|
||||
// if only a uturn exists, there is nothing we can do
|
||||
if (intersection.size() == 1)
|
||||
return TurnLaneHandler::NONE;
|
||||
|
||||
extractLaneData(via_edge, lane_description_id, lane_data);
|
||||
|
||||
// traffic lights are not compressed during our preprocessing. Due to this *shortcoming*, we can
|
||||
// get to the following situation:
|
||||
//
|
||||
// d
|
||||
// a - b - c
|
||||
// e
|
||||
//
|
||||
// with a traffic light at b and a-b as well as b-c offering the same turn lanes.
|
||||
// In such a situation, we don't need to handle the lanes at a-b, since we will get the same
|
||||
// information at b-c, where the actual turns are taking place.
|
||||
const bool is_going_straight_and_turns_continue =
|
||||
(intersection.size() == 2 &&
|
||||
((lane_description_id != INVALID_LANE_DESCRIPTIONID &&
|
||||
lane_description_id ==
|
||||
node_based_graph.GetEdgeData(intersection[1].turn.eid).lane_description_id) ||
|
||||
angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE));
|
||||
|
||||
if (is_going_straight_and_turns_continue)
|
||||
return TurnLaneHandler::NONE;
|
||||
|
||||
// if we see an invalid conversion, we stop immediately
|
||||
if (!turn_lane_description.empty() && lane_data.empty())
|
||||
return intersection;
|
||||
if (lane_description_id != INVALID_LANE_DESCRIPTIONID && lane_data.empty())
|
||||
return TurnLaneScenario::INVALID;
|
||||
|
||||
// might be reasonable to handle multiple turns, if we know of a sequence of lanes
|
||||
// e.g. one direction per lane, if three lanes and right, through, left available
|
||||
if (!turn_lane_description.empty() && lane_data.size() == 1 &&
|
||||
if (lane_description_id != INVALID_LANE_DESCRIPTIONID && lane_data.size() == 1 &&
|
||||
lane_data[0].tag == TurnLaneType::none)
|
||||
return intersection;
|
||||
return TurnLaneScenario::NONE;
|
||||
|
||||
// Due to sliproads, we might need access to the previous intersection at this point already;
|
||||
previous_node = SPECIAL_NODEID;
|
||||
previous_via_edge = SPECIAL_EDGEID;
|
||||
if (findPreviousIntersection(at,
|
||||
via_edge,
|
||||
intersection,
|
||||
turn_analysis,
|
||||
node_based_graph,
|
||||
previous_node,
|
||||
previous_via_edge,
|
||||
previous_intersection))
|
||||
{
|
||||
extractLaneData(previous_via_edge, previous_description_id, previous_lane_data);
|
||||
for (std::size_t road_index = 0; road_index < previous_intersection.size(); ++road_index)
|
||||
{
|
||||
const auto &road = previous_intersection[road_index];
|
||||
// in case of a sliproad that is connected to road of simlar angle, we handle the
|
||||
// turn as a combined turn
|
||||
if (road.turn.instruction.type == TurnType::Sliproad)
|
||||
{
|
||||
if (via_edge == road.turn.eid)
|
||||
return TurnLaneScenario::SLIPROAD;
|
||||
|
||||
const auto &closest_road = [&]() {
|
||||
if (road_index + 1 == previous_intersection.size())
|
||||
{
|
||||
BOOST_ASSERT(road_index > 1);
|
||||
return previous_intersection[road_index - 1];
|
||||
}
|
||||
else if (road_index == 1)
|
||||
{
|
||||
BOOST_ASSERT(road_index + 1 < previous_intersection.size());
|
||||
return previous_intersection[road_index + 1];
|
||||
}
|
||||
else if (angularDeviation(road.turn.angle,
|
||||
previous_intersection.at(road_index - 1).turn.angle) <
|
||||
angularDeviation(road.turn.angle,
|
||||
previous_intersection.at(road_index + 1).turn.angle))
|
||||
return previous_intersection[road_index - 1];
|
||||
else
|
||||
return previous_intersection[road_index + 1];
|
||||
}();
|
||||
|
||||
if (via_edge == closest_road.turn.eid)
|
||||
return TurnLaneScenario::SLIPROAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::size_t possible_entries = getNumberOfTurns(intersection);
|
||||
|
||||
// merge does not justify an instruction
|
||||
const bool has_merge_lane =
|
||||
hasTag(TurnLaneType::merge_to_left | TurnLaneType::merge_to_right, lane_data);
|
||||
if (has_merge_lane)
|
||||
return TurnLaneScenario::MERGE;
|
||||
|
||||
// Dead end streets that don't have any left-tag. This can happen due to the fallbacks for
|
||||
// broken data/barriers.
|
||||
@ -114,12 +279,13 @@ Intersection TurnLaneHandler::assignTurnLanes(const NodeID at,
|
||||
lane_data) &&
|
||||
lane_data.size() + 1 == possible_entries);
|
||||
|
||||
if (has_merge_lane || has_non_usable_u_turn)
|
||||
return intersection;
|
||||
if (has_non_usable_u_turn)
|
||||
return TurnLaneScenario::INVALID;
|
||||
|
||||
// check if a u-turn is allowed (for some reason) and is missing from the list of tags (u-turn
|
||||
// is often allowed from the `left` lane without an additional indication dedicated to u-turns).
|
||||
const bool is_missing_valid_u_turn =
|
||||
!lane_data.empty() && canMatchTrivially(intersection, lane_data) &&
|
||||
lane_data.size() !=
|
||||
static_cast<std::size_t>((
|
||||
!hasTag(TurnLaneType::uturn, lane_data) && intersection[0].entry_allowed ? 1 : 0)) +
|
||||
@ -132,18 +298,31 @@ Intersection TurnLaneHandler::assignTurnLanes(const NodeID at,
|
||||
lane_data.push_back({TurnLaneType::uturn, lane_data.back().to, lane_data.back().to});
|
||||
|
||||
bool is_simple = isSimpleIntersection(lane_data, intersection);
|
||||
// simple intersections can be assigned directly
|
||||
|
||||
if (is_simple)
|
||||
{
|
||||
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
|
||||
return simpleMatchTuplesToTurns(
|
||||
std::move(intersection), lane_data, data.lane_description_id, id_map);
|
||||
}
|
||||
// if the intersection is not simple but we have lane data, we check for intersections with
|
||||
// middle islands. We have two cases. The first one is providing lane data on the current
|
||||
// segment and we only need to consider the part of the current segment. In this case we
|
||||
// partition the data and only consider the first part.
|
||||
else if (!lane_data.empty())
|
||||
return TurnLaneScenario::SIMPLE;
|
||||
|
||||
// In case of intersections that don't offer all turns right away, we have to account for
|
||||
// *delayed* turns. Consider the following example:
|
||||
//
|
||||
// e
|
||||
// a - b - c - f
|
||||
// d
|
||||
//
|
||||
// With lanes on a-b indicating: | left | through | right |.
|
||||
// While right obviously refers to a-b-d, through and left refer to a-b-c-f and a-b-c-e
|
||||
// respectively. While we are at a-b, we have to consider the right turn only.
|
||||
// The turn a-b-c gets assigned the lanes of both *left* and *through*.
|
||||
// At b-c, we get access to either a new set of lanes, or -- via the previous intersection
|
||||
// -- to
|
||||
// the second part of | left | through | right |. Lane anticipation can then deduce which
|
||||
// lanes
|
||||
// correspond to what and suppress unnecessary instructions.
|
||||
//
|
||||
// For our initial case, we consider only the turns that are available at the current
|
||||
// location,
|
||||
// which are given by partitioning the lane data and selecting the first part.
|
||||
if (!lane_data.empty())
|
||||
{
|
||||
if (lane_data.size() >= possible_entries)
|
||||
{
|
||||
@ -155,115 +334,73 @@ Intersection TurnLaneHandler::assignTurnLanes(const NodeID at,
|
||||
// check if we were successfull in trimming
|
||||
if (lane_data.size() == possible_entries &&
|
||||
isSimpleIntersection(lane_data, intersection))
|
||||
{
|
||||
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
|
||||
return simpleMatchTuplesToTurns(
|
||||
std::move(intersection), lane_data, data.lane_description_id, id_map);
|
||||
}
|
||||
return TurnLaneScenario::PARTITION_LOCAL;
|
||||
}
|
||||
}
|
||||
// The second part does not provide lane data on the current segment, but on the segment prior
|
||||
// to the turn. We try to partition the data and only consider the second part.
|
||||
else if (turn_lane_description.empty())
|
||||
{
|
||||
// acquire the lane data of a previous segment and, if possible, use it for the current
|
||||
// intersection.
|
||||
return handleTurnAtPreviousIntersection(at, via_edge, std::move(intersection), id_map);
|
||||
// If partitioning doesn't solve the problem, we don't know how to handle it right now
|
||||
return TurnLaneScenario::UNKNOWN;
|
||||
}
|
||||
|
||||
return intersection;
|
||||
if (lane_description_id != INVALID_LANE_DESCRIPTIONID)
|
||||
return TurnLaneScenario::UNKNOWN;
|
||||
|
||||
// acquire the lane data of a previous segment and, if possible, use it for the current
|
||||
// intersection.
|
||||
if (previous_via_edge == SPECIAL_EDGEID)
|
||||
return TurnLaneScenario::NONE;
|
||||
|
||||
if (previous_lane_data.empty())
|
||||
return TurnLaneScenario::NONE;
|
||||
|
||||
const bool previous_has_merge_lane =
|
||||
hasTag(TurnLaneType::merge_to_left | TurnLaneType::merge_to_right, previous_lane_data);
|
||||
if (previous_has_merge_lane)
|
||||
return TurnLaneScenario::MERGE;
|
||||
|
||||
const auto is_simple_previous =
|
||||
isSimpleIntersection(previous_lane_data, intersection) && previous_intersection.size() == 2;
|
||||
if (is_simple_previous)
|
||||
return TurnLaneScenario::SIMPLE_PREVIOUS;
|
||||
|
||||
// This is the second part of the previously described partitioning scenario.
|
||||
if (previous_lane_data.size() >= getNumberOfTurns(previous_intersection) &&
|
||||
previous_intersection.size() != 2)
|
||||
{
|
||||
previous_lane_data = partitionLaneData(node_based_graph.GetTarget(previous_via_edge),
|
||||
std::move(previous_lane_data),
|
||||
previous_intersection)
|
||||
.second;
|
||||
|
||||
std::sort(previous_lane_data.begin(), previous_lane_data.end());
|
||||
|
||||
// check if we were successfull in trimming
|
||||
if ((previous_lane_data.size() == possible_entries) &&
|
||||
isSimpleIntersection(previous_lane_data, intersection))
|
||||
return TurnLaneScenario::PARTITION_PREVIOUS;
|
||||
}
|
||||
|
||||
return TurnLaneScenario::UNKNOWN;
|
||||
}
|
||||
|
||||
// At segregated intersections, turn lanes will often only be specified up until the first turn. To
|
||||
// actually take the turn, we need to look back to the edge we drove onto the intersection with.
|
||||
Intersection TurnLaneHandler::handleTurnAtPreviousIntersection(const NodeID at,
|
||||
const EdgeID via_edge,
|
||||
Intersection intersection,
|
||||
LaneDataIdMap &id_map) const
|
||||
void TurnLaneHandler::extractLaneData(const EdgeID via_edge,
|
||||
LaneDescriptionID &lane_description_id,
|
||||
LaneDataVector &lane_data) const
|
||||
{
|
||||
NodeID previous_node = SPECIAL_NODEID;
|
||||
Intersection previous_intersection;
|
||||
EdgeID previous_id = SPECIAL_EDGEID;
|
||||
LaneDataVector lane_data;
|
||||
const auto &edge_data = node_based_graph.GetEdgeData(via_edge);
|
||||
// TODO access correct data
|
||||
lane_description_id = edge_data.lane_description_id;
|
||||
const auto lane_description =
|
||||
lane_description_id != INVALID_LANE_DESCRIPTIONID
|
||||
? TurnLaneDescription(turn_lane_masks.begin() + turn_lane_offsets[lane_description_id],
|
||||
turn_lane_masks.begin() +
|
||||
turn_lane_offsets[lane_description_id + 1])
|
||||
: TurnLaneDescription();
|
||||
|
||||
// Get the previous lane string. We only accept strings that stem from a not-simple intersection
|
||||
// and are not empty.
|
||||
const auto previous_lane_description = [&]() -> TurnLaneDescription {
|
||||
if (!findPreviousIntersection(at,
|
||||
via_edge,
|
||||
intersection,
|
||||
turn_analysis,
|
||||
node_based_graph,
|
||||
previous_node,
|
||||
previous_id,
|
||||
previous_intersection))
|
||||
return {};
|
||||
if (!lane_description.empty())
|
||||
lane_data = laneDataFromDescription(lane_description);
|
||||
|
||||
BOOST_ASSERT(previous_id != SPECIAL_EDGEID);
|
||||
|
||||
const auto &previous_edge_data = node_based_graph.GetEdgeData(previous_id);
|
||||
// TODO access correct data
|
||||
const auto previous_description =
|
||||
previous_edge_data.lane_description_id != INVALID_LANE_DESCRIPTIONID
|
||||
? TurnLaneDescription(
|
||||
turn_lane_masks.begin() +
|
||||
turn_lane_offsets[previous_edge_data.lane_description_id],
|
||||
turn_lane_masks.begin() +
|
||||
turn_lane_offsets[previous_edge_data.lane_description_id + 1])
|
||||
: TurnLaneDescription();
|
||||
if (previous_description.empty())
|
||||
return previous_description;
|
||||
|
||||
previous_intersection = turn_analysis.assignTurnTypes(
|
||||
previous_node, previous_id, std::move(previous_intersection));
|
||||
|
||||
lane_data = laneDataFromDescription(previous_description);
|
||||
|
||||
if (isSimpleIntersection(lane_data, previous_intersection))
|
||||
return {};
|
||||
else
|
||||
return previous_description;
|
||||
}();
|
||||
|
||||
// no lane string, no problems
|
||||
if (previous_lane_description.empty())
|
||||
return intersection;
|
||||
|
||||
// stop on invalid lane data conversion
|
||||
if (lane_data.empty())
|
||||
return intersection;
|
||||
|
||||
const auto &previous_data = node_based_graph.GetEdgeData(previous_id);
|
||||
const auto is_simple = isSimpleIntersection(lane_data, intersection);
|
||||
if (is_simple)
|
||||
{
|
||||
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
|
||||
return simpleMatchTuplesToTurns(
|
||||
std::move(intersection), lane_data, previous_data.lane_description_id, id_map);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lane_data.size() >= getNumberOfTurns(previous_intersection) &&
|
||||
previous_intersection.size() != 2)
|
||||
{
|
||||
lane_data = partitionLaneData(node_based_graph.GetTarget(previous_id),
|
||||
std::move(lane_data),
|
||||
previous_intersection)
|
||||
.second;
|
||||
|
||||
std::sort(lane_data.begin(), lane_data.end());
|
||||
|
||||
// check if we were successfull in trimming
|
||||
if (lane_data.size() == getNumberOfTurns(intersection) &&
|
||||
isSimpleIntersection(lane_data, intersection))
|
||||
{
|
||||
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
|
||||
return simpleMatchTuplesToTurns(
|
||||
std::move(intersection), lane_data, previous_data.lane_description_id, id_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
return intersection;
|
||||
BOOST_ASSERT(lane_description.empty() ||
|
||||
lane_description.size() == (turn_lane_offsets[lane_description_id + 1] -
|
||||
turn_lane_offsets[lane_description_id]));
|
||||
}
|
||||
|
||||
/* A simple intersection does not depend on the next intersection coming up. This is important
|
||||
@ -414,8 +551,10 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
* case left) turn lane as if it were to continue straight onto the intersection and
|
||||
* look back between (1) and (2) to make sure we find the correct lane for the left-turn.
|
||||
*
|
||||
* Intersections like these have two parts. Turns that can be made at the first intersection and
|
||||
* turns that have to be made at the second. The partitioning returns the lane data split into
|
||||
* Intersections like these have two parts. Turns that can be made at the first intersection
|
||||
* and
|
||||
* turns that have to be made at the second. The partitioning returns the lane data split
|
||||
* into
|
||||
* two parts, one for the first and one for the second intersection.
|
||||
*/
|
||||
|
||||
@ -433,7 +572,8 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
std::vector<bool> matched_at_first(turn_lane_data.size(), false);
|
||||
std::vector<bool> matched_at_second(turn_lane_data.size(), false);
|
||||
|
||||
// find out about the next intersection. To check for valid matches, we also need the turn types
|
||||
// find out about the next intersection. To check for valid matches, we also need the turn
|
||||
// types
|
||||
auto next_intersection = turn_analysis.getIntersection(at, straightmost->turn.eid);
|
||||
next_intersection =
|
||||
turn_analysis.assignTurnTypes(at, straightmost->turn.eid, std::move(next_intersection));
|
||||
@ -447,7 +587,8 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
continue;
|
||||
|
||||
const auto best_match = findBestMatch(turn_lane_data[lane].tag, intersection);
|
||||
if (isValidMatch(turn_lane_data[lane].tag, best_match->turn.instruction))
|
||||
if (best_match->entry_allowed &&
|
||||
isValidMatch(turn_lane_data[lane].tag, best_match->turn.instruction))
|
||||
{
|
||||
matched_at_first[lane] = true;
|
||||
|
||||
@ -457,9 +598,20 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
|
||||
const auto best_match_at_next_intersection =
|
||||
findBestMatch(turn_lane_data[lane].tag, next_intersection);
|
||||
if (isValidMatch(turn_lane_data[lane].tag,
|
||||
if (best_match_at_next_intersection->entry_allowed &&
|
||||
isValidMatch(turn_lane_data[lane].tag,
|
||||
best_match_at_next_intersection->turn.instruction))
|
||||
matched_at_second[lane] = true;
|
||||
{
|
||||
if (!matched_at_first[lane] || turn_lane_data[lane].tag == TurnLaneType::straight ||
|
||||
getMatchingQuality(turn_lane_data[lane].tag, *best_match) >
|
||||
getMatchingQuality(turn_lane_data[lane].tag, *best_match_at_next_intersection))
|
||||
{
|
||||
if (turn_lane_data[lane].tag != TurnLaneType::straight)
|
||||
matched_at_first[lane] = false;
|
||||
|
||||
matched_at_second[lane] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// we need to match all items to either the current or the next intersection
|
||||
if (!(matched_at_first[lane] || matched_at_second[lane]))
|
||||
@ -504,7 +656,6 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
LaneDataVector first, second;
|
||||
for (std::size_t lane = 0; lane < turn_lane_data.size(); ++lane)
|
||||
{
|
||||
|
||||
if (matched_at_second[lane])
|
||||
second.push_back(turn_lane_data[lane]);
|
||||
|
||||
@ -535,8 +686,7 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
|
||||
Intersection TurnLaneHandler::simpleMatchTuplesToTurns(Intersection intersection,
|
||||
const LaneDataVector &lane_data,
|
||||
const LaneDescriptionID lane_description_id,
|
||||
LaneDataIdMap &id_map) const
|
||||
const LaneDescriptionID lane_description_id)
|
||||
{
|
||||
if (lane_data.empty() || !canMatchTrivially(intersection, lane_data))
|
||||
return intersection;
|
||||
@ -545,10 +695,127 @@ Intersection TurnLaneHandler::simpleMatchTuplesToTurns(Intersection intersection
|
||||
!hasTag(TurnLaneType::none | TurnLaneType::merge_to_left | TurnLaneType::merge_to_right,
|
||||
lane_data));
|
||||
|
||||
(*count_handled)++;
|
||||
|
||||
return triviallyMatchLanesToTurns(
|
||||
std::move(intersection), lane_data, node_based_graph, lane_description_id, id_map);
|
||||
}
|
||||
|
||||
Intersection
|
||||
TurnLaneHandler::handleSliproadTurn(Intersection intersection,
|
||||
const LaneDescriptionID lane_description_id,
|
||||
LaneDataVector lane_data,
|
||||
const Intersection &previous_intersection,
|
||||
const LaneDescriptionID &previous_lane_description_id,
|
||||
const LaneDataVector &previous_lane_data)
|
||||
{
|
||||
const auto sliproad_index =
|
||||
std::distance(previous_intersection.begin(),
|
||||
std::find_if(previous_intersection.begin(),
|
||||
previous_intersection.end(),
|
||||
[](const ConnectedRoad &road) {
|
||||
return road.turn.instruction.type == TurnType::Sliproad;
|
||||
}));
|
||||
|
||||
BOOST_ASSERT(sliproad_index <= previous_intersection.size());
|
||||
const auto &sliproad = previous_intersection[sliproad_index];
|
||||
|
||||
// code duplicatino with deduceScenario: TODO refactor
|
||||
const auto &main_road = [&]() {
|
||||
if (sliproad_index + 1 == previous_intersection.size())
|
||||
{
|
||||
BOOST_ASSERT(sliproad_index > 1);
|
||||
return previous_intersection[sliproad_index - 1];
|
||||
}
|
||||
else if (sliproad_index == 1)
|
||||
{
|
||||
BOOST_ASSERT(sliproad_index + 1 < previous_intersection.size());
|
||||
return previous_intersection[sliproad_index + 1];
|
||||
}
|
||||
else if (angularDeviation(sliproad.turn.angle,
|
||||
previous_intersection.at(sliproad_index - 1).turn.angle) <
|
||||
angularDeviation(sliproad.turn.angle,
|
||||
previous_intersection.at(sliproad_index + 1).turn.angle))
|
||||
return previous_intersection[sliproad_index - 1];
|
||||
else
|
||||
return previous_intersection[sliproad_index + 1];
|
||||
}();
|
||||
const auto main_description_id =
|
||||
node_based_graph.GetEdgeData(main_road.turn.eid).lane_description_id;
|
||||
const auto sliproad_description_id =
|
||||
node_based_graph.GetEdgeData(sliproad.turn.eid).lane_description_id;
|
||||
|
||||
if (main_description_id == INVALID_LANE_DESCRIPTIONID ||
|
||||
sliproad_description_id == INVALID_LANE_DESCRIPTIONID)
|
||||
return intersection;
|
||||
|
||||
TurnLaneDescription combined_description;
|
||||
// is the sliproad going off to the right?
|
||||
if (main_road.turn.angle > sliproad.turn.angle)
|
||||
{
|
||||
combined_description.insert(
|
||||
combined_description.end(),
|
||||
turn_lane_masks.begin() + turn_lane_offsets[main_description_id],
|
||||
turn_lane_masks.begin() + turn_lane_offsets[main_description_id + 1]);
|
||||
|
||||
combined_description.insert(
|
||||
combined_description.end(),
|
||||
turn_lane_masks.begin() + turn_lane_offsets[sliproad_description_id],
|
||||
turn_lane_masks.begin() + turn_lane_offsets[sliproad_description_id + 1]);
|
||||
|
||||
// if we handle the main road, we have to adjust the lane-data
|
||||
if (main_description_id == lane_description_id)
|
||||
{
|
||||
const auto offset = turn_lane_offsets[sliproad_description_id + 1] -
|
||||
turn_lane_offsets[sliproad_description_id];
|
||||
for (auto &item : lane_data)
|
||||
{
|
||||
item.from += offset;
|
||||
item.to += offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
// or to the left?
|
||||
else
|
||||
{
|
||||
combined_description.insert(
|
||||
combined_description.end(),
|
||||
turn_lane_masks.begin() + turn_lane_offsets[sliproad_description_id],
|
||||
turn_lane_masks.begin() + turn_lane_offsets[sliproad_description_id + 1]);
|
||||
combined_description.insert(
|
||||
combined_description.end(),
|
||||
turn_lane_masks.begin() + turn_lane_offsets[main_description_id],
|
||||
turn_lane_masks.begin() + turn_lane_offsets[main_description_id + 1]);
|
||||
|
||||
// if we are handling the sliproad, we have to adjust its lane data
|
||||
if (sliproad_description_id == lane_description_id)
|
||||
{
|
||||
const auto offset =
|
||||
turn_lane_offsets[main_description_id + 1] - turn_lane_offsets[main_description_id];
|
||||
for (auto &item : lane_data)
|
||||
{
|
||||
item.from += offset;
|
||||
item.to += offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto combined_id = [&]() {
|
||||
auto itr = lane_description_map.find(combined_description);
|
||||
if (lane_description_map.find(combined_description) == lane_description_map.end())
|
||||
{
|
||||
const auto new_id = boost::numeric_cast<LaneDescriptionID>(lane_description_map.size());
|
||||
lane_description_map[combined_description] = new_id;
|
||||
return new_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return itr->second;
|
||||
}
|
||||
}();
|
||||
return simpleMatchTuplesToTurns(std::move(intersection), lane_data, combined_id);
|
||||
}
|
||||
|
||||
} // namespace lanes
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
|
@ -17,7 +17,7 @@ namespace lanes
|
||||
{
|
||||
|
||||
// Translate Turn Tags into a Matching Direction Modifier
|
||||
DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask &tag)
|
||||
DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask tag)
|
||||
{
|
||||
const constexpr TurnLaneType::Mask tag_by_modifier[] = {TurnLaneType::uturn,
|
||||
TurnLaneType::sharp_right,
|
||||
@ -51,7 +51,7 @@ DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask &tag)
|
||||
}
|
||||
|
||||
// check whether a match of a given tag and a turn instruction can be seen as valid
|
||||
bool isValidMatch(const TurnLaneType::Mask &tag, const TurnInstruction instruction)
|
||||
bool isValidMatch(const TurnLaneType::Mask tag, const TurnInstruction instruction)
|
||||
{
|
||||
using util::guidance::hasLeftModifier;
|
||||
using util::guidance::hasRightModifier;
|
||||
@ -102,29 +102,34 @@ bool isValidMatch(const TurnLaneType::Mask &tag, const TurnInstruction instructi
|
||||
return false;
|
||||
}
|
||||
|
||||
double getMatchingQuality( const TurnLaneType::Mask tag, const ConnectedRoad &road)
|
||||
{
|
||||
const constexpr double idealized_turn_angles[] = {0, 35, 90, 135, 180, 225, 270, 315};
|
||||
const auto idealized_angle = idealized_turn_angles[getMatchingModifier(tag)];
|
||||
return angularDeviation(idealized_angle,road.turn.angle);
|
||||
}
|
||||
|
||||
// Every tag is somewhat idealized in form of the expected angle. A through lane should go straight
|
||||
// (or follow a 180 degree turn angle between in/out segments.) The following function tries to find
|
||||
// the best possible match for every tag in a given intersection, considering a few corner cases
|
||||
// introduced to OSRM handling u-turns
|
||||
typename Intersection::const_iterator findBestMatch(const TurnLaneType::Mask &tag,
|
||||
typename Intersection::const_iterator findBestMatch(const TurnLaneType::Mask tag,
|
||||
const Intersection &intersection)
|
||||
{
|
||||
const constexpr double idealized_turn_angles[] = {0, 35, 90, 135, 180, 225, 270, 315};
|
||||
const auto idealized_angle = idealized_turn_angles[getMatchingModifier(tag)];
|
||||
return std::min_element(
|
||||
intersection.begin(),
|
||||
intersection.end(),
|
||||
[idealized_angle, &tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
|
||||
[tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
|
||||
// prefer valid matches
|
||||
if (isValidMatch(tag, lhs.turn.instruction) != isValidMatch(tag, rhs.turn.instruction))
|
||||
return isValidMatch(tag, lhs.turn.instruction);
|
||||
|
||||
// if the entry allowed flags don't match, we select the one with
|
||||
// entry allowed set to true
|
||||
if (lhs.entry_allowed != rhs.entry_allowed)
|
||||
return lhs.entry_allowed;
|
||||
|
||||
return angularDeviation(idealized_angle, lhs.turn.angle) <
|
||||
angularDeviation(idealized_angle, rhs.turn.angle);
|
||||
return getMatchingQuality(tag,lhs) < getMatchingQuality(tag,rhs);
|
||||
});
|
||||
}
|
||||
|
||||
@ -140,23 +145,21 @@ findBestMatchForReverse(const TurnLaneType::Mask &neighbor_tag, const Intersecti
|
||||
if (neighbor_itr + 1 == intersection.cend())
|
||||
return intersection.begin();
|
||||
|
||||
const constexpr double idealized_turn_angles[] = {0, 35, 90, 135, 180, 225, 270, 315};
|
||||
const TurnLaneType::Mask tag = TurnLaneType::uturn;
|
||||
const auto idealized_angle = idealized_turn_angles[getMatchingModifier(tag)];
|
||||
return std::min_element(
|
||||
intersection.begin() + std::distance(intersection.begin(), neighbor_itr),
|
||||
intersection.end(),
|
||||
[idealized_angle, &tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
|
||||
[tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
|
||||
// prefer valid matches
|
||||
if (isValidMatch(tag, lhs.turn.instruction) != isValidMatch(tag, rhs.turn.instruction))
|
||||
return isValidMatch(tag, lhs.turn.instruction);
|
||||
|
||||
// if the entry allowed flags don't match, we select the one with
|
||||
// entry allowed set to true
|
||||
if (lhs.entry_allowed != rhs.entry_allowed)
|
||||
return lhs.entry_allowed;
|
||||
|
||||
return angularDeviation(idealized_angle, lhs.turn.angle) <
|
||||
angularDeviation(idealized_angle, rhs.turn.angle);
|
||||
return getMatchingQuality(tag,lhs) < getMatchingQuality(tag,rhs);
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user