Move guidance pre-processing code into GUIDANCE library

This commit is contained in:
Michael Krasnyk
2018-01-05 13:05:53 +01:00
parent 1794185d43
commit 36877e4de5
89 changed files with 153 additions and 151 deletions
@@ -8,15 +8,15 @@
#include "extractor/edge_based_edge.hpp"
#include "extractor/edge_based_node_segment.hpp"
#include "extractor/extraction_turn.hpp"
#include "extractor/guidance/turn_analysis.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "extractor/guidance/turn_lane_types.hpp"
#include "extractor/nbg_to_ebg.hpp"
#include "extractor/node_data_container.hpp"
#include "extractor/original_edge_data.hpp"
#include "extractor/query_node.hpp"
#include "extractor/restriction_index.hpp"
#include "extractor/way_restriction_map.hpp"
#include "guidance/turn_analysis.hpp"
#include "guidance/turn_instruction.hpp"
#include "guidance/turn_lane_types.hpp"
#include "util/concurrent_id_map.hpp"
#include "util/deallocating_vector.hpp"
@@ -12,7 +12,7 @@
#include <limits>
#include <string>
#include "extractor/guidance/parsing_toolkit.hpp"
#include "guidance/parsing_toolkit.hpp"
namespace osrm
{
+1 -1
View File
@@ -3,7 +3,7 @@
#include <boost/numeric/conversion/cast.hpp>
#include <extractor/guidance/intersection.hpp>
#include <guidance/intersection.hpp>
#include <cstdint>
+1 -1
View File
@@ -1,8 +1,8 @@
#ifndef EXTRACTION_WAY_HPP
#define EXTRACTION_WAY_HPP
#include "extractor/guidance/road_classification.hpp"
#include "extractor/travel_mode.hpp"
#include "guidance/road_classification.hpp"
#include "util/guidance/turn_lanes.hpp"
#include "util/typedefs.hpp"
+1 -1
View File
@@ -2,7 +2,7 @@
#define EXTRACTOR_CALLBACKS_HPP
#include "extractor/class_data.hpp"
#include "extractor/guidance/turn_lane_types.hpp"
#include "guidance/turn_lane_types.hpp"
#include "util/typedefs.hpp"
#include <boost/functional/hash.hpp>
+1 -1
View File
@@ -2,11 +2,11 @@
#define OSRM_EXTRACTOR_FILES_HPP
#include "extractor/edge_based_edge.hpp"
#include "extractor/guidance/turn_lane_types.hpp"
#include "extractor/node_data_container.hpp"
#include "extractor/profile_properties.hpp"
#include "extractor/serialization.hpp"
#include "extractor/turn_data_container.hpp"
#include "guidance/turn_lane_types.hpp"
#include "util/coordinate.hpp"
#include "util/guidance/bearing_class.hpp"
+2 -2
View File
@@ -10,8 +10,8 @@
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/guidance/intersection.hpp"
#include "guidance/coordinate_extractor.hpp"
#include "guidance/intersection.hpp"
#include "util/coordinate.hpp"
#include "util/geojson_debug_policy_toolkit.hpp"
-50
View File
@@ -1,50 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_CONSTANTS_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_CONSTANTS_HPP_
namespace osrm
{
namespace extractor
{
namespace guidance
{
const bool constexpr INVERT = true;
// what angle is interpreted as going straight
const double constexpr STRAIGHT_ANGLE = 180.;
const double constexpr ORTHOGONAL_ANGLE = 90.;
// if a turn deviates this much from going straight, it will be kept straight
const double constexpr MAXIMAL_ALLOWED_NO_TURN_DEVIATION = 3.;
// angle that lies between two nearly indistinguishable roads
const double constexpr NARROW_TURN_ANGLE = 40.;
const double constexpr GROUP_ANGLE = 60;
// angle difference that can be classified as straight, if its the only narrow turn
const double constexpr FUZZY_ANGLE_DIFFERENCE = 25.;
const double constexpr DISTINCTION_RATIO = 2;
// Named roundabouts with radii larger then than this are seen as rotary
const double constexpr MAX_ROUNDABOUT_RADIUS = 15;
// Unnamed small roundabouts that look like intersections are announced as turns,
// guard against data issues or such roundabout intersections getting too large.
const double constexpr MAX_ROUNDABOUT_INTERSECTION_RADIUS = 15;
const double constexpr INCREASES_BY_FOURTY_PERCENT = 1.4;
const int constexpr MAX_SLIPROAD_THRESHOLD = 250;
// Road priorities give an idea of how obvious a turn is. If two priorities differ greatly (e.g.
// service road over a primary road, the better priority can be seen as obvious due to its road
// category).
const double constexpr PRIORITY_DISTINCTION_FACTOR = 1.75;
// the lane width we assume for a single lane
const auto constexpr ASSUMED_LANE_WIDTH = 3.25;
// how far apart can roads be at the most, when thinking about merging them?
const auto constexpr MERGABLE_ANGLE_DIFFERENCE = 95.0;
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_GUIDANCE_CONSTANTS_HPP_
@@ -1,258 +0,0 @@
#ifndef OSRM_EXTRACTOR_COORDINATE_EXTRACTOR_HPP_
#define OSRM_EXTRACTOR_COORDINATE_EXTRACTOR_HPP_
#include <utility>
#include <vector>
#include "extractor/compressed_edge_container.hpp"
#include "extractor/query_node.hpp"
#include "util/attributes.hpp"
#include "util/coordinate.hpp"
#include "util/node_based_graph.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
class CoordinateExtractor
{
public:
CoordinateExtractor(const util::NodeBasedDynamicGraph &node_based_graph,
const extractor::CompressedEdgeContainer &compressed_geometries,
const std::vector<util::Coordinate> &node_coordinates);
/* Find a interpolated coordinate a long the compressed geometries. The desired coordinate
* should be in a certain distance. This method is dedicated to find representative coordinates
* at turns.
* Note: The segment between intersection and turn coordinate can be zero, if the OSM modelling
* is unfortunate. See https://github.com/Project-OSRM/osrm-backend/issues/3470
*/
OSRM_ATTR_WARN_UNUSED
util::Coordinate GetCoordinateAlongRoad(const NodeID intersection_node,
const EdgeID turn_edge,
const bool traversed_in_reverse,
const NodeID to_node,
const std::uint8_t number_of_in_lanes) const;
// Given a set of precomputed coordinates, select the representative coordinate along the road
// that best describes the turn
OSRM_ATTR_WARN_UNUSED
util::Coordinate
ExtractRepresentativeCoordinate(const NodeID intersection_node,
const EdgeID turn_edge,
const bool traversed_in_reverse,
const NodeID to_node,
const std::uint8_t intersection_lanes,
std::vector<util::Coordinate> coordinates) const;
// instead of finding only a single coordinate, we can also list all coordinates along a
// road.
OSRM_ATTR_WARN_UNUSED std::vector<util::Coordinate>
GetCoordinatesAlongRoad(const NodeID intersection_node,
const EdgeID turn_edge,
const bool traversed_in_reverse,
const NodeID to_node) const;
// wrapper in case of normal forward edges (traversed_in_reverse = false, to_node =
// node_based_graph.GetTarget(turn_edge)
OSRM_ATTR_WARN_UNUSED
std::vector<util::Coordinate> GetForwardCoordinatesAlongRoad(const NodeID from,
const EdgeID turn_edge) const;
// a less precise way to compute coordinates along a route. Due to the heavy interaction of
// graph traversal and turn instructions, we often don't care for high precision. We only want
// to check for available connections in order, or find (with room for error) the straightmost
// turn. This function will offer a bit more error potential but allow for much higher
// performance
OSRM_ATTR_WARN_UNUSED
util::Coordinate GetCoordinateCloseToTurn(const NodeID from_node,
const EdgeID turn_edge,
const bool traversed_in_reverse,
const NodeID to_node) const;
/* When extracting the coordinates, we first extract all coordinates. We don't care about most
* of them, though.
*
* Our very first step trims the coordinates to a saller set, close to the intersection.. The
* idea here is to filter all coordinates at the end of the road and consider only the formi
* close to the intersection:
*
* a -------------- v ----------.
* .
* .
* .
* b
*
* For calculating the turn angle for the intersection at `a`, we do not care about the turn
* between `v` and `b`. This calculation trims the coordinates to the ones immediately at the
* intersection.
*
* The optional length cache needs to store the accumulated distance up to the respective
* coordinate index [0,d(0,1),...]
*/
OSRM_ATTR_WARN_UNUSED
std::vector<util::Coordinate>
TrimCoordinatesToLength(std::vector<util::Coordinate> coordinates,
const double desired_length,
const std::vector<double> &length_cache = {}) const;
OSRM_ATTR_WARN_UNUSED
std::vector<double> PrepareLengthCache(const std::vector<util::Coordinate> &coordinates,
const double limit) const;
/* when looking at a set of coordinates, this function allows trimming the vector to a smaller,
* only containing coordinates up to a given distance along the path. The last coordinate might
* be interpolated
*/
OSRM_ATTR_WARN_UNUSED
std::vector<util::Coordinate>
TrimCoordinatesByLengthFront(std::vector<util::Coordinate> coordinates,
const double desired_length) const;
/*
* to correct for the initial offset, we move the lookahead coordinate close
* to the original road. We do so by subtracting the difference between the
* turn coordinate and the offset coordinate from the lookahead coordinge:
*
* a ------ b ------ c
* |
* d
* \
* \
* e
*
* is converted to:
*
* a ------ b ------ c
* \
* \
* e
*
* for fixpoint `b`, vector_base `d` and vector_head `e`
*/
OSRM_ATTR_WARN_UNUSED
util::Coordinate GetCorrectedCoordinate(const util::Coordinate fixpoint,
const util::Coordinate vector_base,
const util::Coordinate vector_head) const;
/* generate a uniform vector of coordinates in same range distances
*
* Turns:
* x ------------ x -- x - x
*
* Into:
* x -- x -- x -- x -- x - x
*/
OSRM_ATTR_WARN_UNUSED
std::vector<util::Coordinate>
SampleCoordinates(const std::vector<util::Coordinate> &coordinates,
const double length,
const double rate) const;
// find the coordinate at a specific distance in the vector
util::Coordinate
ExtractCoordinateAtLength(const double distance,
const std::vector<util::Coordinate> &coordinates) const;
util::Coordinate ExtractCoordinateAtLength(const double distance,
const std::vector<util::Coordinate> &coordinates,
const std::vector<double> &length_cache) const;
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const extractor::CompressedEdgeContainer &compressed_geometries;
const std::vector<util::Coordinate> &node_coordinates;
double ComputeInterpolationFactor(const double desired_distance,
const double distance_to_first,
const double distance_to_second) const;
std::pair<util::Coordinate, util::Coordinate>
RegressionLine(const std::vector<util::Coordinate> &coordinates) const;
/* In an ideal world, the road would only have two coordinates if it goes mainly straigt. Since
* OSM is operating on noisy data, we have some variations going straight.
*
* b d
* a ---------------------------------------------- e
* c
*
* The road from a-e offers a lot of variation, even though it is mostly straight. Here we
* calculate the distances of all nodes in between to the straight line between a and e. If the
* distances inbetween are small, we assume a straight road. To calculate these distances, we
* don't use the coordinates of the road itself but our just calculated regression vector
*/
double GetMaxDeviation(std::vector<util::Coordinate>::const_iterator range_begin,
const std::vector<util::Coordinate>::const_iterator &range_end,
const util::Coordinate straight_begin,
const util::Coordinate straight_end) const;
/*
* the curve is still best described as looking at the very first vector for the turn angle.
* Consider:
*
* |
* a - 1
* | o
* | 2
* | o
* | 3
* | o
* | 4
*
* The turn itself from a-1 would be considered as a 90 degree turn, even though the road is
* taking a turn later.
* In this situaiton we return the very first coordinate, describing the road just at the
* turn.
* As an added benefit, we get a straight turn at a curved road:
*
* o b o
* o o
* o o
* o o
* o o
* a c
*
* The turn from a-b to b-c is straight. With every vector we go further down the road, the
* turn
* angle would get stronger. Therefore we consider the very first coordinate as our best
* choice
*/
bool IsCurve(const std::vector<util::Coordinate> &coordinates,
const std::vector<double> &segment_distances,
const double segment_length,
const double considered_lane_width,
const extractor::NodeBasedEdgeClassification &edge_data) const;
/*
* If the very first coordinate is within lane offsets and the rest offers a near straight line,
* we use an offset coordinate.
*
* ----------------------------------------
*
* ----------------------------------------
* a -
* ----------------------------------------
* \
* ----------------------------------------
* \
* b --------------------c
*
* Will be considered a very slight turn, instead of the near 90 degree turn we see right here.
*/
bool IsDirectOffset(const std::vector<util::Coordinate> &coordinates,
const std::size_t straight_index,
const double straight_distance,
const double segment_length,
const std::vector<double> &segment_distances,
const std::uint8_t considered_lanes) const;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_COORDINATE_EXTRACTOR_HPP_
@@ -1,44 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_DRIVEWAY_HANDLER_HPP
#define OSRM_EXTRACTOR_GUIDANCE_DRIVEWAY_HANDLER_HPP
#include "extractor/guidance/intersection_handler.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
// Intersection handlers deal with all issues related to intersections.
class DrivewayHandler final : public IntersectionHandler
{
public:
DrivewayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
~DrivewayHandler() override final = default;
// check whether the handler can actually handle the intersection
bool canProcess(const NodeID nid,
const EdgeID via_eid,
const Intersection &intersection) const override final;
// process the intersection
Intersection operator()(const NodeID nid,
const EdgeID via_eid,
Intersection intersection) const override final;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /* OSRM_EXTRACTOR_GUIDANCE_DRIVEWAY_HANDLER_HPP */
@@ -1,27 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_HAVE_IDENTICAL_NAMES_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_HAVE_IDENTICAL_NAMES_HPP_
#include "extractor/guidance/constants.hpp"
#include "extractor/suffix_table.hpp"
#include "util/name_table.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
// check if two name ids can be seen as identical (in presence of refs/others)
// in our case this translates into no name announcement in either direction (lhs->rhs and
// rhs->lhs)
bool HaveIdenticalNames(const NameID lhs,
const NameID rhs,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_HAVE_IDENTICAL_NAMES_HPP_*/
-331
View File
@@ -1,331 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_
#include <algorithm>
#include <functional>
#include <limits>
#include <string>
#include <type_traits>
#include <vector>
#include "util/bearing.hpp"
#include "util/log.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp" // EdgeID
#include "extractor/guidance/turn_instruction.hpp"
#include <boost/range/algorithm/count_if.hpp>
#include <boost/range/algorithm/find_if.hpp>
#include <boost/range/algorithm/min_element.hpp>
namespace osrm
{
namespace extractor
{
namespace guidance
{
// the shape of an intersection only knows about edge IDs and bearings
// `bearing` is the direction in clockwise angle from true north after taking the turn:
// 0 = heading north, 90 = east, 180 = south, 270 = west
struct IntersectionShapeData
{
EdgeID eid;
double bearing;
double segment_length;
};
inline auto makeCompareShapeDataByBearing(const double base_bearing)
{
return [base_bearing](const auto &lhs, const auto &rhs) {
return util::angularDeviation(lhs.bearing, base_bearing) <
util::angularDeviation(rhs.bearing, base_bearing);
};
}
inline auto makeCompareAngularDeviation(const double angle)
{
return [angle](const auto &lhs, const auto &rhs) {
return util::angularDeviation(lhs.angle, angle) < util::angularDeviation(rhs.angle, angle);
};
}
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();
};
}
// 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;
};
// 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
// instructions themselves. An possible example can be described like this:
//
// 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
// turn-operation, but rather a name change.
//
// 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.
struct ConnectedRoad final : IntersectionViewData
{
ConnectedRoad(const IntersectionViewData &view,
const TurnInstruction instruction,
const LaneDataID lane_data_id)
: IntersectionViewData(view), instruction(instruction), lane_data_id(lane_data_id)
{
}
TurnInstruction instruction;
LaneDataID lane_data_id;
// 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;
};
// small helper function to print the content of a connected road
std::string toString(const IntersectionShapeData &shape);
std::string toString(const IntersectionViewData &view);
std::string toString(const ConnectedRoad &road);
// Intersections are sorted roads: [0] being the UTurn road, then from sharp right to sharp left.
// 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
{
return boost::range::find_if(*self(), [eid](const auto &road) { return road.eid == eid; });
}
// 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);
}
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>;
};
// 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);
return boost::range::min_element(*self(), comp);
}
// returns a non-const_interator
auto findClosestTurn(double angle)
{
auto comp = makeCompareAngularDeviation(angle);
return std::min_element(self()->begin(), self()->end(), comp);
}
/* 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
*/
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; };
return std::none_of(self()->begin() + 1, self()->end(), pred);
}
// Returns the number of roads we can enter at this intersection, respectively.
auto countEnterable() const
{
auto pred = [](const auto &road) { return road.entry_allowed; };
return boost::range::count_if(*self(), pred);
}
// Returns the number of roads we can not enter at this intersection, respectively.
auto countNonEnterable() const { return self()->size() - self()->countEnterable(); }
// 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());
const auto candidate =
boost::range::min_element(*self(), [angle, &filter](const auto &lhs, const auto &rhs) {
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;
}
// 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; });
}
private:
auto self() { return static_cast<Self *>(this); }
auto self() const { return static_cast<const Self *>(this); }
};
struct IntersectionView final : std::vector<IntersectionViewData>, //
EnableShapeOps<IntersectionView>, //
EnableIntersectionOps<IntersectionView> //
{
using Base = std::vector<IntersectionViewData>;
};
// `Intersection` is a relative view of an intersection by an incoming edge.
// `Intersection` are streets at an intersection stored as an ordered list of connected roads
// ordered 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 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
//
struct Intersection final : std::vector<ConnectedRoad>, //
EnableShapeOps<Intersection>, //
EnableIntersectionOps<Intersection> //
{
using Base = std::vector<ConnectedRoad>;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_*/
@@ -1,621 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/node_based_graph_walker.hpp"
#include "extractor/intersection/intersection_analysis.hpp"
#include "extractor/query_node.hpp"
#include "extractor/suffix_table.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/guidance/name_announcements.hpp"
#include "util/name_table.hpp"
#include "util/node_based_graph.hpp"
#include <algorithm>
#include <cstddef>
#include <utility>
#include <vector>
#include <boost/optional.hpp>
namespace osrm
{
namespace extractor
{
namespace guidance
{
// Intersection handlers deal with all issues related to intersections.
// This base class provides both the interface and implementations for
// common functions.
class IntersectionHandler
{
public:
IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
virtual ~IntersectionHandler() = default;
// check whether the handler can actually handle the intersection
virtual bool
canProcess(const NodeID nid, const EdgeID via_eid, const Intersection &intersection) const = 0;
// handle and process the intersection
virtual Intersection
operator()(const NodeID nid, const EdgeID via_eid, Intersection intersection) const = 0;
protected:
const util::NodeBasedDynamicGraph &node_based_graph;
const EdgeBasedNodeDataContainer &node_data_container;
const std::vector<util::Coordinate> &node_coordinates;
const extractor::CompressedEdgeContainer &compressed_geometries;
const RestrictionMap &node_restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const guidance::TurnLanesIndexedArray &turn_lanes_data;
const util::NameTable &name_table;
const SuffixTable &street_name_suffix_table;
const NodeBasedGraphWalker graph_walker; // for skipping traffic signal, distances etc.
// Decide on a basic turn types
TurnType::Enum findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
TurnType::Enum areSameClasses(const EdgeID via_edge, const ConnectedRoad &road) const;
// Find the most obvious turn to follow. The function returns an index into the intersection
// determining whether there is a road that can be seen as obvious turn in the presence of many
// other possible turns. The function will consider road categories and other inputs like the
// turn angles.
template <typename IntersectionType> // works with Intersection and IntersectionView
std::size_t findObviousTurn(const EdgeID via_edge, const IntersectionType &intersection) const;
// Obvious turns can still take multiple forms. This function looks at the turn onto a road
// candidate when coming from a via_edge and determines the best instruction to emit.
// `through_street` indicates if the street turned onto is a through sreet (think mergees and
// similar)
TurnInstruction getInstructionForObvious(const std::size_t number_of_candidates,
const EdgeID via_edge,
const bool through_street,
const ConnectedRoad &candidate) const;
// Treating potential forks
void assignFork(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
void assignFork(const EdgeID via_edge,
ConnectedRoad &left,
ConnectedRoad &center,
ConnectedRoad &right) const;
// Trivial Turns use findBasicTurnType and getTurnDirection as only criteria
void assignTrivialTurns(const EdgeID via_eid,
Intersection &intersection,
const std::size_t begin,
const std::size_t end) const;
// See `getNextIntersection`
struct IntersectionViewAndNode final
{
IntersectionView intersection; // < actual intersection
NodeID node; // < node at this intersection
};
// Skips over artificial intersections i.e. traffic lights, barriers etc.
// Returns the next non-artificial intersection and its node in the node based
// graph if an intersection could be found or none otherwise.
//
// a ... tl ... b .. c
// .
// .
// d
//
// ^ at
// ^ via
//
// For this scenario returns intersection at `b` and `b`.
boost::optional<IntersectionHandler::IntersectionViewAndNode>
getNextIntersection(const NodeID at, const EdgeID via) const;
bool isSameName(const EdgeID source_edge_id, const EdgeID target_edge_id) const;
};
// Impl.
template <typename IntersectionType> // works with Intersection and IntersectionView
std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
const IntersectionType &intersection) const
{
using Road = typename IntersectionType::value_type;
using osrm::util::angularDeviation;
// no obvious road
if (intersection.size() == 1)
return 0;
// a single non u-turn is obvious
if (intersection.size() == 2)
return 1;
const auto &in_way_edge = node_based_graph.GetEdgeData(via_edge);
const auto &in_way_data = node_data_container.GetAnnotation(in_way_edge.annotation_data);
// the strategy for picking the most obvious turn involves deciding between
// an overall best candidate and a best candidate that shares the same name
// as the in road, i.e. a continue road
std::size_t best_option = 0;
double best_option_deviation = 180;
std::size_t best_continue = 0;
double best_continue_deviation = 180;
/* helper functions */
const auto IsContinueRoad = [&](const NodeBasedEdgeAnnotation &way_data) {
return !util::guidance::requiresNameAnnounced(
in_way_data.name_id, way_data.name_id, name_table, street_name_suffix_table);
};
auto sameOrHigherPriority = [&](const auto &way_data) {
return way_data.flags.road_classification.GetPriority() <=
in_way_edge.flags.road_classification.GetPriority();
};
auto IsLowPriority = [](const auto &way_data) {
return way_data.flags.road_classification.IsLowPriorityRoadClass();
};
// These two Compare functions are used for sifting out best option and continue
// candidates by evaluating all the ways in an intersection by what they share
// with the in way. Ideal candidates are of similar road class with the in way
// and are require relatively straight turns.
const auto RoadCompare = [&](const auto &lhs, const auto &rhs) {
const auto &lhs_edge = node_based_graph.GetEdgeData(lhs.eid);
const auto &rhs_edge = node_based_graph.GetEdgeData(rhs.eid);
const auto lhs_deviation = angularDeviation(lhs.angle, STRAIGHT_ANGLE);
const auto rhs_deviation = angularDeviation(rhs.angle, STRAIGHT_ANGLE);
const bool rhs_same_classification =
rhs_edge.flags.road_classification == in_way_edge.flags.road_classification;
const bool lhs_same_classification =
lhs_edge.flags.road_classification == in_way_edge.flags.road_classification;
const bool rhs_same_or_higher_priority = sameOrHigherPriority(rhs_edge);
const bool rhs_low_priority = IsLowPriority(rhs_edge);
const bool lhs_same_or_higher_priority = sameOrHigherPriority(lhs_edge);
const bool lhs_low_priority = IsLowPriority(lhs_edge);
auto left_tie = std::tie(lhs.entry_allowed,
lhs_same_or_higher_priority,
rhs_low_priority,
rhs_deviation,
lhs_same_classification);
auto right_tie = std::tie(rhs.entry_allowed,
rhs_same_or_higher_priority,
lhs_low_priority,
lhs_deviation,
rhs_same_classification);
return left_tie > right_tie;
};
const auto RoadCompareSameName = [&](const auto &lhs, const auto &rhs) {
const auto &lhs_data = node_data_container.GetAnnotation(
node_based_graph.GetEdgeData(lhs.eid).annotation_data);
const auto &rhs_data = node_data_container.GetAnnotation(
node_based_graph.GetEdgeData(rhs.eid).annotation_data);
const auto lhs_continues = IsContinueRoad(lhs_data);
const auto rhs_continues = IsContinueRoad(rhs_data);
const auto left_tie = std::tie(lhs.entry_allowed, lhs_continues);
const auto right_tie = std::tie(rhs.entry_allowed, rhs_continues);
return left_tie > right_tie || (left_tie == right_tie && RoadCompare(lhs, rhs));
};
auto best_option_it = std::min_element(begin(intersection), end(intersection), RoadCompare);
// min element should only return end() when vector is empty
BOOST_ASSERT(best_option_it != end(intersection));
best_option = std::distance(begin(intersection), best_option_it);
best_option_deviation = angularDeviation(intersection[best_option].angle, STRAIGHT_ANGLE);
const auto &best_option_edge = node_based_graph.GetEdgeData(intersection[best_option].eid);
const auto &best_option_data =
node_data_container.GetAnnotation(best_option_edge.annotation_data);
// Unless the in way is also low priority, it is generally undesirable to
// indicate that a low priority road is obvious
if (IsLowPriority(best_option_edge) &&
best_option_edge.flags.road_classification != in_way_edge.flags.road_classification)
{
best_option = 0;
best_option_deviation = 180;
}
// double check if the way with the lowest deviation from straight is still be better choice
const auto straightest = intersection.findClosestTurn(STRAIGHT_ANGLE);
if (straightest != best_option_it)
{
const auto &straightest_edge = node_based_graph.GetEdgeData(straightest->eid);
double straightest_data_deviation = angularDeviation(straightest->angle, STRAIGHT_ANGLE);
const auto deviation_diff =
std::abs(best_option_deviation - straightest_data_deviation) > FUZZY_ANGLE_DIFFERENCE;
const auto not_ramp_class = !straightest_edge.flags.road_classification.IsRampClass();
const auto not_link_class = !straightest_edge.flags.road_classification.IsLinkClass();
if (deviation_diff && !IsLowPriority(straightest_edge) && not_ramp_class &&
not_link_class && !IsContinueRoad(best_option_data))
{
best_option = std::distance(begin(intersection), straightest);
best_option_deviation =
angularDeviation(intersection[best_option].angle, STRAIGHT_ANGLE);
}
}
// No non-low priority roads? Declare no obvious turn
if (best_option == 0)
return 0;
auto best_continue_it =
std::min_element(begin(intersection), end(intersection), RoadCompareSameName);
const auto best_continue_edge = node_based_graph.GetEdgeData(best_continue_it->eid);
const auto best_continue_data =
node_data_container.GetAnnotation(best_continue_edge.annotation_data);
if (IsContinueRoad(best_continue_data) ||
(in_way_data.name_id == EMPTY_NAMEID && best_continue_data.name_id == EMPTY_NAMEID))
{
best_continue = std::distance(begin(intersection), best_continue_it);
best_continue_deviation =
angularDeviation(intersection[best_continue].angle, STRAIGHT_ANGLE);
}
// if the best angle is going straight but the road is turning, declare no obvious turn
if (0 != best_continue && best_option != best_continue &&
best_option_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
best_continue_edge.flags.road_classification == best_option_edge.flags.road_classification)
{
return 0;
}
// get a count of number of ways from that intersection that qualify to have
// continue instruction because they share a name with the approaching way
const std::int64_t continue_count =
count_if(++begin(intersection), end(intersection), [&](const auto &way) {
return IsContinueRoad(node_data_container.GetAnnotation(
node_based_graph.GetEdgeData(way.eid).annotation_data));
});
const std::int64_t continue_count_valid =
count_if(++begin(intersection), end(intersection), [&](const auto &way) {
return IsContinueRoad(node_data_container.GetAnnotation(
node_based_graph.GetEdgeData(way.eid).annotation_data)) &&
way.entry_allowed;
});
// checks if continue candidates are sharp turns
const bool all_continues_are_narrow = [&]() {
return std::count_if(begin(intersection), end(intersection), [&](const Road &road) {
const auto &road_data = node_data_container.GetAnnotation(
node_based_graph.GetEdgeData(road.eid).annotation_data);
const double &road_angle = angularDeviation(road.angle, STRAIGHT_ANGLE);
return IsContinueRoad(road_data) && (road_angle < NARROW_TURN_ANGLE);
}) == continue_count;
}();
// return true if the best_option candidate is more promising than the best_continue candidate
// otherwise return false, the best_continue candidate is more promising
const auto best_over_best_continue = [&]() {
// no continue road exists
if (best_continue == 0)
return true;
// we have multiple continues and not all are narrow. This suggests that
// the continue candidates are ambiguous
if (!all_continues_are_narrow && (continue_count >= 2 && intersection.size() >= 4))
return true;
// if the best continue is not narrow and we also have at least 2 possible choices, the
// intersection size does not matter anymore
if (continue_count_valid >= 2 && best_continue_deviation >= 2 * NARROW_TURN_ANGLE)
return true;
// continue data now most certainly exists
const auto &continue_edge = node_based_graph.GetEdgeData(intersection[best_continue].eid);
// best_continue is obvious by road class
if (obviousByRoadClass(in_way_edge.flags.road_classification,
continue_edge.flags.road_classification,
best_option_edge.flags.road_classification))
return false;
// best_option is obvious by road class
if (obviousByRoadClass(in_way_edge.flags.road_classification,
best_option_edge.flags.road_classification,
continue_edge.flags.road_classification))
return true;
// the best_option deviation is very straight and not a ramp
if (best_option_deviation < best_continue_deviation &&
best_option_deviation < FUZZY_ANGLE_DIFFERENCE &&
!best_option_edge.flags.road_classification.IsRampClass())
return true;
// the continue road is of a lower priority, while the road continues on the same priority
// with a better angle
if (best_option_deviation < best_continue_deviation &&
in_way_edge.flags.road_classification == best_option_edge.flags.road_classification &&
continue_edge.flags.road_classification.GetPriority() >
best_option_edge.flags.road_classification.GetPriority())
return true;
return false;
}();
// check whether we turn onto a oneway through street. These typically happen at the end of
// roads and might not seem obvious, since it isn't always as visible that you cannot turn
// left/right. To be on the safe side, we announce these as non-obvious
const auto turns_onto_through_street = [&](const auto &road) {
// find edge opposite to the one we are checking (in-road)
const auto in_through_candidate =
intersection.FindClosestBearing(util::bearing::reverse(road.bearing));
const auto &in_edge = node_based_graph.GetEdgeData(in_through_candidate->eid);
const auto &out_edge = node_based_graph.GetEdgeData(road.eid);
// by asking for the same class, we ensure that we do not overrule obvious by road-class
// decisions
const auto same_class =
in_edge.flags.road_classification == out_edge.flags.road_classification;
// only if the entry is allowed for one of the two, but not the other, we need to check.
// Otherwise other handlers do it better
const bool is_oneway = !in_through_candidate->entry_allowed && road.entry_allowed;
const bool not_roundabout = !(in_edge.flags.roundabout || in_edge.flags.circular ||
out_edge.flags.roundabout || out_edge.flags.circular);
// for the purpose of this check, we do not care about low-priority roads (parking lots,
// mostly). Since we postulate both classes to be the same, checking one of the two is
// enough
const bool not_low_priority = !in_edge.flags.road_classification.IsLowPriorityRoadClass();
const auto in_deviation = angularDeviation(in_through_candidate->angle, STRAIGHT_ANGLE);
const auto out_deviaiton = angularDeviation(road.angle, STRAIGHT_ANGLE);
// in case the deviation isn't considerably lower for the road we are turning onto,
// consider it non-obvious. The threshold here requires a slight (60) vs sharp (120)
// degree variation, at lest (120/60 == 2)
return is_oneway && same_class && not_roundabout && not_low_priority &&
(in_deviation / (std::max(out_deviaiton, 0.5)) <= 2);
};
if (best_over_best_continue)
{
// Find left/right deviation
// skipping over service roads
const std::size_t left_index = [&]() {
const auto index_candidate = (best_option + 1) % intersection.size();
if (index_candidate == 0)
return index_candidate;
const auto &candidate_edge =
node_based_graph.GetEdgeData(intersection[index_candidate].eid);
if (obviousByRoadClass(in_way_edge.flags.road_classification,
best_option_edge.flags.road_classification,
candidate_edge.flags.road_classification))
return (index_candidate + 1) % intersection.size();
else
return index_candidate;
}();
const auto right_index = [&]() {
BOOST_ASSERT(best_option > 0);
const auto index_candidate = best_option - 1;
if (index_candidate == 0)
return index_candidate;
const auto &candidate_edge =
node_based_graph.GetEdgeData(intersection[index_candidate].eid);
if (obviousByRoadClass(in_way_edge.flags.road_classification,
best_option_edge.flags.road_classification,
candidate_edge.flags.road_classification))
return index_candidate - 1;
else
return index_candidate;
}();
const double left_deviation =
angularDeviation(intersection[left_index].angle, STRAIGHT_ANGLE);
const double right_deviation =
angularDeviation(intersection[right_index].angle, STRAIGHT_ANGLE);
// return best_option candidate if it is nearly straight and distinct from the nearest other
// out way
if (best_option_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
std::min(left_deviation, right_deviation) > FUZZY_ANGLE_DIFFERENCE)
return best_option;
const auto &left_edge = node_based_graph.GetEdgeData(intersection[left_index].eid);
const auto &right_edge = node_based_graph.GetEdgeData(intersection[right_index].eid);
const bool obvious_to_left =
left_index == 0 || obviousByRoadClass(in_way_edge.flags.road_classification,
best_option_edge.flags.road_classification,
left_edge.flags.road_classification);
const bool obvious_to_right =
right_index == 0 || obviousByRoadClass(in_way_edge.flags.road_classification,
best_option_edge.flags.road_classification,
right_edge.flags.road_classification);
// if the best_option turn isn't narrow, but there is a nearly straight turn, we don't
// consider the turn obvious
const auto check_narrow = [&intersection, best_option_deviation](const std::size_t index) {
return angularDeviation(intersection[index].angle, STRAIGHT_ANGLE) <=
FUZZY_ANGLE_DIFFERENCE &&
(best_option_deviation > NARROW_TURN_ANGLE || intersection[index].entry_allowed);
};
// other narrow turns?
if (check_narrow(right_index) && !obvious_to_right)
return 0;
if (check_narrow(left_index) && !obvious_to_left)
return 0;
// we are turning onto a through street (possibly at the end of the road). Ensure that we
// announce a turn, if it isn't a slight merge
if (turns_onto_through_street(intersection[best_option]))
return 0;
// checks if a given way in the intersection is distinct enough from the best_option
// candidate
const auto isDistinct = [&](const std::size_t index, const double deviation) {
/*
If the neighbor is not possible to enter, we allow for a lower
distinction rate. If the road category is smaller, its also adjusted. Only
roads of the same priority require the full distinction ratio.
*/
const auto &best_option_edge =
node_based_graph.GetEdgeData(intersection[best_option].eid);
const auto adjusted_distinction_ratio = [&]() {
// obviousness by road classes
if (in_way_edge.flags.road_classification ==
best_option_edge.flags.road_classification &&
best_option_edge.flags.road_classification.GetPriority() <
node_based_graph.GetEdgeData(intersection[index].eid)
.flags.road_classification.GetPriority())
return 0.8 * DISTINCTION_RATIO;
// if road classes are the same, we use the full ratio
else
return DISTINCTION_RATIO;
}();
return index == 0 || deviation / best_option_deviation >= adjusted_distinction_ratio ||
(deviation <= NARROW_TURN_ANGLE && !intersection[index].entry_allowed);
};
const bool distinct_to_left = isDistinct(left_index, left_deviation);
const bool distinct_to_right = isDistinct(right_index, right_deviation);
// Well distinct turn that is nearly straight
if ((distinct_to_left || obvious_to_left) && (distinct_to_right || obvious_to_right))
return best_option;
}
else
{
const auto &continue_edge = node_based_graph.GetEdgeData(intersection[best_continue].eid);
const auto &continue_data =
node_data_container.GetAnnotation(continue_edge.annotation_data);
if (std::abs(best_continue_deviation) < 1)
return best_continue;
// we are turning onto a through street (possibly at the end of the road). Ensure that we
// announce a turn, if it isn't a slight merge
if (turns_onto_through_street(intersection[best_continue]))
return 0;
// check if any other similar best continues exist
std::size_t i, last = intersection.size();
for (i = 1; i < last; ++i)
{
if (i == best_continue || !intersection[i].entry_allowed)
continue;
const auto &turn_edge = node_based_graph.GetEdgeData(intersection[i].eid);
const auto &turn_data = node_data_container.GetAnnotation(turn_edge.annotation_data);
const bool is_obvious_by_road_class =
obviousByRoadClass(in_way_edge.flags.road_classification,
continue_edge.flags.road_classification,
turn_edge.flags.road_classification);
// if the main road is obvious by class, we ignore the current road as a potential
// prevention of obviousness
if (is_obvious_by_road_class)
continue;
// continuation could be grouped with a straight turn and the turning road is a ramp
if (turn_edge.flags.road_classification.IsRampClass() &&
best_continue_deviation < GROUP_ANGLE &&
!continue_edge.flags.road_classification.IsRampClass())
continue;
// perfectly straight turns prevent obviousness
const auto turn_deviation = angularDeviation(intersection[i].angle, STRAIGHT_ANGLE);
if (turn_deviation < FUZZY_ANGLE_DIFFERENCE)
return 0;
const auto deviation_ratio = turn_deviation / best_continue_deviation;
// in comparison to normal deviations, a continue road can offer a smaller distinction
// ratio. Other roads close to the turn angle are not as obvious, if one road continues.
if (deviation_ratio < DISTINCTION_RATIO / 1.5)
return 0;
/* in comparison to another continuing road, we need a better distinction. This prevents
situations where the turn is probably less obvious. An example are places that have a
road with the same name entering/exiting:
d
/
/
a -- b
\
\
c
*/
const auto same_name = !util::guidance::requiresNameAnnounced(
turn_data.name_id, continue_data.name_id, name_table, street_name_suffix_table);
if (same_name && deviation_ratio < 1.5 * DISTINCTION_RATIO)
return 0;
}
// Segregated intersections can result in us finding an obvious turn, even though its only
// obvious due to a very short segment in between. So if the segment coming in is very
// short, we check the previous intersection for other continues in the opposite bearing.
const auto node_at_intersection = node_based_graph.GetTarget(via_edge);
const double constexpr MAX_COLLAPSE_DISTANCE = 30;
const auto distance_at_u_turn = intersection[0].segment_length;
if (distance_at_u_turn < MAX_COLLAPSE_DISTANCE)
{
// this request here actually goes against the direction of the ingoing edgeid. This can
// even reverse the direction. Since we don't want to compute actual turns but simply
// try to find whether there is a turn going to the opposite direction of our obvious
// turn, this should be alright.
const auto previous_intersection = [&]() -> IntersectionView {
const auto parameters = intersection::skipDegreeTwoNodes(
node_based_graph, {node_at_intersection, intersection[0].eid});
if (node_based_graph.GetTarget(parameters.edge) == node_at_intersection)
return {};
return intersection::getConnectedRoads<false>(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
parameters);
}();
if (!previous_intersection.empty())
{
const auto continue_road = intersection[best_continue];
for (const auto &comparison_road : previous_intersection)
{
// since we look at the intersection in the wrong direction, a similar angle
// actually represents a near 180 degree different in bearings between the two
// roads. So if there is a road that is enterable in the opposite direction just
// prior, a turn is not obvious
const auto &turn_edge_data = node_based_graph.GetEdgeData(comparison_road.eid);
const auto &turn_data =
node_data_container.GetAnnotation(turn_edge_data.annotation_data);
if (angularDeviation(comparison_road.angle, STRAIGHT_ANGLE) > GROUP_ANGLE &&
angularDeviation(comparison_road.angle, continue_road.angle) <
FUZZY_ANGLE_DIFFERENCE &&
!turn_edge_data.reversed && continue_data.CanCombineWith(turn_data))
return 0;
}
}
}
return best_continue;
}
return 0;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_*/
@@ -1,63 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_IS_THROUGH_STREET_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_IS_THROUGH_STREET_HPP_
#include "extractor/guidance/constants.hpp"
#include "extractor/suffix_table.hpp"
#include "util/guidance/name_announcements.hpp"
using osrm::util::angularDeviation;
namespace osrm
{
namespace extractor
{
namespace guidance
{
template <typename IntersectionType>
inline bool isThroughStreet(const std::size_t index,
const IntersectionType &intersection,
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
{
const auto &data_at_index = node_data_container.GetAnnotation(
node_based_graph.GetEdgeData(intersection[index].eid).annotation_data);
if (data_at_index.name_id == EMPTY_NAMEID)
return false;
// a through street cannot start at our own position -> index 1
for (std::size_t road_index = 1; road_index < intersection.size(); ++road_index)
{
if (road_index == index)
continue;
const auto &road = intersection[road_index];
const auto &road_data = node_data_container.GetAnnotation(
node_based_graph.GetEdgeData(road.eid).annotation_data);
// roads have a near straight angle (180 degree)
const bool is_nearly_straight = angularDeviation(road.angle, intersection[index].angle) >
(STRAIGHT_ANGLE - FUZZY_ANGLE_DIFFERENCE);
const bool have_same_name = HaveIdenticalNames(
data_at_index.name_id, road_data.name_id, name_table, street_name_suffix_table);
const bool have_same_category =
node_based_graph.GetEdgeData(intersection[index].eid).flags.road_classification ==
node_based_graph.GetEdgeData(road.eid).flags.road_classification;
if (is_nearly_straight && have_same_name && have_same_category)
return true;
}
return false;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_IS_THROUGH_STREET_HPP_*/
@@ -1,184 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_MERGEABLE_ROADS
#define OSRM_EXTRACTOR_GUIDANCE_MERGEABLE_ROADS
#include "extractor/compressed_edge_container.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/guidance/have_identical_names.hpp"
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/turn_lane_types.hpp"
#include "extractor/restriction_index.hpp"
#include "util/coordinate.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
#include <cstdint>
#include <functional>
#include <limits>
#include <unordered_set>
#include <vector>
namespace osrm
{
// FWD declarations
namespace util
{
class NameTable;
} // namespace util
namespace extractor
{
class SuffixTable;
namespace guidance
{
class IntersectionGenerator;
class CoordinateExtractor;
class MergableRoadDetector
{
public:
// in case we have to change the mode we are operating on
using MergableRoadData = IntersectionShapeData;
MergableRoadDetector(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
// OSM ways tend to be modelled as separate ways for different directions. This is often due to
// small gras strips in the middle between the two directions or due to pedestrian islands at
// intersections.
//
// To reduce unnecessary information due to these artificial intersections (which are not
// actually perceived as such) we try and merge these for our internal representation to both
// get better perceived turn angles and get a better reprsentation of our intersections in
// general.
//
// i h i,h
// | | |
// | | |
// b - - - v - - - g |
// > a < is transformed into: b,c - - - a - - - g,f
// c - - - ^ - - - f |
// | | |
// | | |
// d e d,e
bool CanMergeRoad(const NodeID intersection_node,
const MergableRoadData &lhs,
const MergableRoadData &rhs) const;
// check if a road cannot influence the merging of the other. This is necessary to prevent
// situations with more than two roads that could participate in a merge
bool IsDistinctFrom(const MergableRoadData &lhs, const MergableRoadData &rhs) const;
private:
// When it comes to merging roads, we need to find out if two ways actually represent the
// same road. This check tries to identify roads which are the same road in opposite directions
bool EdgeDataSupportsMerge(const NodeBasedEdgeClassification &lhs_flags,
const NodeBasedEdgeClassification &rhs_flags,
const NodeBasedEdgeAnnotation &lhs_edge_annotation,
const NodeBasedEdgeAnnotation &rhs_edge_annotation) const;
// Detect traffic loops.
// Since OSRM cannot handle loop edges, we cannot directly see a connection between a node and
// itself. We need to skip at least a single node in between.
bool IsTrafficLoop(const NodeID intersection_node, const MergableRoadData &road) const;
// Detector to check if we are looking at roads splitting up just prior to entering an
// intersection:
//
// c
// / |
// a -< |
// \ |
// b
//
// A common scheme in OSRM is that roads spit up in separate ways when approaching an
// intersection. This detector tries to detect these narrow triangles which usually just offer a
// small island for pedestrians in the middle.
bool IsNarrowTriangle(const NodeID intersection_node,
const MergableRoadData &lhs,
const MergableRoadData &rhs) const;
// Detector to check for whether two roads are following the same direction.
// If roads don't end up right at a connected intersection, we could look at a situation like
//
// __________________________
// /
// ---
// \__________________________
//
// This detector tries to find out about whether two roads are parallel after the separation
bool HaveSameDirection(const NodeID intersection_node,
const MergableRoadData &lhs,
const MergableRoadData &rhs) const;
// Detector for small traffic islands. If a road is splitting up, just to connect again later,
// we don't wan't to have this information within our list of intersections/possible turn
// locations.
//
// ___________
// ---<___________>-----
//
//
// Would feel just like a single straight road to a driver and should be represented as such in
// our engine
bool IsTrafficIsland(const NodeID intersection_node,
const MergableRoadData &lhs,
const MergableRoadData &rhs) const;
// A negative detector, preventing a merge, trying to detect link roads between two main roads.
//
// d - - - - - - - - e - f
// . / '
// a - - - b - - - - - - c
//
// The detector wants to prevent merges that are connected to `b-e`
bool IsLinkRoad(const NodeID intersection_node, const MergableRoadData &road) const;
// The condition suppresses roads merging for intersections like
// . .
// . .
// ---- ----
// . .
// . .
// but will allow roads merging for intersections like
// -------
// / \ 
// ---- ----
// \ /
// -------
bool IsCircularShape(const NodeID intersection_node,
const MergableRoadData &lhs,
const MergableRoadData &rhs) const;
const util::NodeBasedDynamicGraph &node_based_graph;
const EdgeBasedNodeDataContainer &node_data_container;
const std::vector<util::Coordinate> &node_coordinates;
const extractor::CompressedEdgeContainer &compressed_geometries;
const RestrictionMap &node_restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const guidance::TurnLanesIndexedArray &turn_lanes_data;
// name detection
const util::NameTable &name_table;
const SuffixTable &street_name_suffix_table;
const CoordinateExtractor coordinate_extractor;
// limit for detecting circles / parallel roads
const static double constexpr distance_to_extract = 120;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif
@@ -1,67 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/is_through_street.hpp"
#include "extractor/query_node.hpp"
#include "util/attributes.hpp"
#include "util/name_table.hpp"
#include "util/node_based_graph.hpp"
#include <vector>
namespace osrm
{
namespace extractor
{
namespace guidance
{
// Intersection handlers deal with all issues related to intersections.
class MotorwayHandler : public IntersectionHandler
{
public:
MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
~MotorwayHandler() override final = default;
// check whether the handler can actually handle the intersection
bool canProcess(const NodeID nid,
const EdgeID via_eid,
const Intersection &intersection) const override final;
// process the intersection
Intersection operator()(const NodeID nid,
const EdgeID via_eid,
Intersection intersection) const override final;
private:
OSRM_ATTR_WARN_UNUSED
Intersection handleSliproads(const NodeID intersection_node_id,
Intersection intersection) const;
OSRM_ATTR_WARN_UNUSED
Intersection fromMotorway(const EdgeID via_edge, Intersection intersection) const;
OSRM_ATTR_WARN_UNUSED
Intersection fromRamp(const EdgeID via_edge, Intersection intersection) const;
OSRM_ATTR_WARN_UNUSED
Intersection fallback(Intersection intersection) const;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_*/
@@ -1,318 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_NODE_BASED_GRAPH_WALKER
#define OSRM_EXTRACTOR_GUIDANCE_NODE_BASED_GRAPH_WALKER
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/guidance/turn_lane_data.hpp"
#include "extractor/intersection/intersection_analysis.hpp"
#include "util/coordinate.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
#include <boost/assert.hpp>
#include <boost/optional.hpp>
#include <cstdint>
#include <utility>
namespace osrm
{
namespace extractor
{
namespace guidance
{
/*
* The graph hopper is a utility that lets you find certain intersections with a node based graph,
* accumulating information along the way
*/
class NodeBasedGraphWalker
{
public:
NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data);
/*
* the returned node-id, edge-id are either the last ones used, just prior accumulator
* terminating or empty if the traversal ran into a dead end. For examples of the
* selector/accumulator look below. You can find an example for both (and the required interface
* description). The function returns the last used `NodeID` and `EdgeID` (node just prior to
* the last intersection and the edge it was reached by), if it wasn't stopped early (e.g. the
* selector not provinding any further edge to traverse)
*/
template <class accumulator_type, class selector_type>
boost::optional<std::pair<NodeID, EdgeID>> TraverseRoad(NodeID starting_at_node_id,
EdgeID following_edge_id,
accumulator_type &accumulator,
const selector_type &selector) const;
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const EdgeBasedNodeDataContainer &node_data_container;
const std::vector<util::Coordinate> &node_coordinates;
const extractor::CompressedEdgeContainer &compressed_geometries;
const RestrictionMap &node_restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const guidance::TurnLanesIndexedArray &turn_lanes_data;
};
/*
* Accumulate all coordinates following a road until we
* Example of a possible accumulator for walking a node-based graph
*/
struct LengthLimitedCoordinateAccumulator
{
LengthLimitedCoordinateAccumulator(
const extractor::guidance::CoordinateExtractor &coordinate_extractor,
const double max_length);
/*
* !! REQUIRED - Function for the use of TraverseRoad in the graph walker.
* Terminate should return true if the last intersection given to accumulator is supposed to
* stop the search. A typical example would be to find the next intersection with degree larger
* than 2 (an actual intersection). Here you should return true if the last intersection you
* looked at was of degree larger than 2.
*/
bool terminate(); // true if the path has traversed enough distance
/*
* !! REQUIRED - Function for the use of TraverseRoad in the graph walker.
* starting with the very first provided node and edge, the graph walker will call `update` on
* your accumulator. Here you can choose to accumulate any data that you might want to collect /
* update your termination criteria. The accumulator described here will extract the coordinates
* that we see traversing `via_edge` and store them for later usage.
*/
void update(const NodeID from_node, const EdgeID via_edge, const NodeID to_node);
double accumulated_length = 0;
std::vector<util::Coordinate> coordinates;
private:
const extractor::guidance::CoordinateExtractor &coordinate_extractor;
const double max_length;
};
/*
* The SelectRoadByNameOnlyChoiceAndStraightness tries to follow a given name along a route. We
* offer methods to skip
* over bridges/similar situations if desired, following narrow turns
* This struct offers an example implementation of a possible road selector for traversing the
* node-based graph using the NodeBasedGraphWalker
*/
struct SelectRoadByNameOnlyChoiceAndStraightness
{
SelectRoadByNameOnlyChoiceAndStraightness(const NameID desired_name_id,
const bool requires_entry);
/*
* !! REQUIRED - Function for the use of TraverseRoad in the graph walker.
* The operator() needs to return (if any is found) the next road to continue in the graph
* traversal. If no such edge is found, return {} is allowed. Usually you want to choose some
* form of obious turn to follow.
*/
boost::optional<EdgeID> operator()(const NodeID nid,
const EdgeID via_edge_id,
const IntersectionView &intersection,
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container) const;
private:
const NameID desired_name_id;
const bool requires_entry;
};
/* Following only a straight road
* Follow only the straightmost turn, as long as its the only choice or has the desired name
*/
struct SelectStraightmostRoadByNameAndOnlyChoice
{
SelectStraightmostRoadByNameAndOnlyChoice(const NameID desired_name_id,
const double initial_bearing,
const bool requires_entry,
const bool stop_on_ambiguous_turns);
/*
* !! REQUIRED - Function for the use of TraverseRoad in the graph walker.
* The operator() needs to return (if any is found) the next road to continue in the graph
* traversal. If no such edge is found, return {} is allowed. Usually you want to choose some
* form of obious turn to follow.
*/
boost::optional<EdgeID> operator()(const NodeID nid,
const EdgeID via_edge_id,
const IntersectionView &intersection,
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container) const;
private:
const NameID desired_name_id;
const double initial_bearing;
const bool requires_entry;
const bool stop_on_ambiguous_turns;
};
// find the next intersection given a hop limit
struct IntersectionFinderAccumulator
{
IntersectionFinderAccumulator(const std::uint8_t hop_limit,
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data);
// true if the path has traversed enough distance
bool terminate();
// update the accumulator
void update(const NodeID from_node, const EdgeID via_edge, const NodeID to_node);
std::uint8_t hops;
const std::uint8_t hop_limit;
// the result we are looking for
NodeID nid;
EdgeID via_edge_id;
IntersectionView intersection;
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const EdgeBasedNodeDataContainer &node_data_container;
const std::vector<util::Coordinate> &node_coordinates;
const extractor::CompressedEdgeContainer &compressed_geometries;
const RestrictionMap &node_restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const guidance::TurnLanesIndexedArray &turn_lanes_data;
};
template <class accumulator_type, class selector_type>
boost::optional<std::pair<NodeID, EdgeID>>
NodeBasedGraphWalker::TraverseRoad(NodeID current_node_id,
EdgeID current_edge_id,
accumulator_type &accumulator,
const selector_type &selector) const
{
/*
* since graph hopping is used in many ways, we don't generate an adjusted intersection
* (otherwise we could end up in infinite recursion if we call the graph hopper during the
* adjustment itself). Relying only on `GetConnectedRoads` (which itself does no graph hopping),
* we prevent this from happening.
*/
const auto stop_node_id = current_node_id;
/* we wan't to put out the last valid entries. To do so, we need to update within the following
* loop. We use a for loop since traversal of the node-based-graph is expensive and we don't
* want to look at many coordinates. If you require more than 2/3 intersections down the road,
* you are doing something wrong/unsupported by OSRM. To not fail hard in cases that offer
* strange loop contractions, we restrict ourselves to an extremely large number of possible
* steps and simply warn in cases were extraction runs into these limits.
*/
for (std::size_t safety_hop_limit = 0; safety_hop_limit < 1000; ++safety_hop_limit)
{
accumulator.update(
current_node_id, current_edge_id, node_based_graph.GetTarget(current_edge_id));
// we have looped back to our initial intersection
if (node_based_graph.GetTarget(current_edge_id) == stop_node_id)
return {};
// look at the next intersection
const auto next_intersection =
intersection::getConnectedRoads<true>(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
{current_node_id, current_edge_id});
// don't follow u-turns or go past our initial intersection
if (next_intersection.size() <= 1)
return {};
auto next_edge_id = selector(current_node_id,
current_edge_id,
next_intersection,
node_based_graph,
node_data_container);
if (!next_edge_id)
return {};
if (accumulator.terminate())
return {std::make_pair(current_node_id, current_edge_id)};
current_node_id = node_based_graph.GetTarget(current_edge_id);
current_edge_id = *next_edge_id;
}
BOOST_ASSERT(
"Reached safety hop limit. Graph hopper seems to have been caught in an endless loop");
return {};
}
struct SkipTrafficSignalBarrierRoadSelector
{
boost::optional<EdgeID> operator()(const NodeID,
const EdgeID,
const IntersectionView &intersection,
const util::NodeBasedDynamicGraph &,
const EdgeBasedNodeDataContainer &) const
{
if (intersection.isTrafficSignalOrBarrier())
{
return boost::make_optional(intersection[1].eid);
}
else
{
return boost::none;
}
}
};
struct DistanceToNextIntersectionAccumulator
{
DistanceToNextIntersectionAccumulator(
const extractor::guidance::CoordinateExtractor &extractor_,
const util::NodeBasedDynamicGraph &graph_,
const double threshold)
: extractor{extractor_}, graph{graph_}, threshold{threshold}
{
}
bool terminate()
{
if (distance > threshold)
{
too_far_away = true;
return true;
}
return false;
}
void update(const NodeID start, const EdgeID onto, const NodeID)
{
using namespace util::coordinate_calculation;
const auto coords = extractor.GetForwardCoordinatesAlongRoad(start, onto);
distance += getLength(coords.begin(), coords.end(), &haversineDistance);
}
const extractor::guidance::CoordinateExtractor &extractor;
const util::NodeBasedDynamicGraph &graph;
const double threshold;
bool too_far_away = false;
double distance = 0.;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /* OSRM_EXTRACTOR_GUIDANCE_NODE_BASED_GRAPH_WALKER */
@@ -1,112 +0,0 @@
#ifndef OSRM_GUIDANCE_PARSING_TOOLKIT_HPP_
#define OSRM_GUIDANCE_PARSING_TOOLKIT_HPP_
#include <cstdint>
#include <string>
#include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>
#include "util/attributes.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
// 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
OSRM_ATTR_WARN_UNUSED
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
if (lane_string[i] != '|')
{
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)
{
if (*itr != '|')
{
sane = false;
break;
}
}
if (sane)
lane_string.resize(lane_string.size() - count_right);
}
return lane_string;
}
// 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
OSRM_ATTR_WARN_UNUSED
inline std::string applyAccessTokens(std::string lane_string, const std::string &access_tokens)
{
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;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_GUIDANCE_PARSING_TOOLKIT_HPP_
@@ -1,149 +0,0 @@
#ifndef OSRM_EXTRACTOR_CLASSIFICATION_DATA_HPP_
#define OSRM_EXTRACTOR_CLASSIFICATION_DATA_HPP_
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <string>
#include "extractor/guidance/constants.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
// Priorities are used to distinguish between how likely a turn is in comparison to a different
// road. The priorities here are used to distinguish between obvious turns (e.g. following a primary
// road next to a residential one is obvious). The decision what is obvious is described in the
// guidance constants.
namespace RoadPriorityClass
{
typedef std::uint8_t Enum;
// Top priority Road
const constexpr Enum MOTORWAY = 0;
// Second highest priority
const constexpr Enum TRUNK = 2;
// Main roads
const constexpr Enum PRIMARY = 4;
const constexpr Enum SECONDARY = 6;
const constexpr Enum TERTIARY = 8;
// Residential Categories
const constexpr Enum MAIN_RESIDENTIAL = 10;
const constexpr Enum SIDE_RESIDENTIAL = 11;
// Link Category
const constexpr Enum LINK_ROAD = 14;
// Bike Accessible
const constexpr Enum BIKE_PATH = 16;
// Walk Accessible
const constexpr Enum FOOT_PATH = 18;
// Link types are usually not considered in forks, unless amongst each other.
// a road simply offered for connectivity. Will be ignored in forks/other decisions. Always
// considered non-obvious to continue on
const constexpr Enum CONNECTIVITY = 31;
} // namespace Road Class
#pragma pack(push, 1)
class RoadClassification
{
// a class that behaves like a motorway (separated directions)
std::uint8_t motorway_class : 1;
// all types of link classes
std::uint8_t link_class : 1;
// a low priority class is a pure connectivity way. It can be ignored in multiple decisions
// (e.g. fork on a primary vs service will not happen)
std::uint8_t may_be_ignored : 1;
// the road priority is used as an indicator for forks. If the roads are of similar priority
// (difference <=1), we can see the road as a fork. Else one of the road classes is seen as
// obvious choice
RoadPriorityClass::Enum road_priority_class : 5;
// the number of lanes in the road
std::uint8_t number_of_lanes;
public:
// default construction
RoadClassification()
: motorway_class(0), link_class(0), may_be_ignored(0),
road_priority_class(RoadPriorityClass::CONNECTIVITY), number_of_lanes(0)
{
}
RoadClassification(bool motorway_class,
bool link_class,
bool may_be_ignored,
RoadPriorityClass::Enum road_priority_class,
std::uint8_t number_of_lanes)
: motorway_class(motorway_class), link_class(link_class), may_be_ignored(may_be_ignored),
road_priority_class(road_priority_class), number_of_lanes(number_of_lanes)
{
}
bool IsMotorwayClass() const { return (0 != motorway_class) && (0 == link_class); }
void SetMotorwayFlag(const bool new_value) { motorway_class = new_value; }
bool IsRampClass() const { return (0 != motorway_class) && (0 != link_class); }
bool IsLinkClass() const { return (0 != link_class); }
void SetLinkClass(const bool new_value) { link_class = new_value; }
bool IsLowPriorityRoadClass() const { return (0 != may_be_ignored); }
void SetLowPriorityFlag(const bool new_value) { may_be_ignored = new_value; }
std::uint8_t GetNumberOfLanes() const { return number_of_lanes; }
void SetNumberOfLanes(const std::uint8_t new_value) { number_of_lanes = new_value; }
std::uint32_t GetPriority() const { return static_cast<std::uint32_t>(road_priority_class); }
RoadPriorityClass::Enum GetClass() const { return road_priority_class; }
void SetClass(const RoadPriorityClass::Enum new_value) { road_priority_class = new_value; }
bool operator==(const RoadClassification &other) const
{
return motorway_class == other.motorway_class && link_class == other.link_class &&
may_be_ignored == other.may_be_ignored &&
road_priority_class == other.road_priority_class;
}
bool operator!=(const RoadClassification &other) const { return !(*this == other); }
std::string ToString() const
{
return std::string() + (motorway_class ? "motorway" : "normal") +
(link_class ? "_link" : "") + (may_be_ignored ? " ignorable " : " important ") +
std::to_string(road_priority_class);
}
};
#pragma pack(pop)
static_assert(
sizeof(RoadClassification) == 2,
"Road Classification should fit two bytes. Increasing this has a severe impact on memory.");
inline bool canBeSeenAsFork(const RoadClassification first, const RoadClassification second)
{
return std::abs(static_cast<int>(first.GetPriority()) -
static_cast<int>(second.GetPriority())) <= 1;
}
inline bool obviousByRoadClass(const RoadClassification in_classification,
const RoadClassification obvious_candidate,
const RoadClassification compare_candidate)
{
// lower numbers are of higher priority
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());
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_CLASSIFICATION_DATA_HPP_
@@ -1,92 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_
#include "extractor/compressed_edge_container.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/is_through_street.hpp"
#include "extractor/guidance/roundabout_type.hpp"
#include "extractor/query_node.hpp"
#include "util/name_table.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
#include <unordered_set>
#include <vector>
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace detail
{
struct RoundaboutFlags
{
bool on_roundabout;
bool can_enter;
bool can_exit_separately;
};
} // namespace detail
// The roundabout handler processes all roundabout related instructions.
// It performs both the distinction between rotaries and roundabouts and
// assigns appropriate entry/exit instructions.
class RoundaboutHandler : public IntersectionHandler
{
public:
RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
~RoundaboutHandler() override final = default;
// check whether the handler can actually handle the intersection
bool canProcess(const NodeID from_nid,
const EdgeID via_eid,
const Intersection &intersection) const override final;
// process the intersection
Intersection operator()(const NodeID from_nid,
const EdgeID via_eid,
Intersection intersection) const override final;
private:
detail::RoundaboutFlags getRoundaboutFlags(const NodeID from_nid,
const EdgeID via_eid,
const Intersection &intersection) const;
// decide whether we lookk at a roundabout or a rotary
RoundaboutType getRoundaboutType(const NodeID nid) const;
// TODO handle bike/walk cases that allow crossing a roundabout!
// Processing of roundabouts
// Produces instructions to enter/exit a roundabout or to stay on it.
// Performs the distinction between roundabout and rotaries.
Intersection handleRoundabouts(const RoundaboutType roundabout_type,
const EdgeID via_edge,
const bool on_roundabout,
const bool can_exit_roundabout,
Intersection intersection) const;
bool
qualifiesAsRoundaboutIntersection(const std::unordered_set<NodeID> &roundabout_nodes) const;
const CoordinateExtractor coordinate_extractor;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_*/
@@ -1,21 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_TYPES_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_TYPES_HPP_
namespace osrm
{
namespace extractor
{
namespace guidance
{
enum class RoundaboutType
{
None, // not a roundabout
Roundabout, // standard roundabout
Rotary, // traffic circle (large roundabout) with dedicated name
RoundaboutIntersection // small roundabout with distinct turns, handled as intersection
};
} /* namespace guidance */
} /* namespace extractor */
} /* namespace osrm */
#endif /* OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_TYPES_HPP_ */
@@ -1,27 +0,0 @@
#include "util/typedefs.hpp"
#include <unordered_set>
namespace osrm
{
namespace util
{
class NameTable;
}
namespace extractor
{
class NodeBasedGraphFactory;
namespace guidance
{
// Find all "segregated" edges, e.g. edges that can be skipped in turn instructions.
// The main cases are:
// - middle edges between two osm ways in one logic road (U-turn)
// - staggered intersections (X-cross)
// - square/circle intersections
std::unordered_set<EdgeID> findSegregatedNodes(const NodeBasedGraphFactory &factory,
const util::NameTable &names);
}
}
}
@@ -1,89 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_SLIPROAD_HANDLER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_SLIPROAD_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/is_through_street.hpp"
#include "extractor/query_node.hpp"
#include "util/name_table.hpp"
#include "util/node_based_graph.hpp"
#include <vector>
#include <boost/optional.hpp>
namespace osrm
{
namespace extractor
{
namespace guidance
{
// Intersection handlers deal with all issues related to intersections.
class SliproadHandler final : public IntersectionHandler
{
public:
SliproadHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
~SliproadHandler() override final = default;
// check whether the handler can actually handle the intersection
bool canProcess(const NodeID /*nid*/,
const EdgeID /*via_eid*/,
const Intersection & /*intersection*/) const override final;
// process the intersection
Intersection operator()(const NodeID nid,
const EdgeID via_eid,
Intersection intersection) const override final;
private:
boost::optional<std::size_t> getObviousIndexWithSliproads(const EdgeID from,
const Intersection &intersection,
const NodeID at) const;
// Next intersection from `start` onto `onto` is too far away for a Siproad scenario
bool nextIntersectionIsTooFarAway(const NodeID start, const EdgeID onto) const;
// Does the road from `current` to `next` continue
bool roadContinues(const EdgeID current, const EdgeID next) const;
// Is the area under the triangle a valid Sliproad triangle
bool isValidSliproadArea(const double max_area, const NodeID, const NodeID, const NodeID) const;
// Is the Sliproad a link the both roads it shortcuts must not be links
bool isValidSliproadLink(const IntersectionViewData &sliproad,
const IntersectionViewData &first,
const IntersectionViewData &second) const;
// check if no mode changes are involved
bool allSameMode(const EdgeID in_road,
const EdgeID sliproad_candidate,
const EdgeID target_road) const;
// Could a Sliproad reach this intersection?
static bool canBeTargetOfSliproad(const IntersectionView &intersection);
// Scales a threshold based on the underlying road classification.
// Example: a 100 m threshold for a highway if different on living streets.
// The return value is guaranteed to not be larger than `threshold`.
static double scaledThresholdByRoadClass(const double max_threshold,
const RoadClassification &classification);
const CoordinateExtractor coordinate_extractor;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_SLIPROAD_HANDLER_HPP_*/
@@ -1,116 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_STATISTICS_HANDLER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_STATISTICS_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "util/log.hpp"
#include <algorithm>
#include <iomanip>
#include <iterator>
#include <map>
#include <mutex>
#include <cstdint>
namespace osrm
{
namespace extractor
{
namespace guidance
{
// Unconditionally runs over all intersections and gathers statistics for
// instruction turn types and direction modifiers (see turn_instruction.hpp).
class StatisticsHandler final : public IntersectionHandler
{
public:
StatisticsHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
: IntersectionHandler(node_based_graph,
node_data_container,
coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table)
{
}
~StatisticsHandler() override final
{
const auto add_second = [](const auto acc, const auto &kv) { return acc + kv.second; };
const auto num_types =
std::accumulate(begin(type_hist), end(type_hist), std::uint64_t{0}, add_second);
const auto num_modifiers =
std::accumulate(begin(modifier_hist), end(modifier_hist), std::uint64_t{0}, add_second);
util::Log() << "Assigned " << num_types << " turn instruction types:";
for (const auto &kv : type_hist)
if (kv.second > 0)
util::Log() << " " << std::fixed << std::setprecision(2)
<< internalInstructionTypeToString(kv.first) << ": " << kv.second
<< " (" << (kv.second / static_cast<float>(num_types) * 100.) << "%)";
util::Log() << "Assigned " << num_modifiers << " turn instruction modifiers:";
for (const auto &kv : modifier_hist)
if (kv.second > 0)
util::Log() << " " << std::fixed << std::setprecision(2)
<< instructionModifierToString(kv.first) << ": " << kv.second << " ("
<< (kv.second / static_cast<float>(num_modifiers) * 100.) << "%)";
}
bool canProcess(const NodeID, const EdgeID, const Intersection &) const override final
{
return true;
}
Intersection
operator()(const NodeID, const EdgeID, Intersection intersection) const override final
{
// Lock histograms updates on a per-intersection basis.
std::lock_guard<std::mutex> defer{lock};
// Generate histograms for all roads; this way we will get duplicates
// which we would not get doing it after EBF generation. But we want
// numbers closer to the handlers and see how often handlers ran.
for (const auto &road : intersection)
{
if (road.entry_allowed)
{
const auto type = road.instruction.type;
const auto modifier = road.instruction.direction_modifier;
type_hist[type] += 1;
modifier_hist[modifier] += 1;
}
}
return intersection;
}
private:
mutable std::mutex lock;
mutable std::map<TurnType::Enum, std::uint64_t> type_hist;
mutable std::map<DirectionModifier::Enum, std::uint64_t> modifier_hist;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_GUIDANCE_VALIDATION_HANDLER_HPP_
@@ -1,48 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_SUPPRESS_MODE_HANDLER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_SUPPRESS_MODE_HANDLER_HPP_
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/travel_mode.hpp"
#include "util/node_based_graph.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
// Suppresses instructions for certain modes.
// Think: ferry route. This handler suppresses all instructions while on the ferry route.
// We don't want to announce "Turn Right", "Turn Left" while on ferries, as one example.
class SuppressModeHandler final : public IntersectionHandler
{
public:
SuppressModeHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
~SuppressModeHandler() override final = default;
bool canProcess(const NodeID nid,
const EdgeID via_eid,
const Intersection &intersection) const override final;
Intersection operator()(const NodeID nid,
const EdgeID via_eid,
Intersection intersection) const override final;
};
} // namespace osrm
} // namespace extractor
} // namespace guidance
#endif /* OSRM_EXTRACTOR_GUIDANCE_SUPPRESS_MODE_HANDLER_HPP_ */
@@ -1,82 +0,0 @@
#ifndef OSRM_EXTRACTOR_TURN_ANALYSIS
#define OSRM_EXTRACTOR_TURN_ANALYSIS
#include "extractor/compressed_edge_container.hpp"
#include "extractor/guidance/driveway_handler.hpp"
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/motorway_handler.hpp"
#include "extractor/guidance/roundabout_handler.hpp"
#include "extractor/guidance/sliproad_handler.hpp"
#include "extractor/guidance/statistics_handler.hpp"
#include "extractor/guidance/suppress_mode_handler.hpp"
#include "extractor/guidance/turn_classification.hpp"
#include "extractor/guidance/turn_handler.hpp"
#include "extractor/query_node.hpp"
#include "extractor/restriction_index.hpp"
#include "extractor/suffix_table.hpp"
#include "util/attributes.hpp"
#include "util/name_table.hpp"
#include "util/node_based_graph.hpp"
#include <cstdint>
#include <memory>
#include <string>
#include <tuple>
#include <unordered_set>
#include <utility>
#include <vector>
namespace osrm
{
namespace extractor
{
namespace guidance
{
class TurnAnalysis
{
public:
TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const CompressedEdgeContainer &compressed_edge_container,
const RestrictionMap &restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
/* Full Analysis Process for a single node/edge combination. Use with caution, as the process is
* relatively expensive */
OSRM_ATTR_WARN_UNUSED
Intersection operator()(const NodeID node_prior_to_intersection,
const EdgeID entering_via_edge) const;
// Select turn types based on the intersection shape
OSRM_ATTR_WARN_UNUSED
Intersection AssignTurnTypes(const NodeID from_node,
const EdgeID via_eid,
const IntersectionView &intersection) const;
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const RoundaboutHandler roundabout_handler;
const MotorwayHandler motorway_handler;
const TurnHandler turn_handler;
const SliproadHandler sliproad_handler;
const SuppressModeHandler suppress_mode_handler;
const DrivewayHandler driveway_handler;
const StatisticsHandler statistics_handler;
// Utility function, setting basic turn types. Prepares for normal turn handling.
Intersection
setTurnTypes(const NodeID from, const EdgeID via_edge, Intersection intersection) const;
}; // class TurnAnalysis
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_TURN_ANALYSIS
@@ -1,26 +0,0 @@
#ifndef OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_
#define OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_
#include "extractor/guidance/intersection.hpp"
#include "util/coordinate.hpp"
#include "util/guidance/bearing_class.hpp"
#include "util/guidance/entry_class.hpp"
#include <utility>
namespace osrm
{
namespace extractor
{
namespace guidance
{
std::pair<util::guidance::EntryClass, util::guidance::BearingClass>
classifyIntersection(Intersection intersection, const osrm::util::Coordinate &location);
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_
@@ -1,53 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_DISCOVERY_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_TURN_DISCOVERY_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/turn_lane_data.hpp"
#include "extractor/restriction_index.hpp"
#include "util/typedefs.hpp"
#include <unordered_set>
namespace osrm
{
namespace util
{
struct Coordinate;
}
namespace extractor
{
class CompressedEdgeContainer;
namespace guidance
{
namespace lanes
{
// OSRM processes edges by looking at a via_edge, coming into an intersection. For turn lanes, we
// might require to actually look back a turn. We do so in the hope that the turn lanes match up at
// the previous intersection for all incoming lanes.
bool findPreviousIntersection(
const NodeID node,
const EdgeID via_edge,
const Intersection &intersection,
const util::NodeBasedDynamicGraph &node_based_graph, // query edge data
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
// output parameters, will be in an arbitrary state on failure
NodeID &result_node,
EdgeID &result_via_edge,
IntersectionView &result_intersection);
} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_TURN_DISCOVERY_HPP_*/
-119
View File
@@ -1,119 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/is_through_street.hpp"
#include "extractor/query_node.hpp"
#include "util/attributes.hpp"
#include "util/name_table.hpp"
#include "util/node_based_graph.hpp"
#include <boost/optional.hpp>
#include <cstddef>
#include <utility>
#include <vector>
namespace osrm
{
namespace extractor
{
namespace guidance
{
// Intersection handlers deal with all issues related to intersections.
class TurnHandler : public IntersectionHandler
{
public:
TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
~TurnHandler() override final = default;
// check whether the handler can actually handle the intersection
bool canProcess(const NodeID nid,
const EdgeID via_eid,
const Intersection &intersection) const override final;
// process the intersection
Intersection operator()(const NodeID nid,
const EdgeID via_eid,
Intersection intersection) const override final;
private:
struct Fork
{
const Intersection::iterator intersection_base;
const Intersection::iterator begin;
const Intersection::iterator end;
const std::size_t size;
Fork(const Intersection::iterator intersection_base,
const Intersection::iterator begin,
const Intersection::iterator end);
ConnectedRoad &getRight() const;
ConnectedRoad &getLeft() const;
ConnectedRoad &getMiddle() const;
ConnectedRoad &getRight();
ConnectedRoad &getLeft();
ConnectedRoad &getMiddle();
std::size_t getRightIndex() const;
std::size_t getLeftIndex() const;
};
bool isObviousOfTwo(const EdgeID via_edge,
const ConnectedRoad &road,
const ConnectedRoad &other) const;
bool hasObvious(const EdgeID &via_edge, const Fork &fork) const;
boost::optional<Fork> findForkCandidatesByGeometry(Intersection &intersection) const;
bool isCompatibleByRoadClass(const Intersection &intersection, const Fork fork) const;
// Dead end.
OSRM_ATTR_WARN_UNUSED
Intersection handleOneWayTurn(Intersection intersection) const;
// Mode Changes, new names...
OSRM_ATTR_WARN_UNUSED
Intersection handleTwoWayTurn(const EdgeID via_edge, Intersection intersection) const;
// Forks, T intersections and similar
OSRM_ATTR_WARN_UNUSED
Intersection handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const;
// Handling of turns larger then degree three
OSRM_ATTR_WARN_UNUSED
Intersection handleComplexTurn(const EdgeID via_edge, Intersection intersection) const;
void
handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
// Classification
boost::optional<Fork> findFork(const EdgeID via_edge, Intersection &intersection) const;
OSRM_ATTR_WARN_UNUSED
Intersection assignLeftTurns(const EdgeID via_edge,
Intersection intersection,
const std::size_t starting_at) const;
OSRM_ATTR_WARN_UNUSED
Intersection assignRightTurns(const EdgeID via_edge,
Intersection intersection,
const std::size_t up_to) const;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_*/
@@ -1,400 +0,0 @@
#ifndef OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
#define OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
#include <algorithm>
#include <cstdint>
#include "extractor/guidance/roundabout_type.hpp"
#include "util/attributes.hpp"
#include "util/typedefs.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
// direction modifiers based on angle
namespace DirectionModifier
{
typedef std::uint8_t Enum;
const constexpr Enum UTurn = 0;
const constexpr Enum SharpRight = 1;
const constexpr Enum Right = 2;
const constexpr Enum SlightRight = 3;
const constexpr Enum Straight = 4;
const constexpr Enum SlightLeft = 5;
const constexpr Enum Left = 6;
const constexpr Enum SharpLeft = 7;
const constexpr Enum MaxDirectionModifier = 8;
}
namespace TurnType
{
typedef std::uint8_t Enum;
const constexpr Enum Invalid = 0; // no valid turn instruction
const constexpr Enum NewName = 1; // no turn, but name changes
const constexpr Enum Continue = 2; // remain on a street
const constexpr Enum Turn = 3; // basic turn
const constexpr Enum Merge = 4; // merge onto a street
const constexpr Enum OnRamp = 5; // special turn (highway ramp on-ramps)
const constexpr Enum OffRamp = 6; // special turn, highway exit
const constexpr Enum Fork = 7; // fork road splitting up
const constexpr Enum EndOfRoad = 8; // T intersection
const constexpr Enum Notification = 9; // Travel Mode Changes, Restrictions apply...
const constexpr Enum EnterRoundabout = 10; // Entering a small Roundabout
const constexpr Enum EnterAndExitRoundabout = 11; // Touching a roundabout
const constexpr Enum EnterRotary = 12; // Enter a rotary
const constexpr Enum EnterAndExitRotary = 13; // Touching a rotary
const constexpr Enum EnterRoundaboutIntersection = 14; // Entering a small Roundabout
const constexpr Enum EnterAndExitRoundaboutIntersection = 15; // Touching a roundabout
// depreacted: const constexpr Enum UseLane = 16; // No Turn, but you need to stay on a given lane!
// Values below here are silent instructions
const constexpr Enum NoTurn = 17; // end of segment without turn/middle of a segment
const constexpr Enum Suppressed = 18; // location that suppresses a turn
const constexpr Enum EnterRoundaboutAtExit = 19; // Entering a small Roundabout at a countable exit
const constexpr Enum ExitRoundabout = 20; // Exiting a small Roundabout
const constexpr Enum EnterRotaryAtExit = 21; // Enter A Rotary at a countable exit
const constexpr Enum ExitRotary = 22; // Exit a rotary
const constexpr Enum EnterRoundaboutIntersectionAtExit =
23; // Entering a small Roundabout at a countable exit
const constexpr Enum ExitRoundaboutIntersection = 24; // Exiting a small Roundabout
const constexpr Enum StayOnRoundabout = 25; // Continue on Either a small or a large Roundabout
const constexpr Enum Sliproad =
26; // Something that looks like a ramp, but is actually just a small sliproad
const constexpr Enum MaxTurnType = 27; // Special value for static asserts
}
struct TurnInstruction
{
TurnInstruction(const TurnType::Enum type = TurnType::Invalid,
const DirectionModifier::Enum direction_modifier = DirectionModifier::UTurn)
: type(type), direction_modifier(direction_modifier)
{
}
TurnType::Enum type : 5;
DirectionModifier::Enum direction_modifier : 3;
bool IsUTurn() const
{
return type != TurnType::NoTurn && direction_modifier == DirectionModifier::UTurn;
}
static TurnInstruction INVALID() { return {TurnType::Invalid, DirectionModifier::UTurn}; }
static TurnInstruction NO_TURN() { return {TurnType::NoTurn, DirectionModifier::UTurn}; }
static TurnInstruction REMAIN_ROUNDABOUT(const RoundaboutType,
const DirectionModifier::Enum modifier)
{
return {TurnType::StayOnRoundabout, modifier};
}
static TurnInstruction ENTER_ROUNDABOUT(const RoundaboutType roundabout_type,
const DirectionModifier::Enum modifier)
{
const constexpr TurnType::Enum enter_instruction[] = {
TurnType::Invalid,
TurnType::EnterRoundabout,
TurnType::EnterRotary,
TurnType::EnterRoundaboutIntersection};
return {enter_instruction[static_cast<int>(roundabout_type)], modifier};
}
static TurnInstruction EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
const DirectionModifier::Enum modifier)
{
const constexpr TurnType::Enum exit_instruction[] = {TurnType::Invalid,
TurnType::ExitRoundabout,
TurnType::ExitRotary,
TurnType::ExitRoundaboutIntersection};
return {exit_instruction[static_cast<int>(roundabout_type)], modifier};
}
static TurnInstruction ENTER_AND_EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
const DirectionModifier::Enum modifier)
{
const constexpr TurnType::Enum exit_instruction[] = {
TurnType::Invalid,
TurnType::EnterAndExitRoundabout,
TurnType::EnterAndExitRotary,
TurnType::EnterAndExitRoundaboutIntersection};
return {exit_instruction[static_cast<int>(roundabout_type)], modifier};
}
static TurnInstruction ENTER_ROUNDABOUT_AT_EXIT(const RoundaboutType roundabout_type,
const DirectionModifier::Enum modifier)
{
const constexpr TurnType::Enum enter_instruction[] = {
TurnType::Invalid,
TurnType::EnterRoundaboutAtExit,
TurnType::EnterRotaryAtExit,
TurnType::EnterRoundaboutIntersectionAtExit};
return {enter_instruction[static_cast<int>(roundabout_type)], modifier};
}
static TurnInstruction SUPPRESSED(const DirectionModifier::Enum modifier)
{
return {TurnType::Suppressed, modifier};
}
};
static_assert(sizeof(TurnInstruction) == 1, "TurnInstruction does not fit a byte");
inline bool operator!=(const TurnInstruction lhs, const TurnInstruction rhs)
{
return lhs.type != rhs.type || lhs.direction_modifier != rhs.direction_modifier;
}
inline bool operator==(const TurnInstruction lhs, const TurnInstruction rhs)
{
return lhs.type == rhs.type && lhs.direction_modifier == rhs.direction_modifier;
}
// check if a instruction is associated in any form with a roundabout
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};
const auto *first = valid_types;
const auto *last = first + sizeof(valid_types) / sizeof(valid_types[0]);
return std::find(first, last, instruction.type) != last;
}
inline bool entersRoundabout(const extractor::guidance::TurnInstruction instruction)
{
return (instruction.type == extractor::guidance::TurnType::EnterRoundabout ||
instruction.type == extractor::guidance::TurnType::EnterRotary ||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersection ||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutAtExit ||
instruction.type == extractor::guidance::TurnType::EnterRotaryAtExit ||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersectionAtExit ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundaboutIntersection);
}
inline bool leavesRoundabout(const extractor::guidance::TurnInstruction instruction)
{
return (instruction.type == extractor::guidance::TurnType::ExitRoundabout ||
instruction.type == extractor::guidance::TurnType::ExitRotary ||
instruction.type == extractor::guidance::TurnType::ExitRoundaboutIntersection ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundaboutIntersection);
}
inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruction)
{
return instruction.type == extractor::guidance::TurnType::StayOnRoundabout ||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutAtExit ||
instruction.type == extractor::guidance::TurnType::EnterRotaryAtExit ||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersectionAtExit;
}
// Silent Turn Instructions are not to be mentioned to the outside world but
inline bool isSilent(const extractor::guidance::TurnInstruction instruction)
{
return instruction.type == extractor::guidance::TurnType::NoTurn ||
instruction.type == extractor::guidance::TurnType::Suppressed ||
instruction.type == extractor::guidance::TurnType::StayOnRoundabout;
}
inline bool hasRampType(const extractor::guidance::TurnInstruction instruction)
{
return instruction.type == extractor::guidance::TurnType::OffRamp ||
instruction.type == extractor::guidance::TurnType::OnRamp;
}
inline extractor::guidance::DirectionModifier::Enum getTurnDirection(const double angle)
{
// An angle of zero is a u-turn
// 180 goes perfectly straight
// 0-180 are right turns
// 180-360 are left turns
if (angle > 0 && angle < 60)
return extractor::guidance::DirectionModifier::SharpRight;
if (angle >= 60 && angle < 140)
return extractor::guidance::DirectionModifier::Right;
if (angle >= 140 && angle < 160)
return extractor::guidance::DirectionModifier::SlightRight;
if (angle >= 160 && angle <= 200)
return extractor::guidance::DirectionModifier::Straight;
if (angle > 200 && angle <= 220)
return extractor::guidance::DirectionModifier::SlightLeft;
if (angle > 220 && angle <= 300)
return extractor::guidance::DirectionModifier::Left;
if (angle > 300 && angle < 360)
return extractor::guidance::DirectionModifier::SharpLeft;
return extractor::guidance::DirectionModifier::UTurn;
}
// swaps left <-> right modifier types
OSRM_ATTR_WARN_UNUSED
inline extractor::guidance::DirectionModifier::Enum
mirrorDirectionModifier(const extractor::guidance::DirectionModifier::Enum modifier)
{
const constexpr extractor::guidance::DirectionModifier::Enum results[] = {
extractor::guidance::DirectionModifier::UTurn,
extractor::guidance::DirectionModifier::SharpLeft,
extractor::guidance::DirectionModifier::Left,
extractor::guidance::DirectionModifier::SlightLeft,
extractor::guidance::DirectionModifier::Straight,
extractor::guidance::DirectionModifier::SlightRight,
extractor::guidance::DirectionModifier::Right,
extractor::guidance::DirectionModifier::SharpRight};
return results[modifier];
}
inline bool hasLeftModifier(const extractor::guidance::TurnInstruction instruction)
{
return instruction.direction_modifier == extractor::guidance::DirectionModifier::SharpLeft ||
instruction.direction_modifier == extractor::guidance::DirectionModifier::Left ||
instruction.direction_modifier == extractor::guidance::DirectionModifier::SlightLeft;
}
inline bool hasRightModifier(const extractor::guidance::TurnInstruction instruction)
{
return instruction.direction_modifier == extractor::guidance::DirectionModifier::SharpRight ||
instruction.direction_modifier == extractor::guidance::DirectionModifier::Right ||
instruction.direction_modifier == extractor::guidance::DirectionModifier::SlightRight;
}
inline bool isLeftTurn(const extractor::guidance::TurnInstruction instruction)
{
switch (instruction.type)
{
case TurnType::Merge:
return hasRightModifier(instruction);
default:
return hasLeftModifier(instruction);
}
}
inline bool isRightTurn(const extractor::guidance::TurnInstruction instruction)
{
switch (instruction.type)
{
case TurnType::Merge:
return hasLeftModifier(instruction);
default:
return hasRightModifier(instruction);
}
}
inline DirectionModifier::Enum bearingToDirectionModifier(const double bearing)
{
if (bearing < 135)
{
return extractor::guidance::DirectionModifier::Right;
}
if (bearing <= 225)
{
return extractor::guidance::DirectionModifier::Straight;
}
return extractor::guidance::DirectionModifier::Left;
}
namespace detail
{
const constexpr char *modifier_names[] = {"uturn",
"sharp right",
"right",
"slight right",
"straight",
"slight left",
"left",
"sharp left"};
/**
* Human readable values for TurnType enum values
*/
struct TurnTypeName
{
// String value we return with our API
const char *external_name;
// Internal only string name for the turn type - useful for debugging
// and used by debug tiles for visualizing hidden turn types
const char *internal_name;
};
// Indexes in this list correspond to the Enum values of osrm::extractor::guidance::TurnType
const constexpr TurnTypeName turn_type_names[] = {
{"invalid", "(not set)"},
{"new name", "new name"},
{"continue", "continue"},
{"turn", "turn"},
{"merge", "merge"},
{"on ramp", "on ramp"},
{"off ramp", "off ramp"},
{"fork", "fork"},
{"end of road", "end of road"},
{"notification", "notification"},
{"roundabout", "enter roundabout"},
{"exit roundabout", "enter and exit roundabout"},
{"rotary", "enter rotary"},
{"exit rotary", "enter and exit rotary"},
{"roundabout turn", "enter roundabout turn"},
{"roundabout turn", "enter and exit roundabout turn"},
{"use lane", "use lane"},
{"invalid", "(noturn)"},
{"invalid", "(suppressed)"},
{"roundabout", "roundabout"},
{"exit roundabout", "exit roundabout"},
{"rotary", "rotary"},
{"exit rotary", "exit rotary"},
{"roundabout turn", "roundabout turn"},
{"exit roundabout", "exit roundabout turn"},
{"invalid", "(stay on roundabout)"},
{"invalid", "(sliproad)"}};
} // ns detail
inline std::string instructionTypeToString(const TurnType::Enum type)
{
static_assert(sizeof(detail::turn_type_names) / sizeof(detail::turn_type_names[0]) >=
TurnType::MaxTurnType,
"Some turn types have no string representation.");
return detail::turn_type_names[static_cast<std::size_t>(type)].external_name;
}
inline std::string internalInstructionTypeToString(const TurnType::Enum type)
{
static_assert(sizeof(detail::turn_type_names) / sizeof(detail::turn_type_names[0]) >=
TurnType::MaxTurnType,
"Some turn types have no string representation.");
return detail::turn_type_names[static_cast<std::size_t>(type)].internal_name;
}
inline std::string instructionModifierToString(const DirectionModifier::Enum modifier)
{
static_assert(sizeof(detail::modifier_names) / sizeof(detail::modifier_names[0]) >=
DirectionModifier::MaxDirectionModifier,
"Some direction modifiers have no string representation.");
return detail::modifier_names[static_cast<std::size_t>(modifier)];
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
@@ -1,26 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_AUGMENTATION_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_AUGMENTATION_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/turn_lane_data.hpp"
#include "util/attributes.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{
OSRM_ATTR_WARN_UNUSED
LaneDataVector handleNoneValueAtSimpleTurn(LaneDataVector lane_data,
const Intersection &intersection);
} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /* OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_AUGMENTATION_HPP_ */
@@ -1,47 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_DATA_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_DATA_HPP_
#include "extractor/guidance/turn_lane_types.hpp"
#include "util/attributes.hpp"
#include "util/typedefs.hpp"
#include <vector>
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{
struct TurnLaneData
{
TurnLaneType::Mask tag;
LaneID from;
LaneID to;
// a temporary data entry that does not need to be assigned to an entry.
// This is the case in situations that use partition and require the entry to perform the
// one-to-one mapping.
bool operator<(const TurnLaneData &other) const;
};
typedef std::vector<TurnLaneData> LaneDataVector;
// convertes a string given in the OSM format into a TurnLaneData vector
OSRM_ATTR_WARN_UNUSED
LaneDataVector laneDataFromDescription(TurnLaneDescription turn_lane_description);
// Locate A Tag in a lane data vector (if multiple tags are set, the first one found is returned)
LaneDataVector::const_iterator findTag(const TurnLaneType::Mask tag, const LaneDataVector &data);
LaneDataVector::iterator findTag(const TurnLaneType::Mask tag, LaneDataVector &data);
// Returns true if any of the queried tags is contained
bool hasTag(const TurnLaneType::Mask tag, const LaneDataVector &data);
} // namespace lane_data_generation
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /* OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_DATA_HPP_ */
@@ -1,161 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_HANDLER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/turn_analysis.hpp"
#include "extractor/guidance/turn_lane_data.hpp"
#include "extractor/guidance/turn_lane_types.hpp"
#include "extractor/query_node.hpp"
#include "util/attributes.hpp"
#include "util/guidance/turn_lanes.hpp"
#include "util/name_table.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <map>
#include <string>
#include <utility>
#include <vector>
namespace osrm
{
namespace extractor
{
namespace guidance
{
// Given an Intersection, the graph to access the data and the turn lanes, the turn lane matcher
// assigns appropriate turn tupels to the different turns.
namespace lanes
{
namespace
{
typedef enum TurnLaneScenario {
SIMPLE, // a straightforward assignment
PARTITION_LOCAL, // an assignment that requires partitioning, using local turns
SIMPLE_PREVIOUS, // an assignemtnn using the turns specified at the previous road (e.g.
// traffic light, lanes not drawn up to the intersection)
PARTITION_PREVIOUS, // a set of lanes on a turn with a traffic island. The lanes for the
// turn end at the previous turn (parts of it remain valid without being
// shown again)
SLIPROAD, // Sliproads are simple assignments that, for better visual representation should
// include turns from other roads in their listings
MERGE, // Merging Lanes
NONE, // not a turn lane scenario at all
INVALID, // some error might have occurred
UNKNOWN, // UNKNOWN describes all cases that we are currently not able to handle
NUM_SCENARIOS
} TurnLaneScenario;
const constexpr char *scenario_names[] = {"Simple",
"Partition Local",
"Simple Previous",
"Partition Previous",
"Sliproad",
"Merge",
"None",
"Invalid",
"Unknown"};
} // namespace
class TurnLaneHandler
{
using UpgradableMutex = boost::interprocess::interprocess_upgradable_mutex;
using ScopedReaderLock = boost::interprocess::sharable_lock<UpgradableMutex>;
using ScopedWriterLock = boost::interprocess::scoped_lock<UpgradableMutex>;
public:
typedef std::vector<TurnLaneData> LaneDataVector;
TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
LaneDescriptionMap &lane_description_map,
const TurnAnalysis &turn_analysis,
util::guidance::LaneDataIdMap &id_map);
~TurnLaneHandler();
OSRM_ATTR_WARN_UNUSED
Intersection assignTurnLanes(const NodeID at, const EdgeID via_edge, Intersection intersection);
private:
mutable std::atomic<std::size_t> count_handled;
mutable std::atomic<std::size_t> count_called;
// we need to be able to look at previous intersections to, in some cases, find the correct turn
// lanes for a turn
const util::NodeBasedDynamicGraph &node_based_graph;
const EdgeBasedNodeDataContainer &node_data_container;
const std::vector<util::Coordinate> &node_coordinates;
const extractor::CompressedEdgeContainer &compressed_geometries;
const RestrictionMap &node_restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const guidance::TurnLanesIndexedArray &turn_lanes_data;
std::vector<std::uint32_t> turn_lane_offsets;
std::vector<TurnLaneType::Mask> turn_lane_masks;
LaneDescriptionMap &lane_description_map;
const TurnAnalysis &turn_analysis;
util::guidance::LaneDataIdMap &id_map;
// Find out which scenario we have to handle
TurnLaneScenario deduceScenario(const NodeID at,
const EdgeID via_edge,
const Intersection &intersection,
// Output Parameters to reduce repeated creation
LaneDescriptionID &lane_description_id,
LaneDataVector &lane_data,
NodeID &previous_node,
EdgeID &previous_id,
Intersection &previous_intersection,
LaneDataVector &previous_lane_data,
LaneDescriptionID &previous_description_id);
// check whether we can handle an intersection
bool isSimpleIntersection(const LaneDataVector &turn_lane_data,
const Intersection &intersection) const;
// in case of a simple intersection, assign the lane entries
OSRM_ATTR_WARN_UNUSED
Intersection simpleMatchTuplesToTurns(Intersection intersection,
const LaneDataVector &lane_data,
const LaneDescriptionID lane_string_id);
// partition lane data into lane data relevant at current turn and at next turn
OSRM_ATTR_WARN_UNUSED
std::pair<TurnLaneHandler::LaneDataVector, TurnLaneHandler::LaneDataVector> partitionLaneData(
const NodeID at, LaneDataVector turn_lane_data, const Intersection &intersection) const;
// Sliproad turns have a separated lane to the right/left of other depicted lanes. These lanes
// are not necessarily separated clearly from the rest of the way. As a result, we combine both
// lane entries for our output, while performing the matching with the separated lanes only.
OSRM_ATTR_WARN_UNUSED
Intersection handleSliproadTurn(Intersection intersection,
const LaneDescriptionID lane_description_id,
LaneDataVector lane_data,
const Intersection &previous_intersection);
// get the lane data for an intersection
void extractLaneData(const EdgeID via_edge,
LaneDescriptionID &lane_description_id,
LaneDataVector &lane_data) const;
};
static_assert(sizeof(scenario_names) / sizeof(*scenario_names) == TurnLaneScenario::NUM_SCENARIOS,
"Number of scenarios needs to match the number of scenario names.");
} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_HANDLER_HPP_
@@ -1,54 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_MATCHER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_MATCHER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "extractor/guidance/turn_lane_data.hpp"
#include "util/attributes.hpp"
#include "util/guidance/turn_lanes.hpp"
#include "util/node_based_graph.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{
// Translate Turn Lane Tags into a matching modifier
DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask tag);
// check whether a match of a given tag and a turn instruction can be seen as valid
bool isValidMatch(const TurnLaneType::Mask tag, const TurnInstruction instruction);
// localisation of the best possible match for a tag
typename Intersection::const_iterator findBestMatch(const TurnLaneType::Mask tag,
const Intersection &intersection);
// the quality of a matching to decide between first/second possibility on segregated intersections
double getMatchingQuality(const TurnLaneType::Mask tag, const ConnectedRoad &road);
typename Intersection::const_iterator findBestMatchForReverse(const TurnLaneType::Mask leftmost_tag,
const Intersection &intersection);
// a match is trivial if all turns can be associated with their best match in a valid way and the
// matches occur in order
bool canMatchTrivially(const Intersection &intersection, const LaneDataVector &lane_data);
// perform a trivial match on the turn lanes
OSRM_ATTR_WARN_UNUSED
Intersection triviallyMatchLanesToTurns(Intersection intersection,
const LaneDataVector &lane_data,
const util::NodeBasedDynamicGraph &node_based_graph,
const LaneDescriptionID lane_string_id,
util::guidance::LaneDataIdMap &lane_data_to_id);
} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_MATCHER_HPP_*/
@@ -1,137 +0,0 @@
#ifndef OSRM_GUIDANCE_TURN_LANE_TYPES_HPP_
#define OSRM_GUIDANCE_TURN_LANE_TYPES_HPP_
#include <bitset>
#include <cstddef>
#include <cstdint>
#include <numeric> //partial_sum
#include <string>
#include <unordered_map>
#include <vector>
#include <boost/functional/hash.hpp>
#include "util/concurrent_id_map.hpp"
#include "util/json_container.hpp"
#include "util/typedefs.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace TurnLaneType
{
namespace detail
{
const constexpr std::size_t num_supported_lane_types = 11;
const constexpr char *translations[detail::num_supported_lane_types] = {"none",
"straight",
"sharp left",
"left",
"slight left",
"slight right",
"right",
"sharp right",
"uturn",
"merge to left",
"merge to right"};
} // namespace detail
typedef std::uint16_t Mask;
const constexpr Mask empty = 0u;
const constexpr Mask none = 1u << 0u;
const constexpr Mask straight = 1u << 1u;
const constexpr Mask sharp_left = 1u << 2u;
const constexpr Mask left = 1u << 3u;
const constexpr Mask slight_left = 1u << 4u;
const constexpr Mask slight_right = 1u << 5u;
const constexpr Mask right = 1u << 6u;
const constexpr Mask sharp_right = 1u << 7u;
const constexpr Mask uturn = 1u << 8u;
const constexpr Mask merge_to_left = 1u << 9u;
const constexpr Mask merge_to_right = 1u << 10u;
inline std::string toString(const Mask lane_type)
{
if (lane_type == 0)
return "none";
std::bitset<8 * sizeof(Mask)> mask(lane_type);
std::string result = "";
for (std::size_t lane_id_nr = 0; lane_id_nr < detail::num_supported_lane_types; ++lane_id_nr)
if (mask[lane_id_nr])
result += (result.empty() ? detail::translations[lane_id_nr]
: (std::string(";") + detail::translations[lane_id_nr]));
return result;
}
inline util::json::Array toJsonArray(const Mask lane_type)
{
util::json::Array result;
std::bitset<8 * sizeof(Mask)> mask(lane_type);
for (std::size_t lane_id_nr = 0; lane_id_nr < detail::num_supported_lane_types; ++lane_id_nr)
if (mask[lane_id_nr])
result.values.push_back(detail::translations[lane_id_nr]);
return result;
}
} // TurnLaneType
typedef std::vector<TurnLaneType::Mask> TurnLaneDescription;
// hash function for TurnLaneDescription
struct TurnLaneDescription_hash
{
std::size_t operator()(const TurnLaneDescription &lane_description) const
{
std::size_t seed = 0;
boost::hash_range(seed, lane_description.begin(), lane_description.end());
return seed;
}
};
typedef util::ConcurrentIDMap<guidance::TurnLaneDescription,
LaneDescriptionID,
guidance::TurnLaneDescription_hash>
LaneDescriptionMap;
using TurnLanesIndexedArray =
std::tuple<std::vector<std::uint32_t>, std::vector<TurnLaneType::Mask>>;
inline TurnLanesIndexedArray transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map)
{
// could use some additional capacity? To avoid a copy during processing, though small data so
// probably not that important.
//
// From the map, we construct an adjacency array that allows access from all IDs to the list of
// associated Turn Lane Masks.
//
// turn lane offsets points into the locations of the turn_lane_masks array. We use a standard
// adjacency array like structure to store the turn lane masks.
std::vector<std::uint32_t> turn_lane_offsets(turn_lane_map.data.size() + 1); // + sentinel
for (auto entry = turn_lane_map.data.begin(); entry != turn_lane_map.data.end(); ++entry)
turn_lane_offsets[entry->second + 1] = entry->first.size();
// inplace prefix sum
std::partial_sum(turn_lane_offsets.begin(), turn_lane_offsets.end(), turn_lane_offsets.begin());
// allocate the current masks
std::vector<guidance::TurnLaneType::Mask> turn_lane_masks(turn_lane_offsets.back());
for (auto entry = turn_lane_map.data.begin(); entry != turn_lane_map.data.end(); ++entry)
std::copy(entry->first.begin(),
entry->first.end(),
turn_lane_masks.begin() + turn_lane_offsets[entry->second]);
return std::make_tuple(std::move(turn_lane_offsets), std::move(turn_lane_masks));
}
} // guidance
} // extractor
} // osrm
#endif /* OSRM_GUIDANCE_TURN_LANE_TYPES_HPP_ */
@@ -1,10 +1,10 @@
#ifndef INTERNAL_EXTRACTOR_EDGE_HPP
#define INTERNAL_EXTRACTOR_EDGE_HPP
#include "extractor/guidance/road_classification.hpp"
#include "extractor/guidance/turn_lane_types.hpp"
#include "extractor/node_based_edge.hpp"
#include "extractor/travel_mode.hpp"
#include "guidance/road_classification.hpp"
#include "guidance/turn_lane_types.hpp"
#include "osrm/coordinate.hpp"
#include "util/typedefs.hpp"
@@ -2,10 +2,10 @@
#define OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_ANALYSIS_HPP
#include "extractor/compressed_edge_container.hpp"
#include "extractor/guidance/mergable_road_detector.hpp"
#include "extractor/guidance/turn_lane_types.hpp"
#include "extractor/intersection/intersection_edge.hpp"
#include "extractor/restriction_index.hpp"
#include "guidance/mergable_road_detector.hpp"
#include "guidance/turn_lane_types.hpp"
#include "util/coordinate.hpp"
#include "util/node_based_graph.hpp"
+1 -1
View File
@@ -8,7 +8,7 @@
#include "extractor/travel_mode.hpp"
#include "util/typedefs.hpp"
#include "extractor/guidance/road_classification.hpp"
#include "guidance/road_classification.hpp"
namespace osrm
{
+1 -1
View File
@@ -1,8 +1,8 @@
#ifndef ORIGINAL_EDGE_DATA_HPP
#define ORIGINAL_EDGE_DATA_HPP
#include "extractor/guidance/turn_instruction.hpp"
#include "extractor/travel_mode.hpp"
#include "guidance/turn_instruction.hpp"
#include "util/guidance/turn_bearing.hpp"
#include "util/typedefs.hpp"
+1 -1
View File
@@ -1,10 +1,10 @@
#ifndef SCRIPTING_ENVIRONMENT_HPP
#define SCRIPTING_ENVIRONMENT_HPP
#include "extractor/guidance/turn_lane_types.hpp"
#include "extractor/internal_extractor_edge.hpp"
#include "extractor/profile_properties.hpp"
#include "extractor/restriction.hpp"
#include "guidance/turn_lane_types.hpp"
#include <osmium/memory/buffer.hpp>
+1 -1
View File
@@ -1,8 +1,8 @@
#ifndef OSRM_EXTRACTOR_TURN_DATA_CONTAINER_HPP
#define OSRM_EXTRACTOR_TURN_DATA_CONTAINER_HPP
#include "extractor/guidance/turn_instruction.hpp"
#include "extractor/travel_mode.hpp"
#include "guidance/turn_instruction.hpp"
#include "storage/io_fwd.hpp"
#include "storage/shared_memory_ownership.hpp"