Rember Intersection Shapes
Changes the processing order in the edge based graph factory. Instead of iterating over all outgoing edges in order, we compute the edge expanded graph in the order of intersections. This allows to remember intersection shapes and re-use them for all possible ingoing edges. Also: use low accuracry mode for intersections degree 2 intersections We can use lower accuracy here, since the `bearing` after the turn is not as relevant for off-route detection. Getting lost is near impossible here.
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
#include <tuple>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -60,19 +61,12 @@ inline EdgeBasedEdge::EdgeBasedEdge(const NodeID source,
|
||||
|
||||
inline bool EdgeBasedEdge::operator<(const EdgeBasedEdge &other) const
|
||||
{
|
||||
if (source == other.source)
|
||||
{
|
||||
if (target == other.target)
|
||||
{
|
||||
if (weight == other.weight)
|
||||
{
|
||||
return forward && backward && ((!other.forward) || (!other.backward));
|
||||
}
|
||||
return weight < other.weight;
|
||||
}
|
||||
return target < other.target;
|
||||
}
|
||||
return source < other.source;
|
||||
const auto unidirectional = (!forward || !backward);
|
||||
const auto other_is_unidirectional = (!other.forward || !other.backward);
|
||||
// if all items are the same, we want to keep bidirectional edges. due to the `<` operator,
|
||||
// preferring 0 (false) over 1 (true), we need to compare the inverse of `bidirectional`
|
||||
return std::tie(source, target, weight, unidirectional) <
|
||||
std::tie(other.source, other.target, other.weight, other_is_unidirectional);
|
||||
}
|
||||
} // ns extractor
|
||||
} // ns osrm
|
||||
|
||||
@@ -38,7 +38,8 @@ class CoordinateExtractor
|
||||
const NodeID to_node,
|
||||
const std::uint8_t number_of_in_lanes) const;
|
||||
|
||||
// same as above, only with precomputed coordinate vector (move it in)
|
||||
// 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,
|
||||
@@ -240,6 +241,14 @@ class CoordinateExtractor
|
||||
const double segment_length,
|
||||
const std::vector<double> &segment_distances,
|
||||
const std::uint8_t considered_lanes) const;
|
||||
|
||||
// find the coordinate at a specific location in the vector
|
||||
util::Coordinate ExtractCoordinateAtLength(const double distance,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const std::vector<double> &length_cache) const;
|
||||
util::Coordinate
|
||||
ExtractCoordinateAtLength(const double distance,
|
||||
const std::vector<util::Coordinate> &coordinates) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
|
||||
@@ -5,12 +5,11 @@
|
||||
#include <vector>
|
||||
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/guidance/toolkit.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
#include "util/typedefs.hpp" // EdgeID
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
@@ -18,16 +17,37 @@ namespace extractor
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
// Every Turn Operation describes a way of switching onto a segment, indicated by an EdgeID. The
|
||||
// associated turn is described by an angle and an instruction that is used to announce it.
|
||||
// The Turn Operation indicates what is exposed to the outside of the turn analysis.
|
||||
struct TurnOperation
|
||||
// the shape of an intersection only knows about edge IDs and bearings
|
||||
struct IntersectionShapeData
|
||||
{
|
||||
EdgeID eid;
|
||||
double angle;
|
||||
double bearing;
|
||||
TurnInstruction instruction;
|
||||
LaneDataID lane_data_id;
|
||||
double segment_length;
|
||||
};
|
||||
|
||||
inline auto makeCompareShapeDataByBearing(const double base_bearing)
|
||||
{
|
||||
return [base_bearing](const IntersectionShapeData &lhs, const IntersectionShapeData &rhs) {
|
||||
return util::bearing::angleBetweenBearings(base_bearing, lhs.bearing) <
|
||||
util::bearing::angleBetweenBearings(base_bearing, rhs.bearing);
|
||||
};
|
||||
};
|
||||
|
||||
// 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
|
||||
@@ -51,17 +71,17 @@ struct TurnOperation
|
||||
// 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 : public TurnOperation
|
||||
struct ConnectedRoad final : IntersectionViewData
|
||||
{
|
||||
using Base = TurnOperation;
|
||||
ConnectedRoad(const IntersectionViewData &view,
|
||||
const TurnInstruction instruction,
|
||||
const LaneDataID lane_data_id)
|
||||
: IntersectionViewData(view), instruction(instruction), lane_data_id(lane_data_id)
|
||||
{
|
||||
}
|
||||
|
||||
ConnectedRoad(const TurnOperation turn,
|
||||
const bool entry_allowed = false,
|
||||
const boost::optional<double> segment_length = {});
|
||||
|
||||
// a turn may be relevant to good instructions, even if we cannot enter the road
|
||||
bool entry_allowed;
|
||||
boost::optional<double> segment_length;
|
||||
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;
|
||||
@@ -76,10 +96,24 @@ struct ConnectedRoad final : public TurnOperation
|
||||
// small helper function to print the content of a connected road
|
||||
std::string toString(const ConnectedRoad &road);
|
||||
|
||||
struct Intersection final : public std::vector<ConnectedRoad>
|
||||
using IntersectionShape = std::vector<IntersectionShapeData>;
|
||||
|
||||
struct IntersectionView final : std::vector<IntersectionViewData>
|
||||
{
|
||||
using Base = std::vector<IntersectionViewData>;
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return std::is_sorted(begin(), end(), std::mem_fn(&IntersectionViewData::CompareByAngle));
|
||||
};
|
||||
|
||||
Base::iterator findClosestTurn(double angle);
|
||||
Base::const_iterator findClosestTurn(double angle) const;
|
||||
};
|
||||
|
||||
struct Intersection final : std::vector<ConnectedRoad>
|
||||
{
|
||||
using Base = std::vector<ConnectedRoad>;
|
||||
|
||||
/*
|
||||
* find the turn whose angle offers the least angularDeviation to the specified angle
|
||||
* E.g. for turn angles [0,90,260] and a query of 180 we return the 260 degree turn (difference
|
||||
|
||||
@@ -11,8 +11,11 @@
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
@@ -32,7 +35,18 @@ class IntersectionGenerator
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const CompressedEdgeContainer &compressed_edge_container);
|
||||
|
||||
Intersection operator()(const NodeID nid, const EdgeID via_eid) const;
|
||||
IntersectionView operator()(const NodeID nid, const EdgeID via_eid) const;
|
||||
|
||||
/*
|
||||
* Compute the shape of an intersection, returning a set of connected roads, without any further
|
||||
* concern for which of the entries are actually allowed.
|
||||
* The shape also only comes with turn bearings, not with turn angles. All turn angles will be
|
||||
* set to zero
|
||||
*/
|
||||
IntersectionShape
|
||||
ComputeIntersectionShape(const NodeID center_node,
|
||||
const boost::optional<NodeID> sorting_base = boost::none,
|
||||
bool use_low_precision_angles = false) const;
|
||||
|
||||
// Graph Compression cannot compress every setting. For example any barrier/traffic light cannot
|
||||
// be compressed. As a result, a simple road of the form `a ----- b` might end up as having an
|
||||
@@ -40,10 +54,10 @@ class IntersectionGenerator
|
||||
// down a road, finding the next actual decision requires the look at multiple intersections.
|
||||
// Here we follow the road until we either reach a dead end or find the next intersection with
|
||||
// more than a single next road.
|
||||
Intersection GetActualNextIntersection(const NodeID starting_node,
|
||||
const EdgeID via_edge,
|
||||
NodeID *resulting_from_node,
|
||||
EdgeID *resulting_via_edge) const;
|
||||
IntersectionView GetActualNextIntersection(const NodeID starting_node,
|
||||
const EdgeID via_edge,
|
||||
NodeID *resulting_from_node,
|
||||
EdgeID *resulting_via_edge) const;
|
||||
|
||||
// Allow access to the coordinate extractor for all owners
|
||||
const CoordinateExtractor &GetCoordinateExtractor() const;
|
||||
@@ -56,9 +70,28 @@ class IntersectionGenerator
|
||||
// turns. Even good enough to do some simple angle verifications. It is mostly available to
|
||||
// allow for faster graph traversal in the extraction phase.
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection GetConnectedRoads(const NodeID from_node,
|
||||
const EdgeID via_eid,
|
||||
const bool use_low_precision_angles = false) const;
|
||||
IntersectionView GetConnectedRoads(const NodeID from_node,
|
||||
const EdgeID via_eid,
|
||||
const bool use_low_precision_angles = false) const;
|
||||
|
||||
/*
|
||||
* To be used in the road network, we need to check for valid/restricted turns. These two
|
||||
* functions transform a basic intersection / a normalised intersection into the
|
||||
* correct view when entering via a given edge.
|
||||
*/
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
IntersectionView
|
||||
TransformIntersectionShapeIntoView(const NodeID previous_node,
|
||||
const EdgeID entering_via_edge,
|
||||
const IntersectionShape &intersection) const;
|
||||
// version for normalised intersection
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
IntersectionView TransformIntersectionShapeIntoView(
|
||||
const NodeID previous_node,
|
||||
const EdgeID entering_via_edge,
|
||||
const IntersectionShape &normalised_intersection,
|
||||
const IntersectionShape &intersection,
|
||||
const std::vector<std::pair<EdgeID, EdgeID>> &merging_map) const;
|
||||
|
||||
private:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
@@ -68,6 +101,14 @@ class IntersectionGenerator
|
||||
|
||||
// own state, used to find the correct coordinates along a road
|
||||
const CoordinateExtractor coordinate_extractor;
|
||||
|
||||
// check turn restrictions to find a node that is the only allowed target when coming from a
|
||||
// node to an intersection
|
||||
// d
|
||||
// |
|
||||
// a - b - c and `only_straight_on ab | bc would return `c` for `a,b`
|
||||
boost::optional<NodeID> GetOnlyAllowedTurnIfExistent(const NodeID coming_from_node,
|
||||
const NodeID node_at_intersection) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "extractor/suffix_table.hpp"
|
||||
#include "util/name_table.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
@@ -45,7 +46,8 @@ class IntersectionNormalizer
|
||||
// The function takes an intersection an converts it to a `perceived` intersection which closer
|
||||
// represents how a human might experience the intersection
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection operator()(const NodeID node_at_intersection, Intersection intersection) const;
|
||||
std::pair<IntersectionShape, std::vector<std::pair<EdgeID, EdgeID>>>
|
||||
operator()(const NodeID node_at_intersection, IntersectionShape intersection) const;
|
||||
|
||||
private:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
@@ -57,12 +59,26 @@ class IntersectionNormalizer
|
||||
|
||||
// check if two indices in an intersection can be seen as a single road in the perceived
|
||||
// intersection representation. See below for an example. Utility function for
|
||||
// MergeSegregatedRoads
|
||||
// MergeSegregatedRoads. It also checks for neighboring merges.
|
||||
// This is due possible segments where multiple roads could end up being merged into one.
|
||||
// We only support merging two roads, not three or more, though.
|
||||
// c c
|
||||
// / /
|
||||
// a - b -> a - b - (c,d) but not a - b d -> a,b,(cde)
|
||||
// \ \
|
||||
// d e
|
||||
bool CanMerge(const NodeID intersection_node,
|
||||
const Intersection &intersection,
|
||||
const IntersectionShape &intersection,
|
||||
std::size_t first_index,
|
||||
std::size_t second_index) const;
|
||||
|
||||
// A tool called by CanMerge. It checks whether two indices can be merged, not concerned without
|
||||
// remaining parts of the intersection.
|
||||
bool InnerCanMerge(const NodeID intersection_node,
|
||||
const IntersectionShape &intersection,
|
||||
std::size_t first_index,
|
||||
std::size_t second_index) const;
|
||||
|
||||
// Merge segregated roads to omit invalid turns in favor of treating segregated roads as
|
||||
// one.
|
||||
// This function combines roads the following way:
|
||||
@@ -75,8 +91,8 @@ class IntersectionNormalizer
|
||||
// The treatment results in a straight turn angle of 180º rather than a turn angle of approx
|
||||
// 160
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection MergeSegregatedRoads(const NodeID intersection_node,
|
||||
Intersection intersection) const;
|
||||
std::pair<IntersectionShape, std::vector<std::pair<EdgeID, EdgeID>>>
|
||||
MergeSegregatedRoads(const NodeID intersection_node, IntersectionShape intersection) const;
|
||||
|
||||
// The counterpiece to mergeSegregatedRoads. While we can adjust roads that split up at the
|
||||
// intersection itself, it can also happen that intersections are connected to joining roads.
|
||||
@@ -90,8 +106,8 @@ class IntersectionNormalizer
|
||||
//
|
||||
// for the local view of b at a.
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection AdjustForJoiningRoads(const NodeID node_at_intersection,
|
||||
Intersection intersection) const;
|
||||
IntersectionShape AdjustBearingsForMergeAtDestination(const NodeID node_at_intersection,
|
||||
IntersectionShape intersection) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
|
||||
@@ -133,7 +133,7 @@ struct IntersectionFinderAccumulator
|
||||
// the result we are looking for
|
||||
NodeID nid;
|
||||
EdgeID via_edge_id;
|
||||
Intersection intersection;
|
||||
IntersectionView intersection;
|
||||
};
|
||||
|
||||
template <class accumulator_type, class selector_type>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -36,7 +37,6 @@ namespace guidance
|
||||
|
||||
class TurnAnalysis
|
||||
{
|
||||
|
||||
public:
|
||||
TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
@@ -47,29 +47,35 @@ class TurnAnalysis
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const ProfileProperties &profile_properties);
|
||||
|
||||
/* 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;
|
||||
|
||||
/*
|
||||
* Returns a normalized intersection without any assigned turn types.
|
||||
* This intersection can be used as input for intersection classification, turn lane assignment
|
||||
* and similar.
|
||||
*/
|
||||
struct ShapeResult
|
||||
{
|
||||
// the basic shape, containing all turns
|
||||
IntersectionShape intersection_shape;
|
||||
// normalised shape, merged some roads into others, adjusted bearings
|
||||
// see intersection_normaliser for further explanations
|
||||
IntersectionShape normalised_intersection_shape;
|
||||
// map containing information about which road was merged into which
|
||||
std::vector<std::pair<EdgeID, EdgeID>> merging_map;
|
||||
};
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection operator()(const NodeID from_node, const EdgeID via_eid) const;
|
||||
ShapeResult ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const;
|
||||
|
||||
/*
|
||||
* Post-Processing a generated intersection is useful for any intersection that was simply
|
||||
* generated using an intersection generator. In the normal use case, you don't have to call
|
||||
* this function.
|
||||
* This function is part of the normal process of the operator().
|
||||
*/
|
||||
// Select turn types based on the intersection shape
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
Intersection
|
||||
PostProcess(const NodeID from_node, const EdgeID via_eid, Intersection intersection) const;
|
||||
|
||||
std::vector<TurnOperation>
|
||||
transformIntersectionIntoTurns(const Intersection &intersection) const;
|
||||
|
||||
Intersection
|
||||
assignTurnTypes(const NodeID from_node, const EdgeID via_eid, Intersection intersection) const;
|
||||
Intersection AssignTurnTypes(const NodeID from_node,
|
||||
const EdgeID via_eid,
|
||||
const IntersectionView &intersection) const;
|
||||
|
||||
const IntersectionGenerator &GetIntersectionGenerator() const;
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ bool findPreviousIntersection(
|
||||
// output parameters, will be in an arbitrary state on failure
|
||||
NodeID &result_node,
|
||||
EdgeID &result_via_edge,
|
||||
Intersection &result_intersection);
|
||||
IntersectionView &result_intersection);
|
||||
|
||||
} // namespace lanes
|
||||
} // namespace guidance
|
||||
|
||||
@@ -73,8 +73,9 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge
|
||||
|
||||
inline NodeBasedEdge::NodeBasedEdge()
|
||||
: source(SPECIAL_NODEID), target(SPECIAL_NODEID), name_id(0), weight(0), forward(false),
|
||||
backward(false), roundabout(false), circular(false), access_restricted(false), startpoint(true),
|
||||
is_split(false), travel_mode(false), lane_description_id(INVALID_LANE_DESCRIPTIONID)
|
||||
backward(false), roundabout(false), circular(false), access_restricted(false),
|
||||
startpoint(true), is_split(false), travel_mode(false),
|
||||
lane_description_id(INVALID_LANE_DESCRIPTIONID)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -93,9 +94,10 @@ inline NodeBasedEdge::NodeBasedEdge(NodeID source,
|
||||
const LaneDescriptionID lane_description_id,
|
||||
guidance::RoadClassification road_classification)
|
||||
: source(source), target(target), name_id(name_id), weight(weight), forward(forward),
|
||||
backward(backward), roundabout(roundabout), circular(circular), access_restricted(access_restricted),
|
||||
startpoint(startpoint), is_split(is_split), travel_mode(travel_mode),
|
||||
lane_description_id(lane_description_id), road_classification(std::move(road_classification))
|
||||
backward(backward), roundabout(roundabout), circular(circular),
|
||||
access_restricted(access_restricted), startpoint(startpoint), is_split(is_split),
|
||||
travel_mode(travel_mode), lane_description_id(lane_description_id),
|
||||
road_classification(std::move(road_classification))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user