Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 28a5fe06bb | |||
| 4c817de697 | |||
| 5133f39d76 | |||
| a29dcfa951 | |||
| e546ddd777 | |||
| c21c9c9106 | |||
| 511f3cff33 | |||
| d5ffbc6a9a | |||
| 60551d209d | |||
| 57be7beb5d | |||
| a8d0c115da | |||
| d9fcf1ecf7 | |||
| 1967701385 | |||
| a2cdc063d4 | |||
| 6e06daff15 | |||
| 2b6bdaf727 | |||
| 2347c0ada7 | |||
| 65034612ff | |||
| bd427e966d | |||
| b777fb1052 | |||
| 11d491e00f | |||
| aa51e8dadd | |||
| 83ca62aa09 | |||
| a26695c04a | |||
| 4787fe30b7 | |||
| fdef3fdfd7 | |||
| f148810a1c | |||
| 9d5a4be2d9 | |||
| 5484118d77 | |||
| 2116cc68ca | |||
| 531b281b73 | |||
| 7137459fb0 | |||
| f57fd4ffc3 | |||
| 275f71a7d7 | |||
| 2c7d1ed48c | |||
| d67cd34edd | |||
| c8c3a49fb5 | |||
| 3ae313d35d |
@@ -4,7 +4,6 @@ What issue is this PR targeting? If there is no issue that addresses the problem
|
||||
|
||||
## Tasklist
|
||||
- [ ] ADD OWN TASKS HERE
|
||||
- [ ] CHANGELOG.md entry ([How to write a changelog entry](http://keepachangelog.com/en/1.0.0/#how))
|
||||
- [ ] update relevant [Wiki pages](https://github.com/Project-OSRM/osrm-backend/wiki)
|
||||
- [ ] add regression / cucumber cases (see docs/testing.md)
|
||||
- [ ] review
|
||||
|
||||
@@ -15,6 +15,7 @@ branches:
|
||||
- master
|
||||
# enable building tags
|
||||
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||
- 5.14
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
|
||||
+4
-11
@@ -1,13 +1,6 @@
|
||||
# UNRELEASED
|
||||
- Changes from 5.14.3:
|
||||
- Bugfixes:
|
||||
- FIXED #4704: Fixed regression in bearings reordering introduced in 5.13 [#4704](https://github.com/Project-OSRM/osrm-backend/issues/4704)
|
||||
- Guidance:
|
||||
- CHANGED #4706: Guidance refactoring step to decouple intersection connectivity analysis and turn instructions generation [#4706](https://github.com/Project-OSRM/osrm-backend/pull/4706)
|
||||
|
||||
# 5.14.3
|
||||
- Changes from 5.14.2:
|
||||
- Bugfixes:
|
||||
- Changes from 5.14.2:
|
||||
- Bugfixes:
|
||||
- FIXED #4754: U-Turn penalties are applied to straight turns.
|
||||
- FIXED #4756: Removed too restrictive road name check in the sliproad handler
|
||||
- FIXED #4731: Use correct weights for edge-based graph duplicated via nodes.
|
||||
@@ -16,8 +9,8 @@
|
||||
- CHANGED: set default urban speed in Ukraine to 50kmh
|
||||
|
||||
# 5.14.2
|
||||
- Changes from 5.14.1:
|
||||
- Bugfixes:
|
||||
- Changes from 5.14.1:
|
||||
- Bugfixes:
|
||||
- FIXED #4727: Erroring when a old .core file is present.
|
||||
- FIXED #4642: Update checks for EMPTY_NAMEID to check for empty name strings
|
||||
- FIXED #4738: Fix potential segmentation fault
|
||||
|
||||
+2
-2
@@ -61,8 +61,8 @@ if (POLICY CMP0048)
|
||||
endif()
|
||||
project(OSRM C CXX)
|
||||
set(OSRM_VERSION_MAJOR 5)
|
||||
set(OSRM_VERSION_MINOR 15)
|
||||
set(OSRM_VERSION_PATCH 0)
|
||||
set(OSRM_VERSION_MINOR 14)
|
||||
set(OSRM_VERSION_PATCH 3)
|
||||
set(OSRM_VERSION "${OSRM_VERSION_MAJOR}.${OSRM_VERSION_MINOR}.${OSRM_VERSION_PATCH}")
|
||||
|
||||
add_definitions(-DOSRM_PROJECT_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
+1
-1
@@ -586,7 +586,7 @@ step.
|
||||
|
||||
- `name`: The name of the way along which travel proceeds.
|
||||
- `ref`: A reference number or code for the way. Optionally included, if ref data is available for the given way.
|
||||
- `pronunciation`: A string containing an [IPA](https://en.wikipedia.org/wiki/International_Phonetic_Alphabet) phonetic transcription indicating how to pronounce the name in the `name` property. This property is omitted if pronunciation data is unavailable for the step.
|
||||
- `pronunciation`: The pronunciation hint of the way name. Will be `undefined` if there is no pronunciation hit.
|
||||
- `destinations`: The destinations of the way. Will be `undefined` if there are no destinations.
|
||||
- `exits`: The exit numbers or names of the way. Will be `undefined` if there are no exit numbers or names.
|
||||
- `mode`: A string signifying the mode of transportation.
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
@routing @car @way
|
||||
Feature: Car - Avoid defined areas
|
||||
|
||||
Background:
|
||||
Given the profile file "car" initialized with
|
||||
"""
|
||||
profile.avoid = Set { 'motorway', 'motorway_link' }
|
||||
profile.speeds = Sequence {
|
||||
highway = {
|
||||
motorway = 90,
|
||||
motorway_link = 45,
|
||||
primary = 50
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: Car - Avoid motorways
|
||||
Then routability should be
|
||||
| highway | bothw |
|
||||
| motorway | |
|
||||
| motorway_link | |
|
||||
| primary | x |
|
||||
|
||||
@@ -884,7 +884,7 @@ Feature: Car - Turn restrictions
|
||||
| a | c | albic,dobe,dobe,albic,albic | depart,turn left,continue uturn,turn left,arrive |
|
||||
| a | e | albic,dobe,dobe | depart,turn left,arrive |
|
||||
|
||||
@no_turning @conditionals @restriction-way
|
||||
@no_turning @conditionals
|
||||
Scenario: Car - Conditional restriction with multiple time windows
|
||||
Given the extract extra arguments "--parse-conditional-restrictions"
|
||||
# 5pm Wed 02 May, 2017 GMT
|
||||
@@ -1054,3 +1054,4 @@ Feature: Car - Turn restrictions
|
||||
| a | f | ab,be,ef,ef | depart,turn right,turn left,arrive | a,b,e,f |
|
||||
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
|
||||
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
@routing @car
|
||||
Feature: Car - Handle physical limitation
|
||||
|
||||
Background:
|
||||
Given the profile "car"
|
||||
|
||||
Scenario: Car - Use a narrow way
|
||||
Then routability should be
|
||||
| highway | width | narrow | bothw |
|
||||
| primary | | | x |
|
||||
| primary | narrow | | x |
|
||||
| primary | | yes | x |
|
||||
| primary | 1.8 | | |
|
||||
| primary | 1.9 | | |
|
||||
| primary | 2.0 | | x |
|
||||
| primary | 2.1 | | x |
|
||||
| primary | 1m | | |
|
||||
| primary | 1 m | | |
|
||||
| primary | 3 m | | x |
|
||||
| primary | 6' | | |
|
||||
| primary | 6'0" | | |
|
||||
| primary | 6'2" | | |
|
||||
| primary | 6'3" | | x |
|
||||
| primary | 7' | | x |
|
||||
| primary | 7'0" | | x |
|
||||
|
||||
Scenario: Car - Limited by width
|
||||
Then routability should be
|
||||
| highway | maxwidth:physical | maxwidth | width | est_width | bothw |
|
||||
| primary | 1 | | | | |
|
||||
| primary | 3 | | | | x |
|
||||
| primary | | 1 | | | |
|
||||
| primary | | 3 | | | x |
|
||||
| primary | | | 1 | | |
|
||||
| primary | | | 3 | | x |
|
||||
| primary | | | | 1 | |
|
||||
| primary | | | | 3 | x |
|
||||
|
||||
Scenario: Car - Limited by height
|
||||
Then routability should be
|
||||
| highway | maxheight:physical | maxheight | bothw |
|
||||
| primary | 1 | | |
|
||||
| primary | 3 | | x |
|
||||
| primary | | 1 | |
|
||||
| primary | | 3 | x |
|
||||
@@ -575,7 +575,7 @@ Feature: Car - Turn restrictions
|
||||
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
|
||||
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
||||
|
||||
@restriction-way @overlap
|
||||
@restriction @overlap
|
||||
Scenario: Car - prohibit turn
|
||||
Given the node map
|
||||
"""
|
||||
@@ -710,7 +710,7 @@ Feature: Car - Turn restrictions
|
||||
| a | j | left,first,right,right |
|
||||
| f | e | right,third,left,left |
|
||||
|
||||
@restriction-way
|
||||
@restriction
|
||||
Scenario: Car - allow only turn
|
||||
Given the node map
|
||||
"""
|
||||
@@ -742,7 +742,7 @@ Feature: Car - Turn restrictions
|
||||
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
|
||||
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
||||
|
||||
@restriction-way
|
||||
@restriction
|
||||
Scenario: Car - allow only turn
|
||||
Given the node map
|
||||
"""
|
||||
@@ -771,7 +771,7 @@ Feature: Car - Turn restrictions
|
||||
| from | to | route |
|
||||
| a | d | ab,be,de,de |
|
||||
|
||||
@restriction-way
|
||||
@restriction
|
||||
Scenario: Multi Way restriction
|
||||
Given the node map
|
||||
"""
|
||||
@@ -808,7 +808,7 @@ Feature: Car - Turn restrictions
|
||||
| from | to | route |
|
||||
| a | h | horiz,vert,horiz,horiz |
|
||||
|
||||
@restriction-way
|
||||
@restriction
|
||||
Scenario: Multi-Way overlapping single-way
|
||||
Given the node map
|
||||
"""
|
||||
@@ -847,7 +847,7 @@ Feature: Car - Turn restrictions
|
||||
| h | d | hfb,abcd,abcd | depart,end of road right,arrive | h,b,d |
|
||||
|
||||
|
||||
@restriction-way
|
||||
@restriction
|
||||
Scenario: Car - prohibit turn, traffic lights
|
||||
Given the node map
|
||||
"""
|
||||
@@ -890,7 +890,7 @@ Feature: Car - Turn restrictions
|
||||
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
|
||||
|
||||
|
||||
@restriction-way @overlap @geometry
|
||||
@restriction @overlap @geometry
|
||||
Scenario: Geometry
|
||||
Given the node map
|
||||
"""
|
||||
@@ -925,7 +925,7 @@ Feature: Car - Turn restrictions
|
||||
| c | d | bc,bge,de,de |
|
||||
| c | f | bc,bge,de,de,ef,ef |
|
||||
|
||||
@restriction-way @overlap @geometry @traffic-signals
|
||||
@restriction @overlap @geometry @traffic-signals
|
||||
Scenario: Geometry
|
||||
Given the node map
|
||||
"""
|
||||
@@ -967,7 +967,7 @@ Feature: Car - Turn restrictions
|
||||
| c | f | bc,bge,de,de,ef,ef |
|
||||
|
||||
# don't crash hard on invalid restrictions
|
||||
@restriction-way @invalid
|
||||
@restriction @invalid
|
||||
Scenario: Geometry
|
||||
Given the node map
|
||||
"""
|
||||
@@ -999,7 +999,7 @@ Feature: Car - Turn restrictions
|
||||
| a | f | ab,be,ef,ef |
|
||||
|
||||
|
||||
@restriction @restriction-way @overlap @geometry
|
||||
@restriction @overlap @geometry
|
||||
Scenario: Duplicated restriction
|
||||
Given the node map
|
||||
"""
|
||||
|
||||
@@ -394,16 +394,12 @@ Feature: Merge Segregated Roads
|
||||
a
|
||||
|
|
||||
b
|
||||
/ \
|
||||
c h
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
d g
|
||||
\ /
|
||||
e
|
||||
|
|
||||
f
|
||||
|
||||
@@ -961,12 +961,12 @@ Feature: Simple Turns
|
||||
g
|
||||
.
|
||||
.
|
||||
.
|
||||
.
|
||||
h f
|
||||
.
|
||||
. .
|
||||
. j
|
||||
.
|
||||
.
|
||||
f
|
||||
h .
|
||||
. .
|
||||
. j
|
||||
. .
|
||||
c
|
||||
. . .
|
||||
|
||||
@@ -12,10 +12,6 @@ module.exports = {
|
||||
|
||||
FuzzyMatch: class {
|
||||
match (got, want) {
|
||||
// don't fail if bearings input and extected string is empty and actual result is undefined
|
||||
if (want === '' && (got === '' || got === undefined))
|
||||
return true;
|
||||
|
||||
var matchPercent = want.match(/(.*)\s+~(.+)%$/),
|
||||
matchAbs = want.match(/(.*)\s+\+\-(.+)$/),
|
||||
matchRe = want.match(/^\/(.*)\/$/),
|
||||
|
||||
@@ -309,7 +309,7 @@ Feature: Via points
|
||||
| waypoints | route |
|
||||
| a,b,e | |
|
||||
|
||||
@3359
|
||||
@todo @3359
|
||||
Scenario: U-Turn In Bearings
|
||||
Given the node map
|
||||
"""
|
||||
|
||||
@@ -53,12 +53,12 @@ template <typename Algorithm> class Engine final : public EngineInterface
|
||||
{
|
||||
public:
|
||||
explicit Engine(const EngineConfig &config)
|
||||
: route_plugin(config.max_locations_viaroute, config.max_alternatives), //
|
||||
table_plugin(config.max_locations_distance_table), //
|
||||
nearest_plugin(config.max_results_nearest), //
|
||||
trip_plugin(config.max_locations_trip), //
|
||||
match_plugin(config.max_locations_map_matching, config.max_radius_map_matching), //
|
||||
tile_plugin() //
|
||||
: route_plugin(config.max_locations_viaroute, config.max_alternatives), //
|
||||
table_plugin(config.max_locations_distance_table), //
|
||||
nearest_plugin(config.max_results_nearest), //
|
||||
trip_plugin(config.max_locations_trip), //
|
||||
match_plugin(config.max_locations_map_matching), //
|
||||
tile_plugin() //
|
||||
|
||||
{
|
||||
if (config.use_shared_memory)
|
||||
|
||||
@@ -84,7 +84,6 @@ struct EngineConfig final
|
||||
int max_locations_viaroute = -1;
|
||||
int max_locations_distance_table = -1;
|
||||
int max_locations_map_matching = -1;
|
||||
double max_radius_map_matching = -1.0;
|
||||
int max_results_nearest = -1;
|
||||
int max_alternatives = 3; // set an arbitrary upper bound; can be adjusted by user
|
||||
bool use_shared_memory = true;
|
||||
|
||||
@@ -24,9 +24,8 @@ class MatchPlugin : public BasePlugin
|
||||
using CandidateLists = routing_algorithms::CandidateLists;
|
||||
static const constexpr double RADIUS_MULTIPLIER = 3;
|
||||
|
||||
MatchPlugin(const int max_locations_map_matching, const double max_radius_map_matching)
|
||||
: max_locations_map_matching(max_locations_map_matching),
|
||||
max_radius_map_matching(max_radius_map_matching)
|
||||
MatchPlugin(const int max_locations_map_matching)
|
||||
: max_locations_map_matching(max_locations_map_matching)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -36,7 +35,6 @@ class MatchPlugin : public BasePlugin
|
||||
|
||||
private:
|
||||
const int max_locations_map_matching;
|
||||
const double max_radius_map_matching;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace extractor
|
||||
|
||||
class ScriptingEnvironment;
|
||||
struct ProfileProperties;
|
||||
class NodeBasedGraphFactory;
|
||||
|
||||
class Extractor
|
||||
{
|
||||
@@ -101,6 +102,13 @@ class Extractor
|
||||
void WriteConditionalRestrictions(
|
||||
const std::string &path,
|
||||
std::vector<ConditionalTurnRestriction> &conditional_turn_restrictions);
|
||||
|
||||
// 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(NodeBasedGraphFactory &factory);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ operator()(const NodeID intersection_node,
|
||||
const boost::optional<util::json::Object> &way_style) const
|
||||
{
|
||||
// request the number of lanes. This process needs to be in sync with what happens over at
|
||||
// intersection analysis
|
||||
// intersection_generator
|
||||
const auto intersection_lanes =
|
||||
intersection.FindMaximum(guidance::makeExtractLanesForRoad(node_based_graph));
|
||||
|
||||
|
||||
@@ -14,13 +14,10 @@ namespace guidance
|
||||
class DrivewayHandler final : public IntersectionHandler
|
||||
{
|
||||
public:
|
||||
DrivewayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
DrivewayHandler(const IntersectionGenerator &intersection_generator,
|
||||
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);
|
||||
|
||||
|
||||
@@ -44,6 +44,14 @@ inline auto makeCompareShapeDataByBearing(const double base_bearing)
|
||||
};
|
||||
}
|
||||
|
||||
inline auto makeCompareShapeDataAngleToBearing(const double base_bearing)
|
||||
{
|
||||
return [base_bearing](const auto &lhs, const auto &rhs) {
|
||||
return util::bearing::angleBetween(lhs.bearing, base_bearing) <
|
||||
util::bearing::angleBetween(rhs.bearing, base_bearing);
|
||||
};
|
||||
}
|
||||
|
||||
inline auto makeCompareAngularDeviation(const double angle)
|
||||
{
|
||||
return [angle](const auto &lhs, const auto &rhs) {
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_
|
||||
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/intersection_normalization_operation.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "extractor/restriction_index.hpp"
|
||||
#include "util/attributes.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
struct IntersectionGenerationParameters
|
||||
{
|
||||
NodeID nid;
|
||||
EdgeID via_eid;
|
||||
};
|
||||
|
||||
// The Intersection Generator is given a turn location and generates an intersection representation
|
||||
// from it. For this all turn possibilities are analysed.
|
||||
// We consider turn restrictions to indicate possible turns. U-turns are generated based on profile
|
||||
// decisions.
|
||||
class IntersectionGenerator
|
||||
{
|
||||
public:
|
||||
IntersectionGenerator(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const CompressedEdgeContainer &compressed_edge_container);
|
||||
|
||||
// For a source node `a` and a via edge `ab` creates an intersection at target `b`.
|
||||
//
|
||||
// a . . . b . .
|
||||
// .
|
||||
// .
|
||||
//
|
||||
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
|
||||
*/
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
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
|
||||
// intermediate intersection, if there is a traffic light in between. If we want to look farther
|
||||
// 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. This function skips over degree two nodes to find coorect input
|
||||
// for GetConnectedRoads.
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
IntersectionGenerationParameters SkipDegreeTwoNodes(const NodeID starting_node,
|
||||
const EdgeID via_edge) const;
|
||||
|
||||
// Allow access to the coordinate extractor for all owners
|
||||
const CoordinateExtractor &GetCoordinateExtractor() const;
|
||||
|
||||
// Check for restrictions/barriers and generate a list of valid and invalid turns present at
|
||||
// the node reached from `from_node` via `via_eid`. The resulting candidates have to be analysed
|
||||
// for their actual instructions later on.
|
||||
// The switch for `use_low_precision_angles` enables a faster mode that will procude less
|
||||
// accurate coordinates. It should be good enough to check order of turns, find straightmost
|
||||
// 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
|
||||
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<IntersectionNormalizationOperation> &merging_map) const;
|
||||
|
||||
private:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
const EdgeBasedNodeDataContainer &node_data_container;
|
||||
const RestrictionMap &restriction_map;
|
||||
const std::unordered_set<NodeID> &barrier_nodes;
|
||||
const std::vector<util::Coordinate> &coordinates;
|
||||
|
||||
// own state, used to find the correct coordinates along a road
|
||||
const CoordinateExtractor coordinate_extractor;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif /* OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_ */
|
||||
@@ -2,8 +2,8 @@
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_
|
||||
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/intersection_generator.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"
|
||||
|
||||
@@ -34,13 +34,10 @@ 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 std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table);
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator);
|
||||
|
||||
virtual ~IntersectionHandler() = default;
|
||||
|
||||
@@ -55,13 +52,10 @@ class IntersectionHandler
|
||||
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 std::vector<util::Coordinate> &coordinates;
|
||||
const util::NameTable &name_table;
|
||||
const SuffixTable &street_name_suffix_table;
|
||||
const IntersectionGenerator &intersection_generator;
|
||||
const NodeBasedGraphWalker graph_walker; // for skipping traffic signal, distances etc.
|
||||
|
||||
// Decide on a basic turn types
|
||||
@@ -573,19 +567,11 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
// 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)
|
||||
const auto parameters = intersection_generator.SkipDegreeTwoNodes(
|
||||
node_at_intersection, intersection[0].eid);
|
||||
if (node_based_graph.GetTarget(parameters.via_eid) == 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);
|
||||
return intersection_generator.GetConnectedRoads(parameters.nid, parameters.via_eid);
|
||||
}();
|
||||
|
||||
if (!previous_intersection.empty())
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
struct IntersectionNormalizationOperation
|
||||
{
|
||||
// the source of the merge, not part of the intersection after the merge is performed.
|
||||
EdgeID merged_eid;
|
||||
// the edge that is covering the `merged_eid`
|
||||
EdgeID into_eid;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_*/
|
||||
@@ -0,0 +1,125 @@
|
||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_
|
||||
|
||||
#include "util/attributes.hpp"
|
||||
#include "util/name_table.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "extractor/guidance/intersection_normalization_operation.hpp"
|
||||
#include "extractor/guidance/mergable_road_detector.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "extractor/suffix_table.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
/*
|
||||
* An intersection is a central part in computing guidance decisions. However the model in OSM and
|
||||
* the view we want to use in guidance are not necessarily the same thing. We have to account for
|
||||
* some models that are chosen explicitly in OSM and that don't actually describe how a human would
|
||||
* experience an intersection.
|
||||
*
|
||||
* For example, if a small pedestrian island is located at a traffic light right in the middle of a
|
||||
* road, OSM tends to model the road as two separate ways. A human would consider these two ways a
|
||||
* single road, though. In this normalizer, we try to account for these subtle differences between
|
||||
* OSM data and human perception to improve our decision base for guidance later on.
|
||||
*/
|
||||
class IntersectionNormalizer
|
||||
{
|
||||
public:
|
||||
struct NormalizationResult
|
||||
{
|
||||
IntersectionShape normalized_shape;
|
||||
std::vector<IntersectionNormalizationOperation> performed_merges;
|
||||
};
|
||||
IntersectionNormalizer(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &node_coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator);
|
||||
|
||||
// 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
|
||||
NormalizationResult operator()(const NodeID node_at_intersection,
|
||||
IntersectionShape intersection) const;
|
||||
|
||||
private:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
const IntersectionGenerator &intersection_generator;
|
||||
const MergableRoadDetector mergable_road_detector;
|
||||
|
||||
/* 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. 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 IntersectionShape &intersection,
|
||||
std::size_t first_index,
|
||||
std::size_t second_index) const;
|
||||
|
||||
// Perform an Actual Merge
|
||||
IntersectionNormalizationOperation
|
||||
DetermineMergeDirection(const IntersectionShapeData &lhs,
|
||||
const IntersectionShapeData &rhs) const;
|
||||
IntersectionShapeData MergeRoads(const IntersectionShapeData &destination,
|
||||
const IntersectionShapeData &source) const;
|
||||
IntersectionShapeData MergeRoads(const IntersectionNormalizationOperation direction,
|
||||
const IntersectionShapeData &lhs,
|
||||
const IntersectionShapeData &rhs,
|
||||
const double opposite_bearing) const;
|
||||
|
||||
// Merge segregated roads to omit invalid turns in favor of treating segregated roads as
|
||||
// one.
|
||||
// This function combines roads the following way:
|
||||
//
|
||||
// * *
|
||||
// * is converted to *
|
||||
// v ^ +
|
||||
// v ^ +
|
||||
//
|
||||
// The treatment results in a straight turn angle of 180º rather than a turn angle of approx
|
||||
// 160
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
NormalizationResult 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.
|
||||
//
|
||||
// * *
|
||||
// * is converted to *
|
||||
// v a --- a ---
|
||||
// v ^ +
|
||||
// v ^ +
|
||||
// b
|
||||
//
|
||||
// for the local view of b at a.
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
IntersectionShape AdjustBearingsForMergeAtDestination(const NodeID node_at_intersection,
|
||||
IntersectionShape intersection) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif /* OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_ */
|
||||
@@ -1,11 +1,7 @@
|
||||
#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/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"
|
||||
@@ -13,7 +9,6 @@
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
@@ -44,10 +39,8 @@ class MergableRoadDetector
|
||||
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 IntersectionGenerator &intersection_generator,
|
||||
const CoordinateExtractor &coordinate_extractor,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table);
|
||||
|
||||
@@ -166,19 +159,15 @@ class 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 IntersectionGenerator &intersection_generator;
|
||||
const CoordinateExtractor &coordinate_extractor;
|
||||
|
||||
// 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;
|
||||
const static double constexpr distance_to_extract = 150;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
|
||||
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "extractor/guidance/intersection_handler.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
|
||||
@@ -25,12 +26,9 @@ class MotorwayHandler : public IntersectionHandler
|
||||
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);
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator);
|
||||
|
||||
~MotorwayHandler() override final = default;
|
||||
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
#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 "extractor/guidance/intersection_generator.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
@@ -31,11 +29,7 @@ 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);
|
||||
const IntersectionGenerator &intersection_generator);
|
||||
|
||||
/*
|
||||
* the returned node-id, edge-id are either the last ones used, just prior accumulator
|
||||
@@ -54,11 +48,7 @@ class NodeBasedGraphWalker
|
||||
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;
|
||||
const IntersectionGenerator &intersection_generator;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -159,13 +149,7 @@ struct SelectStraightmostRoadByNameAndOnlyChoice
|
||||
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);
|
||||
const IntersectionGenerator &intersection_generator);
|
||||
// true if the path has traversed enough distance
|
||||
bool terminate();
|
||||
|
||||
@@ -175,19 +159,13 @@ struct IntersectionFinderAccumulator
|
||||
std::uint8_t hops;
|
||||
const std::uint8_t hop_limit;
|
||||
|
||||
// we need to be able to look-up the intersection
|
||||
const IntersectionGenerator &intersection_generator;
|
||||
|
||||
// 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>
|
||||
@@ -221,15 +199,9 @@ NodeBasedGraphWalker::TraverseRoad(NodeID current_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});
|
||||
const constexpr auto LOW_PRECISION = true;
|
||||
const auto next_intersection = intersection_generator.GetConnectedRoads(
|
||||
current_node_id, current_edge_id, LOW_PRECISION);
|
||||
|
||||
// don't follow u-turns or go past our initial intersection
|
||||
if (next_intersection.size() <= 1)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "extractor/guidance/intersection_handler.hpp"
|
||||
#include "extractor/guidance/roundabout_type.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
@@ -41,12 +42,10 @@ class RoundaboutHandler : public IntersectionHandler
|
||||
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 CompressedEdgeContainer &compressed_edge_container,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table);
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator);
|
||||
|
||||
~RoundaboutHandler() override final = default;
|
||||
|
||||
@@ -65,6 +64,10 @@ class RoundaboutHandler : public IntersectionHandler
|
||||
const EdgeID via_eid,
|
||||
const Intersection &intersection) const;
|
||||
|
||||
void invalidateExitAgainstDirection(const NodeID from_nid,
|
||||
const EdgeID via_eid,
|
||||
Intersection &intersection) const;
|
||||
|
||||
// decide whether we lookk at a roundabout or a rotary
|
||||
RoundaboutType getRoundaboutType(const NodeID nid) const;
|
||||
|
||||
@@ -81,6 +84,7 @@ class RoundaboutHandler : public IntersectionHandler
|
||||
bool
|
||||
qualifiesAsRoundaboutIntersection(const std::unordered_set<NodeID> &roundabout_nodes) const;
|
||||
|
||||
const CompressedEdgeContainer &compressed_edge_container;
|
||||
const CoordinateExtractor coordinate_extractor;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_SLIPROAD_HANDLER_HPP_
|
||||
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "extractor/guidance/intersection_handler.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
|
||||
@@ -23,13 +24,10 @@ namespace guidance
|
||||
class SliproadHandler final : public IntersectionHandler
|
||||
{
|
||||
public:
|
||||
SliproadHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
SliproadHandler(const IntersectionGenerator &intersection_generator,
|
||||
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);
|
||||
|
||||
@@ -80,8 +78,6 @@ class SliproadHandler final : public IntersectionHandler
|
||||
// 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
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -27,24 +27,18 @@ namespace guidance
|
||||
class StatisticsHandler final : public IntersectionHandler
|
||||
{
|
||||
public:
|
||||
StatisticsHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
StatisticsHandler(const IntersectionGenerator &intersection_generator,
|
||||
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)
|
||||
street_name_suffix_table,
|
||||
intersection_generator)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -61,7 +55,7 @@ class StatisticsHandler final : public IntersectionHandler
|
||||
|
||||
for (const auto &kv : type_hist)
|
||||
if (kv.second > 0)
|
||||
util::Log() << " " << std::fixed << std::setprecision(2)
|
||||
util::Log() << std::fixed << std::setprecision(2)
|
||||
<< internalInstructionTypeToString(kv.first) << ": " << kv.second
|
||||
<< " (" << (kv.second / static_cast<float>(num_types) * 100.) << "%)";
|
||||
|
||||
@@ -69,7 +63,7 @@ class StatisticsHandler final : public IntersectionHandler
|
||||
|
||||
for (const auto &kv : modifier_hist)
|
||||
if (kv.second > 0)
|
||||
util::Log() << " " << std::fixed << std::setprecision(2)
|
||||
util::Log() << std::fixed << std::setprecision(2)
|
||||
<< instructionModifierToString(kv.first) << ": " << kv.second << " ("
|
||||
<< (kv.second / static_cast<float>(num_modifiers) * 100.) << "%)";
|
||||
}
|
||||
@@ -90,14 +84,12 @@ class StatisticsHandler final : public IntersectionHandler
|
||||
// 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;
|
||||
}
|
||||
const auto type = road.instruction.type;
|
||||
const auto modifier = road.instruction.direction_modifier;
|
||||
|
||||
type_hist[type] += 1;
|
||||
modifier_hist[modifier] += 1;
|
||||
}
|
||||
|
||||
return intersection;
|
||||
@@ -105,8 +97,8 @@ class StatisticsHandler final : public IntersectionHandler
|
||||
|
||||
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;
|
||||
mutable std::unordered_map<TurnType::Enum, std::uint64_t> type_hist;
|
||||
mutable std::unordered_map<DirectionModifier::Enum, std::uint64_t> modifier_hist;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "extractor/guidance/intersection_handler.hpp"
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
@@ -20,13 +21,10 @@ namespace guidance
|
||||
class SuppressModeHandler final : public IntersectionHandler
|
||||
{
|
||||
public:
|
||||
SuppressModeHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
SuppressModeHandler(const IntersectionGenerator &intersection_generator,
|
||||
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);
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/guidance/driveway_handler.hpp"
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "extractor/guidance/intersection_normalization_operation.hpp"
|
||||
#include "extractor/guidance/intersection_normalizer.hpp"
|
||||
#include "extractor/guidance/motorway_handler.hpp"
|
||||
#include "extractor/guidance/roundabout_handler.hpp"
|
||||
#include "extractor/guidance/sliproad_handler.hpp"
|
||||
@@ -40,11 +43,10 @@ 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 std::vector<util::Coordinate> &coordinates,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||
const CompressedEdgeContainer &compressed_edge_container,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table);
|
||||
|
||||
@@ -54,14 +56,34 @@ class TurnAnalysis
|
||||
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;
|
||||
// normalized shape, merged some roads into others, adjusted bearings
|
||||
// see intersection_normalizer for further explanations
|
||||
IntersectionNormalizer::NormalizationResult annotated_normalized_shape;
|
||||
};
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
ShapeResult ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) 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;
|
||||
|
||||
const IntersectionGenerator &GetIntersectionGenerator() const;
|
||||
|
||||
private:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
const IntersectionGenerator intersection_generator;
|
||||
const IntersectionNormalizer intersection_normalizer;
|
||||
const RoundaboutHandler roundabout_handler;
|
||||
const MotorwayHandler motorway_handler;
|
||||
const TurnHandler turn_handler;
|
||||
|
||||
@@ -2,27 +2,15 @@
|
||||
#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 "extractor/guidance/intersection_generator.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
class Coordinate;
|
||||
}
|
||||
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
class CompressedEdgeContainer;
|
||||
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
namespace lanes
|
||||
{
|
||||
|
||||
@@ -33,13 +21,8 @@ bool findPreviousIntersection(
|
||||
const NodeID node,
|
||||
const EdgeID via_edge,
|
||||
const Intersection &intersection,
|
||||
const IntersectionGenerator &intersection_generator,
|
||||
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,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_
|
||||
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "extractor/guidance/intersection_handler.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
|
||||
@@ -29,12 +30,9 @@ class TurnHandler : public IntersectionHandler
|
||||
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);
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator);
|
||||
|
||||
~TurnHandler() override final = default;
|
||||
|
||||
|
||||
@@ -74,11 +74,6 @@ class TurnLaneHandler
|
||||
|
||||
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);
|
||||
@@ -95,12 +90,6 @@ class TurnLaneHandler
|
||||
// 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;
|
||||
|
||||
@@ -100,10 +100,8 @@ typedef util::ConcurrentIDMap<guidance::TurnLaneDescription,
|
||||
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)
|
||||
inline std::tuple<std::vector<std::uint32_t>, std::vector<TurnLaneType::Mask>>
|
||||
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.
|
||||
@@ -113,7 +111,8 @@ inline TurnLanesIndexedArray transformTurnLaneMapIntoArrays(const LaneDescriptio
|
||||
//
|
||||
// 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
|
||||
std::vector<std::uint32_t> turn_lane_offsets(turn_lane_map.data.size() +
|
||||
2); // empty ID + 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();
|
||||
|
||||
@@ -126,7 +125,6 @@ inline TurnLanesIndexedArray transformTurnLaneMapIntoArrays(const LaneDescriptio
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
#ifndef OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_ANALYSIS_HPP
|
||||
#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 "util/coordinate.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace intersection
|
||||
{
|
||||
|
||||
IntersectionEdges getIncomingEdges(const util::NodeBasedDynamicGraph &graph,
|
||||
const NodeID intersection);
|
||||
|
||||
IntersectionEdges getOutgoingEdges(const util::NodeBasedDynamicGraph &graph,
|
||||
const NodeID intersection);
|
||||
|
||||
bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const IntersectionEdgeGeometries &geometries,
|
||||
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||
const IntersectionEdge &from,
|
||||
const IntersectionEdge &to);
|
||||
|
||||
double findEdgeBearing(const IntersectionEdgeGeometries &geometries, const EdgeID &edge);
|
||||
|
||||
double findEdgeLength(const IntersectionEdgeGeometries &geometries, const EdgeID &edge);
|
||||
|
||||
std::pair<IntersectionEdgeGeometries, std::unordered_set<EdgeID>>
|
||||
getIntersectionGeometries(const util::NodeBasedDynamicGraph &graph,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<util::Coordinate> &node_coordinates,
|
||||
const guidance::MergableRoadDetector &detector,
|
||||
const NodeID intersection);
|
||||
|
||||
guidance::IntersectionView
|
||||
convertToIntersectionView(const util::NodeBasedDynamicGraph &graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const IntersectionEdgeGeometries &edge_geometries,
|
||||
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||
const IntersectionEdge &incoming_edge,
|
||||
const IntersectionEdges &outgoing_edges,
|
||||
const std::unordered_set<EdgeID> &merged_edges);
|
||||
|
||||
// Check for restrictions/barriers and generate a list of valid and invalid turns present at
|
||||
// the node reached from `incoming_edge`. The resulting candidates have to be analyzed
|
||||
// for their actual instructions later on.
|
||||
template <bool USE_CLOSE_COORDINATE>
|
||||
guidance::IntersectionView
|
||||
getConnectedRoads(const util::NodeBasedDynamicGraph &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 IntersectionEdge &incoming_edge);
|
||||
|
||||
// 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
|
||||
// intermediate intersection, if there is a traffic light in between. If we want to look farther
|
||||
// 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. This function skips over degree two nodes to find correct input
|
||||
// for getConnectedRoads.
|
||||
IntersectionEdge skipDegreeTwoNodes(const util::NodeBasedDynamicGraph &graph,
|
||||
IntersectionEdge road);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,44 +0,0 @@
|
||||
#ifndef OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_EDGE_HPP
|
||||
#define OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_EDGE_HPP
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace intersection
|
||||
{
|
||||
|
||||
// IntersectionEdge is an alias for incoming and outgoing node-based graph edges of an intersection
|
||||
struct IntersectionEdge
|
||||
{
|
||||
NodeID node;
|
||||
EdgeID edge;
|
||||
|
||||
bool operator<(const IntersectionEdge &other) const
|
||||
{
|
||||
return std::tie(node, edge) < std::tie(other.node, other.edge);
|
||||
}
|
||||
};
|
||||
|
||||
using IntersectionEdges = std::vector<IntersectionEdge>;
|
||||
|
||||
struct IntersectionEdgeGeometry
|
||||
{
|
||||
EdgeID edge;
|
||||
double initial_bearing;
|
||||
double perceived_bearing;
|
||||
double length;
|
||||
|
||||
bool operator<(const IntersectionEdgeGeometry &other) const { return edge < other.edge; }
|
||||
};
|
||||
|
||||
using IntersectionEdgeGeometries = std::vector<IntersectionEdgeGeometry>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -41,13 +41,9 @@ class NodeBasedGraphFactory
|
||||
std::vector<TurnRestriction> &turn_restrictions,
|
||||
std::vector<ConditionalTurnRestriction> &conditional_turn_restrictions);
|
||||
|
||||
auto const &GetGraph() const { return compressed_output_graph; }
|
||||
auto const &GetGraph() { return compressed_output_graph; }
|
||||
auto const &GetBarriers() const { return barriers; }
|
||||
auto const &GetTrafficSignals() const { return traffic_signals; }
|
||||
auto const &GetCompressedEdges() const { return compressed_edge_container; }
|
||||
auto const &GetCoordinates() const { return coordinates; }
|
||||
auto const &GetAnnotationData() const { return annotation_data; }
|
||||
auto const &GetOsmNodes() const { return osm_node_ids; }
|
||||
auto &GetCompressedEdges() { return compressed_edge_container; }
|
||||
auto &GetCoordinates() { return coordinates; }
|
||||
auto &GetAnnotationData() { return annotation_data; }
|
||||
|
||||
@@ -186,8 +186,6 @@ inline engine_config_ptr argumentsToEngineConfig(const Nan::FunctionCallbackInfo
|
||||
params->Get(Nan::New("max_locations_map_matching").ToLocalChecked());
|
||||
auto max_results_nearest = params->Get(Nan::New("max_results_nearest").ToLocalChecked());
|
||||
auto max_alternatives = params->Get(Nan::New("max_alternatives").ToLocalChecked());
|
||||
auto max_radius_map_matching =
|
||||
params->Get(Nan::New("max_radius_map_matching").ToLocalChecked());
|
||||
|
||||
if (!max_locations_trip->IsUndefined() && !max_locations_trip->IsNumber())
|
||||
{
|
||||
@@ -235,9 +233,6 @@ inline engine_config_ptr argumentsToEngineConfig(const Nan::FunctionCallbackInfo
|
||||
engine_config->max_results_nearest = static_cast<int>(max_results_nearest->NumberValue());
|
||||
if (max_alternatives->IsNumber())
|
||||
engine_config->max_alternatives = static_cast<int>(max_alternatives->NumberValue());
|
||||
if (max_radius_map_matching->IsNumber())
|
||||
engine_config->max_radius_map_matching =
|
||||
static_cast<double>(max_radius_map_matching->NumberValue());
|
||||
|
||||
return engine_config;
|
||||
}
|
||||
|
||||
@@ -144,6 +144,18 @@ inline double restrictAngleToValidRange(const double angle)
|
||||
return angle;
|
||||
}
|
||||
|
||||
// finds the angle between two angles, based on the minum difference between the two
|
||||
inline double angleBetween(const double lhs, const double rhs)
|
||||
{
|
||||
const auto difference = std::abs(lhs - rhs);
|
||||
const auto is_clockwise_difference = difference <= 180;
|
||||
const auto angle_between_candidate = .5 * (lhs + rhs);
|
||||
if (is_clockwise_difference)
|
||||
return angle_between_candidate;
|
||||
else
|
||||
return restrictAngleToValidRange(angle_between_candidate + 180);
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace osrm
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "osrm",
|
||||
"version": "5.15.0-latest.4",
|
||||
"version": "5.14.3",
|
||||
"private": false,
|
||||
"description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.",
|
||||
"dependencies": {
|
||||
|
||||
@@ -37,10 +37,6 @@ function setup()
|
||||
turn_bias = 1.075,
|
||||
cardinal_directions = false,
|
||||
|
||||
-- Size of the vehicle, to be limited by physical restriction of the way
|
||||
vehicle_height = 2.5, -- in metters, 2.5m is the height of van
|
||||
vehicle_width = 1.9, -- in metters, ways with narrow tag are considered narrower than 2.2m
|
||||
|
||||
-- a list of suffixes to suppress in name change instructions. The suffixes also include common substrings of each other
|
||||
suffix_list = {
|
||||
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East', 'Nor', 'Sou', 'We', 'Ea'
|
||||
@@ -361,9 +357,6 @@ function process_way(profile, way, result, relations)
|
||||
-- routable. this includes things like status=impassable,
|
||||
-- toll=yes and oneway=reversible
|
||||
WayHandlers.blocked_ways,
|
||||
WayHandlers.avoid_ways,
|
||||
WayHandlers.handle_height,
|
||||
WayHandlers.handle_width,
|
||||
|
||||
-- determine access status by checking our hierarchy of
|
||||
-- access tags, e.g: motorcar, motor_vehicle, vehicle
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
local Sequence = require('lib/sequence')
|
||||
|
||||
Measure = {}
|
||||
|
||||
-- measurements conversion constants
|
||||
local inch_to_meters = 0.0254
|
||||
local feet_to_inches = 12
|
||||
|
||||
--- Parse string as a height in meters.
|
||||
--- according to http://wiki.openstreetmap.org/wiki/Key:maxheight
|
||||
function Measure.parse_value_meters(value)
|
||||
local n = tonumber(value:gsub(",", "."):match("%d+%.?%d*"))
|
||||
if n then
|
||||
inches = value:match("'.*")
|
||||
if inches then -- Imperial unit to metric
|
||||
-- try to parse feets/inch
|
||||
n = n * feet_to_inches
|
||||
local m = tonumber(inches:match("%d+"))
|
||||
if m then
|
||||
n = n + m
|
||||
end
|
||||
n = n * inch_to_meters
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
print("Can't parse value: ", value)
|
||||
end
|
||||
|
||||
--- according to http://wiki.openstreetmap.org/wiki/Map_Features/Units#Explicit_specifications
|
||||
local tonns_parse_patterns = Sequence {
|
||||
"%d+",
|
||||
"%d+.%d+",
|
||||
"%d+.%d+ ?t"
|
||||
}
|
||||
|
||||
local kg_parse_patterns = Sequence {
|
||||
"%d+ ?kg"
|
||||
}
|
||||
|
||||
--- Parse weight value in kilograms
|
||||
function Measure.parse_value_kilograms(value)
|
||||
-- try to parse kilograms
|
||||
for i, templ in ipairs(kg_parse_patterns) do
|
||||
m = string.match(value, templ)
|
||||
if m then
|
||||
return tonumber(m)
|
||||
end
|
||||
end
|
||||
|
||||
-- try to parse tonns
|
||||
for i, templ in ipairs(tonns_parse_patterns) do
|
||||
m = string.match(value, templ)
|
||||
if m then
|
||||
return tonumber(m) * 1000
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
print("Can't parse value: ", value)
|
||||
return
|
||||
end
|
||||
|
||||
--- Get maxheight of specified way in meters. If there are no
|
||||
--- max height, then return nil
|
||||
function Measure.get_max_height(raw_value)
|
||||
if raw_value then
|
||||
return Measure.parse_value_meters(raw_value)
|
||||
end
|
||||
end
|
||||
|
||||
--- Get maxwidth of specified way in meters.
|
||||
function Measure.get_max_width(raw_value)
|
||||
if raw_value then
|
||||
return Measure.parse_value_meters(raw_value)
|
||||
end
|
||||
end
|
||||
|
||||
--- Get maxweight of specified way in kilogramms
|
||||
function Measure.get_max_weight(raw_value)
|
||||
if raw_value then
|
||||
return Measure.parse_value_kilograms(raw_value)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return Measure;
|
||||
@@ -8,7 +8,6 @@ local get_turn_lanes = require("lib/guidance").get_turn_lanes
|
||||
local set_classification = require("lib/guidance").set_classification
|
||||
local get_destination = require("lib/destination").get_destination
|
||||
local Tags = require('lib/tags')
|
||||
local Measure = require("lib/measure")
|
||||
|
||||
WayHandlers = {}
|
||||
|
||||
@@ -430,47 +429,6 @@ function WayHandlers.parse_maxspeed(source,profile)
|
||||
return n
|
||||
end
|
||||
|
||||
-- handle maxheight tags
|
||||
function WayHandlers.handle_height(profile,way,result,data)
|
||||
local keys = Sequence { 'maxheight:physical', 'maxheight' }
|
||||
local forward, backward = Tags.get_forward_backward_by_set(way,data,keys)
|
||||
forward = Measure.get_max_height(forward)
|
||||
backward = Measure.get_max_height(backward)
|
||||
|
||||
if forward and forward < profile.vehicle_height then
|
||||
result.forward_mode = mode.inaccessible
|
||||
end
|
||||
|
||||
if backward and backward < profile.vehicle_height then
|
||||
result.backward_mode = mode.inaccessible
|
||||
end
|
||||
end
|
||||
|
||||
-- handle maxwidth tags
|
||||
function WayHandlers.handle_width(profile,way,result,data)
|
||||
local keys = Sequence { 'maxwidth:physical', 'maxwidth', 'width', 'est_width' }
|
||||
local forward, backward = Tags.get_forward_backward_by_set(way,data,keys)
|
||||
local narrow = way:get_value_by_key('narrow')
|
||||
|
||||
if ((forward and forward == 'narrow') or (narrow and narrow == 'yes')) and profile.vehicle_width > 2.2 then
|
||||
result.forward_mode = mode.inaccessible
|
||||
elseif forward then
|
||||
forward = Measure.get_max_width(forward)
|
||||
if forward and forward <= profile.vehicle_width then
|
||||
result.forward_mode = mode.inaccessible
|
||||
end
|
||||
end
|
||||
|
||||
if ((backward and backward == 'narrow') or (narrow and narrow == 'yes')) and profile.vehicle_width > 2.2 then
|
||||
result.backward_mode = mode.inaccessible
|
||||
elseif backward then
|
||||
backward = Measure.get_max_width(backward)
|
||||
if backward and backward <= profile.vehicle_width then
|
||||
result.backward_mode = mode.inaccessible
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- handle oneways tags
|
||||
function WayHandlers.oneway(profile,way,result,data)
|
||||
if not profile.oneway_handling then
|
||||
@@ -532,15 +490,6 @@ function WayHandlers.weights(profile,way,result,data)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- handle general avoid rules
|
||||
|
||||
function WayHandlers.avoid_ways(profile,way,result,data)
|
||||
if profile.avoid[data.highway] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- handle various that can block access
|
||||
function WayHandlers.blocked_ways(profile,way,result,data)
|
||||
|
||||
|
||||
@@ -71,10 +71,10 @@ def build_pretty_printer():
|
||||
pp.add_printer('TurnLaneData', '::TurnLaneData$', TurnLaneDataPrinter)
|
||||
return pp
|
||||
|
||||
## unregister OSRM pretty printer before (re)loading
|
||||
gdb.pretty_printers = [x for x in gdb.pretty_printers if not isinstance(x, gdb.printing.RegexpCollectionPrettyPrinter) or x.name != 'OSRM']
|
||||
gdb.pretty_printers = [x for x in gdb.pretty_printers if x.name != 'OSRM'] # unregister OSRM pretty printer before (re)loading
|
||||
gdb.printing.register_pretty_printer(gdb.current_objfile(), build_pretty_printer())
|
||||
|
||||
|
||||
import geojson
|
||||
import os
|
||||
import time
|
||||
|
||||
@@ -11,13 +11,12 @@ bool EngineConfig::IsValid() const
|
||||
// leads to an empty path
|
||||
const bool all_path_are_empty = storage_config.GetPath("").empty();
|
||||
|
||||
const auto unlimited_or_more_than = [](const auto v, const auto limit) {
|
||||
const auto unlimited_or_more_than = [](const int v, const int limit) {
|
||||
return v == -1 || v > limit;
|
||||
};
|
||||
|
||||
const bool limits_valid = unlimited_or_more_than(max_locations_distance_table, 2) &&
|
||||
unlimited_or_more_than(max_locations_map_matching, 2) &&
|
||||
unlimited_or_more_than(max_radius_map_matching, 0) &&
|
||||
unlimited_or_more_than(max_locations_trip, 2) &&
|
||||
unlimited_or_more_than(max_locations_viaroute, 2) &&
|
||||
unlimited_or_more_than(max_results_nearest, 0) &&
|
||||
|
||||
@@ -139,17 +139,6 @@ Status MatchPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
|
||||
return Error("InvalidValue", "Invalid coordinate value.", json_result);
|
||||
}
|
||||
|
||||
if (max_radius_map_matching > 0 && std::any_of(parameters.radiuses.begin(),
|
||||
parameters.radiuses.end(),
|
||||
[&](const auto &radius) {
|
||||
if (!radius)
|
||||
return false;
|
||||
return *radius > max_radius_map_matching;
|
||||
}))
|
||||
{
|
||||
return Error("TooBig", "Radius search size is too large for map matching.", json_result);
|
||||
}
|
||||
|
||||
// Check for same or increasing timestamps. Impl. note: Incontrast to `sort(first,
|
||||
// last, less_equal)` checking `greater` in reverse meets irreflexive requirements.
|
||||
const auto time_increases_monotonically = std::is_sorted(
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include "extractor/scripting_environment.hpp"
|
||||
#include "extractor/suffix_table.hpp"
|
||||
|
||||
#include "extractor/intersection/intersection_analysis.hpp"
|
||||
|
||||
#include "extractor/serialization.hpp"
|
||||
#include "storage/io.hpp"
|
||||
|
||||
@@ -423,39 +421,22 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
|
||||
TurnDataExternalContainer turn_data_container;
|
||||
|
||||
SuffixTable street_name_suffix_table(scripting_environment);
|
||||
const auto &turn_lanes_data = transformTurnLaneMapIntoArrays(lane_description_map);
|
||||
guidance::MergableRoadDetector mergable_road_detector(m_node_based_graph,
|
||||
m_edge_based_node_container,
|
||||
m_coordinates,
|
||||
m_compressed_edge_container,
|
||||
node_restriction_map,
|
||||
m_barrier_nodes,
|
||||
turn_lanes_data,
|
||||
name_table,
|
||||
street_name_suffix_table);
|
||||
|
||||
// Loop over all turns and generate new set of edges.
|
||||
// Three nested loop look super-linear, but we are dealing with a (kind of)
|
||||
// linear number of turns only.
|
||||
SuffixTable street_name_suffix_table(scripting_environment);
|
||||
guidance::TurnAnalysis turn_analysis(m_node_based_graph,
|
||||
m_edge_based_node_container,
|
||||
m_coordinates,
|
||||
m_compressed_edge_container,
|
||||
node_restriction_map,
|
||||
m_barrier_nodes,
|
||||
turn_lanes_data,
|
||||
m_compressed_edge_container,
|
||||
name_table,
|
||||
street_name_suffix_table);
|
||||
|
||||
util::guidance::LaneDataIdMap lane_data_map;
|
||||
guidance::lanes::TurnLaneHandler turn_lane_handler(m_node_based_graph,
|
||||
m_edge_based_node_container,
|
||||
m_coordinates,
|
||||
m_compressed_edge_container,
|
||||
node_restriction_map,
|
||||
m_barrier_nodes,
|
||||
turn_lanes_data,
|
||||
lane_description_map,
|
||||
turn_analysis,
|
||||
lane_data_map);
|
||||
@@ -558,14 +539,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
// the situation of the turn
|
||||
const auto node_along_road_entering,
|
||||
const auto node_based_edge_from,
|
||||
const auto intersection_node,
|
||||
const auto node_at_center_of_intersection,
|
||||
const auto node_based_edge_to,
|
||||
const auto incoming_bearing,
|
||||
const auto &intersection,
|
||||
const auto &turn,
|
||||
const auto entry_class_id) {
|
||||
|
||||
const auto node_restricted = isRestricted(node_along_road_entering,
|
||||
intersection_node,
|
||||
node_at_center_of_intersection,
|
||||
m_node_based_graph.GetTarget(turn.eid),
|
||||
conditional_restriction_map);
|
||||
|
||||
@@ -577,7 +558,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
conditional = {{edge_based_node_from,
|
||||
edge_based_node_to,
|
||||
{static_cast<std::uint64_t>(-1),
|
||||
m_coordinates[intersection_node],
|
||||
m_coordinates[node_at_center_of_intersection],
|
||||
conditions}}};
|
||||
}
|
||||
|
||||
@@ -593,14 +574,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
TurnData turn_data = {turn.instruction,
|
||||
turn.lane_data_id,
|
||||
entry_class_id,
|
||||
util::guidance::TurnBearing(incoming_bearing),
|
||||
util::guidance::TurnBearing(intersection[0].bearing),
|
||||
util::guidance::TurnBearing(turn.bearing)};
|
||||
|
||||
// compute weight and duration penalties
|
||||
auto is_traffic_light = m_traffic_lights.count(intersection_node);
|
||||
auto is_traffic_light = m_traffic_lights.count(node_at_center_of_intersection);
|
||||
ExtractionTurn extracted_turn(
|
||||
turn.angle,
|
||||
m_node_based_graph.GetOutDegree(intersection_node),
|
||||
m_node_based_graph.GetOutDegree(node_at_center_of_intersection),
|
||||
turn.instruction.IsUTurn(),
|
||||
is_traffic_light,
|
||||
edge_data1.flags.restricted,
|
||||
@@ -651,7 +632,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
: m_compressed_edge_container.GetLastEdgeSourceID(node_based_edge_from);
|
||||
const auto &to_node = m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid);
|
||||
|
||||
lookup::TurnIndexBlock turn_index_block = {from_node, intersection_node, to_node};
|
||||
lookup::TurnIndexBlock turn_index_block = {
|
||||
from_node, node_at_center_of_intersection, to_node};
|
||||
|
||||
// insert data into the designated buffer
|
||||
return std::make_pair(
|
||||
@@ -673,26 +655,17 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
if (buffer->nodes_processed == 0)
|
||||
return buffer;
|
||||
|
||||
for (auto intersection_node = intersection_node_range.begin(),
|
||||
for (auto node_at_center_of_intersection = intersection_node_range.begin(),
|
||||
end = intersection_node_range.end();
|
||||
intersection_node < end;
|
||||
++intersection_node)
|
||||
node_at_center_of_intersection < end;
|
||||
++node_at_center_of_intersection)
|
||||
{
|
||||
|
||||
// We capture the thread-local work in these objects, then flush
|
||||
// them in a controlled manner at the end of the parallel range
|
||||
const auto &incoming_edges =
|
||||
intersection::getIncomingEdges(m_node_based_graph, intersection_node);
|
||||
const auto &outgoing_edges =
|
||||
intersection::getOutgoingEdges(m_node_based_graph, intersection_node);
|
||||
|
||||
intersection::IntersectionEdgeGeometries edge_geometries;
|
||||
std::unordered_set<EdgeID> merged_edge_ids;
|
||||
std::tie(edge_geometries, merged_edge_ids) =
|
||||
intersection::getIntersectionGeometries(m_node_based_graph,
|
||||
m_compressed_edge_container,
|
||||
m_coordinates,
|
||||
mergable_road_detector,
|
||||
intersection_node);
|
||||
const auto shape_result =
|
||||
turn_analysis.ComputeIntersectionShapes(node_at_center_of_intersection);
|
||||
|
||||
// all nodes in the graph are connected in both directions. We check all
|
||||
// outgoing nodes to find the incoming edge. This is a larger search overhead,
|
||||
@@ -712,33 +685,45 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
// From the flags alone, we cannot determine which nodes are connected to
|
||||
// `b` by an outgoing edge. Therefore, we have to search all connected edges for
|
||||
// edges entering `b`
|
||||
|
||||
for (const auto &incoming_edge : incoming_edges)
|
||||
for (const EdgeID outgoing_edge :
|
||||
m_node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection))
|
||||
{
|
||||
const NodeID node_along_road_entering =
|
||||
m_node_based_graph.GetTarget(outgoing_edge);
|
||||
|
||||
const auto incoming_edge = m_node_based_graph.FindEdge(
|
||||
node_along_road_entering, node_at_center_of_intersection);
|
||||
|
||||
if (m_node_based_graph.GetEdgeData(incoming_edge).reversed)
|
||||
continue;
|
||||
|
||||
++node_based_edge_counter;
|
||||
|
||||
const auto intersection_view =
|
||||
convertToIntersectionView(m_node_based_graph,
|
||||
m_edge_based_node_container,
|
||||
node_restriction_map,
|
||||
m_barrier_nodes,
|
||||
edge_geometries,
|
||||
turn_lanes_data,
|
||||
incoming_edge,
|
||||
outgoing_edges,
|
||||
merged_edge_ids);
|
||||
auto intersection_with_flags_and_angles =
|
||||
turn_analysis.GetIntersectionGenerator()
|
||||
.TransformIntersectionShapeIntoView(
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
shape_result.annotated_normalized_shape.normalized_shape,
|
||||
shape_result.intersection_shape,
|
||||
shape_result.annotated_normalized_shape.performed_merges);
|
||||
|
||||
auto intersection = turn_analysis.AssignTurnTypes(
|
||||
incoming_edge.node, incoming_edge.edge, intersection_view);
|
||||
auto intersection =
|
||||
turn_analysis.AssignTurnTypes(node_along_road_entering,
|
||||
incoming_edge,
|
||||
intersection_with_flags_and_angles);
|
||||
|
||||
OSRM_ASSERT(intersection.valid(),
|
||||
m_coordinates[node_at_center_of_intersection]);
|
||||
|
||||
OSRM_ASSERT(intersection.valid(), m_coordinates[intersection_node]);
|
||||
intersection = turn_lane_handler.assignTurnLanes(
|
||||
incoming_edge.node, incoming_edge.edge, std::move(intersection));
|
||||
node_along_road_entering, incoming_edge, std::move(intersection));
|
||||
|
||||
// the entry class depends on the turn, so we have to classify the
|
||||
// interesction for every edge
|
||||
const auto turn_classification =
|
||||
classifyIntersection(intersection, m_coordinates[intersection_node]);
|
||||
// interesction for
|
||||
// every edge
|
||||
const auto turn_classification = classifyIntersection(
|
||||
intersection, m_coordinates[node_at_center_of_intersection]);
|
||||
|
||||
const auto entry_class_id =
|
||||
entry_class_hash.ConcurrentFindOrAdd(turn_classification.first);
|
||||
@@ -749,37 +734,19 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
// Note - this is strictly speaking not thread safe, but we know we
|
||||
// should never be touching the same element twice, so we should
|
||||
// be fine.
|
||||
bearing_class_by_node_based_node[intersection_node] = bearing_class_id;
|
||||
bearing_class_by_node_based_node[node_at_center_of_intersection] =
|
||||
bearing_class_id;
|
||||
|
||||
// check if we are turning off a via way
|
||||
const auto turning_off_via_way =
|
||||
way_restriction_map.IsViaWay(incoming_edge.node, intersection_node);
|
||||
const auto turning_off_via_way = way_restriction_map.IsViaWay(
|
||||
node_along_road_entering, node_at_center_of_intersection);
|
||||
|
||||
// Save reversed incoming bearing to compute turn angles
|
||||
const auto reversed_incoming_bearing = util::bearing::reverse(
|
||||
findEdgeBearing(edge_geometries, incoming_edge.edge));
|
||||
|
||||
for (const auto &outgoing_edge : outgoing_edges)
|
||||
for (const auto &turn : intersection)
|
||||
{
|
||||
if (!intersection::isTurnAllowed(m_node_based_graph,
|
||||
m_edge_based_node_container,
|
||||
node_restriction_map,
|
||||
m_barrier_nodes,
|
||||
edge_geometries,
|
||||
turn_lanes_data,
|
||||
incoming_edge,
|
||||
outgoing_edge))
|
||||
// only keep valid turns
|
||||
if (!turn.entry_allowed)
|
||||
continue;
|
||||
|
||||
const auto turn =
|
||||
std::find_if(intersection.begin(),
|
||||
intersection.end(),
|
||||
[edge = outgoing_edge.edge](const auto &road) {
|
||||
return road.eid == edge;
|
||||
});
|
||||
OSRM_ASSERT(turn != intersection.end(),
|
||||
m_coordinates[intersection_node]);
|
||||
|
||||
// In case a way restriction starts at a given location, add a turn onto
|
||||
// every artificial node eminating here.
|
||||
//
|
||||
@@ -801,22 +768,22 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
// duplicated node associated with the turn. (e.g. ab via bc switches bc
|
||||
// to bc_dup)
|
||||
auto const target_id = way_restriction_map.RemapIfRestricted(
|
||||
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||
incoming_edge.node,
|
||||
outgoing_edge.node,
|
||||
m_node_based_graph.GetTarget(outgoing_edge.edge),
|
||||
nbe_to_ebn_mapping[turn.eid],
|
||||
node_along_road_entering,
|
||||
node_at_center_of_intersection,
|
||||
m_node_based_graph.GetTarget(turn.eid),
|
||||
m_number_of_edge_based_nodes);
|
||||
|
||||
{ // scope to forget edge_with_data after
|
||||
const auto edge_with_data_and_condition =
|
||||
generate_edge(nbe_to_ebn_mapping[incoming_edge.edge],
|
||||
generate_edge(nbe_to_ebn_mapping[incoming_edge],
|
||||
target_id,
|
||||
incoming_edge.node,
|
||||
incoming_edge.edge,
|
||||
outgoing_edge.node,
|
||||
outgoing_edge.edge,
|
||||
reversed_incoming_bearing,
|
||||
*turn,
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
node_at_center_of_intersection,
|
||||
turn.eid,
|
||||
intersection,
|
||||
turn,
|
||||
entry_class_id);
|
||||
|
||||
buffer->continuous_data.edges_list.push_back(
|
||||
@@ -843,7 +810,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
if (turning_off_via_way)
|
||||
{
|
||||
const auto duplicated_nodes = way_restriction_map.DuplicatedNodeIDs(
|
||||
incoming_edge.node, intersection_node);
|
||||
node_along_road_entering, node_at_center_of_intersection);
|
||||
|
||||
// next to the normal restrictions tracked in `entry_allowed`, via
|
||||
// ways might introduce additional restrictions. These are handled
|
||||
@@ -851,12 +818,12 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
for (auto duplicated_node_id : duplicated_nodes)
|
||||
{
|
||||
const auto from_id =
|
||||
NodeID(m_number_of_edge_based_nodes -
|
||||
way_restriction_map.NumberOfDuplicatedNodes() +
|
||||
duplicated_node_id);
|
||||
m_number_of_edge_based_nodes -
|
||||
way_restriction_map.NumberOfDuplicatedNodes() +
|
||||
duplicated_node_id;
|
||||
|
||||
auto const node_at_end_of_turn =
|
||||
m_node_based_graph.GetTarget(outgoing_edge.edge);
|
||||
m_node_based_graph.GetTarget(turn.eid);
|
||||
|
||||
const auto is_way_restricted = way_restriction_map.IsRestricted(
|
||||
duplicated_node_id, node_at_end_of_turn);
|
||||
@@ -871,14 +838,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
|
||||
// add into delayed data
|
||||
auto edge_with_data_and_condition =
|
||||
generate_edge(from_id,
|
||||
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||
incoming_edge.node,
|
||||
incoming_edge.edge,
|
||||
outgoing_edge.node,
|
||||
outgoing_edge.edge,
|
||||
reversed_incoming_bearing,
|
||||
*turn,
|
||||
generate_edge(NodeID(from_id),
|
||||
nbe_to_ebn_mapping[turn.eid],
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
node_at_center_of_intersection,
|
||||
turn.eid,
|
||||
intersection,
|
||||
turn,
|
||||
entry_class_id);
|
||||
|
||||
buffer->delayed_data.push_back(
|
||||
@@ -895,24 +862,24 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
{
|
||||
// add a new conditional for the edge we just created
|
||||
buffer->conditionals.push_back(
|
||||
{from_id,
|
||||
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||
{NodeID(from_id),
|
||||
nbe_to_ebn_mapping[turn.eid],
|
||||
{static_cast<std::uint64_t>(-1),
|
||||
m_coordinates[intersection_node],
|
||||
m_coordinates[node_at_center_of_intersection],
|
||||
restriction.condition}});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto edge_with_data_and_condition =
|
||||
generate_edge(from_id,
|
||||
nbe_to_ebn_mapping[outgoing_edge.edge],
|
||||
incoming_edge.node,
|
||||
incoming_edge.edge,
|
||||
outgoing_edge.node,
|
||||
outgoing_edge.edge,
|
||||
reversed_incoming_bearing,
|
||||
*turn,
|
||||
generate_edge(NodeID(from_id),
|
||||
nbe_to_ebn_mapping[turn.eid],
|
||||
node_along_road_entering,
|
||||
incoming_edge,
|
||||
node_at_center_of_intersection,
|
||||
turn.eid,
|
||||
intersection,
|
||||
turn,
|
||||
entry_class_id);
|
||||
|
||||
buffer->delayed_data.push_back(
|
||||
|
||||
+228
-4
@@ -13,8 +13,6 @@
|
||||
#include "extractor/restriction_parser.hpp"
|
||||
#include "extractor/scripting_environment.hpp"
|
||||
|
||||
#include "extractor/guidance/segregated_intersection_classification.hpp"
|
||||
|
||||
#include "storage/io.hpp"
|
||||
|
||||
#include "util/exception.hpp"
|
||||
@@ -222,8 +220,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
util::Log() << "Find segregated edges in node-based graph ..." << std::flush;
|
||||
TIMER_START(segregated);
|
||||
|
||||
util::NameTable names(config.GetPath(".osrm.names").string());
|
||||
auto segregated_edges = guidance::findSegregatedNodes(node_based_graph_factory, names);
|
||||
auto segregated_edges = FindSegregatedNodes(node_based_graph_factory);
|
||||
|
||||
TIMER_STOP(segregated);
|
||||
util::Log() << "ok, after " << TIMER_SEC(segregated) << "s";
|
||||
@@ -841,5 +838,232 @@ void Extractor::WriteCompressedNodeBasedGraph(const std::string &path,
|
||||
}
|
||||
}
|
||||
|
||||
struct EdgeInfo
|
||||
{
|
||||
NodeID node;
|
||||
|
||||
util::StringView name;
|
||||
|
||||
// 0 - outgoing (forward), 1 - incoming (reverse), 2 - both outgoing and incoming
|
||||
int direction;
|
||||
|
||||
ClassData road_class;
|
||||
|
||||
guidance::RoadPriorityClass::Enum road_priority_class;
|
||||
|
||||
struct LessName
|
||||
{
|
||||
bool operator()(EdgeInfo const &e1, EdgeInfo const &e2) const { return e1.name < e2.name; }
|
||||
};
|
||||
};
|
||||
|
||||
bool IsSegregated(std::vector<EdgeInfo> v1,
|
||||
std::vector<EdgeInfo> v2,
|
||||
EdgeInfo const ¤t,
|
||||
double edgeLength)
|
||||
{
|
||||
if (v1.size() < 2 || v2.size() < 2)
|
||||
return false;
|
||||
|
||||
auto const sort_by_name_fn = [](std::vector<EdgeInfo> &v) {
|
||||
std::sort(v.begin(), v.end(), EdgeInfo::LessName());
|
||||
};
|
||||
|
||||
sort_by_name_fn(v1);
|
||||
sort_by_name_fn(v2);
|
||||
|
||||
// Internal edge with the name should be connected with any other neibour edge with the same
|
||||
// name, e.g. isolated edge with unique name is not segregated.
|
||||
// b - 'b' road continues here
|
||||
// |
|
||||
// - - a - |
|
||||
// b - segregated edge
|
||||
// - - a - |
|
||||
if (!current.name.empty())
|
||||
{
|
||||
auto const findNameFn = [¤t](std::vector<EdgeInfo> const &v) {
|
||||
return std::binary_search(v.begin(), v.end(), current, EdgeInfo::LessName());
|
||||
};
|
||||
|
||||
if (!findNameFn(v1) && !findNameFn(v2))
|
||||
return false;
|
||||
}
|
||||
|
||||
// set_intersection like routine to get equal result pairs
|
||||
std::vector<std::pair<EdgeInfo const *, EdgeInfo const *>> commons;
|
||||
|
||||
auto i1 = v1.begin();
|
||||
auto i2 = v2.begin();
|
||||
|
||||
while (i1 != v1.end() && i2 != v2.end())
|
||||
{
|
||||
if (i1->name == i2->name)
|
||||
{
|
||||
if (!i1->name.empty())
|
||||
commons.push_back(std::make_pair(&(*i1), &(*i2)));
|
||||
|
||||
++i1;
|
||||
++i2;
|
||||
}
|
||||
else if (i1->name < i2->name)
|
||||
++i1;
|
||||
else
|
||||
++i2;
|
||||
}
|
||||
|
||||
if (commons.size() < 2)
|
||||
return false;
|
||||
|
||||
auto const check_equal_class = [](std::pair<EdgeInfo const *, EdgeInfo const *> const &e) {
|
||||
// Or (e.first->road_class & e.second->road_class != 0)
|
||||
return e.first->road_class == e.second->road_class;
|
||||
};
|
||||
|
||||
size_t equal_class_count = 0;
|
||||
for (auto const &e : commons)
|
||||
if (check_equal_class(e))
|
||||
++equal_class_count;
|
||||
|
||||
if (equal_class_count < 2)
|
||||
return false;
|
||||
|
||||
auto const get_length_threshold = [](EdgeInfo const *e) {
|
||||
switch (e->road_priority_class)
|
||||
{
|
||||
case guidance::RoadPriorityClass::MOTORWAY:
|
||||
case guidance::RoadPriorityClass::TRUNK:
|
||||
return 30.0;
|
||||
case guidance::RoadPriorityClass::PRIMARY:
|
||||
return 20.0;
|
||||
case guidance::RoadPriorityClass::SECONDARY:
|
||||
case guidance::RoadPriorityClass::TERTIARY:
|
||||
return 10.0;
|
||||
default:
|
||||
return 5.0;
|
||||
}
|
||||
};
|
||||
|
||||
double threshold = std::numeric_limits<double>::max();
|
||||
for (auto const &e : commons)
|
||||
threshold =
|
||||
std::min(threshold, get_length_threshold(e.first) + get_length_threshold(e.second));
|
||||
|
||||
return edgeLength <= threshold;
|
||||
}
|
||||
|
||||
std::unordered_set<EdgeID> Extractor::FindSegregatedNodes(NodeBasedGraphFactory &factory)
|
||||
{
|
||||
util::NameTable names(config.GetPath(".osrm.names").string());
|
||||
|
||||
auto const &graph = factory.GetGraph();
|
||||
auto const &annotation = factory.GetAnnotationData();
|
||||
|
||||
guidance::CoordinateExtractor coordExtractor(
|
||||
graph, factory.GetCompressedEdges(), factory.GetCoordinates());
|
||||
|
||||
auto const get_edge_length = [&](NodeID from_node, EdgeID edgeID, NodeID to_node) {
|
||||
auto const geom = coordExtractor.GetCoordinatesAlongRoad(from_node, edgeID, false, to_node);
|
||||
double length = 0.0;
|
||||
for (size_t i = 1; i < geom.size(); ++i)
|
||||
{
|
||||
length += osrm::util::coordinate_calculation::haversineDistance(geom[i - 1], geom[i]);
|
||||
}
|
||||
return length;
|
||||
};
|
||||
|
||||
auto const get_edge_info = [&](NodeID node, auto const &edgeData) -> EdgeInfo {
|
||||
/// @todo Make string normalization/lowercase/trim for comparison ...
|
||||
|
||||
auto const id = annotation[edgeData.annotation_data].name_id;
|
||||
BOOST_ASSERT(id != INVALID_NAMEID);
|
||||
auto const name = names.GetNameForID(id);
|
||||
|
||||
return {node,
|
||||
name,
|
||||
edgeData.reversed ? 1 : 0,
|
||||
annotation[edgeData.annotation_data].classes,
|
||||
edgeData.flags.road_classification.GetClass()};
|
||||
};
|
||||
|
||||
auto const collect_edge_info_fn = [&](auto const &edges1, NodeID node2) {
|
||||
std::vector<EdgeInfo> info;
|
||||
|
||||
for (auto const &e : edges1)
|
||||
{
|
||||
NodeID const target = graph.GetTarget(e);
|
||||
if (target == node2)
|
||||
continue;
|
||||
|
||||
info.push_back(get_edge_info(target, graph.GetEdgeData(e)));
|
||||
}
|
||||
|
||||
if (info.empty())
|
||||
return info;
|
||||
|
||||
std::sort(info.begin(), info.end(), [](EdgeInfo const &e1, EdgeInfo const &e2) {
|
||||
return e1.node < e2.node;
|
||||
});
|
||||
|
||||
// Merge equal infos with correct direction.
|
||||
auto curr = info.begin();
|
||||
auto next = curr;
|
||||
while (++next != info.end())
|
||||
{
|
||||
if (curr->node == next->node)
|
||||
{
|
||||
BOOST_ASSERT(curr->name == next->name);
|
||||
BOOST_ASSERT(curr->road_class == next->road_class);
|
||||
BOOST_ASSERT(curr->direction != next->direction);
|
||||
curr->direction = 2;
|
||||
}
|
||||
else
|
||||
curr = next;
|
||||
}
|
||||
|
||||
info.erase(
|
||||
std::unique(info.begin(),
|
||||
info.end(),
|
||||
[](EdgeInfo const &e1, EdgeInfo const &e2) { return e1.node == e2.node; }),
|
||||
info.end());
|
||||
|
||||
return info;
|
||||
};
|
||||
|
||||
auto const isSegregatedFn = [&](auto const &edgeData,
|
||||
auto const &edges1,
|
||||
NodeID node1,
|
||||
auto const &edges2,
|
||||
NodeID node2,
|
||||
double edgeLength) {
|
||||
return IsSegregated(collect_edge_info_fn(edges1, node2),
|
||||
collect_edge_info_fn(edges2, node1),
|
||||
get_edge_info(node1, edgeData),
|
||||
edgeLength);
|
||||
};
|
||||
|
||||
std::unordered_set<EdgeID> segregated_edges;
|
||||
|
||||
for (NodeID sourceID = 0; sourceID < graph.GetNumberOfNodes(); ++sourceID)
|
||||
{
|
||||
auto const sourceEdges = graph.GetAdjacentEdgeRange(sourceID);
|
||||
for (EdgeID edgeID : sourceEdges)
|
||||
{
|
||||
auto const &edgeData = graph.GetEdgeData(edgeID);
|
||||
|
||||
if (edgeData.reversed)
|
||||
continue;
|
||||
|
||||
NodeID const targetID = graph.GetTarget(edgeID);
|
||||
auto const targetEdges = graph.GetAdjacentEdgeRange(targetID);
|
||||
|
||||
double const length = get_edge_length(sourceID, edgeID, targetID);
|
||||
if (isSegregatedFn(edgeData, sourceEdges, sourceID, targetEdges, targetID, length))
|
||||
segregated_edges.insert(edgeID);
|
||||
}
|
||||
}
|
||||
|
||||
return segregated_edges;
|
||||
}
|
||||
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
@@ -12,24 +12,18 @@ namespace extractor
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
DrivewayHandler::DrivewayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
DrivewayHandler::DrivewayHandler(const IntersectionGenerator &intersection_generator,
|
||||
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 std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table)
|
||||
: IntersectionHandler(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table)
|
||||
street_name_suffix_table,
|
||||
intersection_generator)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -70,12 +64,12 @@ operator()(const NodeID nid, const EdgeID source_edge_id, Intersection intersect
|
||||
});
|
||||
|
||||
(void)nid;
|
||||
OSRM_ASSERT(road != intersection.end(), node_coordinates[nid]);
|
||||
OSRM_ASSERT(road != intersection.end(), coordinates[nid]);
|
||||
|
||||
if (road->instruction == TurnInstruction::INVALID())
|
||||
return intersection;
|
||||
|
||||
OSRM_ASSERT(road->instruction.type == TurnType::Turn, node_coordinates[nid]);
|
||||
OSRM_ASSERT(road->instruction.type == TurnType::Turn, coordinates[nid]);
|
||||
|
||||
road->instruction.type =
|
||||
isSameName(source_edge_id, road->eid) ? TurnType::NoTurn : TurnType::NewName;
|
||||
|
||||
@@ -0,0 +1,488 @@
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
|
||||
#include "extractor/geojson_debug_policies.hpp"
|
||||
|
||||
#include "util/geojson_debug_logger.hpp"
|
||||
|
||||
#include "util/assert.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/log.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <functional> // mem_fn
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/range/algorithm/count_if.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
namespace
|
||||
{
|
||||
const constexpr bool USE_LOW_PRECISION_MODE = true;
|
||||
// the inverse of use low precision mode
|
||||
const constexpr bool USE_HIGH_PRECISION_MODE = !USE_LOW_PRECISION_MODE;
|
||||
}
|
||||
|
||||
IntersectionGenerator::IntersectionGenerator(
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const CompressedEdgeContainer &compressed_edge_container)
|
||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||
restriction_map(restriction_map), barrier_nodes(barrier_nodes), coordinates(coordinates),
|
||||
coordinate_extractor(node_based_graph, compressed_edge_container, coordinates)
|
||||
{
|
||||
}
|
||||
|
||||
IntersectionView IntersectionGenerator::operator()(const NodeID from_node,
|
||||
const EdgeID via_eid) const
|
||||
{
|
||||
return GetConnectedRoads(from_node, via_eid, USE_HIGH_PRECISION_MODE);
|
||||
}
|
||||
|
||||
IntersectionShape
|
||||
IntersectionGenerator::ComputeIntersectionShape(const NodeID node_at_center_of_intersection,
|
||||
const boost::optional<NodeID> sorting_base,
|
||||
const bool use_low_precision_angles) const
|
||||
{
|
||||
const auto intersection_degree = node_based_graph.GetOutDegree(node_at_center_of_intersection);
|
||||
const util::Coordinate turn_coordinate = coordinates[node_at_center_of_intersection];
|
||||
|
||||
// compute bearings in a relatively small circle to prevent wrong roads order with true bearings
|
||||
struct RoadWithInitialBearing
|
||||
{
|
||||
double bearing;
|
||||
IntersectionShapeData road;
|
||||
};
|
||||
std::vector<RoadWithInitialBearing> initial_roads_ordering;
|
||||
// reserve enough items (+ the possibly missing u-turn edge)
|
||||
initial_roads_ordering.reserve(intersection_degree);
|
||||
|
||||
// number of lanes at the intersection changes how far we look down the road
|
||||
const auto edge_range = node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection);
|
||||
const auto max_lanes_intersection =
|
||||
std::accumulate(edge_range.begin(),
|
||||
edge_range.end(),
|
||||
std::uint8_t{0},
|
||||
[this](const auto current_max, const auto current_eid) {
|
||||
return std::max(current_max,
|
||||
node_based_graph.GetEdgeData(current_eid)
|
||||
.flags.road_classification.GetNumberOfLanes());
|
||||
});
|
||||
|
||||
for (const EdgeID edge_connected_to_intersection :
|
||||
node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection))
|
||||
{
|
||||
BOOST_ASSERT(edge_connected_to_intersection != SPECIAL_EDGEID);
|
||||
const NodeID to_node = node_based_graph.GetTarget(edge_connected_to_intersection);
|
||||
double bearing = 0.;
|
||||
|
||||
auto coordinates = coordinate_extractor.GetCoordinatesAlongRoad(
|
||||
node_at_center_of_intersection, edge_connected_to_intersection, !INVERT, to_node);
|
||||
|
||||
const auto close_coordinate =
|
||||
coordinate_extractor.ExtractCoordinateAtLength(2. /*m*/, coordinates);
|
||||
const auto initial_bearing =
|
||||
util::coordinate_calculation::bearing(turn_coordinate, close_coordinate);
|
||||
|
||||
const auto segment_length = util::coordinate_calculation::getLength(
|
||||
coordinates.begin(),
|
||||
coordinates.end(),
|
||||
util::coordinate_calculation::haversineDistance);
|
||||
|
||||
const auto extract_coordinate = [&](const NodeID from_node,
|
||||
const EdgeID via_eid,
|
||||
const bool traversed_in_reverse,
|
||||
const NodeID to_node) {
|
||||
return (use_low_precision_angles || intersection_degree <= 2)
|
||||
? coordinate_extractor.GetCoordinateCloseToTurn(
|
||||
from_node, via_eid, traversed_in_reverse, to_node)
|
||||
: coordinate_extractor.ExtractRepresentativeCoordinate(
|
||||
from_node,
|
||||
via_eid,
|
||||
traversed_in_reverse,
|
||||
to_node,
|
||||
max_lanes_intersection,
|
||||
std::move(coordinates));
|
||||
};
|
||||
|
||||
// we have to look down the road a bit to get the correct turn
|
||||
const auto coordinate_along_edge_leaving = extract_coordinate(
|
||||
node_at_center_of_intersection, edge_connected_to_intersection, !INVERT, to_node);
|
||||
|
||||
bearing =
|
||||
util::coordinate_calculation::bearing(turn_coordinate, coordinate_along_edge_leaving);
|
||||
|
||||
// OSM data sometimes contains duplicated nodes with identical coordinates, or
|
||||
// because of coordinate precision rounding, end up at the same coordinate.
|
||||
// It's impossible to calculate a bearing between these, so we log a warning
|
||||
// that the data should be checked.
|
||||
// The bearing calculation should return 0 in these cases, which may not be correct,
|
||||
// but is at least not random.
|
||||
if (turn_coordinate == coordinate_along_edge_leaving)
|
||||
{
|
||||
util::Log(logDEBUG) << "Zero length segment at " << coordinate_along_edge_leaving
|
||||
<< " could cause invalid intersection exit bearing.";
|
||||
BOOST_ASSERT(std::abs(bearing) <= 0.1);
|
||||
}
|
||||
|
||||
initial_roads_ordering.push_back(
|
||||
{initial_bearing, {edge_connected_to_intersection, bearing, segment_length}});
|
||||
}
|
||||
|
||||
if (!initial_roads_ordering.empty())
|
||||
{
|
||||
const auto base_initial_bearing = [&]() {
|
||||
if (sorting_base)
|
||||
{
|
||||
const auto itr = std::find_if(initial_roads_ordering.begin(),
|
||||
initial_roads_ordering.end(),
|
||||
[&](const auto &data) {
|
||||
return node_based_graph.GetTarget(
|
||||
data.road.eid) == *sorting_base;
|
||||
});
|
||||
if (itr != initial_roads_ordering.end())
|
||||
return util::bearing::reverse(itr->bearing);
|
||||
}
|
||||
return util::bearing::reverse(initial_roads_ordering.begin()->bearing);
|
||||
}();
|
||||
|
||||
// sort roads with respect to the initial bearings, a tie-breaker for equal initial bearings
|
||||
// is to order roads via final bearings to have roads in clockwise order
|
||||
//
|
||||
// rhs <---. lhs <----.
|
||||
// / /
|
||||
// lhs / rhs /
|
||||
//
|
||||
// lhs road is before rhs one rhs road is before lhs one
|
||||
// bearing::angleBetween < 180 bearing::angleBetween > 180
|
||||
const auto initial_bearing_order = makeCompareShapeDataAngleToBearing(base_initial_bearing);
|
||||
std::sort(initial_roads_ordering.begin(),
|
||||
initial_roads_ordering.end(),
|
||||
[&initial_bearing_order](const auto &lhs, const auto &rhs) {
|
||||
return initial_bearing_order(lhs, rhs) ||
|
||||
(lhs.bearing == rhs.bearing &&
|
||||
util::bearing::angleBetween(lhs.road.bearing, rhs.road.bearing) <
|
||||
180);
|
||||
});
|
||||
|
||||
// copy intersection data in the initial order
|
||||
IntersectionShape intersection;
|
||||
intersection.reserve(initial_roads_ordering.size());
|
||||
std::transform(initial_roads_ordering.begin(),
|
||||
initial_roads_ordering.end(),
|
||||
std::back_inserter(intersection),
|
||||
[](const auto &entry) { return entry.road; });
|
||||
|
||||
if (intersection.size() > 2)
|
||||
{ // Check bearings ordering with respect to true bearings
|
||||
const auto base_bearing = intersection.front().bearing;
|
||||
const auto bearings_order =
|
||||
makeCompareShapeDataAngleToBearing(util::bearing::reverse(base_bearing));
|
||||
for (auto curr = intersection.begin(), next = std::next(curr);
|
||||
next != intersection.end();
|
||||
++curr, ++next)
|
||||
{
|
||||
if (bearings_order(*next, *curr))
|
||||
{ // If the true bearing is out of the initial order (next before current) then
|
||||
// adjust the next bearing to keep the order. The adjustment angle is at most
|
||||
// 0.5° or a half-angle between the current bearing and the base bearing.
|
||||
// to prevent overlapping over base bearing + 360°.
|
||||
const auto angle_adjustment = std::min(
|
||||
.5, util::restrictAngleToValidRange(base_bearing - curr->bearing) / 2.);
|
||||
next->bearing =
|
||||
util::restrictAngleToValidRange(curr->bearing + angle_adjustment);
|
||||
}
|
||||
}
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
|
||||
return IntersectionShape{};
|
||||
}
|
||||
|
||||
// a
|
||||
// |
|
||||
// |
|
||||
// v
|
||||
// For an intersection from_node --via_eid--> turn_node ----> c
|
||||
// ^
|
||||
// |
|
||||
// |
|
||||
// b
|
||||
// This functions returns _all_ turns as if the graph was undirected.
|
||||
// That means we not only get (from_node, turn_node, c) in the above example
|
||||
// but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are
|
||||
// marked as invalid and only needed for intersection classification.
|
||||
IntersectionView IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
|
||||
const EdgeID via_eid,
|
||||
const bool use_low_precision_angles) const
|
||||
{
|
||||
// make sure the via-eid is valid
|
||||
BOOST_ASSERT([this](const NodeID from_node, const EdgeID via_eid) {
|
||||
const auto range = node_based_graph.GetAdjacentEdgeRange(from_node);
|
||||
return range.front() <= via_eid && via_eid <= range.back();
|
||||
}(from_node, via_eid));
|
||||
|
||||
auto intersection = ComputeIntersectionShape(
|
||||
node_based_graph.GetTarget(via_eid), boost::none, use_low_precision_angles);
|
||||
return TransformIntersectionShapeIntoView(from_node, via_eid, std::move(intersection));
|
||||
}
|
||||
|
||||
IntersectionGenerationParameters
|
||||
IntersectionGenerator::SkipDegreeTwoNodes(const NodeID starting_node, const EdgeID via_edge) const
|
||||
{
|
||||
NodeID query_node = starting_node;
|
||||
EdgeID query_edge = via_edge;
|
||||
|
||||
const auto get_next_edge = [this](const NodeID from, const EdgeID via) {
|
||||
const NodeID new_node = node_based_graph.GetTarget(via);
|
||||
BOOST_ASSERT(node_based_graph.GetOutDegree(new_node) == 2);
|
||||
const EdgeID begin_edges_new_node = node_based_graph.BeginEdges(new_node);
|
||||
return (node_based_graph.GetTarget(begin_edges_new_node) == from) ? begin_edges_new_node + 1
|
||||
: begin_edges_new_node;
|
||||
};
|
||||
|
||||
std::unordered_set<NodeID> visited_nodes;
|
||||
// skip trivial nodes without generating the intersection in between, stop at the very first
|
||||
// intersection of degree > 2
|
||||
while (0 == visited_nodes.count(query_node) &&
|
||||
2 == node_based_graph.GetOutDegree(node_based_graph.GetTarget(query_edge)))
|
||||
{
|
||||
visited_nodes.insert(query_node);
|
||||
const auto next_node = node_based_graph.GetTarget(query_edge);
|
||||
const auto next_edge = get_next_edge(query_node, query_edge);
|
||||
|
||||
query_node = next_node;
|
||||
query_edge = next_edge;
|
||||
|
||||
// check if there is a relevant change in the graph
|
||||
if (!CanBeCompressed(node_based_graph.GetEdgeData(query_edge),
|
||||
node_based_graph.GetEdgeData(next_edge),
|
||||
node_data_container) ||
|
||||
(node_based_graph.GetTarget(next_edge) == starting_node))
|
||||
break;
|
||||
}
|
||||
|
||||
return {query_node, query_edge};
|
||||
}
|
||||
|
||||
IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
|
||||
const NodeID previous_node,
|
||||
const EdgeID entering_via_edge,
|
||||
const IntersectionShape &intersection_shape) const
|
||||
{
|
||||
// requires a copy of the intersection
|
||||
return TransformIntersectionShapeIntoView(previous_node,
|
||||
entering_via_edge,
|
||||
intersection_shape, // creates a copy
|
||||
intersection_shape, // reference to local
|
||||
{}); // empty vector of performed merges
|
||||
}
|
||||
|
||||
IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
|
||||
const NodeID previous_node,
|
||||
const EdgeID entering_via_edge,
|
||||
const IntersectionShape &normalized_intersection,
|
||||
const IntersectionShape &intersection,
|
||||
const std::vector<IntersectionNormalizationOperation> &performed_merges) const
|
||||
{
|
||||
const auto node_at_intersection = node_based_graph.GetTarget(entering_via_edge);
|
||||
|
||||
// request all turn restrictions
|
||||
auto const restrictions = restriction_map.Restrictions(previous_node, node_at_intersection);
|
||||
|
||||
// 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`
|
||||
const auto find_only_valid_turn = [&]() -> boost::optional<NodeID> {
|
||||
const auto itr = std::find_if(restrictions.first, restrictions.second, [](auto pair) {
|
||||
return pair.second->is_only;
|
||||
});
|
||||
if (itr != restrictions.second)
|
||||
return itr->second->AsNodeRestriction().to;
|
||||
else
|
||||
return boost::none;
|
||||
};
|
||||
|
||||
const auto only_valid_turn = find_only_valid_turn();
|
||||
|
||||
// barriers change our behaviour regarding u-turns
|
||||
const bool is_barrier_node = barrier_nodes.find(node_at_intersection) != barrier_nodes.end();
|
||||
|
||||
const auto connect_to_previous_node = [this, previous_node](const IntersectionShapeData road) {
|
||||
return node_based_graph.GetTarget(road.eid) == previous_node;
|
||||
};
|
||||
|
||||
// check which of the edges is the u-turn edge
|
||||
const auto uturn_edge_itr =
|
||||
std::find_if(intersection.begin(), intersection.end(), connect_to_previous_node);
|
||||
|
||||
// there needs to be a connection, otherwise stuff went seriously wrong. Note that this is not
|
||||
// necessarily the same id as `entering_via_edge`.
|
||||
// In cases where parallel edges are present, we only remember the minimal edge. Both share
|
||||
// exactly the same coordinates, so the u-turn is still the best choice here.
|
||||
BOOST_ASSERT(uturn_edge_itr != intersection.end());
|
||||
|
||||
const auto is_restricted = [&](const NodeID destination) {
|
||||
// check if we have a dedicated destination
|
||||
if (only_valid_turn)
|
||||
return *only_valid_turn != destination;
|
||||
|
||||
// check if explicitly forbidden
|
||||
return restrictions.second !=
|
||||
std::find_if(restrictions.first, restrictions.second, [&](const auto &restriction) {
|
||||
return restriction.second->AsNodeRestriction().to == destination;
|
||||
});
|
||||
};
|
||||
|
||||
const auto is_allowed_turn = [&](const IntersectionShapeData &road) {
|
||||
const auto &road_data = node_based_graph.GetEdgeData(road.eid);
|
||||
const NodeID road_destination_node = node_based_graph.GetTarget(road.eid);
|
||||
// reverse edges are never valid turns because the resulting turn would look like this:
|
||||
// from_node --via_edge--> node_at_intersection <--onto_edge-- to_node
|
||||
// however we need this for capture intersection shape for incoming one-ways
|
||||
return !road_data.reversed &&
|
||||
// we are not turning over a barrier
|
||||
(!is_barrier_node || road_destination_node == previous_node) &&
|
||||
// don't allow restricted turns
|
||||
!is_restricted(road_destination_node);
|
||||
|
||||
};
|
||||
|
||||
// due to merging of roads, the u-turn might actually not be part of the intersection anymore
|
||||
// uturn is a pair of {edge id, bearing}
|
||||
const auto uturn = [&]() {
|
||||
const auto merge_entry = std::find_if(
|
||||
performed_merges.begin(), performed_merges.end(), [&uturn_edge_itr](const auto entry) {
|
||||
return entry.merged_eid == uturn_edge_itr->eid;
|
||||
});
|
||||
if (merge_entry != performed_merges.end())
|
||||
{
|
||||
const auto merged_into_id = merge_entry->into_eid;
|
||||
const auto merged_u_turn = std::find_if(
|
||||
normalized_intersection.begin(),
|
||||
normalized_intersection.end(),
|
||||
[&](const IntersectionShapeData &road) { return road.eid == merged_into_id; });
|
||||
BOOST_ASSERT(merged_u_turn != normalized_intersection.end());
|
||||
return std::make_pair(merged_u_turn->eid,
|
||||
util::bearing::reverse(merged_u_turn->bearing));
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto uturn_edge_at_normalized_intersection_itr =
|
||||
std::find_if(normalized_intersection.begin(),
|
||||
normalized_intersection.end(),
|
||||
connect_to_previous_node);
|
||||
BOOST_ASSERT(uturn_edge_at_normalized_intersection_itr !=
|
||||
normalized_intersection.end());
|
||||
return std::make_pair(
|
||||
uturn_edge_at_normalized_intersection_itr->eid,
|
||||
util::bearing::reverse(uturn_edge_at_normalized_intersection_itr->bearing));
|
||||
}
|
||||
}();
|
||||
|
||||
IntersectionView intersection_view;
|
||||
intersection_view.reserve(normalized_intersection.size());
|
||||
std::transform(normalized_intersection.begin(),
|
||||
normalized_intersection.end(),
|
||||
std::back_inserter(intersection_view),
|
||||
[&](const IntersectionShapeData &road) {
|
||||
return IntersectionViewData(
|
||||
road,
|
||||
is_allowed_turn(road),
|
||||
util::bearing::angleBetween(uturn.second, road.bearing));
|
||||
});
|
||||
|
||||
const auto uturn_edge_at_intersection_view_itr =
|
||||
std::find_if(intersection_view.begin(), intersection_view.end(), connect_to_previous_node);
|
||||
// number of found valid exit roads
|
||||
const auto valid_count =
|
||||
std::count_if(intersection_view.begin(),
|
||||
intersection_view.end(),
|
||||
[](const IntersectionViewData &road) { return road.entry_allowed; });
|
||||
// in general, we don't wan't to allow u-turns. If we don't look at a barrier, we have to check
|
||||
// for dead end streets. These are the only ones that we allow uturns for, next to barriers
|
||||
// (which are also kind of a dead end, but we don't have to check these again :))
|
||||
if (uturn_edge_at_intersection_view_itr != intersection_view.end() &&
|
||||
((uturn_edge_at_intersection_view_itr->entry_allowed && !is_barrier_node &&
|
||||
valid_count != 1) ||
|
||||
valid_count == 0))
|
||||
{
|
||||
const auto allow_uturn_at_dead_end = [&]() {
|
||||
const auto &uturn_data = node_based_graph.GetEdgeData(uturn_edge_itr->eid);
|
||||
|
||||
// we can't turn back onto oneway streets
|
||||
if (uturn_data.reversed)
|
||||
return false;
|
||||
|
||||
// don't allow explicitly restricted turns
|
||||
if (is_restricted(previous_node))
|
||||
return false;
|
||||
|
||||
// we define dead ends as roads that can only be entered via the possible u-turn
|
||||
const auto is_bidirectional = [&](const EdgeID entering_via_edge) {
|
||||
const auto to_node = node_based_graph.GetTarget(entering_via_edge);
|
||||
const auto reverse_edge = node_based_graph.FindEdge(to_node, node_at_intersection);
|
||||
BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID);
|
||||
return !node_based_graph.GetEdgeData(reverse_edge).reversed;
|
||||
};
|
||||
|
||||
const auto bidirectional_edges = [&]() {
|
||||
std::uint32_t count = 0;
|
||||
for (const auto eid : node_based_graph.GetAdjacentEdgeRange(node_at_intersection))
|
||||
if (is_bidirectional(eid))
|
||||
++count;
|
||||
return count;
|
||||
}();
|
||||
|
||||
// Checking for dead-end streets is kind of difficult. There is obvious dead ends
|
||||
// (single road connected)
|
||||
return bidirectional_edges <= 1;
|
||||
}();
|
||||
uturn_edge_at_intersection_view_itr->entry_allowed = allow_uturn_at_dead_end;
|
||||
}
|
||||
std::sort(std::begin(intersection_view),
|
||||
std::end(intersection_view),
|
||||
std::mem_fn(&IntersectionViewData::CompareByAngle));
|
||||
|
||||
// Move entering_via_edge to intersection front and place all roads prior entering_via_edge
|
||||
// at the end of the intersection view with 360° angle
|
||||
auto entering_via_it = std::find_if(intersection_view.begin(),
|
||||
intersection_view.end(),
|
||||
[&uturn](auto &road) { return road.eid == uturn.first; });
|
||||
|
||||
OSRM_ASSERT(entering_via_it != intersection_view.end() && entering_via_it->angle >= 0. &&
|
||||
entering_via_it->angle < std::numeric_limits<double>::epsilon(),
|
||||
coordinates[node_at_intersection]);
|
||||
|
||||
if (entering_via_it != intersection_view.begin() && entering_via_it != intersection_view.end())
|
||||
{
|
||||
std::for_each(
|
||||
intersection_view.begin(), entering_via_it, [](auto &road) { road.angle = 360.; });
|
||||
std::rotate(intersection_view.begin(), entering_via_it, intersection_view.end());
|
||||
}
|
||||
|
||||
return intersection_view;
|
||||
}
|
||||
|
||||
const CoordinateExtractor &IntersectionGenerator::GetCoordinateExtractor() const
|
||||
{
|
||||
return coordinate_extractor;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "extractor/guidance/intersection_handler.hpp"
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/intersection/intersection_analysis.hpp"
|
||||
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/guidance/name_announcements.hpp"
|
||||
@@ -46,27 +45,17 @@ inline bool requiresAnnouncement(const util::NodeBasedDynamicGraph &node_based_g
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
IntersectionHandler::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)
|
||||
IntersectionHandler::IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||
node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
|
||||
node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
|
||||
turn_lanes_data(turn_lanes_data), name_table(name_table),
|
||||
street_name_suffix_table(street_name_suffix_table), graph_walker(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data)
|
||||
coordinates(coordinates), name_table(name_table),
|
||||
street_name_suffix_table(street_name_suffix_table),
|
||||
intersection_generator(intersection_generator),
|
||||
graph_walker(node_based_graph, node_data_container, intersection_generator)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -173,8 +162,8 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t
|
||||
// or actually follow the full road. When 2399 lands, we can exchange here for a
|
||||
// precalculated distance value.
|
||||
const auto distance = util::coordinate_calculation::haversineDistance(
|
||||
node_coordinates[node_based_graph.GetTarget(via_edge)],
|
||||
node_coordinates[node_based_graph.GetTarget(road.eid)]);
|
||||
coordinates[node_based_graph.GetTarget(via_edge)],
|
||||
coordinates[node_based_graph.GetTarget(road.eid)]);
|
||||
|
||||
return {TurnType::Turn,
|
||||
(angularDeviation(road.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE &&
|
||||
@@ -473,24 +462,17 @@ IntersectionHandler::getNextIntersection(const NodeID at, const EdgeID via) cons
|
||||
// Starting at node `a` via edge `e0` the intersection generator returns the intersection at `c`
|
||||
// writing `tl` (traffic signal) node and the edge `e1` which has the intersection as target.
|
||||
|
||||
const auto intersection_parameters =
|
||||
intersection::skipDegreeTwoNodes(node_based_graph, {at, via});
|
||||
const auto intersection_parameters = intersection_generator.SkipDegreeTwoNodes(at, via);
|
||||
// This should never happen, guard against nevertheless
|
||||
if (intersection_parameters.node == SPECIAL_NODEID ||
|
||||
intersection_parameters.edge == SPECIAL_EDGEID)
|
||||
if (intersection_parameters.nid == SPECIAL_NODEID ||
|
||||
intersection_parameters.via_eid == SPECIAL_EDGEID)
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
auto intersection = intersection::getConnectedRoads<false>(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
intersection_parameters);
|
||||
auto intersection_node = node_based_graph.GetTarget(intersection_parameters.edge);
|
||||
auto intersection =
|
||||
intersection_generator(intersection_parameters.nid, intersection_parameters.via_eid);
|
||||
auto intersection_node = node_based_graph.GetTarget(intersection_parameters.via_eid);
|
||||
|
||||
if (intersection.size() <= 2 || intersection.isTrafficSignalOrBarrier())
|
||||
{
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
#include "extractor/guidance/intersection_normalizer.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
using osrm::util::angularDeviation;
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
IntersectionNormalizer::IntersectionNormalizer(
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: node_based_graph(node_based_graph), intersection_generator(intersection_generator),
|
||||
mergable_road_detector(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
intersection_generator,
|
||||
intersection_generator.GetCoordinateExtractor(),
|
||||
name_table,
|
||||
street_name_suffix_table)
|
||||
{
|
||||
}
|
||||
|
||||
IntersectionNormalizer::NormalizationResult IntersectionNormalizer::
|
||||
operator()(const NodeID node_at_intersection, IntersectionShape intersection) const
|
||||
{
|
||||
const auto intersection_copy = intersection;
|
||||
auto merged_shape_and_merges =
|
||||
MergeSegregatedRoads(node_at_intersection, std::move(intersection));
|
||||
merged_shape_and_merges.normalized_shape = AdjustBearingsForMergeAtDestination(
|
||||
node_at_intersection, std::move(merged_shape_and_merges.normalized_shape));
|
||||
return merged_shape_and_merges;
|
||||
}
|
||||
|
||||
bool IntersectionNormalizer::CanMerge(const NodeID intersection_node,
|
||||
const IntersectionShape &intersection,
|
||||
std::size_t fist_index_in_ccw,
|
||||
std::size_t second_index_in_ccw) const
|
||||
{
|
||||
BOOST_ASSERT(((fist_index_in_ccw + 1) % intersection.size()) == second_index_in_ccw);
|
||||
|
||||
// don't merge on degree two, since it's most likely a bollard/traffic light or a round way
|
||||
if (intersection.size() <= 2)
|
||||
return false;
|
||||
|
||||
const auto can_merge = mergable_road_detector.CanMergeRoad(
|
||||
intersection_node, intersection[fist_index_in_ccw], intersection[second_index_in_ccw]);
|
||||
|
||||
/*
|
||||
* Merging should never depend on order/never merge more than two roads. To ensure that we don't
|
||||
* merge anything that is impacted by neighboring roads (e.g. three roads of the same name as in
|
||||
* parking lots/border checkpoints), we check if the neigboring roads would be merged as well.
|
||||
* In that case, we cannot merge, since we would end up merging multiple items together
|
||||
*/
|
||||
const auto is_distinct = [&]() {
|
||||
const auto next_index_in_ccw = (second_index_in_ccw + 1) % intersection.size();
|
||||
const auto distinct_to_next_in_ccw = mergable_road_detector.IsDistinctFrom(
|
||||
intersection[second_index_in_ccw], intersection[next_index_in_ccw]);
|
||||
const auto prev_index_in_ccw =
|
||||
(fist_index_in_ccw + intersection.size() - 1) % intersection.size();
|
||||
const auto distinct_to_prev_in_ccw = mergable_road_detector.IsDistinctFrom(
|
||||
intersection[prev_index_in_ccw], intersection[fist_index_in_ccw]);
|
||||
return distinct_to_next_in_ccw && distinct_to_prev_in_ccw;
|
||||
};
|
||||
|
||||
// use lazy evaluation to check only if mergable
|
||||
return can_merge && is_distinct();
|
||||
}
|
||||
|
||||
IntersectionNormalizationOperation
|
||||
IntersectionNormalizer::DetermineMergeDirection(const IntersectionShapeData &lhs,
|
||||
const IntersectionShapeData &rhs) const
|
||||
{
|
||||
if (node_based_graph.GetEdgeData(lhs.eid).reversed)
|
||||
return {lhs.eid, rhs.eid};
|
||||
else
|
||||
return {rhs.eid, lhs.eid};
|
||||
}
|
||||
|
||||
IntersectionShapeData IntersectionNormalizer::MergeRoads(const IntersectionShapeData &into,
|
||||
const IntersectionShapeData &from) const
|
||||
{
|
||||
// we only merge small angles. If the difference between both is large, we are looking at a
|
||||
// bearing leading north. Such a bearing cannot be handled via the basic average. In this
|
||||
// case we actually need to shift the bearing by half the difference.
|
||||
const auto aroundZero = [](const double first, const double second) {
|
||||
return (std::max(first, second) - std::min(first, second)) >= 180;
|
||||
};
|
||||
|
||||
// find the angle between two other angles
|
||||
const auto combineAngles = [aroundZero](const double first, const double second) {
|
||||
if (!aroundZero(first, second))
|
||||
return .5 * (first + second);
|
||||
else
|
||||
{
|
||||
const auto offset = angularDeviation(first, second);
|
||||
auto new_angle = std::max(first, second) + .5 * offset;
|
||||
if (new_angle >= 360)
|
||||
return new_angle - 360;
|
||||
return new_angle;
|
||||
}
|
||||
};
|
||||
|
||||
auto result = into;
|
||||
BOOST_ASSERT(!node_based_graph.GetEdgeData(into.eid).reversed);
|
||||
result.bearing = combineAngles(into.bearing, from.bearing);
|
||||
BOOST_ASSERT(0 <= result.bearing && result.bearing < 360.0);
|
||||
return result;
|
||||
}
|
||||
|
||||
IntersectionShapeData
|
||||
IntersectionNormalizer::MergeRoads(const IntersectionNormalizationOperation direction,
|
||||
const IntersectionShapeData &lhs,
|
||||
const IntersectionShapeData &rhs,
|
||||
const double opposite_bearing) const
|
||||
{
|
||||
// In some intersections, turning roads can introduce artificial turns if we merge here.
|
||||
// Consider a scenario like:
|
||||
//
|
||||
// a . g - f
|
||||
// | .
|
||||
// | .
|
||||
// |.
|
||||
// d-b--------e
|
||||
// |
|
||||
// c
|
||||
//
|
||||
// Merging `bgf` and `be` would introduce an angle, even though d-b-e is perfectly straight
|
||||
// We don't change the angle, if such an opposite road exists
|
||||
if (direction.merged_eid == lhs.eid)
|
||||
{
|
||||
// change the angle only if the opposite direction is not nearly straight
|
||||
if (angularDeviation(opposite_bearing, rhs.bearing) >
|
||||
(STRAIGHT_ANGLE - MAXIMAL_ALLOWED_NO_TURN_DEVIATION))
|
||||
return rhs;
|
||||
else
|
||||
return MergeRoads(rhs, lhs);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (angularDeviation(opposite_bearing, lhs.bearing) >
|
||||
(STRAIGHT_ANGLE - MAXIMAL_ALLOWED_NO_TURN_DEVIATION))
|
||||
return lhs;
|
||||
else
|
||||
return MergeRoads(lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Segregated Roads often merge onto a single intersection.
|
||||
* While technically representing different roads, they are
|
||||
* often looked at as a single road.
|
||||
* Due to the merging, turn Angles seem off, wenn we compute them from the
|
||||
* initial positions.
|
||||
*
|
||||
* b<b<b<b(1)<b<b<b
|
||||
* aaaaa-b
|
||||
* b>b>b>b(2)>b>b>b
|
||||
*
|
||||
* Would be seen as a slight turn going fro a to (2). A Sharp turn going from
|
||||
* (1) to (2).
|
||||
*
|
||||
* In cases like these, we megre this segregated roads into a single road to
|
||||
* end up with a case like:
|
||||
*
|
||||
* aaaaa-bbbbbb
|
||||
*
|
||||
* for the turn representation.
|
||||
* Anything containing the first u-turn in a merge affects all other angles
|
||||
* and is handled separately from all others.
|
||||
*/
|
||||
IntersectionNormalizer::NormalizationResult
|
||||
IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
|
||||
IntersectionShape intersection) const
|
||||
{
|
||||
const auto getRight = [&](std::size_t index) {
|
||||
return (index + intersection.size() - 1) % intersection.size();
|
||||
};
|
||||
|
||||
// This map stores for all edges that participated in a merging operation in which edge id they
|
||||
// end up in the end. We only store what we have merged into other edges.
|
||||
std::vector<IntersectionNormalizationOperation> merging_map;
|
||||
const auto merge = [this, &merging_map](const IntersectionShapeData &first,
|
||||
const IntersectionShapeData &second,
|
||||
const double opposite_bearing) {
|
||||
|
||||
const auto direction = DetermineMergeDirection(first, second);
|
||||
BOOST_ASSERT(
|
||||
std::find_if(merging_map.begin(), merging_map.end(), [direction](const auto pair) {
|
||||
return pair.merged_eid == direction.merged_eid;
|
||||
}) == merging_map.end());
|
||||
merging_map.push_back(direction);
|
||||
return MergeRoads(direction, first, second, opposite_bearing);
|
||||
};
|
||||
|
||||
if (intersection.size() <= 1)
|
||||
return {intersection, merging_map};
|
||||
|
||||
const auto intersection_copy = intersection;
|
||||
const auto opposite_bearing = [this, intersection_copy](const IntersectionShapeData &lhs,
|
||||
const IntersectionShapeData &rhs) {
|
||||
if (node_based_graph.GetEdgeData(lhs.eid).reversed)
|
||||
{
|
||||
return intersection_copy.FindClosestBearing(util::bearing::reverse(rhs.bearing))
|
||||
->bearing;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(node_based_graph.GetEdgeData(rhs.eid).reversed);
|
||||
return intersection_copy.FindClosestBearing(util::bearing::reverse(lhs.bearing))
|
||||
->bearing;
|
||||
}
|
||||
};
|
||||
// check for merges including the basic u-turn
|
||||
// these result in an adjustment of all other angles. This is due to how these angles are
|
||||
// perceived. Considering the following example:
|
||||
//
|
||||
// c b
|
||||
// Y
|
||||
// a
|
||||
//
|
||||
// coming from a to b (given a road that splits at the fork into two one-ways), the turn is not
|
||||
// considered as a turn but rather as going straight.
|
||||
// Now if we look at the situation merging:
|
||||
//
|
||||
// a b
|
||||
// \ /
|
||||
// e - + - d
|
||||
// |
|
||||
// c
|
||||
//
|
||||
// With a,b representing the same road, the intersection itself represents a classif for way
|
||||
// intersection so we handle it like
|
||||
//
|
||||
// (a),b
|
||||
// |
|
||||
// e - + - d
|
||||
// |
|
||||
// c
|
||||
//
|
||||
// To be able to consider this adjusted representation down the line, we merge some roads.
|
||||
// If the merge occurs at the u-turn edge, we need to adjust all angles, though, since they are
|
||||
// with respect to the now changed perceived location of a. If we move (a) to the left, we add
|
||||
// the difference to all angles. Otherwise we subtract it.
|
||||
// these result in an adjustment of all other angles
|
||||
if (CanMerge(intersection_node, intersection, intersection.size() - 1, 0))
|
||||
{
|
||||
// moving `a` to the left
|
||||
const auto opposite = opposite_bearing(intersection.front(), intersection.back());
|
||||
intersection[0] = merge(intersection.front(), intersection.back(), opposite);
|
||||
// FIXME if we have a left-sided country, we need to switch this off and enable it
|
||||
// below
|
||||
intersection.pop_back();
|
||||
}
|
||||
else if (CanMerge(intersection_node, intersection, 0, 1))
|
||||
{
|
||||
const auto opposite = opposite_bearing(intersection.front(), intersection[1]);
|
||||
intersection[0] = merge(intersection.front(), intersection[1], opposite);
|
||||
intersection.erase(intersection.begin() + 1);
|
||||
}
|
||||
|
||||
// a merge including the first u-turn requires an adjustment of the turn angles
|
||||
// therefore these are handled prior to this step
|
||||
for (std::size_t index = 2; index < intersection.size(); ++index)
|
||||
{
|
||||
if (CanMerge(intersection_node, intersection, getRight(index), index))
|
||||
{
|
||||
const auto opposite =
|
||||
opposite_bearing(intersection[getRight(index)], intersection[index]);
|
||||
intersection[getRight(index)] =
|
||||
merge(intersection[getRight(index)], intersection[index], opposite);
|
||||
intersection.erase(intersection.begin() + index);
|
||||
--index;
|
||||
}
|
||||
}
|
||||
return {intersection, merging_map};
|
||||
}
|
||||
|
||||
// OSM can have some very steep angles for joining roads. Considering the following intersection:
|
||||
// x
|
||||
// |
|
||||
// v __________c
|
||||
// /
|
||||
// a ---d
|
||||
// \ __________b
|
||||
//
|
||||
// with c->d as a oneway
|
||||
// and d->b as a oneway, the turn von x->d is actually a turn from x->a. So when looking at the
|
||||
// intersection coming from x, we want to interpret the situation as
|
||||
// x
|
||||
// |
|
||||
// a __ d __ v__________c
|
||||
// |
|
||||
// |_______________b
|
||||
//
|
||||
// Where we see the turn to `d` as a right turn, rather than going straight.
|
||||
// We do this by adjusting the local turn angle at `x` to turn onto `d` to be reflective of this
|
||||
// situation, where `v` would be the node at the intersection.
|
||||
IntersectionShape
|
||||
IntersectionNormalizer::AdjustBearingsForMergeAtDestination(const NodeID node_at_intersection,
|
||||
IntersectionShape intersection) const
|
||||
{
|
||||
// nothing to do for dead ends
|
||||
if (intersection.size() <= 1)
|
||||
return intersection;
|
||||
|
||||
// we don't adjust any road that is longer than 30 meters (between centers of intersections),
|
||||
// since the road is probably too long otherwise to impact perception.
|
||||
const double constexpr PRUNING_DISTANCE = 30;
|
||||
// never adjust u-turns
|
||||
for (std::size_t index = 0; index < intersection.size(); ++index)
|
||||
{
|
||||
auto &road = intersection[index];
|
||||
// only consider roads that are close
|
||||
if (road.segment_length > PRUNING_DISTANCE)
|
||||
continue;
|
||||
|
||||
// to find out about the above situation, we need to look at the next intersection (at d in
|
||||
// the example). If the initial road can be merged to the left/right, we are about to adjust
|
||||
// the angle.
|
||||
const auto next_intersection_along_road = intersection_generator.ComputeIntersectionShape(
|
||||
node_based_graph.GetTarget(road.eid), node_at_intersection);
|
||||
|
||||
if (next_intersection_along_road.size() <= 1)
|
||||
continue;
|
||||
|
||||
const auto node_at_next_intersection = node_based_graph.GetTarget(road.eid);
|
||||
|
||||
const auto adjustAngle = [](double angle, double offset) {
|
||||
angle += offset;
|
||||
if (angle > 360)
|
||||
return angle - 360.;
|
||||
else if (angle < 0)
|
||||
return angle + 360.;
|
||||
return angle;
|
||||
};
|
||||
|
||||
const auto range = node_based_graph.GetAdjacentEdgeRange(node_at_next_intersection);
|
||||
if (range.size() <= 1)
|
||||
continue;
|
||||
|
||||
// the order does not matter
|
||||
const auto get_offset = [](const IntersectionShapeData &lhs,
|
||||
const IntersectionShapeData &rhs) {
|
||||
return 0.5 * angularDeviation(lhs.bearing, rhs.bearing);
|
||||
};
|
||||
|
||||
// When offsetting angles in our turns, we don't want to get past the next turn. This
|
||||
// function simply limits an offset to be at most half the distance to the next turn in the
|
||||
// offfset direction
|
||||
const auto get_corrected_offset = [](
|
||||
const double offset,
|
||||
const IntersectionShapeData &road,
|
||||
const IntersectionShapeData &next_road_in_offset_direction) {
|
||||
const auto offset_limit =
|
||||
angularDeviation(road.bearing, next_road_in_offset_direction.bearing);
|
||||
// limit the offset with an additional buffer
|
||||
return (offset + MAXIMAL_ALLOWED_NO_TURN_DEVIATION > offset_limit) ? 0.5 * offset_limit
|
||||
: offset;
|
||||
};
|
||||
|
||||
// only if straighmost angles get smaller, we consider it an improvement
|
||||
auto const improves_straightmost = [&](auto const index, auto const offset) {
|
||||
const auto itr = next_intersection_along_road.FindClosestBearing(
|
||||
util::bearing::reverse(next_intersection_along_road[index].bearing));
|
||||
const auto angle = util::bearing::angleBetween(
|
||||
util::bearing::reverse(itr->bearing), next_intersection_along_road[index].bearing);
|
||||
|
||||
return util::angularDeviation(angle, STRAIGHT_ANGLE) >
|
||||
util::angularDeviation(angle + offset, STRAIGHT_ANGLE);
|
||||
};
|
||||
|
||||
// check if the u-turn edge at the next intersection could be merged to the left/right. If
|
||||
// this is the case and the road is not far away (see previous distance check), if
|
||||
// influences the perceived angle.
|
||||
if (CanMerge(node_at_next_intersection, next_intersection_along_road, 0, 1))
|
||||
{
|
||||
const auto offset =
|
||||
get_offset(next_intersection_along_road[0], next_intersection_along_road[1]);
|
||||
|
||||
if (improves_straightmost(0, -offset) && improves_straightmost(1, offset))
|
||||
{
|
||||
const auto corrected_offset = get_corrected_offset(
|
||||
offset,
|
||||
road,
|
||||
intersection[(intersection.size() + index - 1) % intersection.size()]);
|
||||
// at the target intersection, we merge to the right, so we need to shift the
|
||||
// current
|
||||
// angle to the left
|
||||
road.bearing = adjustAngle(road.bearing, corrected_offset);
|
||||
}
|
||||
}
|
||||
else if (CanMerge(node_at_next_intersection,
|
||||
next_intersection_along_road,
|
||||
next_intersection_along_road.size() - 1,
|
||||
0))
|
||||
{
|
||||
const auto offset =
|
||||
get_offset(next_intersection_along_road[0],
|
||||
next_intersection_along_road[next_intersection_along_road.size() - 1]);
|
||||
|
||||
if (improves_straightmost(0, offset) &&
|
||||
improves_straightmost(next_intersection_along_road.size() - 1, -offset))
|
||||
{
|
||||
const auto corrected_offset = get_corrected_offset(
|
||||
offset, road, intersection[(index + 1) % intersection.size()]);
|
||||
// at the target intersection, we merge to the left, so we need to shift the current
|
||||
// angle to the right
|
||||
road.bearing = adjustAngle(road.bearing, -corrected_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "extractor/guidance/mergable_road_detector.hpp"
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||
#include "extractor/guidance/intersection_generator.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"
|
||||
|
||||
@@ -51,22 +52,17 @@ inline auto makeCheckRoadForName(const NameID name_id,
|
||||
}
|
||||
}
|
||||
|
||||
MergableRoadDetector::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)
|
||||
MergableRoadDetector::MergableRoadDetector(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &node_coordinates,
|
||||
const IntersectionGenerator &intersection_generator,
|
||||
const CoordinateExtractor &coordinate_extractor,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table)
|
||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||
node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
|
||||
node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
|
||||
turn_lanes_data(turn_lanes_data), name_table(name_table),
|
||||
street_name_suffix_table(street_name_suffix_table),
|
||||
coordinate_extractor(node_based_graph, compressed_geometries, node_coordinates)
|
||||
node_coordinates(node_coordinates), intersection_generator(intersection_generator),
|
||||
coordinate_extractor(coordinate_extractor), name_table(name_table),
|
||||
street_name_suffix_table(street_name_suffix_table)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -174,9 +170,8 @@ bool MergableRoadDetector::EdgeDataSupportsMerge(
|
||||
bool MergableRoadDetector::IsTrafficLoop(const NodeID intersection_node,
|
||||
const MergableRoadData &road) const
|
||||
{
|
||||
const auto connection =
|
||||
intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, road.eid});
|
||||
return intersection_node == node_based_graph.GetTarget(connection.edge);
|
||||
const auto connection = intersection_generator.SkipDegreeTwoNodes(intersection_node, road.eid);
|
||||
return intersection_node == node_based_graph.GetTarget(connection.via_eid);
|
||||
}
|
||||
|
||||
bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
||||
@@ -185,22 +180,8 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
||||
{
|
||||
// selection data to the right and left
|
||||
const auto constexpr SMALL_RANDOM_HOPLIMIT = 5;
|
||||
IntersectionFinderAccumulator left_accumulator(SMALL_RANDOM_HOPLIMIT,
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data),
|
||||
right_accumulator(SMALL_RANDOM_HOPLIMIT,
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data);
|
||||
IntersectionFinderAccumulator left_accumulator(SMALL_RANDOM_HOPLIMIT, intersection_generator),
|
||||
right_accumulator(SMALL_RANDOM_HOPLIMIT, intersection_generator);
|
||||
|
||||
/* Standard following the straightmost road
|
||||
* Since both items have the same id, we can `select` based on any setup
|
||||
@@ -212,13 +193,8 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
||||
/*requires entry=*/false,
|
||||
false);
|
||||
|
||||
NodeBasedGraphWalker graph_walker(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data);
|
||||
NodeBasedGraphWalker graph_walker(
|
||||
node_based_graph, node_data_container, intersection_generator);
|
||||
graph_walker.TraverseRoad(intersection_node, lhs.eid, left_accumulator, selector);
|
||||
/* if the intersection does not have a right turn, we continue onto the next one once
|
||||
* (skipping over a single small side street)
|
||||
@@ -290,13 +266,7 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
||||
|
||||
// check if both intersections are connected
|
||||
IntersectionFinderAccumulator connect_accumulator(SMALL_RANDOM_HOPLIMIT,
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data);
|
||||
intersection_generator);
|
||||
graph_walker.TraverseRoad(node_based_graph.GetTarget(left_accumulator.via_edge_id),
|
||||
connector_turn->eid,
|
||||
connect_accumulator,
|
||||
@@ -311,13 +281,8 @@ bool MergableRoadDetector::IsCircularShape(const NodeID intersection_node,
|
||||
const MergableRoadData &lhs,
|
||||
const MergableRoadData &rhs) const
|
||||
{
|
||||
NodeBasedGraphWalker graph_walker(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data);
|
||||
NodeBasedGraphWalker graph_walker(
|
||||
node_based_graph, node_data_container, intersection_generator);
|
||||
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
|
||||
LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length);
|
||||
SelectStraightmostRoadByNameAndOnlyChoice selector(
|
||||
@@ -383,13 +348,8 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
|
||||
return false;
|
||||
|
||||
// Find a coordinate following a road that is far away
|
||||
NodeBasedGraphWalker graph_walker(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data);
|
||||
NodeBasedGraphWalker graph_walker(
|
||||
node_based_graph, node_data_container, intersection_generator);
|
||||
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
|
||||
LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length);
|
||||
SelectStraightmostRoadByNameAndOnlyChoice selector(
|
||||
@@ -459,16 +419,10 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
|
||||
return false;
|
||||
|
||||
// compare reference distance:
|
||||
const auto distance_mid_left_to_right = util::coordinate_calculation::findClosestDistance(
|
||||
const auto distance_between_roads = util::coordinate_calculation::findClosestDistance(
|
||||
coordinates_to_the_left[coordinates_to_the_left.size() / 2],
|
||||
coordinates_to_the_right.begin(),
|
||||
coordinates_to_the_right.end());
|
||||
const auto distance_mid_right_to_left = util::coordinate_calculation::findClosestDistance(
|
||||
coordinates_to_the_right[coordinates_to_the_right.size() / 2],
|
||||
coordinates_to_the_left.begin(),
|
||||
coordinates_to_the_left.end());
|
||||
const auto distance_between_roads =
|
||||
std::min(distance_mid_left_to_right, distance_mid_right_to_left);
|
||||
|
||||
const auto lane_count_lhs = std::max<int>(
|
||||
1, node_based_graph.GetEdgeData(lhs.eid).flags.road_classification.GetNumberOfLanes());
|
||||
@@ -489,12 +443,12 @@ bool MergableRoadDetector::IsTrafficIsland(const NodeID intersection_node,
|
||||
* location with the same name repeatet at least three times
|
||||
*/
|
||||
const auto left_connection =
|
||||
intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, lhs.eid});
|
||||
intersection_generator.SkipDegreeTwoNodes(intersection_node, lhs.eid);
|
||||
const auto right_connection =
|
||||
intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, rhs.eid});
|
||||
intersection_generator.SkipDegreeTwoNodes(intersection_node, rhs.eid);
|
||||
|
||||
const auto left_candidate = node_based_graph.GetTarget(left_connection.edge);
|
||||
const auto right_candidate = node_based_graph.GetTarget(right_connection.edge);
|
||||
const auto left_candidate = node_based_graph.GetTarget(left_connection.via_eid);
|
||||
const auto right_candidate = node_based_graph.GetTarget(right_connection.via_eid);
|
||||
|
||||
const auto candidate_is_valid =
|
||||
left_candidate == right_candidate && left_candidate != intersection_node;
|
||||
@@ -562,16 +516,9 @@ bool MergableRoadDetector::IsLinkRoad(const NodeID intersection_node,
|
||||
const MergableRoadData &road) const
|
||||
{
|
||||
const auto next_intersection_parameters =
|
||||
intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, road.eid});
|
||||
const auto next_intersection_along_road =
|
||||
intersection::getConnectedRoads<false>(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
next_intersection_parameters);
|
||||
intersection_generator.SkipDegreeTwoNodes(intersection_node, road.eid);
|
||||
const auto next_intersection_along_road = intersection_generator.GetConnectedRoads(
|
||||
next_intersection_parameters.nid, next_intersection_parameters.via_eid);
|
||||
const auto extract_name_id = [this](const MergableRoadData &road) {
|
||||
return node_data_container
|
||||
.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
||||
@@ -596,7 +543,7 @@ bool MergableRoadDetector::IsLinkRoad(const NodeID intersection_node,
|
||||
|
||||
// we cannot be looking at the same road we came from
|
||||
if (node_based_graph.GetTarget(opposite_of_next_road_along_path->eid) ==
|
||||
next_intersection_parameters.node)
|
||||
next_intersection_parameters.nid)
|
||||
return false;
|
||||
|
||||
/* check if the opposite of the next road decision was sane. It could have been just as well our
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/road_classification.hpp"
|
||||
|
||||
#include "util/assert.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/guidance/name_announcements.hpp"
|
||||
|
||||
@@ -43,21 +42,15 @@ inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_base
|
||||
MotorwayHandler::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)
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: IntersectionHandler(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
name_table,
|
||||
street_name_suffix_table)
|
||||
street_name_suffix_table,
|
||||
intersection_generator)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -278,11 +271,17 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
|
||||
// handle motorway forks
|
||||
else if (exiting_motorways > 1)
|
||||
{
|
||||
if (exiting_motorways == 2)
|
||||
if (exiting_motorways == 2 && intersection.size() == 2)
|
||||
{
|
||||
intersection[1].instruction =
|
||||
getInstructionForObvious(intersection.size(),
|
||||
via_eid,
|
||||
isThroughStreet(1, intersection),
|
||||
intersection[1]);
|
||||
intersection[0].entry_allowed = false; // UTURN on the freeway
|
||||
}
|
||||
else if (exiting_motorways == 2)
|
||||
{
|
||||
OSRM_ASSERT(intersection.size() != 2,
|
||||
node_coordinates[node_based_graph.GetTarget(via_eid)]);
|
||||
|
||||
// standard fork
|
||||
std::size_t first_valid = std::numeric_limits<std::size_t>::max(),
|
||||
second_valid = std::numeric_limits<std::size_t>::max();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "extractor/guidance/node_based_graph_walker.hpp"
|
||||
#include "extractor/intersection/intersection_analysis.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
|
||||
@@ -15,18 +14,11 @@ namespace guidance
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
NodeBasedGraphWalker::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)
|
||||
NodeBasedGraphWalker::NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||
node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
|
||||
node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
|
||||
turn_lanes_data(turn_lanes_data)
|
||||
intersection_generator(intersection_generator)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -243,18 +235,8 @@ operator()(const NodeID /*nid*/,
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
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)
|
||||
: hops(0), hop_limit(hop_limit), node_based_graph(node_based_graph),
|
||||
node_data_container(node_data_container), node_coordinates(node_coordinates),
|
||||
compressed_geometries(compressed_geometries), node_restriction_map(node_restriction_map),
|
||||
barrier_nodes(barrier_nodes), turn_lanes_data(turn_lanes_data)
|
||||
const std::uint8_t hop_limit, const IntersectionGenerator &intersection_generator)
|
||||
: hops(0), hop_limit(hop_limit), intersection_generator(intersection_generator)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -279,14 +261,7 @@ void IntersectionFinderAccumulator::update(const NodeID from_node,
|
||||
nid = from_node;
|
||||
via_edge_id = via_edge;
|
||||
|
||||
intersection = intersection::getConnectedRoads<true>(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
{from_node, via_edge});
|
||||
intersection = intersection_generator.GetConnectedRoads(from_node, via_edge, true);
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
|
||||
@@ -23,26 +23,21 @@ namespace extractor
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
RoundaboutHandler::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::RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const CompressedEdgeContainer &compressed_edge_container,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: IntersectionHandler(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
name_table,
|
||||
street_name_suffix_table),
|
||||
coordinate_extractor(node_based_graph, compressed_geometries, coordinates)
|
||||
street_name_suffix_table,
|
||||
intersection_generator),
|
||||
compressed_edge_container(compressed_edge_container),
|
||||
coordinate_extractor(node_based_graph, compressed_edge_container, coordinates)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -61,6 +56,7 @@ bool RoundaboutHandler::canProcess(const NodeID from_nid,
|
||||
Intersection RoundaboutHandler::
|
||||
operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersection) const
|
||||
{
|
||||
invalidateExitAgainstDirection(from_nid, via_eid, intersection);
|
||||
const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
|
||||
const auto roundabout_type = getRoundaboutType(node_based_graph.GetTarget(via_eid));
|
||||
// find the radius of the roundabout
|
||||
@@ -111,6 +107,77 @@ detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags(
|
||||
return {on_roundabout, can_enter_roundabout, can_exit_roundabout_separately};
|
||||
}
|
||||
|
||||
void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid,
|
||||
const EdgeID via_eid,
|
||||
Intersection &intersection) const
|
||||
{
|
||||
const auto &in_edge_class = node_based_graph.GetEdgeData(via_eid).flags;
|
||||
if (in_edge_class.roundabout || in_edge_class.circular)
|
||||
return;
|
||||
|
||||
// Find range in which exits that must be invalidated (shaded areas):
|
||||
// exit..end exit..end begin..exit for ↺ roundabouts
|
||||
// *************************************
|
||||
// * <--. ^ <--. / <--. *
|
||||
// * | / | /░ | *
|
||||
// * |/ |v░░ -->| *
|
||||
// * |^ |\ ░ ░░░|\ *
|
||||
// * |░\ |░\░ ░░░| \ *
|
||||
// * --'░░░\ --'░░░v --' v *
|
||||
// *************************************
|
||||
//
|
||||
// begin..exit begin..exit exit..end for ↻ roundabouts
|
||||
// *************************************
|
||||
// * --.░░░^ --.░░░/ --. ^ *
|
||||
// * |░/░ |░/ ░░░| / *
|
||||
// * |/░░ |v ░░░|/ *
|
||||
// * |^░░ |\ -->| *
|
||||
// * | \░ | \ | *
|
||||
// * <--' \ <--' v <--' *
|
||||
// *************************************
|
||||
bool roundabout_entry_first = false;
|
||||
auto invalidate_from = intersection.end(), invalidate_to = intersection.end();
|
||||
for (auto road = intersection.begin(); road != intersection.end(); ++road)
|
||||
{
|
||||
const auto &edge = node_based_graph.GetEdgeData(road->eid);
|
||||
if (edge.flags.roundabout || edge.flags.circular)
|
||||
{
|
||||
if (edge.reversed)
|
||||
{
|
||||
if (roundabout_entry_first)
|
||||
{ // invalidate turns in range exit..end
|
||||
invalidate_from = road + 1;
|
||||
invalidate_to = intersection.end();
|
||||
}
|
||||
else
|
||||
{ // invalidate turns in range begin..exit
|
||||
invalidate_from = intersection.begin() + 1;
|
||||
invalidate_to = road;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
roundabout_entry_first = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OSRM_ASSERT(invalidate_from <= invalidate_to, coordinates[from_nid]);
|
||||
|
||||
// Exiting roundabouts at an entry point is technically a data-modelling issue.
|
||||
// This workaround handles cases in which an exit precedes and entry. The resulting
|
||||
// u-turn against the roundabout direction is invalidated.
|
||||
for (; invalidate_from != invalidate_to; ++invalidate_from)
|
||||
{
|
||||
const auto &edge = node_based_graph.GetEdgeData(invalidate_from->eid);
|
||||
if (!edge.flags.roundabout && !edge.flags.circular &&
|
||||
node_based_graph.GetTarget(invalidate_from->eid) != from_nid)
|
||||
{
|
||||
invalidate_from->entry_allowed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we want to see a roundabout as a turn, the exits have to be distinct enough to be seen a
|
||||
// dedicated turns. We are limiting it to four-way intersections with well distinct bearings.
|
||||
// All entry/roads and exit roads have to be simple. Not segregated roads.
|
||||
@@ -148,7 +215,7 @@ bool RoundaboutHandler::qualifiesAsRoundaboutIntersection(
|
||||
continue;
|
||||
|
||||
// there is a single non-roundabout edge
|
||||
const auto src_coordinate = node_coordinates[node];
|
||||
const auto src_coordinate = coordinates[node];
|
||||
|
||||
const auto edge_range = node_based_graph.GetAdjacentEdgeRange(node);
|
||||
const auto number_of_lanes_at_intersection = std::accumulate(
|
||||
@@ -267,11 +334,11 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
|
||||
|
||||
const auto getEdgeLength = [&](const NodeID source_node, EdgeID eid) {
|
||||
double length = 0.;
|
||||
auto last_coord = node_coordinates[source_node];
|
||||
const auto &edge_bucket = compressed_geometries.GetBucketReference(eid);
|
||||
auto last_coord = coordinates[source_node];
|
||||
const auto &edge_bucket = compressed_edge_container.GetBucketReference(eid);
|
||||
for (const auto &compressed_edge : edge_bucket)
|
||||
{
|
||||
const auto next_coord = node_coordinates[compressed_edge.node_id];
|
||||
const auto next_coord = coordinates[compressed_edge.node_id];
|
||||
length += util::coordinate_calculation::haversineDistance(last_coord, next_coord);
|
||||
last_coord = next_coord;
|
||||
}
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
#include "extractor/guidance/segregated_intersection_classification.hpp"
|
||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||
#include "extractor/node_based_graph_factory.hpp"
|
||||
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/name_table.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
struct EdgeInfo
|
||||
{
|
||||
NodeID node;
|
||||
|
||||
util::StringView name;
|
||||
|
||||
// 0 - outgoing (forward), 1 - incoming (reverse), 2 - both outgoing and incoming
|
||||
int direction;
|
||||
|
||||
ClassData road_class;
|
||||
|
||||
RoadPriorityClass::Enum road_priority_class;
|
||||
|
||||
struct LessName
|
||||
{
|
||||
bool operator()(EdgeInfo const &e1, EdgeInfo const &e2) const { return e1.name < e2.name; }
|
||||
};
|
||||
};
|
||||
|
||||
bool IsSegregated(std::vector<EdgeInfo> v1,
|
||||
std::vector<EdgeInfo> v2,
|
||||
EdgeInfo const ¤t,
|
||||
double edgeLength)
|
||||
{
|
||||
if (v1.size() < 2 || v2.size() < 2)
|
||||
return false;
|
||||
|
||||
auto const sort_by_name_fn = [](std::vector<EdgeInfo> &v) {
|
||||
std::sort(v.begin(), v.end(), EdgeInfo::LessName());
|
||||
};
|
||||
|
||||
sort_by_name_fn(v1);
|
||||
sort_by_name_fn(v2);
|
||||
|
||||
// Internal edge with the name should be connected with any other neibour edge with the same
|
||||
// name, e.g. isolated edge with unique name is not segregated.
|
||||
// b - 'b' road continues here
|
||||
// |
|
||||
// - - a - |
|
||||
// b - segregated edge
|
||||
// - - a - |
|
||||
if (!current.name.empty())
|
||||
{
|
||||
auto const findNameFn = [¤t](std::vector<EdgeInfo> const &v) {
|
||||
return std::binary_search(v.begin(), v.end(), current, EdgeInfo::LessName());
|
||||
};
|
||||
|
||||
if (!findNameFn(v1) && !findNameFn(v2))
|
||||
return false;
|
||||
}
|
||||
|
||||
// set_intersection like routine to get equal result pairs
|
||||
std::vector<std::pair<EdgeInfo const *, EdgeInfo const *>> commons;
|
||||
|
||||
auto i1 = v1.begin();
|
||||
auto i2 = v2.begin();
|
||||
|
||||
while (i1 != v1.end() && i2 != v2.end())
|
||||
{
|
||||
if (i1->name == i2->name)
|
||||
{
|
||||
if (!i1->name.empty())
|
||||
commons.push_back(std::make_pair(&(*i1), &(*i2)));
|
||||
|
||||
++i1;
|
||||
++i2;
|
||||
}
|
||||
else if (i1->name < i2->name)
|
||||
++i1;
|
||||
else
|
||||
++i2;
|
||||
}
|
||||
|
||||
if (commons.size() < 2)
|
||||
return false;
|
||||
|
||||
auto const check_equal_class = [](std::pair<EdgeInfo const *, EdgeInfo const *> const &e) {
|
||||
// Or (e.first->road_class & e.second->road_class != 0)
|
||||
return e.first->road_class == e.second->road_class;
|
||||
};
|
||||
|
||||
size_t equal_class_count = 0;
|
||||
for (auto const &e : commons)
|
||||
if (check_equal_class(e))
|
||||
++equal_class_count;
|
||||
|
||||
if (equal_class_count < 2)
|
||||
return false;
|
||||
|
||||
auto const get_length_threshold = [](EdgeInfo const *e) {
|
||||
switch (e->road_priority_class)
|
||||
{
|
||||
case RoadPriorityClass::MOTORWAY:
|
||||
case RoadPriorityClass::TRUNK:
|
||||
return 30.0;
|
||||
case RoadPriorityClass::PRIMARY:
|
||||
return 20.0;
|
||||
case RoadPriorityClass::SECONDARY:
|
||||
case RoadPriorityClass::TERTIARY:
|
||||
return 10.0;
|
||||
default:
|
||||
return 5.0;
|
||||
}
|
||||
};
|
||||
|
||||
double threshold = std::numeric_limits<double>::max();
|
||||
for (auto const &e : commons)
|
||||
threshold =
|
||||
std::min(threshold, get_length_threshold(e.first) + get_length_threshold(e.second));
|
||||
|
||||
return edgeLength <= threshold;
|
||||
}
|
||||
|
||||
std::unordered_set<EdgeID> findSegregatedNodes(const NodeBasedGraphFactory &factory,
|
||||
const util::NameTable &names)
|
||||
{
|
||||
|
||||
auto const &graph = factory.GetGraph();
|
||||
auto const &annotation = factory.GetAnnotationData();
|
||||
|
||||
CoordinateExtractor coordExtractor(
|
||||
graph, factory.GetCompressedEdges(), factory.GetCoordinates());
|
||||
|
||||
auto const get_edge_length = [&](NodeID from_node, EdgeID edgeID, NodeID to_node) {
|
||||
auto const geom = coordExtractor.GetCoordinatesAlongRoad(from_node, edgeID, false, to_node);
|
||||
double length = 0.0;
|
||||
for (size_t i = 1; i < geom.size(); ++i)
|
||||
{
|
||||
length += util::coordinate_calculation::haversineDistance(geom[i - 1], geom[i]);
|
||||
}
|
||||
return length;
|
||||
};
|
||||
|
||||
auto const get_edge_info = [&](NodeID node, auto const &edgeData) -> EdgeInfo {
|
||||
/// @todo Make string normalization/lowercase/trim for comparison ...
|
||||
|
||||
auto const id = annotation[edgeData.annotation_data].name_id;
|
||||
BOOST_ASSERT(id != INVALID_NAMEID);
|
||||
auto const name = names.GetNameForID(id);
|
||||
|
||||
return {node,
|
||||
name,
|
||||
edgeData.reversed ? 1 : 0,
|
||||
annotation[edgeData.annotation_data].classes,
|
||||
edgeData.flags.road_classification.GetClass()};
|
||||
};
|
||||
|
||||
auto const collect_edge_info_fn = [&](auto const &edges1, NodeID node2) {
|
||||
std::vector<EdgeInfo> info;
|
||||
|
||||
for (auto const &e : edges1)
|
||||
{
|
||||
NodeID const target = graph.GetTarget(e);
|
||||
if (target == node2)
|
||||
continue;
|
||||
|
||||
info.push_back(get_edge_info(target, graph.GetEdgeData(e)));
|
||||
}
|
||||
|
||||
if (info.empty())
|
||||
return info;
|
||||
|
||||
std::sort(info.begin(), info.end(), [](EdgeInfo const &e1, EdgeInfo const &e2) {
|
||||
return e1.node < e2.node;
|
||||
});
|
||||
|
||||
// Merge equal infos with correct direction.
|
||||
auto curr = info.begin();
|
||||
auto next = curr;
|
||||
while (++next != info.end())
|
||||
{
|
||||
if (curr->node == next->node)
|
||||
{
|
||||
BOOST_ASSERT(curr->name == next->name);
|
||||
BOOST_ASSERT(curr->road_class == next->road_class);
|
||||
BOOST_ASSERT(curr->direction != next->direction);
|
||||
curr->direction = 2;
|
||||
}
|
||||
else
|
||||
curr = next;
|
||||
}
|
||||
|
||||
info.erase(
|
||||
std::unique(info.begin(),
|
||||
info.end(),
|
||||
[](EdgeInfo const &e1, EdgeInfo const &e2) { return e1.node == e2.node; }),
|
||||
info.end());
|
||||
|
||||
return info;
|
||||
};
|
||||
|
||||
auto const isSegregatedFn = [&](auto const &edgeData,
|
||||
auto const &edges1,
|
||||
NodeID node1,
|
||||
auto const &edges2,
|
||||
NodeID node2,
|
||||
double edgeLength) {
|
||||
return IsSegregated(collect_edge_info_fn(edges1, node2),
|
||||
collect_edge_info_fn(edges2, node1),
|
||||
get_edge_info(node1, edgeData),
|
||||
edgeLength);
|
||||
};
|
||||
|
||||
std::unordered_set<EdgeID> segregated_edges;
|
||||
|
||||
for (NodeID sourceID = 0; sourceID < graph.GetNumberOfNodes(); ++sourceID)
|
||||
{
|
||||
auto const sourceEdges = graph.GetAdjacentEdgeRange(sourceID);
|
||||
for (EdgeID edgeID : sourceEdges)
|
||||
{
|
||||
auto const &edgeData = graph.GetEdgeData(edgeID);
|
||||
|
||||
if (edgeData.reversed)
|
||||
continue;
|
||||
|
||||
NodeID const targetID = graph.GetTarget(edgeID);
|
||||
auto const targetEdges = graph.GetAdjacentEdgeRange(targetID);
|
||||
|
||||
double const length = get_edge_length(sourceID, edgeID, targetID);
|
||||
if (isSegregatedFn(edgeData, sourceEdges, sourceID, targetEdges, targetID, length))
|
||||
segregated_edges.insert(edgeID);
|
||||
}
|
||||
}
|
||||
|
||||
return segregated_edges;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,25 +22,18 @@ namespace extractor
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
SliproadHandler::SliproadHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
SliproadHandler::SliproadHandler(const IntersectionGenerator &intersection_generator,
|
||||
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 std::vector<util::Coordinate> &coordinates,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table)
|
||||
: IntersectionHandler(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table),
|
||||
coordinate_extractor(node_based_graph, compressed_geometries, node_coordinates)
|
||||
street_name_suffix_table,
|
||||
intersection_generator)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -250,14 +243,7 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
|
||||
// Starting out at the intersection and going onto the Sliproad we skip artificial
|
||||
// degree two intersections and limit the max hop count in doing so.
|
||||
IntersectionFinderAccumulator intersection_finder{10,
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data};
|
||||
IntersectionFinderAccumulator intersection_finder{10, intersection_generator};
|
||||
const SkipTrafficSignalBarrierRoadSelector road_selector{};
|
||||
(void)graph_walker.TraverseRoad(intersection_node_id, // start node
|
||||
sliproad_edge, // onto edge
|
||||
@@ -386,6 +372,8 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
//
|
||||
// Sliproad Not a Sliproad
|
||||
{
|
||||
const auto &coordinate_extractor = intersection_generator.GetCoordinateExtractor();
|
||||
|
||||
const NodeID start = intersection_node_id; // b
|
||||
const EdgeID edge = sliproad_edge; // bd
|
||||
|
||||
@@ -436,8 +424,8 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
// Only check for curvature and ~90 degree when it makes sense to do so.
|
||||
const constexpr auto MIN_LENGTH = 3.;
|
||||
|
||||
const auto length = haversineDistance(node_coordinates[intersection_node_id],
|
||||
node_coordinates[main_road_intersection->node]);
|
||||
const auto length = haversineDistance(coordinates[intersection_node_id],
|
||||
coordinates[main_road_intersection->node]);
|
||||
|
||||
const double minimal_crossroad_angle_of_intersection = 40.;
|
||||
|
||||
@@ -558,15 +546,8 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto skip_traffic_light_intersection = intersection::getConnectedRoads<false>(
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
{node_based_graph.GetTarget(sliproad_edge), candidate_road.eid});
|
||||
const auto skip_traffic_light_intersection = intersection_generator(
|
||||
node_based_graph.GetTarget(sliproad_edge), candidate_road.eid);
|
||||
if (skip_traffic_light_intersection.isTrafficSignalOrBarrier() &&
|
||||
node_based_graph.GetTarget(skip_traffic_light_intersection[1].eid) ==
|
||||
main_road_intersection->node)
|
||||
@@ -675,6 +656,8 @@ bool SliproadHandler::nextIntersectionIsTooFarAway(const NodeID start, const Edg
|
||||
BOOST_ASSERT(start != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(onto != SPECIAL_EDGEID);
|
||||
|
||||
const auto &coordinate_extractor = intersection_generator.GetCoordinateExtractor();
|
||||
|
||||
// Base max distance threshold on the current road class we're on
|
||||
const auto &data = node_based_graph.GetEdgeData(onto).flags;
|
||||
const auto threshold = scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, // <- scales down
|
||||
@@ -745,9 +728,9 @@ bool SliproadHandler::isValidSliproadArea(const double max_area,
|
||||
{
|
||||
using namespace util::coordinate_calculation;
|
||||
|
||||
const auto first = node_coordinates[a];
|
||||
const auto second = node_coordinates[b];
|
||||
const auto third = node_coordinates[c];
|
||||
const auto first = coordinates[a];
|
||||
const auto second = coordinates[b];
|
||||
const auto third = coordinates[c];
|
||||
|
||||
const auto length = haversineDistance(first, second);
|
||||
const auto heigth = haversineDistance(second, third);
|
||||
|
||||
@@ -11,25 +11,18 @@ namespace extractor
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
SuppressModeHandler::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::SuppressModeHandler(const IntersectionGenerator &intersection_generator,
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
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)
|
||||
street_name_suffix_table,
|
||||
intersection_generator)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -23,79 +23,89 @@ using EdgeData = util::NodeBasedDynamicGraph::EdgeData;
|
||||
|
||||
TurnAnalysis::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 std::vector<util::Coordinate> &coordinates,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||
const CompressedEdgeContainer &compressed_edge_container,
|
||||
const util::NameTable &name_table,
|
||||
const SuffixTable &street_name_suffix_table)
|
||||
: node_based_graph(node_based_graph), roundabout_handler(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_edge_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
name_table,
|
||||
street_name_suffix_table),
|
||||
: node_based_graph(node_based_graph), intersection_generator(node_based_graph,
|
||||
node_data_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
coordinates,
|
||||
compressed_edge_container),
|
||||
intersection_normalizer(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
intersection_generator),
|
||||
roundabout_handler(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
compressed_edge_container,
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
intersection_generator),
|
||||
motorway_handler(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_edge_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table),
|
||||
street_name_suffix_table,
|
||||
intersection_generator),
|
||||
turn_handler(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_edge_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table),
|
||||
sliproad_handler(node_based_graph,
|
||||
street_name_suffix_table,
|
||||
intersection_generator),
|
||||
sliproad_handler(intersection_generator,
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_edge_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table),
|
||||
suppress_mode_handler(node_based_graph,
|
||||
suppress_mode_handler(intersection_generator,
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_edge_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table),
|
||||
driveway_handler(node_based_graph,
|
||||
driveway_handler(intersection_generator,
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_edge_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table),
|
||||
statistics_handler(node_based_graph,
|
||||
statistics_handler(intersection_generator,
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_edge_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
coordinates,
|
||||
name_table,
|
||||
street_name_suffix_table)
|
||||
{
|
||||
}
|
||||
|
||||
Intersection TurnAnalysis::operator()(const NodeID node_prior_to_intersection,
|
||||
const EdgeID entering_via_edge) const
|
||||
{
|
||||
TurnAnalysis::ShapeResult shape_result =
|
||||
ComputeIntersectionShapes(node_based_graph.GetTarget(entering_via_edge));
|
||||
|
||||
// assign valid flags to normalized_shape
|
||||
const auto intersection_view = intersection_generator.TransformIntersectionShapeIntoView(
|
||||
node_prior_to_intersection,
|
||||
entering_via_edge,
|
||||
shape_result.annotated_normalized_shape.normalized_shape,
|
||||
shape_result.intersection_shape,
|
||||
shape_result.annotated_normalized_shape.performed_merges);
|
||||
|
||||
// assign the turn types to the intersection
|
||||
return AssignTurnTypes(node_prior_to_intersection, entering_via_edge, intersection_view);
|
||||
}
|
||||
|
||||
Intersection TurnAnalysis::AssignTurnTypes(const NodeID node_prior_to_intersection,
|
||||
const EdgeID entering_via_edge,
|
||||
const IntersectionView &intersection_view) const
|
||||
@@ -181,6 +191,19 @@ Intersection TurnAnalysis::AssignTurnTypes(const NodeID node_prior_to_intersecti
|
||||
return intersection;
|
||||
}
|
||||
|
||||
TurnAnalysis::ShapeResult
|
||||
TurnAnalysis::ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const
|
||||
{
|
||||
ShapeResult intersection_shape;
|
||||
intersection_shape.intersection_shape =
|
||||
intersection_generator.ComputeIntersectionShape(node_at_center_of_intersection);
|
||||
|
||||
intersection_shape.annotated_normalized_shape = intersection_normalizer(
|
||||
node_at_center_of_intersection, intersection_shape.intersection_shape);
|
||||
|
||||
return intersection_shape;
|
||||
}
|
||||
|
||||
// Sets basic turn types as fallback for otherwise unhandled turns
|
||||
Intersection TurnAnalysis::setTurnTypes(const NodeID node_prior_to_intersection,
|
||||
const EdgeID,
|
||||
@@ -202,6 +225,11 @@ Intersection TurnAnalysis::setTurnTypes(const NodeID node_prior_to_intersection,
|
||||
return intersection;
|
||||
}
|
||||
|
||||
const IntersectionGenerator &TurnAnalysis::GetIntersectionGenerator() const
|
||||
{
|
||||
return intersection_generator;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "extractor/guidance/turn_discovery.hpp"
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||
#include "extractor/intersection/intersection_analysis.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
|
||||
@@ -16,16 +14,16 @@ namespace guidance
|
||||
namespace lanes
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
const constexpr bool USE_LOW_PRECISION_MODE = true;
|
||||
}
|
||||
|
||||
bool findPreviousIntersection(const NodeID node_v,
|
||||
const EdgeID via_edge,
|
||||
const Intersection &intersection,
|
||||
const IntersectionGenerator &intersection_generator,
|
||||
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,
|
||||
// output parameters
|
||||
NodeID &result_node,
|
||||
EdgeID &result_via_edge,
|
||||
@@ -45,9 +43,7 @@ bool findPreviousIntersection(const NodeID node_v,
|
||||
*/
|
||||
const constexpr double COMBINE_DISTANCE_CUTOFF = 30;
|
||||
|
||||
const CoordinateExtractor coordinate_extractor(
|
||||
node_based_graph, compressed_geometries, node_coordinates);
|
||||
|
||||
const auto coordinate_extractor = intersection_generator.GetCoordinateExtractor();
|
||||
const auto coordinates_along_via_edge =
|
||||
coordinate_extractor.GetForwardCoordinatesAlongRoad(node_v, via_edge);
|
||||
const auto via_edge_length =
|
||||
@@ -75,14 +71,7 @@ bool findPreviousIntersection(const NodeID node_v,
|
||||
return false;
|
||||
|
||||
const auto node_v_reverse_intersection =
|
||||
intersection::getConnectedRoads<true>(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
{node_w, u_turn_at_node_w});
|
||||
intersection_generator.GetConnectedRoads(node_w, u_turn_at_node_w, USE_LOW_PRECISION_MODE);
|
||||
// Continue along the straightmost turn. If there is no straight turn, we cannot find a valid
|
||||
// previous intersection.
|
||||
const auto straightmost_at_v_in_reverse =
|
||||
@@ -94,15 +83,8 @@ bool findPreviousIntersection(const NodeID node_v,
|
||||
return false;
|
||||
|
||||
const auto node_u = node_based_graph.GetTarget(straightmost_at_v_in_reverse->eid);
|
||||
const auto node_u_reverse_intersection =
|
||||
intersection::getConnectedRoads<true>(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
{node_v, straightmost_at_v_in_reverse->eid});
|
||||
const auto node_u_reverse_intersection = intersection_generator.GetConnectedRoads(
|
||||
node_v, straightmost_at_v_in_reverse->eid, USE_LOW_PRECISION_MODE);
|
||||
|
||||
// now check that the u-turn at the given intersection connects to via-edge
|
||||
// The u-turn at the now found intersection should, hopefully, represent the previous edge.
|
||||
@@ -120,14 +102,7 @@ bool findPreviousIntersection(const NodeID node_v,
|
||||
return false;
|
||||
}
|
||||
|
||||
result_intersection = intersection::getConnectedRoads<false>(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
{node_u, result_via_edge});
|
||||
result_intersection = intersection_generator(node_u, result_via_edge);
|
||||
const auto check_via_edge =
|
||||
result_intersection.end() !=
|
||||
std::find_if(result_intersection.begin(),
|
||||
|
||||
@@ -113,21 +113,15 @@ std::size_t TurnHandler::Fork::getLeftIndex() const
|
||||
TurnHandler::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)
|
||||
const SuffixTable &street_name_suffix_table,
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: IntersectionHandler(node_based_graph,
|
||||
node_data_container,
|
||||
coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
name_table,
|
||||
street_name_suffix_table)
|
||||
street_name_suffix_table,
|
||||
intersection_generator)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "extractor/guidance/turn_discovery.hpp"
|
||||
#include "extractor/guidance/turn_lane_augmentation.hpp"
|
||||
#include "extractor/guidance/turn_lane_matcher.hpp"
|
||||
#include "extractor/intersection/intersection_analysis.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
@@ -36,21 +35,14 @@ std::size_t getNumberOfTurns(const Intersection &intersection)
|
||||
|
||||
TurnLaneHandler::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)
|
||||
: node_based_graph(node_based_graph), node_data_container(node_data_container),
|
||||
node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
|
||||
node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
|
||||
turn_lanes_data(turn_lanes_data), lane_description_map(lane_description_map),
|
||||
turn_analysis(turn_analysis), id_map(id_map)
|
||||
lane_description_map(lane_description_map), turn_analysis(turn_analysis), id_map(id_map)
|
||||
{
|
||||
std::tie(turn_lane_offsets, turn_lane_masks) = turn_lanes_data;
|
||||
std::tie(turn_lane_offsets, turn_lane_masks) =
|
||||
transformTurnLaneMapIntoArrays(lane_description_map);
|
||||
count_handled = count_called = 0;
|
||||
}
|
||||
|
||||
@@ -213,13 +205,8 @@ TurnLaneScenario TurnLaneHandler::deduceScenario(const NodeID at,
|
||||
if (findPreviousIntersection(at,
|
||||
via_edge,
|
||||
intersection,
|
||||
turn_analysis.GetIntersectionGenerator(),
|
||||
node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
previous_node,
|
||||
previous_via_edge,
|
||||
previous_intersection_view))
|
||||
@@ -573,16 +560,7 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
// find out about the next intersection. To check for valid matches, we also need the turn
|
||||
// types. We can skip merging/angle adjustments, though
|
||||
const auto next_intersection = turn_analysis.AssignTurnTypes(
|
||||
at,
|
||||
straightmost->eid,
|
||||
intersection::getConnectedRoads<false>(node_based_graph,
|
||||
node_data_container,
|
||||
node_coordinates,
|
||||
compressed_geometries,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
turn_lanes_data,
|
||||
{at, straightmost->eid}));
|
||||
at, straightmost->eid, turn_analysis.GetIntersectionGenerator()(at, straightmost->eid));
|
||||
|
||||
// check where we can match turn lanes
|
||||
std::size_t straightmost_tag_index = turn_lane_data.size();
|
||||
|
||||
@@ -228,6 +228,7 @@ Intersection triviallyMatchLanesToTurns(Intersection intersection,
|
||||
u_turn = 1;
|
||||
road_index = 2;
|
||||
}
|
||||
intersection[u_turn].entry_allowed = true;
|
||||
intersection[u_turn].instruction.type = TurnType::Continue;
|
||||
intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn;
|
||||
|
||||
@@ -267,6 +268,7 @@ Intersection triviallyMatchLanesToTurns(Intersection intersection,
|
||||
}
|
||||
u_turn = intersection.size() - 1;
|
||||
}
|
||||
intersection[u_turn].entry_allowed = true;
|
||||
intersection[u_turn].instruction.type = TurnType::Continue;
|
||||
intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn;
|
||||
|
||||
|
||||
@@ -1,841 +0,0 @@
|
||||
#include "extractor/intersection/intersection_analysis.hpp"
|
||||
|
||||
#include "util/assert.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
|
||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||
|
||||
#include <boost/optional/optional_io.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace intersection
|
||||
{
|
||||
|
||||
IntersectionEdges getIncomingEdges(const util::NodeBasedDynamicGraph &graph,
|
||||
const NodeID intersection_node)
|
||||
{
|
||||
IntersectionEdges result;
|
||||
|
||||
for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
|
||||
{
|
||||
const auto from_node = graph.GetTarget(outgoing_edge);
|
||||
const auto incoming_edge = graph.FindEdge(from_node, intersection_node);
|
||||
|
||||
if (!graph.GetEdgeData(incoming_edge).reversed)
|
||||
{
|
||||
result.push_back({from_node, incoming_edge});
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce ordering of incoming edges
|
||||
std::sort(result.begin(), result.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
IntersectionEdges getOutgoingEdges(const util::NodeBasedDynamicGraph &graph,
|
||||
const NodeID intersection_node)
|
||||
{
|
||||
IntersectionEdges result;
|
||||
|
||||
for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
|
||||
{
|
||||
result.push_back({intersection_node, outgoing_edge});
|
||||
}
|
||||
|
||||
BOOST_ASSERT(std::is_sorted(result.begin(), result.end()));
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<util::Coordinate>
|
||||
getEdgeCoordinates(const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<util::Coordinate> &node_coordinates,
|
||||
const NodeID from_node,
|
||||
const EdgeID edge,
|
||||
const NodeID to_node)
|
||||
{
|
||||
if (!compressed_geometries.HasEntryForID(edge))
|
||||
return {node_coordinates[from_node], node_coordinates[to_node]};
|
||||
|
||||
BOOST_ASSERT(from_node < node_coordinates.size());
|
||||
BOOST_ASSERT(to_node < node_coordinates.size());
|
||||
|
||||
// extracts the geometry in coordinates from the compressed edge container
|
||||
std::vector<util::Coordinate> result;
|
||||
const auto &geometry = compressed_geometries.GetBucketReference(edge);
|
||||
result.reserve(geometry.size() + 1);
|
||||
|
||||
result.push_back(node_coordinates[from_node]);
|
||||
std::transform(geometry.begin(),
|
||||
geometry.end(),
|
||||
std::back_inserter(result),
|
||||
[&node_coordinates](const auto &compressed_edge) {
|
||||
return node_coordinates[compressed_edge.node_id];
|
||||
});
|
||||
|
||||
// filter duplicated coordinates
|
||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
double findAngleBisector(double alpha, double beta)
|
||||
{
|
||||
alpha *= M_PI / 180.;
|
||||
beta *= M_PI / 180.;
|
||||
const auto average =
|
||||
180. * std::atan2(std::sin(alpha) + std::sin(beta), std::cos(alpha) + std::cos(beta)) /
|
||||
M_PI;
|
||||
return std::fmod(average + 360., 360.);
|
||||
}
|
||||
|
||||
double findClosestOppositeBearing(const IntersectionEdgeGeometries &edge_geometries,
|
||||
const double bearing)
|
||||
{
|
||||
BOOST_ASSERT(!edge_geometries.empty());
|
||||
const auto min = std::min_element(
|
||||
edge_geometries.begin(),
|
||||
edge_geometries.end(),
|
||||
[bearing = util::bearing::reverse(bearing)](const auto &lhs, const auto &rhs) {
|
||||
return util::angularDeviation(lhs.perceived_bearing, bearing) <
|
||||
util::angularDeviation(rhs.perceived_bearing, bearing);
|
||||
});
|
||||
return util::bearing::reverse(min->perceived_bearing);
|
||||
}
|
||||
|
||||
std::pair<bool, double> findMergedBearing(const util::NodeBasedDynamicGraph &graph,
|
||||
const IntersectionEdgeGeometries &edge_geometries,
|
||||
std::size_t lhs_index,
|
||||
std::size_t rhs_index,
|
||||
bool neighbor_intersection)
|
||||
{
|
||||
// Function returns a pair with a flag and a value of bearing for merged roads
|
||||
// If the flag is false the bearing must not be used as a merged value at neighbor intersections
|
||||
|
||||
using guidance::STRAIGHT_ANGLE;
|
||||
using guidance::MAXIMAL_ALLOWED_NO_TURN_DEVIATION;
|
||||
using util::bearing::angleBetween;
|
||||
using util::angularDeviation;
|
||||
|
||||
const auto &lhs = edge_geometries[lhs_index];
|
||||
const auto &rhs = edge_geometries[rhs_index];
|
||||
BOOST_ASSERT(graph.GetEdgeData(lhs.edge).reversed != graph.GetEdgeData(rhs.edge).reversed);
|
||||
|
||||
const auto &entry = graph.GetEdgeData(lhs.edge).reversed ? rhs : lhs;
|
||||
const auto opposite_bearing =
|
||||
findClosestOppositeBearing(edge_geometries, entry.perceived_bearing);
|
||||
const auto merged_bearing = findAngleBisector(rhs.perceived_bearing, lhs.perceived_bearing);
|
||||
|
||||
if (angularDeviation(angleBetween(opposite_bearing, entry.perceived_bearing), STRAIGHT_ANGLE) <
|
||||
MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
|
||||
{
|
||||
// In some intersections, turning roads can introduce artificial turns if we merge here.
|
||||
// Consider a scenario like:
|
||||
//
|
||||
// a . g - f
|
||||
// | .
|
||||
// | .
|
||||
// |.
|
||||
// d-b--------e
|
||||
// |
|
||||
// c
|
||||
//
|
||||
// Merging `bgf` and `be` would introduce an angle, even though d-b-e is perfectly straight
|
||||
// We don't change the angle, if such an opposite road exists
|
||||
return {false, entry.perceived_bearing};
|
||||
}
|
||||
|
||||
if (neighbor_intersection)
|
||||
{
|
||||
// Check that the merged bearing makes both turns closer to straight line
|
||||
const auto turn_angle_lhs = angleBetween(opposite_bearing, lhs.perceived_bearing);
|
||||
const auto turn_angle_rhs = angleBetween(opposite_bearing, rhs.perceived_bearing);
|
||||
const auto turn_angle_new = angleBetween(opposite_bearing, merged_bearing);
|
||||
|
||||
if (util::angularDeviation(turn_angle_lhs, STRAIGHT_ANGLE) <
|
||||
util::angularDeviation(turn_angle_new, STRAIGHT_ANGLE) ||
|
||||
util::angularDeviation(turn_angle_rhs, STRAIGHT_ANGLE) <
|
||||
util::angularDeviation(turn_angle_new, STRAIGHT_ANGLE))
|
||||
return {false, opposite_bearing};
|
||||
}
|
||||
|
||||
return {true, merged_bearing};
|
||||
}
|
||||
|
||||
bool isRoadsPairMergeable(const guidance::MergableRoadDetector &detector,
|
||||
const IntersectionEdgeGeometries &edge_geometries,
|
||||
const NodeID intersection_node,
|
||||
const std::size_t index)
|
||||
{
|
||||
const auto size = edge_geometries.size();
|
||||
BOOST_ASSERT(index < size);
|
||||
|
||||
const auto &llhs = edge_geometries[(index + size - 1) % size];
|
||||
const auto &lhs = edge_geometries[index];
|
||||
const auto &rhs = edge_geometries[(index + 1) % size];
|
||||
const auto &rrhs = edge_geometries[(index + 2) % size];
|
||||
|
||||
// TODO: check IsDistinctFrom - it is an angle and name-only check
|
||||
// also check CanMergeRoad for all merging scenarios
|
||||
return detector.IsDistinctFrom({llhs.edge, llhs.perceived_bearing, llhs.length},
|
||||
{lhs.edge, lhs.perceived_bearing, lhs.length}) &&
|
||||
detector.CanMergeRoad(intersection_node,
|
||||
{lhs.edge, lhs.perceived_bearing, lhs.length},
|
||||
{rhs.edge, rhs.perceived_bearing, rhs.length}) &&
|
||||
detector.IsDistinctFrom({rhs.edge, rhs.perceived_bearing, rhs.length},
|
||||
{rrhs.edge, rrhs.perceived_bearing, rrhs.length});
|
||||
}
|
||||
|
||||
auto getIntersectionLanes(const util::NodeBasedDynamicGraph &graph, const NodeID intersection_node)
|
||||
{
|
||||
std::uint8_t max_lanes_intersection = 0;
|
||||
for (auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
|
||||
{
|
||||
max_lanes_intersection =
|
||||
std::max(max_lanes_intersection,
|
||||
graph.GetEdgeData(outgoing_edge).flags.road_classification.GetNumberOfLanes());
|
||||
}
|
||||
return max_lanes_intersection;
|
||||
}
|
||||
|
||||
template <bool USE_CLOSE_COORDINATE>
|
||||
IntersectionEdgeGeometries
|
||||
getIntersectionOutgoingGeometries(const util::NodeBasedDynamicGraph &graph,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<util::Coordinate> &node_coordinates,
|
||||
const NodeID intersection_node)
|
||||
{
|
||||
IntersectionEdgeGeometries edge_geometries;
|
||||
|
||||
// TODO: keep CoordinateExtractor to reproduce bearings, simplify later
|
||||
const guidance::CoordinateExtractor coordinate_extractor(
|
||||
graph, compressed_geometries, node_coordinates);
|
||||
|
||||
const auto max_lanes_intersection = getIntersectionLanes(graph, intersection_node);
|
||||
|
||||
// Collect outgoing edges
|
||||
for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
|
||||
{
|
||||
const auto remote_node = graph.GetTarget(outgoing_edge);
|
||||
|
||||
const auto &geometry = getEdgeCoordinates(
|
||||
compressed_geometries, node_coordinates, intersection_node, outgoing_edge, remote_node);
|
||||
|
||||
// OSRM_ASSERT(geometry.size() >= 2, node_coordinates[intersection_node]);
|
||||
|
||||
const auto close_coordinate =
|
||||
coordinate_extractor.ExtractCoordinateAtLength(2. /*m*/, geometry);
|
||||
const auto initial_bearing =
|
||||
util::coordinate_calculation::bearing(geometry[0], close_coordinate);
|
||||
|
||||
const auto representative_coordinate =
|
||||
USE_CLOSE_COORDINATE || graph.GetOutDegree(intersection_node) <= 2
|
||||
? coordinate_extractor.GetCoordinateCloseToTurn(
|
||||
intersection_node, outgoing_edge, false, remote_node)
|
||||
: coordinate_extractor.ExtractRepresentativeCoordinate(intersection_node,
|
||||
outgoing_edge,
|
||||
false,
|
||||
remote_node,
|
||||
max_lanes_intersection,
|
||||
geometry);
|
||||
const auto perceived_bearing =
|
||||
util::coordinate_calculation::bearing(geometry[0], representative_coordinate);
|
||||
|
||||
const auto edge_length = util::coordinate_calculation::getLength(
|
||||
geometry.begin(), geometry.end(), util::coordinate_calculation::haversineDistance);
|
||||
|
||||
edge_geometries.push_back({outgoing_edge, initial_bearing, perceived_bearing, edge_length});
|
||||
}
|
||||
|
||||
// Sort edges in the clockwise bearings order
|
||||
std::sort(edge_geometries.begin(), edge_geometries.end(), [](const auto &lhs, const auto &rhs) {
|
||||
return lhs.perceived_bearing < rhs.perceived_bearing;
|
||||
});
|
||||
return edge_geometries;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<IntersectionEdgeGeometries, std::unordered_set<EdgeID>>
|
||||
getIntersectionGeometries(const util::NodeBasedDynamicGraph &graph,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<util::Coordinate> &node_coordinates,
|
||||
const guidance::MergableRoadDetector &detector,
|
||||
const NodeID intersection_node)
|
||||
{
|
||||
IntersectionEdgeGeometries edge_geometries = getIntersectionOutgoingGeometries<false>(
|
||||
graph, compressed_geometries, node_coordinates, intersection_node);
|
||||
|
||||
const auto edges_number = edge_geometries.size();
|
||||
|
||||
std::vector<bool> merged_edges(edges_number, false);
|
||||
|
||||
// TODO: intersection views do not contain merged and not allowed edges
|
||||
// but contain other restricted edges that are used in TurnAnalysis,
|
||||
// to be deleted after TurnAnalysis refactoring
|
||||
std::unordered_set<EdgeID> merged_edge_ids;
|
||||
|
||||
if (edges_number >= 3)
|
||||
{ // Adjust bearings of mergeable roads
|
||||
for (std::size_t index = 0; index < edges_number; ++index)
|
||||
{
|
||||
if (isRoadsPairMergeable(detector, edge_geometries, intersection_node, index))
|
||||
{ // Merge bearings of roads left & right
|
||||
const auto next = (index + 1) % edges_number;
|
||||
auto &lhs = edge_geometries[index];
|
||||
auto &rhs = edge_geometries[next];
|
||||
merged_edges[index] = true;
|
||||
merged_edges[next] = true;
|
||||
|
||||
const auto merge = findMergedBearing(graph, edge_geometries, index, next, false);
|
||||
|
||||
lhs.perceived_bearing = lhs.initial_bearing = merge.second;
|
||||
rhs.perceived_bearing = rhs.initial_bearing = merge.second;
|
||||
merged_edge_ids.insert(lhs.edge);
|
||||
merged_edge_ids.insert(rhs.edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (edges_number >= 2)
|
||||
{ // Adjust bearings of roads that will be merged at the neighbor intersections
|
||||
const double constexpr PRUNING_DISTANCE = 30.;
|
||||
|
||||
for (std::size_t index = 0; index < edges_number; ++index)
|
||||
{
|
||||
auto &edge_geometry = edge_geometries[index];
|
||||
|
||||
// Don't adjust bearings of roads that were merged at the current intersection
|
||||
// or have neighbor intersection farer than the pruning distance
|
||||
if (merged_edges[index] || edge_geometry.length > PRUNING_DISTANCE)
|
||||
continue;
|
||||
|
||||
const auto neighbor_intersection_node = graph.GetTarget(edge_geometry.edge);
|
||||
|
||||
const auto neighbor_geometries = getIntersectionOutgoingGeometries<false>(
|
||||
graph, compressed_geometries, node_coordinates, neighbor_intersection_node);
|
||||
|
||||
const auto neighbor_edges = neighbor_geometries.size();
|
||||
if (neighbor_edges <= 1)
|
||||
continue;
|
||||
|
||||
const auto neighbor_curr = std::distance(
|
||||
neighbor_geometries.begin(),
|
||||
std::find_if(neighbor_geometries.begin(),
|
||||
neighbor_geometries.end(),
|
||||
[&graph, &intersection_node](const auto &road) {
|
||||
return graph.GetTarget(road.edge) == intersection_node;
|
||||
}));
|
||||
BOOST_ASSERT(static_cast<std::size_t>(neighbor_curr) != neighbor_geometries.size());
|
||||
const auto neighbor_prev = (neighbor_curr + neighbor_edges - 1) % neighbor_edges;
|
||||
const auto neighbor_next = (neighbor_curr + 1) % neighbor_edges;
|
||||
|
||||
if (isRoadsPairMergeable(
|
||||
detector, neighbor_geometries, neighbor_intersection_node, neighbor_prev))
|
||||
{ // Neighbor intersection has mergable neighbor_prev and neighbor_curr roads
|
||||
BOOST_ASSERT(!isRoadsPairMergeable(
|
||||
detector, neighbor_geometries, neighbor_intersection_node, neighbor_curr));
|
||||
|
||||
// TODO: merge with an angle bisector, but not a reversed closed turn, to be
|
||||
// checked as a difference with the previous implementation
|
||||
const auto merge = findMergedBearing(
|
||||
graph, neighbor_geometries, neighbor_prev, neighbor_curr, true);
|
||||
|
||||
if (merge.first)
|
||||
{
|
||||
const auto offset = util::angularDeviation(
|
||||
merge.second, neighbor_geometries[neighbor_curr].perceived_bearing);
|
||||
|
||||
// Adjust bearing of AB at the node A if at the node B roads BA (neighbor_curr)
|
||||
// and BC (neighbor_prev) will be merged and will have merged bearing Bb.
|
||||
// The adjustment value is ∠bBA with negative sign (counter-clockwise) to Aa
|
||||
// A ~~~ a
|
||||
// \
|
||||
// b --- B ---
|
||||
// /
|
||||
// C
|
||||
edge_geometry.perceived_bearing = edge_geometry.initial_bearing =
|
||||
std::fmod(edge_geometry.perceived_bearing + 360. - offset, 360.);
|
||||
}
|
||||
}
|
||||
else if (isRoadsPairMergeable(
|
||||
detector, neighbor_geometries, neighbor_intersection_node, neighbor_curr))
|
||||
{ // Neighbor intersection has mergable neighbor_curr and neighbor_next roads
|
||||
BOOST_ASSERT(!isRoadsPairMergeable(
|
||||
detector, neighbor_geometries, neighbor_intersection_node, neighbor_prev));
|
||||
|
||||
// TODO: merge with an angle bisector, but not a reversed closed turn, to be
|
||||
// checked as a difference with the previous implementation
|
||||
const auto merge = findMergedBearing(
|
||||
graph, neighbor_geometries, neighbor_curr, neighbor_next, true);
|
||||
if (merge.first)
|
||||
{
|
||||
const auto offset = util::angularDeviation(
|
||||
merge.second, neighbor_geometries[neighbor_curr].perceived_bearing);
|
||||
|
||||
// Adjust bearing of AB at the node A if at the node B roads BA (neighbor_curr)
|
||||
// and BC (neighbor_next) will be merged and will have merged bearing Bb.
|
||||
// The adjustment value is ∠bBA with positive sign (clockwise) to Aa
|
||||
// a ~~~ A
|
||||
// /
|
||||
// --- B --- b
|
||||
// \
|
||||
// C
|
||||
edge_geometry.perceived_bearing = edge_geometry.initial_bearing =
|
||||
std::fmod(edge_geometry.perceived_bearing + offset, 360.);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add incoming edges with reversed bearings
|
||||
edge_geometries.resize(2 * edges_number);
|
||||
for (std::size_t index = 0; index < edges_number; ++index)
|
||||
{
|
||||
const auto &geometry = edge_geometries[index];
|
||||
const auto remote_node = graph.GetTarget(geometry.edge);
|
||||
const auto incoming_edge = graph.FindEdge(remote_node, intersection_node);
|
||||
edge_geometries[edges_number + index] = {incoming_edge,
|
||||
util::bearing::reverse(geometry.initial_bearing),
|
||||
util::bearing::reverse(geometry.perceived_bearing),
|
||||
geometry.length};
|
||||
}
|
||||
|
||||
// Enforce ordering of edges by IDs
|
||||
std::sort(edge_geometries.begin(), edge_geometries.end());
|
||||
|
||||
return std::make_pair(edge_geometries, merged_edge_ids);
|
||||
}
|
||||
|
||||
inline auto findEdge(const IntersectionEdgeGeometries &geometries, const EdgeID &edge)
|
||||
{
|
||||
const auto it = std::lower_bound(
|
||||
geometries.begin(), geometries.end(), edge, [](const auto &geometry, const auto edge) {
|
||||
return geometry.edge < edge;
|
||||
});
|
||||
BOOST_ASSERT(it != geometries.end() && it->edge == edge);
|
||||
return it;
|
||||
}
|
||||
|
||||
double findEdgeBearing(const IntersectionEdgeGeometries &geometries, const EdgeID &edge)
|
||||
{
|
||||
return findEdge(geometries, edge)->perceived_bearing;
|
||||
}
|
||||
|
||||
double findEdgeLength(const IntersectionEdgeGeometries &geometries, const EdgeID &edge)
|
||||
{
|
||||
return findEdge(geometries, edge)->length;
|
||||
}
|
||||
|
||||
template <typename RestrictionsRange>
|
||||
bool isTurnRestricted(const RestrictionsRange &restrictions, const NodeID to)
|
||||
{
|
||||
// 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`
|
||||
const auto is_only = std::find_if(restrictions.first,
|
||||
restrictions.second,
|
||||
[](const auto &pair) { return pair.second->is_only; });
|
||||
if (is_only != restrictions.second)
|
||||
return is_only->second->AsNodeRestriction().to != to;
|
||||
|
||||
// Check if explicitly forbidden
|
||||
const auto no_turn =
|
||||
std::find_if(restrictions.first, restrictions.second, [&to](const auto &restriction) {
|
||||
return restriction.second->AsNodeRestriction().to == to;
|
||||
});
|
||||
|
||||
return no_turn != restrictions.second;
|
||||
}
|
||||
|
||||
bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const IntersectionEdgeGeometries &geometries,
|
||||
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||
const IntersectionEdge &from,
|
||||
const IntersectionEdge &to)
|
||||
{
|
||||
BOOST_ASSERT(graph.GetTarget(from.edge) == to.node);
|
||||
|
||||
// TODO: to use TurnAnalysis all outgoing edges are required, to be removed later
|
||||
if (graph.GetEdgeData(from.edge).reversed || graph.GetEdgeData(to.edge).reversed)
|
||||
return false;
|
||||
|
||||
const auto intersection_node = to.node;
|
||||
const auto destination_node = graph.GetTarget(to.edge);
|
||||
auto const &restrictions = restriction_map.Restrictions(from.node, intersection_node);
|
||||
|
||||
// Check if turn is explicitly restricted by a turn restriction
|
||||
if (isTurnRestricted(restrictions, destination_node))
|
||||
return false;
|
||||
|
||||
// Precompute reversed bearing of the `from` edge
|
||||
const auto from_edge_reversed_bearing =
|
||||
util::bearing::reverse(findEdgeBearing(geometries, from.edge));
|
||||
|
||||
// Collect some information about the intersection
|
||||
// 1) number of allowed exits and adjacent bidirectional edges
|
||||
std::uint32_t allowed_exits = 0, bidirectional_edges = 0;
|
||||
// 2) edge IDs of roundabouts edges
|
||||
EdgeID roundabout_from = SPECIAL_EDGEID, roundabout_to = SPECIAL_EDGEID;
|
||||
double roundabout_from_angle = 0., roundabout_to_angle = 0.;
|
||||
|
||||
for (const auto eid : graph.GetAdjacentEdgeRange(intersection_node))
|
||||
{
|
||||
const auto &edge_data = graph.GetEdgeData(eid);
|
||||
const auto &edge_class = edge_data.flags;
|
||||
const auto to_node = graph.GetTarget(eid);
|
||||
const auto reverse_edge = graph.FindEdge(to_node, intersection_node);
|
||||
BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID);
|
||||
|
||||
const auto is_exit_edge = !edge_data.reversed && !isTurnRestricted(restrictions, to_node);
|
||||
const auto is_bidirectional = !graph.GetEdgeData(reverse_edge).reversed;
|
||||
allowed_exits += is_exit_edge;
|
||||
bidirectional_edges += is_bidirectional;
|
||||
|
||||
if (edge_class.roundabout || edge_class.circular)
|
||||
{
|
||||
if (edge_data.reversed)
|
||||
{
|
||||
// "Linked Roundabouts" is an example of tie between two linked roundabouts
|
||||
// A tie breaker for that maximizes ∠(roundabout_from_bearing, ¬from_edge_bearing)
|
||||
const auto angle = util::bearing::angleBetween(
|
||||
findEdgeBearing(geometries, reverse_edge), from_edge_reversed_bearing);
|
||||
if (angle > roundabout_from_angle)
|
||||
{
|
||||
roundabout_from = reverse_edge;
|
||||
roundabout_from_angle = angle;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// a tie breaker that maximizes ∠(¬from_edge_bearing, roundabout_to_bearing)
|
||||
const auto angle = util::bearing::angleBetween(from_edge_reversed_bearing,
|
||||
findEdgeBearing(geometries, eid));
|
||||
if (angle > roundabout_to_angle)
|
||||
{
|
||||
roundabout_to = eid;
|
||||
roundabout_to_angle = angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3) if the intersection has a barrier
|
||||
const bool is_barrier_node = barrier_nodes.find(intersection_node) != barrier_nodes.end();
|
||||
|
||||
// Check a U-turn
|
||||
if (from.node == destination_node)
|
||||
{
|
||||
// Allow U-turns before barrier nodes
|
||||
if (is_barrier_node)
|
||||
return true;
|
||||
|
||||
// Allow U-turns at dead-ends
|
||||
if (graph.GetAdjacentEdgeRange(intersection_node).size() == 1)
|
||||
return true;
|
||||
|
||||
// Allow U-turns at dead-ends if there is at most one bidirectional road at the intersection
|
||||
// The condition allows U-turns d→a→d and c→b→c ("Bike - Around the Block" test)
|
||||
// a→b
|
||||
// ↕ ↕
|
||||
// d↔c
|
||||
if (allowed_exits == 1 || bidirectional_edges <= 1)
|
||||
return true;
|
||||
|
||||
// Allow U-turn if the incoming edge has a U-turn lane
|
||||
// TODO: revisit the use-case, related PR #2753
|
||||
const auto &incoming_edge_annotation_id = graph.GetEdgeData(from.edge).annotation_data;
|
||||
const auto lane_description_id = static_cast<std::size_t>(
|
||||
node_data_container.GetAnnotation(incoming_edge_annotation_id).lane_description_id);
|
||||
if (lane_description_id != INVALID_LANE_DESCRIPTIONID)
|
||||
{
|
||||
const auto &turn_lane_offsets = std::get<0>(turn_lanes_data);
|
||||
const auto &turn_lanes = std::get<1>(turn_lanes_data);
|
||||
BOOST_ASSERT(lane_description_id + 1 < turn_lane_offsets.size());
|
||||
|
||||
if (std::any_of(turn_lanes.begin() + turn_lane_offsets[lane_description_id],
|
||||
turn_lanes.begin() + turn_lane_offsets[lane_description_id + 1],
|
||||
[](const auto &lane) { return lane & guidance::TurnLaneType::uturn; }))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't allow U-turns on usual intersections
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't allow turns via barriers for not U-turn maneuvers
|
||||
if (is_barrier_node)
|
||||
return false;
|
||||
|
||||
// Check for roundabouts exits in the opposite direction of roundabout flow
|
||||
if (roundabout_from != SPECIAL_EDGEID && roundabout_to != SPECIAL_EDGEID)
|
||||
{
|
||||
// Get bearings of edges
|
||||
const auto roundabout_from_bearing = findEdgeBearing(geometries, roundabout_from);
|
||||
const auto roundabout_to_bearing = findEdgeBearing(geometries, roundabout_to);
|
||||
const auto to_edge_bearing = findEdgeBearing(geometries, to.edge);
|
||||
|
||||
// Get angles from the roundabout edge to three other edges
|
||||
const auto roundabout_angle =
|
||||
util::bearing::angleBetween(roundabout_from_bearing, roundabout_to_bearing);
|
||||
const auto roundabout_from_angle =
|
||||
util::bearing::angleBetween(roundabout_from_bearing, from_edge_reversed_bearing);
|
||||
const auto roundabout_to_angle =
|
||||
util::bearing::angleBetween(roundabout_from_bearing, to_edge_bearing);
|
||||
|
||||
// Restrict turning over a roundabout if `roundabout_to_angle` is in
|
||||
// a sector between `roundabout_from_bearing` to `from_bearing` (shaded area)
|
||||
//
|
||||
// roundabout_angle = 270° roundabout_angle = 90°
|
||||
// roundabout_from_angle = 150° roundabout_from_angle = 150°
|
||||
// roundabout_to_angle = 90° roundabout_to_angle = 270°
|
||||
//
|
||||
// 150° 150°
|
||||
// v░░░░░░ ░░░░░░░░░v
|
||||
// v░░░░░░░ ░░░░░░░░v
|
||||
// 270° <-ooo- v -ttt-> 90° 270° <-ttt- v -ooo-> 90°
|
||||
// ^░░░░░░░ ░░░░░░░^
|
||||
// r░░░░░░░ ░░░░░░░r
|
||||
// r░░░░░░░ ░░░░░░░r
|
||||
if ((roundabout_from_angle < roundabout_angle &&
|
||||
roundabout_to_angle < roundabout_from_angle) ||
|
||||
(roundabout_from_angle > roundabout_angle &&
|
||||
roundabout_to_angle > roundabout_from_angle))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// The function adapts intersection geometry data to TurnAnalysis
|
||||
guidance::IntersectionView
|
||||
convertToIntersectionView(const util::NodeBasedDynamicGraph &graph,
|
||||
const EdgeBasedNodeDataContainer &node_data_container,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const IntersectionEdgeGeometries &edge_geometries,
|
||||
const guidance::TurnLanesIndexedArray &turn_lanes_data,
|
||||
const IntersectionEdge &incoming_edge,
|
||||
const IntersectionEdges &outgoing_edges,
|
||||
const std::unordered_set<EdgeID> &merged_edges)
|
||||
{
|
||||
using util::bearing::angleBetween;
|
||||
|
||||
const auto edge_it = findEdge(edge_geometries, incoming_edge.edge);
|
||||
const auto incoming_bearing = edge_it->perceived_bearing;
|
||||
const auto initial_incoming_bearing = edge_it->initial_bearing;
|
||||
|
||||
using IntersectionViewDataWithAngle = std::pair<guidance::IntersectionViewData, double>;
|
||||
std::vector<IntersectionViewDataWithAngle> pre_intersection_view;
|
||||
guidance::IntersectionViewData uturn{{SPECIAL_EDGEID, 0., 0.}, false, 0.};
|
||||
std::size_t allowed_uturns_number = 0;
|
||||
for (const auto &outgoing_edge : outgoing_edges)
|
||||
{
|
||||
const auto is_uturn = [](const auto angle) {
|
||||
return std::fabs(angle) < std::numeric_limits<double>::epsilon();
|
||||
};
|
||||
|
||||
const auto edge_it = findEdge(edge_geometries, outgoing_edge.edge);
|
||||
const auto segment_length = edge_it->length;
|
||||
const auto is_merged = merged_edges.count(outgoing_edge.edge) != 0;
|
||||
const auto is_turn_allowed = intersection::isTurnAllowed(graph,
|
||||
node_data_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
edge_geometries,
|
||||
turn_lanes_data,
|
||||
incoming_edge,
|
||||
outgoing_edge);
|
||||
|
||||
// Compute angles
|
||||
const auto outgoing_bearing = edge_it->perceived_bearing;
|
||||
const auto initial_outgoing_bearing = edge_it->initial_bearing;
|
||||
auto turn_angle = std::fmod(
|
||||
std::round(angleBetween(incoming_bearing, outgoing_bearing) * 1e8) / 1e8, 360.);
|
||||
auto initial_angle = angleBetween(initial_incoming_bearing, initial_outgoing_bearing);
|
||||
|
||||
// If angle of the allowed turn is in a neighborhood of 0° (±15°) but the initial OSM angle
|
||||
// is in the opposite semi-plane then assume explicitly a U-turn to avoid incorrect
|
||||
// adjustments due to numerical noise in selection of representative_coordinate
|
||||
if (is_turn_allowed &&
|
||||
((turn_angle < 15 && initial_angle > 180) || (turn_angle > 345 && initial_angle < 180)))
|
||||
{
|
||||
turn_angle = 0;
|
||||
initial_angle = 0;
|
||||
}
|
||||
|
||||
const auto is_uturn_angle = is_uturn(turn_angle);
|
||||
|
||||
guidance::IntersectionViewData road{
|
||||
{outgoing_edge.edge, outgoing_bearing, segment_length}, is_turn_allowed, turn_angle};
|
||||
|
||||
if (graph.GetTarget(outgoing_edge.edge) == incoming_edge.node)
|
||||
{ // Save the true U-turn road to add later if no allowed U-turns will be added
|
||||
uturn = road;
|
||||
}
|
||||
else if (is_turn_allowed || (!is_merged && !is_uturn_angle))
|
||||
{ // Add roads that have allowed entry or not U-turns and not merged
|
||||
allowed_uturns_number += is_uturn_angle;
|
||||
|
||||
// Adjust computed initial turn angle for non-U-turn road edge cases:
|
||||
// 1) use 0° or 360° if the road has 0° initial angle
|
||||
// 2) use turn angle if the smallest arc between turn and initial angles passes 0°
|
||||
const auto use_turn_angle = (turn_angle > 270 && initial_angle < 90) ||
|
||||
(turn_angle < 90 && initial_angle > 270);
|
||||
const auto adjusted_angle = is_uturn(initial_angle)
|
||||
? (turn_angle > 180. ? 360. : 0.)
|
||||
: use_turn_angle ? turn_angle : initial_angle;
|
||||
pre_intersection_view.push_back({road, adjusted_angle});
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_ASSERT(uturn.eid != SPECIAL_EDGEID);
|
||||
if (uturn.entry_allowed || allowed_uturns_number == 0)
|
||||
{ // Add the true U-turn if it is allowed or no other U-turns found
|
||||
pre_intersection_view.insert(pre_intersection_view.begin(), {uturn, 0});
|
||||
}
|
||||
|
||||
// Order roads in counter-clockwise order starting from the U-turn edge in the OSM order
|
||||
std::stable_sort(pre_intersection_view.begin(),
|
||||
pre_intersection_view.end(),
|
||||
[](const auto &lhs, const auto &rhs) {
|
||||
return std::tie(lhs.second, lhs.first.angle) <
|
||||
std::tie(rhs.second, rhs.first.angle);
|
||||
});
|
||||
|
||||
// Adjust perceived bearings to keep the initial OSM order with respect to the first edge
|
||||
for (auto curr = pre_intersection_view.begin(), next = std::next(curr);
|
||||
next != pre_intersection_view.end();
|
||||
++curr, ++next)
|
||||
{
|
||||
// Check that the perceived angles order is the same as the initial OSM one
|
||||
if (next->first.angle < curr->first.angle)
|
||||
{ // If the true bearing is out of the initial order (next before current) then
|
||||
// adjust the next road angle to keep the order. The adjustment angle is at most
|
||||
// 0.5° or a half-angle between the current angle and 360° to prevent overlapping
|
||||
const auto angle_adjustment =
|
||||
std::min(.5, util::restrictAngleToValidRange(360. - curr->first.angle) / 2.);
|
||||
next->first.angle =
|
||||
util::restrictAngleToValidRange(curr->first.angle + angle_adjustment);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy intersection view data
|
||||
guidance::IntersectionView intersection_view;
|
||||
intersection_view.reserve(pre_intersection_view.size());
|
||||
std::transform(pre_intersection_view.begin(),
|
||||
pre_intersection_view.end(),
|
||||
std::back_inserter(intersection_view),
|
||||
[](const auto &road) { return road.first; });
|
||||
|
||||
return intersection_view;
|
||||
}
|
||||
|
||||
// a
|
||||
// |
|
||||
// |
|
||||
// v
|
||||
// For an intersection from_node --via_eid--> turn_node ----> c
|
||||
// ^
|
||||
// |
|
||||
// |
|
||||
// b
|
||||
// This functions returns _all_ turns as if the graph was undirected.
|
||||
// That means we not only get (from_node, turn_node, c) in the above example
|
||||
// but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are
|
||||
// marked as invalid and only needed for intersection classification.
|
||||
template <bool USE_CLOSE_COORDINATE>
|
||||
guidance::IntersectionView
|
||||
getConnectedRoads(const util::NodeBasedDynamicGraph &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 IntersectionEdge &incoming_edge)
|
||||
{
|
||||
const auto intersection_node = graph.GetTarget(incoming_edge.edge);
|
||||
const auto &outgoing_edges = intersection::getOutgoingEdges(graph, intersection_node);
|
||||
auto edge_geometries = getIntersectionOutgoingGeometries<USE_CLOSE_COORDINATE>(
|
||||
graph, compressed_geometries, node_coordinates, intersection_node);
|
||||
|
||||
// Add incoming edges with reversed bearings
|
||||
const auto edges_number = edge_geometries.size();
|
||||
edge_geometries.resize(2 * edges_number);
|
||||
for (std::size_t index = 0; index < edges_number; ++index)
|
||||
{
|
||||
const auto &geometry = edge_geometries[index];
|
||||
const auto remote_node = graph.GetTarget(geometry.edge);
|
||||
const auto incoming_edge = graph.FindEdge(remote_node, intersection_node);
|
||||
edge_geometries[edges_number + index] = {incoming_edge,
|
||||
util::bearing::reverse(geometry.initial_bearing),
|
||||
util::bearing::reverse(geometry.perceived_bearing),
|
||||
geometry.length};
|
||||
}
|
||||
|
||||
// Enforce ordering of edges by IDs
|
||||
std::sort(edge_geometries.begin(), edge_geometries.end());
|
||||
|
||||
return convertToIntersectionView(graph,
|
||||
node_data_container,
|
||||
node_restriction_map,
|
||||
barrier_nodes,
|
||||
edge_geometries,
|
||||
turn_lanes_data,
|
||||
incoming_edge,
|
||||
outgoing_edges,
|
||||
std::unordered_set<EdgeID>());
|
||||
}
|
||||
|
||||
template guidance::IntersectionView
|
||||
getConnectedRoads<false>(const util::NodeBasedDynamicGraph &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 IntersectionEdge &incoming_edge);
|
||||
|
||||
template guidance::IntersectionView
|
||||
getConnectedRoads<true>(const util::NodeBasedDynamicGraph &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 IntersectionEdge &incoming_edge);
|
||||
|
||||
IntersectionEdge skipDegreeTwoNodes(const util::NodeBasedDynamicGraph &graph, IntersectionEdge road)
|
||||
{
|
||||
std::unordered_set<NodeID> visited_nodes;
|
||||
(void)visited_nodes;
|
||||
|
||||
// Skip trivial nodes without generating the intersection in between, stop at the very first
|
||||
// intersection of degree > 2
|
||||
const auto starting_node = road.node;
|
||||
auto next_node = graph.GetTarget(road.edge);
|
||||
while (graph.GetOutDegree(next_node) == 2 && next_node != starting_node)
|
||||
{
|
||||
BOOST_ASSERT(visited_nodes.insert(next_node).second);
|
||||
const auto next_edge = graph.BeginEdges(next_node);
|
||||
road.edge = graph.GetTarget(next_edge) == road.node ? next_edge + 1 : next_edge;
|
||||
road.node = next_node;
|
||||
next_node = graph.GetTarget(road.edge);
|
||||
}
|
||||
|
||||
return road;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,9 +76,8 @@ NAN_MODULE_INIT(Engine::Init)
|
||||
* @param {Number} [options.max_locations_viaroute] Max. locations supported in viaroute query (default: unlimited).
|
||||
* @param {Number} [options.max_locations_distance_table] Max. locations supported in distance table query (default: unlimited).
|
||||
* @param {Number} [options.max_locations_map_matching] Max. locations supported in map-matching query (default: unlimited).
|
||||
* @param {Number} [options.max_radius_map_matching] Max. radius size supported in map matching query (default: 5).
|
||||
* @param {Number} [options.max_results_nearest] Max. results supported in nearest query (default: unlimited).
|
||||
* @param {Number} [options.max_alternatives] Max. number of alternatives supported in alternative routes query (default: 3).
|
||||
* @param {Number} [options.max_alternatives] Max.number of alternatives supported in alternative routes query (default: 3).
|
||||
*
|
||||
* @class OSRM
|
||||
*
|
||||
|
||||
@@ -133,10 +133,7 @@ inline unsigned generateServerProgramOptions(const int argc,
|
||||
"Max. results supported in nearest query") //
|
||||
("max-alternatives",
|
||||
value<int>(&config.max_alternatives)->default_value(3),
|
||||
"Max. number of alternatives supported in the MLD route query") //
|
||||
("max-matching-radius",
|
||||
value<double>(&config.max_radius_map_matching)->default_value(5),
|
||||
"Max. radius size supported in map matching query");
|
||||
"Max. number of alternatives supported in the MLD route query");
|
||||
|
||||
// hidden options, will be allowed on command line, but will not be shown to the user
|
||||
boost::program_options::options_description hidden_options("Hidden options");
|
||||
|
||||
@@ -26,8 +26,10 @@ namespace
|
||||
// creates a default edge of unit weight
|
||||
inline InputEdge MakeUnitEdge(const NodeID from, const NodeID to)
|
||||
{
|
||||
return {from, // source
|
||||
to, // target
|
||||
// src, tgt, dist, edge_id, name_id, fwd, bkwd, roundabout, circular, startpoint, local access,
|
||||
// split edge, travel_mode
|
||||
return {from,
|
||||
to,
|
||||
1, // weight
|
||||
1, // duration
|
||||
GeometryID{0, false}, // geometry_id
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
#include "extractor/intersection/intersection_analysis.hpp"
|
||||
|
||||
#include "extractor/graph_compressor.hpp"
|
||||
|
||||
#include "../common/range_tools.hpp"
|
||||
#include "../unit_tests/mocks/mock_scripting_environment.hpp"
|
||||
|
||||
#include <boost/test/test_case_template.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(intersection_analysis_tests)
|
||||
|
||||
using namespace osrm;
|
||||
using namespace osrm::extractor;
|
||||
using namespace osrm::extractor::guidance;
|
||||
using namespace osrm::extractor::intersection;
|
||||
using InputEdge = util::NodeBasedDynamicGraph::InputEdge;
|
||||
using Graph = util::NodeBasedDynamicGraph;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(simple_intersection_connectivity)
|
||||
{
|
||||
std::unordered_set<NodeID> barrier_nodes{6};
|
||||
std::unordered_set<NodeID> traffic_lights;
|
||||
std::vector<NodeBasedEdgeAnnotation> annotations{
|
||||
{EMPTY_NAMEID, 0, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false},
|
||||
{EMPTY_NAMEID, 1, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}};
|
||||
std::vector<TurnRestriction> restrictions{TurnRestriction{NodeRestriction{0, 2, 1}, false}};
|
||||
std::vector<ConditionalTurnRestriction> conditional_restrictions;
|
||||
CompressedEdgeContainer container;
|
||||
test::MockScriptingEnvironment scripting_environment;
|
||||
|
||||
TurnLanesIndexedArray turn_lanes_data{{0, 0, 3},
|
||||
{TurnLaneType::uturn | TurnLaneType::left,
|
||||
TurnLaneType::straight,
|
||||
TurnLaneType::straight | TurnLaneType::right}};
|
||||
|
||||
// Graph with an additional turn restriction 0→2→1 and bollard at 6
|
||||
// 0→5↔6↔7
|
||||
// ↕
|
||||
// 1↔2←3
|
||||
// ↓
|
||||
// 4
|
||||
const auto unit_edge =
|
||||
[](const NodeID from, const NodeID to, bool allowed, AnnotationID annotation) {
|
||||
return InputEdge{from,
|
||||
to,
|
||||
1,
|
||||
1,
|
||||
GeometryID{0, false},
|
||||
!allowed,
|
||||
NodeBasedEdgeClassification(),
|
||||
annotation};
|
||||
};
|
||||
|
||||
std::vector<InputEdge> edges = {unit_edge(0, 2, true, 1),
|
||||
unit_edge(0, 5, true, 0),
|
||||
unit_edge(1, 2, true, 0),
|
||||
unit_edge(2, 0, true, 0),
|
||||
unit_edge(2, 1, true, 0),
|
||||
unit_edge(2, 3, false, 0),
|
||||
unit_edge(2, 4, true, 0),
|
||||
unit_edge(3, 2, true, 0),
|
||||
unit_edge(4, 2, false, 0),
|
||||
unit_edge(5, 0, false, 0),
|
||||
unit_edge(5, 6, true, 0),
|
||||
unit_edge(6, 5, true, 0),
|
||||
unit_edge(6, 7, true, 0),
|
||||
unit_edge(7, 6, true, 0)};
|
||||
IntersectionEdgeGeometries edge_geometries{
|
||||
{0, 180, 180, 10.}, // 0→2
|
||||
{1, 90, 90, 10.}, // 0→5
|
||||
{2, 90, 90, 10.}, // 1→2
|
||||
{3, 0, 0, 10.}, // 2→0
|
||||
{4, 270, 270, 10.}, // 2→1
|
||||
{5, 90, 90, 10.}, // 2→3
|
||||
{6, 180, 180, 10.}, // 2→4
|
||||
{7, 270, 270, 10.}, // 3→2
|
||||
{8, 0, 0, 10.}, // 4→2
|
||||
{9, 270, 270, 10.}, // 5→0
|
||||
{10, 90, 90, 10.}, // 5→6
|
||||
{11, 270, 270, 10.}, // 6→5
|
||||
{12, 90, 90, 10.}, // 6→7
|
||||
{13, 270, 270, 10.} // 7→6
|
||||
};
|
||||
|
||||
Graph graph(8, edges);
|
||||
|
||||
GraphCompressor().Compress(barrier_nodes,
|
||||
traffic_lights,
|
||||
scripting_environment,
|
||||
restrictions,
|
||||
conditional_restrictions,
|
||||
graph,
|
||||
annotations,
|
||||
container);
|
||||
|
||||
REQUIRE_SIZE_RANGE(getIncomingEdges(graph, 2), 3);
|
||||
REQUIRE_SIZE_RANGE(getOutgoingEdges(graph, 2), 4);
|
||||
|
||||
EdgeBasedNodeDataContainer node_data_container(
|
||||
std::vector<EdgeBasedNode>(graph.GetNumberOfEdges()), annotations);
|
||||
RestrictionMap restriction_map(restrictions, IndexNodeByFromAndVia());
|
||||
|
||||
const auto connectivity_matrix = [&](NodeID node) {
|
||||
std::vector<bool> result;
|
||||
const auto incoming_edges = getIncomingEdges(graph, node);
|
||||
const auto outgoing_edges = getOutgoingEdges(graph, node);
|
||||
for (const auto incoming_edge : incoming_edges)
|
||||
{
|
||||
for (const auto outgoing_edge : outgoing_edges)
|
||||
{
|
||||
result.push_back(isTurnAllowed(graph,
|
||||
node_data_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
edge_geometries,
|
||||
turn_lanes_data,
|
||||
incoming_edge,
|
||||
outgoing_edge));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(0), 1, 1); // from node 2 allowed U-turn and to node 5
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(1), 1); // from node 2 allowed U-turn
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(2),
|
||||
// clang-format off
|
||||
1, 0, 0, 1, // from node 0 to node 4 and a U-turn at 2
|
||||
1, 0, 0, 1, // from node 1 to nodes 0 and 4
|
||||
1, 1, 0, 1 // from node 3 to nodes 0, 1 and 4
|
||||
// clang-format on
|
||||
);
|
||||
REQUIRE_SIZE_RANGE(connectivity_matrix(3), 0); // no incoming edges, empty matrix
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(4), 0); // from node 2 not allowed U-turn
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(5),
|
||||
// clang-format off
|
||||
0, 1, // from node 0 to node 6
|
||||
0, 1, // from node 6 a U-turn to node 6
|
||||
// clang-format on
|
||||
);
|
||||
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(6),
|
||||
// clang-format off
|
||||
1, 0, // from node 5 a U-turn to node 5
|
||||
0, 1, // from node 7 a U-turn to node 7
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity)
|
||||
{
|
||||
std::unordered_set<NodeID> barrier_nodes;
|
||||
std::unordered_set<NodeID> traffic_lights;
|
||||
std::vector<NodeBasedEdgeAnnotation> annotations;
|
||||
std::vector<TurnRestriction> restrictions;
|
||||
std::vector<ConditionalTurnRestriction> conditional_restrictions;
|
||||
CompressedEdgeContainer container;
|
||||
test::MockScriptingEnvironment scripting_environment;
|
||||
|
||||
TurnLanesIndexedArray turn_lanes_data;
|
||||
|
||||
// Graph with roundabout edges 5→0→2
|
||||
// 1 2 3
|
||||
// ↘ ↑ ↙
|
||||
// 0
|
||||
// ↙ ↑ ↘
|
||||
// 4 5 6
|
||||
const auto unit_edge = [](const NodeID from, const NodeID to, bool allowed, bool roundabout) {
|
||||
return InputEdge{
|
||||
from,
|
||||
to,
|
||||
1,
|
||||
1,
|
||||
GeometryID{0, false},
|
||||
!allowed,
|
||||
NodeBasedEdgeClassification{true, false, false, roundabout, false, false, false, {}},
|
||||
0};
|
||||
};
|
||||
std::vector<InputEdge> edges = {unit_edge(0, 1, false, false),
|
||||
unit_edge(0, 2, true, true),
|
||||
unit_edge(0, 3, false, false),
|
||||
unit_edge(0, 4, true, false),
|
||||
unit_edge(0, 5, false, true),
|
||||
unit_edge(0, 6, true, false),
|
||||
unit_edge(1, 0, true, false),
|
||||
unit_edge(2, 0, false, true),
|
||||
unit_edge(3, 0, true, false),
|
||||
unit_edge(4, 0, false, false),
|
||||
unit_edge(5, 0, true, true),
|
||||
unit_edge(6, 0, false, false)};
|
||||
IntersectionEdgeGeometries edge_geometries{
|
||||
{0, 315, 315, 10}, // 0→1
|
||||
{1, 0, 0, 10}, // 0→2
|
||||
{2, 45, 45, 10}, // 0→3
|
||||
{3, 225, 225, 10}, // 0→4
|
||||
{4, 180, 180, 10}, // 0→5
|
||||
{5, 135, 135, 10}, // 0→6
|
||||
{6, 135, 135, 10}, // 1→0
|
||||
{7, 180, 180, 10}, // 2→0
|
||||
{8, 225, 225, 10}, // 3→0
|
||||
{9, 45, 45, 10}, // 4→0
|
||||
{10, 0, 0, 10}, // 5→0
|
||||
{11, 315, 315, 10} // 6→0
|
||||
};
|
||||
|
||||
Graph graph(7, edges);
|
||||
|
||||
GraphCompressor().Compress(barrier_nodes,
|
||||
traffic_lights,
|
||||
scripting_environment,
|
||||
restrictions,
|
||||
conditional_restrictions,
|
||||
graph,
|
||||
annotations,
|
||||
container);
|
||||
|
||||
REQUIRE_SIZE_RANGE(getIncomingEdges(graph, 0), 3);
|
||||
REQUIRE_SIZE_RANGE(getOutgoingEdges(graph, 0), 6);
|
||||
|
||||
EdgeBasedNodeDataContainer node_data_container(
|
||||
std::vector<EdgeBasedNode>(graph.GetNumberOfEdges()), annotations);
|
||||
RestrictionMap restriction_map(restrictions, IndexNodeByFromAndVia());
|
||||
|
||||
const auto connectivity_matrix = [&](NodeID node) {
|
||||
std::vector<bool> result;
|
||||
const auto incoming_edges = getIncomingEdges(graph, node);
|
||||
const auto outgoing_edges = getOutgoingEdges(graph, node);
|
||||
for (const auto incoming_edge : incoming_edges)
|
||||
{
|
||||
for (const auto outgoing_edge : outgoing_edges)
|
||||
{
|
||||
result.push_back(isTurnAllowed(graph,
|
||||
node_data_container,
|
||||
restriction_map,
|
||||
barrier_nodes,
|
||||
edge_geometries,
|
||||
turn_lanes_data,
|
||||
incoming_edge,
|
||||
outgoing_edge));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
CHECK_EQUAL_RANGE(connectivity_matrix(0),
|
||||
// clang-format off
|
||||
0, 1, 0, 0, 0, 1, // from node 1 to nodes 2 and 6
|
||||
0, 1, 0, 1, 0, 0, // from node 3 to nodes 2 and 4
|
||||
0, 1, 0, 1, 0, 1 // from node 5 to nodes 2, 4 and 6
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(skip_degree_two_nodes)
|
||||
{
|
||||
std::unordered_set<NodeID> barrier_nodes{1};
|
||||
std::unordered_set<NodeID> traffic_lights{2};
|
||||
std::vector<NodeBasedEdgeAnnotation> annotations(1);
|
||||
std::vector<TurnRestriction> restrictions;
|
||||
std::vector<ConditionalTurnRestriction> conditional_restrictions;
|
||||
CompressedEdgeContainer container;
|
||||
test::MockScriptingEnvironment scripting_environment;
|
||||
|
||||
TurnLanesIndexedArray turn_lanes_data;
|
||||
|
||||
// Graph
|
||||
//
|
||||
// 0↔1→2↔3↔4→5 7
|
||||
// ↑ ↕ ↕
|
||||
// 6 8 ↔ 9
|
||||
//
|
||||
const auto unit_edge = [](const NodeID from, const NodeID to, bool allowed) {
|
||||
return InputEdge{
|
||||
from, to, 1, 1, GeometryID{0, false}, !allowed, NodeBasedEdgeClassification{}, 0};
|
||||
};
|
||||
std::vector<InputEdge> edges = {unit_edge(0, 1, true), // 0
|
||||
unit_edge(1, 0, true),
|
||||
unit_edge(1, 2, true),
|
||||
unit_edge(2, 1, false),
|
||||
unit_edge(2, 3, true),
|
||||
unit_edge(3, 2, true), // 5
|
||||
unit_edge(3, 4, true),
|
||||
unit_edge(4, 3, true),
|
||||
unit_edge(4, 5, true),
|
||||
unit_edge(4, 6, false),
|
||||
unit_edge(5, 4, false), // 10
|
||||
unit_edge(6, 4, true),
|
||||
// Circle
|
||||
unit_edge(7, 8, true), // 12
|
||||
unit_edge(7, 9, true),
|
||||
unit_edge(8, 7, true),
|
||||
unit_edge(8, 9, true),
|
||||
unit_edge(9, 7, true),
|
||||
unit_edge(9, 8, true)};
|
||||
|
||||
Graph graph(10, edges);
|
||||
|
||||
GraphCompressor().Compress(barrier_nodes,
|
||||
traffic_lights,
|
||||
scripting_environment,
|
||||
restrictions,
|
||||
conditional_restrictions,
|
||||
graph,
|
||||
annotations,
|
||||
container);
|
||||
|
||||
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {0, 0}).edge), 4);
|
||||
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {4, 7}).edge), 0);
|
||||
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {5, 10}).edge), 4);
|
||||
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {6, 11}).edge), 4);
|
||||
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {7, 12}).edge), 7);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(test_table_limits)
|
||||
BOOST_CHECK(code == "TooBig"); // per the New-Server API spec
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_match_coordinate_limits)
|
||||
BOOST_AUTO_TEST_CASE(test_match_limits)
|
||||
{
|
||||
using namespace osrm;
|
||||
|
||||
@@ -131,38 +131,6 @@ BOOST_AUTO_TEST_CASE(test_match_coordinate_limits)
|
||||
BOOST_CHECK(code == "TooBig"); // per the New-Server API spec
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_match_radiuses_limits)
|
||||
{
|
||||
using namespace osrm;
|
||||
|
||||
EngineConfig config;
|
||||
config.storage_config = {OSRM_TEST_DATA_DIR "/ch/monaco.osrm"};
|
||||
config.use_shared_memory = false;
|
||||
config.max_radius_map_matching = 2.0;
|
||||
|
||||
OSRM osrm{config};
|
||||
|
||||
MatchParameters params;
|
||||
osrm::util::Coordinate coord1 = {osrm::util::FloatLongitude{7.41748809814453},
|
||||
osrm::util::FloatLatitude{43.73558473009846}};
|
||||
osrm::util::Coordinate coord2 = {osrm::util::FloatLongitude{7.417193055152893},
|
||||
osrm::util::FloatLatitude{43.735162245104775}};
|
||||
params.coordinates.emplace_back(coord1);
|
||||
params.coordinates.emplace_back(coord2);
|
||||
params.radiuses.emplace_back(3.0);
|
||||
params.radiuses.emplace_back(2.0);
|
||||
|
||||
json::Object result;
|
||||
|
||||
const auto rc = osrm.Match(params, result);
|
||||
|
||||
BOOST_CHECK(rc == Status::Error);
|
||||
|
||||
// Make sure we're not accidentally hitting a guard code path before
|
||||
const auto code = result.values["code"].get<json::String>().value;
|
||||
BOOST_CHECK(code == "TooBig"); // per the New-Server API spec
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_nearest_limits)
|
||||
{
|
||||
using namespace osrm;
|
||||
|
||||
@@ -74,6 +74,8 @@ BOOST_AUTO_TEST_CASE(invalid_route_urls)
|
||||
BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>("1,2;3,4?annotations=true,false"), 24UL);
|
||||
BOOST_CHECK_EQUAL(
|
||||
testInvalidOptions<RouteParameters>("1,2;3,4?annotations=&overview=simplified"), 20UL);
|
||||
|
||||
// BOOST_CHECK_EQUAL(testInvalidOptions<RouteParameters>(), );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(invalid_table_urls)
|
||||
@@ -557,22 +559,6 @@ BOOST_AUTO_TEST_CASE(valid_match_urls)
|
||||
CHECK_EQUAL_RANGE(reference_2.coordinates, result_2->coordinates);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(invalid_match_urls)
|
||||
{
|
||||
std::vector<util::Coordinate> coords_1 = {{util::FloatLongitude{1}, util::FloatLatitude{2}},
|
||||
{util::FloatLongitude{3}, util::FloatLatitude{4}}};
|
||||
|
||||
MatchParameters reference_1{};
|
||||
reference_1.coordinates = coords_1;
|
||||
auto result_1 = parseParameters<MatchParameters>("1,2;3,4?radiuses=unlimited;60");
|
||||
BOOST_CHECK(result_1);
|
||||
CHECK_EQUAL_RANGE(reference_1.timestamps, result_1->timestamps);
|
||||
CHECK_EQUAL_RANGE(reference_1.bearings, result_1->bearings);
|
||||
BOOST_CHECK(reference_1.radiuses != result_1->radiuses);
|
||||
CHECK_EQUAL_RANGE(reference_1.approaches, result_1->approaches);
|
||||
CHECK_EQUAL_RANGE(reference_1.coordinates, result_1->coordinates);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(valid_nearest_urls)
|
||||
{
|
||||
std::vector<util::Coordinate> coords_1 = {{util::FloatLongitude{1}, util::FloatLatitude{2}}};
|
||||
|
||||
Reference in New Issue
Block a user