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-08-02 09:43:29 -04:00
|
|
|
#include "util/attributes.hpp"
|
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-06-15 08:38:24 -04:00
|
|
|
#include "util/guidance/turn_lanes.hpp"
|
2016-08-17 03:49:19 -04:00
|
|
|
#include "util/node_based_graph.hpp"
|
2016-06-15 08:38:24 -04:00
|
|
|
#include "util/typedefs.hpp"
|
2016-02-24 04:29:23 -05:00
|
|
|
|
|
|
|
#include "extractor/compressed_edge_container.hpp"
|
|
|
|
#include "extractor/query_node.hpp"
|
|
|
|
|
2016-08-17 03:49:19 -04:00
|
|
|
#include "extractor/guidance/constants.hpp"
|
2016-04-11 06:51:06 -04:00
|
|
|
#include "extractor/guidance/intersection.hpp"
|
2016-08-17 03:49:19 -04:00
|
|
|
#include "extractor/guidance/road_classification.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-16 10:47:33 -04:00
|
|
|
#include <string>
|
2016-06-15 08:38:24 -04:00
|
|
|
#include <unordered_map>
|
2016-04-28 04:54:14 -04:00
|
|
|
#include <utility>
|
2016-08-17 03:49:19 -04:00
|
|
|
#include <vector>
|
2016-02-24 04:29:23 -05:00
|
|
|
|
2016-04-22 05:31:46 -04:00
|
|
|
#include <boost/algorithm/string.hpp>
|
2016-06-15 08:38:24 -04:00
|
|
|
#include <boost/functional/hash.hpp>
|
2016-10-04 15:28:13 -04:00
|
|
|
#include <boost/range/iterator_range.hpp>
|
2016-07-08 04:44:49 -04:00
|
|
|
#include <boost/tokenizer.hpp>
|
2016-04-21 07:59:03 -04:00
|
|
|
|
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-10-04 15:28:13 -04:00
|
|
|
using util::guidance::LaneTupleIdPair;
|
|
|
|
using LaneDataIdMap = std::unordered_map<LaneTupleIdPair, LaneDataID, boost::hash<LaneTupleIdPair>>;
|
2016-06-15 08:38:24 -04:00
|
|
|
|
2016-04-18 07:41:19 -04:00
|
|
|
using util::guidance::angularDeviation;
|
2016-07-01 05:34:44 -04:00
|
|
|
using util::guidance::entersRoundabout;
|
|
|
|
using util::guidance::leavesRoundabout;
|
2016-04-18 07:41:19 -04:00
|
|
|
|
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.
|
|
|
|
|
2016-06-21 08:37:20 -04:00
|
|
|
inline bool hasRoundaboutType(const TurnInstruction instruction)
|
|
|
|
{
|
|
|
|
using namespace extractor::guidance::TurnType;
|
|
|
|
const constexpr TurnType::Enum valid_types[] = {TurnType::EnterRoundabout,
|
|
|
|
TurnType::EnterAndExitRoundabout,
|
|
|
|
TurnType::EnterRotary,
|
|
|
|
TurnType::EnterAndExitRotary,
|
|
|
|
TurnType::EnterRoundaboutIntersection,
|
|
|
|
TurnType::EnterAndExitRoundaboutIntersection,
|
|
|
|
TurnType::EnterRoundaboutAtExit,
|
|
|
|
TurnType::ExitRoundabout,
|
|
|
|
TurnType::EnterRotaryAtExit,
|
|
|
|
TurnType::ExitRotary,
|
|
|
|
TurnType::EnterRoundaboutIntersectionAtExit,
|
|
|
|
TurnType::ExitRoundaboutIntersection,
|
|
|
|
TurnType::StayOnRoundabout};
|
2016-07-08 06:39:29 -04:00
|
|
|
|
|
|
|
const auto *first = valid_types;
|
|
|
|
const auto *last = first + sizeof(valid_types) / sizeof(valid_types[0]);
|
|
|
|
|
|
|
|
return std::find(first, last, instruction.type) != last;
|
2016-06-22 07:20:55 -04:00
|
|
|
}
|
2016-06-21 08:37:20 -04:00
|
|
|
|
2016-05-13 13:18:00 -04:00
|
|
|
// Public service vehicle lanes and similar can introduce additional lanes into the lane string that
|
|
|
|
// are not specifically marked for left/right turns. This function can be used from the profile to
|
|
|
|
// trim the lane string appropriately
|
|
|
|
//
|
|
|
|
// left|throught|
|
|
|
|
// in combination with lanes:psv:forward=1
|
|
|
|
// will be corrected to left|throught, since the final lane is not drivable.
|
|
|
|
// This is in contrast to a situation with lanes:psv:forward=0 (or not set) where left|through|
|
|
|
|
// represents left|through|through
|
2016-08-02 09:43:29 -04:00
|
|
|
OSRM_ATTR_WARN_UNUSED
|
2016-05-13 13:18:00 -04:00
|
|
|
inline std::string
|
|
|
|
trimLaneString(std::string lane_string, std::int32_t count_left, std::int32_t count_right)
|
|
|
|
{
|
|
|
|
if (count_left)
|
|
|
|
{
|
|
|
|
bool sane = count_left < static_cast<std::int32_t>(lane_string.size());
|
|
|
|
for (std::int32_t i = 0; i < count_left; ++i)
|
|
|
|
// this is adjusted for our fake pipe. The moment cucumber can handle multiple escaped
|
|
|
|
// pipes, the '&' part can be removed
|
2016-08-12 11:04:40 -04:00
|
|
|
if (lane_string[i] != '|')
|
2016-05-13 13:18:00 -04:00
|
|
|
{
|
|
|
|
sane = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sane)
|
|
|
|
{
|
|
|
|
lane_string.erase(lane_string.begin(), lane_string.begin() + count_left);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (count_right)
|
|
|
|
{
|
|
|
|
bool sane = count_right < static_cast<std::int32_t>(lane_string.size());
|
|
|
|
for (auto itr = lane_string.rbegin();
|
|
|
|
itr != lane_string.rend() && itr != lane_string.rbegin() + count_right;
|
|
|
|
++itr)
|
|
|
|
{
|
2016-08-12 11:04:40 -04:00
|
|
|
if (*itr != '|')
|
2016-05-13 13:18:00 -04:00
|
|
|
{
|
|
|
|
sane = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sane)
|
|
|
|
lane_string.resize(lane_string.size() - count_right);
|
|
|
|
}
|
|
|
|
return lane_string;
|
|
|
|
}
|
|
|
|
|
2016-07-08 04:44:49 -04:00
|
|
|
// https://github.com/Project-OSRM/osrm-backend/issues/2638
|
|
|
|
// It can happen that some lanes are not drivable by car. Here we handle this tagging scheme
|
|
|
|
// (vehicle:lanes) to filter out not-allowed roads
|
|
|
|
// lanes=3
|
|
|
|
// turn:lanes=left|through|through|right
|
|
|
|
// vehicle:lanes=yes|yes|no|yes
|
|
|
|
// bicycle:lanes=yes|no|designated|yes
|
2016-08-02 09:43:29 -04:00
|
|
|
OSRM_ATTR_WARN_UNUSED
|
2016-07-26 05:23:14 -04:00
|
|
|
inline std::string applyAccessTokens(std::string lane_string, const std::string &access_tokens)
|
2016-07-08 04:44:49 -04:00
|
|
|
{
|
|
|
|
typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
|
|
|
|
boost::char_separator<char> sep("|", "", boost::keep_empty_tokens);
|
|
|
|
tokenizer tokens(lane_string, sep);
|
|
|
|
tokenizer access(access_tokens, sep);
|
|
|
|
|
|
|
|
// strings don't match, don't do anything
|
|
|
|
if (std::distance(std::begin(tokens), std::end(tokens)) !=
|
|
|
|
std::distance(std::begin(access), std::end(access)))
|
|
|
|
return lane_string;
|
|
|
|
|
|
|
|
std::string result_string = "";
|
|
|
|
const static std::string yes = "yes";
|
|
|
|
|
|
|
|
for (auto token_itr = std::begin(tokens), access_itr = std::begin(access);
|
|
|
|
token_itr != std::end(tokens);
|
|
|
|
++token_itr, ++access_itr)
|
|
|
|
{
|
|
|
|
if (*access_itr == yes)
|
|
|
|
{
|
|
|
|
// we have to add this in front, because the next token could be invalid. Doing this on
|
|
|
|
// non-empty strings makes sure that the token string will be valid in the end
|
|
|
|
if (!result_string.empty())
|
|
|
|
result_string += '|';
|
|
|
|
|
|
|
|
result_string += *token_itr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result_string;
|
|
|
|
}
|
|
|
|
|
2016-08-17 03:49:19 -04:00
|
|
|
inline bool obviousByRoadClass(const RoadClassification in_classification,
|
|
|
|
const RoadClassification obvious_candidate,
|
|
|
|
const RoadClassification compare_candidate)
|
|
|
|
{
|
2016-10-24 20:29:51 -04:00
|
|
|
// lower numbers are of higher priority
|
2016-08-17 03:49:19 -04:00
|
|
|
const bool has_high_priority = PRIORITY_DISTINCTION_FACTOR * obvious_candidate.GetPriority() <
|
|
|
|
compare_candidate.GetPriority();
|
|
|
|
|
|
|
|
const bool continues_on_same_class = in_classification == obvious_candidate;
|
|
|
|
return (has_high_priority && continues_on_same_class) ||
|
|
|
|
(!obvious_candidate.IsLowPriorityRoadClass() &&
|
|
|
|
!in_classification.IsLowPriorityRoadClass() &&
|
|
|
|
compare_candidate.IsLowPriorityRoadClass());
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We use the sum of least squares to calculate a linear regression through our
|
|
|
|
* coordinates.
|
|
|
|
* This regression gives a good idea of how the road can be perceived and corrects for
|
|
|
|
* initial and final corrections
|
|
|
|
*/
|
|
|
|
inline std::pair<util::Coordinate, util::Coordinate>
|
|
|
|
leastSquareRegression(const std::vector<util::Coordinate> &coordinates)
|
|
|
|
{
|
|
|
|
BOOST_ASSERT(coordinates.size() >= 2);
|
|
|
|
double sum_lon = 0, sum_lat = 0, sum_lon_lat = 0, sum_lon_lon = 0;
|
|
|
|
double min_lon = static_cast<double>(toFloating(coordinates.front().lon));
|
|
|
|
double max_lon = static_cast<double>(toFloating(coordinates.front().lon));
|
|
|
|
for (const auto coord : coordinates)
|
|
|
|
{
|
|
|
|
min_lon = std::min(min_lon, static_cast<double>(toFloating(coord.lon)));
|
|
|
|
max_lon = std::max(max_lon, static_cast<double>(toFloating(coord.lon)));
|
|
|
|
sum_lon += static_cast<double>(toFloating(coord.lon));
|
|
|
|
sum_lon_lon +=
|
|
|
|
static_cast<double>(toFloating(coord.lon)) * static_cast<double>(toFloating(coord.lon));
|
|
|
|
sum_lat += static_cast<double>(toFloating(coord.lat));
|
|
|
|
sum_lon_lat +=
|
|
|
|
static_cast<double>(toFloating(coord.lon)) * static_cast<double>(toFloating(coord.lat));
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto dividend = coordinates.size() * sum_lon_lat - sum_lon * sum_lat;
|
|
|
|
const auto divisor = coordinates.size() * sum_lon_lon - sum_lon * sum_lon;
|
|
|
|
if (std::abs(divisor) < std::numeric_limits<double>::epsilon())
|
|
|
|
return std::make_pair(coordinates.front(), coordinates.back());
|
|
|
|
|
|
|
|
// slope of the regression line
|
|
|
|
const auto slope = dividend / divisor;
|
|
|
|
const auto intercept = (sum_lat - slope * sum_lon) / coordinates.size();
|
|
|
|
|
|
|
|
const auto GetLatAtLon = [intercept,
|
|
|
|
slope](const util::FloatLongitude longitude) -> util::FloatLatitude {
|
|
|
|
return {intercept + slope * static_cast<double>((longitude))};
|
|
|
|
};
|
|
|
|
|
|
|
|
const util::Coordinate regression_first = {
|
|
|
|
toFixed(util::FloatLongitude{min_lon - 1}),
|
|
|
|
toFixed(util::FloatLatitude(GetLatAtLon(util::FloatLongitude{min_lon - 1})))};
|
|
|
|
const util::Coordinate regression_end = {
|
|
|
|
toFixed(util::FloatLongitude{max_lon + 1}),
|
|
|
|
toFixed(util::FloatLatitude(GetLatAtLon(util::FloatLongitude{max_lon + 1})))};
|
|
|
|
|
|
|
|
return {regression_first, regression_end};
|
|
|
|
}
|
|
|
|
|
2016-11-03 05:18:27 -04:00
|
|
|
inline std::uint8_t getLaneCountAtIntersection(const NodeID intersection_node,
|
|
|
|
const util::NodeBasedDynamicGraph &node_based_graph)
|
|
|
|
{
|
|
|
|
std::uint8_t lanes = 0;
|
|
|
|
for (const EdgeID onto_edge : node_based_graph.GetAdjacentEdgeRange(intersection_node))
|
|
|
|
lanes = std::max(
|
|
|
|
lanes, node_based_graph.GetEdgeData(onto_edge).road_classification.GetNumberOfLanes());
|
|
|
|
return lanes;
|
|
|
|
}
|
|
|
|
|
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_
|