turn lane handler moved to scenario based handling

This commit is contained in:
Moritz Kobitzsch 2016-06-30 09:31:08 +02:00
parent 802b93fa9a
commit 3b81b39998
21 changed files with 955 additions and 292 deletions

View 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, |

View File

@ -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, |

View File

@ -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();

View File

@ -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);
};
}
}

View File

@ -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;
};
}
}

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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];

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
});
}