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
+17 -9
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
+1 -35
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)
+63 -12
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
+7 -12
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
+20 -5
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));
@@ -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
+24 -1
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
+420 -153
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
+16 -13
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);
});
}