basic turn lane handling
This commit is contained in:
@@ -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"
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
#include "extractor/guidance/turn_analysis.hpp"
|
||||
#include "extractor/guidance/turn_lane_handler.hpp"
|
||||
#include "extractor/suffix_table.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
@@ -39,12 +40,14 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory(
|
||||
std::shared_ptr<const RestrictionMap> restriction_map,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
ProfileProperties profile_properties,
|
||||
const util::NameTable &name_table)
|
||||
const util::NameTable &name_table,
|
||||
const util::NameTable &turn_lanes)
|
||||
: 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)
|
||||
profile_properties(std::move(profile_properties)), name_table(name_table),
|
||||
turn_lanes(turn_lanes)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -339,6 +342,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
m_compressed_edge_container,
|
||||
name_table,
|
||||
street_name_suffix_table);
|
||||
guidance::lanes::TurnLaneHandler turn_lane_handler(
|
||||
*m_node_based_graph, turn_lanes, m_node_info_list, turn_analysis);
|
||||
|
||||
bearing_class_by_node_based_node.resize(m_node_based_graph->GetNumberOfNodes(),
|
||||
std::numeric_limits<std::uint32_t>::max());
|
||||
@@ -353,19 +358,23 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
continue;
|
||||
}
|
||||
|
||||
++node_based_edge_counter;
|
||||
auto possible_turns = turn_analysis.getTurns(node_u, edge_from_u);
|
||||
|
||||
const NodeID node_v = m_node_based_graph->GetTarget(edge_from_u);
|
||||
++node_based_edge_counter;
|
||||
auto intersection = turn_analysis.getIntersection(node_u, edge_from_u);
|
||||
intersection =
|
||||
turn_analysis.assignTurnTypes(node_u, edge_from_u, std::move(intersection));
|
||||
|
||||
intersection =
|
||||
turn_lane_handler.assignTurnLanes(node_u, edge_from_u, std::move(intersection));
|
||||
const auto possible_turns = turn_analysis.transformIntersectionIntoTurns(intersection);
|
||||
|
||||
// the entry class depends on the turn, so we have to classify the interesction for
|
||||
// every edge
|
||||
const auto turn_classification =
|
||||
classifyIntersection(node_v,
|
||||
turn_analysis.getIntersection(node_u, edge_from_u),
|
||||
*m_node_based_graph,
|
||||
m_compressed_edge_container,
|
||||
m_node_info_list);
|
||||
const auto turn_classification = classifyIntersection(node_v,
|
||||
intersection,
|
||||
*m_node_based_graph,
|
||||
m_compressed_edge_container,
|
||||
m_node_info_list);
|
||||
|
||||
const auto entry_class_id = [&](const util::guidance::EntryClass entry_class) {
|
||||
if (0 == entry_class_hash.count(entry_class))
|
||||
|
||||
@@ -51,6 +51,9 @@ ExtractionContainers::ExtractionContainers()
|
||||
name_lengths.push_back(0);
|
||||
name_lengths.push_back(0);
|
||||
name_lengths.push_back(0);
|
||||
name_lengths.push_back(0);
|
||||
name_lengths.push_back(0);
|
||||
turn_lane_lengths.push_back(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,6 +69,7 @@ ExtractionContainers::ExtractionContainers()
|
||||
void ExtractionContainers::PrepareData(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,
|
||||
lua_State *segment_state)
|
||||
{
|
||||
try
|
||||
@@ -83,7 +87,8 @@ void ExtractionContainers::PrepareData(const std::string &output_file_name,
|
||||
PrepareRestrictions();
|
||||
WriteRestrictions(restrictions_file_name);
|
||||
|
||||
WriteNames(name_file_name);
|
||||
WriteCharData(name_file_name,name_lengths,name_char_data);
|
||||
WriteCharData(turn_lane_file_name,turn_lane_lengths,turn_lane_char_data);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
@@ -91,44 +96,46 @@ void ExtractionContainers::PrepareData(const std::string &output_file_name,
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractionContainers::WriteNames(const std::string &names_file_name) const
|
||||
void ExtractionContainers::WriteCharData(const std::string &file_name,
|
||||
const stxxl::vector<unsigned> &offsets,
|
||||
const stxxl::vector<char> &char_data) const
|
||||
{
|
||||
std::cout << "[extractor] writing street name index ... " << std::flush;
|
||||
TIMER_START(write_name_index);
|
||||
boost::filesystem::ofstream name_file_stream(names_file_name, std::ios::binary);
|
||||
TIMER_START(write_index);
|
||||
boost::filesystem::ofstream file_stream(file_name, std::ios::binary);
|
||||
|
||||
unsigned total_length = 0;
|
||||
|
||||
for (const auto name_length : name_lengths)
|
||||
for (const auto length : offsets)
|
||||
{
|
||||
total_length += name_length;
|
||||
total_length += length;
|
||||
}
|
||||
|
||||
// builds and writes the index
|
||||
util::RangeTable<> name_index_range(name_lengths);
|
||||
name_file_stream << name_index_range;
|
||||
util::RangeTable<> index_range(offsets);
|
||||
file_stream << index_range;
|
||||
|
||||
name_file_stream.write((char *)&total_length, sizeof(unsigned));
|
||||
file_stream.write((char *)&total_length, sizeof(unsigned));
|
||||
|
||||
// write all chars consecutively
|
||||
char write_buffer[WRITE_BLOCK_BUFFER_SIZE];
|
||||
unsigned buffer_len = 0;
|
||||
|
||||
for (const auto c : name_char_data)
|
||||
for (const auto c : char_data)
|
||||
{
|
||||
write_buffer[buffer_len++] = c;
|
||||
|
||||
if (buffer_len >= WRITE_BLOCK_BUFFER_SIZE)
|
||||
{
|
||||
name_file_stream.write(write_buffer, WRITE_BLOCK_BUFFER_SIZE);
|
||||
file_stream.write(write_buffer, WRITE_BLOCK_BUFFER_SIZE);
|
||||
buffer_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
name_file_stream.write(write_buffer, buffer_len);
|
||||
file_stream.write(write_buffer, buffer_len);
|
||||
|
||||
TIMER_STOP(write_name_index);
|
||||
std::cout << "ok, after " << TIMER_SEC(write_name_index) << "s" << std::endl;
|
||||
TIMER_STOP(write_index);
|
||||
std::cout << "ok, after " << TIMER_SEC(write_index) << "s" << std::endl;
|
||||
}
|
||||
|
||||
void ExtractionContainers::PrepareNodes()
|
||||
|
||||
@@ -239,6 +239,7 @@ int Extractor::run()
|
||||
extraction_containers.PrepareData(config.output_file_name,
|
||||
config.restriction_file_name,
|
||||
config.names_file_name,
|
||||
config.turn_lane_file_name,
|
||||
main_context.state);
|
||||
|
||||
WriteProfileProperties(config.profile_properties_output_path, main_context.properties);
|
||||
@@ -503,6 +504,7 @@ Extractor::BuildEdgeExpandedGraph(lua_State *lua_state,
|
||||
compressed_edge_container.SerializeInternalVector(config.geometry_output_path);
|
||||
|
||||
util::NameTable name_table(config.names_file_name);
|
||||
util::NameTable turn_lanes(config.turn_lane_file_name);
|
||||
|
||||
EdgeBasedGraphFactory edge_based_graph_factory(
|
||||
node_based_graph,
|
||||
@@ -512,7 +514,8 @@ Extractor::BuildEdgeExpandedGraph(lua_State *lua_state,
|
||||
std::const_pointer_cast<RestrictionMap const>(restriction_map),
|
||||
internal_to_external_node_map,
|
||||
profile_properties,
|
||||
name_table);
|
||||
name_table,
|
||||
turn_lanes);
|
||||
|
||||
edge_based_graph_factory.Run(config.edge_output_path,
|
||||
lua_state,
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
#include "extractor/extraction_containers.hpp"
|
||||
#include "extractor/extraction_node.hpp"
|
||||
#include "extractor/extraction_way.hpp"
|
||||
#include "extractor/extractor_callbacks.hpp"
|
||||
|
||||
#include "extractor/external_memory_node.hpp"
|
||||
#include "extractor/restriction.hpp"
|
||||
#include "util/for_each_pair.hpp"
|
||||
#include "util/guidance/turn_lanes.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
|
||||
#include "extractor/extractor_callbacks.hpp"
|
||||
#include <boost/numeric/conversion/cast.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
||||
#include <osmium/osm.hpp>
|
||||
@@ -29,6 +32,7 @@ ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containe
|
||||
{
|
||||
// we reserved 0, 1, 2 for the empty case
|
||||
string_map[MapKey("", "")] = 0;
|
||||
lane_map[""] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,6 +149,30 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
||||
// Otherwise fetches the id based on the name and returns it without insertion.
|
||||
|
||||
const constexpr auto MAX_STRING_LENGTH = 255u;
|
||||
const auto requestId = [this,MAX_STRING_LENGTH](const std::string turn_lane_string) {
|
||||
if( turn_lane_string == "" )
|
||||
return INVALID_LANE_STRINGID;
|
||||
const auto &lane_map_iterator = lane_map.find(turn_lane_string);
|
||||
if (lane_map.end() == lane_map_iterator)
|
||||
{
|
||||
LaneStringID turn_lane_id =
|
||||
boost::numeric_cast<LaneStringID>(external_memory.turn_lane_lengths.size());
|
||||
auto turn_lane_length = std::min<unsigned>(MAX_STRING_LENGTH, turn_lane_string.size());
|
||||
std::copy(turn_lane_string.c_str(),
|
||||
turn_lane_string.c_str() + turn_lane_length,
|
||||
std::back_inserter(external_memory.turn_lane_char_data));
|
||||
external_memory.turn_lane_lengths.push_back(turn_lane_length);
|
||||
lane_map.insert(std::make_pair(turn_lane_string, turn_lane_id));
|
||||
return turn_lane_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return lane_map_iterator->second;
|
||||
}
|
||||
};
|
||||
|
||||
const auto turn_lane_id_forward = requestId(parsed_way.turn_lanes_forward);
|
||||
const auto turn_lane_id_backward = requestId(parsed_way.turn_lanes_backward);
|
||||
|
||||
// Get the unique identifier for the street name
|
||||
// Get the unique identifier for the street name and destination
|
||||
@@ -191,7 +219,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
||||
(parsed_way.backward_speed > 0) &&
|
||||
(TRAVEL_MODE_INACCESSIBLE != parsed_way.backward_travel_mode) &&
|
||||
((parsed_way.forward_speed != parsed_way.backward_speed) ||
|
||||
(parsed_way.forward_travel_mode != parsed_way.backward_travel_mode));
|
||||
(parsed_way.forward_travel_mode != parsed_way.backward_travel_mode) ||
|
||||
(turn_lane_id_forward != turn_lane_id_backward));
|
||||
|
||||
external_memory.used_node_id_list.reserve(external_memory.used_node_id_list.size() +
|
||||
input_way.nodes().size());
|
||||
@@ -224,6 +253,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
||||
parsed_way.is_startpoint,
|
||||
parsed_way.backward_travel_mode,
|
||||
false,
|
||||
turn_lane_id_backward,
|
||||
road_classification));
|
||||
});
|
||||
|
||||
@@ -254,6 +284,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
||||
parsed_way.is_startpoint,
|
||||
parsed_way.forward_travel_mode,
|
||||
split_edge,
|
||||
turn_lane_id_forward,
|
||||
road_classification));
|
||||
});
|
||||
if (split_edge)
|
||||
@@ -275,6 +306,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
||||
parsed_way.is_startpoint,
|
||||
parsed_way.backward_travel_mode,
|
||||
true,
|
||||
turn_lane_id_backward,
|
||||
road_classification));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -113,10 +113,8 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
|
||||
// traffic signals in the `traffic_lights` list, which EdgeData
|
||||
// doesn't have access to.
|
||||
const bool has_node_penalty = traffic_lights.find(node_v) != traffic_lights.end();
|
||||
if (has_node_penalty)
|
||||
{
|
||||
if( has_node_penalty )
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get distances before graph is modified
|
||||
const int forward_weight1 = graph.GetEdgeData(forward_e1).distance;
|
||||
@@ -139,6 +137,42 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
|
||||
graph.SetTarget(forward_e1, node_w);
|
||||
graph.SetTarget(reverse_e1, node_u);
|
||||
|
||||
/*
|
||||
* Remember Lane Data for compressed parts. This handles scenarios where lane-data is
|
||||
* only kept up until a traffic light.
|
||||
*
|
||||
* | |
|
||||
* ---------------- |
|
||||
* -^ | |
|
||||
* ----------- |
|
||||
* -v | |
|
||||
* --------------- |
|
||||
* | |
|
||||
*
|
||||
* u ------- v ---- w
|
||||
*
|
||||
* Since the edge is compressable, we can transfer:
|
||||
* "left|right" (uv) and "" (uw) into a string with "left|right" (uw) for the compressed
|
||||
* edge.
|
||||
* Doing so, we might mess up the point from where the lanes are shown. It should be
|
||||
* reasonable, since the announcements have to come early anyhow. So there is a
|
||||
* potential danger in here, but it saves us from adding a lot of additional edges for
|
||||
* turn-lanes. Without this,we would have to treat any turn-lane beginning/ending just
|
||||
* like a barrier.
|
||||
*/
|
||||
const auto selectLaneID = [](const LaneStringID front, const LaneStringID back) {
|
||||
// A lane has tags: u - (front) - v - (back) - w
|
||||
// During contraction, we keep only one of the tags. Usually the one closer to the
|
||||
// intersection is preferred. If its empty, however, we keep the non-empty one
|
||||
if (back == INVALID_LANE_STRINGID)
|
||||
return front;
|
||||
return back;
|
||||
};
|
||||
graph.GetEdgeData(forward_e1).lane_string_id =
|
||||
selectLaneID(graph.GetEdgeData(forward_e1).lane_string_id, fwd_edge_data2.lane_string_id);
|
||||
graph.GetEdgeData(reverse_e1).lane_string_id =
|
||||
selectLaneID(graph.GetEdgeData(reverse_e1).lane_string_id, rev_edge_data2.lane_string_id);
|
||||
|
||||
// remove e2's (if bidir, otherwise only one)
|
||||
graph.DeleteEdge(node_v, forward_e2);
|
||||
graph.DeleteEdge(node_v, reverse_e2);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -21,11 +22,34 @@ std::string toString(const ConnectedRoad &road)
|
||||
result += " angle: ";
|
||||
result += std::to_string(road.turn.angle);
|
||||
result += " instruction: ";
|
||||
result += std::to_string(static_cast<std::int32_t>(road.turn.instruction.type)) + " " +
|
||||
std::to_string(static_cast<std::int32_t>(road.turn.instruction.direction_modifier));
|
||||
result +=
|
||||
std::to_string(static_cast<std::int32_t>(road.turn.instruction.type)) + " " +
|
||||
std::to_string(static_cast<std::int32_t>(road.turn.instruction.direction_modifier)) + " " +
|
||||
std::to_string(static_cast<std::int32_t>(road.turn.instruction.lane_tupel.lanes_in_turn)) +
|
||||
" " + std::to_string(static_cast<std::int32_t>(
|
||||
road.turn.instruction.lane_tupel.first_lane_from_the_right));
|
||||
return result;
|
||||
}
|
||||
|
||||
Intersection::iterator findClosestTurn(Intersection &intersection, const double angle)
|
||||
{
|
||||
return std::min_element(intersection.begin(),
|
||||
intersection.end(),
|
||||
[angle](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
|
||||
return angularDeviation(lhs.turn.angle, angle) <
|
||||
angularDeviation(rhs.turn.angle, angle);
|
||||
});
|
||||
}
|
||||
Intersection::const_iterator findClosestTurn(const Intersection &intersection, const double angle)
|
||||
{
|
||||
return std::min_element(intersection.cbegin(),
|
||||
intersection.cend(),
|
||||
[angle](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
|
||||
return angularDeviation(lhs.turn.angle, angle) <
|
||||
angularDeviation(rhs.turn.angle, angle);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -42,10 +42,8 @@ inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_base
|
||||
MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table),
|
||||
intersection_generator(intersection_generator)
|
||||
const SuffixTable &street_name_suffix_table)
|
||||
: IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -373,7 +373,13 @@ Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabou
|
||||
if (1 == node_based_graph.GetDirectedOutDegree(node_v))
|
||||
{
|
||||
// No turn possible.
|
||||
turn.instruction = TurnInstruction::NO_TURN();
|
||||
if( intersection.size() == 2 )
|
||||
turn.instruction = TurnInstruction::NO_TURN();
|
||||
else
|
||||
{
|
||||
turn.instruction.type = TurnType::Suppressed; //make sure to report intersection
|
||||
turn.instruction.direction_modifier = getTurnDirection(turn.angle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -48,19 +48,15 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
compressed_edge_container,
|
||||
name_table,
|
||||
street_name_suffix_table),
|
||||
motorway_handler(node_based_graph,
|
||||
node_info_list,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
intersection_generator),
|
||||
motorway_handler(node_based_graph, node_info_list, name_table, street_name_suffix_table),
|
||||
turn_handler(node_based_graph, node_info_list, name_table, street_name_suffix_table)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const EdgeID via_eid) const
|
||||
Intersection TurnAnalysis::assignTurnTypes(const NodeID from_nid,
|
||||
const EdgeID via_eid,
|
||||
Intersection intersection) const
|
||||
{
|
||||
auto intersection = intersection_generator(from_nid, via_eid);
|
||||
|
||||
// Roundabouts are a main priority. If there is a roundabout instruction present, we process the
|
||||
// turn as a roundabout
|
||||
if (roundabout_handler.canProcess(from_nid, via_eid, intersection))
|
||||
@@ -81,7 +77,6 @@ std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const E
|
||||
intersection = turn_handler(from_nid, via_eid, std::move(intersection));
|
||||
}
|
||||
}
|
||||
|
||||
// Handle sliproads
|
||||
intersection = handleSliproads(via_eid, std::move(intersection));
|
||||
|
||||
@@ -94,6 +89,12 @@ std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const E
|
||||
});
|
||||
}
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
std::vector<TurnOperation>
|
||||
TurnAnalysis::transformIntersectionIntoTurns(const Intersection &intersection) const
|
||||
{
|
||||
std::vector<TurnOperation> turns;
|
||||
for (auto road : intersection)
|
||||
if (road.entry_allowed)
|
||||
@@ -234,6 +235,8 @@ Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id,
|
||||
return intersection;
|
||||
}
|
||||
|
||||
const IntersectionGenerator &TurnAnalysis::getGenerator() const { return intersection_generator; }
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/turn_discovery.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
namespace lanes
|
||||
{
|
||||
|
||||
bool findPreviousIntersection(const NodeID node_v,
|
||||
const EdgeID via_edge,
|
||||
const Intersection intersection,
|
||||
const TurnAnalysis &turn_analysis,
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
// output parameters
|
||||
NodeID &result_node,
|
||||
EdgeID &result_via_edge,
|
||||
Intersection &result_intersection)
|
||||
{
|
||||
/* We need to find the intersection that is located prior to via_edge.
|
||||
|
||||
*
|
||||
* NODE_U -> PREVIOUS_ID -> NODE_V -> VIA_EDGE -> NODE_W:INTERSECTION
|
||||
* NODE_U? <- STRAIGHTMOST <- NODE_V <- UTURN
|
||||
* NODE_U? -> UTURN == PREVIOUSE_ID? -> NODE_V -> VIA_EDGE
|
||||
*
|
||||
* To do so, we first get the intersection atNODE and find the straightmost turn from that
|
||||
* node. This will result in NODE_X. The uturn in the intersection at NODE_X should be
|
||||
* PREVIOUS_ID. To verify that find, we check the intersection using our PREVIOUS_ID candidate
|
||||
* to check the intersection at NODE for via_edge
|
||||
*/
|
||||
const constexpr double COMBINE_DISTANCE_CUTOFF = 30;
|
||||
|
||||
// we check if via-edge is too short. In this case the previous turn cannot influence the turn
|
||||
// at via_edge and the intersection at NODE_W
|
||||
if (node_based_graph.GetEdgeData(via_edge).distance > COMBINE_DISTANCE_CUTOFF)
|
||||
return false;
|
||||
|
||||
// Node -> Via_Edge -> Intersection[0 == UTURN] -> reverse_of(via_edge) -> Intersection at node
|
||||
// (looking at the reverse direction).
|
||||
const auto node_w = node_based_graph.GetTarget(via_edge);
|
||||
const auto u_turn_at_node_w = intersection[0].turn.eid;
|
||||
const auto node_v_reverse_intersection =
|
||||
turn_analysis.getIntersection(node_w, u_turn_at_node_w);
|
||||
|
||||
// Continue along the straightmost turn. If there is no straight turn, we cannot find a valid
|
||||
// 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)
|
||||
return false;
|
||||
|
||||
const auto node_u = node_based_graph.GetTarget(straightmost_at_v_in_reverse->turn.eid);
|
||||
const auto node_u_reverse_intersection =
|
||||
turn_analysis.getIntersection(node_v, straightmost_at_v_in_reverse->turn.eid);
|
||||
|
||||
// now check that the u-turn at the given intersection connects to via-edge
|
||||
// The u-turn at the now found intersection should, hopefully, represent the previous edge.
|
||||
result_node = node_u;
|
||||
result_via_edge = node_u_reverse_intersection[0].turn.eid;
|
||||
|
||||
// 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)
|
||||
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)
|
||||
return false;
|
||||
|
||||
result_intersection =
|
||||
turn_analysis.assignTurnTypes(node_u, result_via_edge, std::move(result_intersection));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace lanes
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
@@ -78,7 +78,8 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
|
||||
const auto &first_data = node_based_graph.GetEdgeData(intersection[1].turn.eid);
|
||||
const auto &second_data = node_based_graph.GetEdgeData(intersection[2].turn.eid);
|
||||
BOOST_ASSERT(intersection[0].turn.angle < 0.001);
|
||||
const auto isObviousOfTwo = [this](const ConnectedRoad road, const ConnectedRoad other) {
|
||||
const auto isObviousOfTwo = [this, in_data](const ConnectedRoad road,
|
||||
const ConnectedRoad other) {
|
||||
const auto first_class =
|
||||
node_based_graph.GetEdgeData(road.turn.eid).road_classification.road_class;
|
||||
|
||||
@@ -104,7 +105,8 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
|
||||
const bool turn_is_perfectly_straight = angularDeviation(road.turn.angle, STRAIGHT_ANGLE) <
|
||||
std::numeric_limits<double>::epsilon();
|
||||
|
||||
if (turn_is_perfectly_straight)
|
||||
if (turn_is_perfectly_straight && in_data.name_id != EMPTY_NAMEID &&
|
||||
in_data.name_id == node_based_graph.GetEdgeData(road.turn.eid).name_id)
|
||||
return true;
|
||||
|
||||
const bool is_much_narrower_than_other =
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
#include "extractor/guidance/turn_lane_augmentation.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
namespace lanes
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const constexpr char *tag_by_modifier[] = {"reverse",
|
||||
"sharp_right",
|
||||
"right",
|
||||
"slight_right",
|
||||
"through",
|
||||
"slight_left",
|
||||
"left",
|
||||
"sharp_left"};
|
||||
|
||||
std::size_t getNumberOfTurns(const Intersection &intersection)
|
||||
{
|
||||
return std::count_if(intersection.begin(), intersection.end(), [](const ConnectedRoad &road) {
|
||||
return road.entry_allowed;
|
||||
});
|
||||
}
|
||||
|
||||
LaneDataVector augmentMultiple(const std::size_t none_index,
|
||||
const std::size_t connection_count,
|
||||
LaneDataVector lane_data,
|
||||
const Intersection &intersection)
|
||||
{
|
||||
|
||||
// a none-turn is allowing multiple turns. we have to add a lane-data entry for
|
||||
// every possible turn. This should, hopefully, only be the case for single lane
|
||||
// entries?
|
||||
|
||||
// looking at the left side first
|
||||
const auto range = [&]() {
|
||||
if (none_index == 0)
|
||||
{
|
||||
// find first connection_count - lane_data.size() valid turns
|
||||
std::size_t count = 0;
|
||||
for (std::size_t intersection_index = 1; intersection_index < intersection.size();
|
||||
++intersection_index)
|
||||
{
|
||||
|
||||
count += static_cast<int>(intersection[intersection_index].entry_allowed);
|
||||
if (count > connection_count - lane_data.size())
|
||||
return std::make_pair(std::size_t{1}, intersection_index + 1);
|
||||
}
|
||||
}
|
||||
else if (none_index + 1 == lane_data.size())
|
||||
{
|
||||
BOOST_ASSERT(!lane_data.empty());
|
||||
// find last connection-count - last_data.size() valid turns
|
||||
std::size_t count = 0;
|
||||
for (std::size_t intersection_index = intersection.size() - 1; intersection_index > 0;
|
||||
--intersection_index)
|
||||
{
|
||||
count += static_cast<int>(intersection[intersection_index].entry_allowed);
|
||||
if (count > connection_count - lane_data.size())
|
||||
return std::make_pair(intersection_index, intersection.size());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// skip the first #index valid turns, find next connection_count -
|
||||
// lane_data.size() valid ones
|
||||
|
||||
std::size_t begin = 1, count = 0, intersection_index;
|
||||
for (intersection_index = 1; intersection_index < intersection.size();
|
||||
++intersection_index)
|
||||
{
|
||||
count += static_cast<int>(intersection[intersection_index].entry_allowed);
|
||||
// if we reach the amount of
|
||||
if (count >= none_index)
|
||||
{
|
||||
begin = intersection_index + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// reset count to find the number of necessary entries
|
||||
count = 0;
|
||||
for (intersection_index = begin; intersection_index < intersection.size();
|
||||
++intersection_index)
|
||||
{
|
||||
count += static_cast<int>(intersection[intersection_index].entry_allowed);
|
||||
if (count > connection_count - lane_data.size())
|
||||
{
|
||||
return std::make_pair(begin, intersection_index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// this should, theoretically, never be reached
|
||||
util::SimpleLogger().Write(logWARNING) << "Failed lane assignment. Reached bad situation.";
|
||||
return std::make_pair(std::size_t{0}, std::size_t{0});
|
||||
}();
|
||||
for (auto intersection_index = range.first; intersection_index < range.second;
|
||||
++intersection_index)
|
||||
{
|
||||
if (intersection[intersection_index].entry_allowed)
|
||||
{
|
||||
// FIXME this probably can be only a subset of these turns here?
|
||||
lane_data.push_back({tag_by_modifier[intersection[intersection_index]
|
||||
.turn.instruction.direction_modifier],
|
||||
lane_data[none_index].from,
|
||||
lane_data[none_index].to});
|
||||
}
|
||||
}
|
||||
lane_data.erase(lane_data.begin() + none_index);
|
||||
return std::move(lane_data);
|
||||
}
|
||||
|
||||
// Merging none-tag into its neighboring fields
|
||||
// This handles situations like "left | | | right".
|
||||
LaneDataVector mergeNoneTag(const std::size_t none_index, LaneDataVector lane_data)
|
||||
{
|
||||
|
||||
if (none_index == 0 || none_index + 1 == lane_data.size())
|
||||
{
|
||||
if (none_index == 0)
|
||||
{
|
||||
lane_data[1].from = lane_data[0].from;
|
||||
}
|
||||
else
|
||||
{
|
||||
lane_data[none_index - 1].to = lane_data[none_index].to;
|
||||
}
|
||||
lane_data.erase(lane_data.begin() + none_index);
|
||||
}
|
||||
else if (lane_data[none_index].to - lane_data[none_index].from <= 1)
|
||||
{
|
||||
lane_data[none_index - 1].to = lane_data[none_index].from;
|
||||
lane_data[none_index + 1].from = lane_data[none_index].to;
|
||||
|
||||
lane_data.erase(lane_data.begin() + none_index);
|
||||
}
|
||||
return std::move(lane_data);
|
||||
}
|
||||
|
||||
LaneDataVector handleRenamingSituations(const std::size_t none_index,
|
||||
LaneDataVector lane_data,
|
||||
const Intersection &intersection)
|
||||
{
|
||||
bool has_right = false;
|
||||
bool has_through = false;
|
||||
bool has_left = false;
|
||||
for (const auto &road : intersection)
|
||||
{
|
||||
if (!road.entry_allowed)
|
||||
continue;
|
||||
|
||||
const auto modifier = road.turn.instruction.direction_modifier;
|
||||
has_right |= modifier == DirectionModifier::Right;
|
||||
has_right |= modifier == DirectionModifier::SlightRight;
|
||||
has_right |= modifier == DirectionModifier::SharpRight;
|
||||
has_through |= modifier == DirectionModifier::Straight;
|
||||
has_left |= modifier == DirectionModifier::Left;
|
||||
has_left |= modifier == DirectionModifier::SlightLeft;
|
||||
has_left |= modifier == DirectionModifier::SharpLeft;
|
||||
}
|
||||
|
||||
|
||||
// find missing tag and augment neighboring, if possible
|
||||
if (none_index == 0)
|
||||
{
|
||||
if (has_right &&
|
||||
(lane_data.size() == 1 || (lane_data[none_index + 1].tag != "sharp_right" &&
|
||||
lane_data[none_index + 1].tag != "right")))
|
||||
{
|
||||
lane_data[none_index].tag = "right";
|
||||
if (lane_data.size() > 1 && lane_data[none_index + 1].tag == "through")
|
||||
{
|
||||
lane_data[none_index + 1].from = lane_data[none_index].from;
|
||||
// turning right through a possible through lane is not possible
|
||||
lane_data[none_index].to = lane_data[none_index].from;
|
||||
}
|
||||
}
|
||||
else if (has_through &&
|
||||
(lane_data.size() == 1 || lane_data[none_index + 1].tag != "through"))
|
||||
{
|
||||
lane_data[none_index].tag = "through";
|
||||
}
|
||||
}
|
||||
else if (none_index + 1 == lane_data.size())
|
||||
{
|
||||
if (has_left && ((lane_data[none_index - 1].tag != "sharp_left" &&
|
||||
lane_data[none_index - 1].tag != "left")))
|
||||
{
|
||||
lane_data[none_index].tag = "left";
|
||||
if (lane_data[none_index - 1].tag == "through")
|
||||
{
|
||||
lane_data[none_index - 1].to = lane_data[none_index].to;
|
||||
// turning left through a possible through lane is not possible
|
||||
lane_data[none_index].from = lane_data[none_index].to;
|
||||
}
|
||||
}
|
||||
else if (has_through && lane_data[none_index - 1].tag != "through")
|
||||
{
|
||||
lane_data[none_index].tag = "through";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((lane_data[none_index + 1].tag == "left" ||
|
||||
lane_data[none_index + 1].tag == "slight_left" ||
|
||||
lane_data[none_index + 1].tag == "sharp_left") &&
|
||||
(lane_data[none_index - 1].tag == "right" ||
|
||||
lane_data[none_index - 1].tag == "slight_right" ||
|
||||
lane_data[none_index - 1].tag == "sharp_right"))
|
||||
{
|
||||
lane_data[none_index].tag = "through";
|
||||
}
|
||||
}
|
||||
return std::move(lane_data);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/*
|
||||
Lanes can have the tag none. While a nice feature for visibility, it is a terrible feature
|
||||
for parsing. None can be part of neighboring turns, or not. We have to look at both the
|
||||
intersection and the lane data to see what turns we have to augment by the none-lanes
|
||||
*/
|
||||
LaneDataVector handleNoneValueAtSimpleTurn(LaneDataVector lane_data,
|
||||
const Intersection &intersection)
|
||||
{
|
||||
const bool needs_no_processing =
|
||||
(intersection.empty() || lane_data.empty() || !hasTag("none", lane_data));
|
||||
|
||||
if (needs_no_processing)
|
||||
return std::move(lane_data);
|
||||
|
||||
// FIXME all this needs to consider the number of lanes at the target to ensure that we
|
||||
// augment lanes correctly, if the target lane allows for more turns
|
||||
//
|
||||
// -----------------
|
||||
//
|
||||
// ----- ----
|
||||
// -v |
|
||||
// ----- |
|
||||
// | | |
|
||||
//
|
||||
// A situation like this would allow a right turn from the through lane.
|
||||
//
|
||||
// -----------------
|
||||
//
|
||||
// ----- --------
|
||||
// -v |
|
||||
// ----- |
|
||||
// | |
|
||||
//
|
||||
// Here, the number of lanes in the right road would not allow turns from both lanes, but
|
||||
// only from the right one.
|
||||
|
||||
const std::size_t connection_count =
|
||||
getNumberOfTurns(intersection) -
|
||||
((intersection[0].entry_allowed && lane_data.back().tag != "reverse") ? 1 : 0);
|
||||
|
||||
// TODO check for impossible turns to see whether the turn lane is at the correct place
|
||||
const std::size_t none_index = std::distance(lane_data.begin(), findTag("none", lane_data));
|
||||
BOOST_ASSERT(none_index != lane_data.size());
|
||||
// we have to create multiple turns
|
||||
if (connection_count > lane_data.size())
|
||||
{
|
||||
lane_data =
|
||||
augmentMultiple(none_index, connection_count, std::move(lane_data), intersection);
|
||||
}
|
||||
// we have to reduce it, assigning it to neighboring turns
|
||||
else if (connection_count < lane_data.size())
|
||||
{
|
||||
// a prerequisite 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
|
||||
BOOST_ASSERT(connection_count + 1 == lane_data.size());
|
||||
|
||||
lane_data = mergeNoneTag(none_index, std::move(lane_data));
|
||||
}
|
||||
// we have to rename and possibly augment existing ones. The pure count remains the
|
||||
// same.
|
||||
else
|
||||
{
|
||||
lane_data = handleRenamingSituations(none_index, std::move(lane_data), intersection);
|
||||
}
|
||||
// finally make sure we are still sorted
|
||||
std::sort(lane_data.begin(), lane_data.end());
|
||||
return std::move(lane_data);
|
||||
}
|
||||
|
||||
} // namespace lanes
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
@@ -0,0 +1,145 @@
|
||||
#include "extractor/guidance/turn_lane_data.hpp"
|
||||
#include "util/guidance/turn_lanes.hpp"
|
||||
|
||||
#include <boost/numeric/conversion/cast.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
namespace lanes
|
||||
{
|
||||
|
||||
bool TurnLaneData::operator<(const TurnLaneData &other) const
|
||||
{
|
||||
if (from < other.from)
|
||||
return true;
|
||||
if (from > other.from)
|
||||
return false;
|
||||
|
||||
if (to < other.to)
|
||||
return true;
|
||||
if (to > other.to)
|
||||
return false;
|
||||
|
||||
const constexpr char *tag_by_modifier[] = {"sharp_right",
|
||||
"right",
|
||||
"slight_right",
|
||||
"through",
|
||||
"slight_left",
|
||||
"left",
|
||||
"sharp_left",
|
||||
"reverse"};
|
||||
return std::find(tag_by_modifier, tag_by_modifier + 8, this->tag) <
|
||||
std::find(tag_by_modifier, tag_by_modifier + 8, other.tag);
|
||||
}
|
||||
|
||||
LaneDataVector laneDataFromString(std::string turn_lane_string)
|
||||
{
|
||||
typedef std::unordered_map<std::string, std::pair<LaneID, LaneID>> LaneMap;
|
||||
|
||||
// FIXME this is a workaround due to https://github.com/cucumber/cucumber-js/issues/417,
|
||||
// need to switch statements when fixed
|
||||
// const auto num_lanes = std::count(turn_lane_string.begin(), turn_lane_string.end(), '|') + 1;
|
||||
// count the number of lanes
|
||||
const auto num_lanes = [](const std::string &turn_lane_string) {
|
||||
return boost::numeric_cast<LaneID>(
|
||||
std::count(turn_lane_string.begin(), turn_lane_string.end(), '|') + 1 +
|
||||
std::count(turn_lane_string.begin(), turn_lane_string.end(), '&'));
|
||||
}(turn_lane_string);
|
||||
|
||||
const auto getNextTag = [](std::string &string, const char *separators) {
|
||||
auto pos = string.find_last_of(separators);
|
||||
auto result = pos != std::string::npos ? string.substr(pos + 1) : string;
|
||||
|
||||
string.resize(pos == std::string::npos ? 0 : pos);
|
||||
return result;
|
||||
};
|
||||
|
||||
const auto setLaneData = [&](LaneMap &map, std::string lane, const LaneID current_lane) {
|
||||
do
|
||||
{
|
||||
auto identifier = getNextTag(lane, ";");
|
||||
if (identifier.empty())
|
||||
identifier = "none";
|
||||
auto map_iterator = map.find(identifier);
|
||||
if (map_iterator == map.end())
|
||||
map[identifier] = std::make_pair(current_lane, current_lane);
|
||||
else
|
||||
{
|
||||
map_iterator->second.second = current_lane;
|
||||
}
|
||||
} while (!lane.empty());
|
||||
};
|
||||
|
||||
// check whether a given turn lane string resulted in valid lane data
|
||||
const auto hasValidOverlaps = [](const LaneDataVector &lane_data) {
|
||||
// Allow an overlap of at most one. Larger overlaps would result in crossing another turn,
|
||||
// which is invalid
|
||||
for (std::size_t index = 1; index < lane_data.size(); ++index)
|
||||
{
|
||||
if (lane_data[index - 1].to > lane_data[index].from)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
LaneMap lane_map;
|
||||
LaneID lane_nr = 0;
|
||||
LaneDataVector lane_data;
|
||||
if (turn_lane_string.empty())
|
||||
return lane_data;
|
||||
|
||||
do
|
||||
{
|
||||
// FIXME this is a cucumber workaround, since escaping does not work properly in
|
||||
// cucumber.js (see https://github.com/cucumber/cucumber-js/issues/417). Needs to be
|
||||
// changed to "|" only, when the bug is fixed
|
||||
auto lane = getNextTag(turn_lane_string, "|&");
|
||||
setLaneData(lane_map, lane, lane_nr);
|
||||
++lane_nr;
|
||||
} while (lane_nr < num_lanes);
|
||||
|
||||
for (const auto tag : lane_map)
|
||||
{
|
||||
lane_data.push_back({tag.first, tag.second.first, tag.second.second});
|
||||
}
|
||||
|
||||
std::sort(lane_data.begin(), lane_data.end());
|
||||
if (!hasValidOverlaps(lane_data))
|
||||
{
|
||||
lane_data.clear();
|
||||
}
|
||||
|
||||
return lane_data;
|
||||
}
|
||||
|
||||
LaneDataVector::iterator findTag(const std::string &tag, LaneDataVector &data)
|
||||
{
|
||||
return std::find_if(data.begin(), data.end(), [&](const TurnLaneData &lane_data) {
|
||||
return tag == lane_data.tag;
|
||||
});
|
||||
}
|
||||
LaneDataVector::const_iterator findTag(const std::string &tag, const LaneDataVector &data)
|
||||
{
|
||||
return std::find_if(data.cbegin(), data.cend(), [&](const TurnLaneData &lane_data) {
|
||||
return tag == lane_data.tag;
|
||||
});
|
||||
}
|
||||
|
||||
bool hasTag(const std::string &tag, const LaneDataVector &data){
|
||||
return findTag(tag,data) != data.cend();
|
||||
}
|
||||
|
||||
} // namespace lanes
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
@@ -0,0 +1,505 @@
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/turn_discovery.hpp"
|
||||
#include "extractor/guidance/turn_lane_handler.hpp"
|
||||
#include "extractor/guidance/turn_lane_matcher.hpp"
|
||||
#include "extractor/guidance/turn_lane_augmentation.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/numeric/conversion/cast.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
namespace lanes
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
std::size_t getNumberOfTurns(const Intersection &intersection)
|
||||
{
|
||||
return std::count_if(intersection.begin(), intersection.end(), [](const ConnectedRoad &road) {
|
||||
return road.entry_allowed;
|
||||
});
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const util::NameTable &turn_lane_strings,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const TurnAnalysis &turn_analysis)
|
||||
: node_based_graph(node_based_graph), turn_lane_strings(turn_lane_strings),
|
||||
node_info_list(node_info_list), turn_analysis(turn_analysis)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
Turn lanes are given in the form of strings that closely correspond to the direction modifiers
|
||||
we use for our turn types. However, we still cannot simply perform a 1:1 assignment.
|
||||
|
||||
This function parses the turn_lane_strings of a format that describes an intersection as:
|
||||
|
||||
----------
|
||||
A -^
|
||||
----------
|
||||
B -> -v
|
||||
----------
|
||||
C -v
|
||||
----------
|
||||
|
||||
with a string like |left|through;right|right| and performs an 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) const
|
||||
{
|
||||
// initialize to invalid
|
||||
for (auto &road : intersection)
|
||||
road.turn.instruction.lane_tupel = {0, INVALID_LANEID};
|
||||
const auto &data = node_based_graph.GetEdgeData(via_edge);
|
||||
const auto turn_lane_string = data.lane_string_id != INVALID_LANE_STRINGID
|
||||
? turn_lane_strings.GetNameForID(data.lane_string_id)
|
||||
: "";
|
||||
// going straight, due to traffic signals, we can have uncompressed geometry
|
||||
if (intersection.size() == 2 &&
|
||||
((data.lane_string_id != INVALID_LANE_STRINGID &&
|
||||
data.lane_string_id ==
|
||||
node_based_graph.GetEdgeData(intersection[1].turn.eid).lane_string_id) ||
|
||||
angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE))
|
||||
return std::move(intersection);
|
||||
|
||||
auto lane_data = laneDataFromString(turn_lane_string);
|
||||
|
||||
// if we see an invalid conversion, we stop immediately
|
||||
if (!turn_lane_string.empty() && lane_data.empty())
|
||||
return std::move(intersection);
|
||||
|
||||
// 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_string.empty() && lane_data.size() == 1 && lane_data[0].tag == "none")
|
||||
return std::move(intersection);
|
||||
|
||||
const std::size_t possible_entries = getNumberOfTurns(intersection);
|
||||
|
||||
// merge does not justify an instruction
|
||||
const bool has_merge_lane = (hasTag("merge_to_left", lane_data) ||
|
||||
hasTag("merge_to_right", lane_data));
|
||||
|
||||
// Dead end streets that don't have any left-tag. This can happen due to the fallbacks for
|
||||
// broken data/barriers.
|
||||
const bool has_non_usable_u_turn =
|
||||
(intersection[0].entry_allowed && !hasTag("none", lane_data) &&
|
||||
!hasTag("left", lane_data) &&
|
||||
!hasTag("sharp_left", lane_data) &&
|
||||
!hasTag("reverse", lane_data) &&
|
||||
lane_data.size() + 1 == possible_entries);
|
||||
|
||||
if (has_merge_lane || has_non_usable_u_turn)
|
||||
return std::move(intersection);
|
||||
|
||||
if (!lane_data.empty() && canMatchTrivially(intersection, lane_data) &&
|
||||
lane_data.size() !=
|
||||
static_cast<std::size_t>(
|
||||
lane_data.back().tag != "reverse" && intersection[0].entry_allowed ? 1 : 0) +
|
||||
possible_entries &&
|
||||
intersection[0].entry_allowed && !hasTag("none", lane_data))
|
||||
lane_data.push_back({"reverse", 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);
|
||||
}
|
||||
// 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())
|
||||
{
|
||||
if (lane_data.size() >= possible_entries)
|
||||
{
|
||||
lane_data = partitionLaneData(node_based_graph.GetTarget(via_edge),
|
||||
std::move(lane_data),
|
||||
intersection)
|
||||
.first;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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_string.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));
|
||||
}
|
||||
|
||||
return std::move(intersection);
|
||||
}
|
||||
|
||||
// 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) const
|
||||
{
|
||||
NodeID previous_node = SPECIAL_NODEID;
|
||||
Intersection previous_intersection;
|
||||
EdgeID previous_id = SPECIAL_EDGEID;
|
||||
|
||||
// Get the previous lane string. We only accept strings that stem from a not-simple intersection
|
||||
// and are not empty.
|
||||
const auto previous_lane_string = [&]() -> std::string {
|
||||
if (!findPreviousIntersection(at,
|
||||
via_edge,
|
||||
intersection,
|
||||
turn_analysis,
|
||||
node_based_graph,
|
||||
previous_node,
|
||||
previous_id,
|
||||
previous_intersection))
|
||||
return "";
|
||||
|
||||
const auto &previous_data = node_based_graph.GetEdgeData(previous_id);
|
||||
auto previous_string = previous_data.lane_string_id != INVALID_LANE_STRINGID
|
||||
? turn_lane_strings.GetNameForID(previous_data.lane_string_id)
|
||||
: "";
|
||||
if (previous_string.empty())
|
||||
return "";
|
||||
|
||||
previous_intersection = turn_analysis.assignTurnTypes(
|
||||
previous_node, previous_id, std::move(previous_intersection));
|
||||
|
||||
auto previous_lane_data = laneDataFromString(previous_string);
|
||||
|
||||
if (isSimpleIntersection(previous_lane_data, previous_intersection))
|
||||
return "";
|
||||
return previous_string;
|
||||
}();
|
||||
|
||||
// no lane string, no problems
|
||||
if (previous_lane_string.empty())
|
||||
return std::move(intersection);
|
||||
|
||||
auto lane_data = laneDataFromString(previous_lane_string);
|
||||
|
||||
// stop on invalid lane data conversion
|
||||
if (lane_data.empty())
|
||||
return std::move(intersection);
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::move(intersection);
|
||||
}
|
||||
|
||||
|
||||
/* A simple intersection does not depend on the next intersection coming up. This is important
|
||||
* for turn lanes, since traffic signals and/or segregated a intersection can influence the
|
||||
* interpretation of turn-lanes at a given turn.
|
||||
*
|
||||
* Here we check for a simple intersection. A simple intersection has a long enough segment
|
||||
* followin the turn, offers no straight turn, or only non-trivial turn operations.
|
||||
*/
|
||||
bool TurnLaneHandler::isSimpleIntersection(const LaneDataVector &lane_data,
|
||||
const Intersection &intersection) const
|
||||
{
|
||||
if (lane_data.empty())
|
||||
return false;
|
||||
// if we are on a straight road, turn lanes are only reasonable in connection to the next
|
||||
// intersection, or in case of a merge. If not all but one (straight) are merges, we don't
|
||||
// consider the intersection simple
|
||||
if (intersection.size() == 2)
|
||||
{
|
||||
return std::count_if(
|
||||
lane_data.begin(),
|
||||
lane_data.end(),
|
||||
[](const TurnLaneData &data) { return boost::starts_with(data.tag, "merge"); }) +
|
||||
std::size_t{1} >=
|
||||
lane_data.size();
|
||||
}
|
||||
|
||||
// in case an intersection offers far more lane data items than actual turns, some of them
|
||||
// have
|
||||
// to be for another intersection. A single additional item can be for an invalid bus lane.
|
||||
const auto num_turns = [&]() {
|
||||
auto count = getNumberOfTurns(intersection);
|
||||
if (count < lane_data.size() && !intersection[0].entry_allowed &&
|
||||
lane_data.back().tag == "reverse")
|
||||
return count + 1;
|
||||
return count;
|
||||
}();
|
||||
|
||||
// more than two additional lane data entries -> lanes target a different intersection
|
||||
if (num_turns + std::size_t{2} <= lane_data.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// single additional lane data entry is alright, if it is none at the side. This usually
|
||||
// refers to a bus-lane
|
||||
if (num_turns + std::size_t{1} == lane_data.size() && lane_data.front().tag != "none" &&
|
||||
lane_data.back().tag != "none")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// more turns than lane data
|
||||
if (num_turns > lane_data.size() &&
|
||||
lane_data.end() ==
|
||||
std::find_if(lane_data.begin(), lane_data.end(), [](const TurnLaneData &data) {
|
||||
return data.tag == "none";
|
||||
}))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (num_turns > lane_data.size() && intersection[0].entry_allowed &&
|
||||
!( hasTag("reverse", lane_data) ||
|
||||
(lane_data.back().tag != "left" && lane_data.back().tag != "sharp_left")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if we can find a valid 1:1 mapping in a straightforward manner
|
||||
bool all_simple = true;
|
||||
bool has_none = false;
|
||||
std::unordered_set<std::size_t> matched_indices;
|
||||
for (const auto &data : lane_data)
|
||||
{
|
||||
if (data.tag == "none")
|
||||
{
|
||||
has_none = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto best_match = [&]() {
|
||||
if (data.tag != "reverse" || lane_data.size() == 1)
|
||||
return findBestMatch(data.tag, intersection);
|
||||
|
||||
// lane_data.size() > 1
|
||||
if (lane_data.back().tag == "reverse")
|
||||
return findBestMatchForReverse(lane_data[lane_data.size() - 2].tag, intersection);
|
||||
|
||||
BOOST_ASSERT(lane_data.front().tag == "reverse");
|
||||
return findBestMatchForReverse(lane_data[1].tag, intersection);
|
||||
}();
|
||||
std::size_t match_index = std::distance(intersection.begin(), best_match);
|
||||
all_simple &= (matched_indices.count(match_index) == 0);
|
||||
matched_indices.insert(match_index);
|
||||
// in case of u-turns, we might need to activate them first
|
||||
all_simple &= (best_match->entry_allowed || match_index == 0);
|
||||
all_simple &= isValidMatch(data.tag, best_match->turn.instruction);
|
||||
}
|
||||
|
||||
// either all indices are matched, or we have a single none-value
|
||||
if (all_simple && (matched_indices.size() == lane_data.size() ||
|
||||
(matched_indices.size() + 1 == lane_data.size() && has_none)))
|
||||
return true;
|
||||
|
||||
// better save than sorry
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
const NodeID at, LaneDataVector turn_lane_data, const Intersection &intersection) const
|
||||
{
|
||||
BOOST_ASSERT(turn_lane_data.size() >= getNumberOfTurns(intersection));
|
||||
/*
|
||||
* A Segregated intersection can provide turn lanes for turns that are not yet possible.
|
||||
* The straightforward example would be coming up to the following situation:
|
||||
* (1) (2)
|
||||
* | A | | A |
|
||||
* | | | | ^ |
|
||||
* | v | | | |
|
||||
* ------- ----------- ------
|
||||
* B ->-^ B
|
||||
* ------- ----------- ------
|
||||
* B ->-v B
|
||||
* ------- ----------- ------
|
||||
* | A | | A |
|
||||
*
|
||||
* Traveling on road B, we have to pass A at (1) to turn left onto A at (2). The turn
|
||||
* lane itself may only be specified prior to (1) and/or could be repeated between (1)
|
||||
* and (2). To make sure to announce the lane correctly, we need to treat the (in this
|
||||
* 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
|
||||
* two parts, one for the first and one for the second intersection.
|
||||
*/
|
||||
|
||||
// Try and maitch lanes to available turns. For Turns that are not directly matchable, check
|
||||
// whether we can match them at the upcoming intersection.
|
||||
|
||||
const auto straightmost = findClosestTurn(intersection, STRAIGHT_ANGLE);
|
||||
|
||||
BOOST_ASSERT(straightmost < intersection.cend());
|
||||
|
||||
// we need to be able to enter the straightmost turn
|
||||
if (!straightmost->entry_allowed)
|
||||
return {turn_lane_data, {}};
|
||||
|
||||
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
|
||||
auto next_intersection = turn_analysis.getIntersection(at, straightmost->turn.eid);
|
||||
next_intersection =
|
||||
turn_analysis.assignTurnTypes(at, straightmost->turn.eid, std::move(next_intersection));
|
||||
|
||||
// check where we can match turn lanes
|
||||
std::size_t straightmost_tag_index = turn_lane_data.size();
|
||||
for (std::size_t lane = 0; lane < turn_lane_data.size(); ++lane)
|
||||
{
|
||||
if (turn_lane_data[lane].tag == "none" || turn_lane_data[lane].tag == "reverse")
|
||||
continue;
|
||||
|
||||
const auto best_match = findBestMatch(turn_lane_data[lane].tag, intersection);
|
||||
if (isValidMatch(turn_lane_data[lane].tag, best_match->turn.instruction))
|
||||
{
|
||||
matched_at_first[lane] = true;
|
||||
|
||||
if (straightmost == best_match)
|
||||
straightmost_tag_index = lane;
|
||||
}
|
||||
|
||||
const auto best_match_at_next_intersection =
|
||||
findBestMatch(turn_lane_data[lane].tag, next_intersection);
|
||||
if (isValidMatch(turn_lane_data[lane].tag,
|
||||
best_match_at_next_intersection->turn.instruction))
|
||||
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]))
|
||||
return {turn_lane_data, {}};
|
||||
}
|
||||
|
||||
std::size_t none_index = std::distance(turn_lane_data.begin(),findTag("none", turn_lane_data));
|
||||
|
||||
// if the turn lanes are pull forward, we might have to add an additional straight tag
|
||||
// did we find something that matches against the straightmost road?
|
||||
if (straightmost_tag_index == turn_lane_data.size())
|
||||
{
|
||||
if (none_index != turn_lane_data.size())
|
||||
straightmost_tag_index = none_index;
|
||||
}
|
||||
|
||||
// TODO handle reverse
|
||||
|
||||
// handle none values
|
||||
if (none_index != turn_lane_data.size())
|
||||
{
|
||||
if (static_cast<std::size_t>(
|
||||
std::count(matched_at_first.begin(), matched_at_first.end(), true)) <=
|
||||
getNumberOfTurns(intersection))
|
||||
matched_at_first[none_index] = true;
|
||||
|
||||
if (static_cast<std::size_t>(
|
||||
std::count(matched_at_second.begin(), matched_at_second.end(), true)) <=
|
||||
getNumberOfTurns(next_intersection))
|
||||
matched_at_second[none_index] = true;
|
||||
}
|
||||
|
||||
const auto augmentEntry = [&](TurnLaneData &data) {
|
||||
for (std::size_t lane = 0; lane < turn_lane_data.size(); ++lane)
|
||||
if (matched_at_second[lane])
|
||||
{
|
||||
data.from = std::min(turn_lane_data[lane].from, data.from);
|
||||
data.to = std::max(turn_lane_data[lane].to, data.to);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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]);
|
||||
|
||||
// augment straightmost at this intersection to match all turns that happen at the next
|
||||
if (lane == straightmost_tag_index)
|
||||
{
|
||||
augmentEntry(turn_lane_data[straightmost_tag_index]);
|
||||
}
|
||||
|
||||
if (matched_at_first[lane])
|
||||
first.push_back(turn_lane_data[lane]);
|
||||
}
|
||||
|
||||
if (straightmost_tag_index == turn_lane_data.size() &&
|
||||
static_cast<std::size_t>(
|
||||
std::count(matched_at_second.begin(), matched_at_second.end(), true)) ==
|
||||
getNumberOfTurns(next_intersection))
|
||||
{
|
||||
TurnLaneData data = {"through", 255, 0};
|
||||
augmentEntry(data);
|
||||
first.push_back(data);
|
||||
std::sort(first.begin(), first.end());
|
||||
}
|
||||
|
||||
// TODO augment straightmost turn
|
||||
return {std::move(first), std::move(second)};
|
||||
}
|
||||
|
||||
Intersection TurnLaneHandler::simpleMatchTuplesToTurns(Intersection intersection,
|
||||
const LaneDataVector &lane_data) const
|
||||
{
|
||||
if (lane_data.empty() || !canMatchTrivially(intersection, lane_data))
|
||||
return std::move(intersection);
|
||||
|
||||
BOOST_ASSERT(!hasTag("none", lane_data));
|
||||
BOOST_ASSERT(std::count_if(lane_data.begin(), lane_data.end(), [](const TurnLaneData &data) {
|
||||
return boost::starts_with(data.tag, "merge");
|
||||
}) == 0);
|
||||
|
||||
return triviallyMatchLanesToTurns(std::move(intersection), lane_data, node_based_graph);
|
||||
}
|
||||
|
||||
} // namespace lanes
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
@@ -0,0 +1,221 @@
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
#include "extractor/guidance/turn_lane_matcher.hpp"
|
||||
#include "util/guidance/toolkit.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
namespace lanes
|
||||
{
|
||||
|
||||
// Translate Turn Tags into a Matching Direction Modifier
|
||||
DirectionModifier::Enum getMatchingModifier(const std::string &tag)
|
||||
{
|
||||
const constexpr char *tag_by_modifier[] = {"reverse",
|
||||
"sharp_right",
|
||||
"right",
|
||||
"slight_right",
|
||||
"through",
|
||||
"slight_left",
|
||||
"left",
|
||||
"sharp_left",
|
||||
"merge_to_left",
|
||||
"merge_to_right"};
|
||||
const auto index =
|
||||
std::distance(tag_by_modifier, std::find(tag_by_modifier, tag_by_modifier + 10, tag));
|
||||
|
||||
BOOST_ASSERT(index <= 10);
|
||||
|
||||
const constexpr DirectionModifier::Enum modifiers[11] = {
|
||||
DirectionModifier::UTurn,
|
||||
DirectionModifier::SharpRight,
|
||||
DirectionModifier::Right,
|
||||
DirectionModifier::SlightRight,
|
||||
DirectionModifier::Straight,
|
||||
DirectionModifier::SlightLeft,
|
||||
DirectionModifier::Left,
|
||||
DirectionModifier::SharpLeft,
|
||||
DirectionModifier::Straight,
|
||||
DirectionModifier::Straight,
|
||||
DirectionModifier::UTurn}; // fallback for invalid tags
|
||||
|
||||
return modifiers[index];
|
||||
}
|
||||
|
||||
// check whether a match of a given tag and a turn instruction can be seen as valid
|
||||
bool isValidMatch(const std::string &tag, const TurnInstruction instruction)
|
||||
{
|
||||
using util::guidance::hasLeftModifier;
|
||||
using util::guidance::hasRightModifier;
|
||||
const auto isMirroredModifier = [](const TurnInstruction instruction) {
|
||||
return instruction.type == TurnType::Merge;
|
||||
};
|
||||
|
||||
if (tag == "reverse")
|
||||
{
|
||||
return hasLeftModifier(instruction) ||
|
||||
instruction.direction_modifier == DirectionModifier::UTurn;
|
||||
}
|
||||
else if (tag == "sharp_right" || tag == "right" || tag == "slight_right")
|
||||
{
|
||||
if (isMirroredModifier(instruction))
|
||||
return hasLeftModifier(instruction);
|
||||
else
|
||||
// needs to be adjusted for left side driving
|
||||
return leavesRoundabout(instruction) || hasRightModifier(instruction);
|
||||
}
|
||||
else if (tag == "through")
|
||||
{
|
||||
return instruction.direction_modifier == DirectionModifier::Straight ||
|
||||
instruction.type == TurnType::Suppressed || instruction.type == TurnType::NewName ||
|
||||
instruction.type == TurnType::StayOnRoundabout || entersRoundabout(instruction) ||
|
||||
(instruction.type ==
|
||||
TurnType::Fork && // Forks can be experienced, even for straight segments
|
||||
(instruction.direction_modifier == DirectionModifier::SlightLeft ||
|
||||
instruction.direction_modifier == DirectionModifier::SlightRight)) ||
|
||||
(instruction.type ==
|
||||
TurnType::Continue && // Forks can be experienced, even for straight segments
|
||||
(instruction.direction_modifier == DirectionModifier::SlightLeft ||
|
||||
instruction.direction_modifier == DirectionModifier::SlightRight)) ||
|
||||
instruction.type == TurnType::UseLane;
|
||||
}
|
||||
else if (tag == "slight_left" || tag == "left" || tag == "sharp_left")
|
||||
{
|
||||
if (isMirroredModifier(instruction))
|
||||
return hasRightModifier(instruction);
|
||||
else
|
||||
{
|
||||
// Needs to be fixed for left side driving
|
||||
return (instruction.type == TurnType::StayOnRoundabout) || hasLeftModifier(instruction);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
typename Intersection::const_iterator findBestMatch(const std::string &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) {
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
|
||||
typename Intersection::const_iterator findBestMatchForReverse(const std::string &leftmost_tag,
|
||||
const Intersection &intersection)
|
||||
{
|
||||
const auto leftmost_itr = findBestMatch(leftmost_tag, intersection);
|
||||
if (leftmost_itr + 1 == intersection.cend())
|
||||
return intersection.begin();
|
||||
|
||||
const constexpr double idealized_turn_angles[] = {0, 35, 90, 135, 180, 225, 270, 315};
|
||||
const std::string tag = "reverse";
|
||||
const auto idealized_angle = idealized_turn_angles[getMatchingModifier(tag)];
|
||||
return std::min_element(
|
||||
intersection.begin() + std::distance(intersection.begin(), leftmost_itr),
|
||||
intersection.end(),
|
||||
[idealized_angle, &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);
|
||||
});
|
||||
}
|
||||
|
||||
bool canMatchTrivially(const Intersection &intersection, const LaneDataVector &lane_data)
|
||||
{
|
||||
std::size_t road_index = 1, lane = 0;
|
||||
for (; road_index < intersection.size() && lane < lane_data.size(); ++road_index)
|
||||
{
|
||||
if (intersection[road_index].entry_allowed)
|
||||
{
|
||||
BOOST_ASSERT(lane_data[lane].from != INVALID_LANEID);
|
||||
if (!isValidMatch(lane_data[lane].tag, intersection[road_index].turn.instruction))
|
||||
return false;
|
||||
|
||||
if (findBestMatch(lane_data[lane].tag, intersection) !=
|
||||
intersection.begin() + road_index)
|
||||
return false;
|
||||
++lane;
|
||||
}
|
||||
}
|
||||
return lane == lane_data.size() ||
|
||||
(lane + 1 == lane_data.size() && lane_data.back().tag == "reverse");
|
||||
}
|
||||
|
||||
Intersection triviallyMatchLanesToTurns(Intersection intersection,
|
||||
const LaneDataVector &lane_data,
|
||||
const util::NodeBasedDynamicGraph &node_based_graph)
|
||||
{
|
||||
std::size_t road_index = 1, lane = 0;
|
||||
for (; road_index < intersection.size() && lane < lane_data.size(); ++road_index)
|
||||
{
|
||||
if (intersection[road_index].entry_allowed)
|
||||
{
|
||||
BOOST_ASSERT(lane_data[lane].from != INVALID_LANEID);
|
||||
BOOST_ASSERT(
|
||||
isValidMatch(lane_data[lane].tag, intersection[road_index].turn.instruction));
|
||||
BOOST_ASSERT(findBestMatch(lane_data[lane].tag, intersection) ==
|
||||
intersection.begin() + road_index);
|
||||
|
||||
if (TurnType::Suppressed == intersection[road_index].turn.instruction.type)
|
||||
intersection[road_index].turn.instruction.type = TurnType::UseLane;
|
||||
|
||||
intersection[road_index].turn.instruction.lane_tupel = {
|
||||
LaneID(lane_data[lane].to - lane_data[lane].from + 1), lane_data[lane].from};
|
||||
++lane;
|
||||
}
|
||||
}
|
||||
|
||||
// handle reverse tag, if present
|
||||
if (lane + 1 == lane_data.size() && lane_data.back().tag == "reverse")
|
||||
{
|
||||
std::size_t u_turn = 0;
|
||||
if (node_based_graph.GetEdgeData(intersection[0].turn.eid).reversed)
|
||||
{
|
||||
if (intersection.back().entry_allowed ||
|
||||
intersection.back().turn.instruction.direction_modifier !=
|
||||
DirectionModifier::SharpLeft)
|
||||
{
|
||||
// cannot match u-turn in a valid way
|
||||
return std::move(intersection);
|
||||
}
|
||||
u_turn = intersection.size() - 1;
|
||||
}
|
||||
intersection[u_turn].entry_allowed = true;
|
||||
intersection[u_turn].turn.instruction.type = TurnType::Turn;
|
||||
intersection[u_turn].turn.instruction.direction_modifier = DirectionModifier::UTurn;
|
||||
intersection[u_turn].turn.instruction.lane_tupel = {
|
||||
LaneID(lane_data.back().to - lane_data.back().from + 1), lane_data.back().from};
|
||||
}
|
||||
return std::move(intersection);
|
||||
}
|
||||
|
||||
} // namespace lane_matching
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
@@ -77,6 +77,7 @@ void ScriptingEnvironment::InitContext(ScriptingEnvironment::Context &context)
|
||||
luabind::module(context.state)
|
||||
[luabind::def("durationIsValid", durationIsValid),
|
||||
luabind::def("parseDuration", parseDuration),
|
||||
luabind::def("trimLaneString", trimLaneString),
|
||||
luabind::class_<TravelMode>("mode").enum_(
|
||||
"enums")[luabind::value("inaccessible", TRAVEL_MODE_INACCESSIBLE),
|
||||
luabind::value("driving", TRAVEL_MODE_DRIVING),
|
||||
@@ -141,6 +142,8 @@ void ScriptingEnvironment::InitContext(ScriptingEnvironment::Context &context)
|
||||
.def_readwrite("is_access_restricted", &ExtractionWay::is_access_restricted)
|
||||
.def_readwrite("is_startpoint", &ExtractionWay::is_startpoint)
|
||||
.def_readwrite("duration", &ExtractionWay::duration)
|
||||
.def_readwrite("turn_lanes_forward", &ExtractionWay::turn_lanes_forward)
|
||||
.def_readwrite("turn_lanes_backward", &ExtractionWay::turn_lanes_backward)
|
||||
.property(
|
||||
"forward_mode", &ExtractionWay::get_forward_mode, &ExtractionWay::set_forward_mode)
|
||||
.property("backward_mode",
|
||||
|
||||
Reference in New Issue
Block a user