2016-03-01 16:30:31 -05:00
|
|
|
#ifndef OSRM_GUIDANCE_TOOLKIT_HPP_
|
|
|
|
#define OSRM_GUIDANCE_TOOLKIT_HPP_
|
2016-02-24 04:29:23 -05:00
|
|
|
|
2016-02-24 08:03:51 -05:00
|
|
|
#include "util/bearing.hpp"
|
2016-02-24 04:29:23 -05:00
|
|
|
#include "util/coordinate.hpp"
|
|
|
|
#include "util/coordinate_calculation.hpp"
|
2016-04-18 07:41:19 -04:00
|
|
|
#include "util/guidance/toolkit.hpp"
|
2016-02-24 04:29:23 -05:00
|
|
|
|
|
|
|
#include "extractor/compressed_edge_container.hpp"
|
|
|
|
#include "extractor/query_node.hpp"
|
2016-04-22 05:31:46 -04:00
|
|
|
#include "extractor/suffix_table.hpp"
|
2016-02-24 04:29:23 -05:00
|
|
|
|
2016-03-01 16:30:31 -05:00
|
|
|
#include "extractor/guidance/classification_data.hpp"
|
2016-03-23 08:04:23 -04:00
|
|
|
#include "extractor/guidance/discrete_angle.hpp"
|
2016-04-11 06:51:06 -04:00
|
|
|
#include "extractor/guidance/intersection.hpp"
|
2016-03-01 16:30:31 -05:00
|
|
|
#include "extractor/guidance/turn_instruction.hpp"
|
2016-02-24 04:29:23 -05:00
|
|
|
|
2016-03-16 10:47:33 -04:00
|
|
|
#include <algorithm>
|
2016-02-24 04:29:23 -05:00
|
|
|
#include <cmath>
|
2016-03-03 11:24:59 -05:00
|
|
|
#include <cstdint>
|
2016-03-23 08:04:23 -04:00
|
|
|
#include <map>
|
2016-03-16 10:47:33 -04:00
|
|
|
#include <string>
|
2016-04-28 04:54:14 -04:00
|
|
|
#include <utility>
|
2016-02-24 04:29:23 -05:00
|
|
|
|
2016-04-22 05:31:46 -04:00
|
|
|
#include <boost/algorithm/string.hpp>
|
2016-04-21 07:59:03 -04:00
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
|
|
|
2016-02-24 04:29:23 -05:00
|
|
|
namespace osrm
|
|
|
|
{
|
2016-03-01 16:30:31 -05:00
|
|
|
namespace extractor
|
2016-02-24 04:29:23 -05:00
|
|
|
{
|
|
|
|
namespace guidance
|
|
|
|
{
|
|
|
|
|
2016-04-18 07:41:19 -04:00
|
|
|
using util::guidance::angularDeviation;
|
|
|
|
|
2016-02-24 04:29:23 -05:00
|
|
|
namespace detail
|
|
|
|
{
|
|
|
|
const constexpr double DESIRED_SEGMENT_LENGTH = 10.0;
|
|
|
|
const constexpr bool shiftable_ccw[] = {false, true, true, false, false, true, true, false};
|
|
|
|
const constexpr bool shiftable_cw[] = {false, false, true, true, false, false, true, true};
|
2016-03-03 11:24:59 -05:00
|
|
|
const constexpr std::uint8_t modifier_bounds[detail::num_direction_modifiers] = {
|
|
|
|
0, 36, 93, 121, 136, 163, 220, 255};
|
2016-04-26 07:27:40 -04:00
|
|
|
|
|
|
|
const constexpr double discrete_angle_step_size = 360. / 24;
|
2016-02-24 04:29:23 -05:00
|
|
|
|
|
|
|
template <typename IteratorType>
|
|
|
|
util::Coordinate
|
|
|
|
getCoordinateFromCompressedRange(util::Coordinate current_coordinate,
|
2016-03-03 11:24:59 -05:00
|
|
|
const IteratorType compressed_geometry_begin,
|
2016-02-24 04:29:23 -05:00
|
|
|
const IteratorType compressed_geometry_end,
|
|
|
|
const util::Coordinate final_coordinate,
|
|
|
|
const std::vector<extractor::QueryNode> &query_nodes)
|
|
|
|
{
|
2016-03-23 08:04:23 -04:00
|
|
|
const auto extractCoordinateFromNode =
|
|
|
|
[](const extractor::QueryNode &node) -> util::Coordinate {
|
2016-02-24 04:29:23 -05:00
|
|
|
return {node.lon, node.lat};
|
|
|
|
};
|
|
|
|
double distance_to_current_coordinate = 0;
|
|
|
|
double distance_to_next_coordinate = 0;
|
|
|
|
|
|
|
|
// get the length that is missing from the current segment to reach DESIRED_SEGMENT_LENGTH
|
2016-03-23 08:04:23 -04:00
|
|
|
const auto getFactor = [](const double first_distance, const double second_distance) {
|
2016-02-24 04:29:23 -05:00
|
|
|
BOOST_ASSERT(first_distance < detail::DESIRED_SEGMENT_LENGTH);
|
|
|
|
double segment_length = second_distance - first_distance;
|
|
|
|
BOOST_ASSERT(segment_length > 0);
|
|
|
|
BOOST_ASSERT(second_distance >= detail::DESIRED_SEGMENT_LENGTH);
|
|
|
|
double missing_distance = detail::DESIRED_SEGMENT_LENGTH - first_distance;
|
2016-04-10 20:23:22 -04:00
|
|
|
return std::max(0., std::min(missing_distance / segment_length, 1.0));
|
2016-02-24 04:29:23 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
for (auto compressed_geometry_itr = compressed_geometry_begin;
|
|
|
|
compressed_geometry_itr != compressed_geometry_end; ++compressed_geometry_itr)
|
|
|
|
{
|
|
|
|
const auto next_coordinate =
|
|
|
|
extractCoordinateFromNode(query_nodes[compressed_geometry_itr->node_id]);
|
|
|
|
distance_to_next_coordinate =
|
|
|
|
distance_to_current_coordinate +
|
|
|
|
util::coordinate_calculation::haversineDistance(current_coordinate, next_coordinate);
|
|
|
|
|
|
|
|
// reached point where coordinates switch between
|
|
|
|
if (distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH)
|
|
|
|
return util::coordinate_calculation::interpolateLinear(
|
|
|
|
getFactor(distance_to_current_coordinate, distance_to_next_coordinate),
|
|
|
|
current_coordinate, next_coordinate);
|
|
|
|
|
|
|
|
// prepare for next iteration
|
|
|
|
current_coordinate = next_coordinate;
|
|
|
|
distance_to_current_coordinate = distance_to_next_coordinate;
|
|
|
|
}
|
|
|
|
|
|
|
|
distance_to_next_coordinate =
|
|
|
|
distance_to_current_coordinate +
|
|
|
|
util::coordinate_calculation::haversineDistance(current_coordinate, final_coordinate);
|
|
|
|
|
|
|
|
// reached point where coordinates switch between
|
2016-04-10 20:23:22 -04:00
|
|
|
if (distance_to_current_coordinate < detail::DESIRED_SEGMENT_LENGTH &&
|
|
|
|
distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH)
|
2016-02-24 04:29:23 -05:00
|
|
|
return util::coordinate_calculation::interpolateLinear(
|
|
|
|
getFactor(distance_to_current_coordinate, distance_to_next_coordinate),
|
|
|
|
current_coordinate, final_coordinate);
|
|
|
|
else
|
|
|
|
return final_coordinate;
|
|
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
|
2016-04-29 03:48:13 -04:00
|
|
|
// Finds a (potentially interpolated) coordinate that is DESIRED_SEGMENT_LENGTH away
|
2016-02-24 04:29:23 -05:00
|
|
|
// from the start of an edge
|
|
|
|
inline util::Coordinate
|
|
|
|
getRepresentativeCoordinate(const NodeID from_node,
|
|
|
|
const NodeID to_node,
|
|
|
|
const EdgeID via_edge_id,
|
|
|
|
const bool traverse_in_reverse,
|
|
|
|
const extractor::CompressedEdgeContainer &compressed_geometries,
|
|
|
|
const std::vector<extractor::QueryNode> &query_nodes)
|
|
|
|
{
|
2016-03-23 08:04:23 -04:00
|
|
|
const auto extractCoordinateFromNode =
|
|
|
|
[](const extractor::QueryNode &node) -> util::Coordinate {
|
2016-02-24 04:29:23 -05:00
|
|
|
return {node.lon, node.lat};
|
|
|
|
};
|
|
|
|
|
|
|
|
// Uncompressed roads are simple, return the coordinate at the end
|
|
|
|
if (!compressed_geometries.HasEntryForID(via_edge_id))
|
|
|
|
{
|
|
|
|
return extractCoordinateFromNode(traverse_in_reverse ? query_nodes[from_node]
|
|
|
|
: query_nodes[to_node]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const auto &geometry = compressed_geometries.GetBucketReference(via_edge_id);
|
|
|
|
|
|
|
|
const auto base_node_id = (traverse_in_reverse) ? to_node : from_node;
|
|
|
|
const auto base_coordinate = extractCoordinateFromNode(query_nodes[base_node_id]);
|
|
|
|
|
|
|
|
const auto final_node = (traverse_in_reverse) ? from_node : to_node;
|
|
|
|
const auto final_coordinate = extractCoordinateFromNode(query_nodes[final_node]);
|
|
|
|
|
|
|
|
if (traverse_in_reverse)
|
|
|
|
return detail::getCoordinateFromCompressedRange(
|
|
|
|
base_coordinate, geometry.rbegin(), geometry.rend(), final_coordinate, query_nodes);
|
|
|
|
else
|
|
|
|
return detail::getCoordinateFromCompressedRange(
|
|
|
|
base_coordinate, geometry.begin(), geometry.end(), final_coordinate, query_nodes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// shift an instruction around the degree circle in CCW order
|
2016-05-19 17:26:07 -04:00
|
|
|
inline DirectionModifier::Enum forcedShiftCCW(const DirectionModifier::Enum modifier)
|
2016-02-24 04:29:23 -05:00
|
|
|
{
|
2016-05-19 17:26:07 -04:00
|
|
|
return static_cast<DirectionModifier::Enum>((static_cast<std::uint32_t>(modifier) + 1) %
|
|
|
|
detail::num_direction_modifiers);
|
2016-02-24 04:29:23 -05:00
|
|
|
}
|
|
|
|
|
2016-05-19 17:26:07 -04:00
|
|
|
inline DirectionModifier::Enum shiftCCW(const DirectionModifier::Enum modifier)
|
2016-02-24 04:29:23 -05:00
|
|
|
{
|
|
|
|
if (detail::shiftable_ccw[static_cast<int>(modifier)])
|
|
|
|
return forcedShiftCCW(modifier);
|
|
|
|
else
|
|
|
|
return modifier;
|
|
|
|
}
|
|
|
|
|
|
|
|
// shift an instruction around the degree circle in CW order
|
2016-05-19 17:26:07 -04:00
|
|
|
inline DirectionModifier::Enum forcedShiftCW(const DirectionModifier::Enum modifier)
|
2016-02-24 04:29:23 -05:00
|
|
|
{
|
2016-05-19 17:26:07 -04:00
|
|
|
return static_cast<DirectionModifier::Enum>(
|
2016-03-03 11:24:59 -05:00
|
|
|
(static_cast<std::uint32_t>(modifier) + detail::num_direction_modifiers - 1) %
|
2016-02-24 04:29:23 -05:00
|
|
|
detail::num_direction_modifiers);
|
|
|
|
}
|
|
|
|
|
2016-05-19 17:26:07 -04:00
|
|
|
inline DirectionModifier::Enum shiftCW(const DirectionModifier::Enum modifier)
|
2016-02-24 04:29:23 -05:00
|
|
|
{
|
|
|
|
if (detail::shiftable_cw[static_cast<int>(modifier)])
|
|
|
|
return forcedShiftCW(modifier);
|
|
|
|
else
|
|
|
|
return modifier;
|
|
|
|
}
|
|
|
|
|
2016-05-19 17:26:07 -04:00
|
|
|
inline bool isBasic(const TurnType::Enum type)
|
2016-02-24 04:29:23 -05:00
|
|
|
{
|
2016-03-03 09:36:03 -05:00
|
|
|
return type == TurnType::Turn || type == TurnType::EndOfRoad;
|
2016-02-24 04:29:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool isUturn(const TurnInstruction instruction)
|
|
|
|
{
|
2016-03-03 09:36:03 -05:00
|
|
|
return isBasic(instruction.type) && instruction.direction_modifier == DirectionModifier::UTurn;
|
2016-02-24 04:29:23 -05:00
|
|
|
}
|
|
|
|
|
2016-03-03 09:36:03 -05:00
|
|
|
inline bool resolve(TurnInstruction &to_resolve, const TurnInstruction neighbor, bool resolve_cw)
|
2016-02-24 04:29:23 -05:00
|
|
|
{
|
|
|
|
const auto shifted_turn = resolve_cw ? shiftCW(to_resolve.direction_modifier)
|
|
|
|
: shiftCCW(to_resolve.direction_modifier);
|
|
|
|
if (shifted_turn == neighbor.direction_modifier ||
|
|
|
|
shifted_turn == to_resolve.direction_modifier)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
to_resolve.direction_modifier = shifted_turn;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool resolveTransitive(TurnInstruction &first,
|
|
|
|
TurnInstruction &second,
|
|
|
|
const TurnInstruction third,
|
|
|
|
bool resolve_cw)
|
|
|
|
{
|
|
|
|
if (resolve(second, third, resolve_cw))
|
|
|
|
{
|
|
|
|
first.direction_modifier =
|
|
|
|
resolve_cw ? shiftCW(first.direction_modifier) : shiftCCW(first.direction_modifier);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool isSlightTurn(const TurnInstruction turn)
|
|
|
|
{
|
|
|
|
return (isBasic(turn.type) || turn.type == TurnType::NoTurn) &&
|
|
|
|
(turn.direction_modifier == DirectionModifier::Straight ||
|
|
|
|
turn.direction_modifier == DirectionModifier::SlightRight ||
|
|
|
|
turn.direction_modifier == DirectionModifier::SlightLeft);
|
|
|
|
}
|
|
|
|
|
2016-05-19 17:26:07 -04:00
|
|
|
inline bool isSlightModifier(const DirectionModifier::Enum direction_modifier)
|
2016-02-24 04:29:23 -05:00
|
|
|
{
|
|
|
|
return (direction_modifier == DirectionModifier::Straight ||
|
|
|
|
direction_modifier == DirectionModifier::SlightRight ||
|
|
|
|
direction_modifier == DirectionModifier::SlightLeft);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool isSharpTurn(const TurnInstruction turn)
|
|
|
|
{
|
2016-03-03 09:36:03 -05:00
|
|
|
return isBasic(turn.type) && (turn.direction_modifier == DirectionModifier::SharpLeft ||
|
|
|
|
turn.direction_modifier == DirectionModifier::SharpRight);
|
2016-02-24 04:29:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool isStraight(const TurnInstruction turn)
|
|
|
|
{
|
|
|
|
return (isBasic(turn.type) || turn.type == TurnType::NoTurn) &&
|
|
|
|
turn.direction_modifier == DirectionModifier::Straight;
|
|
|
|
}
|
|
|
|
|
2016-03-03 09:36:03 -05:00
|
|
|
inline bool isConflict(const TurnInstruction first, const TurnInstruction second)
|
2016-02-24 04:29:23 -05:00
|
|
|
{
|
|
|
|
return (first.type == second.type && first.direction_modifier == second.direction_modifier) ||
|
|
|
|
(isStraight(first) && isStraight(second));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline DiscreteAngle discretizeAngle(const double angle)
|
|
|
|
{
|
|
|
|
BOOST_ASSERT(angle >= 0. && angle <= 360.);
|
2016-05-16 07:37:00 -04:00
|
|
|
return DiscreteAngle(static_cast<std::uint8_t>(
|
|
|
|
(angle + 0.5 * detail::discrete_angle_step_size) / detail::discrete_angle_step_size));
|
2016-02-24 04:29:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline double angleFromDiscreteAngle(const DiscreteAngle angle)
|
|
|
|
{
|
2016-05-16 07:37:00 -04:00
|
|
|
return static_cast<double>(angle) * detail::discrete_angle_step_size +
|
|
|
|
0.5 * detail::discrete_angle_step_size;
|
2016-02-24 04:29:23 -05:00
|
|
|
}
|
|
|
|
|
2016-05-19 17:26:07 -04:00
|
|
|
inline double getAngularPenalty(const double angle, DirectionModifier::Enum modifier)
|
2016-02-24 04:29:23 -05:00
|
|
|
{
|
2016-03-07 08:52:26 -05:00
|
|
|
// these are not aligned with getTurnDirection but represent an ideal center
|
2016-02-24 04:29:23 -05:00
|
|
|
const double center[] = {0, 45, 90, 135, 180, 225, 270, 315};
|
2016-03-07 08:52:26 -05:00
|
|
|
return angularDeviation(center[static_cast<int>(modifier)], angle);
|
2016-02-24 04:29:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inline double getTurnConfidence(const double angle, TurnInstruction instruction)
|
|
|
|
{
|
|
|
|
|
|
|
|
// special handling of U-Turns and Roundabout
|
2016-03-03 09:36:03 -05:00
|
|
|
if (!isBasic(instruction.type) || instruction.direction_modifier == DirectionModifier::UTurn)
|
2016-02-24 04:29:23 -05:00
|
|
|
return 1.0;
|
|
|
|
|
2016-03-07 08:52:26 -05:00
|
|
|
const double deviations[] = {0, 45, 50, 30, 20, 30, 50, 45};
|
|
|
|
const double difference = getAngularPenalty(angle, instruction.direction_modifier);
|
2016-02-24 04:29:23 -05:00
|
|
|
const double max_deviation = deviations[static_cast<int>(instruction.direction_modifier)];
|
|
|
|
return 1.0 - (difference / max_deviation) * (difference / max_deviation);
|
|
|
|
}
|
|
|
|
|
|
|
|
// swaps left <-> right modifier types
|
2016-05-19 17:26:07 -04:00
|
|
|
inline DirectionModifier::Enum mirrorDirectionModifier(const DirectionModifier::Enum modifier)
|
2016-02-24 04:29:23 -05:00
|
|
|
{
|
2016-05-19 17:26:07 -04:00
|
|
|
const constexpr DirectionModifier::Enum results[] = {
|
2016-03-23 08:04:23 -04:00
|
|
|
DirectionModifier::UTurn, DirectionModifier::SharpLeft, DirectionModifier::Left,
|
|
|
|
DirectionModifier::SlightLeft, DirectionModifier::Straight, DirectionModifier::SlightRight,
|
|
|
|
DirectionModifier::Right, DirectionModifier::SharpRight};
|
2016-02-24 04:29:23 -05:00
|
|
|
return results[modifier];
|
|
|
|
}
|
|
|
|
|
2016-05-19 17:26:07 -04:00
|
|
|
inline bool canBeSuppressed(const TurnType::Enum type)
|
2016-02-24 04:29:23 -05:00
|
|
|
{
|
|
|
|
if (type == TurnType::Turn)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool isLowPriorityRoadClass(const FunctionalRoadClass road_class)
|
|
|
|
{
|
|
|
|
return road_class == FunctionalRoadClass::LOW_PRIORITY_ROAD ||
|
|
|
|
road_class == FunctionalRoadClass::SERVICE;
|
|
|
|
}
|
|
|
|
|
2016-05-19 17:26:07 -04:00
|
|
|
inline bool isDistinct(const DirectionModifier::Enum first, const DirectionModifier::Enum second)
|
2016-03-07 08:52:26 -05:00
|
|
|
{
|
|
|
|
if ((first + 1) % detail::num_direction_modifiers == second)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if ((second + 1) % detail::num_direction_modifiers == first)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-04-28 04:54:14 -04:00
|
|
|
inline std::pair<std::string, std::string> getPrefixAndSuffix(const std::string &data)
|
|
|
|
{
|
|
|
|
const auto suffix_pos = data.find_last_of(' ');
|
|
|
|
if (suffix_pos == std::string::npos)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
const auto prefix_pos = data.find_first_of(' ');
|
|
|
|
auto result = std::make_pair(data.substr(0, prefix_pos), data.substr(suffix_pos + 1));
|
|
|
|
boost::to_lower(result.first);
|
|
|
|
boost::to_lower(result.second);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-04-22 05:31:46 -04:00
|
|
|
inline bool requiresNameAnnounced(const std::string &from,
|
|
|
|
const std::string &to,
|
|
|
|
const SuffixTable &suffix_table)
|
2016-03-16 10:47:33 -04:00
|
|
|
{
|
2016-05-16 07:37:00 -04:00
|
|
|
// first is empty and the second is not
|
|
|
|
if (from.empty() && !to.empty())
|
2016-05-04 04:32:12 -04:00
|
|
|
return true;
|
|
|
|
|
2016-03-16 10:47:33 -04:00
|
|
|
// FIXME, handle in profile to begin with?
|
|
|
|
// this uses the encoding of references in the profile, which is very BAD
|
|
|
|
// Input for this function should be a struct separating streetname, suffix (e.g. road,
|
|
|
|
// boulevard, North, West ...), and a list of references
|
2016-03-18 13:16:20 -04:00
|
|
|
std::string from_name;
|
|
|
|
std::string from_ref;
|
|
|
|
std::string to_name;
|
|
|
|
std::string to_ref;
|
2016-03-16 10:47:33 -04:00
|
|
|
|
2016-03-18 13:16:20 -04:00
|
|
|
// Split from the format "{name} ({ref})" -> name, ref
|
2016-03-23 08:04:23 -04:00
|
|
|
auto split = [](const std::string &name, std::string &out_name, std::string &out_ref) {
|
2016-03-16 10:47:33 -04:00
|
|
|
const auto ref_begin = name.find_first_of('(');
|
|
|
|
if (ref_begin != std::string::npos)
|
|
|
|
{
|
2016-03-23 08:04:23 -04:00
|
|
|
if (ref_begin != 0)
|
|
|
|
out_name = name.substr(0, ref_begin - 1);
|
|
|
|
const auto ref_end = name.find_first_of(')');
|
|
|
|
out_ref = name.substr(ref_begin + 1, ref_end - ref_begin - 1);
|
2016-03-16 10:47:33 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
out_name = name;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
split(from, from_name, from_ref);
|
|
|
|
split(to, to_name, to_ref);
|
|
|
|
|
|
|
|
// check similarity of names
|
2016-03-23 08:04:23 -04:00
|
|
|
const auto names_are_empty = from_name.empty() && to_name.empty();
|
2016-04-22 05:31:46 -04:00
|
|
|
const auto name_is_contained =
|
|
|
|
boost::starts_with(from_name, to_name) || boost::starts_with(to_name, from_name);
|
2016-04-28 04:54:14 -04:00
|
|
|
|
|
|
|
const auto checkForPrefixOrSuffixChange =
|
|
|
|
[](const std::string &first, const std::string &second, const SuffixTable &suffix_table) {
|
|
|
|
|
|
|
|
const auto first_prefix_and_suffixes = getPrefixAndSuffix(first);
|
|
|
|
const auto second_prefix_and_suffixes = getPrefixAndSuffix(second);
|
|
|
|
// reverse strings, get suffices and reverse them to get prefixes
|
|
|
|
const auto checkTable = [&](const std::string str) {
|
|
|
|
return str.empty() || suffix_table.isSuffix(str);
|
|
|
|
};
|
|
|
|
|
|
|
|
const bool is_prefix_change = [&]() -> bool {
|
|
|
|
if (!checkTable(first_prefix_and_suffixes.first))
|
|
|
|
return false;
|
|
|
|
if (!checkTable(first_prefix_and_suffixes.first))
|
|
|
|
return false;
|
|
|
|
return !first.compare(first_prefix_and_suffixes.first.length(), std::string::npos,
|
2016-05-16 07:37:00 -04:00
|
|
|
second, second_prefix_and_suffixes.first.length(),
|
|
|
|
std::string::npos);
|
2016-04-28 04:54:14 -04:00
|
|
|
}();
|
|
|
|
|
|
|
|
const bool is_suffix_change = [&]() -> bool {
|
|
|
|
if (!checkTable(first_prefix_and_suffixes.second))
|
|
|
|
return false;
|
|
|
|
if (!checkTable(first_prefix_and_suffixes.second))
|
|
|
|
return false;
|
|
|
|
return !first.compare(0, first.length() - first_prefix_and_suffixes.second.length(),
|
2016-05-16 07:37:00 -04:00
|
|
|
second, 0,
|
|
|
|
second.length() - second_prefix_and_suffixes.second.length());
|
2016-04-28 04:54:14 -04:00
|
|
|
}();
|
|
|
|
|
|
|
|
return is_prefix_change || is_suffix_change;
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto is_suffix_change = checkForPrefixOrSuffixChange(from_name, to_name, suffix_table);
|
|
|
|
const auto names_are_equal = from_name == to_name || name_is_contained || is_suffix_change;
|
2016-03-23 08:04:23 -04:00
|
|
|
const auto name_is_removed = !from_name.empty() && to_name.empty();
|
2016-03-18 13:16:20 -04:00
|
|
|
// references are contained in one another
|
2016-03-23 08:04:23 -04:00
|
|
|
const auto refs_are_empty = from_ref.empty() && to_ref.empty();
|
|
|
|
const auto ref_is_contained =
|
|
|
|
from_ref.empty() || to_ref.empty() ||
|
2016-03-18 13:16:20 -04:00
|
|
|
(from_ref.find(to_ref) != std::string::npos || to_ref.find(from_ref) != std::string::npos);
|
2016-03-23 08:04:23 -04:00
|
|
|
const auto ref_is_removed = !from_ref.empty() && to_ref.empty();
|
2016-03-18 13:16:20 -04:00
|
|
|
|
2016-04-22 05:31:46 -04:00
|
|
|
const auto obvious_change = (names_are_empty && refs_are_empty) ||
|
|
|
|
(names_are_equal && ref_is_contained) ||
|
|
|
|
(names_are_equal && refs_are_empty) || name_is_removed ||
|
|
|
|
ref_is_removed || is_suffix_change;
|
2016-03-18 13:16:20 -04:00
|
|
|
|
|
|
|
return !obvious_change;
|
2016-03-16 10:47:33 -04:00
|
|
|
}
|
|
|
|
|
2016-03-23 08:04:23 -04:00
|
|
|
inline int getPriority(const FunctionalRoadClass road_class)
|
2016-03-16 10:47:33 -04:00
|
|
|
{
|
2016-03-23 08:04:23 -04:00
|
|
|
// The road priorities indicate which roads can bee seen as more or less equal.
|
|
|
|
// They are used in Fork-Discovery. Possibly should be moved to profiles post v5?
|
|
|
|
// A fork can happen between road types that are at most 1 priority apart from each other
|
|
|
|
const constexpr int road_priority[] = {10, 0, 10, 2, 10, 4, 10, 6,
|
|
|
|
10, 8, 10, 11, 10, 12, 10, 14};
|
2016-03-16 10:47:33 -04:00
|
|
|
return road_priority[static_cast<int>(road_class)];
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool canBeSeenAsFork(const FunctionalRoadClass first, const FunctionalRoadClass second)
|
|
|
|
{
|
|
|
|
// forks require similar road categories
|
2016-03-23 08:04:23 -04:00
|
|
|
// Based on the priorities assigned above, we can set forks only if the road priorities match
|
|
|
|
// closely.
|
2016-03-17 09:09:09 -04:00
|
|
|
// Potentially we could include features like number of lanes here and others?
|
|
|
|
// Should also be moved to profiles
|
2016-03-16 10:47:33 -04:00
|
|
|
return std::abs(getPriority(first) - getPriority(second)) <= 1;
|
|
|
|
}
|
|
|
|
|
2016-04-11 06:51:06 -04:00
|
|
|
// To simplify handling of Left/Right hand turns, we can mirror turns and write an intersection
|
|
|
|
// handler only for one side. The mirror function turns a left-hand turn in a equivalent right-hand
|
|
|
|
// turn and vice versa.
|
|
|
|
inline ConnectedRoad mirror(ConnectedRoad road)
|
|
|
|
{
|
2016-05-19 17:26:07 -04:00
|
|
|
const constexpr DirectionModifier::Enum mirrored_modifiers[] = {
|
2016-04-11 06:51:06 -04:00
|
|
|
DirectionModifier::UTurn, DirectionModifier::SharpLeft, DirectionModifier::Left,
|
|
|
|
DirectionModifier::SlightLeft, DirectionModifier::Straight, DirectionModifier::SlightRight,
|
|
|
|
DirectionModifier::Right, DirectionModifier::SharpRight};
|
|
|
|
|
|
|
|
if (angularDeviation(road.turn.angle, 0) > std::numeric_limits<double>::epsilon())
|
|
|
|
{
|
|
|
|
road.turn.angle = 360 - road.turn.angle;
|
|
|
|
road.turn.instruction.direction_modifier =
|
|
|
|
mirrored_modifiers[road.turn.instruction.direction_modifier];
|
|
|
|
}
|
|
|
|
return road;
|
|
|
|
}
|
|
|
|
|
2016-02-24 04:29:23 -05:00
|
|
|
} // namespace guidance
|
2016-03-01 16:30:31 -05:00
|
|
|
} // namespace extractor
|
2016-02-24 04:29:23 -05:00
|
|
|
} // namespace osrm
|
|
|
|
|
2016-03-01 16:30:31 -05:00
|
|
|
#endif // OSRM_GUIDANCE_TOOLKIT_HPP_
|