2016-04-08 06:49:14 -04:00
|
|
|
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_
|
|
|
|
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_
|
|
|
|
|
2016-10-26 17:32:29 -04:00
|
|
|
#include <algorithm>
|
|
|
|
#include <functional>
|
|
|
|
#include <limits>
|
2016-04-08 06:49:14 -04:00
|
|
|
#include <string>
|
2016-12-02 06:02:42 -05:00
|
|
|
#include <type_traits>
|
2016-04-08 06:49:14 -04:00
|
|
|
#include <vector>
|
|
|
|
|
2016-11-18 03:38:26 -05:00
|
|
|
#include "util/bearing.hpp"
|
2017-01-07 08:13:32 -05:00
|
|
|
#include "util/log.hpp"
|
2016-11-03 05:18:27 -04:00
|
|
|
#include "util/node_based_graph.hpp"
|
2016-04-08 06:49:14 -04:00
|
|
|
#include "util/typedefs.hpp" // EdgeID
|
|
|
|
|
2016-12-06 07:22:51 -05:00
|
|
|
#include "extractor/guidance/turn_instruction.hpp"
|
2016-10-26 17:32:29 -04:00
|
|
|
|
2016-12-06 07:22:51 -05:00
|
|
|
#include <boost/range/algorithm/count_if.hpp>
|
2017-01-03 16:11:59 -05:00
|
|
|
#include <boost/range/algorithm/find_if.hpp>
|
|
|
|
#include <boost/range/algorithm/min_element.hpp>
|
2016-10-26 17:32:29 -04:00
|
|
|
|
2016-04-08 06:49:14 -04:00
|
|
|
namespace osrm
|
|
|
|
{
|
|
|
|
namespace extractor
|
|
|
|
{
|
|
|
|
namespace guidance
|
|
|
|
{
|
|
|
|
|
2016-11-18 03:38:26 -05:00
|
|
|
// the shape of an intersection only knows about edge IDs and bearings
|
2017-01-07 08:13:32 -05:00
|
|
|
// `bearing` is the direction in clockwise angle from true north after taking the turn:
|
|
|
|
// 0 = heading north, 90 = east, 180 = south, 270 = west
|
2016-11-18 03:38:26 -05:00
|
|
|
struct IntersectionShapeData
|
2016-04-08 06:49:14 -04:00
|
|
|
{
|
|
|
|
EdgeID eid;
|
2016-08-17 03:49:19 -04:00
|
|
|
double bearing;
|
2016-11-18 03:38:26 -05:00
|
|
|
double segment_length;
|
|
|
|
};
|
|
|
|
|
|
|
|
inline auto makeCompareShapeDataByBearing(const double base_bearing)
|
2017-01-17 06:53:39 -05:00
|
|
|
{
|
|
|
|
return [base_bearing](const auto &lhs, const auto &rhs) {
|
|
|
|
return util::angularDeviation(lhs.bearing, base_bearing) <
|
|
|
|
util::angularDeviation(rhs.bearing, base_bearing);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-12-02 06:02:42 -05:00
|
|
|
inline auto makeCompareAngularDeviation(const double angle)
|
|
|
|
{
|
|
|
|
return [angle](const auto &lhs, const auto &rhs) {
|
2016-12-02 04:53:22 -05:00
|
|
|
return util::angularDeviation(lhs.angle, angle) < util::angularDeviation(rhs.angle, angle);
|
2016-12-02 06:02:42 -05:00
|
|
|
};
|
|
|
|
}
|
2016-11-18 03:38:26 -05:00
|
|
|
|
2016-12-06 07:22:51 -05:00
|
|
|
inline auto makeExtractLanesForRoad(const util::NodeBasedDynamicGraph &node_based_graph)
|
|
|
|
{
|
|
|
|
return [&node_based_graph](const auto &road) {
|
|
|
|
return node_based_graph.GetEdgeData(road.eid).road_classification.GetNumberOfLanes();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-11-18 03:38:26 -05:00
|
|
|
// When viewing an intersection from an incoming edge, we can transform a shape into a view which
|
|
|
|
// gives additional information on angles and whether a turn is allowed
|
|
|
|
struct IntersectionViewData : IntersectionShapeData
|
|
|
|
{
|
|
|
|
IntersectionViewData(const IntersectionShapeData &shape,
|
|
|
|
const bool entry_allowed,
|
|
|
|
const double angle)
|
|
|
|
: IntersectionShapeData(shape), entry_allowed(entry_allowed), angle(angle)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool entry_allowed;
|
|
|
|
double angle;
|
|
|
|
|
|
|
|
bool CompareByAngle(const IntersectionViewData &other) const;
|
2016-04-08 06:49:14 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// A Connected Road is the internal representation of a potential turn. Internally, we require
|
|
|
|
// full list of all connected roads to determine the outcome.
|
|
|
|
// The reasoning behind is that even invalid turns can influence the perceived angles, or even
|
2016-10-26 17:32:29 -04:00
|
|
|
// instructions themselves. An possible example can be described like this:
|
2016-04-08 06:49:14 -04:00
|
|
|
//
|
|
|
|
// aaa(2)aa
|
|
|
|
// a - bbbbb
|
|
|
|
// aaa(1)aa
|
|
|
|
//
|
|
|
|
// will not be perceived as a turn from (1) -> b, and as a U-turn from (1) -> (2).
|
|
|
|
// In addition, they can influence whether a turn is obvious or not. b->(2) would also be no
|
2016-10-26 17:32:29 -04:00
|
|
|
// turn-operation, but rather a name change.
|
2016-04-08 06:49:14 -04:00
|
|
|
//
|
|
|
|
// If this were a normal intersection with
|
|
|
|
//
|
|
|
|
// cccccccc
|
|
|
|
// o bbbbb
|
|
|
|
// aaaaaaaa
|
|
|
|
//
|
|
|
|
// We would perceive a->c as a sharp turn, a->b as a slight turn, and b->c as a slight turn.
|
2016-11-18 03:38:26 -05:00
|
|
|
struct ConnectedRoad final : IntersectionViewData
|
2016-04-08 06:49:14 -04:00
|
|
|
{
|
2016-11-18 03:38:26 -05:00
|
|
|
ConnectedRoad(const IntersectionViewData &view,
|
|
|
|
const TurnInstruction instruction,
|
|
|
|
const LaneDataID lane_data_id)
|
|
|
|
: IntersectionViewData(view), instruction(instruction), lane_data_id(lane_data_id)
|
|
|
|
{
|
|
|
|
}
|
2016-04-08 06:49:14 -04:00
|
|
|
|
2016-11-18 03:38:26 -05:00
|
|
|
TurnInstruction instruction;
|
|
|
|
LaneDataID lane_data_id;
|
2016-11-03 05:18:27 -04:00
|
|
|
|
|
|
|
// used to sort the set of connected roads (we require sorting throughout turn handling)
|
|
|
|
bool compareByAngle(const ConnectedRoad &other) const;
|
|
|
|
|
|
|
|
// make a left turn into an equivalent right turn and vice versa
|
|
|
|
void mirror();
|
|
|
|
|
|
|
|
OSRM_ATTR_WARN_UNUSED
|
|
|
|
ConnectedRoad getMirroredCopy() const;
|
2016-04-08 06:49:14 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// small helper function to print the content of a connected road
|
2016-12-06 07:22:51 -05:00
|
|
|
std::string toString(const IntersectionShapeData &shape);
|
|
|
|
std::string toString(const IntersectionViewData &view);
|
2016-04-08 06:49:14 -04:00
|
|
|
std::string toString(const ConnectedRoad &road);
|
|
|
|
|
2016-10-26 17:32:29 -04:00
|
|
|
// Intersections are sorted roads: [0] being the UTurn road, then from sharp right to sharp left.
|
2016-12-06 07:22:51 -05:00
|
|
|
// common operations shared amongst all intersection types
|
|
|
|
template <typename Self> struct EnableShapeOps
|
|
|
|
{
|
|
|
|
// same as closest turn, but for bearings
|
|
|
|
auto FindClosestBearing(double bearing) const
|
|
|
|
{
|
|
|
|
auto comp = makeCompareShapeDataByBearing(bearing);
|
|
|
|
return std::min_element(self()->begin(), self()->end(), comp);
|
|
|
|
}
|
|
|
|
|
|
|
|
// search a given eid in the intersection
|
|
|
|
auto FindEid(const EdgeID eid) const
|
|
|
|
{
|
2017-01-03 16:11:59 -05:00
|
|
|
return boost::range::find_if(*self(), [eid](const auto &road) { return road.eid == eid; });
|
2016-12-06 07:22:51 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// find the maximum value based on a conversion operator
|
|
|
|
template <typename UnaryProjection> auto FindMaximum(UnaryProjection converter) const
|
|
|
|
{
|
|
|
|
BOOST_ASSERT(!self()->empty());
|
|
|
|
auto initial = converter(self()->front());
|
|
|
|
|
|
|
|
const auto extract_maximal_value = [&initial, converter](const auto &road) {
|
|
|
|
initial = std::max(initial, converter(road));
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
boost::range::find_if(*self(), extract_maximal_value);
|
|
|
|
return initial;
|
|
|
|
}
|
|
|
|
|
|
|
|
// find the maximum value based on a conversion operator and a predefined initial value
|
|
|
|
template <typename UnaryPredicate> auto Count(UnaryPredicate detector) const
|
|
|
|
{
|
|
|
|
BOOST_ASSERT(!self()->empty());
|
|
|
|
return boost::range::count_if(*self(), detector);
|
|
|
|
}
|
2016-10-26 17:32:29 -04:00
|
|
|
|
2016-12-06 07:22:51 -05:00
|
|
|
private:
|
|
|
|
auto self() { return static_cast<Self *>(this); }
|
|
|
|
auto self() const { return static_cast<const Self *>(this); }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct IntersectionShape final : std::vector<IntersectionShapeData>, //
|
|
|
|
EnableShapeOps<IntersectionShape> //
|
|
|
|
{
|
|
|
|
using Base = std::vector<IntersectionShapeData>;
|
|
|
|
};
|
2016-11-18 03:38:26 -05:00
|
|
|
|
2016-12-02 06:02:42 -05:00
|
|
|
// Common operations shared among IntersectionView and Intersections.
|
|
|
|
// Inherit to enable those operations on your compatible type. CRTP pattern.
|
|
|
|
template <typename Self> struct EnableIntersectionOps
|
|
|
|
{
|
|
|
|
// Find the turn whose angle offers the least angular deviation to the specified angle
|
|
|
|
// For turn angles [0, 90, 260] and a query of 180 we return the 260 degree turn.
|
|
|
|
auto findClosestTurn(double angle) const
|
|
|
|
{
|
|
|
|
auto comp = makeCompareAngularDeviation(angle);
|
2016-12-06 07:22:51 -05:00
|
|
|
return boost::range::min_element(*self(), comp);
|
2016-12-02 06:02:42 -05:00
|
|
|
}
|
2017-01-07 08:13:32 -05:00
|
|
|
// returns a non-const_interator
|
|
|
|
auto findClosestTurn(double angle)
|
|
|
|
{
|
|
|
|
auto comp = makeCompareAngularDeviation(angle);
|
|
|
|
return std::min_element(self()->begin(), self()->end(), comp);
|
|
|
|
}
|
2016-12-02 06:02:42 -05:00
|
|
|
|
2016-12-06 07:22:51 -05:00
|
|
|
/* Check validity of the intersection object. We assume a few basic properties every set of
|
|
|
|
* connected roads should follow throughout guidance pre-processing. This utility function
|
|
|
|
* allows checking intersections for validity
|
|
|
|
*/
|
2016-10-26 17:32:29 -04:00
|
|
|
auto valid() const
|
|
|
|
{
|
|
|
|
if (self()->empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
auto comp = [](const auto &lhs, const auto &rhs) { return lhs.CompareByAngle(rhs); };
|
|
|
|
|
|
|
|
const auto ordered = std::is_sorted(self()->begin(), self()->end(), comp);
|
|
|
|
|
|
|
|
if (!ordered)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const auto uturn = self()->operator[](0).angle < std::numeric_limits<double>::epsilon();
|
|
|
|
|
|
|
|
if (!uturn)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the UTurn road we took to arrive at this intersection.
|
|
|
|
const auto &getUTurnRoad() const { return self()->operator[](0); }
|
|
|
|
|
|
|
|
// Returns the right-most road at this intersection.
|
|
|
|
const auto &getRightmostRoad() const
|
|
|
|
{
|
|
|
|
return self()->size() > 1 ? self()->operator[](1) : self()->getUTurnRoad();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the left-most road at this intersection.
|
|
|
|
const auto &getLeftmostRoad() const
|
|
|
|
{
|
|
|
|
return self()->size() > 1 ? self()->back() : self()->getUTurnRoad();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Can this be skipped over?
|
|
|
|
auto isTrafficSignalOrBarrier() const { return self()->size() == 2; }
|
|
|
|
|
|
|
|
// Checks if there is at least one road available (except UTurn road) on which to continue.
|
|
|
|
auto isDeadEnd() const
|
|
|
|
{
|
|
|
|
auto pred = [](const auto &road) { return road.entry_allowed; };
|
2016-12-06 07:22:51 -05:00
|
|
|
return std::none_of(self()->begin() + 1, self()->end(), pred);
|
2016-10-26 17:32:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the number of roads we can enter at this intersection, respectively.
|
|
|
|
auto countEnterable() const
|
|
|
|
{
|
|
|
|
auto pred = [](const auto &road) { return road.entry_allowed; };
|
2016-12-06 07:22:51 -05:00
|
|
|
return boost::range::count_if(*self(), pred);
|
2016-10-26 17:32:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the number of roads we can not enter at this intersection, respectively.
|
|
|
|
auto countNonEnterable() const { return self()->size() - self()->countEnterable(); }
|
|
|
|
|
2016-12-06 07:22:51 -05:00
|
|
|
// same as find closests turn but with an additional predicate to allow filtering
|
|
|
|
// the filter has to return `true` for elements that should be ignored
|
|
|
|
template <typename UnaryPredicate>
|
|
|
|
auto findClosestTurn(const double angle, const UnaryPredicate filter) const
|
|
|
|
{
|
|
|
|
BOOST_ASSERT(!self()->empty());
|
2017-01-03 16:11:59 -05:00
|
|
|
const auto candidate =
|
|
|
|
boost::range::min_element(*self(), [angle, &filter](const auto &lhs, const auto &rhs) {
|
2016-12-06 07:22:51 -05:00
|
|
|
const auto filtered_lhs = filter(lhs), filtered_rhs = filter(rhs);
|
|
|
|
const auto deviation_lhs = util::angularDeviation(lhs.angle, angle),
|
|
|
|
deviation_rhs = util::angularDeviation(rhs.angle, angle);
|
|
|
|
return std::tie(filtered_lhs, deviation_lhs) <
|
|
|
|
std::tie(filtered_rhs, deviation_rhs);
|
|
|
|
});
|
|
|
|
|
|
|
|
// make sure only to return valid elements
|
|
|
|
return filter(*candidate) ? self()->end() : candidate;
|
|
|
|
}
|
|
|
|
|
2017-01-07 08:13:32 -05:00
|
|
|
// check if all roads between begin and end allow entry
|
|
|
|
template <typename InputIt>
|
|
|
|
bool hasAllValidEntries(const InputIt begin, const InputIt end) const
|
|
|
|
{
|
|
|
|
static_assert(
|
|
|
|
std::is_base_of<std::input_iterator_tag,
|
|
|
|
typename std::iterator_traits<InputIt>::iterator_category>::value,
|
|
|
|
"hasAllValidEntries() only accepts input iterators");
|
|
|
|
return std::all_of(
|
|
|
|
begin, end, [](const IntersectionViewData &road) { return road.entry_allowed; });
|
|
|
|
}
|
|
|
|
|
2016-12-02 06:02:42 -05:00
|
|
|
private:
|
|
|
|
auto self() { return static_cast<Self *>(this); }
|
|
|
|
auto self() const { return static_cast<const Self *>(this); }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct IntersectionView final : std::vector<IntersectionViewData>, //
|
2016-12-06 07:22:51 -05:00
|
|
|
EnableShapeOps<IntersectionView>, //
|
2016-12-02 06:02:42 -05:00
|
|
|
EnableIntersectionOps<IntersectionView> //
|
2016-10-29 15:38:10 -04:00
|
|
|
{
|
2016-11-18 03:38:26 -05:00
|
|
|
using Base = std::vector<IntersectionViewData>;
|
|
|
|
};
|
2016-10-29 15:38:10 -04:00
|
|
|
|
2017-01-07 08:13:32 -05:00
|
|
|
// `Intersection` is a relative view of an intersection by an incoming edge.
|
|
|
|
// `Intersection` are streets at an intersection ordered from from sharp right counter-clockwise to
|
|
|
|
// sharp left where `intersection[0]` is _always_ a u-turn
|
|
|
|
|
|
|
|
// An intersection is an ordered list of connected roads ordered from from sharp right
|
|
|
|
// counter-clockwise to sharp left where `intersection[0]` is always a u-turn
|
|
|
|
//
|
|
|
|
// |
|
|
|
|
// |
|
|
|
|
// (intersec[3])
|
|
|
|
// |
|
|
|
|
// |
|
|
|
|
// |
|
|
|
|
// nid ---(via_eid/intersec[0])--- nbg.GetTarget(via) ---(intersec[2])---
|
|
|
|
// |
|
|
|
|
// |
|
|
|
|
// |
|
|
|
|
// (intersec[1])
|
|
|
|
// |
|
|
|
|
// |
|
|
|
|
//
|
|
|
|
// intersec := intersection
|
|
|
|
// nbh := node_based_graph
|
|
|
|
//
|
2016-12-02 06:02:42 -05:00
|
|
|
struct Intersection final : std::vector<ConnectedRoad>, //
|
2016-12-06 07:22:51 -05:00
|
|
|
EnableShapeOps<Intersection>, //
|
2016-12-02 06:02:42 -05:00
|
|
|
EnableIntersectionOps<Intersection> //
|
2016-11-18 03:38:26 -05:00
|
|
|
{
|
|
|
|
using Base = std::vector<ConnectedRoad>;
|
2016-10-29 15:38:10 -04:00
|
|
|
};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
|
|
} // namespace guidance
|
|
|
|
} // namespace extractor
|
|
|
|
} // namespace osrm
|
|
|
|
|
|
|
|
#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_*/
|